AngoraTek FR
← Back to Blog
Field Notes 8 min read

You Can Route on Overture, and You Own Every Tile You Build

18.0 seconds. That is what DuckDB needed, on Boris's laptop on 2026-10-11, to filter 106,242 transportation segments out of Overture release 2026-09-23.1 for the Tahoe basin, pruning Parquet files on S3 in place instead of downloading whole themes. The number with the story attached is different: zero, the count of managed routing services that will serve you those same segments as turn-by-turn directions.

The claim, phrased so a skeptic can push against it: you can route on Overture now, because the Overture Maps Foundation maintains omf-bifrost, a Rust tool in its OvertureMaps/transportation-routing repository that builds Valhalla graph tiles straight from Overture GeoParquet. The engine underneath has years of production use behind it. The tool that feeds it is young, and that difference is where this post lives. What nobody offers on top is an endpoint with an SLA, a traffic model, or anyone to call when the chosen route crosses a bridge that closed in March.

On Overture you are the operator of your own map. We already run that role on OpenStreetMap: AngoraTek's trail planner sits on a graph we filtered and elevation-joined ourselves, a build we wrote up in August as a trail-graph field report. The engine was the easy part of that build, and we expect it to stay easy here. The interesting difference is the raw material, because this time it arrives pre-typed.

A pinned release never surprises you, and never corrects itself either

Nothing in the transportation theme of release 2026-09-23.1 is telemetry. The schema knows the speed limit on a segment; it has never sat in the queue behind it. Overture publishes no traffic, no historical speeds, and nothing resembling a live feed at any version.

That absence is also the product. A graph built from a pinned release is reproducible: rebuild against 2026-09-23.1 and the input has not moved under you, which is how you debug a route somebody disputed last spring without first asking whether the data changed underneath. A live feed never offers that, and it never tells you what it altered this week. Releases arrive monthly, so rebuilds can land on your calendar instead of a feed's: pin 2026-09-23.1 through the quarter, adopt the next release when your own tests pass against it.

The price deserves its own sentence. A graph built in November is a November map, closures included, for as long as you keep building from that release. The workaround is dated, scheduled rebuilds plus a diff report somebody actually reads, and that somebody is you. When the reconciliation job grows into a full-time job, you have recreated a vendor's costs without the vendor, and that is the moment to revisit the deal.

Two commands between a release and a routing engine

omf-bifrost's CLI exposes two relevant commands, build-tiles and build-admins. The first reads Overture's transportation segments and writes Valhalla graph tiles. The second builds the administrative database Valhalla consults for country-specific routing rules and time zones. Both read the same GeoParquet layout every other Overture tool reads:

s3://overturemaps-us-west-2/release/2026-09-23.1/theme=transportation/type=segment/*.parquet

Because those files are hive-partitioned and carry a bbox struct, a build can prune whole regions before any geometry is read. The same mechanism let our laptop return 106,242 Tahoe-basin segments in 18.0 seconds. That is why a general builder like omf-bifrost can exist as a Rust CLI rather than a platform: a stable, prunable, versioned distribution made the build a weekend-shaped project instead of a platform team's quarter.

Candor belongs here more than anywhere else in the post. We have no build timings to publish, because we do not own them yet, and this is a field report on young tooling rather than a benchmark. omf-bifrost is pre-1.0. It is something you run and patch around while the edges sand down. A button does not exist on any layer of this stack, and pretending otherwise is how young tooling disappoints people.

The segment schema carries more structure than most tag toolchains read

OpenStreetMap gives you a way and a bag of tags, then leaves the opinions to your filter. We have lived that: our first trail-graph filter trusted every highway=track until the router started recommending logging roads to people in running shoes. Overture, at release 2026-09-23.1, hands those opinions over pre-typed on every segment.

A subclass comes with subclass_rules that scope it by value or by between, instead of a single tag that everybody guesses at. speed_limits carries a floor, a ceiling and a unit, so a 55 mph segment cannot be quietly re-read as km/h half a continent away. The schema also carries access_restrictions, prohibited_transitions and destinations, then road_surface and road_flags for the physical truth of the pavement. Fields as documented for that release, nesting simplified and values illustrative:

{
  "id": "…",
  "subtype": "road",
  "class": "residential",
  "speed_limits": { "min": 15, "max": 55, "unit": "mph" },
  "road_surface": "paved",
  "access_restrictions": [],
  "prohibited_transitions": [],
  "destinations": []
}

Structure moves where the argument lives. On OpenStreetMap the filter is where our opinions lived, and ours were wrong in ways we could learn only by routing on them. On Overture the opinions arrive versioned inside the schema, and the question left is whether you agree with them. You cannot settle a disagreement by adding a tag the schema does not know. A typed column makes the disagreement visible in one place instead of scattered through filter code, which is real progress, and one place is still not zero places.

The graph thins first at boundaries, restrictions and turns

Connectivity is the failure class we know best, because we generated a cousin of it ourselves. The August trail-graph post describes how deleting motor ways while keeping their crossing nodes left holes a router bridged with straight lines it had no business drawing. Overture segments carry a connectors array, and a graph is only as strong as the topology those connectors share. Where two contributions meet, a thin seam has a small symptom: a route that doglegs around one block for no reason any map shows. The audit is the one we already run on our own graph. Count the connected components, walk into the largest gaps, and refuse to promise coverage until the pieces join.

Restrictions are the second thin place. access_restrictions is a typed column, and typed columns are only as complete as the people who filled them. Coverage follows contribution history, which has never been uniform across a continent. Count the restrictions in your region before promising a customer anything; with bbox pruning the query costs seconds, and an honest count beats a coverage claim that has to be retracted in week three.

Turns are the third. The schema carries prohibited_transitions, and a router without its turn restrictions produces routes that are legal on paper and illegal at the corner. What omf-bifrost does with them inside the Valhalla build is the kind of question a pre-1.0 repository answers in its issue tracker more reliably than in its docs, and we have not yet put it on a region dense with turn restrictions. Untested beats overstated, here and everywhere else.

What a snapshot cannot be

The honest accounting, limits first. omf-bifrost is pre-1.0 tooling, not a managed routing service: nobody is on call for your graph, so the release pin, the Valhalla pin and the rebuild schedule are yours. So is map-matching, the step that decides which segment a GPS point belongs to, and that step does not arrive with any provider's years of tuning. The snapshot is a snapshot: no traffic, no historical speeds, no live flows of any kind, at any release, on any layer.

Our workarounds, then, since the limits are the load-bearing part. Where a customer's SLA names response times, we pair a graph built from Overture with a live traffic service, keeping the two answers separate in the product: your own graph speaks for legality, the feed speaks for delay. We argued in an earlier post about provider choice that a live traffic feed is exactly the substance no abstraction layer carries. Where no SLA demands it, we schedule the rebuild, publish the release ID beside the route, and let an honest map age in public.

A graph you built tells you which release it came from. A feed tells you nothing about its own age, and calls the silence freshness.

Build the graph when reproducibility sells, buy the feed when latency does

One decision, stated so it can be argued with. If your product's promise is a route you can explain in a meeting six weeks later, dated, auditable, unchanged, the Overture stack is worth a pilot week this month, and the worst case is that you lose that week. If the promise is the fastest ETA in Friday city traffic, buy the feed, because no release will carry it. Where legality, cost control and the ability to point at the exact data version matter more than live congestion, build the graph; you get 106,242 segments for one basin in 18.0 seconds, and every decision after that has a paper trail.

AngoraTek builds routing graphs on OpenStreetMap and evaluates Overture release by release. Bring us your routing question.

Thanks for reading this far. If this sounds like the problem you're wrestling with, tell us about it. A principal reads every message.

Start a conversation