//! Bridge from the codecs to the `Tile` cargo (SPEC §1.1, §1.2). //! //! Each function wraps a codec's encoded bytes as a content-addressed //! [`map_schema::Tile`] at the cascade [`Tier`] that payload serves, with a //! stable `codec` tag. A reader resolves the tile, verifies its content id, and //! the cascade knows which tier it closed (SPEC §1.2). use map_schema::{CellKey, Tier, Tile}; use crate::raster::{RasterFormat, RasterTile}; /// Codec tag strings (the `Tile::codec` field). Stable identifiers a reader /// dispatches a decoder on. pub mod codec { pub const MVT: &str = "mvt"; pub const MVT_GZIP: &str = "mvt+gzip"; pub const PNG: &str = "png"; pub const WEBP: &str = "webp"; pub const QUANTIZED_MESH: &str = "quantized-mesh"; pub const B3DM: &str = "b3dm"; } /// Wrap MVT vector bytes as an `F3:Feature` tile (roads, boundaries, labels). pub fn vector_tile(key: CellKey, mvt_bytes: Vec, gzipped: bool) -> Tile { Tile { key, tier: Tier::Feature, codec: if gzipped { codec::MVT_GZIP } else { codec::MVT }.to_string(), data: mvt_bytes, } } /// Wrap a raster (imagery / scalar field) tile as an `F2:Field` tile. pub fn raster_tile(key: CellKey, raster: &RasterTile) -> Tile { let codec = match raster.format { RasterFormat::Png => codec::PNG, RasterFormat::WebP => codec::WEBP, }; Tile { key, tier: Tier::Field, codec: codec.to_string(), data: raster.data.clone() } } /// Wrap a terrain quantized-mesh payload as the `F1:Base` floor tile. pub fn terrain_tile(key: CellKey, quantized_mesh: Vec) -> Tile { Tile { key, tier: Tier::Base, codec: codec::QUANTIZED_MESH.to_string(), data: quantized_mesh, } } /// Wrap a Cesium 3D Tiles b3dm (buildings) payload as an `F3:Feature` tile. pub fn buildings_tile(key: CellKey, b3dm_bytes: Vec) -> Tile { Tile { key, tier: Tier::Feature, codec: codec::B3DM.to_string(), data: b3dm_bytes } } /// Wrap a photoreal payload (3D Gaussian splats — KHR_gaussian_splatting / SOG) /// as the `F4:Photoreal` escape-hatch tile. The `codec` names the splat /// container; Map does not yet ship a splat encoder, so the bytes are supplied /// by the publisher's capture pipeline. pub fn photoreal_tile(key: CellKey, codec: &str, bytes: Vec) -> Tile { Tile { key, tier: Tier::Photoreal, codec: codec.to_string(), data: bytes } } #[cfg(test)] mod tests { use super::*; use crate::mvt::{self, Layer}; use crate::raster::{solid_tile, QuantizedMeshTile, RasterFormat}; use crate::TileCoord; use map_schema::{AltBand, Lod, S2CellId, TimeBucket}; fn key() -> CellKey { CellKey { s2: S2CellId(0x42), alt: AltBand(0), time: TimeBucket::ATEMPORAL, lod: Lod(0) } } #[test] fn raster_wraps_to_field_tier() { let coord = TileCoord::new(1, 0, 0).unwrap(); let rt = solid_tile(coord, [0, 100, 200, 255], RasterFormat::Png).unwrap(); let tile = raster_tile(key(), &rt); assert_eq!(tile.tier, Tier::Field); assert_eq!(tile.codec, "png"); assert!(tile.content_id().is_ok()); } #[test] fn vector_wraps_to_feature_tier() { let bytes = mvt::encode_tile(&[Layer::new("roads")]).unwrap(); let tile = vector_tile(key(), bytes, false); assert_eq!(tile.tier, Tier::Feature); assert_eq!(tile.codec, "mvt"); } #[test] fn terrain_wraps_to_base_floor() { let coord = TileCoord::new(2, 1, 1).unwrap(); let mesh = QuantizedMeshTile::from_elevation(coord, &[0.0; 9], 3).encode(); let tile = terrain_tile(key(), mesh); assert_eq!(tile.tier, Tier::Base); assert_eq!(tile.codec, "quantized-mesh"); } #[test] fn photoreal_wraps_to_escape_hatch() { let tile = photoreal_tile(key(), "sog", vec![1, 2, 3, 4]); assert_eq!(tile.tier, Tier::Photoreal); assert_eq!(tile.codec, "sog"); } #[test] fn codecs_map_to_ascending_tiers() { // The codec→tier mapping respects the cascade's energy ordering. let f1 = terrain_tile(key(), vec![]).tier; let f2 = raster_tile( key(), &solid_tile(TileCoord::new(0, 0, 0).unwrap(), [0; 4], RasterFormat::Png).unwrap(), ) .tier; let f3 = vector_tile(key(), vec![], false).tier; let f4 = photoreal_tile(key(), "sog", vec![]).tier; assert!(f1.rank() < f2.rank() && f2.rank() < f3.rank() && f3.rank() < f4.rank()); } }