Builds on the merged A-only proposal. Broadens the capability: - breakdown now on routes AND activities; - Path 1 (sync) unchanged: BRouter waytags → breakdown at Planner save; - Path 2 (async): a `surface-backfill` pg-boss job map-matches geometry to OSM via Overpass for imports/uploads/older rows, stores the breakdown, and pushes a `surface_breakdown` SSE event so an open detail page fills in live; - privacy note: backfill sends geometry to Overpass → route via the proxy / self-hostable Overpass; documented in the manifest. Validates with `openspec validate --strict`. Specs only; no code. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2.8 KiB
2.8 KiB
Why
"How much of this is gravel?" is a top-three question for choosing a route, and
Komoot's surface/waytype bars are one of its most-used features.
@trails-cool/map-core already ships the surface/highway colour palettes, and
the Planner already computes accurate per-segment surface/highway from BRouter
(route-merge → EnrichedRoute.surfaces[]) — then discards it on save.
This change captures that data, shows it as proportion bars, and backfills
everything else (imports, uploads, older rows) asynchronously so the bars
aren't limited to Planner routes.
What Changes
- On route and activity detail, show surface and waytype proportion bars
(e.g. "62% asphalt · 28% gravel · 10% path"), coloured with the
map-coreSURFACE_COLORS/HIGHWAY_COLORSpalettes, with per-category km/percent. - Path 1 — synchronous (Planner routes): compute a distance-weighted surface + waytype distribution from the BRouter waytags at save time, send it in the planner→journal handoff, store it. Accurate, free, no external call.
- Path 2 — async backfill (everything else): for a route/activity that has geometry but no breakdown (imports, manual uploads, pre-existing rows), a background job map-matches the geometry to OSM ways via Overpass, computes the breakdown, stores it, and pushes an SSE event so an open detail page fills the bars in live.
- Degrade gracefully: no geometry / not-yet-backfilled → no bars (or a "computing…" state while a backfill is in flight).
Capabilities
New Capabilities
route-surface-breakdown: the surface/waytype proportion bars on route & activity detail — derived synchronously from BRouter waytags (Planner) or asynchronously via an Overpass backfill job with an SSE live-update.
Modified Capabilities
Impact
- Schema: nullable
surface_breakdownjsonb onjournal.routesandjournal.activities(additive —pnpm db:push). - Planner:
api.save-to-journalcomputes + sends the breakdown. - Journal:
- route callback stores the synchronous breakdown;
- a typed
surface-backfillpg-boss job (Overpass map-match → breakdown → store → SSE), enqueued after imports/uploads and available as a sweep; - a journal-side Overpass way/surface client (reusing the existing
/api/overpassproxy pattern); - an SSE
surface_breakdownevent + a detail-page subscription; - a
SurfaceBreakdowncomponent (reusesmap-corepalettes +stats.ts).
- i18n:
journal.surface.*(en + de). - Privacy: the backfill sends route geometry to Overpass — see design for the proxy/self-host + visibility considerations.