Skip to content

geo + cockpit: OSM ⊕ Garmin drape — the road/trail/river network lifted onto the terrain surface#95

Merged
AdaWorldAPI merged 2 commits into
mainfrom
claude/geo-osm-drape
Jul 8, 2026
Merged

geo + cockpit: OSM ⊕ Garmin drape — the road/trail/river network lifted onto the terrain surface#95
AdaWorldAPI merged 2 commits into
mainfrom
claude/geo-osm-drape

Conversation

@AdaWorldAPI

@AdaWorldAPI AdaWorldAPI commented Jul 8, 2026

Copy link
Copy Markdown
Owner

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 / Stream polylines, 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 (cmp clean) — the drape rides alongside as a separate DRP1 sidecar, so it can only add.

Bake (geo)

  • garmin/drape.rsbuild_drape lifts every DRAPE_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 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 (proof in the module header + plan doc).
  • DRP1 wiremagic | ver | nLines | palette | scale | per-line: kind + i16 points. Points are i16 at 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.

Serve (cockpit-server)

  • New /api/garmin-drape/:location resolves a garmin_drapes manifest map (mirror of garmin_scenes); 404 = no drape → client renders bare terrain (the overlay is purely additive). resolve_garmin_map generalizes the scene resolver.

Render (GeoHelix.tsx)

  • 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.

Verify

  • cargo test geo 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_scene tests green (incl. the new drape_resolves_from_its_own_map_and_is_optional).
  • npx tsc --noEmit — clean.
  • Headless screenshot of /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 off returns the bare canyon. Co-registered, toggleable.

Follow-ups (own PRs, captured in the plan)

  • Iceland drape — same build_drape over the huge otm-iceland.img database (NET/NOD roads + typed features).
  • Iceland height dequantizeotm-iceland.img DEM subfiles as the height source (real elevation, kills the ver-8 F16-quantization needle field on /garmin/iceland).
  • Layer-toggle matrix — features-only / x-ray ontology (this PR ships fused ↔ Garmin-only) · label anchors (lift LBL place-names).

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

    • Added Garmin terrain draping support for vector lines such as roads, trails, and rivers.
    • Introduced a new overlay toggle in the viewer so draped features can be shown or hidden.
    • Added support for serving drape data separately from terrain scenes.
  • Bug Fixes

    • Improved scene loading so terrain can render even if drape data is still loading or unavailable.
    • Added validation and safer handling for invalid scene locations.

… 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
@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 70ae86af-0674-4cb9-956d-5032d3b29db0

📥 Commits

Reviewing files that changed from the base of the PR and between c75253a and 5d180f7.

⛔ Files ignored due to path filters (1)
  • cockpit/public/canyon.v8grid.drape.soa.gz is excluded by !**/*.gz
📒 Files selected for processing (7)
  • claude-notes/plans/2026-07-08-osm-garmin-drape.md
  • cockpit/public/body.manifest.json
  • cockpit/src/GeoHelix.tsx
  • crates/cockpit-server/src/main.rs
  • geo/src/bin/garmin_bake.rs
  • geo/src/garmin/drape.rs
  • geo/src/garmin/mod.rs

📝 Walkthrough

Walkthrough

Adds 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.

Changes

OSM ⊕ Garmin drape overlay

Layer / File(s) Summary
Drape geometry sampling and DRP1 encoding
geo/src/garmin/drape.rs, geo/src/garmin/mod.rs
Adds DrapeLine/DRAPE_KINDS, bilinear sample_pos, build_drape, and encode_drape DRP1 serialization with unit tests; exports the new drape submodule.
Bake pipeline integration
geo/src/bin/garmin_bake.rs
Builds and encodes the drape sidecar during baking and writes a third *.drape.soa output alongside existing terrain artifacts.
Server drape route and manifest resolution
crates/cockpit-server/src/main.rs
Adds GET /api/garmin-drape/:location, a shared resolve_garmin_map helper, resolve_garmin_drape, garmin_drape_handler with 404/503 handling, and corresponding tests/fixtures.
Manifest data and planning docs
cockpit/public/body.manifest.json, claude-notes/plans/2026-07-08-osm-garmin-drape.md
Adds garmin_drapes mapping and garmin_drape_note, plus documents the full pipeline design and work items.
GeoHelix client overlay rendering
cockpit/src/GeoHelix.tsx
Adds DrapeData/decodeDrape, lazy overlay geometry building in mount(), state/ref wiring, tick-loop toggling, unmount cleanup, and a "features on/off" UI button.

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
Loading

Possibly related PRs

  • AdaWorldAPI/q2#90: Extends the same shared manifest slug resolver in crates/cockpit-server/src/main.rs to add the new /api/garmin-drape/:location route alongside the existing Garmin scene route.
  • AdaWorldAPI/q2#92: Builds on the same GeoHelix fork behavior extended here with the DRP1 decoder, overlay mounting, and features toggle.

Poem

A rabbit hopped across the grid,
and draped a road where terrain hid,
DRP1 bytes now light the way,
roads and rivers on display,
toggle "features" — hop, and see! 🐇🗺️

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@cursor

cursor Bot commented Jul 8, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot 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)

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment thread cockpit/src/GeoHelix.tsx Outdated
Comment on lines +871 to +872
const dr = await fetch(`/api/garmin-drape/${scene.slice('garmin:'.length)}`);
if (dr.ok) x.drape = decodeDrape(await inflate(dr));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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
@AdaWorldAPI AdaWorldAPI merged commit 48bf521 into main Jul 8, 2026
3 of 4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants