{"cells":[{"metadata":{},"cell_type":"markdown","source":"# Preparing for map matching\n\n* Map matching is the problem of how to match predicted geographic coordinates to a model of the real map (See a figure below from wikipedia).\n* To match your predicted coordinates to real map, we have to make a model from real map information.\n* In this notebook, I present an example to create real map model.\n\n![map matching](https://upload.wikimedia.org/wikipedia/commons/thumb/8/8c/Map_Matching_Example_with_GraphHopper.png/483px-Map_Matching_Example_with_GraphHopper.png)\n\n\n## References and Acknowledgements\n\n* Papers\n * https://www.mdpi.com/1424-8220/17/6/1272\n* Notebooks\n * https://www.kaggle.com/ihelon/indoor-location-exploratory-data-analysis\n * https://www.kaggle.com/wineplanetary/can-your-predicted-positions-really-stand\n* Datasets\n * https://www.kaggle.com/hiro5299834/indoor-navigation-and-location-wifi-features\n* Discussions\n * https://www.kaggle.com/c/indoor-location-navigation/discussion/217874"},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"import os\nimport cv2\nimport math\nimport json\nimport glob\nimport numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"base_dir = \"../input/indoor-location-navigation/metadata\"\nfloor_map = {\"B2\":-2, \"B1\":-1, \"F1\":0, \"F2\":1, \"F3\":2, \"F4\":3, \"F5\":4, \"F6\":5, \"F7\":6, \"F8\":7, \"F9\":8,\n             \"1F\":0, \"2F\":1, \"3F\":2, \"4F\":3, \"5F\":4, \"6F\":5, \"7F\":6, \"8F\":7, \"9F\":8}\n\ndef floor2strs(floor):\n    return [key for key, val in floor_map.items() if val == floor]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# sample\nsite = \"5d27097f03f801723c320d97\"","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## First, let's see real floor maps!"},{"metadata":{"trusted":true},"cell_type":"code","source":"%matplotlib inline\nfloor_image_list = sorted(glob.glob(os.path.join(base_dir, site, \"*\", \"*.png\")))\nw = math.ceil(len(floor_image_list) / 2)\nh = math.ceil(len(floor_image_list) / w)\nfig = plt.figure(figsize=(16, 10))\nfor i, floor_image in enumerate(floor_image_list):\n    floor = os.path.basename(os.path.dirname(floor_image))\n    plt.subplot(h, w, i + 1)\n    img = cv2.imread(floor_image, cv2.IMREAD_UNCHANGED) # need alpha\n    plt.imshow(img)\n    plt.axis(\"off\")\n    plt.title(floor)\nplt.tight_layout()\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Check distribution of position in train data"},{"metadata":{},"cell_type":"markdown","source":"* To correspond coordinates in train data to real maps, we have to convert coordinates into pixels"},{"metadata":{"trusted":true},"cell_type":"code","source":"def coord2pix(x, y, img_width, img_height, train_floor_info):\n    pixx = x * img_width / train_floor_info[\"map_info\"][\"width\"]\n    pixy = img_height - y * img_height / train_floor_info[\"map_info\"][\"height\"]\n    return pixx, pixy","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"floor_image_list = sorted(glob.glob(os.path.join(base_dir, site, \"*\", \"*.png\")))\nw = math.ceil(len(floor_image_list) / 2)\nh = math.ceil(len(floor_image_list) / w)\n\ntrain_csv = \"../input/indoor-navigation-and-location-wifi-features/%s_train.csv\" % (site)\ntrain_df = pd.read_csv(train_csv)\n\nfig = plt.figure(figsize=(20, 15))\nfor i, floor_image in enumerate(floor_image_list):\n    floor = os.path.basename(os.path.dirname(floor_image))\n    plt.subplot(h, w, i + 1)\n    train_df_ext = train_df[train_df[\"f\"] == floor_map[floor]]\n    \n    json_path = \"../input/indoor-location-navigation/metadata/%s/%s/floor_info.json\" % (site, floor)\n    with open(json_path, \"r\") as f:\n        train_floor_info = json.load(f)\n    \n    img = cv2.imread(floor_image, cv2.IMREAD_UNCHANGED) # need alpha\n    img_height, img_width, _ = img.shape\n    pixx, pixy = coord2pix(train_df_ext[\"x\"].values, train_df_ext[\"y\"].values, img_width, img_height, train_floor_info)\n    plt.imshow(img)\n    plt.scatter(pixx, pixy, marker=\"o\", color=\"blue\", label=\"train\")\n    plt.axis(\"off\")\n    plt.title(floor)\nplt.tight_layout()\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Discussion\n\n* Blue positions are waypoins recorded in train data.\n* It seems that we have to predict most of positions from white areas.\n* To predict positions from white areas, we have to extract white areas from map information."},{"metadata":{},"cell_type":"markdown","source":"## Extract white area from map information"},{"metadata":{"trusted":true},"cell_type":"code","source":"def extract_permitted_area_from_map(site, floor):\n    floor_image_path = os.path.join(base_dir, site, floor, \"floor_image.png\")\n    img = cv2.imread(floor_image_path, cv2.IMREAD_UNCHANGED)\n    height, width, channel = img.shape\n    _, thimg_soft = cv2.threshold(img[:,:,3], 1, 1, cv2.THRESH_BINARY)\n    _, thimg_hard = cv2.threshold(img[:,:,3], 254, 1, cv2.THRESH_BINARY_INV)\n    thimg_soft[0, :] = 0\n    thimg_soft[height - 1, :] = 0\n    thimg_soft[:, 0] = 0\n    thimg_soft[:, width - 1] = 0\n    mask_img = np.zeros_like(thimg_soft).astype(np.uint8)\n    contours, hierarchy = cv2.findContours(thimg_soft, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)\n    contours = [contour for contour in contours if cv2.contourArea(contour) > 1000]\n    cv2.fillPoly(mask_img, contours, 1)\n    permitted_img = np.minimum(mask_img, thimg_hard)\n    return img, cv2.blur(permitted_img, (5, 5))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"* The second returned value of this function is a 2-bit map of permitted area which is shown as red regions in figure below.\n* Let's plot and check !\n* If you map your predicted coordinate to red areas, you have to convert your coordinates into pixels."},{"metadata":{"trusted":true},"cell_type":"code","source":"floor_image_list = sorted(glob.glob(os.path.join(base_dir, site, \"*\", \"*.png\")))\nw = math.ceil(len(floor_image_list) / 2)\nh = math.ceil(len(floor_image_list) / w)\nfloor_list = [os.path.basename(os.path.dirname(floor_image)) for floor_image in floor_image_list]\n\nfig = plt.figure(figsize=(20, 15))\nfor i, floor in enumerate(floor_list):\n    plt.subplot(h, w, i + 1)   \n    floor_image, permitted_mask = extract_permitted_area_from_map(site, floor)\n    permitted_area = np.zeros_like(floor_image)\n    permitted_area[:,:,0][permitted_mask == 1] = 255\n    permitted_area[:,:,3][permitted_mask == 1] = 255\n    plt.imshow(floor_image)\n    plt.imshow(permitted_area)\n    plt.axis(\"off\")\n    plt.title(floor)\nplt.tight_layout()\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Thank you for reading :)"}],"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}