Public Transit Isochrones with Valhalla

Isochrone map for a location in San Francisco

I keep getting questions about the state of isochrones in conjunction with transit routing in Valhalla. There’s been support for it for a couple of years now, and with Valhalla being able to read GTFS feeds directly, it’s really easy to set up yourself. So here’s really just a short recipe to get a GTFS feed into Valhalla and see some isochrone results.

# get some GTFS feeds, e.g. at https://www.sfmta.com/reports/gtfs-transit-data
# unzip it
7z e muni.zip

# create a directory that holds all feeds
mkdir feeds && mv muni feeds

# get an OSM file that covers the whole extent of all feeds
wget http://download.geofabrik.de/north-america/us/california-latest.osm.pbf -O cal.pbf

# maybe fit it a bit so the graph build doesn't take forever
osmium extract -b -122.567596,37.675669,-122.332764,37.853170 --overwrite -o sf.pbf  cal.pbf

# build timezones
valhalla_build_timezones > tz.sqlite
# create a valhalla config file
valhalla_build_config --mjolnir-transit-feeds-dir=feeds --mjolnir-transit-dir=transit --mjolnir-tile-dir=sf \
                      --mjolnir-tile-extract=sf.tar --mjolnir-timezone=tz.sqlite > valhalla.json

# build transit
valhalla_ingest_transit -c valhalla.json
valhalla_convert_transit -c valhalla.json

# build the graph
valhalla_build_tiles -c valhalla.json sf.pbf

# finally, make the tar
valhalla_build_extract -c valhalla.json

# and voila, run the service and fire requests
valhalla_service valhalla.json

curl -L 'localhost:8002/isochrone' \
-H 'Content-Type: application/json' \
-d '{
        "costing": "multimodal",
        "show_locations": false,
        "contours": [
                {
                        "time": 25.0
                }
        ],
        "polygons": true,
        "id": 1,
        "denoise": 0.0,
        "generalize": 0.0,
        "date_time": {
                "type": 1,
                "value": "2024-01-28T17:20"
        },
        "locations": [
                {
                        "lon": -122.475279,
                        "lat": 37.721529
                }
        ],
        "format": "geotiff"
}' > out.geotiff

…and Bob’s your uncle. If you look at the geotiff file (you can e.g. simply pull it into QGIS, you have to apply a pseudo-color to the raster layer though), you should see a shape similar to the one in the image. If the shape of the raster data is more circular with no islands, that indicates that the transit building might not have worked properly and that the isochrone expansion simply resorted to walking.

This is a minimal working example of course. The geotiff format is optional and can be avoided by leaving out the "format" argument in the request. In reality, you may want to have a base graph that you repeatedly add transit to every time you get updated feeds. This can be achieved by specifying -e build when building the graph. You can then resume the build once you have rebuilt your transit data by specifying -s enhance with valhalla_build_tiles.

This is a little list of instructions for those who want to work with their own Valhalla build; if this is too inconvenient for you, you can also use this docker image, which also allows for easy adding of transit data, just follow its docs.