Route calculations… locally!
For a recent project, we needed to calculate vehicle routes… a LOT of routes.
We were using an optimization algorithm to plan delivery vehicle routes with multiple stops. Because the previous stop of a vehicle determines the start location of the vehicle for the next “delivery” and the sheer scale of the system, it did not seem practical to pre-calculate all the routes before running the optimization. So we decided to calculate routes while running the algorithm.
So given these architectural decisions (we discussed alternatives, but I won’t go into those for this post) we had 2 hurdles we had to tackle:
- Cost: Lots of calls to a routing service usually equals a high cost;
- Speed/Delay: Any kind of optimization algorithm takes a lot of time already, so any kind of network delay would have hurt that processing time badly.
So to minimize our cost and maximize the speed a which we can calculate routes we started looking for an alternative to the online map/route-services like Google Maps.
We quickly realized that something like OpenStreetMaps would be perfect for us… if we could only host it ourselves. Turns out…. that’s pretty easy to do! So lets dive into it! How do you host your own OpenStreetMaps? 🤔
Hosting your own OpenStreetMaps API
Data is king! Without the data involving streets and addresses… there is no way to do any kind of route calculation. As it turns out, you can download the OpenStreetMaps data… FOR FREE! You can download the data for a continent or specific country from http://download.geofabrik.de/.
As I’m Belgian, I’m going to use the data for my home country as an example.
# Download the data!
wget http://download.geofabrik.de/europe/belgium-latest.osm.pbf
or
curl http://download.geofabrik.de/europe/belgium-latest.osm.pbf --output belgium-latest.osm.pbf
or
# download with your favorite downloading tool...
Note: These .osm.pbf files are updated frequently, so you might want to take that into account when hosting your own. Maybe download them every once in a while.
Now that we got the data, we need some way to calculate routes between two points and then to expose that capability through an API. Sounds like a lot of work… 🤔 but I had an inkling that somewhere, someone, would have built something like this already.
I leveraged the most powerful software development tool on the planet (yes, it’s Google) and found a solution: The OSRM Project (full name: Open Source Routing Machine) which is a C++ based routing engine to calculate the shortest paths in road networks.
Not only is it awesomely free, the project also kindly provides us with Docker Containers 🤤 which contain all the tools needed to get a Routing API up and running! No messing with custom installations, just a few simple commands to run and get going!
# Note 1: We are using a volume to share the downloaded belgium-latest.osm.pbf file with the container in all the commands below.
# Note 2: These commands do a ton of calculations. For the Belgian data, it took me about 10 minutes on my laptop to run all 3.
# This first command extracts the data from the .PFB file and uses the provided Lua script to create a graph.
# More details on this command: [here](https://github.com/Project-OSRM/osrm-backend/wiki/Running-OSRM#extracting-the-road-network-osrm-extract)
docker run -t -v %cd%:/data osrm/osrm-backend osrm-extract -p /opt/car.lua /data/belgium-latest.osm.pbf
[ $? -eq 0 ] && echo "Extract command was successful" || echo "Extract failed"
# This next command creates a Hierarchy that enables the calculation of shortest route..
# More details on this command: [here](https://github.com/Project-OSRM/osrm-backend/wiki/Running-OSRM#creating-the-hierarchy-osrm-contract)
docker run -t -v %cd%:/data osrm/osrm-backend osrm-contract /data/belgium-latest.osrm
[ $? -eq 0 ] && echo "Contract command was successful" || echo "Contract failed"
# This command starts an API service on port 5000.
docker run -t -i -p 5000:5000 -v %cd%:/data osrm/osrm-backend osrm-routed /data/belgium-latest.osrm
Since running that last command, we got an API running locally! So, what does a typical “Calculate Route”-action look like? 👇
#/driving/location1;location2?parameters. Details about the parameters http://project-osrm.org/docs/v5.5.1/api/#route-service
curl 'http://localhost:5000/route/v1/driving/4.455436170101167,51.054425046743894;3.0794441699981694,51.299335146604115'
And the response… after 5 milliseconds! 👇
// Omitting some fields for readability ;)
{
code: "Ok",
routes: [
{
legs: [
{
steps: [],
distance: 136778.6,
duration: 5873.8,
summary: "",
weight: 5909.8
}
],
distance: 136778.6, // IN METERS => 136KM
duration: 5873.8, // IN SECONDS => Slightly more than 1.5 hours.
}
]
}
So within 5 miliseconds, we are getting the distance and duration of a route between 2 points! Awesomesauce!
This is not even using all functionality of the API! It has support for alternatives (returns multiple “legs”, aka different routes to take) and steps (returns all the streets the route passes!). You can find the full details in the official documentation. It even provides a Trip API which solves the Traveling salesman problem! 🤤
Wow, Amazing! But…
If the thought of hosting this all locally, using Open Source Data and using Open Source Software (in Docker containers! 🤯 ) doesn’t get you excited… this blogpost clearly wasn’t for you! (No refunds!)
Personally, being able to run something like this locally makes me all warm and fuzzy inside… and even more in love with software engineering than I already am.
There are a few things I want to point out about this post before we finish up this blogpost.
- ⚠️ While you don’t have to pay “per request”, this still isn’t a free solution! You need to host (and maintain!) all of this stuff yourself. Don’t underestimate the cost of that.
- ⚠️ The routing data does not include “live traffic” information, so any traffic jams or accidents are not taken into account. Consider if your usecase requires this information.
If these points don’t worry you however, you are good to go! In a follow-up post, I’ll show you how to host your own map tiles, so you can show these routes drawn out on a map!
Lots of 💖
Tom