geo: typed Garmin IMG terrain pipeline — parser → classify → kurvenlineal → contours → ver-8 bake#91
Conversation
…he prototype
Ports scripts/garmin_proto.py — the validated Grand Canyon decoder — into a
dep-free, pure-std `geo/src/garmin/` module. Four stages, one submodule each:
- container.rs — the IMG container: XOR de-obfuscation, the 512-byte FAT from
0x600, and multi-part subfile reassembly (blocks concatenated in part*240+i
order) → named `name.TYPE` byte slices.
- tre.rs — the TRE: N/E/S/W int24 bbox, the LOD level pyramid, and the
subdivision tree (16-byte records, 14-byte on the most-detailed level).
- rgn.rs — the RGN: points / indexed-points / polylines / polygons, including
the LSB-first delta bitstream with QMapShack CShiftReg continuation +
sign semantics (the marker naive decoders drop, which turns fine levels into
random-walk mush — the plan's documented level-4 bug).
- lbl.rs — the LBL: 6-bit-packed and 8-bit-latin1 label text (road names,
elevation-labelled contours).
Public API in mod.rs: `Img::{parse,read,decode}`, `Feature`, `Kind`, `Decoded`,
plus shared bounds-checked little-endian byte readers.
Parity is proven, not asserted: an in-module test folds every decoded
coordinate through FNV-1a-64 and matches the Python golden
(0xadb368a3b063c74d) over all 120,174 features of the village tile 47505316,
with exact per-kind (line 114616 / poly 3185 / point 2373) and per-level
counts, a concrete first-feature check, and a decoded road name ("US Hwy 180").
Three more banked tiles decode to their exact prototype feature counts.
This replaces raster colour-guessing with typed lookups for the geo bake
pipeline (tasks #13-#16): every polygon/polyline carries a Garmin type code
(building / water / forest / street / path / contour) plus contour elevations.
geo is its own cargo workspace (isolated from the q2 workspace); clippy
(-D warnings) and rustfmt clean on the new files.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012jEwwaT5JZ5x8qWvcnaMYC
…he needle bug) The prior terrain bake seeded a fresh helix::CurveRuler per lattice cell (iceland_dem::ruler_phase → from_place(mix(floor(pos·detail)))). Adjacent cells drew uncorrelated phases, so the golden-spiral residue STEPPED discontinuously at every cell boundary. Harmless while it only tinted colour; pushed into geometry it IS the "needle field" — every cell its own isolated spike, never connected to its neighbour. That is the intra-family failure. This lifts the residue into a shared `kurvenlineal` module that samples the exact stride-4-over-17 CurveRuler value at the integer lattice CORNERS and smoothstep-interpolates between them (trilinear value-noise). The field is now C1-continuous ACROSS cell boundaries — inter-family: neighbouring cells share corner values, so relief flows smoothly to the next spike. It is NOT a gaussian blur — the field passes through the exact CurveRuler value at every lattice corner; only the cell interior is blended. Deterministic (phase is convention, not data — OGAR D-QUANTGATE). Test `inter_family_is_continuous_where_intra_family_jumps` proves the fix by reproducing the old per-cell construction alongside: the old residue's max adjacent step is > 0.3 (a seam jump); the new field's is < 0.05 (continuous). Plus determinism/range and pass-through-lattice-values tests. helix-gated (uses the real helix::CurveRuler, the same crate the sculpt tool + anatomy body use). clippy clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012jEwwaT5JZ5x8qWvcnaMYC
…uristics stage)
The "heuristics" stage of the bake pipeline: replaces raster colour-guessing
with a typed lookup. A Garmin polygon IS a building/water/forest and a polyline
IS a contour/street/stream, by its type code — no inference, no colour
thresholds.
GeoKind { Building, Water, Stream, Woods, Park, Street, Path, Contour, Other }
with a sweet /helix-look palette (water reads as water, vegetation as
vegetation, no muddy browns), a stable PALETTE index order for the ver-8 wire
palette block, is_area() (building/water/woods/park fill; rest are lines), and
classify(kind, type_code) over the OpenTopoMap/US-topo code family. Feature gains
geo_kind() and is_contour() (the contour lines are the elevation source for the
terrain heightfield).
Validated against the real village tile: the full-tile KIND histogram is asserted
exactly (Contour 62574, Street 32154, Stream 15161, Path 3600, Other 3664, Water
2255, Park 766; Building/Woods 0 — wilderness tile), plus a partition check that
every one of the 120174 features classifies to exactly one KIND. Dep-free,
clippy clean.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012jEwwaT5JZ5x8qWvcnaMYC
The "terrain" stage: labeled contour polylines → a connected grid heightfield. Method B — rasterize each contour polyline onto the W×H grid at its elevation, then fill the gaps by harmonic relaxation (SOR) with the contour cells held FIXED (Dirichlet BC). The contours stay razor-sharp; only the interior fills, so the surface is one connected sheet (grid neighbours = structural connectivity), never a field of isolated spikes. This is NOT gaussian smoothing — a blur would smear the contours; here they are immovable and relaxation only interpolates the unconstrained cells. The banked canyon data is ideal: 100% of contours are labeled (62573/62574), uniform 40-ft interval, distributed across TRE levels 2/3/4 (3690/8068/50816) — so LOD is the level pyramid for free: heightfield_for_level bakes one surface per level, the renderer swaps tier by camera distance. API: HeightField, grid_from_contours (domain-agnostic gridder), contours_for_level + heightfield_for_level (Garmin adapter: is_contour filter + LBL elevation, feet→m). Dep-free, clippy clean. Tests: (1) label parse feet/metres; (2) synthetic two-contour ramp interpolates linearly (mid ≈ 50, monotone); (3) real village-tile level-4 reconstruction — 50816 contours → a connected canyon: elevation stays in the real envelope (560..3158 m), >1500 m relief, and isolated needle spikes are <0.1% of cells (the discrete maximum principle the harmonic fill guarantees — the exact property the old per-cell needle bake violated). All finite, no NaN. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012jEwwaT5JZ5x8qWvcnaMYC
… pipeline)
Ties the typed pipeline together into a bakeable scene:
IMG → decode → contours → heightfield (terrain::heightfield_for_level)
→ landcover KIND overlay → ver-8 radix-grid wire.
terrain.rs additions:
- kind_grid(dec, bbox, w, h, overlay): rasterize selected features' GeoKind onto
the grid (areas first, lines on top); bare terrain stays Other for the shader's
hypsometric tint. LANDCOVER = [Water, Stream, Woods, Park] — roads/paths/
buildings are excluded so a wilderness scene reads as TERRAIN, not a street grid
(the "cluttered lines" look, avoided by construction). project() made pub.
garmin_bake bin (helix-gated, mirrors iceland_dem's projection/concept/encode):
level (TRE LOD tier, default 4), aspect-correct grid, equirectangular metric
projection about the tile centre, true-scale elevation, HHTL-keyed row concepts,
GeoKind palette, encode_grid_bso2. The rich look (hypsometric LUT, inter-family
kurvenlineal brightness, Gouraud, sunset, Ice/Ocean specular) is client-derived
in the shader from the stored height+kind — the ver-8 contract.
Verified end-to-end: baking the Grand Canyon village tile (47505316) →
844x1024 grid, elevation 561..3121 m (real river-to-rim envelope), 864256 verts /
1.72M tris / 2.64 MB ver-8 soa. Header inspected: BSO2 ver8, nC 1024, W 844,
H 1024. New kind_grid test asserts streams overlay while roads/paths do not and
terrain stays the majority. clippy + fmt clean.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012jEwwaT5JZ5x8qWvcnaMYC
|
Warning Review limit reached
Next review available in: 53 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (12)
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_7402a70b-26bd-4fec-892c-6b26741f8b65) |
Builds the ground-up typed geo pipeline that replaces raster colour-guessing: Garmin IMG maps carry a type code on every polygon/polyline (building / water / forest / street / path / contour) plus elevation-labelled contours, so the terrain is reconstructed from real data instead of inferred from pixels. Five stages, each dep-free pure-
stdand regression-locked against the validatedscripts/garmin_proto.pyprototype and the real banked canyon tiles.The stages
IMG parser (
geo/src/garmin/{container,tre,rgn,lbl,mod}.rs) — a byte-exact Rust port of the validated prototype. Container FAT + multi-part subfile reassembly → TRE (bbox, LOD level pyramid, subdivision tree) → RGN (points/lines/polys incl. the LSB-first delta bitstream with QMapShackCShiftRegcontinuation semantics — the part naive decoders drop) → LBL (6-bit + 8-bit labels). Parity is proven: an FNV-1a-64 fold over every decoded coordinate matches the Python golden0xadb368a3b063c74dacross all 120,174 features of the village tile, with exact per-kind counts and a decoded road name; three more tiles hit their exact feature counts.Typed classifier (
classify.rs) —(kind, type_code) → GeoKindwith a clean/helix-look palette. Validated: the full-tile KIND histogram is asserted exactly (Contour 62574, Street 32154, Stream 15161, …), plus a partition check that all 120,174 features classify to exactly one KIND.Inter-family kurvenlineal (
kurvenlineal.rs) — fixes the "needle field" bug at its root. The old bake seeded a freshhelix::CurveRulerper cell, so the golden-spiral residue stepped discontinuously at every cell boundary (isolated spikes). This lifts it into a continuous field: the exact stride-4-over-17 residue sampled at lattice corners, smoothstep-interpolated between them — C1-continuous across cells, not a gaussian blur (it passes through the exact residue at every corner). A test reproduces the old construction alongside and proves it: old max seam-step > 0.3, new < 0.05.Terrain reconstruction (
terrain.rs) — labeled contours → a connected grid heightfield. Rasterize each contour onto the grid at its elevation, then fill the gaps by harmonic relaxation (SOR) with the contour cells held fixed — the contours stay sharp, only the interior fills, so the surface is one connected sheet (never spikes), and it is not gaussian smoothing. LOD is the TRE level pyramid for free. Akind_gridoverlays natural landcover (water/rivers/forest) while excluding roads/paths so a wilderness scene reads as terrain, not a street grid. Tests: synthetic ramp interpolates linearly; real level-4 canyon reconstructs to 561–3158 m with <0.1% isolated needles (the discrete maximum principle the harmonic fill guarantees).Bake bin (
garmin_bake.rs, helix-gated) — ties it together into the BSO2 ver-8 radix-grid wire the cockpit/garmin/<location>route already decodes. The rich look (hypsometric LUT, kurvenlineal brightness, Gouraud, sunset, Ice/Ocean specular) is client-derived in the shader from the stored height + kind — the ver-8 contract.Verify
cargo test --lib(dep-free): 19 tests green — parser parity, classifier golden histogram, terrain reconstruction/connectivity, landcover overlay.cargo test --lib --features helix: 3 kurvenlineal tests green (continuity proof).garmin_bakeon the Grand Canyon village tile → 844×1024 grid, elevation 561–3121 m (real river-to-rim envelope), 864,256 verts / 1.72M tris / 2.64 MB ver-8 soa; header inspected (BSO2ver8, W 844, H 1024).geois its own cargo workspace (isolated from the q2 workspace), so pre-existing unformatted sibling files are left untouched.Not in this PR (follow-up)
The shader lighting (hypsometric/kurvenlineal/sunset/specular in
BodyHelix.tsx), staging a scene into thegarmin_scenesmanifest, and the visual-verification pass. This PR is the complete, tested Rust foundation those sit on.🤖 Generated with Claude Code
https://claude.ai/code/session_012jEwwaT5JZ5x8qWvcnaMYC
Generated by Claude Code