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]>
188 lines
14 KiB
Markdown
188 lines
14 KiB
Markdown
# 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.
|