Routing docs

Routing: traffic and live data

How geolith routing uses speeds: historical speeds by time of week in the routing part (builder flag --routing-predicted-speeds, reference CSV format), live speeds and closures supplied by the caller, and the planned per-account live overlay. In development, not deployed for public use. As of 8 Nov 2026.

By · Updated

Status: in development, as of 8 Nov 2026. Not deployed for public use. Historical speeds in the builder (--routing-predicted-speeds) are done, and the router uses them for date_time types 0 (current) and 1 (depart at); types 2 (arrive by) and 3 (invariant) return error 199 for now. Live speeds and closures are supplied by the program that runs the router. No hosted service collects or serves live data yet.

On Berlin, responses are byte-identical to Valhalla 3.9.1 on 50/50 requests with historical speeds, 30/30 with live speeds and 5/5 with closures; see Status and roadmap.

Where speeds come from

The router picks one speed per road edge, in this order (the same order as Valhalla 3.9.1):

SourceWhere it livesUsed when
Live speedAn overlay beside the .glx fileThe request has a date_time and the overlay has the edge
Historical speedThe routing part (predicted speeds)The request has a date_time and the edge has a profile
Constrained flowThe routing part (edge field)Daytime (07:00 to 19:00), or no date_time
Free flowThe routing part (edge field)Night, or no date_time
Edge speedThe routing part (edge field)Otherwise: from OpenStreetMap tags and road class

A live speed counts fully at the start of the route and fades out over the first hour of travel time. A request without a date_time never uses live or historical speeds.

Historical speeds: the CSV format

Pass a directory to the builder:

geolith --output-format routing --osm-pbf berlin.osm.pbf \
  --routing-output berlin-routing.glx \
  --routing-predicted-speeds speeds/

The directory holds one CSV file per graph tile, named by its tile path, for example speeds/2/000/753/554.csv. Each row describes one directed edge of that tile:

edge_graph_id,free_flow_kph,constrained_kph,encoded_dct2/753554/120,52,38,AbC...==
FieldMeaning
edge_graph_idThe edge, as level/tile/index. Only the index is read; the tile comes from the file name.
free_flow_kphNight speed in km/h. 0 means not given. Capped at 140.
constrained_kphDaytime speed in km/h. 0 means not given. Capped at 140.
encoded_dctOptional. The week of speeds in 2016 five-minute buckets, compressed to 200 DCT-II coefficients, stored as big-endian 16-bit integers and base64 encoded (400 bytes).

The format and its parsing are the reference tool's (valhalla_add_predicted_traffic), and geolith's output tiles are byte-identical to that tool's output on the same tiles and CSV. Data that already targets Valhalla works unchanged. Identical profiles are stored once per tile. Each tile with profiles grows by 4 bytes per edge plus 400 bytes per distinct profile.

Edge ids change with every graph build. Providers usually key speeds by OpenStreetMap way and map them to edges for each build.

Live data

Today the router takes live speeds and closures from the program that runs it, as overlay records beside the tile bytes. The hosted overlay is planned: live speeds, closures and incidents will live in an overlay beside the .glx file, never inside it, so the file stays immutable and cacheable. The design:

  • Private per account. A customer's probe data (for example a fleet's driver pings) is used only for that account. Sharing into a common traffic layer is an explicit opt-in.
  • Short-lived. Each overlay record carries an expiry. Live speeds are 5-minute aggregates per edge, published only when enough distinct vehicles drove the edge. Driver ids are not stored, and raw pings are not kept.
  • Keyed to one graph build. When a new routing file goes live, overlays for the old build stop matching.
  • Read like tiles. The router library takes the overlay records from the program that runs it, like tile bytes, so the same code runs in a Worker and in tests.

The full design, including the ingestion API and storage, is in the routing repository at docs/live-data.md. Search plans the same per-account overlay pattern for custom places.