open-standards/map/ROADMAP.md
Transaction Science 08faf05ff0 docs: drop vanity test-counts (no-vanity-metrics policy)
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]>
2026-06-07 19:00:28 -04:00

14 KiB
Raw Permalink Blame History

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 in conformance/v1/.
  • Workspace: cargo test green, cargo clippy --all-targets zero 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.

AltSchedule note. map-index's recommended altitude scheme is logarithmic, but map_schema::AltSchedule is currently linear ({base_m, width_m}). So a tileset using the log scheme can carry the right AltBand(i16) indices, but the manifest can't yet describe the non-linear band edges. Generalizing AltSchedule to an enum (linear | explicit edges) is a planned, contained pre-1.0 change.

map-tiles MVT note. The upstream encode_polygon was broken — it clobbered the CMD_MOVE_TO command 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 to joule-tiles.

map-index polygon note. The upstream polygon module shipped broken (its containment and area routines fail their own tests in joule-s2) and never implemented S2Region, so a polygon could not be covered at all. The port fixes it: orientation-robust spherical winding-angle containment, van Oosterom–Strackee signed area, and impl S2Region for S2Polygon. The coverer now covers an arbitrary boundary / airspace footprint / geofence — not just caps and rectangles. A polygon's may_intersect_cell is 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-render is a fresh wgpu renderer (replacing the original's hand-rolled JS-interop WebGPU, which was web-only). The globe renders write-once: cargo check/clippy clean on both native (--all-targets) and wasm32-unknown-unknown (--lib), zero warnings. Camera + icosphere ported; globe shader uses a procedural day/night fragment (the textured path is the F2 raster 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-index and 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 full map-route::feed chain (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-addressed Tile, resolved through map-cascade (closes at F2:Field), decoded, and uploaded. The native example prints the map-receipt JCR-1 wire for the resolve (tier + joules). A runtime test exercises the encode→resolve→decode path.
    • Feature layer (vector): map-tiles gained the missing MVT decoder (decode_tile / decode_geometry), and the renderer draws an F3 vector layer through the full path — a graticule is MVT-encoded, content-addressed, resolved through the cascade (closes at F3: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:Base fallback), 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 + CachedSource short-circuits the cascade — a cell already in the store closes at F0:Cache (picojoules) instead of re-resolving at F2+; a test shows the miss→F2→record→hit→F0 transition and the ~1000× energy drop. Browser persistence is an OPFS backend (one content-addressed file; opfs feature, compiles against the unstable-gated File System Access bindings).
    • Building extrusions (F3): footprints are MVT-encoded as polygons with a height property, 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 at F4 via the map-tiles splat 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-n SH ((n+1)²−1 quantized coefficients above DC); the renderer now holds splat data in a read-only storage buffer (flat layout, indexed by instance_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_color is 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_ply ingests the de-facto INRIA .ply 3DGS interchange format (ASCII + binary-LE), inverting the trainer's storage conventions (log-scales, logit-opacity, (w,x,y,z) rotation, 0.5 + C₀·f_dc DC, channel-major f_rest SH) into [Splat]s ready for encode_splats — so a real capture re-encodes into a conforming F4 tile. The demonstrator's splat::load_ply routes a capture through the codec into the renderer.
    • glTF / b3dm decode: map_tiles::tiles3d::read_b3dm / read_glb are 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-trips B3dmBuilder::encode.
  • 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 (strong ETag, Cache-Control: immutable, If-None-Match → 304, X-Map-Tier / fell-back, and a JCR-1 energy-receipt header). Ships a MemoryStore reference store and a filesystem DirStore (content-addressed blobs on disk + a JSON index, reloaded on open so 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_with over a CostModel: 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-like Timetable — 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 through serve, and run(addr, …) is a one-call server. The async stack is confined to this crate, keeping map-serve framework- free. Verified by oneshot integration 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-cascade for an in-browser coordinate playground at maps.transaction.science.
  • Cross-language conformance certifications (Python/Go) against conformance/v1/.
  • A map-time binding for forecast/AIRAC revision streams as first-class dated archives.