Using OpenStreetMaps and R’s igraph package to create a routing engine for Singapore’s streets.
This article is about my experience in building a routing engine for streets in Singapore. Starting from downloading line data from the openstreetmap(OSM), constructing the graph structure from the lines, ended with sampling on the shortest path output of the graph. In the process, I use R programming language and packages developed by its community.
I will explain in detail how each package contributes for this project. But for now, let’s load them first.
library(osmdata)
library(sf)
library(dplyr)
library(lwgeom)
library(igraph)
library(mapview)
The end product will be the shortest path computation. I am pretty familiar with R’s interface to igraph package. I have used that before for routing Singapore’s bus and train network. If you deal with a graph problems, I really feel that igraph is a complete package. It has Python and C interface too.
I have used igraph in the past. But in the past, the network lines were given by my supervisor. The current problem is where do you get the graph?
Fortunately, I came across osmdata package, which offer a convenient way to download OSM geometries to R. So, we download the data using osmdata package:
if(file.exists("sg highway 20200119b.rds")==F){
sg=opq(bbox=c(103.614311874285,1.23047931395298,104.031394289329,1.46968609740717)) %>%
add_osm_feature(key='highway')%>%
osmdata_sf()
saveRDS(sg,"sg highway 20200119b.rds")
#saving the data and not making multiple attempts on the same query.
}else{
#load the data if it already exist
sg=readRDS("sg highway 20200119b.rds")
}
Let’s see what we get from the download:
summary(sg)
Length Class Mode
bbox 1 -none- character
overpass_call 1 -none- character
meta 3 -none- list
osm_points 345 sf list
osm_lines 271 sf list
osm_polygons 271 sf list
osm_multilines 30 sf list
osm_multipolygons 29 sf list
Great, it’s an sf object from sf packaga. Looking at 190262 linestrings, it seems that this data will be the robust one. So, I will explore this dataset first, and I choose dplyr package for this task.
lineSG=sg$osm_lines
One of the step for exploration:
lineSG %>% pull(highway) %>% unique()
[1] "primary" "residential" "tertiary"
[4] "footway" "service" "secondary"
[7] "motorway" "motorway_link" "trunk"
[10] "trunk_link" "primary_link" "secondary_link"
[13] "living_street" "construction" "pedestrian"
[16] "unclassified" "tertiary_link" "proposed"
[19] "steps" "track" "cycleway"
[22] "path" "bridleway" "raceway"
[25] "corridor" NA "footpath"
Apparrently, there is footpath too. Great data, but not what I need at the moment.
After several other steps of exploration, I decided that the following features of the dataset are the lines and attributes that we need:
lineSG=lineSG %>%
filter(highway%in%c("motorway","motorway_link",
"primary","primary_link",
"secondary","secondary_link",
"tertiary","tertiary_link",
"residential",
#"service",
"unclassified",
"trunk","trunk_link")) %>%
select(osm_id,name,highway,lanes,maxspeed,oneway,geometry)
Let’s inpect graphically whether the chosen dataset is promising:
mapview(lineSG)