Simple self-hosted OpenStreetMap routing using Valhalla and Docker
I came up with an interesting plan for an artistic map recently (more on that when I’ve finished working on it), and to create it I needed to be able to calculate a large number of driving routes around Southampton, my home city.
Specifically, I needed to be able to get lines showing the driving route from A to B for around 5,000 combinations of A and B. I didn’t want to overload a free hosted routing server, or have to deal with rate limiting – so I decided to look into running some sort of routing service myself, and it was actually significantly easier than I thought it would be.
It so happened that I’d come across a talk recently on a free routing engine called Valhalla. I hadn’t actually watched the talk yet (it is on my ever-expanding list of talks to watch), but it had put the name into my head – so I started investigating Valhalla. It seemed to do what I wanted, so I started working out how to run it locally. Using Docker, it was nice and easy – and to make it even easier for you, here’s a set of instructions.
-
Download an OpenStreetMap extract for your area of interest. All of your routes will need to be within the area of this extract. I was focusing on Southampton, UK, so I downloaded the Hampshire extract from the England page on GeoFabrik. If you start from the home page you should be able to navigate to any region in the world.
-
Put the downloaded file (in this case,
hampshire-latest.osm.pbf
) in a folder calledcustom_files
. You can download multiple files and put them all in this folder and they will all be processed. -
Run the following Docker command:
docker run -p 8002:8002 -v $PWD/custom_files:/custom_files ghcr.io/gis-ops/docker-valhalla/valhalla:latest
This will run the valhalla Docker image, exposing port 8002 and mapping the
custom_files
subdirectory to/custom_files
inside the container. Full docs for the Docker image are available here. -
You’ll see various bits of output as Valhalla processes the OSM extract, and eventually the output will stop appearing and the API server will start.
-
Visit
http://localhost:8002
and you should see an error – this is totally expected, it is just telling you that you haven’t used one of the valid API endpoints. This shows that the server is running properly. -
Start using the API. See the documentation for instructions on what to pass the API.
Once you’ve got the server running, it’s quite easy to call the API from Python and get the resulting route geometries as Shapely LineString objects. These can easily be put into a GeoPandas GeoDataFrame. For example:
import urllib
import requests
from pypolyline.cutil import decode_polyline
# Set up the API request parameters - in this case, from one point
# to another, via car
data = {"locations":[ {"lat":from_lat,"lon":from_lon},{"lat":to_lat,"lon":to_lon}],
"costing":"auto"}
# Convert to JSON and make the URL
path = f"http://localhost:8002/route?json={urllib.parse.quote(json.dumps(data))}"
# Get the URL
resp = requests.get(path)
# Extract the geometry of the route (ie. the line from start to end point)
# This is in the polyline format that needs decoding
polyline = bytes(resp.json()['trip']['legs'][0]['shape'], 'utf8')
# Decode the polyline
decoded_coords = decode_polyline(polyline, 6)
# Convert to a shapely LineString
geom = shapely.LineString(decoded_coords)
To run this, you’ll need to install pypolyline and requests.
Note that you need to pass a second parameter of 6
into decode_polyline
or you’ll get nonsense out (this parameter tells it that it is in polyline6
format, which seems to not be documented particularly well in the Valhalla documentation). Also, I’m sure there is a better way of passing JSON in a URL parameter using requests, but I couldn’t find it – whatever I did I got a JSON parsing error back from the API. The urllib.parse.quote(json.dumps(data))
code was the simplest thing I found that worked.
This code could easily be extended to work with multi-leg routes, to extract other data like the length or duration of the route, and more. The Docker image can also do more, like load public transport information, download OSM files itself and even more – see the docs for more on this.
Check back soon to see how I used this for some cool map-based art.
If you found this post useful, please consider buying me a coffee.
This post originally appeared on Robin's Blog.
Categorised as: GIS, How To, Programming, Python
[…] Wilson has written a blog post showing how easy it is to self-host OSM-based routing using Valhalla and Docker, […]
How much computing power was required to be able to host locally?
Not a huge amount. I ran this on my home server, which isn’t particularly powerful (it’s quite old, and is designed for power efficiency rather than speed/compute power) and I got the API responses back very quickly for routing within Southampton, UK.