{"cells":[{"metadata":{},"cell_type":"markdown","source":"## Purpose\n\nUsing the `sf` package in R, find all references to a specific BSSID (a single wifi point) for a specific floor in the training data , interpolate their location based on the waypoints, then plot the signal strength on the GeoJSON map."},{"metadata":{},"cell_type":"markdown","source":"## Setup\n\nFirst, load the required packages. Tidyverse (plus stringr and magrittr) for data manipulation, sf for maps, grid for plotting."},{"metadata":{"trusted":true},"cell_type":"code","source":"library(tidyverse) \nlibrary(stringr)\nlibrary(magrittr)\nlibrary(sf)\nlibrary(grid)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Define some helper functions. `fig` changes kaggle's default figure size. `interpolate_location` returns the x,y coordinates at a specific time given waypoint data. `clean_data` processes the text files."},{"metadata":{"_uuid":"051d70d956493feee0c6d64651c6a088724dca2a","_execution_state":"idle","trusted":true,"_kg_hide-output":true},"cell_type":"code","source":"fig <- function(width, heigth){\n     options(repr.plot.width = width, repr.plot.height = heigth)\n}\n\noptions(scipen = 999)\noptions(warn=-1)\n\ninterpolate_location <- function(times, waypoint){\n    interp_x <- approxfun(waypoint$time, waypoint$x)\n    interp_y <- approxfun(waypoint$time, waypoint$y)\n    return(tibble(time = times, x = interp_x(times), y = interp_y(times)))\n}\n\nclean_data <- function(place, floor_path){\n    raw <- tibble(raw = read_lines(paste0(\"../input/indoor-location-navigation/train/\",\n      place, \"/\", floor_path)))\n\n    metadata_rows <- str_detect(raw$raw, \"^#\")\n\n    data <- raw[!metadata_rows, ]\n    \n    wifi <- data %>% separate(raw, into = paste0(\"V\", 1:7), sep = \"\\\\s\") %>%\n      filter(V2 == \"TYPE_WIFI\") %>%\n      janitor::remove_empty(., which = \"cols\") %>%\n      set_colnames(c(\"time\", \"type\", \"ssid\", \"bssid\", \"RSSI\", \"frequency\", \"lasttime\")) %>%\n      mutate(across(-(type:bssid), as.numeric)) %>%\n      mutate(place = place, path = floor_path)\n    \n    waypoint <- data %>% separate(raw, into = c(\"time\", \"type\", \"x\", \"y\"), sep = \"\\\\s\") %>%\n      filter(type == \"TYPE_WAYPOINT\") %>%\n      mutate(across(-type, as.numeric))\n    \n    wifi_loc <- interpolate_location(pull(distinct(select(wifi, time))), waypoint)\n    \n    return(left_join(wifi, wifi_loc, by = \"time\"))\n}","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Get the paths to all files in this building's directory."},{"metadata":{"trusted":true},"cell_type":"code","source":"all_paths <- list.files(\"../input/indoor-location-navigation/train/5a0546857ecc773753327266/\", recursive = TRUE)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Making the Heatmap\n\nUsing `purrr::map`, we apply the clean data function to the first 109 paths (these are the ones that occur on B1)."},{"metadata":{"trusted":true},"cell_type":"code","source":"place <- \"5a0546857ecc773753327266\"\n\nall_wifi <- purrr::map(all_paths[1:109], ~clean_data(place = place, floor_path = .))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Using `purrr::reduce`, we get the collected data frame and filter for a specific BSSID. Plot a heatmap using `ggplot`. For the example, there's a clear point of origin, plus some additional hallways where the signal can still be detected. Let's see if that matches up with the given floorplan."},{"metadata":{"trusted":true},"cell_type":"code","source":"fig(12,12)\n\nwifi_one_bssid <- all_wifi %>% purrr::reduce(rbind) %>%\n  filter(bssid == \"c08ad78a45798cfe176a42b35c7381ae602711c5\") \n\nwifi_one_bssid %>%\n  ggplot(aes(x = x, y = y, z = RSSI)) + stat_summary_hex() + theme_minimal()\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Get the Locations of All Waypoints\n\nDefine a function that gets all waypoint data from the raw data.**"},{"metadata":{"trusted":true},"cell_type":"code","source":"get_paths <- function(place, floor_path){\n    raw <- tibble(raw = read_lines(paste0(\"../input/indoor-location-navigation/train/\",\n      place, \"/\", floor_path)))\n\n    metadata_rows <- str_detect(raw$raw, \"^#\")\n\n    data <- raw[!metadata_rows, ]\n    \n    return(data %>% separate(raw, into = c(\"time\", \"type\", \"x\", \"y\"), sep = \"\\\\s\") %>%\n      filter(type == \"TYPE_WAYPOINT\") %>%\n      mutate(across(-type, as.numeric))\n    )\n}","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Map the waypoint retrieval function to the same 109 paths."},{"metadata":{"trusted":true},"cell_type":"code","source":"all_waypoints <- purrr::map(all_paths[1:109], ~get_paths(place = place, floor_path = .))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Plot Against Known Floorplan\n\nFinally, apply some scaling functions to get the measurements in the same reference system and plot the map, all waypoints, and the wifi heatmap!"},{"metadata":{"trusted":true},"cell_type":"code","source":"fig(12, 12)\n\nlibrary(sf)\n\nmap <- st_read(paste0(\"../input/indoor-location-navigation/metadata/\",\n                          place, \"/B1/geojson_map.json\")) \n\npath_for_map <- purrr::reduce(all_waypoints, rbind)\n\nmax_scale <- st_as_sf(map) %>%\n    filter(!is.na(max_scale))\n\nmap_sf <- st_intersection(map, max_scale) %>% st_as_sf()\n\nms_width <- st_bbox(max_scale)$xmax - st_bbox(max_scale)$xmin\nms_height <- st_bbox(max_scale)$ymax - st_bbox(max_scale)$ymin\n\nmap_info <- rjson::fromJSON(\n    file = paste0(\"../input/indoor-location-navigation/metadata/\",\n                        place, \"/B1/floor_info.json\")\n)\n\npath_for_map$x <- path_for_map$x * (ms_width/map_info$map_info$width) + st_bbox(max_scale)$xmin\npath_for_map$y <- path_for_map$y * (ms_height/map_info$map_info$height) + st_bbox(max_scale)$ymin\n\naligned_wifi <- wifi_one_bssid\n\naligned_wifi$x <- aligned_wifi$x * (ms_width/map_info$map_info$width) + st_bbox(max_scale)$xmin\naligned_wifi$y <- aligned_wifi$y * (ms_height/map_info$map_info$height) + st_bbox(max_scale)$ymin\n\npoints <- st_sfc(st_multipoint(as.matrix(path_for_map[,3:4]))) %>%\n    st_set_crs(st_crs(map))\n\nggplot() + geom_sf(data = map_sf) +\n    geom_sf(data = points, color = \"black\") +\n    stat_summary_hex(data = aligned_wifi, alpha = 0.95, aes(x = x, y = y, z = RSSI))","execution_count":null,"outputs":[]}],"metadata":{"kernelspec":{"name":"ir","display_name":"R","language":"R"},"language_info":{"name":"R","codemirror_mode":"r","pygments_lexer":"r","mimetype":"text/x-r-source","file_extension":".r","version":"3.6.3"}},"nbformat":4,"nbformat_minor":4}