Simple navigation in R
By: Oscar Rojo
Tags: Map, Navigation, Osmar, Donostia, R

Oscar Rojo

This is a simple demonstration of finding the shortest path between two nodes on a map

Abstract OpenStreetMap provides freely accessible and editable geographic data. The osmar package smoothly integrates the OpenStreetMap project into the R ecosystem. The osmar package provides infrastructure to access OpenStreetMap data from different sources, to enable working with the OSM data in the familiar R idiom, and to convert the data into objects based on classes provided by existing R packages. This paper explains the package’s concept and shows how to use it. As an application we present a simple navigation device. Introduction

Photo by Waldemar Brandt on Unsplash

“OpenStreetMap creates and provides free geographic data such as street maps to anyone who wants them” announces the OpenStreetMap wiki main page (OSM Foundation, 2011) — and we think R users want free geographic data. Therefore, the add-on package osmar (Schlesinger and Eugster, 2012) provides extensible infrastructure for integrating the OpenStreetMap project (OSM) into the R project. The aim of the OpenStreetMap project is to create a free editable map of the world. The project maintains a database of geographic elements (nodes, ways and relations) and features (such as streets, buildings and landmarks). These data are collected and provided by volunteers using GPS devices, aerial imagery, and local knowledge. The most prominent application is the rendering of the geographic data and features into raster images (for example, for the OSM map on the website). However, the project also provides an application programming interface (API) for fetching raw data from and saving to the OSM database.

Prepare to work

## First, we clean the working memory



rm(list = ls())





## Second, set working directory



setwd("~/Documentos/Medium/crosstalk_R/")



getwd()



## Clean console



cat("\014")



## Clean enviroment



rm()

‘/home/oscar/Documentos/Medium/crosstalk_R’

Let’s load packages osmar, leaflet and igraph

packages <- c("osmar","leaflet","igraph","sp", "purrr", "dplyr", "stringr")

newpack = packages[!(packages %in% installed.packages()[,"Package"])]



if(length(newpack)) install.packages(newpack)

a=lapply(packages, library, character.only=TRUE) Attaching package: ‘purrr’





The following objects are masked from ‘package:igraph’:



compose, simplify







Attaching package: ‘dplyr’





The following objects are masked from ‘package:igraph’:



as_data_frame, groups, union





The following objects are masked from ‘package:stats’:



filter, lag





The following objects are masked from ‘package:base’:



intersect, setdiff, setequal, union

Then, using osmar, let’s load some OSM data for Donostia, Spain

src <- osmsource_api(url = "https://api.openstreetmap.org/api/0.6/")

bb <- center_bbox( -1.994124, 43.307996, 2000, 2000)

donosti_data <- get_osm(bb, source = src) str(donosti_data) List of 3

$ nodes :List of 2

..$ attrs:'data.frame': 21907 obs. of 9 variables:

.. ..$ id : num [1:21907] 939335 27551553 27552455 55235466 55235523 ...

.. ..$ visible : chr [1:21907] "true" "true" "true" "true" ...

.. ..$ timestamp: POSIXlt[1:21907], format: "2012-02-23 15:39:06" "2012-07-25 22:40:49" ...

.. ..$ version : num [1:21907] 2 4 6 3 4 1 2 1 1 1 ...

.. ..$ changeset: num [1:21907] 10768898 12493077 18555773 10783746 11492643 ...

.. ..$ user : Factor w/ 145 levels "\"Yo\"","a903541",..: 103 40 40 103 90 55 93 82 82 82 ...

.. ..$ uid : Factor w/ 145 levels "1008854","101355",..: 68 49 49 68 116 132 75 34 34 34 ...

.. ..$ lat : num [1:21907] 43.3 43.3 43.3 43.3 43.3 ...

.. ..$ lon : num [1:21907] -2 -2 -1.98 -2.01 -2 ...

..$ tags :'data.frame': 17112 obs. of 3 variables:

.. ..$ id: num [1:17112] 5.52e+09 5.52e+09 2.44e+08 2.58e+08 2.59e+08 ...

.. ..$ k : Factor w/ 155 levels "access","addr:city",..: 66 77 40 40 40 40 40 40 40 40 ...

.. ..$ v : Factor w/ 5327 levels "“Riding your load” Hand made Cargo bikes with love. For every frame I wish to build, I spend the time and effor"| __truncated__,..: 4897 5009 4782 4782 4782 4782 4782 4782 4782 4782 ...

..- attr(*, "class")= chr [1:3] "nodes" "osmar_element" "list"

$ ways :List of 3

..$ attrs:'data.frame': 2863 obs. of 7 variables:

.. ..$ id : num [1:2863] 7.81e+08 3.63e+08 1.83e+08 2.39e+07 2.39e+07 ...

.. ..$ visible : chr [1:2863] "true" "true" "true" "true" ...

.. ..$ timestamp: POSIXlt[1:2863], format: "2020-03-14 23:57:46" "2019-05-27 15:39:50" ...

.. ..$ version : num [1:2863] 1 4 7 10 5 4 17 3 2 28 ...

.. ..$ changeset: num [1:2863] 82206404 70666118 70666118 65708906 86740286 ...

.. ..$ user : Factor w/ 94 levels "\"Yo\"","1549Gdr5166",..: 76 62 62 46 62 62 62 81 61 62 ...

.. ..$ uid : Factor w/ 94 levels "1008854","10359760",..: 5 11 11 1 11 11 11 13 39 11 ...

..$ tags :'data.frame': 6975 obs. of 3 variables:

.. ..$ id: num [1:6975] 7.81e+08 7.81e+08 3.63e+08 3.63e+08 3.63e+08 ...

.. ..$ k : Factor w/ 113 levels "access","addr:city",..: 45 73 45 54 62 65 67 73 90 38 ...

.. ..$ v : Factor w/ 994 levels "-1","-2","+34943214287",..: 717 986 813 34 72 884 147 986 431 721 ...

..$ refs :'data.frame': 24814 obs. of 2 variables:

.. ..$ id : num [1:24814] 7.81e+08 7.81e+08 7.81e+08 7.81e+08 7.81e+08 ...

.. ..$ ref: num [1:24814] 2.47e+09 9.39e+05 2.47e+09 2.47e+09 2.47e+09 ...

..- attr(*, "class")= chr [1:3] "ways" "osmar_element" "list"

$ relations:List of 3

..$ attrs:'data.frame': 118 obs. of 7 variables:

.. ..$ id : num [1:118] 3518287 3524741 8406337 3524748 3524742 ...

.. ..$ visible : chr [1:118] "true" "true" "true" "true" ...

.. ..$ timestamp: POSIXlt[1:118], format: "2014-02-20 22:36:49" "2014-02-20 13:43:44" ...

.. ..$ version : num [1:118] 2 1 1 1 2 2 2 6 2 2 ...

.. ..$ changeset: num [1:118] 20684701 20675428 60126425 20675428 21907554 ...

.. ..$ user : Factor w/ 31 levels "A67-A67","Ales-Euskara Zerbitzua",..: 5 11 27 11 26 26 26 27 19 19 ...

.. ..$ uid : Factor w/ 31 levels "10689320","10887461",..: 13 9 21 9 8 8 8 21 6 6 ...

..$ tags :'data.frame': 1265 obs. of 3 variables:

.. ..$ id: num [1:1265] 3518287 3518287 3524741 3524741 8406337 ...

.. ..$ k : Factor w/ 395 levels "addr:city","addr:country",..: 375 389 375 389 375 389 375 389 375 389 ...

.. ..$ v : Factor w/ 681 levels "-2149671","#0063A9",..: 349 441 349 441 357 441 349 441 351 441 ...

..$ refs :'data.frame': 20218 obs. of 4 variables:

.. ..$ id : num [1:20218] 3518287 3518287 3518287 3524741 3524741 ...

.. ..$ type: Factor w/ 3 levels "node","relation",..: 3 1 3 3 3 1 3 3 1 3 ...

.. ..$ ref : num [1:20218] 2.63e+08 2.58e+08 2.40e+07 2.39e+07 2.40e+07 ...

.. ..$ role: Factor w/ 15 levels "","admin_centre",..: 4 15 14 4 14 15 4 14 15 4 ...

..- attr(*, "class")= chr [1:3] "relations" "osmar_element" "list"

- attr(*, "class")= chr [1:2] "osmar" "list"

Let´s get dataframe of each list inside tibble

#library(purrr)

#library(dplyr)

# library(stringr)

# Convert

imap(donosti_data, ~ set_names(tibble(.x), .y)) %>%

set_names(str_c("donosti_data", 1:3)) %>%

list2env(.GlobalEnv) <environment: R_GlobalEnv>

We can split in those dataframe:

donosti_nodes_attrs

donosti_nodes_tags



donosti_ways_attrs

donosti_ways_tags

donosti_ways_refs



donosti_relations_attrs

donosti_relations_tags

donosti_relations_refs head(donosti_nodes_attrs<- donosti_data1[[1]][[1]],10)

head(donosti_nodes_tags<-donosti_data1[[1]][[2]],6)

head(donosti_ways_attrs<-donosti_data2[[1]][[1]],5)

head(donosti_ways_tags<-donosti_data2[[1]][[2]],5)

head(donosti_ways_refs<-donosti_data2[[1]][[2]],5)

head(donosti_relations_attrs<-donosti_data3[[1]][[1]],5)

head(donosti_relations_tags<-donosti_data3[[1]][[2]],5)

head(donosti_relations_refs<-donosti_data3[[1]][[2]],5)

Now plot the data

plot(donosti_data)

Then let’s subset the streets network:

hways_nbgd <- subset(donosti_data, way_ids = find(donosti_data, way(tags(k == "highway"))))

hways <- find(hways_nbgd, way(tags(k == "highway")))

hways <- find_down(donosti_data, way(hways))

hways_nbgd <- subset(donosti_data, ids = hways)

hways_nbgd[1:5]

osmar object

19278 nodes, 3434 ways, 0 relations

Select start and end node… (in this case start node will be a bus stop outside of a student dorm and end node will be in a popular mall)

hway_start_node <- local({

id <- find(donosti_data, node(tags(v == "Andoain kalea")))[3]

find_nearest_node(donosti_data, id, way(tags(k == "highway")))})

hway_start <- subset(donosti_data, node(hway_start_node)) hway_end_node <- local({

id <- find(donosti_data, node(tags(v == "Matia kalea")))[1]

find_nearest_node(donosti_data, id, way(tags(k == "highway")))

})

hway_end <- subset(donosti_data, node(hway_end_node))

Then let’s convert osmar object (network of streets) to an igraph graph object

streetz_nbg<-as_igraph(hways_nbgd)

and find the shortest path

route <- get.shortest.paths(streetz_nbg,

from = as.character(hway_start_node),

to = as.character(hway_end_node))[[1]]

route_nodes <- as.numeric(V(streetz_nbg)[as.integer(route[[1]])]$name)

route_ids <- find_up(hways_nbgd, node(route_nodes))

route_nbg <- subset(hways_nbgd, ids = route_ids)

After we find the shortest path we can plot it using leaflet package:

m <- leaflet()%>%

addTiles()%>%

setView(-1.994124, lat=43.307996, zoom=15)%>%

addPolylines(data=as_sp(route_nbg,"lines"))

Save map to png

save_webshot(m,"~/Documentos/Medium/mapa_ruta/plot1.png")

‘/home/oscar/Documentos/Medium/mapa_ruta/plot1.png’

show plot

save to html

saveWidget(m, "mapa.html", selfcontained = FALSE)

Conclusion

This example is a good exercise to practice with maps and the coordinates between points. You also work with lists, tuples and dataframes.