SOW-LITE self-hosted map

MapLibre 6.4.1 · Terra Draw 1.32.3 · Valhalla truck routing

checking…

Routing goes to — through a same-origin /valhalla/* proxy in the vite dev server. That proxy stands in for the NestJS backend hop the plan recommends (decision C) — the browser never learns the engine's real URL.

Basemap

No satellite layer exists here, by design. There is no free commercially-usable high-res global imagery — EOX Sentinel-2 cloudless is non-commercial, Esri World Imagery's free grant covers OSM editing only, Bing Maps Enterprise retired in 2025. This is decision E made visible: judge whether your operators can draw geofences against street cartography alone.

Places

A working preview of phase O3, the gazetteer — mine sites, smelters, border posts and corridor ports. Click any marker for detail and Set A / Set B. These are hand-authored here; in production they come from the gazetteer table (GeoNames + Overture Places + the customer's own sites). No Google Places call is made — that path is blocked pending legal review.

Switch to Photon to compare open-data coverage of the Copperbelt against the fixture — this is the self-hostable option in the plan (Apache-2.0, built for search-as-you-type, returns coordinates in the suggestion so resolvePlace needs no second call). The public instance is for comparison only — komoot's policy allows it "as long as the number of requests stay in a reasonable limit" with no availability guarantee, so a commercial product must self-host.

Route

Pick a preset, or click the map: first click sets A, second sets B. Both pins are draggable.

A—
B—

NoSegment means an endpoint could not attach to a routable edge — a snapping failure, not a distance or coverage one. The usual cause is minimum_reachability (default 50), which discards candidate edges in small connected components: near the edge of a country extract, or on a private mine spur, every candidate is rejected even though the road is visibly there. This is the same mechanism as the mine-gate snapping caveat in the plan.

On a 350 km haul, 5,000 points is ~70 m spacing — finer than the 100 m trip_routes.corridor_m. On a 5,000 km corridor it is ~1,000 m spacing, ten times coarser than the corridor, so the stored chords cut every curve and a truck on the correct road reads as off-route. Change this and watch the Point spacing figure.

Route quality cannot be judged at continental zoom — a line that looks fine across 1,800 km may be sitting on a footpath. These jump to z15 on the street basemap, where you can see individual carriageways and check the route uses the road a loaded truck would actually take. This is the single most useful thing in the demo for deciding whether Valhalla wins.

Result

no route yet

Is there a road here?

Asks the graph directly via /locate, with a 10 km radius and reachability 0 — the most permissive search possible. This separates the three causes of NoSegment, which need different fixes: nothing mapped nearby, a road that exists but is not truck-accessible, or a road just outside the snap radius. Run this before concluding anything about the engine.

not probed yet

Engine limits

Read from /status?verbose=true — the server's real service_limits, not the documented defaults. Valhalla's truck default is 5,000,000 m, so any low ceiling you hit is configuration, not an engine capability. A public instance may tighten these; local Docker Valhalla uses your own valhalla.json.

not read yet

Route diagnostics

A route can report a correct distance and duration and still not appear, because those come from JSON fields while the line comes from geometry. This panel says which of the four stages failed — decode, data, layer, or camera — instead of leaving you to guess.

run a route

Geofences

Circles are hand-rolled from draggable markers plus a 64-gon — the same technique the existing mapbox adapter uses, so this code ports unchanged. Polygons are Terra Draw, because mapbox-gl-draw crashes on MapLibre. This is the riskiest part of the port (plan risk R1) — try hard to break it.

Per-request avoidance is the capability no other engine offers. It is also where you meet max_exclude_polygons_length, whose default 10000 is the summed ring perimeter in metres — a 2.5 km square is a 10 km perimeter and exhausts it. Draw something big and watch for exception 167.

Response envelope

Proof that Valhalla's format:osrm output is the same shape services.ts already parses for Mapbox Directions v5.

run a route to populate

Log