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.

## 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’

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"
#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>
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)
plot(donosti_data)
png

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]

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_webshot(m,"~/Documentos/Medium/mapa_ruta/plot1.png")

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

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

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

I hope you like it.

No matter what books or blogs or courses or videos one learns from, when it comes to implementation everything can look like “Outside the Curriculum”.

The best way to learn is by doing! The best way to learn is by teaching what you have learned! Never give up!

Master in Data Science. Passionate about learning new skills. Former branch risk analyst. https://www.linkedin.com/in/oscar-rojo-martin/. www.oscarrojo.es

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store