Replace drift-prone test/crate counts with shape descriptions in map/ROADMAP.md and three jouleclaw crate READMEs so the repo passes scripts/check-no-vanity-metrics.sh (the "describe the shape, not the size" rule). No content meaning lost — "green / zero warnings" signals retained. Co-Authored-By: Claude Opus 4.8 <[email protected]>
14 KiB
Map roadmap
Map is the geospatial cargo class of the Transaction Science substrate. The standard is the deliverable; maps.transaction.science is the demonstrator pillar that rides on the reference.
Done — v0.1.0 (the protocol spine)
- Specification core.
SPEC.md(RFC 2119) +spec/sub-specs: the 4D cell key, the tileset container, the manifest + revision chain, the receipt. map-schema— the 4D cell key, deterministic content addressing, the tile cargo, the Hilbert-sorted tileset directory + range planner, and the signed manifest + revision chain.map-cascade— the F0–F4 fidelity resolver with a guaranteed F1 base-floor fallback.map-receipt— the per-resolve JCR-1 receipt (det-CBOR + COSE_Sign1/EdDSA), byte-compatible with the family verifiers, with the enforced provenance coupling.map-conformance— C1–C5 + C-neg, all checks real (no SKIPs), with published deterministic vectors inconformance/v1/.- Workspace:
cargo testgreen,cargo clippy --all-targetszero warnings.
Done — the substrate port (from joulesperbit)
The reference scope's index, tiles, orbital, and routing crates have been ported
out of the existing engine (joulesperbit/crates/joule-*) into self-contained
map-* crates under map-rs/, retrofitted to the schema's cell key and content
addressing. All crates: cargo test green, cargo clippy --all-targets zero warnings.
| Target crate | Ported from | Status | Provides |
|---|---|---|---|
map-index |
joule-s2 |
done (v0.1) | Pure-Rust S2 geometry + a bridge that builds a CellKey from coordinates and covers any region (cap / rect / polygon) into the cell keys the directory range planner consumes. |
map-index (4D) |
joule-spatial3d |
done (v0.1) | The recommended volume scheme — logarithmic altitude bands (bathymetry → cislunar) + logarithmic time tiers (1 s → 100 yr) + SpatialKey + the Spatial3DIndex/PositionRingBuffer runtime utilities — plus bridge::{alt_band, time_bucket, cover_volume} that maps the scheme into CellKeys and covers a horizontal region × altitude range × time range. tests green, zero warnings. |
map-tiles |
joule-tiles |
done (v0.1) | Tile codecs behind the Tile cargo: MVT vector (F3), raster PNG/WebP (F2), terrain quantized-mesh (F1), 3D Tiles b3dm (F3) + a generic photoreal/splat wrapper (F4), with a tile bridge that wraps each as a content-addressed Tile at its tier. tests green, zero warnings. |
map-orbital |
joule-orbital |
done (v0.1) | TLE parsing + SGP4/SDP4 propagation + ECI/ECEF/geodetic transforms for the F3 live-satellite layer, with a bridge mapping a satellite sub-point to a CellKey (S2 cell + altitude band + time bucket). tests green, zero warnings. No correctness bug upstream — lint cleanups only. |
map-route |
joule-maps |
done (v0.1) | Local geocode / route (Dijkstra + A*) / geofence over a tileset (a capability, not a hosted service — SPEC §9), plus a feed-agnostic Feed/Entity adapter: any source (flights, vessels, satellites, CVEs, incidents, supply-chain nodes, enterprise signals) resolves to a CellKey. Energy metering dropped (that's map-receipt). tests green, zero warnings. |
Each remaining port adds: content-addressed, signed tiles (currently content-addressed only); receipt emission per operation; and conformance vectors for its payloads.
AltSchedulenote.map-index's recommended altitude scheme is logarithmic, butmap_schema::AltScheduleis currently linear ({base_m, width_m}). So a tileset using the log scheme can carry the rightAltBand(i16)indices, but the manifest can't yet describe the non-linear band edges. GeneralizingAltScheduleto an enum (linear | explicit edges) is a planned, contained pre-1.0 change.
map-tilesMVT note. The upstreamencode_polygonwas broken — it clobbered theCMD_MOVE_TOcommand word with a coordinate and reset the geometry cursor per ring (wrong for multi-ring polygons / holes); there was no polygon test. The port fixes the command emission and uses a continuous per-feature cursor, with a regression test. Backported tojoule-tiles.
map-indexpolygon note. The upstreampolygonmodule shipped broken (its containment and area routines fail their own tests injoule-s2) and never implementedS2Region, so a polygon could not be covered at all. The port fixes it: orientation-robust spherical winding-angle containment, van Oosterom–Strackee signed area, andimpl S2Region for S2Polygon. The coverer now covers an arbitrary boundary / airspace footprint / geofence — not just caps and rectangles. A polygon'smay_intersect_cellis conservative (its bounding cap); tightening it to per-edge intersection is a future contribution.
In progress — the demonstrator on the standard
The demonstrator (the maps.transaction.science pillar) lives in its own repo,
maps-transaction-science/, and consumes map-rs. It is a demonstration of the
standard, not part of the Apache-2.0 reference.
- Renderer to
wgpu— phase 1 done.map-renderis a freshwgpurenderer (replacing the original's hand-rolled JS-interop WebGPU, which was web-only). The globe renders write-once:cargo check/clippyclean on both native (--all-targets) andwasm32-unknown-unknown(--lib), zero warnings. Camera + icosphere ported; globe shader uses a procedural day/night fragment (the textured path is theF2raster tier, added with the tile pipeline). Native viewer (examples/native.rs, winit) + browser entry (#[wasm_bindgen(start)]). - Phase 2 — consume the standard in the renderer. Done.
- Entity layer: the renderer depends on
map-schema+map-indexand draws cell-addressed billboards —globe_position(cell)maps an S2 cell centre + altitude band to a world position;set_entities(&[(CellKey, color)])is the seam a feed drives. The native example wires the fullmap-route::feedchain (flight / vessel / ISS / CVE →Entity::cell_key→ globe point). - Tile path (2b): the globe texture is produced through the standard — a
raster is
map-tiles-encoded, wrapped as a content-addressedTile, resolved throughmap-cascade(closes atF2:Field), decoded, and uploaded. The native example prints themap-receiptJCR-1 wire for the resolve (tier + joules). A runtime test exercises the encode→resolve→decode path. - Feature layer (vector):
map-tilesgained the missing MVT decoder (decode_tile/decode_geometry), and the renderer draws anF3vector layer through the full path — a graticule is MVT-encoded, content-addressed, resolved through the cascade (closes atF3:Feature), MVT-decoded, un-projected, and drawn as globe lines (new line pipeline). - Multi-tile viewport coverage: the renderer covers a viewport (a cap around
the camera look-at) into cells via
map-index::cover_region, resolves each through the cascade (finest cells →F2:Field, coarser →F1:Basefallback), and shows them coloured by tier. The native example sums the per-tile receipts — SPEC §6's "a viewport's cost is the sum of its tile receipts" (e.g. 127 tiles: 17 Field + 110 Base → 3.51 mJ). - F0 cache: a content-addressed
LocalStore+CachedSourceshort-circuits the cascade — a cell already in the store closes atF0:Cache(picojoules) instead of re-resolving atF2+; a test shows the miss→F2→record→hit→F0transition and the ~1000× energy drop. Browser persistence is an OPFS backend (one content-addressed file;opfsfeature, compiles against the unstable-gated File System Access bindings). - Building extrusions (
F3): footprints are MVT-encoded as polygons with aheightproperty, resolved through the cascade, MVT-decoded (reusing the decoder), and extruded to wireframe boxes on the globe (Mapbox fill-extrusion style) — a faithful round-trip for 3D features without a glTF decoder. F4:Photoreal— 3D Gaussian splatting (SOTA, the original thesis): a real EWA-projected, depth-sorted 3DGS rasterizer. Each splat is stored the SOTA way — mean + per-axis scale + rotation quaternion + colour + opacity — and the 3×3 covarianceΣ = R S Sᵀ Rᵀis reconstructed on the GPU; the shader projects it to a 2D conic (Zwicker/Kerbl), expands an oriented billboard, and composites premultiplied back-to-front. The cloud round-trips through the standard atF4via themap-tilessplat codec (encode_splats/decode_splats— quantized fixed-point + log-scales + byte quat/colour, gzip; SPZ-style, the F4 codec the photoreal tier was missing). Demo cloud is a Gaussian shell of the planet; the data model, codec, covariance math, and rasterizer are real.- Degree-3 spherical-harmonic colour + storage-buffer pipeline: the codec
encodes degree-
nSH ((n+1)²−1quantized coefficients above DC); the renderer now holds splat data in a read-only storage buffer (flat layout, indexed byinstance_index) with a per-frame sorted-index buffer as the only view-dependent upload — so it carries full degree-3 SH (the 3DGS default, beyond vertex-attribute limits) and scales toward millions of splats. The shader evaluates the 15-coefficient SH by view direction;splat::sh_coloris its unit-tested CPU twin. - GPU depth sort: the per-frame back-to-front order is computed on the GPU by a
bitonic sorting network (compute passes: a keygen pass encodes order-
preserving depth keys, then one compare-exchange dispatch per network stage) —
the last CPU cost removed from the splat path. A bitonic network is chosen
because it is data-independent, so the WGSL kernel is a line-for-line port of
gpu_sort.rs, whose CPU twin is unit-tested against a reference sort; GPU dispatch validates clean on a real device. - Captured-scene ingest:
map_tiles::splat::read_plyingests the de-facto INRIA.ply3DGS interchange format (ASCII + binary-LE), inverting the trainer's storage conventions (log-scales, logit-opacity,(w,x,y,z)rotation,0.5 + C₀·f_dcDC, channel-majorf_restSH) into [Splat]s ready forencode_splats— so a real capture re-encodes into a conforming F4 tile. The demonstrator'ssplat::load_plyroutes a capture through the codec into the renderer. - glTF / b3dm decode:
map_tiles::tiles3d::read_b3dm/read_glbare the reader half of the 3D Tiles codec — they strip the b3dm header + feature/batch tables and decode the embedded binary glTF into a triangle mesh, general over accessors/bufferViews (f32 VEC3 positions; u8/u16/u32 indices; byte offsets + strides; multi-primitive merge). Round-tripsB3dmBuilder::encode.
- Entity layer: the renderer depends on
- Tile-serving core (
map-serve) — the transport-agnostic protocol logic of a tile server, with no HTTP-framework dependency: parse a tile request → resolve through the F0–F4 cascade → build a content-addressed response (strongETag,Cache-Control: immutable,If-None-Match→304,X-Map-Tier/ fell-back, and a JCR-1 energy-receipt header). Ships aMemoryStorereference store and a filesystemDirStore(content-addressed blobs on disk + a JSON index, reloaded onopenso a restart keeps serving —std-only); binding to a socket (axum/hyper/worker) is a thin caller-side wrapper. Pure + unit-tested (incl. a runnable doctest). - Routing engine (
map-route) — Dijkstra + A* path-finding now joined by turn-by-turn directions (directions::turn_by_turn: maneuvers from segment bearing deltas, straight-run merging, cardinal-heading departure, arrival) and isochrones (isochrone::isochrone: budget-bounded Dijkstra reachability by travel time or distance, ready to cover into cells), and multi-modal + traffic-aware costs (cost::find_route_withover aCostModel: per-mode access matrices + free-flow speeds for car/bike/foot, a congestion multiplier, and an admissible time-based A* heuristic), and public-transit routing (transit: the Connection Scan Algorithm over a GTFS-likeTimetable— earliest arrival with timetabled connections, walking-footpath transfers, and auto-generated transfers within a radius; reconstructs ride/walk legs). - HTTP binding (
map-serve-axum) — an axum/tokio wrapper:router(store, steward)routes every request throughserve, andrun(addr, …)is a one-call server. The async stack is confined to this crate, keepingmap-serveframework- free. Verified byoneshotintegration tests and a live-socket demo (examples/serve_demo) exercised over real HTTP (200 + receipt / 304 / 400 / 404). - Phase 3 — remaining layers (atmosphere, borders/coastlines, satellites/
aircraft/vessels, labels, routes) onto the same
Renderer. - OPFS tile cache. The F0 cache tier backed by an OPFS-resident slice of the tileset: check the local store, fall back to HTTP range, write back. Offline maps and instant revisits as the purest extension of "it's just a file" to the client.
- Capture → tileset pipeline. Reference tooling to publish a signed tileset (terrain, fields, features, splats) so a third party can light up a layer.
Later
- WASM build of
map-schema+map-cascadefor an in-browser coordinate playground at maps.transaction.science. - Cross-language conformance certifications (Python/Go) against
conformance/v1/. - A
map-timebinding for forecast/AIRAC revision streams as first-class dated archives.