In-process A* over a user-supplied DuckDB road-network graph. No OSRM, no Valhalla, no GraphHopper sidecar, no Docker, no online calls by default. Drop a routable graph on disk, register it, and plan multi-waypoint routes with turn-by-turn directions — entirely inside the enclave.
Routing services usually mean another server to accredit. Here the solver runs in-process against the same DuckDB engine the rest of the platform uses — one POST, one route.
nodes + edges DuckDB file, built offline from OpenStreetMap (osm2pgsql recipe in the docs) or supplied by your GIS team.nodes(id, lat, lon) and edges(id, from_node, to_node) is enough; everything else is synthesized.PriorityQueue.{
"origin": { "lat": 34.0522, "lon": -118.2437, "name": "Start" },
"dest": { "lat": 34.0901, "lon": -118.3614, "name": "End" },
"intermediates": [
{ "lat": 34.0736, "lon": -118.2401, "name": "Stop 1" }
],
"options": {
"travelMode": "vehicle",
"avoidHighways": false,
"useEsriOnline": false,
"snapRadiusM": 500
}
}
→ Route { distanceM, timeSec, geojson, legs[].instructions[] }
One POST /api/routing/graphs validates the schema, backfills what's missing, and builds the indexes the solver's hot path needs.
Missing geometry, length_m, cost, and reverse_cost are backfilled at registration — geometries from lat/lon and node pairs, costs from length over speed, one-ways encoded as 1e18 reverse cost.
B-tree indexes on edges.from_node (A* expansion hot path), edges.to_node (reverse traversal), and nodes.id (path reconstruction) are created for you.
vehicle uses edge max_speed or highway-type defaults (motorway 110, trunk 90, primary 80, … footway 5). foot caps at 5 km/h and ignores motorway/trunk; cycle caps at 25 km/h.
ToolsDrawer → NAVIGATE → Route Planner: add waypoints by typing or clicking the map, solve, and read turn-by-turn directions. Named waypoint lists persist via the waypoints API.
POST /api/routing/export/gpx for Garmin and handheld GPS units, /export/kml for Google Earth — routes and waypoint lists both export.
A complementary offline navigation path: SpatiaLite VirtualRouting over cached OSM road networks with Dijkstra pathfinding, turn-by-turn instructions, and voice guidance announcements at decision points.
The same DuckDB graph feeds an in-process analytics layer — and when you genuinely need country-scale online routing, an explicit toggle is there.
MATCH and ANY SHORTEST.POST /api/graph/run against dbId "routing", edges table, done.options.useEsriOnline = true (or check "Use ArcGIS Online" in the Route Planner) to solve via Esri's World Routing service.{geojson, legs, summary} contract, so the map renderer and turn-by-turn work identically offline and online.The <1 s figure applies at the documented city/region scale (~100k nodes / ~250k edges); country-scale offline solving is out of scope by design. DuckPGQ requires DuckDB 1.5.1 or 1.5.4 — with the currently bundled 1.5.3 the extension may not load; the loader degrades gracefully and Onager remains available. Turn restrictions and real-time traffic are not consulted by the offline solver.
Build a routable graph once, register it, and every operator on the workstation plans, solves, and exports routes with zero external dependencies.