geo + cockpit: OSM ⊕ Garmin drape — the road/trail/river network lifted onto the terrain surface#95
Conversation
… onto the terrain surface The "OSM ⊕ Garmin" fusion, canyon-first. Design spine: Garmin owns height + terrain material; the typed vector features own the semantic layer; HHTL owns alignment; the renderer owns fusion. The Grand Canyon Garmin IMG already carries typed Street/Path/Stream polylines, so the semantic network is decoded data, not colour-guessing — this lifts it onto the SAME ver-8 terrain surface as a toggleable overlay. The proven ver-8 terrain wire + decoder are untouched (the rebaked terrain is byte-identical to the deployed canyon.v8grid.soa.gz); the drape rides alongside as a separate DRP1 sidecar. Bake (geo): - drape.rs — build_drape lifts every line feature of DRAPE_KINDS (Street/Path/ Stream) at the terrain's LOD level onto the surface: each (lon,lat) vertex is projected to fractional grid (col,row) via the SAME terrain::project, then the display-frame surface point is bilinear-sampled from the ver-8 bake's own `pos` grid — so the polylines are co-registered with the grid by construction. - encode_drape — the DRP1 wire: magic | ver | nLines | palette | scale | per-line kind + i16-quantized points (scale 16384, sub-cell exact, half the f32 payload). Colours = GeoKind::PALETTE, matching the terrain KIND palette. - garmin_bake.rs emits <stem>.drape.soa beside the ver-8 grid. Canyon: 29,235 lines (Stream 11,629 / Street 15,860 / Path 1,746), 1.3 MB gz. - 2 tests: canyon level-4 lifts roads+rivers onto the surface (finite, on-surface, spans the relief); DRP1 byte-exact header. geo lib: 22 green. Serve (cockpit-server): - /api/garmin-drape/:location resolves a `garmin_drapes` manifest map (mirror of garmin_scenes); 404 = no drape (client renders bare terrain — additive). - resolve_garmin_map generalizes the scene resolver; new test asserts the drape map is independent + optional + traversal-safe. Render (GeoHelix): - decodeDrape parses DRP1 into segment-paired vertices + per-vertex KIND colour; fetched in parallel for garmin terrain scenes and attached to Decoded.drape. - A LineSegments overlay lifts y by the identical uExag the terrain shader applies (dpos.y = position.y * uExag) plus a hair of offset, so the lines ride on the surface. A `features` toggle (shown only when a drape is present) flips fused ↔ Garmin-only. tsc --noEmit clean. Verified by headless screenshot: the blue dendritic river network threads the canyon valleys and grey roads trace the ridges, all reading ON the 3D surface; `features off` returns the bare terrain. Plan: claude-notes/plans/2026-07-08-osm-garmin-drape.md. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012jEwwaT5JZ5x8qWvcnaMYC
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (7)
📝 WalkthroughWalkthroughAdds an "OSM ⊕ Garmin drape" feature: a Rust bake/encode stage lifts OSM line features onto Garmin terrain into a DRP1 sidecar file, a new server endpoint and manifest entry expose it, and the GeoHelix client decodes and renders it as a toggleable overlay, alongside planning documentation. ChangesOSM ⊕ Garmin drape overlay
Estimated code review effort: 4 (Complex) | ~60 minutes Sequence Diagram(s)sequenceDiagram
participant GeoHelix
participant Server
participant Manifest
participant decodeDrape
participant mount
GeoHelix->>Server: GET /api/garmin-drape/:location
Server->>Manifest: resolve_garmin_drape(location)
Manifest-->>Server: drape filename or None
Server-->>GeoHelix: DRP1 gzip bytes or 404/503
GeoHelix->>decodeDrape: decode DRP1 buffer
decodeDrape-->>GeoHelix: positions/colors buffers
GeoHelix->>mount: build lifted LineSegments overlay
mount-->>GeoHelix: overlay added to scene, toggleable via features flag
Possibly related PRs
Poem
✨ Finishing Touches📝 Generate docstrings
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: serverGenReqId_f6793a9a-7cc0-4ce4-9fb6-db2b8debc1d0) |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 5f5f94cb17
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| const dr = await fetch(`/api/garmin-drape/${scene.slice('garmin:'.length)}`); | ||
| if (dr.ok) x.drape = decodeDrape(await inflate(dr)); |
There was a problem hiding this comment.
Avoid blocking terrain render on optional drape fetch
For Garmin scenes that have a drape registered, this awaited request/decompression runs after fetchSoa() and before setD(x), so /garmin/grand-canyon stays in the loading state until the optional 1.3 MB overlay finishes or the browser gives up. If /api/garmin-drape/... is slow, users see a blank page even though the base terrain is already available; start the drape request in parallel or render the terrain first and attach the overlay when it arrives.
Useful? React with 👍 / 👎.
… the terrain render Codex P2: the drape fetch/inflate was awaited between fetchSoa() and setD(x), so a garmin terrain scene stayed in the loading state until the optional 1.3 MB overlay finished (or the browser gave up) — a slow /api/garmin-drape left the page blank even though the base terrain was already decoded. Now the terrain renders as soon as it decodes (setD(x) is no longer gated on the drape), and the drape is fetched INDEPENDENTLY into a ref. mount builds the LineSegments overlay lazily the first frame the ref is populated (checked in the render tick, woken by a dirty flag), so the overlay pops in when its fetch lands with no remount and no terrain delay. The `features` toggle is revealed by a `hasDrape` state set when the drape arrives. A 404 or slow fetch is now purely additive latency on the overlay alone. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012jEwwaT5JZ5x8qWvcnaMYC
The OSM ⊕ Garmin drape — the semantic vector network (roads / trails / rivers) lifted onto the ver-8 terrain surface, canyon-first. Screenshot-verified end-to-end.
Design spine (operator-endorsed)
Garmin owns height + terrain material · the typed vector features own the semantic layer · HHTL owns alignment · the renderer owns fusion. Think surfaces, not "terrain". The Grand Canyon Garmin IMG already carries typed
Street/Path/Streampolylines, so the network is decoded data, not colour-guessing — this drapes it onto the same surface as a toggleable overlay.Zero risk to the working terrain
The proven ver-8 grid wire + decoder are untouched. The rebaked terrain is byte-identical to the deployed
canyon.v8grid.soa.gz(cmpclean) — the drape rides alongside as a separateDRP1sidecar, so it can only add.Bake (
geo)garmin/drape.rs—build_drapelifts everyDRAPE_KINDS(Street/Path/Stream) line feature at the terrain's LOD level onto the surface: each(lon,lat)vertex is projected to fractional grid(col,row)via the sameterrain::project, then the display-frame surface point is bilinear-sampled from the ver-8 bake's ownposgrid — so the polylines are co-registered with the grid by construction (proof in the module header + plan doc).magic | ver | nLines | palette | scale | per-line: kind + i16 points. Points arei16at scale 16384 (sub-cell exact, half the f32 payload); colours =GeoKind::PALETTE, matching the terrain KIND palette.garmin_bake.rsemits<stem>.drape.soabeside the ver-8 grid. Canyon: 29,235 lines (Stream 11,629 · Street 15,860 · Path 1,746), 1.3 MB gz.Serve (
cockpit-server)/api/garmin-drape/:locationresolves agarmin_drapesmanifest map (mirror ofgarmin_scenes); 404 = no drape → client renders bare terrain (the overlay is purely additive).resolve_garmin_mapgeneralizes the scene resolver.Render (
GeoHelix.tsx)decodeDrapeparses DRP1 into segment-paired vertices + per-vertex KIND colour; fetched in parallel for garmin terrain scenes and attached toDecoded.drape.LineSegmentsoverlay liftsyby the identicaluExagthe terrain shader applies (dpos.y = position.y * uExag) plus a hair of offset, so the lines ride on the surface. Afeaturestoggle (shown only when a drape is present) flips fused ↔ Garmin-only.Verify
cargo testgeo lib — 22 green (2 new drape tests: level-4 lifts roads+rivers onto the surface / DRP1 byte-exact header; terrain regressions unchanged).cargo test -p cockpit-server— compiled +garmin_scenetests green (incl. the newdrape_resolves_from_its_own_map_and_is_optional).npx tsc --noEmit— clean./garmin/grand-canyon: the blue dendritic river network threads the canyon valleys and grey roads trace the ridges, all reading ON the 3D surface;features offreturns the bare canyon. Co-registered, toggleable.Follow-ups (own PRs, captured in the plan)
build_drapeover the hugeotm-iceland.imgdatabase (NET/NOD roads + typed features).otm-iceland.imgDEM subfiles as the height source (real elevation, kills the ver-8 F16-quantization needle field on/garmin/iceland).Plan:
claude-notes/plans/2026-07-08-osm-garmin-drape.md.🤖 Generated with Claude Code
https://claude.ai/code/session_012jEwwaT5JZ5x8qWvcnaMYC
Generated by Claude Code
Summary by CodeRabbit
New Features
Bug Fixes