Skip to content

osm cockpit: rehydrate clicked features into their real shapes - #116

Merged
AdaWorldAPI merged 1 commit into
mainfrom
claude/q2-osm-map-reencoding-56p5e2
Aug 11, 2026
Merged

osm cockpit: rehydrate clicked features into their real shapes#116
AdaWorldAPI merged 1 commit into
mainfrom
claude/q2-osm-map-reencoding-56p5e2

Conversation

@AdaWorldAPI

@AdaWorldAPI AdaWorldAPI commented Aug 11, 2026

Copy link
Copy Markdown
Owner

The decode half of the .chains codec (companion to AdaWorldAPI/openstreetmap-website-rs#23): the bake already computed every way's z=32 vertex chain and dropped it after mean_cell — the sidecar un-discards it, and this side reads it back. Rehydration rules come from the same business logic that encoded the cells; the consumer never re-interprets bytes (encode and decode live in one module of osm-soa-bake).

What changed

  • osm_features.rsGET /api/osm/geometry/:idx: opens the .chains sidecar once (OnceLock), refuses it unless its slab_digest matches the mapped slab (geometry from one bake against another bake's identities is silent cross-bake corruption — the pin makes it loud), resolves row → identity ordinal → chain → lon/lat points. 404 when no chain is stored (nodes, relations) — never 200-with-empty.
  • osm.rs — an SVG shape layer inside #tiles (inherits the map transform); classFor(tags) fills water/building/wood/green rings and strokes highways; showShape wired into the existing click detail; vector-effect: non-scaling-stroke.
  • osm_slab_hydrate.rsARTIFACTS grows to berlin.chains: a deploy now hydrates all three artifacts (~1.42 GB cold; volume ≥ 2 GB). The bucket's SHA256SUMS gained its third line additively, so binaries reading only soa+books still verify.
  • Plan Phase 8 appended (claude-notes/plans/2026-08-08-osm-soa-cockpit-wiring.md).

Verified

  • cargo test --bin q2-cockpit: 90 passed, 0 failed, 3 ignored.
  • Browser, real Berlin bake, hermetic run: a genuine click on a harbour dot resolved "Westhafen I" (natural=water, water=harbour) and drew its shore ring filled — the See/Ufer model on a literal water body. Building ring (8 pts), landuse ring (48 pts), highway polyline drawn via the page's own showFeature; a node correctly 404s; zero page errors.
  • berlin.chains (63,777,240 B, sha256 276253f0…350aae) uploaded to the berlin-v1 S3 prefix; the bake is byte-deterministic (slab digest 8ec93a6ee63e89d2 across two runs), so the new sidecar pins to the already-published slab.

Gaps the POC now shows (by design — the POC is the falsifier)

  1. Shapes are click-only; the target is an area-fill base layer with the street/node overlay above it (bulk per-tile geometry + z-ordering missing).
  2. Multipolygon relations are unassembled (a lake's island hole is not subtracted) — a bake-side concern.
  3. Small rings are sub-pixel at overview zoom; a base layer wants the cascade-cell representative form there.

🤖 Generated with Claude Code

https://claude.ai/code/session_01NMeiLmtDKhomJNSo2ecbJw


Generated by Claude Code

Summary by CodeRabbit

  • New Features

    • Added geometry previews for selected OpenStreetMap features.
    • Water, buildings, land use, highways, and nodes now render as classified polygons, lines, or points.
    • Added an endpoint for retrieving decoded feature geometry.
    • Point features without available geometry now show a clear status message.
  • Documentation

    • Added documentation describing geometry rehydration and current rendering limitations, including bulk layers and multipolygon relations.

The decode half of the .chains codec (osm-soa-bake PR #23): the bake
already computed every way's z=32 vertex chain and dropped it after
mean_cell — the sidecar un-discards it, and this side reads it back.

- osm_features: GET /api/osm/geometry/:idx — open the .chains sidecar
  once (OnceLock), REFUSE it unless its slab_digest matches the mapped
  slab (cross-bake geometry against another bake's identities is the
  drift the pin exists to make loud), resolve row -> identity ordinal ->
  chain -> lon/lat points. 404 when no chain is stored (nodes,
  relations) — never 200-with-empty.
- osm page: an SVG shape layer inside #tiles (inherits the map
  transform); classFor(tags) fills water/building/wood/green rings and
  strokes highways; showShape wired into the existing click detail.
- osm_slab_hydrate: ARTIFACTS grows to berlin.chains — a deploy now
  hydrates all three artifacts (~1.42 GB cold; volume >= 2 GB). The
  bucket's SHA256SUMS gained the third line additively, so binaries
  reading only soa+books still verify.

Verified in a real browser against the real Berlin bake: a genuine
click on a harbour dot resolved "Westhafen I" (natural=water,
water=harbour) and drew its shore ring filled; building and landuse
rings and a highway polyline drawn via the page's own showFeature; a
node correctly 404s; zero page errors.

Gaps the POC now makes visible (by design — the POC is the falsifier):
shapes are click-only rather than a base fill layer with a node/street
overlay; multipolygon relations are unassembled; small rings are
sub-pixel at overview zoom. Recorded in the plan's Phase 8.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NMeiLmtDKhomJNSo2ecbJw
@coderabbitai

coderabbitai Bot commented Aug 11, 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: 5dc3cda3-b83d-4b7c-9be9-11dd147d6b94

📥 Commits

Reviewing files that changed from the base of the PR and between 45a7c91 and 6c14d8c.

📒 Files selected for processing (5)
  • claude-notes/plans/2026-08-08-osm-soa-cockpit-wiring.md
  • crates/cockpit-server/src/main.rs
  • crates/cockpit-server/src/osm.rs
  • crates/cockpit-server/src/osm_features.rs
  • crates/cockpit-server/src/osm_slab_hydrate.rs

📝 Walkthrough

Walkthrough

The change adds a validated .chains geometry sidecar, hydrates it with OSM artifacts, exposes decoded feature geometry through a new API route, and renders selected OSM shapes in the cockpit.

Changes

OSM geometry flow

Layer / File(s) Summary
Geometry artifact hydration
claude-notes/plans/..., crates/cockpit-server/src/osm_slab_hydrate.rs, crates/cockpit-server/src/osm_features.rs
The server requires the .chains sidecar, validates its slab digest, caches it, and updates hydration transfer reporting.
Geometry resolution endpoint
crates/cockpit-server/src/osm_features.rs, crates/cockpit-server/src/main.rs, claude-notes/plans/...
The new /api/osm/geometry/:idx route resolves and decodes feature chains and returns status-specific responses.
Selected feature SVG rendering
crates/cockpit-server/src/osm.rs, claude-notes/plans/...
Selected features render as styled polygons or polylines. Features without geometry display a point-feature message. Browser verification covers classified shapes and node behavior.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Browser
  participant cockpit-server
  participant OSM slab
  participant chains sidecar
  Browser->>cockpit-server: Request geometry for selected feature
  cockpit-server->>OSM slab: Validate feature row and identity
  cockpit-server->>chains sidecar: Resolve ordinal chain
  chains sidecar-->>cockpit-server: Return encoded vertex chain
  cockpit-server-->>Browser: Return decoded coordinates
  Browser->>Browser: Render polygon or polyline
Loading

Possibly related PRs

  • AdaWorldAPI/q2#108: Extends the earlier OSM feature endpoint, module, UI, and slab hydration flow.
  • AdaWorldAPI/q2#112: Extends the OSM artifact-loading flow with the .chains sidecar.

Suggested reviewers: claude

Poem

A rabbit hops through chains of bytes,
And maps the shapes in colored lights.
Water, roads, and buildings appear,
Point features leave their message clear.
The cockpit draws what data brings.

✨ 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 Aug 11, 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_be351fb9-becd-4ddb-869f-42eac801131d)

@AdaWorldAPI
AdaWorldAPI marked this pull request as ready for review August 11, 2026 15:17
@AdaWorldAPI
AdaWorldAPI merged commit 1e77792 into main Aug 11, 2026
4 of 5 checks passed
AdaWorldAPI pushed a commit that referenced this pull request Aug 11, 2026
The map was someone else's raster with our data sprinkled on top:
OSM_TILE_URL pointed at tile.openstreetmap.org and SAT_TILE_URL at Esri,
and the 1.42 GB Berlin bake supplied only the feature dots plus the shape
of the one feature you clicked. "The map pyramid and the slab's row key
are one and the same address" was true as arithmetic while every pixel
was rented — and a deployed host on the OSMF tile server also runs
against its usage policy.

The decode half already existed (.chains holds every way's z=32 vertex
chain; query_geometry reads one by row index). This serves them per
tile and draws them client-side in the SVG layer #116 added.

Server:
- ShapeClass + class_for_tags: the semantic category is derived from the
  tags the bake already stored and served per shape. Shipping every
  shape's tags would multiply the payload by the tag fan-out for data a
  viewer wants about one shape at a time. FeatureGeometryOut carries the
  same field, so the click path and the basemap share ONE classification
  in Rust — the JS classFor(tags) rule is deleted, not duplicated.
- simplify_cells: drops vertices landing on the same (sub)pixel, as an
  integer compare on the codec's own z32 grid — the display's resolution
  IS the tolerance, so there is no constant to tune. First/last always
  survive, so a ring that survives stays closed.
- geometry_row_budget + query_tile_geometry, reusing overview_sample so
  the cascade-cell selection keeps isolated features. Reports
  total/sampled/returned/malformed separately: a thin basemap must read
  as LOD, and a corrupt sidecar must not look like an empty
  neighbourhood.

Client:
- vector is the DEFAULT basemap; the raster skins stay behind the toggle
  as the reference to check our render against.
- The layer is retained across pan frames and rebuilt only on zoom. A
  pan changes the transform, not world-pixel coordinates, so rebuilding
  per frame would be the same n^2 append the drawnCells comment warns
  about.
- The tile-source readout names the source actually in use.

Mobile (bundled per operator request): the page was unusable on a phone.
grid-template-columns:1fr 320px left the map 70px wide on a 390px
viewport, and drag was bound to mousedown/mousemove with no touch-action,
so the map could not be panned by finger at all. Stacked layout under a
720px breakpoint, pointer events with a tracked pointerId, and
touch-action:none.

Measured before/after in headless Chromium against the shipped page, with
the bake endpoints stubbed (the container has no slab):

                  before                    after
  phone map       70x844  (18% of vp)       390x490 (100%)
  external req    141-277 to tile.osm.org   0
  touch pan       does not move the map     moves the map
  raster <img>    18-42                     0

Tests: 95 passed, 0 failed. The five new ones are two-sided —
class precedence is checked in both tag orders (a first-match
implementation returns Green for one of them), and simplification is
pinned at z4/z22/z23 so it tracks pixel_shift's real arithmetic rather
than "fewer points when zoomed out".

NOT verified against the real bake: this container lost the slab on
restart and AWS_S3_BUCKET_NAME is unset, so the render path over real
Berlin data is unverified until the deploy.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NMeiLmtDKhomJNSo2ecbJw
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