Commit the previously-untracked standard directories and catalog them in the
root README alongside proof:
- aac/ — communication cargo class: portable boards + a ten-level
offline-first cascade (Apache-2.0 code / CC-BY-4.0 spec).
- map/ — geospatial cargo class: range-requestable signed tilesets +
an F0–F4 fidelity cascade (Apache-2.0 code / CC-BY-4.0 spec).
- sandbox/— isolated, metered, attested execution across the heterogeneous
compute fabric (Apache-2.0 code / CC-BY-4.0 spec).
- jouleclaw-otel-authz, jouleclaw-trust — new jouleclaw reference crates.
Root README now lists aac, map, sandbox, and proof in the catalog, the tree,
and the contribution guide.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
124 lines
3.6 KiB
Rust
124 lines
3.6 KiB
Rust
//! Map tiles — the payload codecs behind the `Tile` cargo (SPEC §1.1).
|
|
//!
|
|
//! Each codec produces the opaque bytes of a [`map_schema::Tile`]; the
|
|
//! [`tile`] bridge wraps them as a content-addressed tile at the right cascade
|
|
//! tier (SPEC §1.2):
|
|
//!
|
|
//! - **MVT** — Mapbox Vector Tiles (roads, boundaries, labels) → `F3:Feature`
|
|
//! - **Raster** — PNG/WebP imagery and field tiles → `F2:Field`
|
|
//! - **Quantized mesh** — terrain elevation mesh → `F1:Base`
|
|
//! - **3D Tiles** — Cesium 3D Tiles buildings (`F3:Feature`) and a generic
|
|
//! photoreal/splat wrapper (`F4:Photoreal`)
|
|
//!
|
|
//! Ported from `joule-tiles`. The codecs are self-contained (no S2/geo or
|
|
//! routing dependency); metering lives in `map-receipt`, not here.
|
|
|
|
pub mod mvt;
|
|
pub mod raster;
|
|
pub mod splat;
|
|
pub mod tiles3d;
|
|
pub mod tile;
|
|
|
|
use thiserror::Error;
|
|
|
|
/// Errors from tile encoding/decoding operations.
|
|
#[derive(Debug, Error)]
|
|
pub enum TileError {
|
|
#[error("geometry encoding error: {0}")]
|
|
GeometryError(String),
|
|
|
|
#[error("protobuf encoding error: {0}")]
|
|
ProtobufError(String),
|
|
|
|
#[error("image error: {0}")]
|
|
ImageError(String),
|
|
|
|
#[error("invalid tile coordinate: z={z}, x={x}, y={y}")]
|
|
InvalidTileCoord { z: u8, x: u32, y: u32 },
|
|
|
|
#[error("tile not found")]
|
|
NotFound,
|
|
|
|
#[error("compression error: {0}")]
|
|
CompressionError(String),
|
|
}
|
|
|
|
/// Standard tile coordinate (slippy map convention).
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
|
|
pub struct TileCoord {
|
|
pub z: u8,
|
|
pub x: u32,
|
|
pub y: u32,
|
|
}
|
|
|
|
impl TileCoord {
|
|
/// Create a new tile coordinate.
|
|
pub fn new(z: u8, x: u32, y: u32) -> Result<Self, TileError> {
|
|
let max = 1u32 << z;
|
|
if x >= max || y >= max {
|
|
return Err(TileError::InvalidTileCoord { z, x, y });
|
|
}
|
|
Ok(Self { z, x, y })
|
|
}
|
|
|
|
/// Bounding box of this tile in lat/lon degrees.
|
|
pub fn bounds(&self) -> (f64, f64, f64, f64) {
|
|
let n = (1u32 << self.z) as f64;
|
|
let lon_min = self.x as f64 / n * 360.0 - 180.0;
|
|
let lon_max = (self.x + 1) as f64 / n * 360.0 - 180.0;
|
|
|
|
let lat_max = (std::f64::consts::PI * (1.0 - 2.0 * self.y as f64 / n))
|
|
.sinh()
|
|
.atan()
|
|
.to_degrees();
|
|
let lat_min = (std::f64::consts::PI * (1.0 - 2.0 * (self.y + 1) as f64 / n))
|
|
.sinh()
|
|
.atan()
|
|
.to_degrees();
|
|
|
|
(lat_min, lon_min, lat_max, lon_max)
|
|
}
|
|
|
|
/// Tile coordinate from lat/lon at a given zoom level.
|
|
pub fn from_lat_lon(lat: f64, lon: f64, z: u8) -> Self {
|
|
let n = (1u32 << z) as f64;
|
|
let x = ((lon + 180.0) / 360.0 * n).floor() as u32;
|
|
let lat_rad = lat.to_radians();
|
|
let y = ((1.0 - lat_rad.tan().asinh() / std::f64::consts::PI) / 2.0 * n).floor() as u32;
|
|
let max = (1u32 << z) - 1;
|
|
Self {
|
|
z,
|
|
x: x.min(max),
|
|
y: y.min(max),
|
|
}
|
|
}
|
|
|
|
/// Parent tile (one zoom level up).
|
|
pub fn parent(&self) -> Option<Self> {
|
|
if self.z == 0 {
|
|
return None;
|
|
}
|
|
Some(Self {
|
|
z: self.z - 1,
|
|
x: self.x / 2,
|
|
y: self.y / 2,
|
|
})
|
|
}
|
|
|
|
/// Four children (one zoom level down).
|
|
pub fn children(&self) -> [Self; 4] {
|
|
let z = self.z + 1;
|
|
let x = self.x * 2;
|
|
let y = self.y * 2;
|
|
[
|
|
Self { z, x, y },
|
|
Self { z, x: x + 1, y },
|
|
Self { z, x, y: y + 1 },
|
|
Self {
|
|
z,
|
|
x: x + 1,
|
|
y: y + 1,
|
|
},
|
|
]
|
|
}
|
|
}
|