The Open Source Routing Machine (OSRM) is a library for calculating routes, distances and travel times between spatial locations. It can be accessed via either an HTTP or C++ API. Since it’s open source you can also install locally, download appropriate map data and start making efficient travel calculations.
These are the instructions for getting OSRM installed on a Ubuntu machine and hooking up the osrm
R package.
Setup
Building OSRM is memory intensive, so unless you are installing on a machine with a good chunk of RAM, you’ll want to ensure that there’s around 4 Gb of swap space available.
Building OSRM
First make sure that you have the necessary infrastructure and libraries required to build OSRM. Strictly htop
is not necessary but it’s handy for monitoring performance.
sudo apt update
sudo apt install -y git cmake build-essential jq htop
sudo apt install -y liblua5.2-dev libboost-all-dev libprotobuf-dev libtbb-dev libstxxl-dev libbz2-dev
Now grab the source directly from the repository on GitHub.
git clone --branch v5.18.0 https://github.com/Project-OSRM/osrm-backend.git
Although you could probably just build from the current state of the repository, I prefer to use a specific release. For example, above I clone and then checkout the v5.18.0 tag.
Move into the source folder.
cd osrm-backend/
Create a build
folder and then run cmake
to generate Makefiles.
mkdir build
cd build/
cmake ..
Next initiate the build.
make
Time to kick back and wait: this will take some time!
If you are on a multi-core machine then you can build in parallel too.
make -j$(nproc)
When the build completes, make the install
target.
sudo make install
The complete recipe above is available as a gist.
Getting OpenStreetMap Data
OpenStreetMap Export
Go to the export page on OpenStreetMap. Zoom in on you area of interest and then press the Export button. The area I was working with was too large to download directly from OpenStreetMap, so I followed the link to the Overpass API, which worked flawlessly.
I’m installing on a remote instance, so I used wget
to do the download.
wget -O map.xml http://overpass-api.de/api/map?bbox=29.5,-30.5,31.5,-29.0
The resulting download will be a (possibly rather large) XML file. Move it to the osrm-backend
folder created above.
Data from Geofabrik
You can also download the OpenStreetMap data in a variety of formats from Geofabrik.
Extracting the Map
In the profiles
folder you’ll find three files (bicycle.lua
, car.lua
and foot.lua
) which provide speed profiles for various means of transportation. You can create a custom profile if necessary, but the ones provided will suffice for most situations. We’ll go with the car profile.
The next step is to extract the routing data. This can be very memory intensive, so make sure that you have sufficient swap space or that you’re using STXXL.
osrm-extract map.xml -p profiles/car.lua
Creating a Hierarchy
Now to create data structures that facilitate finding the shortest route between two points.
osrm-contract map.xml.osrm
Launching the Service
We can launch a HTTP server which exposes the OSRM API as follows:
osrm-routed map.xml.osrm
Let’s try a few test queries. First we’ll find the nearest road to a location specified by a longitude/latitude pair.
curl "http://localhost:5000/nearest/v1/driving/31.043515,-29.778562" | jq
{
"waypoints": [
{
"distance": 37.79936,
"hint": "Cr0BgIG9AYD-AAAAYgAAAAAAAAAAAAAA_gAAAGIAAAAAAAAAAAAAACYAAAAosdkBA505_ruv2QF-nTn-AABfAS51HLo=",
"name": "St Andrews Drive",
"location": [
31.04388,
-29.778685
]
}
],
"code": "Ok"
}
Next the distance and time between two locations.
curl "http://127.0.0.1:5000/route/v1/driving/31.043515,-29.778562;31.029080,-29.795506" | jq
{
"code": "Ok",
"routes": [
{
"geometry": "xcwtDggn|DfHbC|AjHnFjBxAlNdDjAwA|Fvq@rUvFTF`KtAL|@g@vIvDRvASzHVr@dBVj@uD",
"legs": [
{
"steps": [],
"distance": 2926.9,
"duration": 357.6,
"summary": "",
"weight": 357.6
}
],
"distance": 2926.9,
"duration": 357.6,
"weight_name": "routability",
"weight": 357.6
}
],
"waypoints": [
{
"hint": "Cr0BgIG9AYD-AAAAYgAAAAAAAAAAAAAA_gAAAGIAAAAAAAAAAAAAACYAAAAosdkBA505_ruv2QF-nTn-AABfAS51HLo=",
"name": "St Andrews Drive",
"location": [
31.04388,
-29.778685
]
},
{
"hint": "p7kBgMO5AYANAAAAeAAAAGACAAALAAAADQAAAHgAAABgAgAACwAAACYAAAB_d9kBwls5_lh32QFOWzn-CQDvES51HLo=",
"name": "Gainsborough Drive",
"location": [
31.029119,
-29.79539
]
}
]
}
The duration
values are in seconds and the distance
is in metres. Looks pretty legit!
More information on the API can be found here.
If you were wanting to expose this service to the outside world then you’d need to integrate it with your web server and maybe set up something to restart the service if the machine reboots. I’m only planning on using OSRM locally, so these are not issues for me.
The osrm
R Package
My primary motivation for setting up OSRM is so that I can use it from within R.
First install a couple of packages.
sudo apt install -y libcurl4-openssl-dev libgeos-dev
Now install the osrm
package.
install.packages("osrm")
Load the package and point it at the local OSRM service.
library(osrm)
Data (c) OpenStreetMap contributors, ODbL 1.0. https://www.openstreetmap.org/copyright
Routes: OSRM. http://project-osrm.org/
If you plan to use the OSRM public API, read the OSRM API Usage Policy:
https://github.com/Project-OSRM/osrm-backend/wiki/Api-usage-policy
options(osrm.server = "http://127.0.0.1:5000/")
Now create a couple of locations.
locations = data.frame(
comm_id = c("A", "B", "C"),
lon = c(31.043515, 31.029080, 31.002896),
lat = c(-29.778562, -29.795506, -29.836168)
)
Generate a table of travel times between those locations.
osrmTable(loc = locations)
$durations
A B C
A 0.0 6.0 11.9
B 6.0 0.0 9.6
C 11.5 9.7 0.0
$sources
lon lat
A 31.04388 -29.77868
B 31.02913 -29.79539
C 31.00286 -29.83625
$destinations
lon lat
A 31.04388 -29.77868
B 31.02913 -29.79539
C 31.00286 -29.83625
Calculate the optimal route between two locations.
route = osrmRoute(src = locations[1,], dst = locations[2,], sp = TRUE)
route$duration
[1] 5.96
route$distance
[1] 2.9269
The units are now minutes for duration
and kilometres for distance
.
I’ve been using the gmapsdistance
package until now. It has worked brilliantly but I’ve had to manage it carefully to avoid overstepping API limits. With a local OSRM I’ll be making the calculations unconstrained!
OSRM on AWS
An AWS instance is a good option for running an OSRM instance.
A few things to consider:
- potentially use OpenStreetMap on AWS as a data source;
- make sure that port 5000 is open in the security group applied to your instance.
OSRM Errors
Too many table coordinates
If you get a “Too many table coordinates” then increase the table size using command line arguments. The required size can be determined by trial and error.
osrm-routed --max-table-size 10000 map.xml.osrm