trails/openspec/changes/route-surface-breakdown/tasks.md
Ullrich Schäfer 47f185598d
openspec: propose route-surface-breakdown
Surface + waytype proportion bars on route detail, from the BRouter waytags the
Planner already computes (and currently discards on save). Design records the
data-source decision: persist a compact distance-weighted breakdown for
Planner-created routes (accurate, no external call); Overpass map-matching for
imports/uploads is parked as a future option.

Validates with `openspec validate --strict`. Specs only; no code.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-14 17:13:44 +02:00

1.8 KiB

1. Breakdown derivation (shared, pure)

  • 1.1 Add a pure surfaceBreakdown(coordinates, surfaces, highways) helper (in @trails-cool/map-core or a planner lib): sum haversine segment lengths into per-surface and per-waytype buckets → { surface: Record<string, number>, highway: Record<string, number> } (metres). Unit-test it.
  • 1.2 Define the shared wire shape (Zod) for the breakdown in @trails-cool/api (SurfaceBreakdownSchema, all values ≥ 0).

2. Capture at save (Planner → Journal)

  • 2.1 In apps/planner/.../api.save-to-journal, read the session EnrichedRoute (coordinates + surfaces + highways), compute the breakdown, and add surfaceBreakdown to the POST body.
  • 2.2 Journal callback (api.routes.$id.callback) validates the optional surfaceBreakdown and persists it; re-save overwrites.

3. Schema

  • 3.1 Add nullable surfaceBreakdown jsonb to journal.routes; pnpm db:push.

4. Render

  • 4.1 Route detail loader exposes surfaceBreakdown.
  • 4.2 SurfaceBreakdown component: stacked proportion bar per dimension (surface, waytype), segments coloured via SURFACE_COLORS/HIGHWAY_COLORS (DEFAULT for unknown), legend with category · km · % (largest first), unknown → "other"; hidden when empty. Mount on routes.$id.tsx.
  • 4.3 i18n journal.surface.* (label + category names + "other") en + de.

5. Tests & checks

  • 5.1 Unit: surfaceBreakdown (distance weighting, multiple surfaces, unknown bucket).
  • 5.2 Component (jsdom): bars render with correct proportions; "other" for unknowns; hidden when empty.
  • 5.3 E2E: a Planner-saved route with waytags shows the breakdown bars on its detail page (or assert via a seeded route if the planner round-trip is too heavy for e2e).
  • 5.4 pnpm typecheck && pnpm lint && pnpm test && pnpm test:e2e green.