Wire the map-core codec into route-data.ts so the Yjs session doc stores the computed route compactly, without changing the routing-host compute-once-and-share model. - Codec: add encodeElevations/decodeElevations (delta+varint) so geometry = encoded [lon,lat] polyline + elevation channel; precision bumped to 1e6 (~0.11 m) to preserve the router's 6-decimal output. - writeComputedRoute: geometry stored once (encoded polyline + elevations, no redundant geojson), road metadata run-length encoded. - Dual-format reads: getCoordinates / readRoadMetadata / new readGeojson transparently handle the new encoding AND legacy JSON docs (+ oldest geojson-only) — no migration, no flag day; legacy docs re-encode on the next recompute. use-elevation-data reads readGeojson (assembled for new docs). - Spec correction: a lossy compact codec can't be byte-identical to legacy (generateGpx emits raw precision), so "GPX byte-compatible" is corrected to "coordinates preserved within the router's ~0.1 m precision". Tests: legacy-format read, size assertion (>4x smaller / round-trips), elevation round-trip. map-core + planner typecheck/lint clean; full pnpm test 11/11. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2.4 KiB
2.4 KiB
1. Codec primitives
- 1.1 Add a framework-free codec in
@trails-cool/map-core(e.g.route-codec.ts):encodePolyline(coords: [lon,lat][]) -> string/decodePolyline(str) -> [lon,lat][]using fixed-precision (1e5) delta + zig-zag varint + base64, with a version tag - 1.2 Add
encodeRuns(values: string[]) -> string/decodeRuns(str, length?) -> string[]run-length codec for the road-metadata channels - 1.3 Unit tests: polyline round-trip within ~1 m over random + real fixtures; empty/single-point; RLE round-trip incl. all-same, all-distinct, and a realistic run pattern
2. Write path (new encoding)
- 2.1 In
route-data.tswriteComputedRoute: store geometry once asencodePolyline(coordinates)(drop the separategeojsonwrite); write eachROAD_METADATA_KEYSchannel viaencodeRuns; keepsegmentBoundaries+ profile - 2.2 Add a
readGeojson(routeData)helper that assembles the GeoJSON LineString from decoded coordinates (so callers that neededgeojsonno longer read a stored copy)
3. Read path (dual-format, backward compatible)
- 3.1 In
route-data.tsread helpers (readComputedRoute,readRoadMetadata, coordinate accessors): detect format per key (encoding version tag/sentinel → new decoder; elseJSON.parselegacy) and return the same decoded shapes as today - 3.2 Point every reader (SessionView map render, elevation profile, GPX export, save-to-journal, road-type/surface coloring) at the helpers; confirm none reads a raw
routeData.get("geojson"|"coordinates"|<metadata>)directly - 3.3 Tests: a legacy-format
routeDatadecodes correctly through the helpers; a new-format one decodes correctly; both yield identical reader-facing data
4. Verification
- 4.1 GPX-export golden test: export the same route via legacy and new encoding → byte-identical output
- 4.2 Size assertion on a realistic route fixture (e.g. the ~77 km / multi-waypoint case): new-encoded
Y.encodeStateAsUpdatesize is ≥~4× smaller than legacy and a small fraction ofMAX_DOC_BYTES - 4.3 Confirm routing-host election / BRouter usage unchanged (no client recompute);
pnpm --filter @trails-cool/planner --filter @trails-cool/map-core typecheck && lint && test, fullpnpm test - 4.4 Encode/decode + legacy-read + size covered by unit tests (route-codec, route-data); render/colors/export paths read through the same helpers and compile; live browser sanity rides the e2e suite (autonomous run)