Epic: M4 — BRouter as router #5
Labels
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
public/wanderer#5
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Goal: evaluate routing on BRouter (custom IGN + trail-running profiles) instead of Valhalla, unifying the two planners and improving trail-following.
feat/pluginRoutingbranch — is routing pluggable? (plugin vs overlay)M4 progress — Phases 1 & 2 shipped & deployed (2026-07-29)
Delivered the pluggable routing port and BRouter integration, deployed to prod (
wanderer-web:deploy-1124b306, homelab-flux PR #250;BROUTER_URL=http://brouter:17777wired). Verified the deployed BRouter proxy end-to-end:POST /api/v1/brouter/routefor a Mercantour pair → distanceM 4235, D+ 765, 258 pts, profilemercantour-run.Phase 1 — routing port + Valhalla adapter (behavior-preserving refactor):
RoutingPorthexagonal interface + domain types (web/src/lib/routing/port.ts):RouteRequest/RoutingPreferences/RouteResult/RouterCapabilities, per-engine escape hatch (backend.{valhalla,brouter}).ValhallaAdapterextracted fromvalhalla_store;calculateRouteBetweenroutes through the port with byte-identical output (characterization test).getRouter()factory + persistedbehavior.routersetting.Phase 2 — BRouter adapter + technicality + escape hatch:
/api/v1/brouter/route($lib/server/brouter.ts): validated param-substituted profile upload mechanism (inline URL params are ignored by BRouter) with hash-cache; typed 422unroutable.BRouterAdapter+translateBRouter(prefs)(SAC technicality →SAC_scale_preferred/limit;avoidRoads→path_preference;elevationBias→consider_elevation); escape hatch mergesbackend.brouter.params/rawProfileover translated prefs.Phase 3 — loop generator (remaining). De-risked (prototype
docs/prototypes/loopgen-brouter.pyvalidated: adaptive via-countn=max(3,round(dist/4000))reaches 20–25 km at <3% error with D+ steering). One prerequisite tracked: the proxy's profile-upload cache can mislabel a stale/evictedcustom_<id>as 422-unroutable — invalidate-and-retry (or TTL) before the generator stresses it with many uploads.Adjacent feature also shipped (same deploy): per-segment off-road (beeline) routing (Komoot-style) — sticky Track/Off-road toggle + Shift one-off + editable per segment + persisted in
trksegextensions + dashed rendering. Built on this port. Spec/plan indocs/superpowers/{specs,plans}/2026-07-29-per-segment-offroad-routing*.Spec/plan for the port + generator:
docs/superpowers/{specs,plans}/2026-07-29-routing-port-and-generator*. All on branchdeploy.Off-road (beeline) segments shipped + deployed (2026-07-29, deploy-8477efc9)
Per-segment off-road routing is live and user-verified: sticky Track/Off-road toggle, Shift+click one-off override, per-segment mode editing, persisted in
trksegextensions, dashed rendering. Off-road = straight beeline with elevation sampled along the line at 10 m (Valhalla/height).Post-deploy fixes from user testing: (1) disabled MapLibre
boxZoomso Shift+click reaches the editor; (2) densified the beeline before/height(was sampling only the 2 endpoints → linear ramp); (3) a mixed-mode "elevation chart jumps back in distance" symptom resolved once 2-point off-road segments were densified.Next: Phase 3 loop generator. Prereq being fixed first — the BRouter proxy profile-upload cache can reuse a stale
custom_<id>after a BRouter restart and mislabel it as 422-unroutable (the generator reuses one profile across ~40 calls, so it would fail wholesale).M4 COMPLETE — loop generator shipped + deployed (2026-07-29, deploy-5256c491)
Phase 3 (loop generator) done and live, completing the M4 "BRouter as router" epic end-to-end: routing port → Valhalla/BRouter adapters → technicality → per-segment off-road → loop generator.
web/src/lib/routing/generator.ts—generateLoops(start, target, prefs, port), router-agnostic (port-only), ports the validated prototype: via-countn=max(3,round(dist/4000)), radiusdist/2πcircle over 8 rotations, proportional-radius convergence, D+ steering by seed selection, degenerate-loop rejection (areaRatio<0.15), hard ~40-call budget, deterministic, best-effort (returns out-of-tolerance candidates + honest "X% off").loop_generator.svelte+ planner wiring — distance/D+/tolerance/technicality inputs, Generate (BRouter-backed), loading + soft-cancel, ranked candidates, map preview, and Accept (interleaved anchor placement → fully editable loop, undoable).custom_<id>on the live-verified HTTP-500 case; unroutable 400 → 422 unchanged and never retried).v1 limitations (tracked, non-blocking): generator has no
AbortSignal(soft-cancel only); Accept clears the undo stack (GPX-import precedent); pairwise re-route on Accept can differ slightly from the whole-loop preview.The M4 epic can be closed once you've exercised the generator in the browser.
Loop-generator roadmap COMPLETE (2026-08-03, deploy-fd08015b) — Features C/D/E all shipped
The full 8-item loop-generator enhancement roadmap is now shipped & deployed on top of M4. Beyond the routing port + BRouter + generator (Phases 1–3) and generator-v2 (direction bias, strict loops, planner UX), the three differentiators:
deploy-bfb298e4): force a generated loop through chosen trail waypoints + map-clicked points (spliced into the via-ring as fixed vias, distance still converges; accepted as editable anchors).deploy-2ec43660): draw a polygon on the map, mark prefer/avoid, soft candidate-scoring by fraction-of-loop-inside (ray-cast point-in-polygon). Introduced the reusable polyline-overlap primitive.deploy-fd08015b, THIS): bias loops by overlap with your own past tracks — novelty favors new ground, familiarity favors known trails; strength slider + source (completed/planned/both). Fetches your nearby tracks from the Meilitrailsindex (author + _geoRadius, precision-5 polyline decode) and scores overlap via the grid-proximity primitive. All these are capabilities Komoot/Garmin lack.Engineering notes: the generator stayed router- and data-agnostic throughout — it scores against provided geometries; all Meili coupling lives in a store. Each feature is additive (off = today's behavior byte-identical), deterministic, port-only. A live-Meili gotcha caught pre-ship: the trails index has
_geofilterable but NOT sortable (migration 1749831369), so a_geoPointsort 500s — removed; validated live (query-shape returns cleanly, the sorted variant 500s).Owed before heavy use: a browser smoke pass of the Familiarity/Area/Must-visit panels (all UI is type-gated only), and confirming the
authorfilter returns your own tracks under a real session.Specs/plans:
docs/superpowers/{specs,plans}/2026-07-30-*(C, E) +2026-08-03-*(D). All on branchdeploy.