{"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_minor":4,"nbformat":4,"cells":[{"cell_type":"raw","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\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))\n\n# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19"}},{"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","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"imu1 = pd.read_csv('../input/cleaned-imu/imu_train_0_8.csv')\nimu1","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"acce = imu1.loc[imu1['Type'] == 'TYPE_ACCELEROMETER']\ngyro = imu1.loc[imu1['Type'] == 'TYPE_GYROSCOPE']\nmagn = imu1.loc[imu1['Type'] == 'TYPE_MAGNETIC_FIELD']\nrota = imu1.loc[imu1['Type'] == 'TYPE_ROTATION_VECTOR']","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"magn","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"acce","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Post-processing based on waypoints","metadata":{}},{"cell_type":"code","source":"train_waypoints = pd.read_csv('../input/waypoint/waypoint_train (1).csv')\n# sub = sub_process(pd.read_csv('../input/indoor-location-train-waypoints/6.578LB_submission.csv'),\n#                  train_waypoints)\n","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_waypoints\nwaypoint_df = train_waypoints[['Time','x','y']]\nwaypoint_df","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"!git clone https://github.com/location-competition/indoor-location-competition-20.git api\n\nfrom api.compute_f import *\nfrom api.io_f import * \nfrom api.visualize_f import * \n\nimport json\nfrom pathlib import Path","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"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    )","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"sample_txt_file_path = Path('../input/indoor-location-navigation/train/5a0546857ecc773753327266/B1/5e15730aa280850006f3d005.txt')\nsample_txt_file_path","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Predicts the position given the IMU and the starting point**","metadata":{}},{"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)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"predicted_positions = predict_position(sample_txt_file_path)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"calc_errors(sample_txt_file_path, predicted_positions)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"visualize_train_step_trajectory(Path(sample_txt_file_path), predicted_positions)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"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()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"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)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Calculate the error per floor and building**","metadata":{}},{"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","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"floor_mean_errors = calc_errors_per_building('../input/indoor-location-navigation/train/5a0546857ecc773753327266/')","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"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()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Calculate the errors for 10 sample buildings**","metadata":{}},{"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()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"\n# **waypoint path with gayro sensor & magnetic sensor**","metadata":{}},{"cell_type":"code","source":"import json\nimport re\nimport gc\nimport pickle\nimport itertools\nimport pandas as pd\nimport numpy as np\nfrom glob import glob\nfrom datetime import datetime as dt\nfrom pathlib import Path\nfrom tqdm import tqdm\nimport datetime\nts_conv = np.vectorize(datetime.datetime.fromtimestamp) # ut(10 digit) -> date\n\n# pandas settings -----------------------------------------\npd.set_option(\"display.max_colwidth\", 100)\npd.set_option(\"display.max_rows\", None)\npd.set_option(\"display.max_columns\", None)\npd.options.display.float_format = '{:,.5f}'.format\n\n# Graph drawing -------------------------------------------\nimport matplotlib\nfrom matplotlib import font_manager\nimport matplotlib.pyplot as plt\nimport matplotlib.cm as cm\nfrom matplotlib import rc\nfrom matplotlib_venn import venn2, venn2_circles\nfrom matplotlib import animation as ani\nfrom IPython.display import Image\nfrom pylab import imread\nfrom IPython.display import HTML\n\nplt.rcParams[\"patch.force_edgecolor\"] = True\nfrom IPython.display import display # Allows the use of display() for DataFrames\nimport seaborn as sns\nsns.set(style=\"whitegrid\", palette=\"muted\", color_codes=True)\nsns.set_style(\"whitegrid\", {'grid.linestyle': '--'})\nred = sns.xkcd_rgb[\"light red\"]\ngreen = sns.xkcd_rgb[\"medium green\"]\nblue = sns.xkcd_rgb[\"denim blue\"]\n\n%matplotlib inline\n%config InlineBackend.figure_format='retina'\n\n# ML -------------------------------------------\nfrom sklearn.preprocessing import LabelEncoder\n\ndef unpickle(filename):\n    with open(filename, 'rb') as fo:\n        p = pickle.load(fo)\n    return p\n\ndef to_pickle(filename, obj):\n    with open(filename, 'wb') as f:\n        pickle.dump(obj, f, -1)\n\n\n\nclass FeatureStore():\n    \n    # necessayr to re-check\n    floor_convert = {'1F' :  0, '2F' : 1, '3F' : 2, '4F' : 3, '5F' : 4, \n                     '6F' : 5, '7F' : 6, '8F' : 7, '9F' : 8,\n                     'B'  : -1, 'B1' : -1, 'B2' : -2, 'B3' : -3, \n                     'BF' : -1, 'BM' : -1, \n                     'F1' : 0, 'F2' : 1, 'F3' : 2, 'F4' : 3, 'F5' : 4, \n                     'F6' : 5, 'F7' : 6, 'F8' : 7, 'F9' : 8, 'F10': 9,\n                     'L1' : 0, 'L2' : 1, 'L3' : 2, 'L4' : 3, 'L5' : 4, \n                     'L6' : 5, 'L7' : 6, 'L8' : 7, 'L9' : 8, 'L10': 9, \n                     'L11': 10,\n                     'G'  : 0, 'LG1': 0, 'LG2': 1, 'LM' : 0, 'M'  : 0, \n                     'P1' : 0, 'P2' : 1,}\n    \n    df_types = ['accelerometer',\n                'gyroscope',\n                'magnetic_field',\n                'rotation_vector',\n                'waypoint']\n    \n    # https://github.com/location-competition/indoor-location-competition-20\n    df_type_cols = {'accelerometer': [\"timestamp\", \"x\", \"y\", \"z\", \"accuracy\"],\n                'gyroscope': [\"timestamp\", \"x\", \"y\", \"z\", \"accuracy\"],\n                'magnetic_field': [\"timestamp\", \"x\", \"y\", \"z\", \"accuracy\"],\n                'rotation_vector': [\"timestamp\", \"x\", \"y\", \"z\", \"accuracy\"],\n                'waypoint': [\"timestamp\", \"x\", \"y\"]}\n\n    dtype_dict = {}\n    dtype_dict[\"accelerometer\"] = {\"timestamp\":int, \"x\":float, \"y\":float, \"z\":float, \n                                   \"accuracy\":int}\n    dtype_dict[\"gyroscope\"] = {\"timestamp\":int, \"x\":float, \"y\":float, \"z\":float, \n                               \"accuracy\":int}\n    dtype_dict[\"magnetic_field\"] = {\"timestamp\":int, \"x\":float, \"y\":float, \n                                    \"z\":float, \"accuracy\":int}\n    dtype_dict[\"rotation_vector\"] = {\"timestamp\":int, \"x\":float, \"y\":float, \n                                     \"z\":float, \"accuracy\":int}\n    dtype_dict[\"waypoint\"] = {\"timestamp\":int, \"x\":float, \"y\":float, \"z\":float}\n\n\n    def __init__(self, site_id, floor, path_id, \n                 input_path=\"../input/indoor-location-navigation/\",\n                 save_path=\"../mid\"):\n        self.site_id = site_id.strip()\n        self.floor = floor.strip()\n        self.n_floor = self.floor_convert[self.floor]\n        self.path_id = path_id.strip()\n        \n        self.input_path = input_path\n        assert Path(input_path).exists(), f\"input_path do not exist: {input_path}\"\n        \n        self.save_path = save_path\n        Path(save_path).mkdir(parents=True, exist_ok=True)\n        \n        self.site_info = SiteInfo(site_id=self.site_id, floor=self.floor, input_path=self.input_path)\n        \n    def _flatten(self, l):\n        return list(itertools.chain.from_iterable(l))\n    \n    def multi_line_spliter(self, s):\n        matches = re.finditer(\"TYPE_\", s)\n        matches_positions = [match.start() for match in matches]\n        split_idx = [0] + [matches_positions[i]-14 for i in range(1, len(matches_positions))] + [len(s)]\n        return [s[split_idx[i]:split_idx[i+1]] for i in range(len(split_idx)-1)]\n    \n    def load_df(self, ):\n        path = str(Path(self.input_path)/f\"train/{self.site_id}/{self.floor}/{self.path_id}.txt\")\n        with open(path) as f:\n            data = f.readlines()\n        \n        modified_data = []\n        for s in data:\n            if s.count(\"TYPE_\")>1:\n                lines = self.multi_line_spliter(s)\n                modified_data.extend(lines)\n            else:\n                modified_data.append(s)\n        del data\n        self.meta_info_len = len([d for d in modified_data if d[0]==\"#\"])\n        self.meta_info_df = pd.DataFrame([m.replace(\"\\n\", \"\").split(\":\") \n                                          for m in self._flatten([d.split(\"\\t\") \n                                                                  for d in modified_data if d[0]==\"#\"]) if m!=\"#\"])\n\n        data_df = pd.DataFrame([d.replace(\"\\n\", \"\").split(\"\\t\") for d in modified_data if d[0]!=\"#\"])\n        for dt in self.df_types:\n            # select data type\n            df_s = data_df[data_df[1]==f\"TYPE_{dt.upper()}\"]\n            if len(df_s)==0:\n                setattr(self, dt, pd.DataFrame(columns=self.df_type_cols[dt]))\n            else:\n                # remove empty cols\n                na_info = df_s.isna().sum(axis=0) == len(df_s)\n                df_s = df_s[[i for i in na_info[na_info==False].index if i!=1]].reset_index(drop=True)\n                \n                if len(df_s.columns)!=len(self.df_type_cols[dt]):\n                    df_s.columns = self.df_type_cols[dt][:len(df_s.columns)]\n                else:\n                    df_s.columns = self.df_type_cols[dt]\n            \n                # set dtype          \n                for c in df_s.columns:\n                    df_s[c] = df_s[c].astype(self.dtype_dict[dt][c])\n                                     \n                # set DataFrame to attr\n                setattr(self, dt, df_s)\n    \n    def get_site_info(self, keep_raw=False):\n        self.site_info.get_site_info(keep_raw=keep_raw)\n            \n    def load_all_data(self, keep_raw=False):     \n        self.load_df()\n        self.get_site_info(keep_raw=keep_raw)\n        \n    def __getitem__(self, item):\n        if item in self.df_types:\n            return getattr(self, item)\n        elif item==\"sensors\":\n            try:\n                return getattr(self, \"sensor_df\")\n            except:\n                self.sensor_df = pd.concat([feature[\"magnetic_field\"].set_index(\"timestamp\"), \n                                       feature[\"accelerometer\"].set_index(\"timestamp\"), \n                                       feature[\"gyroscope\"].set_index(\"timestamp\")], axis=1)\n                if self.sensor_df.shape[1]==12:\n                    self.sensor_df.columns = [\"mag_x\", \"mag_y\", \"mag_z\", \"mag_acc\", \n                                              \"acc_x\", \"acc_y\", \"acc_z\", \"acc_acc\",\n                                              \"gyr_x\", \"gyr_y\", \"gyr_z\", \"gyr_acc\", ]\n                else:\n                    self.sensor_df.columns = [\"mag_x\", \"mag_y\", \"mag_z\", \n                                              \"acc_x\", \"acc_y\", \"acc_z\", \n                                              \"gyr_x\", \"gyr_y\", \"gyr_z\",  ]\n                return self.sensor_df\n        else:\n            return None\n    \n    def save(self, ):\n        # to be implemented\n        pass\n    \n    \nclass SiteInfo():\n    def __init__(self, site_id, floor, input_path=\"../input/indoor-location-navigation/\"):\n        self.site_id = site_id\n        self.floor = floor\n        self.input_path = input_path\n        assert Path(input_path).exists(), f\"input_path do not exist: {input_path}\"\n        \n    def get_site_info(self, keep_raw=False):\n        floor_info_path = f\"{self.input_path}/metadata/{self.site_id}/{self.floor}/floor_info.json\"\n        with open(floor_info_path, \"r\") as f:\n            self.floor_info = json.loads(f.read())\n            self.site_height = self.floor_info[\"map_info\"][\"height\"]\n            self.site_width = self.floor_info[\"map_info\"][\"width\"]\n            if not keep_raw:\n                del self.floor_info\n            \n        geojson_map_path = f\"{self.input_path}/metadata/{self.site_id}/{self.floor}/geojson_map.json\"\n        with open(geojson_map_path, \"r\") as f:\n            self.geojson_map = json.loads(f.read())\n            self.map_type = self.geojson_map[\"type\"]\n            self.features = self.geojson_map[\"features\"]\n            \n            self.floor_coordinates = self.features[0][\"geometry\"][\"coordinates\"]\n            self.store_coordinates = [self.features[i][\"geometry\"][\"coordinates\"] \n                                          for i in range(1, len(self.features))]\n                \n            if not keep_raw:\n                del self.geojson_map\n    \n    def show_site_image(self, ax):\n        path = f\"{self.input_path}/metadata/{self.site_id}/{self.floor}/floor_image.png\"\n        ax.imshow(imread(path), extent=[0, self.site_width, 0, self.site_height])\n\n    def draw_polygon(self, size=8, only_floor=False):\n\n        fig = plt.figure()\n        ax = plt.subplot(111)\n            \n        xmax, xmin, ymax, ymin = self._draw(self.floor_coordinates, ax, calc_minmax=True)\n        if not only_floor:\n            self._draw(self.store_coordinates, ax, fill=True)\n        plt.legend([])\n        \n        xrange = xmax - xmin\n        yrange = ymax - ymin\n        ratio = yrange / xrange\n        \n        self.x_size = size\n        self.y_size = size*ratio\n\n        fig.set_figwidth(size)\n        fig.set_figheight(size*ratio)\n        # plt.show()\n        return ax\n        \n    def _draw(self, coordinates, ax, fill=False, calc_minmax=False):\n        xmax, ymax = -np.inf, -np.inf\n        xmin, ymin = np.inf, np.inf\n        for i in range(len(coordinates)):\n            ndim = np.ndim(coordinates[i])\n            if ndim==2:\n                corrd_df = pd.DataFrame(coordinates[i])\n                if fill:\n                    ax.fill(corrd_df[0], corrd_df[1], alpha=0.7)\n                else:\n                    corrd_df.plot.line(x=0, y=1, style=\"-\", ax=ax)\n                        \n                if calc_minmax:\n                    xmax = max(xmax, corrd_df[0].max())\n                    xmin = min(xmin, corrd_df[0].min())\n\n                    ymax = max(ymax, corrd_df[1].max())\n                    ymin = min(ymin, corrd_df[1].min())\n            elif ndim==3:\n                for j in range(len(coordinates[i])):\n                    corrd_df = pd.DataFrame(coordinates[i][j])\n                    if fill:\n                        ax.fill(corrd_df[0], corrd_df[1], alpha=0.6)\n                    else:\n                        corrd_df.plot.line(x=0, y=1, style=\"-\", ax=ax)\n                        \n                    if calc_minmax:\n                        xmax = max(xmax, corrd_df[0].max())\n                        xmin = min(xmin, corrd_df[0].min())\n\n                        ymax = max(ymax, corrd_df[1].max())\n                        ymin = min(ymin, corrd_df[1].min())\n            else:\n                assert False, f\"ndim of coordinates should be 2 or 3: {ndim}\"\n        if calc_minmax:\n            return xmax, xmin, ymax, ymin\n        else:\n            return None\n        ","metadata":{"_kg_hide-input":true,"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from matplotlib import animation as ani\nimport functools\nfrom IPython.display import Image\nplt.rcParams[\"font.size\"] = 7\n\ndef animate(axes, nframe):\n    global num_frame\n    \n    ts = feature.waypoint.timestamp.values[nframe]\n    \n    # axes[0]------------------------------\n    wp =feature.waypoint.iloc[nframe]\n    axes[0].text(x=wp.x, y=wp.y, s=f\"{nframe}\", fontsize=6)\n    \n    # axes[1]------------------------------\n    axes[1].vlines(ts, -3.5, 3.5, \"k\", lw=1)\n    axes[1].text(ts, -1, f\"{nframe}\", fontsize=8)\n    \n    # axes[2]------------------------------\n    axes[2].vlines(ts, -40, 40, \"k\", lw=1)\n    axes[2].text(ts, -20, f\"{nframe}\", fontsize=8)\n\n\ndef drow_path_gyr_mag(feature, gif_name):    \n    ts_min = feature.waypoint.timestamp.min()\n    ts_max = feature.waypoint.timestamp.max()\n    query = f\"{ts_min} <= timestamp and timestamp <= {ts_max}\"\n    sensor_df = feature[\"sensors\"].reset_index().query(query)\n    sensor_df[\"ts_diff\"] = sensor_df.timestamp.diff() / 1000\n    sensor_df[\"x_pos\"] = 0\n    sensor_df[\"y_pos\"] = 0\n\n    sensor_df[\"gyr_z_dif_ts\"] = sensor_df[\"gyr_z\"] * sensor_df[\"ts_diff\"].fillna(0.02)\n    fig, axes = plt.subplots(3, 1,\n        gridspec_kw=dict(width_ratios=[1], \n                         height_ratios=[4,1,1], \n                         wspace=0.1, \n                         hspace=0.3),)\n    # axes[0]------------------------------\n    feature.site_info.show_site_image(ax=axes[0])\n    axes[0].grid(False)\n    axes[0].tick_params(labelsize=7)\n    # axes[1]------------------------------\n    gyro_data = sensor_df.set_index(\"timestamp\").gyr_z_dif_ts.cumsum()\n    gyro_data.plot(lw=1, label=\"gyr_z_dif_ts\", ax=axes[1])\n    axes[1].hlines(np.pi, ts_min, ts_max, \"gray\", linestyle=\"--\", lw=0.5)\n    axes[1].hlines(-np.pi, ts_min, ts_max, \"gray\", linestyle=\"--\", lw=0.5)\n    axes[1].legend(loc=\"best\", fontsize=8)\n    axes[1].set_title(\"gyr_z\", fontsize=8)\n    axes[1].set_xlabel('timestamp', fontsize=8)\n    axes[1].tick_params(labelsize=7)\n    # axes[2]------------------------------\n    sensor_df.set_index(\"timestamp\").mag_x.plot(lw=1, label=\"mag_x\", ax=axes[2])\n    sensor_df.set_index(\"timestamp\").mag_y.plot(lw=1, label=\"mag_y\", ax=axes[2])\n    axes[2].legend(loc=\"best\", fontsize=8)\n    axes[2].set_title(\"magnetic_field_x, y\", fontsize=8)\n    axes[2].set_xlabel('timestamp', fontsize=8)\n    axes[2].tick_params(labelsize=7)\n    \n    animate_ = functools.partial(animate, axes)\n    anim = ani.FuncAnimation(fig, animate_, frames=len(feature.waypoint))\n\n    fig.set_figwidth(8)\n    fig.set_figheight(12)\n    anim.save(gif_name, fps=2, dpi=256)\n    plt.close()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"feature = FeatureStore(site_id='5da138764db8ce0c98bcaa46', floor='F1', path_id=\"5daae24718410e00067e6d44\")\nfeature.load_all_data()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"drow_path_gyr_mag(feature, 'path_anim01.gif')","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"Image('path_anim01.gif')","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"feature = FeatureStore(site_id='5cd56b5ae2acfd2d33b5854a', floor='F1', path_id=\"5d076ecf0e86b60008036124\")\nfeature.load_all_data()\ndrow_path_gyr_mag(feature, 'path_anim03.gif')","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"Image('path_anim03.gif')","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}