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

188 lines
14 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Map roadmap
Map is the geospatial cargo class of the Transaction Science substrate. The
standard is the deliverable; [maps.transaction.science](https://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 `CellKey`s 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.