{"cells":[{"metadata":{},"cell_type":"markdown","source":"With an IMU, it might be possible to calculate the relative position. I'll do this in this notebook and calculate the error if with a known start position. \n\nThis notebook has not a conclusion or main message (yet). The idea is more to provide some functionality, which might be useful. \n\nThanks to https://www.kaggle.com/hrshtt/kalman-filters-and-imus-starter and https://www.kaggle.com/tosinabase/basic-eda-traces-and-features-visualization"},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\nimport os\nimport matplotlib.pyplot as plt","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"First, I'll clone the github repro since there are some useful functions for calculating the relative position "},{"metadata":{"trusted":true},"cell_type":"code","source":"!git clone https://github.com/location-competition/indoor-location-competition-20.git api","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"from api.compute_f import *\nfrom api.io_f import * \nfrom api.visualize_f import * ","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"import json\nfrom pathlib import Path","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# I copied these visualization functions mainly from https://www.kaggle.com/hrshtt/kalman-filters-and-imus-starter and modified them slightly. \n\ndef visualize_trajectory(trajectory, floor_plan_filename, width_meter, height_meter, title=None, mode='lines + markers + text', show=False, trajectory2=None):\n    \"\"\"\n    Copied and modified from from https://github.com/location-competition/indoor-location-competition-20/blob/master/visualize_f.py\n\n    \"\"\"\n    fig = go.Figure()\n\n    # add trajectory\n    size_list = [6] * trajectory.shape[0]\n    size_list[0] = 10\n    size_list[-1] = 10\n\n    color_list = ['rgba(4, 174, 4, 0.5)'] * trajectory.shape[0]\n    color_list[0] = 'rgba(12, 5, 235, 1)'\n    color_list[-1] = 'rgba(235, 5, 5, 1)'\n    \n    \n\n    position_count = {}\n    text_list = []\n    for i in range(trajectory.shape[0]):\n        if str(trajectory[i]) in position_count:\n            position_count[str(trajectory[i])] += 1\n        else:\n            position_count[str(trajectory[i])] = 0\n        text_list.append('        ' * position_count[str(trajectory[i])] + f'{i}')\n    text_list[0] = 'Start 0'\n    text_list[-1] = f'End {trajectory.shape[0] - 1}'\n\n    fig.add_trace(\n        go.Scattergl(\n            x=trajectory[:, 0],\n            y=trajectory[:, 1],\n            mode=mode,\n            marker=dict(size=size_list, color=color_list),\n            line=dict(shape='linear', color='lightgrey', width=3, dash='dash'),\n            text=text_list,\n            textposition=\"top center\",\n            name='trajectory',\n        ))\n    \n    if trajectory2 is not None:\n        size_list2 = [6] * trajectory2.shape[0]\n        size_list2[0] = 10\n        size_list2[-1] = 10\n        \n        color_list2 = ['rgba(4, 4, 174, 0.5)'] * trajectory2[0].shape[0]\n        color_list2[0] = 'rgba(235, 5, 5, 1)'\n        color_list2[-1] = 'rgba(5, 235, 5, 1)'\n        fig.add_trace(\n            go.Scattergl(\n                x=trajectory2[:, 0],\n                y=trajectory2[:, 1],\n                mode=mode,\n                marker=dict(size=size_list2, color=color_list2),\n                line=dict(shape='linear', color='red', width=3, dash='dash'),\n                #text=text_list,\n                textposition=\"top center\",\n                name='trajectory',\n            ))\n\n    # add floor plan\n    floor_plan = Image.open(floor_plan_filename)\n    fig.update_layout(images=[\n        go.layout.Image(\n            source=floor_plan,\n            xref=\"x\",\n            yref=\"y\",\n            x=0,\n            y=height_meter,\n            sizex=width_meter,\n            sizey=height_meter,\n            sizing=\"contain\",\n            opacity=1,\n            layer=\"below\",\n        )\n    ])\n\n    # configure\n    fig.update_xaxes(autorange=False, range=[0, width_meter])\n    fig.update_yaxes(autorange=False, range=[0, height_meter], scaleanchor=\"x\", scaleratio=1)\n    fig.update_layout(\n        title=go.layout.Title(\n            text=title or \"No title.\",\n            xref=\"paper\",\n            x=0,\n        ),\n        autosize=True,\n        width=800,\n        height=  800 * height_meter / width_meter,\n        template=\"plotly_white\",\n    )\n\n    if show:\n        fig.show()\n\n    return fig\n\ndef visualize_train_trajectory(txt_path):\n    \"\"\"\n    Edited from \n    https://www.kaggle.com/hrshtt/intro-to-indoor-location-navigation/\n    who Edited from \n    https://www.kaggle.com/ihelon/indoor-location-exploratory-data-analysis\n    \"\"\"\n    if not isinstance(txt_path, Path):\n        path = Path(txt_path)\n    \n    _id, floor, filename = txt_path.parts[-3:]\n\n    \n    train_floor_data = read_data_file(txt_path)\n    with open(f\"../input/indoor-location-navigation/metadata/{_id}/{floor}/floor_info.json\") as f:\n        train_floor_info = json.load(f)\n\n    return visualize_trajectory(\n        train_floor_data.waypoint[:, 1:3], \n        f\"../input/indoor-location-navigation/metadata/{_id}/{floor}/floor_image.png\",\n        train_floor_info[\"map_info\"][\"width\"], \n        train_floor_info[\"map_info\"][\"height\"],\n        f\"Visualization of {_id}/{floor}/{filename}\"\n    )\n\ndef visualize_train_step_trajectory(txt_path, step_positions):\n    \"\"\"\n    Edited from \n    https://www.kaggle.com/hrshtt/intro-to-indoor-location-navigation/\n    who Edited from \n    https://www.kaggle.com/ihelon/indoor-location-exploratory-data-analysis\n    \"\"\"\n#     _id, floor = path.split(\"/\")[:2]\n    if not isinstance(txt_path, Path):\n        path = Path(txt_path)\n    \n    _id, floor, filename = txt_path.parts[-3:]\n\n    \n    train_floor_data = read_data_file(txt_path)\n    with open(f\"../input/indoor-location-navigation/metadata/{_id}/{floor}/floor_info.json\") as f:\n        train_floor_info = json.load(f)\n        \n    return visualize_trajectory(\n        train_floor_data.waypoint[:, 1:3], \n        f\"../input/indoor-location-navigation/metadata/{_id}/{floor}/floor_image.png\",\n        train_floor_info[\"map_info\"][\"width\"], \n        train_floor_info[\"map_info\"][\"height\"],\n        f\"Visualization of {_id}/{floor}/{filename}\", \n        trajectory2 = step_positions[:, 1:] #, step_positions[:, 2]\n    )\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"sample_txt_file_path = Path('../input/indoor-location-navigation/train/5a0546857ecc773753327266/B1/5e15730aa280850006f3d005.txt')\nsample_txt_file_path","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Predicts the position given the IMU and the starting point"},{"metadata":{"trusted":true},"cell_type":"code","source":"def predict_position(trajectory_file): \n    # Copied and modified from https://github.com/location-competition/indoor-location-competition-20/blob/master/compute_f.py\n    sample_data = read_data_file(str(trajectory_file))\n    \n    # GT position \n    waypoint_df = pd.DataFrame(sample_data.waypoint)\n    waypoint_df.columns = ['timestamp', 'waypoint_x','waypoint_y']\n    \n    # Compute relative position\n    step_timestamps, step_indexs, step_acce_max_mins = compute_steps(sample_data.acce)\n    headings = compute_headings(sample_data.ahrs)\n    stride_lengths = compute_stride_length(step_acce_max_mins)\n    step_headings = compute_step_heading(step_timestamps, headings)\n    rel_positions = compute_rel_positions(stride_lengths, step_headings)\n    \n    rel_positions[:, 1].cumsum(), rel_positions[:, 2].cumsum()\n    \n    predicted_positions = np.zeros(rel_positions.shape)\n    predicted_positions[:, 0] = rel_positions[:, 0]\n    predicted_positions[:, 1] = rel_positions[:, 1].cumsum() + waypoint_df.loc[0].waypoint_x\n    predicted_positions[:, 2] = rel_positions[:, 2].cumsum() + waypoint_df.loc[0].waypoint_y\n    \n    return predicted_positions\n\ndef calc_errors(trajectory_file, predicted_positions=None, only_mean_error = True): \n    if predicted_positions is None: \n        predicted_positions = predict_position(trajectory_file)\n       \n    # GT position \n    sample_data = read_data_file(str(trajectory_file))\n    \n    errors = [] \n    time_diff = []\n    start_time = sample_data.waypoint[0,0]\n    for predicted_position in predicted_positions: \n        timediff = 10e15\n        best_gt = -1\n        for idx, gt in enumerate(sample_data.waypoint): \n            if abs(predicted_position[0] - gt[0]) < timediff: \n                timediff = abs(predicted_position[0] - gt[0])\n                best_gt = gt\n        errors.append(np.linalg.norm(predicted_position[1:] - best_gt[1:]))\n        time_diff.append(predicted_position[0] - start_time)\n\n    if only_mean_error: \n        return np.mean(errors)\n    return errors, time_diff, np.mean(errors)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"predicted_positions = predict_position(sample_txt_file_path)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"calc_errors(sample_txt_file_path, predicted_positions)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"visualize_train_step_trajectory(Path(sample_txt_file_path), predicted_positions) ","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Some samples "},{"metadata":{"trusted":true},"cell_type":"code","source":"import random\nfor filename in random.sample(os.listdir('../input/indoor-location-navigation/train/5a0546857ecc773753327266/B1/'), 5): \n    sample_txt_file_path = os.path.join('../input/indoor-location-navigation/train/5a0546857ecc773753327266/B1/', filename)\n    predicted_positions = predict_position(sample_txt_file_path)\n    print(calc_errors(sample_txt_file_path, predicted_positions))\n    visualize_train_step_trajectory(Path(sample_txt_file_path), predicted_positions).show()\n    plt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"mean_errors = []\nbasedir = '../input/indoor-location-navigation/train/5a0546857ecc773753327266/B1/'\nfor filename in os.listdir(basedir):\n    sample_txt_file_path = os.path.join(basedir, filename)\n    predicted_positions = predict_position(sample_txt_file_path)\n    mean_errors.append(calc_errors(sample_txt_file_path, predicted_positions))\nnp.mean(mean_errors), np.min(mean_errors), np.max(mean_errors), np.std(mean_errors)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Calculate the error per floor and building "},{"metadata":{"trusted":true},"cell_type":"code","source":"import collections\ndef calc_errors_per_building(building_path): \n    floor_mean_errors = collections.OrderedDict()\n    for floor in os.listdir(building_path): \n        floor_mean_errors[floor] = []\n        basedir = os.path.join(building_path, floor)\n        for filename in os.listdir(basedir):\n            sample_txt_file_path = os.path.join(basedir, filename)\n            predicted_positions = predict_position(sample_txt_file_path)\n            floor_mean_errors[floor] .append(calc_errors(sample_txt_file_path, predicted_positions))\n    return floor_mean_errors","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"floor_mean_errors = calc_errors_per_building('../input/indoor-location-navigation/train/5a0546857ecc773753327266/')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"labels, data = floor_mean_errors.keys(), floor_mean_errors.values()\n\nplt.boxplot(data)\nplt.xticks(range(1, len(labels) + 1), labels)\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Calculate the errors for 10 sample buildings"},{"metadata":{"trusted":true},"cell_type":"code","source":"for building in random.sample(os.listdir('../input/indoor-location-navigation/train'), 10): \n    floor_mean_errors = calc_errors_per_building(os.path.join('../input/indoor-location-navigation/train', building))\n    labels, data = floor_mean_errors.keys(), floor_mean_errors.values()\n    plt.boxplot(data)\n    plt.xticks(range(1, len(labels) + 1), labels)\n    plt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"","execution_count":null,"outputs":[]}],"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat":4,"nbformat_minor":4}