Skip to content

feat(routing): add OSRM as an alternate routing backend behind a provider abstraction #189

@jasoneplumb

Description

@jasoneplumb

Problem

src/routing.ts is hardcoded to FOSSGIS Valhalla at valhalla1.openstreetmap.de. When FOSSGIS takes that service offline (currently down for several days at the time of writing — both valhalla1 and valhalla2 refuse TCP on :80 and :443 while the hosts themselves remain pingable), the app loses all turn-by-turn navigation with no recourse. The only thing the user sees is the routing-unavailable dialog from #188.

We have no swappable routing provider. Adding OSRM at routing.openstreetmap.de (a stable peer service hosted by OSM-DE, currently up while FOSSGIS Valhalla is down) requires first refactoring routing.ts into a provider-agnostic interface, then implementing an OSRM client alongside the existing Valhalla one.

API delta between Valhalla and OSRM

Concern Valhalla (current) OSRM (new)
Endpoint POST /route + JSON body GET /route/v1/{profile}/{lon},{lat};{lon},{lat}?overview=full&geometries=polyline6&steps=true
Profile values auto, bicycle, pedestrian driving, cycling, walking (1:1 map)
Geometry polyline6 (encoded in leg.shape) polyline6 (when requested) — decodePolyline6() is shared
Step distance/duration length (in unit) + time (s) — needs metersPerValhallaUnit() factor distance (m) + duration (s) directly
Instruction text Prose string from Valhalla's narrative generator (step.instruction) Not provided — must be synthesized from maneuver.type + maneuver.modifier + step.name
Units option directions_options.units (km/mi) Always metric internally — our units.ts formats for display, so this is a no-op for us
Costing config costing field Profile is in the URL path

Expected

  • fetchRoute() keeps its external signature; callers in guidance.ts are unchanged.
  • Provider selected by a const (e.g. ROUTING_PROVIDER: 'valhalla' | 'osrm' = 'valhalla'). Runtime fallback is a separate issue.
  • Both providers produce the same Route shape: coords, steps[] with instruction / type / lengthM / durationS / streetNames / beginShapeIndex, plus distanceM and durationS.
  • OSRM maneuver.type values (turn, new name, depart, arrive, merge, on ramp, off ramp, fork, end of road, continue, roundabout, rotary, roundabout turn, notification) are mapped to either our existing numeric Valhalla maneuver-type or a parallel string union — decide during implementation. Whichever direction, the icon-selection in guidance.ts must keep working.
  • Instruction synthesis: small lookup table combining type + modifier + name into English (e.g. "Turn right onto Main St", "Continue on Highway 5", "Arrive at destination"). Localization is out of scope.
  • Both providers covered in src/routing.test.ts with response fixtures.

Scope

  • src/routing.ts — extract provider-specific request building and response parsing; introduce a RoutingProvider interface (or two free functions behind a switch).
  • src/routing-osrm-instructions.ts (new) — instruction synthesis from maneuver.type + modifier + step.name.
  • src/routing.test.ts — parallel fixtures and tests for OSRM responses.
  • No UI changes. guidance.ts, the pill, and the costing chip keep using the same Route shape.

Out of scope

  • Automatic failover / health detection (separate follow-up issue).
  • Switching the default provider — one-line change after this lands.
  • Self-hosted Valhalla.
  • Instruction localization.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Important — fix this cycleenhancementNew feature or requestwebmap.devwebmap.dev app issues

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions