s2_tilejson/
lib.rs

1#![no_std]
2#![deny(missing_docs)]
3//! The `s2-tilejson` Rust crate... TODO
4
5extern crate alloc;
6
7use s2json::{BBox, Face};
8
9use serde::{Deserialize, Deserializer, Serialize, Serializer};
10
11use alloc::borrow::ToOwned;
12use alloc::collections::BTreeMap;
13use alloc::collections::BTreeSet;
14use alloc::format;
15use alloc::string::String;
16use alloc::vec::Vec;
17
18/// Use bounds as floating point numbers for longitude and latitude
19pub type LonLatBounds = BBox<f64>;
20
21/// Use bounds as u64 for the tile index range
22pub type TileBounds = BBox<u64>;
23
24/// 1: points, 2: lines, 3: polys, 4: points3D, 5: lines3D, 6: polys3D
25#[derive(Copy, Clone, Debug, PartialEq)]
26pub enum DrawType {
27    /// Collection of points
28    Points = 1,
29    /// Collection of lines
30    Lines = 2,
31    /// Collection of polygons
32    Polygons = 3,
33    /// Collection of 3D points
34    Points3D = 4,
35    /// Collection of 3D lines
36    Lines3D = 5,
37    /// Collection of 3D polygons
38    Polygons3D = 6,
39    /// Raster data
40    Raster = 7,
41    /// Collection of points
42    Grid = 8,
43}
44impl From<DrawType> for u8 {
45    fn from(draw_type: DrawType) -> Self {
46        draw_type as u8
47    }
48}
49impl From<u8> for DrawType {
50    fn from(draw_type: u8) -> Self {
51        match draw_type {
52            1 => DrawType::Points,
53            2 => DrawType::Lines,
54            3 => DrawType::Polygons,
55            4 => DrawType::Points3D,
56            5 => DrawType::Lines3D,
57            6 => DrawType::Polygons3D,
58            7 => DrawType::Raster,
59            8 => DrawType::Grid,
60            _ => DrawType::Points,
61        }
62    }
63}
64impl Serialize for DrawType {
65    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
66    where
67        S: Serializer,
68    {
69        // Serialize as u8
70        serializer.serialize_u8(*self as u8)
71    }
72}
73
74impl<'de> Deserialize<'de> for DrawType {
75    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
76    where
77        D: Deserializer<'de>,
78    {
79        // Deserialize from u8 or string
80        let value: u8 = Deserialize::deserialize(deserializer)?;
81        match value {
82            1 => Ok(DrawType::Points),
83            2 => Ok(DrawType::Lines),
84            3 => Ok(DrawType::Polygons),
85            4 => Ok(DrawType::Points3D),
86            5 => Ok(DrawType::Lines3D),
87            6 => Ok(DrawType::Polygons3D),
88            7 => Ok(DrawType::Raster),
89            8 => Ok(DrawType::Grid),
90            _ => Err(serde::de::Error::custom(format!("unknown DrawType variant: {}", value))),
91        }
92    }
93}
94
95// Shapes exist solely to deconstruct and rebuild objects.
96//
97// Shape limitations:
98// - all keys are strings.
99// - all values are either:
100// - - primitive types: strings, numbers (f32, f64, u64, i64), true, false, or null
101// - - sub types: an array of a shape or a nested object which is itself a shape
102// - - if the sub type is an array, ensure all elements are of the same type
103// The interfaces below help describe how shapes are built by the user.
104
105/// Primitive types that can be found in a shape
106#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
107#[serde(rename_all = "lowercase")]
108pub enum PrimitiveShape {
109    /// String type utf8 encoded
110    String,
111    /// unsigned 64 bit integer
112    U64,
113    /// signed 64 bit integer
114    I64,
115    /// floating point number
116    F32,
117    /// double precision floating point number
118    F64,
119    /// boolean
120    Bool,
121    /// null
122    Null,
123}
124
125/// Arrays may contain either a primitive or an object whose values are primitives
126#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
127#[serde(untagged)]
128pub enum ShapePrimitiveType {
129    /// Primitive type
130    Primitive(PrimitiveShape),
131    /// Nested shape that can only contain primitives
132    NestedPrimitive(BTreeMap<String, PrimitiveShape>),
133}
134
135/// Shape types that can be found in a shapes object.
136/// Either a primitive, an array containing any type, or a nested shape.
137/// If the type is an array, all elements must be the same type
138#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
139#[serde(untagged)]
140pub enum ShapeType {
141    /// Primitive type
142    Primitive(PrimitiveShape),
143    /// Nested shape that can only contain primitives
144    Array(Vec<ShapePrimitiveType>),
145    /// Nested shape
146    Nested(Shape),
147}
148
149/// The Shape Object
150pub type Shape = BTreeMap<String, ShapeType>;
151
152/// Each layer has metadata associated with it. Defined as blueprints pre-construction of vector data.
153#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
154pub struct LayerMetaData {
155    /// The description of the layer
156    #[serde(skip_serializing_if = "Option::is_none")]
157    pub description: Option<String>,
158    /// the lowest zoom level at which the layer is available
159    pub minzoom: u8,
160    /// the highest zoom level at which the layer is available
161    pub maxzoom: u8,
162    /// The draw types that can be found in this layer
163    pub draw_types: Vec<DrawType>,
164    /// The shape that can be found in this layer
165    pub shape: Shape,
166    /// The shape used inside features that can be found in this layer
167    #[serde(skip_serializing_if = "Option::is_none", rename = "mShape")]
168    pub m_shape: Option<Shape>,
169}
170
171/// Each layer has metadata associated with it. Defined as blueprints pre-construction of vector data.
172pub type LayersMetaData = BTreeMap<String, LayerMetaData>;
173
174/// Tilestats is simply a tracker to see where most of the tiles live
175#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
176pub struct TileStatsMetadata {
177    /// total number of tiles
178    #[serde(default)]
179    pub total: u64,
180    /// number of tiles for face 0
181    #[serde(rename = "0", default)]
182    pub total_0: u64,
183    /// number of tiles for face 1
184    #[serde(rename = "1", default)]
185    pub total_1: u64,
186    /// number of tiles for face 2
187    #[serde(rename = "2", default)]
188    pub total_2: u64,
189    /// number of tiles for face 3
190    #[serde(rename = "3", default)]
191    pub total_3: u64,
192    /// number of tiles for face 4
193    #[serde(rename = "4", default)]
194    pub total_4: u64,
195    /// number of tiles for face 5
196    #[serde(rename = "5", default)]
197    pub total_5: u64,
198}
199impl TileStatsMetadata {
200    /// Access the total number of tiles for a given face
201    pub fn get(&self, face: Face) -> u64 {
202        match face {
203            Face::Face0 => self.total_0,
204            Face::Face1 => self.total_1,
205            Face::Face2 => self.total_2,
206            Face::Face3 => self.total_3,
207            Face::Face4 => self.total_4,
208            Face::Face5 => self.total_5,
209        }
210    }
211
212    /// Increment the total number of tiles for a given face and also the grand total
213    pub fn increment(&mut self, face: Face) {
214        match face {
215            Face::Face0 => self.total_0 += 1,
216            Face::Face1 => self.total_1 += 1,
217            Face::Face2 => self.total_2 += 1,
218            Face::Face3 => self.total_3 += 1,
219            Face::Face4 => self.total_4 += 1,
220            Face::Face5 => self.total_5 += 1,
221        }
222        self.total += 1;
223    }
224}
225
226/// Attribution data is stored in an object.
227/// The key is the name of the attribution, and the value is the link
228pub type Attribution = BTreeMap<String, String>;
229
230/// Track the S2 tile bounds of each face and zoom
231#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
232pub struct FaceBounds {
233    // facesbounds[face][zoom] = [...]
234    /// Tile bounds for face 0 at each zoom
235    #[serde(rename = "0")]
236    pub face0: BTreeMap<u8, TileBounds>,
237    /// Tile bounds for face 1 at each zoom
238    #[serde(rename = "1")]
239    pub face1: BTreeMap<u8, TileBounds>,
240    /// Tile bounds for face 2 at each zoom
241    #[serde(rename = "2")]
242    pub face2: BTreeMap<u8, TileBounds>,
243    /// Tile bounds for face 3 at each zoom
244    #[serde(rename = "3")]
245    pub face3: BTreeMap<u8, TileBounds>,
246    /// Tile bounds for face 4 at each zoom
247    #[serde(rename = "4")]
248    pub face4: BTreeMap<u8, TileBounds>,
249    /// Tile bounds for face 5 at each zoom
250    #[serde(rename = "5")]
251    pub face5: BTreeMap<u8, TileBounds>,
252}
253impl FaceBounds {
254    /// Access the tile bounds for a given face and zoom
255    pub fn get(&self, face: Face) -> &BTreeMap<u8, TileBounds> {
256        match face {
257            Face::Face0 => &self.face0,
258            Face::Face1 => &self.face1,
259            Face::Face2 => &self.face2,
260            Face::Face3 => &self.face3,
261            Face::Face4 => &self.face4,
262            Face::Face5 => &self.face5,
263        }
264    }
265
266    /// Access the mutable tile bounds for a given face and zoom
267    pub fn get_mut(&mut self, face: Face) -> &mut BTreeMap<u8, TileBounds> {
268        match face {
269            Face::Face0 => &mut self.face0,
270            Face::Face1 => &mut self.face1,
271            Face::Face2 => &mut self.face2,
272            Face::Face3 => &mut self.face3,
273            Face::Face4 => &mut self.face4,
274            Face::Face5 => &mut self.face5,
275        }
276    }
277}
278
279/// Track the WM tile bounds of each zoom
280/// `[zoom: number]: BBox`
281pub type WMBounds = BTreeMap<u8, TileBounds>;
282
283/// Check the source type of the layer
284#[derive(Serialize, Debug, Default, Clone, PartialEq)]
285#[serde(rename_all = "lowercase")]
286pub enum SourceType {
287    /// Vector data
288    #[default]
289    Vector,
290    /// Json data
291    Json,
292    /// Raster data
293    Raster,
294    /// Raster DEM data
295    #[serde(rename = "raster-dem")]
296    RasterDem,
297    /// Grid data
298    Grid,
299    /// Marker data
300    Markers,
301    /// Unknown source type
302    Unknown,
303}
304impl From<&str> for SourceType {
305    fn from(source_type: &str) -> Self {
306        match source_type {
307            "vector" => SourceType::Vector,
308            "json" => SourceType::Json,
309            "raster" => SourceType::Raster,
310            "raster-dem" => SourceType::RasterDem,
311            "grid" => SourceType::Grid,
312            "markers" => SourceType::Markers,
313            _ => SourceType::Unknown,
314        }
315    }
316}
317impl<'de> Deserialize<'de> for SourceType {
318    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
319    where
320        D: Deserializer<'de>,
321    {
322        // Deserialize from a string
323        let s: String = Deserialize::deserialize(deserializer)?;
324        Ok(SourceType::from(s.as_str()))
325    }
326}
327
328/// Store the encoding of the data
329#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
330#[serde(rename_all = "lowercase")]
331pub enum Encoding {
332    /// No encoding
333    #[default]
334    None = 0,
335    /// Gzip encoding
336    Gzip = 1,
337    /// Brotli encoding
338    #[serde(rename = "br")]
339    Brotli = 2,
340    /// Zstd encoding
341    Zstd = 3,
342}
343impl From<u8> for Encoding {
344    fn from(encoding: u8) -> Self {
345        match encoding {
346            1 => Encoding::Gzip,
347            2 => Encoding::Brotli,
348            3 => Encoding::Zstd,
349            _ => Encoding::None,
350        }
351    }
352}
353impl From<Encoding> for u8 {
354    fn from(encoding: Encoding) -> Self {
355        match encoding {
356            Encoding::Gzip => 1,
357            Encoding::Brotli => 2,
358            Encoding::Zstd => 3,
359            Encoding::None => 0,
360        }
361    }
362}
363impl From<Encoding> for &str {
364    fn from(encoding: Encoding) -> Self {
365        match encoding {
366            Encoding::Gzip => "gzip",
367            Encoding::Brotli => "br",
368            Encoding::Zstd => "zstd",
369            Encoding::None => "none",
370        }
371    }
372}
373impl From<&str> for Encoding {
374    fn from(encoding: &str) -> Self {
375        match encoding {
376            "gzip" => Encoding::Gzip,
377            "br" => Encoding::Brotli,
378            "zstd" => Encoding::Zstd,
379            _ => Encoding::None,
380        }
381    }
382}
383
384/// Old spec tracks basic vector data
385#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
386pub struct VectorLayer {
387    /// The id of the layer
388    pub id: String,
389    /// The description of the layer
390    #[serde(skip_serializing_if = "Option::is_none")]
391    pub description: Option<String>,
392    /// The min zoom of the layer
393    #[serde(skip_serializing_if = "Option::is_none")]
394    pub minzoom: Option<u8>,
395    /// The max zoom of the layer
396    #[serde(skip_serializing_if = "Option::is_none")]
397    pub maxzoom: Option<u8>,
398    /// Information about each field property
399    pub fields: BTreeMap<String, String>,
400}
401
402/// Default S2 tile scheme is `fzxy`
403/// Default Web Mercator tile scheme is `xyz`
404/// Adding a t prefix to the scheme will change the request to be time sensitive
405/// TMS is an oudated version that is not supported by s2maps-gpu
406#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
407#[serde(rename_all = "lowercase")]
408pub enum Scheme {
409    /// The default scheme with faces (S2)
410    #[default]
411    Fzxy,
412    /// The time sensitive scheme with faces (S2)
413    Tfzxy,
414    /// The basic scheme (Web Mercator)
415    Xyz,
416    /// The time sensitive basic scheme (Web Mercator)
417    Txyz,
418    /// The TMS scheme
419    Tms,
420}
421impl From<&str> for Scheme {
422    fn from(scheme: &str) -> Self {
423        match scheme {
424            "fzxy" => Scheme::Fzxy,
425            "tfzxy" => Scheme::Tfzxy,
426            "xyz" => Scheme::Xyz,
427            "txyz" => Scheme::Txyz,
428            _ => Scheme::Tms,
429        }
430    }
431}
432impl From<Scheme> for &str {
433    fn from(scheme: Scheme) -> Self {
434        match scheme {
435            Scheme::Fzxy => "fzxy",
436            Scheme::Tfzxy => "tfzxy",
437            Scheme::Xyz => "xyz",
438            Scheme::Txyz => "txyz",
439            Scheme::Tms => "tms",
440        }
441    }
442}
443
444/// Store where the center of the data lives
445#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
446pub struct Center {
447    /// The longitude of the center
448    pub lon: f64,
449    /// The latitude of the center
450    pub lat: f64,
451    /// The zoom of the center
452    pub zoom: u8,
453}
454
455/// S2 TileJSON Metadata for the tile data
456#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
457pub struct Metadata {
458    /// The version of the s2-tilejson spec
459    #[serde(default)]
460    pub s2tilejson: String,
461    /// The version of the data
462    #[serde(default)]
463    pub version: String,
464    /// The name of the data
465    #[serde(default)]
466    pub name: String,
467    /// The scheme of the data
468    #[serde(default)]
469    pub scheme: Scheme,
470    /// The description of the data
471    #[serde(default)]
472    pub description: String,
473    /// The type of the data
474    #[serde(rename = "type", default)]
475    pub type_: SourceType,
476    /// The extension to use when requesting a tile
477    #[serde(default)]
478    pub extension: String,
479    /// The encoding of the data
480    #[serde(default)]
481    pub encoding: Encoding,
482    /// List of faces that have data
483    #[serde(default)]
484    pub faces: Vec<Face>,
485    /// WM Tile fetching bounds. Helpful to not make unecessary requests for tiles we know don't exist
486    #[serde(default)]
487    pub bounds: WMBounds,
488    /// S2 Tile fetching bounds. Helpful to not make unecessary requests for tiles we know don't exist
489    #[serde(default)]
490    pub facesbounds: FaceBounds,
491    /// minzoom at which to request tiles. [default=0]
492    #[serde(default)]
493    pub minzoom: u8,
494    /// maxzoom at which to request tiles. [default=27]
495    #[serde(default)]
496    pub maxzoom: u8,
497    /// The center of the data
498    #[serde(default)]
499    pub center: Center,
500    /// { ['human readable string']: 'href' }
501    #[serde(default)]
502    pub attribution: Attribution,
503    /// Track layer metadata
504    #[serde(default)]
505    pub layers: LayersMetaData,
506    /// Track tile stats for each face and total overall
507    #[serde(default)]
508    pub tilestats: TileStatsMetadata,
509    /// Old spec, track basic layer metadata
510    #[serde(default)]
511    pub vector_layers: Vec<VectorLayer>,
512}
513impl Default for Metadata {
514    fn default() -> Self {
515        Self {
516            s2tilejson: "1.0.0".into(),
517            version: "1.0.0".into(),
518            name: "default".into(),
519            scheme: Scheme::default(),
520            description: "Built with s2maps-cli".into(),
521            type_: SourceType::default(),
522            extension: "pbf".into(),
523            encoding: Encoding::default(),
524            faces: Vec::new(),
525            bounds: WMBounds::default(),
526            facesbounds: FaceBounds::default(),
527            minzoom: 0,
528            maxzoom: 27,
529            center: Center::default(),
530            attribution: BTreeMap::new(),
531            layers: LayersMetaData::default(),
532            tilestats: TileStatsMetadata::default(),
533            vector_layers: Vec::new(),
534        }
535    }
536}
537
538/// # TileJSON V3.0.0
539///
540/// ## NOTES
541/// You never have to use this. Parsing/conversion will be done for you. by using:
542///
543/// ```rs
544/// let meta: Metadata =
545///   serde_json::from_str(meta_str).unwrap_or_else(|e| panic!("ERROR: {}", e));
546/// ```
547///
548/// Represents a TileJSON metadata object for the old Mapbox spec.
549/// ## Links
550/// [TileJSON Spec](https://github.com/mapbox/tilejson-spec/blob/master/3.0.0/schema.json)
551#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
552pub struct MapboxTileJSONMetadata {
553    /// Version of the TileJSON spec used.
554    /// Matches the pattern: `\d+\.\d+\.\d+\w?[\w\d]*`.
555    pub tilejson: String,
556    /// Array of tile URL templates.
557    pub tiles: Vec<String>,
558    /// Array of vector layer metadata.
559    pub vector_layers: Vec<VectorLayer>,
560    /// Attribution string.
561    pub attribution: Option<String>,
562    /// Bounding box array [west, south, east, north].
563    pub bounds: Option<BBox>,
564    /// Center coordinate array [longitude, latitude, zoom].
565    pub center: Option<[f64; 3]>,
566    /// Array of data source URLs.
567    pub data: Option<Vec<String>>,
568    /// Description string.
569    pub description: Option<String>,
570    /// Fill zoom level. Must be between 0 and 30.
571    pub fillzoom: Option<u8>,
572    /// Array of UTFGrid URL templates.
573    pub grids: Option<Vec<String>>,
574    /// Legend of the tileset.
575    pub legend: Option<String>,
576    /// Maximum zoom level. Must be between 0 and 30.
577    pub maxzoom: Option<u8>,
578    /// Minimum zoom level. Must be between 0 and 30.
579    pub minzoom: Option<u8>,
580    /// Name of the tileset.
581    pub name: Option<String>,
582    /// Tile scheme, e.g., `xyz` or `tms`.
583    pub scheme: Option<Scheme>,
584    /// Template for interactivity.
585    pub template: Option<String>,
586    /// Version of the tileset. Matches the pattern: `\d+\.\d+\.\d+\w?[\w\d]*`.
587    pub version: Option<String>,
588}
589impl MapboxTileJSONMetadata {
590    /// Converts a MapboxTileJSONMetadata to a Metadata
591    pub fn to_metadata(&self) -> Metadata {
592        Metadata {
593            s2tilejson: "1.0.0".into(),
594            version: self.version.clone().unwrap_or("1.0.0".into()),
595            name: self.name.clone().unwrap_or("default".into()),
596            scheme: self.scheme.clone().unwrap_or_default(),
597            description: self.description.clone().unwrap_or("Built with s2maps-cli".into()),
598            type_: SourceType::default(),
599            extension: "pbf".into(),
600            faces: Vec::from([Face::Face0]),
601            bounds: WMBounds::default(),
602            facesbounds: FaceBounds::default(),
603            minzoom: self.minzoom.unwrap_or(0),
604            maxzoom: self.maxzoom.unwrap_or(27),
605            center: Center {
606                lon: self.center.unwrap_or([0.0, 0.0, 0.0])[0],
607                lat: self.center.unwrap_or([0.0, 0.0, 0.0])[1],
608                zoom: self.center.unwrap_or([0.0, 0.0, 0.0])[2] as u8,
609            },
610            attribution: BTreeMap::new(),
611            layers: LayersMetaData::default(),
612            tilestats: TileStatsMetadata::default(),
613            vector_layers: self.vector_layers.clone(),
614            encoding: Encoding::default(),
615        }
616    }
617}
618
619/// Builder for the metadata
620#[derive(Debug, Clone)]
621pub struct MetadataBuilder {
622    lon_lat_bounds: LonLatBounds,
623    faces: BTreeSet<Face>,
624    metadata: Metadata,
625}
626impl Default for MetadataBuilder {
627    fn default() -> Self {
628        MetadataBuilder {
629            lon_lat_bounds: BBox {
630                left: f64::INFINITY,
631                bottom: f64::INFINITY,
632                right: -f64::INFINITY,
633                top: -f64::INFINITY,
634            },
635            faces: BTreeSet::new(),
636            metadata: Metadata { minzoom: 30, maxzoom: 0, ..Metadata::default() },
637        }
638    }
639}
640impl MetadataBuilder {
641    /// Commit the metadata and take ownership
642    pub fn commit(&mut self) -> Metadata {
643        // set the center
644        self.update_center();
645        // set the faces
646        for face in &self.faces {
647            self.metadata.faces.push(*face);
648        }
649        // return the result
650        self.metadata.to_owned()
651    }
652
653    /// Set the name
654    pub fn set_name(&mut self, name: String) {
655        self.metadata.name = name;
656    }
657
658    /// Set the scheme of the data. [default=fzxy]
659    pub fn set_scheme(&mut self, scheme: Scheme) {
660        self.metadata.scheme = scheme;
661    }
662
663    /// Set the extension of the data. [default=pbf]
664    pub fn set_extension(&mut self, extension: String) {
665        self.metadata.extension = extension;
666    }
667
668    /// Set the type of the data. [default=vector]
669    pub fn set_type(&mut self, type_: SourceType) {
670        self.metadata.type_ = type_;
671    }
672
673    /// Set the version of the data
674    pub fn set_version(&mut self, version: String) {
675        self.metadata.version = version;
676    }
677
678    /// Set the description of the data
679    pub fn set_description(&mut self, description: String) {
680        self.metadata.description = description;
681    }
682
683    /// Set the encoding of the data. [default=none]
684    pub fn set_encoding(&mut self, encoding: Encoding) {
685        self.metadata.encoding = encoding;
686    }
687
688    /// add an attribution
689    pub fn add_attribution(&mut self, display_name: &str, href: &str) {
690        self.metadata.attribution.insert(display_name.into(), href.into());
691    }
692
693    /// Add the layer metadata
694    pub fn add_layer(&mut self, name: &str, layer: &LayerMetaData) {
695        // Only insert if the key does not exist
696        if self.metadata.layers.entry(name.into()).or_insert(layer.clone()).eq(&layer) {
697            // Also add to vector_layers only if the key was not present and the insert was successful
698            self.metadata.vector_layers.push(VectorLayer {
699                id: name.into(), // No need to clone again; we use the moved value
700                description: layer.description.clone(),
701                minzoom: Some(layer.minzoom),
702                maxzoom: Some(layer.maxzoom),
703                fields: BTreeMap::new(),
704            });
705        }
706        // update minzoom and maxzoom
707        if layer.minzoom < self.metadata.minzoom {
708            self.metadata.minzoom = layer.minzoom;
709        }
710        if layer.maxzoom > self.metadata.maxzoom {
711            self.metadata.maxzoom = layer.maxzoom;
712        }
713    }
714
715    /// Add the WM tile metadata
716    pub fn add_tile_wm(&mut self, zoom: u8, x: u32, y: u32, ll_bounds: &LonLatBounds) {
717        self.metadata.tilestats.total += 1;
718        self.faces.insert(Face::Face0);
719        self.add_bounds_wm(zoom, x, y);
720        self.update_lon_lat_bounds(ll_bounds);
721    }
722
723    /// Add the S2 tile metadata
724    pub fn add_tile_s2(&mut self, face: Face, zoom: u8, x: u32, y: u32, ll_bounds: &LonLatBounds) {
725        self.metadata.tilestats.increment(face);
726        self.faces.insert(face);
727        self.add_bounds_s2(face, zoom, x, y);
728        self.update_lon_lat_bounds(ll_bounds);
729    }
730
731    /// Update the center now that all tiles have been added
732    fn update_center(&mut self) {
733        let Metadata { minzoom, maxzoom, .. } = self.metadata;
734        let BBox { left, bottom, right, top } = self.lon_lat_bounds;
735        self.metadata.center.lon = (left + right) / 2.0;
736        self.metadata.center.lat = (bottom + top) / 2.0;
737        self.metadata.center.zoom = (minzoom + maxzoom) >> 1;
738    }
739
740    /// Add the bounds of the tile for WM data
741    fn add_bounds_wm(&mut self, zoom: u8, x: u32, y: u32) {
742        let x = x as u64;
743        let y = y as u64;
744        let bbox = self.metadata.bounds.entry(zoom).or_insert(BBox {
745            left: u64::MAX,
746            bottom: u64::MAX,
747            right: 0,
748            top: 0,
749        });
750
751        bbox.left = bbox.left.min(x);
752        bbox.bottom = bbox.bottom.min(y);
753        bbox.right = bbox.right.max(x);
754        bbox.top = bbox.top.max(y);
755    }
756
757    /// Add the bounds of the tile for S2 data
758    fn add_bounds_s2(&mut self, face: Face, zoom: u8, x: u32, y: u32) {
759        let x = x as u64;
760        let y = y as u64;
761        let bbox = self.metadata.facesbounds.get_mut(face).entry(zoom).or_insert(BBox {
762            left: u64::MAX,
763            bottom: u64::MAX,
764            right: 0,
765            top: 0,
766        });
767
768        bbox.left = bbox.left.min(x);
769        bbox.bottom = bbox.bottom.min(y);
770        bbox.right = bbox.right.max(x);
771        bbox.top = bbox.top.max(y);
772    }
773
774    /// Update the lon-lat bounds so eventually we can find the center point of the data
775    fn update_lon_lat_bounds(&mut self, ll_bounds: &LonLatBounds) {
776        self.lon_lat_bounds.left = ll_bounds.left.min(self.lon_lat_bounds.left);
777        self.lon_lat_bounds.bottom = ll_bounds.bottom.min(self.lon_lat_bounds.bottom);
778        self.lon_lat_bounds.right = ll_bounds.right.max(self.lon_lat_bounds.right);
779        self.lon_lat_bounds.top = ll_bounds.top.max(self.lon_lat_bounds.top);
780    }
781}
782
783#[cfg(test)]
784mod tests {
785    use super::*;
786    use alloc::vec;
787
788    #[test]
789    fn it_works() {
790        let mut meta_builder = MetadataBuilder::default();
791
792        // on initial use be sure to update basic metadata:
793        meta_builder.set_name("OSM".into());
794        meta_builder.set_description("A free editable map of the whole world.".into());
795        meta_builder.set_version("1.0.0".into());
796        meta_builder.set_scheme("fzxy".into()); // 'fzxy' | 'tfzxy' | 'xyz' | 'txyz' | 'tms'
797        meta_builder.set_type("vector".into()); // 'vector' | 'json' | 'raster' | 'raster-dem' | 'grid' | 'markers'
798        meta_builder.set_encoding("none".into()); // 'gz' | 'br' | 'none'
799        meta_builder.set_extension("pbf".into());
800        meta_builder.add_attribution("OpenStreetMap", "https://www.openstreetmap.org/copyright/");
801
802        // Vector Specific: add layers based on how you want to parse data from a source:
803        let shape_str = r#"
804        {
805            "class": "string",
806            "offset": "f64",
807            "info": {
808                "name": "string",
809                "value": "i64"
810            }
811        }
812        "#;
813        let shape: Shape =
814            serde_json::from_str(shape_str).unwrap_or_else(|e| panic!("ERROR: {}", e));
815        let layer = LayerMetaData {
816            minzoom: 0,
817            maxzoom: 13,
818            description: Some("water_lines".into()),
819            draw_types: Vec::from(&[DrawType::Lines]),
820            shape: shape.clone(),
821            m_shape: None,
822        };
823        meta_builder.add_layer("water_lines", &layer);
824
825        // as you build tiles, add the tiles metadata:
826        // WM:
827        meta_builder.add_tile_wm(
828            0,
829            0,
830            0,
831            &LonLatBounds { left: -60.0, bottom: -20.0, right: 5.0, top: 60.0 },
832        );
833        // S2:
834        meta_builder.add_tile_s2(
835            Face::Face1,
836            5,
837            22,
838            37,
839            &LonLatBounds { left: -120.0, bottom: -7.0, right: 44.0, top: 72.0 },
840        );
841
842        // finally to get the resulting metadata:
843        let resulting_metadata: Metadata = meta_builder.commit();
844
845        assert_eq!(
846            resulting_metadata,
847            Metadata {
848                name: "OSM".into(),
849                description: "A free editable map of the whole world.".into(),
850                version: "1.0.0".into(),
851                scheme: "fzxy".into(),
852                type_: "vector".into(),
853                encoding: "none".into(),
854                extension: "pbf".into(),
855                attribution: BTreeMap::from([(
856                    "OpenStreetMap".into(),
857                    "https://www.openstreetmap.org/copyright/".into()
858                ),]),
859                bounds: BTreeMap::from([(0, TileBounds { left: 0, bottom: 0, right: 0, top: 0 }),]),
860                faces: Vec::from(&[Face::Face0, Face::Face1]),
861                facesbounds: FaceBounds {
862                    face0: BTreeMap::new(),
863                    face1: BTreeMap::from([(
864                        5,
865                        TileBounds { left: 22, bottom: 37, right: 22, top: 37 }
866                    ),]),
867                    face2: BTreeMap::new(),
868                    face3: BTreeMap::new(),
869                    face4: BTreeMap::new(),
870                    face5: BTreeMap::new(),
871                },
872                minzoom: 0,
873                maxzoom: 13,
874                center: Center { lon: -38.0, lat: 26.0, zoom: 6 },
875                tilestats: TileStatsMetadata {
876                    total: 2,
877                    total_0: 0,
878                    total_1: 1,
879                    total_2: 0,
880                    total_3: 0,
881                    total_4: 0,
882                    total_5: 0,
883                },
884                layers: BTreeMap::from([(
885                    "water_lines".into(),
886                    LayerMetaData {
887                        description: Some("water_lines".into()),
888                        minzoom: 0,
889                        maxzoom: 13,
890                        draw_types: Vec::from(&[DrawType::Lines]),
891                        shape: BTreeMap::from([
892                            ("class".into(), ShapeType::Primitive(PrimitiveShape::String)),
893                            ("offset".into(), ShapeType::Primitive(PrimitiveShape::F64)),
894                            (
895                                "info".into(),
896                                ShapeType::Nested(BTreeMap::from([
897                                    ("name".into(), ShapeType::Primitive(PrimitiveShape::String)),
898                                    ("value".into(), ShapeType::Primitive(PrimitiveShape::I64)),
899                                ]))
900                            ),
901                        ]),
902                        m_shape: None,
903                    }
904                )]),
905                s2tilejson: "1.0.0".into(),
906                vector_layers: Vec::from([VectorLayer {
907                    id: "water_lines".into(),
908                    description: Some("water_lines".into()),
909                    minzoom: Some(0),
910                    maxzoom: Some(13),
911                    fields: BTreeMap::new()
912                }]),
913            }
914        );
915
916        let meta_str = serde_json::to_string(&resulting_metadata).unwrap();
917
918        assert_eq!(meta_str, "{\"s2tilejson\":\"1.0.0\",\"version\":\"1.0.0\",\"name\":\"OSM\",\"scheme\":\"fzxy\",\"description\":\"A free editable map of the whole world.\",\"type\":\"vector\",\"extension\":\"pbf\",\"encoding\":\"none\",\"faces\":[0,1],\"bounds\":{\"0\":[0,0,0,0]},\"facesbounds\":{\"0\":{},\"1\":{\"5\":[22,37,22,37]},\"2\":{},\"3\":{},\"4\":{},\"5\":{}},\"minzoom\":0,\"maxzoom\":13,\"center\":{\"lon\":-38.0,\"lat\":26.0,\"zoom\":6},\"attribution\":{\"OpenStreetMap\":\"https://www.openstreetmap.org/copyright/\"},\"layers\":{\"water_lines\":{\"description\":\"water_lines\",\"minzoom\":0,\"maxzoom\":13,\"draw_types\":[2],\"shape\":{\"class\":\"string\",\"info\":{\"name\":\"string\",\"value\":\"i64\"},\"offset\":\"f64\"}}},\"tilestats\":{\"total\":2,\"0\":0,\"1\":1,\"2\":0,\"3\":0,\"4\":0,\"5\":0},\"vector_layers\":[{\"id\":\"water_lines\",\"description\":\"water_lines\",\"minzoom\":0,\"maxzoom\":13,\"fields\":{}}]}");
919
920        let meta_reparsed: Metadata =
921            serde_json::from_str(&meta_str).unwrap_or_else(|e| panic!("ERROR: {}", e));
922        assert_eq!(meta_reparsed, resulting_metadata);
923    }
924
925    #[test]
926    fn test_face() {
927        assert_eq!(Face::Face0, Face::from(0));
928        assert_eq!(Face::Face1, Face::from(1));
929        assert_eq!(Face::Face2, Face::from(2));
930        assert_eq!(Face::Face3, Face::from(3));
931        assert_eq!(Face::Face4, Face::from(4));
932        assert_eq!(Face::Face5, Face::from(5));
933
934        assert_eq!(0, u8::from(Face::Face0));
935        assert_eq!(1, u8::from(Face::Face1));
936        assert_eq!(2, u8::from(Face::Face2));
937        assert_eq!(3, u8::from(Face::Face3));
938        assert_eq!(4, u8::from(Face::Face4));
939        assert_eq!(5, u8::from(Face::Face5));
940    }
941
942    #[test]
943    fn test_bbox() {
944        let bbox: BBox = BBox { left: 0.0, bottom: 0.0, right: 0.0, top: 0.0 };
945        // serialize to JSON and back
946        let json = serde_json::to_string(&bbox).unwrap();
947        assert_eq!(json, r#"[0.0,0.0,0.0,0.0]"#);
948        let bbox2: BBox = serde_json::from_str(&json).unwrap();
949        assert_eq!(bbox, bbox2);
950    }
951
952    // TileStatsMetadata
953    #[test]
954    fn test_tilestats() {
955        let mut tilestats = TileStatsMetadata {
956            total: 2,
957            total_0: 0,
958            total_1: 1,
959            total_2: 0,
960            total_3: 0,
961            total_4: 0,
962            total_5: 0,
963        };
964        // serialize to JSON and back
965        let json = serde_json::to_string(&tilestats).unwrap();
966        assert_eq!(json, r#"{"total":2,"0":0,"1":1,"2":0,"3":0,"4":0,"5":0}"#);
967        let tilestats2: TileStatsMetadata = serde_json::from_str(&json).unwrap();
968        assert_eq!(tilestats, tilestats2);
969
970        // get0
971        assert_eq!(tilestats.get(0.into()), 0);
972        // increment0
973        tilestats.increment(0.into());
974        assert_eq!(tilestats.get(0.into()), 1);
975
976        // get 1
977        assert_eq!(tilestats.get(1.into()), 1);
978        // increment 1
979        tilestats.increment(1.into());
980        assert_eq!(tilestats.get(1.into()), 2);
981
982        // get 2
983        assert_eq!(tilestats.get(2.into()), 0);
984        // increment 2
985        tilestats.increment(2.into());
986        assert_eq!(tilestats.get(2.into()), 1);
987
988        // get 3
989        assert_eq!(tilestats.get(3.into()), 0);
990        // increment 3
991        tilestats.increment(3.into());
992        assert_eq!(tilestats.get(3.into()), 1);
993
994        // get 4
995        assert_eq!(tilestats.get(4.into()), 0);
996        // increment 4
997        tilestats.increment(4.into());
998        assert_eq!(tilestats.get(4.into()), 1);
999
1000        // get 5
1001        assert_eq!(tilestats.get(5.into()), 0);
1002        // increment 5
1003        tilestats.increment(5.into());
1004        assert_eq!(tilestats.get(5.into()), 1);
1005    }
1006
1007    // FaceBounds
1008    #[test]
1009    fn test_facebounds() {
1010        let mut facebounds = FaceBounds::default();
1011        // get mut
1012        let face0 = facebounds.get_mut(0.into());
1013        face0.insert(0, TileBounds { left: 0, bottom: 0, right: 0, top: 0 });
1014        // get mut 1
1015        let face1 = facebounds.get_mut(1.into());
1016        face1.insert(0, TileBounds { left: 0, bottom: 0, right: 1, top: 1 });
1017        // get mut 2
1018        let face2 = facebounds.get_mut(2.into());
1019        face2.insert(0, TileBounds { left: 0, bottom: 0, right: 2, top: 2 });
1020        // get mut 3
1021        let face3 = facebounds.get_mut(3.into());
1022        face3.insert(0, TileBounds { left: 0, bottom: 0, right: 3, top: 3 });
1023        // get mut 4
1024        let face4 = facebounds.get_mut(4.into());
1025        face4.insert(0, TileBounds { left: 0, bottom: 0, right: 4, top: 4 });
1026        // get mut 5
1027        let face5 = facebounds.get_mut(5.into());
1028        face5.insert(0, TileBounds { left: 0, bottom: 0, right: 5, top: 5 });
1029
1030        // now get for all 5:
1031        // get 0
1032        assert_eq!(
1033            facebounds.get(0.into()).get(&0).unwrap(),
1034            &TileBounds { left: 0, bottom: 0, right: 0, top: 0 }
1035        );
1036        // get 1
1037        assert_eq!(
1038            facebounds.get(1.into()).get(&0).unwrap(),
1039            &TileBounds { left: 0, bottom: 0, right: 1, top: 1 }
1040        );
1041        // get 2
1042        assert_eq!(
1043            facebounds.get(2.into()).get(&0).unwrap(),
1044            &TileBounds { left: 0, bottom: 0, right: 2, top: 2 }
1045        );
1046        // get 3
1047        assert_eq!(
1048            facebounds.get(3.into()).get(&0).unwrap(),
1049            &TileBounds { left: 0, bottom: 0, right: 3, top: 3 }
1050        );
1051        // get 4
1052        assert_eq!(
1053            facebounds.get(4.into()).get(&0).unwrap(),
1054            &TileBounds { left: 0, bottom: 0, right: 4, top: 4 }
1055        );
1056        // get 5
1057        assert_eq!(
1058            facebounds.get(5.into()).get(&0).unwrap(),
1059            &TileBounds { left: 0, bottom: 0, right: 5, top: 5 }
1060        );
1061
1062        // serialize to JSON and back
1063        let json = serde_json::to_string(&facebounds).unwrap();
1064        assert_eq!(
1065            json,
1066            "{\"0\":{\"0\":[0,0,0,0]},\"1\":{\"0\":[0,0,1,1]},\"2\":{\"0\":[0,0,2,2]},\"3\":{\"0\"\
1067             :[0,0,3,3]},\"4\":{\"0\":[0,0,4,4]},\"5\":{\"0\":[0,0,5,5]}}"
1068        );
1069        let facebounds2 = serde_json::from_str(&json).unwrap();
1070        assert_eq!(facebounds, facebounds2);
1071    }
1072
1073    // DrawType
1074    #[test]
1075    fn test_drawtype() {
1076        assert_eq!(DrawType::from(1), DrawType::Points);
1077        assert_eq!(DrawType::from(2), DrawType::Lines);
1078        assert_eq!(DrawType::from(3), DrawType::Polygons);
1079        assert_eq!(DrawType::from(4), DrawType::Points3D);
1080        assert_eq!(DrawType::from(5), DrawType::Lines3D);
1081        assert_eq!(DrawType::from(6), DrawType::Polygons3D);
1082        assert_eq!(DrawType::from(7), DrawType::Raster);
1083        assert_eq!(DrawType::from(8), DrawType::Grid);
1084
1085        assert_eq!(1, u8::from(DrawType::Points));
1086        assert_eq!(2, u8::from(DrawType::Lines));
1087        assert_eq!(3, u8::from(DrawType::Polygons));
1088        assert_eq!(4, u8::from(DrawType::Points3D));
1089        assert_eq!(5, u8::from(DrawType::Lines3D));
1090        assert_eq!(6, u8::from(DrawType::Polygons3D));
1091        assert_eq!(7, u8::from(DrawType::Raster));
1092        assert_eq!(8, u8::from(DrawType::Grid));
1093
1094        // check json is the number value
1095        let json = serde_json::to_string(&DrawType::Points).unwrap();
1096        assert_eq!(json, "1");
1097        let drawtype: DrawType = serde_json::from_str(&json).unwrap();
1098        assert_eq!(drawtype, DrawType::Points);
1099
1100        let drawtype: DrawType = serde_json::from_str("2").unwrap();
1101        assert_eq!(drawtype, DrawType::Lines);
1102
1103        let drawtype: DrawType = serde_json::from_str("3").unwrap();
1104        assert_eq!(drawtype, DrawType::Polygons);
1105
1106        let drawtype: DrawType = serde_json::from_str("4").unwrap();
1107        assert_eq!(drawtype, DrawType::Points3D);
1108
1109        let drawtype: DrawType = serde_json::from_str("5").unwrap();
1110        assert_eq!(drawtype, DrawType::Lines3D);
1111
1112        let drawtype: DrawType = serde_json::from_str("6").unwrap();
1113        assert_eq!(drawtype, DrawType::Polygons3D);
1114
1115        let drawtype: DrawType = serde_json::from_str("7").unwrap();
1116        assert_eq!(drawtype, DrawType::Raster);
1117
1118        let drawtype: DrawType = serde_json::from_str("8").unwrap();
1119        assert_eq!(drawtype, DrawType::Grid);
1120
1121        assert!(serde_json::from_str::<DrawType>("9").is_err());
1122    }
1123
1124    // SourceType
1125    #[test]
1126    fn test_sourcetype() {
1127        // from string
1128        assert_eq!(SourceType::from("vector"), SourceType::Vector);
1129        assert_eq!(SourceType::from("json"), SourceType::Json);
1130        assert_eq!(SourceType::from("raster"), SourceType::Raster);
1131        assert_eq!(SourceType::from("raster-dem"), SourceType::RasterDem);
1132        assert_eq!(SourceType::from("grid"), SourceType::Grid);
1133        assert_eq!(SourceType::from("markers"), SourceType::Markers);
1134        assert_eq!(SourceType::from("overlay"), SourceType::Unknown);
1135
1136        // json vector
1137        let json = serde_json::to_string(&SourceType::Vector).unwrap();
1138        assert_eq!(json, "\"vector\"");
1139        let sourcetype: SourceType = serde_json::from_str(&json).unwrap();
1140        assert_eq!(sourcetype, SourceType::Vector);
1141
1142        // json json
1143        let json = serde_json::to_string(&SourceType::Json).unwrap();
1144        assert_eq!(json, "\"json\"");
1145        let sourcetype: SourceType = serde_json::from_str(&json).unwrap();
1146        assert_eq!(sourcetype, SourceType::Json);
1147
1148        // json raster
1149        let json = serde_json::to_string(&SourceType::Raster).unwrap();
1150        assert_eq!(json, "\"raster\"");
1151        let sourcetype: SourceType = serde_json::from_str(&json).unwrap();
1152        assert_eq!(sourcetype, SourceType::Raster);
1153
1154        // json raster-dem
1155        let json = serde_json::to_string(&SourceType::RasterDem).unwrap();
1156        assert_eq!(json, "\"raster-dem\"");
1157        let sourcetype: SourceType = serde_json::from_str(&json).unwrap();
1158        assert_eq!(sourcetype, SourceType::RasterDem);
1159
1160        // json grid
1161        let json = serde_json::to_string(&SourceType::Grid).unwrap();
1162        assert_eq!(json, "\"grid\"");
1163        let sourcetype: SourceType = serde_json::from_str(&json).unwrap();
1164        assert_eq!(sourcetype, SourceType::Grid);
1165
1166        // json markers
1167        let json = serde_json::to_string(&SourceType::Markers).unwrap();
1168        assert_eq!(json, "\"markers\"");
1169        let sourcetype: SourceType = serde_json::from_str(&json).unwrap();
1170        assert_eq!(sourcetype, SourceType::Markers);
1171
1172        // json unknown
1173        let json = serde_json::to_string(&SourceType::Unknown).unwrap();
1174        assert_eq!(json, "\"unknown\"");
1175        let sourcetype: SourceType = serde_json::from_str(r#""overlay""#).unwrap();
1176        assert_eq!(sourcetype, SourceType::Unknown);
1177    }
1178
1179    // Encoding
1180    #[test]
1181    fn test_encoding() {
1182        // from string
1183        assert_eq!(Encoding::from("none"), Encoding::None);
1184        assert_eq!(Encoding::from("gzip"), Encoding::Gzip);
1185        assert_eq!(Encoding::from("br"), Encoding::Brotli);
1186        assert_eq!(Encoding::from("zstd"), Encoding::Zstd);
1187
1188        // to string
1189        assert_eq!(core::convert::Into::<&str>::into(Encoding::None), "none");
1190        assert_eq!(core::convert::Into::<&str>::into(Encoding::Gzip), "gzip");
1191        assert_eq!(core::convert::Into::<&str>::into(Encoding::Brotli), "br");
1192        assert_eq!(core::convert::Into::<&str>::into(Encoding::Zstd), "zstd");
1193
1194        // from u8
1195        assert_eq!(Encoding::from(0), Encoding::None);
1196        assert_eq!(Encoding::from(1), Encoding::Gzip);
1197        assert_eq!(Encoding::from(2), Encoding::Brotli);
1198        assert_eq!(Encoding::from(3), Encoding::Zstd);
1199
1200        // to u8
1201        assert_eq!(u8::from(Encoding::None), 0);
1202        assert_eq!(u8::from(Encoding::Gzip), 1);
1203        assert_eq!(u8::from(Encoding::Brotli), 2);
1204        assert_eq!(u8::from(Encoding::Zstd), 3);
1205
1206        // json gzip
1207        let json = serde_json::to_string(&Encoding::Gzip).unwrap();
1208        assert_eq!(json, "\"gzip\"");
1209        let encoding: Encoding = serde_json::from_str(&json).unwrap();
1210        assert_eq!(encoding, Encoding::Gzip);
1211
1212        // json br
1213        let json = serde_json::to_string(&Encoding::Brotli).unwrap();
1214        assert_eq!(json, "\"br\"");
1215        let encoding: Encoding = serde_json::from_str(&json).unwrap();
1216        assert_eq!(encoding, Encoding::Brotli);
1217
1218        // json none
1219        let json = serde_json::to_string(&Encoding::None).unwrap();
1220        assert_eq!(json, "\"none\"");
1221        let encoding: Encoding = serde_json::from_str(&json).unwrap();
1222        assert_eq!(encoding, Encoding::None);
1223
1224        // json zstd
1225        let json = serde_json::to_string(&Encoding::Zstd).unwrap();
1226        assert_eq!(json, "\"zstd\"");
1227        let encoding: Encoding = serde_json::from_str(&json).unwrap();
1228        assert_eq!(encoding, Encoding::Zstd);
1229    }
1230
1231    // Scheme
1232    #[test]
1233    fn test_scheme() {
1234        // from string
1235        assert_eq!(Scheme::from("fzxy"), Scheme::Fzxy);
1236        assert_eq!(Scheme::from("tfzxy"), Scheme::Tfzxy);
1237        assert_eq!(Scheme::from("xyz"), Scheme::Xyz);
1238        assert_eq!(Scheme::from("txyz"), Scheme::Txyz);
1239        assert_eq!(Scheme::from("tms"), Scheme::Tms);
1240
1241        // to string
1242        assert_eq!(core::convert::Into::<&str>::into(Scheme::Fzxy), "fzxy");
1243        assert_eq!(core::convert::Into::<&str>::into(Scheme::Tfzxy), "tfzxy");
1244        assert_eq!(core::convert::Into::<&str>::into(Scheme::Xyz), "xyz");
1245        assert_eq!(core::convert::Into::<&str>::into(Scheme::Txyz), "txyz");
1246        assert_eq!(core::convert::Into::<&str>::into(Scheme::Tms), "tms");
1247    }
1248
1249    #[test]
1250    fn test_tippecanoe_metadata() {
1251        let meta_str = r#"{
1252            "name": "test_fixture_1.pmtiles",
1253            "description": "test_fixture_1.pmtiles",
1254            "version": "2",
1255            "type": "overlay",
1256            "generator": "tippecanoe v2.5.0",
1257            "generator_options": "./tippecanoe -zg -o test_fixture_1.pmtiles --force",
1258            "vector_layers": [
1259                {
1260                    "id": "test_fixture_1pmtiles",
1261                    "description": "",
1262                    "minzoom": 0,
1263                    "maxzoom": 0,
1264                    "fields": {}
1265                }
1266            ],
1267            "tilestats": {
1268                "layerCount": 1,
1269                "layers": [
1270                    {
1271                        "layer": "test_fixture_1pmtiles",
1272                        "count": 1,
1273                        "geometry": "Polygon",
1274                        "attributeCount": 0,
1275                        "attributes": []
1276                    }
1277                ]
1278            }
1279        }"#;
1280
1281        let _meta: Metadata =
1282            serde_json::from_str(meta_str).unwrap_or_else(|e| panic!("ERROR: {}", e));
1283    }
1284
1285    #[test]
1286    fn test_mapbox_metadata() {
1287        let meta_str = r#"{
1288            "tilejson": "3.0.0",
1289            "name": "OpenStreetMap",
1290            "description": "A free editable map of the whole world.",
1291            "version": "1.0.0",
1292            "attribution": "(c) OpenStreetMap contributors, CC-BY-SA",
1293            "scheme": "xyz",
1294            "tiles": [
1295                "https://a.tile.custom-osm-tiles.org/{z}/{x}/{y}.mvt",
1296                "https://b.tile.custom-osm-tiles.org/{z}/{x}/{y}.mvt",
1297                "https://c.tile.custom-osm-tiles.org/{z}/{x}/{y}.mvt"
1298            ],
1299            "minzoom": 0,
1300            "maxzoom": 18,
1301            "bounds": [-180, -85, 180, 85],
1302            "fillzoom": 6,
1303            "something_custom": "this is my unique field",
1304            "vector_layers": [
1305                {
1306                    "id": "telephone",
1307                    "fields": {
1308                        "phone_number": "the phone number",
1309                        "payment": "how to pay"
1310                    }
1311                },
1312                {
1313                    "id": "bicycle_parking",
1314                    "fields": {
1315                        "type": "the type of bike parking",
1316                        "year_installed": "the year the bike parking was installed"
1317                    }
1318                },
1319                {
1320                    "id": "showers",
1321                    "fields": {
1322                        "water_temperature": "the maximum water temperature",
1323                        "wear_sandles": "whether you should wear sandles or not",
1324                        "wheelchair": "is the shower wheelchair friendly?"
1325                    }
1326                }
1327            ]
1328        }"#;
1329
1330        let meta_mapbox: MapboxTileJSONMetadata =
1331            serde_json::from_str(meta_str).unwrap_or_else(|e| panic!("ERROR: {}", e));
1332        let meta_new = meta_mapbox.to_metadata();
1333        assert_eq!(
1334            meta_new,
1335            Metadata {
1336                name: "OpenStreetMap".into(),
1337                description: "A free editable map of the whole world.".into(),
1338                version: "1.0.0".into(),
1339                scheme: Scheme::Xyz,
1340                type_: "vector".into(),
1341                encoding: "none".into(),
1342                extension: "pbf".into(),
1343                attribution: BTreeMap::new(),
1344                vector_layers: meta_mapbox.vector_layers.clone(),
1345                maxzoom: 18,
1346                minzoom: 0,
1347                center: Center { lat: 0.0, lon: 0.0, zoom: 0 },
1348                bounds: WMBounds::default(),
1349                faces: vec![Face::Face0],
1350                facesbounds: FaceBounds::default(),
1351                tilestats: TileStatsMetadata::default(),
1352                layers: LayersMetaData::default(),
1353                s2tilejson: "1.0.0".into(),
1354            },
1355        );
1356    }
1357}