Spike C: BRouter AIDL enrichment of a real komoot GPX #6

Open
opened 2026-08-31 17:14:35 +02:00 by robert · 0 comments
robert commented 2026-08-31 17:14:35 +02:00 (Migrated from git.butzei.de)

Goal

BRouter is the preferred offline cue-sheet backend. Confirm it can turn a komoot GPX (geometry only, no turn data) into usable turn instructions before Phase 3 depends on it.

Acceptance criteria

  • BRouter app plus segment files for the local region installed
  • IBRouterService bound successfully from a scratch Android app
  • A real komoot GPX export passed in as lonlats with a bike profile and turnInstructionMode set
  • Returned GPX inspected: voice hints (TL, TR, KR, ...) present and positioned at the correct junctions
  • Street names checked for availability
  • Behaviour recorded when segment files for the area are missing
  • Result written up: BRouter viable as primary, or fall back to map matching

Notes

AIDL definition: http://brouter.de/brouter/IBRouterService.aidl - docs at https://zod.github.io/brouter/developers/android_service.html . OsmAnd's BRouter adapter is a working reference implementation.

Update — 2026-09-01: BRouter is a router, not a map-matcher

This spike passed on "the returned GPX carries usable voice hints". That is necessary and nowhere
near sufficient, and 19 issues rest on it (D25).

Given via points, BRouter computes its own route between them, under its own profile. It does not
snap a track to OSM the way GraphHopper's or Valhalla's matching endpoints do. Three consequences:

  • Divergence. BRouter's line can differ from the imported line. The cue sheet then describes a
    route the rider is not on — the worst failure a navigation device has, because it is confidently
    wrong rather than obviously broken.
  • Profile refusal. komoot will happily route down a track or footpath that a bike profile
    declines. BRouter detours, silently, and the cues follow the detour.
  • Cost. A 100 km route at 5 m spacing is ~20 000 points. Whatever decimated subset becomes via
    points, BRouter routes once per leg under a running-time cap. NFR-P5 was filed as "needs a number";
    it is not a number problem, it is an unvalidated feasibility problem.

Added criteria:

  • Geometric divergence measured against the source GPX (discrete Fréchet or Hausdorff), on at
    least three real routes including one urban and one rural
  • Divergence rejection threshold of 20 m validated — beyond it, enrichment is discarded rather
    than shown
  • Via-point count / runtime / fidelity curve recorded for a 100 km route, so the decimation is
    chosen from evidence and NFR-P5 gets a real number
  • A route that deliberately uses a path the bike profile dislikes, to observe the refusal
    behaviour
  • BRouter's maxRunningTime / timeout behaviour on a long route recorded
  • Whether BRouter's reference-track mode is reachable over AIDL. If it is, that is the correct
    API and #27 changes shape entirely
  • Recommendation recorded: via-point decimation, or reference-track mode, or BRouter is not the
    right tool

The 20 m divergence check ships in BRouterEnricher regardless of what this spike finds. It is the
only thing between a bad enrichment and a rider being told to turn where there is no turn.

Labelled blocker. It gates twelve issues and was not marked.

Update — 2026-09-02: divergence is per leg, and the stakes are lower than they were

Per-leg rejection (D41). The 20 m threshold above reads as a whole-route check, and NFR-P8 stated
it as one. Applied that way, one bad 50 m stretch discards the cue sheet for a 100 km route and
drops the rider to the geometric heuristic for the entire ride — a worse outcome than the failure it
protects against.

  • Divergence measured per leg, between consecutive via points, not once across the route
  • Cues in legs within threshold are kept; cues in legs beyond it are dropped and the stretch
    marked unenriched, so the watch shows no turn rather than a wrong one
  • The failing-leg fraction beyond which an enrichment is rejected wholesale is established
    here — a route where most legs fail is not a good route with a few gaps

Lower stakes (D40, D42). Two things moved out from under this spike:

  • bikerouter.de is BRouter's own front end and exports GPX with turn instructions, so tier 0 (#63)
    reaches BRouter-quality cues with no BRouter install at all. This spike now decides the komoot
    path
    , not navigation as a whole.
  • Off-route guidance is a bearing arrow (#57), not a mid-ride re-route, so BRouter is no longer on
    the mid-ride critical path
    . Its blocked-by edges to the re-routing work can be dropped.

Keeps blocker: import-time enrichment for geometry-only routes still rests on it.

## Goal BRouter is the preferred offline cue-sheet backend. Confirm it can turn a komoot GPX (geometry only, no turn data) into usable turn instructions before Phase 3 depends on it. ## Acceptance criteria - [ ] BRouter app plus segment files for the local region installed - [ ] `IBRouterService` bound successfully from a scratch Android app - [ ] A real komoot GPX export passed in as `lonlats` with a bike profile and `turnInstructionMode` set - [ ] Returned GPX inspected: voice hints (`TL`, `TR`, `KR`, ...) present and positioned at the correct junctions - [ ] Street names checked for availability - [ ] Behaviour recorded when segment files for the area are missing - [ ] Result written up: BRouter viable as primary, or fall back to map matching ## Notes AIDL definition: http://brouter.de/brouter/IBRouterService.aidl - docs at https://zod.github.io/brouter/developers/android_service.html . OsmAnd's BRouter adapter is a working reference implementation. ## Update — 2026-09-01: BRouter is a router, not a map-matcher This spike passed on "the returned GPX carries usable voice hints". That is necessary and nowhere near sufficient, and 19 issues rest on it (D25). Given via points, BRouter computes **its own route between them, under its own profile**. It does not snap a track to OSM the way GraphHopper's or Valhalla's matching endpoints do. Three consequences: - **Divergence.** BRouter's line can differ from the imported line. The cue sheet then describes a route the rider is not on — the worst failure a navigation device has, because it is confidently wrong rather than obviously broken. - **Profile refusal.** komoot will happily route down a track or footpath that a bike profile declines. BRouter detours, silently, and the cues follow the detour. - **Cost.** A 100 km route at 5 m spacing is ~20 000 points. Whatever decimated subset becomes via points, BRouter routes once per leg under a running-time cap. NFR-P5 was filed as "needs a number"; it is not a number problem, it is an unvalidated feasibility problem. Added criteria: - [ ] **Geometric divergence measured** against the source GPX (discrete Fréchet or Hausdorff), on at least three real routes including one urban and one rural - [ ] Divergence **rejection threshold of 20 m** validated — beyond it, enrichment is discarded rather than shown - [ ] **Via-point count / runtime / fidelity curve** recorded for a 100 km route, so the decimation is chosen from evidence and NFR-P5 gets a real number - [ ] A route that deliberately uses a path the bike profile dislikes, to observe the refusal behaviour - [ ] BRouter's `maxRunningTime` / timeout behaviour on a long route recorded - [ ] **Whether BRouter's reference-track mode is reachable over AIDL.** If it is, that is the correct API and #27 changes shape entirely - [ ] Recommendation recorded: via-point decimation, or reference-track mode, or BRouter is not the right tool The 20 m divergence check ships in `BRouterEnricher` regardless of what this spike finds. It is the only thing between a bad enrichment and a rider being told to turn where there is no turn. **Labelled `blocker`.** It gates twelve issues and was not marked. ## Update — 2026-09-02: divergence is per leg, and the stakes are lower than they were **Per-leg rejection (D41).** The 20 m threshold above reads as a whole-route check, and NFR-P8 stated it as one. Applied that way, **one bad 50 m stretch discards the cue sheet for a 100 km route** and drops the rider to the geometric heuristic for the entire ride — a worse outcome than the failure it protects against. - [ ] Divergence measured **per leg**, between consecutive via points, not once across the route - [ ] Cues in legs within threshold are kept; cues in legs beyond it are dropped and the stretch marked unenriched, so the watch shows no turn rather than a wrong one - [ ] The **failing-leg fraction** beyond which an enrichment is rejected wholesale is established here — a route where most legs fail is not a good route with a few gaps **Lower stakes (D40, D42).** Two things moved out from under this spike: - bikerouter.de is BRouter's own front end and exports GPX with turn instructions, so tier 0 (#63) reaches BRouter-quality cues with no BRouter install at all. This spike now decides the **komoot path**, not navigation as a whole. - Off-route guidance is a bearing arrow (#57), not a mid-ride re-route, so **BRouter is no longer on the mid-ride critical path**. Its blocked-by edges to the re-routing work can be dropped. Keeps `blocker`: import-time enrichment for geometry-only routes still rests on it.
Sign in to join this conversation.
No description provided.