# 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.