s2_tilejson/
lib.rs

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