1#![no_std]
2#![forbid(unsafe_code)]
3#![deny(missing_docs)]
4extern crate alloc;
7
8use alloc::{
9 borrow::ToOwned,
10 boxed::Box,
11 collections::{BTreeMap, BTreeSet},
12 format,
13 string::String,
14 vec::Vec,
15};
16pub use s2json::*;
17use serde::{Deserialize, Deserializer, Serialize, Serializer};
18
19pub type LonLatBounds = BBox<f64>;
21
22pub type TileBounds = BBox<u64>;
24
25#[derive(Copy, Clone, Debug, PartialEq)]
27pub enum DrawType {
28 Points = 1,
30 Lines = 2,
32 Polygons = 3,
34 Points3D = 4,
36 Lines3D = 5,
38 Polygons3D = 6,
40 Raster = 7,
42 Grid = 8,
44}
45impl From<DrawType> for u8 {
46 fn from(draw_type: DrawType) -> Self {
47 draw_type as u8
48 }
49}
50impl From<u8> for DrawType {
51 fn from(draw_type: u8) -> Self {
52 match draw_type {
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, }
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 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 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#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
97pub struct LayerMetaData {
98 #[serde(skip_serializing_if = "Option::is_none")]
100 pub description: Option<String>,
101 pub minzoom: u8,
103 pub maxzoom: u8,
105 pub draw_types: Vec<DrawType>,
107 pub shape: Shape,
109 #[serde(skip_serializing_if = "Option::is_none", rename = "mShape")]
111 pub m_shape: Option<Shape>,
112}
113
114pub type LayersMetaData = BTreeMap<String, LayerMetaData>;
116
117#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
119pub struct TileStatsMetadata {
120 #[serde(default)]
122 pub total: u64,
123 #[serde(rename = "0", default)]
125 pub total_0: u64,
126 #[serde(rename = "1", default)]
128 pub total_1: u64,
129 #[serde(rename = "2", default)]
131 pub total_2: u64,
132 #[serde(rename = "3", default)]
134 pub total_3: u64,
135 #[serde(rename = "4", default)]
137 pub total_4: u64,
138 #[serde(rename = "5", default)]
140 pub total_5: u64,
141}
142impl TileStatsMetadata {
143 pub fn get(&self, face: Face) -> u64 {
145 match face {
146 Face::Face0 => self.total_0,
147 Face::Face1 => self.total_1,
148 Face::Face2 => self.total_2,
149 Face::Face3 => self.total_3,
150 Face::Face4 => self.total_4,
151 Face::Face5 => self.total_5,
152 }
153 }
154
155 pub fn increment(&mut self, face: Face) {
157 match face {
158 Face::Face0 => self.total_0 += 1,
159 Face::Face1 => self.total_1 += 1,
160 Face::Face2 => self.total_2 += 1,
161 Face::Face3 => self.total_3 += 1,
162 Face::Face4 => self.total_4 += 1,
163 Face::Face5 => self.total_5 += 1,
164 }
165 self.total += 1;
166 }
167}
168
169pub type Attribution = BTreeMap<String, String>;
172
173#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
175pub struct FaceBounds {
176 #[serde(rename = "0")]
179 pub face0: BTreeMap<u8, TileBounds>,
180 #[serde(rename = "1")]
182 pub face1: BTreeMap<u8, TileBounds>,
183 #[serde(rename = "2")]
185 pub face2: BTreeMap<u8, TileBounds>,
186 #[serde(rename = "3")]
188 pub face3: BTreeMap<u8, TileBounds>,
189 #[serde(rename = "4")]
191 pub face4: BTreeMap<u8, TileBounds>,
192 #[serde(rename = "5")]
194 pub face5: BTreeMap<u8, TileBounds>,
195}
196impl FaceBounds {
197 pub fn get(&self, face: Face) -> &BTreeMap<u8, TileBounds> {
199 match face {
200 Face::Face0 => &self.face0,
201 Face::Face1 => &self.face1,
202 Face::Face2 => &self.face2,
203 Face::Face3 => &self.face3,
204 Face::Face4 => &self.face4,
205 Face::Face5 => &self.face5,
206 }
207 }
208
209 pub fn get_mut(&mut self, face: Face) -> &mut BTreeMap<u8, TileBounds> {
211 match face {
212 Face::Face0 => &mut self.face0,
213 Face::Face1 => &mut self.face1,
214 Face::Face2 => &mut self.face2,
215 Face::Face3 => &mut self.face3,
216 Face::Face4 => &mut self.face4,
217 Face::Face5 => &mut self.face5,
218 }
219 }
220}
221
222pub type WMBounds = BTreeMap<u8, TileBounds>;
225
226#[derive(Serialize, Debug, Default, Clone, PartialEq)]
228#[serde(rename_all = "lowercase")]
229pub enum SourceType {
230 #[default]
232 Vector,
233 Json,
235 Raster,
237 #[serde(rename = "raster-dem")]
239 RasterDem,
240 Grid,
242 Markers,
244 Unknown,
246}
247impl From<&str> for SourceType {
248 fn from(source_type: &str) -> Self {
249 match source_type {
250 "vector" => SourceType::Vector,
251 "json" => SourceType::Json,
252 "raster" => SourceType::Raster,
253 "raster-dem" => SourceType::RasterDem,
254 "grid" => SourceType::Grid,
255 "markers" => SourceType::Markers,
256 _ => SourceType::Unknown,
257 }
258 }
259}
260impl<'de> Deserialize<'de> for SourceType {
261 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
262 where
263 D: Deserializer<'de>,
264 {
265 let s: String = Deserialize::deserialize(deserializer)?;
267 Ok(SourceType::from(s.as_str()))
268 }
269}
270
271#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
273#[serde(rename_all = "lowercase")]
274pub enum Encoding {
275 #[default]
277 None = 0,
278 Gzip = 1,
280 #[serde(rename = "br")]
282 Brotli = 2,
283 Zstd = 3,
285}
286impl From<u8> for Encoding {
287 fn from(encoding: u8) -> Self {
288 match encoding {
289 1 => Encoding::Gzip,
290 2 => Encoding::Brotli,
291 3 => Encoding::Zstd,
292 _ => Encoding::None,
293 }
294 }
295}
296impl From<Encoding> for u8 {
297 fn from(encoding: Encoding) -> Self {
298 match encoding {
299 Encoding::Gzip => 1,
300 Encoding::Brotli => 2,
301 Encoding::Zstd => 3,
302 Encoding::None => 0,
303 }
304 }
305}
306impl From<Encoding> for &str {
307 fn from(encoding: Encoding) -> Self {
308 match encoding {
309 Encoding::Gzip => "gzip",
310 Encoding::Brotli => "br",
311 Encoding::Zstd => "zstd",
312 Encoding::None => "none",
313 }
314 }
315}
316impl From<&str> for Encoding {
317 fn from(encoding: &str) -> Self {
318 match encoding {
319 "gzip" => Encoding::Gzip,
320 "br" => Encoding::Brotli,
321 "zstd" => Encoding::Zstd,
322 _ => Encoding::None,
323 }
324 }
325}
326
327#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
329pub struct VectorLayer {
330 pub id: String,
332 #[serde(skip_serializing_if = "Option::is_none")]
334 pub description: Option<String>,
335 #[serde(skip_serializing_if = "Option::is_none")]
337 pub minzoom: Option<u8>,
338 #[serde(skip_serializing_if = "Option::is_none")]
340 pub maxzoom: Option<u8>,
341 pub fields: BTreeMap<String, String>,
343}
344
345#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
350#[serde(rename_all = "lowercase")]
351pub enum Scheme {
352 #[default]
354 Fzxy,
355 Tfzxy,
357 Xyz,
359 Txyz,
361 Tms,
363}
364impl From<&str> for Scheme {
365 fn from(scheme: &str) -> Self {
366 match scheme {
367 "fzxy" => Scheme::Fzxy,
368 "tfzxy" => Scheme::Tfzxy,
369 "xyz" => Scheme::Xyz,
370 "txyz" => Scheme::Txyz,
371 _ => Scheme::Tms,
372 }
373 }
374}
375impl From<Scheme> for &str {
376 fn from(scheme: Scheme) -> Self {
377 match scheme {
378 Scheme::Fzxy => "fzxy",
379 Scheme::Tfzxy => "tfzxy",
380 Scheme::Xyz => "xyz",
381 Scheme::Txyz => "txyz",
382 Scheme::Tms => "tms",
383 }
384 }
385}
386
387#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
389pub struct Center {
390 pub lon: f64,
392 pub lat: f64,
394 pub zoom: u8,
396}
397
398#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
400#[serde(default)]
401pub struct Metadata {
402 pub s2tilejson: String,
404 pub version: String,
406 pub name: String,
408 pub scheme: Scheme,
410 pub description: String,
412 #[serde(rename = "type")]
414 pub type_: SourceType,
415 pub extension: String,
417 pub encoding: Encoding,
419 pub faces: Vec<Face>,
421 pub bounds: WMBounds,
423 pub facesbounds: FaceBounds,
425 pub minzoom: u8,
427 pub maxzoom: u8,
429 pub center: Center,
431 pub attribution: Attribution,
433 pub layers: LayersMetaData,
435 pub tilestats: TileStatsMetadata,
437 pub vector_layers: Vec<VectorLayer>,
439}
440impl Default for Metadata {
441 fn default() -> Self {
442 Self {
443 s2tilejson: "1.0.0".into(),
444 version: "1.0.0".into(),
445 name: "default".into(),
446 scheme: Scheme::default(),
447 description: "Built with s2maps-cli".into(),
448 type_: SourceType::default(),
449 extension: "pbf".into(),
450 encoding: Encoding::default(),
451 faces: Vec::new(),
452 bounds: WMBounds::default(),
453 facesbounds: FaceBounds::default(),
454 minzoom: 0,
455 maxzoom: 27,
456 center: Center::default(),
457 attribution: BTreeMap::new(),
458 layers: LayersMetaData::default(),
459 tilestats: TileStatsMetadata::default(),
460 vector_layers: Vec::new(),
461 }
462 }
463}
464
465#[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq)]
479#[serde(default)]
480pub struct MapboxTileJSONMetadata {
481 pub tilejson: String,
484 pub tiles: Vec<String>,
486 pub vector_layers: Vec<VectorLayer>,
488 pub attribution: Option<String>,
490 pub bounds: Option<BBox>,
492 pub center: Option<[f64; 3]>,
494 pub data: Option<Vec<String>>,
496 pub description: Option<String>,
498 pub fillzoom: Option<u8>,
500 pub grids: Option<Vec<String>>,
502 pub legend: Option<String>,
504 pub maxzoom: Option<u8>,
506 pub minzoom: Option<u8>,
508 pub name: Option<String>,
510 pub scheme: Option<Scheme>,
512 pub template: Option<String>,
514 pub version: Option<String>,
516}
517impl MapboxTileJSONMetadata {
518 pub fn to_metadata(&self) -> Metadata {
520 Metadata {
521 s2tilejson: "1.0.0".into(),
522 version: self.version.clone().unwrap_or("1.0.0".into()),
523 name: self.name.clone().unwrap_or("default".into()),
524 scheme: self.scheme.clone().unwrap_or_default(),
525 description: self.description.clone().unwrap_or("Built with s2maps-cli".into()),
526 type_: SourceType::default(),
527 extension: "pbf".into(),
528 faces: Vec::from([Face::Face0]),
529 bounds: WMBounds::default(),
530 facesbounds: FaceBounds::default(),
531 minzoom: self.minzoom.unwrap_or(0),
532 maxzoom: self.maxzoom.unwrap_or(27),
533 center: Center {
534 lon: self.center.unwrap_or([0.0, 0.0, 0.0])[0],
535 lat: self.center.unwrap_or([0.0, 0.0, 0.0])[1],
536 zoom: self.center.unwrap_or([0.0, 0.0, 0.0])[2] as u8,
537 },
538 attribution: BTreeMap::new(),
539 layers: LayersMetaData::default(),
540 tilestats: TileStatsMetadata::default(),
541 vector_layers: self.vector_layers.clone(),
542 encoding: Encoding::default(),
543 }
544 }
545}
546
547#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
549#[serde(untagged)]
550pub enum UnknownMetadata {
551 Metadata(Box<Metadata>),
553 Mapbox(Box<MapboxTileJSONMetadata>),
555}
556impl UnknownMetadata {
557 pub fn to_metadata(&self) -> Metadata {
559 match self {
560 UnknownMetadata::Metadata(m) => *m.clone(),
561 UnknownMetadata::Mapbox(m) => m.to_metadata(),
562 }
563 }
564}
565
566#[derive(Debug, Clone)]
568pub struct MetadataBuilder {
569 lon_lat_bounds: LonLatBounds,
570 faces: BTreeSet<Face>,
571 metadata: Metadata,
572}
573impl Default for MetadataBuilder {
574 fn default() -> Self {
575 MetadataBuilder {
576 lon_lat_bounds: BBox {
577 left: f64::INFINITY,
578 bottom: f64::INFINITY,
579 right: -f64::INFINITY,
580 top: -f64::INFINITY,
581 },
582 faces: BTreeSet::new(),
583 metadata: Metadata { minzoom: 30, maxzoom: 0, ..Metadata::default() },
584 }
585 }
586}
587impl MetadataBuilder {
588 pub fn commit(&mut self) -> Metadata {
590 self.update_center();
592 for face in &self.faces {
594 self.metadata.faces.push(*face);
595 }
596 self.metadata.to_owned()
598 }
599
600 pub fn set_name(&mut self, name: String) {
602 self.metadata.name = name;
603 }
604
605 pub fn set_scheme(&mut self, scheme: Scheme) {
607 self.metadata.scheme = scheme;
608 }
609
610 pub fn set_extension(&mut self, extension: String) {
612 self.metadata.extension = extension;
613 }
614
615 pub fn set_type(&mut self, type_: SourceType) {
617 self.metadata.type_ = type_;
618 }
619
620 pub fn set_version(&mut self, version: String) {
622 self.metadata.version = version;
623 }
624
625 pub fn set_description(&mut self, description: String) {
627 self.metadata.description = description;
628 }
629
630 pub fn set_encoding(&mut self, encoding: Encoding) {
632 self.metadata.encoding = encoding;
633 }
634
635 pub fn add_attribution(&mut self, display_name: &str, href: &str) {
637 self.metadata.attribution.insert(display_name.into(), href.into());
638 }
639
640 pub fn add_layer(&mut self, name: &str, layer: &LayerMetaData) {
642 if self.metadata.layers.entry(name.into()).or_insert(layer.clone()).eq(&layer) {
644 self.metadata.vector_layers.push(VectorLayer {
646 id: name.into(), description: layer.description.clone(),
648 minzoom: Some(layer.minzoom),
649 maxzoom: Some(layer.maxzoom),
650 fields: BTreeMap::new(),
651 });
652 }
653 if layer.minzoom < self.metadata.minzoom {
655 self.metadata.minzoom = layer.minzoom;
656 }
657 if layer.maxzoom > self.metadata.maxzoom {
658 self.metadata.maxzoom = layer.maxzoom;
659 }
660 }
661
662 pub fn add_tile_wm(&mut self, zoom: u8, x: u32, y: u32, ll_bounds: &LonLatBounds) {
664 self.metadata.tilestats.total += 1;
665 self.faces.insert(Face::Face0);
666 self.add_bounds_wm(zoom, x, y);
667 self.update_lon_lat_bounds(ll_bounds);
668 }
669
670 pub fn add_tile_s2(&mut self, face: Face, zoom: u8, x: u32, y: u32, ll_bounds: &LonLatBounds) {
672 self.metadata.tilestats.increment(face);
673 self.faces.insert(face);
674 self.add_bounds_s2(face, zoom, x, y);
675 self.update_lon_lat_bounds(ll_bounds);
676 }
677
678 fn update_center(&mut self) {
680 let Metadata { minzoom, maxzoom, .. } = self.metadata;
681 let BBox { left, bottom, right, top } = self.lon_lat_bounds;
682 self.metadata.center.lon = (left + right) / 2.0;
683 self.metadata.center.lat = (bottom + top) / 2.0;
684 self.metadata.center.zoom = (minzoom + maxzoom) >> 1;
685 }
686
687 fn add_bounds_wm(&mut self, zoom: u8, x: u32, y: u32) {
689 let x = x as u64;
690 let y = y as u64;
691 let bbox = self.metadata.bounds.entry(zoom).or_insert(BBox {
692 left: u64::MAX,
693 bottom: u64::MAX,
694 right: 0,
695 top: 0,
696 });
697
698 bbox.left = bbox.left.min(x);
699 bbox.bottom = bbox.bottom.min(y);
700 bbox.right = bbox.right.max(x);
701 bbox.top = bbox.top.max(y);
702 }
703
704 fn add_bounds_s2(&mut self, face: Face, zoom: u8, x: u32, y: u32) {
706 let x = x as u64;
707 let y = y as u64;
708 let bbox = self.metadata.facesbounds.get_mut(face).entry(zoom).or_insert(BBox {
709 left: u64::MAX,
710 bottom: u64::MAX,
711 right: 0,
712 top: 0,
713 });
714
715 bbox.left = bbox.left.min(x);
716 bbox.bottom = bbox.bottom.min(y);
717 bbox.right = bbox.right.max(x);
718 bbox.top = bbox.top.max(y);
719 }
720
721 fn update_lon_lat_bounds(&mut self, ll_bounds: &LonLatBounds) {
723 self.lon_lat_bounds.left = ll_bounds.left.min(self.lon_lat_bounds.left);
724 self.lon_lat_bounds.bottom = ll_bounds.bottom.min(self.lon_lat_bounds.bottom);
725 self.lon_lat_bounds.right = ll_bounds.right.max(self.lon_lat_bounds.right);
726 self.lon_lat_bounds.top = ll_bounds.top.max(self.lon_lat_bounds.top);
727 }
728}
729
730#[cfg(test)]
731mod tests {
732 use super::*;
733 use alloc::vec;
734 use s2json::{PrimitiveShape, ShapeType};
735
736 #[test]
737 fn it_works() {
738 let mut meta_builder = MetadataBuilder::default();
739
740 meta_builder.set_name("OSM".into());
742 meta_builder.set_description("A free editable map of the whole world.".into());
743 meta_builder.set_version("1.0.0".into());
744 meta_builder.set_scheme("fzxy".into()); meta_builder.set_type("vector".into()); meta_builder.set_encoding("none".into()); meta_builder.set_extension("pbf".into());
748 meta_builder.add_attribution("OpenStreetMap", "https://www.openstreetmap.org/copyright/");
749
750 let shape_str = r#"
752 {
753 "class": "string",
754 "offset": "f64",
755 "info": {
756 "name": "string",
757 "value": "i64"
758 }
759 }
760 "#;
761 let shape: Shape =
762 serde_json::from_str(shape_str).unwrap_or_else(|e| panic!("ERROR: {}", e));
763 let layer = LayerMetaData {
764 minzoom: 0,
765 maxzoom: 13,
766 description: Some("water_lines".into()),
767 draw_types: Vec::from(&[DrawType::Lines]),
768 shape: shape.clone(),
769 m_shape: None,
770 };
771 meta_builder.add_layer("water_lines", &layer);
772
773 meta_builder.add_tile_wm(
776 0,
777 0,
778 0,
779 &LonLatBounds { left: -60.0, bottom: -20.0, right: 5.0, top: 60.0 },
780 );
781 meta_builder.add_tile_s2(
783 Face::Face1,
784 5,
785 22,
786 37,
787 &LonLatBounds { left: -120.0, bottom: -7.0, right: 44.0, top: 72.0 },
788 );
789
790 let resulting_metadata: Metadata = meta_builder.commit();
792
793 assert_eq!(
794 resulting_metadata,
795 Metadata {
796 name: "OSM".into(),
797 description: "A free editable map of the whole world.".into(),
798 version: "1.0.0".into(),
799 scheme: "fzxy".into(),
800 type_: "vector".into(),
801 encoding: "none".into(),
802 extension: "pbf".into(),
803 attribution: BTreeMap::from([(
804 "OpenStreetMap".into(),
805 "https://www.openstreetmap.org/copyright/".into()
806 ),]),
807 bounds: BTreeMap::from([(0, TileBounds { left: 0, bottom: 0, right: 0, top: 0 }),]),
808 faces: Vec::from(&[Face::Face0, Face::Face1]),
809 facesbounds: FaceBounds {
810 face0: BTreeMap::new(),
811 face1: BTreeMap::from([(
812 5,
813 TileBounds { left: 22, bottom: 37, right: 22, top: 37 }
814 ),]),
815 face2: BTreeMap::new(),
816 face3: BTreeMap::new(),
817 face4: BTreeMap::new(),
818 face5: BTreeMap::new(),
819 },
820 minzoom: 0,
821 maxzoom: 13,
822 center: Center { lon: -38.0, lat: 26.0, zoom: 6 },
823 tilestats: TileStatsMetadata {
824 total: 2,
825 total_0: 0,
826 total_1: 1,
827 total_2: 0,
828 total_3: 0,
829 total_4: 0,
830 total_5: 0,
831 },
832 layers: BTreeMap::from([(
833 "water_lines".into(),
834 LayerMetaData {
835 description: Some("water_lines".into()),
836 minzoom: 0,
837 maxzoom: 13,
838 draw_types: Vec::from(&[DrawType::Lines]),
839 shape: Shape::from([
840 ("class".into(), ShapeType::Primitive(PrimitiveShape::String)),
841 ("offset".into(), ShapeType::Primitive(PrimitiveShape::F64)),
842 (
843 "info".into(),
844 ShapeType::Nested(Shape::from([
845 ("name".into(), ShapeType::Primitive(PrimitiveShape::String)),
846 ("value".into(), ShapeType::Primitive(PrimitiveShape::I64)),
847 ]))
848 ),
849 ]),
850 m_shape: None,
851 }
852 )]),
853 s2tilejson: "1.0.0".into(),
854 vector_layers: Vec::from([VectorLayer {
855 id: "water_lines".into(),
856 description: Some("water_lines".into()),
857 minzoom: Some(0),
858 maxzoom: Some(13),
859 fields: BTreeMap::new()
860 }]),
861 }
862 );
863
864 let meta_str = serde_json::to_string(&resulting_metadata).unwrap();
865
866 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\":{}}]}");
867
868 let meta_reparsed: Metadata =
869 serde_json::from_str(&meta_str).unwrap_or_else(|e| panic!("ERROR: {}", e));
870 assert_eq!(meta_reparsed, resulting_metadata);
871 }
872
873 #[test]
874 fn test_face() {
875 assert_eq!(Face::Face0, Face::from(0));
876 assert_eq!(Face::Face1, Face::from(1));
877 assert_eq!(Face::Face2, Face::from(2));
878 assert_eq!(Face::Face3, Face::from(3));
879 assert_eq!(Face::Face4, Face::from(4));
880 assert_eq!(Face::Face5, Face::from(5));
881
882 assert_eq!(0, u8::from(Face::Face0));
883 assert_eq!(1, u8::from(Face::Face1));
884 assert_eq!(2, u8::from(Face::Face2));
885 assert_eq!(3, u8::from(Face::Face3));
886 assert_eq!(4, u8::from(Face::Face4));
887 assert_eq!(5, u8::from(Face::Face5));
888 }
889
890 #[test]
891 fn test_bbox() {
892 let bbox: BBox = BBox { left: 0.0, bottom: 0.0, right: 0.0, top: 0.0 };
893 let json = serde_json::to_string(&bbox).unwrap();
895 assert_eq!(json, r#"[0.0,0.0,0.0,0.0]"#);
896 let bbox2: BBox = serde_json::from_str(&json).unwrap();
897 assert_eq!(bbox, bbox2);
898 }
899
900 #[test]
902 fn test_tilestats() {
903 let mut tilestats = TileStatsMetadata {
904 total: 2,
905 total_0: 0,
906 total_1: 1,
907 total_2: 0,
908 total_3: 0,
909 total_4: 0,
910 total_5: 0,
911 };
912 let json = serde_json::to_string(&tilestats).unwrap();
914 assert_eq!(json, r#"{"total":2,"0":0,"1":1,"2":0,"3":0,"4":0,"5":0}"#);
915 let tilestats2: TileStatsMetadata = serde_json::from_str(&json).unwrap();
916 assert_eq!(tilestats, tilestats2);
917
918 assert_eq!(tilestats.get(0.into()), 0);
920 tilestats.increment(0.into());
922 assert_eq!(tilestats.get(0.into()), 1);
923
924 assert_eq!(tilestats.get(1.into()), 1);
926 tilestats.increment(1.into());
928 assert_eq!(tilestats.get(1.into()), 2);
929
930 assert_eq!(tilestats.get(2.into()), 0);
932 tilestats.increment(2.into());
934 assert_eq!(tilestats.get(2.into()), 1);
935
936 assert_eq!(tilestats.get(3.into()), 0);
938 tilestats.increment(3.into());
940 assert_eq!(tilestats.get(3.into()), 1);
941
942 assert_eq!(tilestats.get(4.into()), 0);
944 tilestats.increment(4.into());
946 assert_eq!(tilestats.get(4.into()), 1);
947
948 assert_eq!(tilestats.get(5.into()), 0);
950 tilestats.increment(5.into());
952 assert_eq!(tilestats.get(5.into()), 1);
953 }
954
955 #[test]
957 fn test_facebounds() {
958 let mut facebounds = FaceBounds::default();
959 let face0 = facebounds.get_mut(0.into());
961 face0.insert(0, TileBounds { left: 0, bottom: 0, right: 0, top: 0 });
962 let face1 = facebounds.get_mut(1.into());
964 face1.insert(0, TileBounds { left: 0, bottom: 0, right: 1, top: 1 });
965 let face2 = facebounds.get_mut(2.into());
967 face2.insert(0, TileBounds { left: 0, bottom: 0, right: 2, top: 2 });
968 let face3 = facebounds.get_mut(3.into());
970 face3.insert(0, TileBounds { left: 0, bottom: 0, right: 3, top: 3 });
971 let face4 = facebounds.get_mut(4.into());
973 face4.insert(0, TileBounds { left: 0, bottom: 0, right: 4, top: 4 });
974 let face5 = facebounds.get_mut(5.into());
976 face5.insert(0, TileBounds { left: 0, bottom: 0, right: 5, top: 5 });
977
978 assert_eq!(
981 facebounds.get(0.into()).get(&0).unwrap(),
982 &TileBounds { left: 0, bottom: 0, right: 0, top: 0 }
983 );
984 assert_eq!(
986 facebounds.get(1.into()).get(&0).unwrap(),
987 &TileBounds { left: 0, bottom: 0, right: 1, top: 1 }
988 );
989 assert_eq!(
991 facebounds.get(2.into()).get(&0).unwrap(),
992 &TileBounds { left: 0, bottom: 0, right: 2, top: 2 }
993 );
994 assert_eq!(
996 facebounds.get(3.into()).get(&0).unwrap(),
997 &TileBounds { left: 0, bottom: 0, right: 3, top: 3 }
998 );
999 assert_eq!(
1001 facebounds.get(4.into()).get(&0).unwrap(),
1002 &TileBounds { left: 0, bottom: 0, right: 4, top: 4 }
1003 );
1004 assert_eq!(
1006 facebounds.get(5.into()).get(&0).unwrap(),
1007 &TileBounds { left: 0, bottom: 0, right: 5, top: 5 }
1008 );
1009
1010 let json = serde_json::to_string(&facebounds).unwrap();
1012 assert_eq!(
1013 json,
1014 "{\"0\":{\"0\":[0,0,0,0]},\"1\":{\"0\":[0,0,1,1]},\"2\":{\"0\":[0,0,2,2]},\"3\":{\"0\"\
1015 :[0,0,3,3]},\"4\":{\"0\":[0,0,4,4]},\"5\":{\"0\":[0,0,5,5]}}"
1016 );
1017 let facebounds2 = serde_json::from_str(&json).unwrap();
1018 assert_eq!(facebounds, facebounds2);
1019 }
1020
1021 #[test]
1023 fn test_drawtype() {
1024 assert_eq!(DrawType::from(1), DrawType::Points);
1025 assert_eq!(DrawType::from(2), DrawType::Lines);
1026 assert_eq!(DrawType::from(3), DrawType::Polygons);
1027 assert_eq!(DrawType::from(4), DrawType::Points3D);
1028 assert_eq!(DrawType::from(5), DrawType::Lines3D);
1029 assert_eq!(DrawType::from(6), DrawType::Polygons3D);
1030 assert_eq!(DrawType::from(7), DrawType::Raster);
1031 assert_eq!(DrawType::from(8), DrawType::Grid);
1032
1033 assert_eq!(1, u8::from(DrawType::Points));
1034 assert_eq!(2, u8::from(DrawType::Lines));
1035 assert_eq!(3, u8::from(DrawType::Polygons));
1036 assert_eq!(4, u8::from(DrawType::Points3D));
1037 assert_eq!(5, u8::from(DrawType::Lines3D));
1038 assert_eq!(6, u8::from(DrawType::Polygons3D));
1039 assert_eq!(7, u8::from(DrawType::Raster));
1040 assert_eq!(8, u8::from(DrawType::Grid));
1041
1042 let json = serde_json::to_string(&DrawType::Points).unwrap();
1044 assert_eq!(json, "1");
1045 let drawtype: DrawType = serde_json::from_str(&json).unwrap();
1046 assert_eq!(drawtype, DrawType::Points);
1047
1048 let drawtype: DrawType = serde_json::from_str("2").unwrap();
1049 assert_eq!(drawtype, DrawType::Lines);
1050
1051 let drawtype: DrawType = serde_json::from_str("3").unwrap();
1052 assert_eq!(drawtype, DrawType::Polygons);
1053
1054 let drawtype: DrawType = serde_json::from_str("4").unwrap();
1055 assert_eq!(drawtype, DrawType::Points3D);
1056
1057 let drawtype: DrawType = serde_json::from_str("5").unwrap();
1058 assert_eq!(drawtype, DrawType::Lines3D);
1059
1060 let drawtype: DrawType = serde_json::from_str("6").unwrap();
1061 assert_eq!(drawtype, DrawType::Polygons3D);
1062
1063 let drawtype: DrawType = serde_json::from_str("7").unwrap();
1064 assert_eq!(drawtype, DrawType::Raster);
1065
1066 let drawtype: DrawType = serde_json::from_str("8").unwrap();
1067 assert_eq!(drawtype, DrawType::Grid);
1068
1069 assert!(serde_json::from_str::<DrawType>("9").is_err());
1070 }
1071
1072 #[test]
1074 fn test_sourcetype() {
1075 assert_eq!(SourceType::from("vector"), SourceType::Vector);
1077 assert_eq!(SourceType::from("json"), SourceType::Json);
1078 assert_eq!(SourceType::from("raster"), SourceType::Raster);
1079 assert_eq!(SourceType::from("raster-dem"), SourceType::RasterDem);
1080 assert_eq!(SourceType::from("grid"), SourceType::Grid);
1081 assert_eq!(SourceType::from("markers"), SourceType::Markers);
1082 assert_eq!(SourceType::from("overlay"), SourceType::Unknown);
1083
1084 let json = serde_json::to_string(&SourceType::Vector).unwrap();
1086 assert_eq!(json, "\"vector\"");
1087 let sourcetype: SourceType = serde_json::from_str(&json).unwrap();
1088 assert_eq!(sourcetype, SourceType::Vector);
1089
1090 let json = serde_json::to_string(&SourceType::Json).unwrap();
1092 assert_eq!(json, "\"json\"");
1093 let sourcetype: SourceType = serde_json::from_str(&json).unwrap();
1094 assert_eq!(sourcetype, SourceType::Json);
1095
1096 let json = serde_json::to_string(&SourceType::Raster).unwrap();
1098 assert_eq!(json, "\"raster\"");
1099 let sourcetype: SourceType = serde_json::from_str(&json).unwrap();
1100 assert_eq!(sourcetype, SourceType::Raster);
1101
1102 let json = serde_json::to_string(&SourceType::RasterDem).unwrap();
1104 assert_eq!(json, "\"raster-dem\"");
1105 let sourcetype: SourceType = serde_json::from_str(&json).unwrap();
1106 assert_eq!(sourcetype, SourceType::RasterDem);
1107
1108 let json = serde_json::to_string(&SourceType::Grid).unwrap();
1110 assert_eq!(json, "\"grid\"");
1111 let sourcetype: SourceType = serde_json::from_str(&json).unwrap();
1112 assert_eq!(sourcetype, SourceType::Grid);
1113
1114 let json = serde_json::to_string(&SourceType::Markers).unwrap();
1116 assert_eq!(json, "\"markers\"");
1117 let sourcetype: SourceType = serde_json::from_str(&json).unwrap();
1118 assert_eq!(sourcetype, SourceType::Markers);
1119
1120 let json = serde_json::to_string(&SourceType::Unknown).unwrap();
1122 assert_eq!(json, "\"unknown\"");
1123 let sourcetype: SourceType = serde_json::from_str(r#""overlay""#).unwrap();
1124 assert_eq!(sourcetype, SourceType::Unknown);
1125 }
1126
1127 #[test]
1129 fn test_encoding() {
1130 assert_eq!(Encoding::from("none"), Encoding::None);
1132 assert_eq!(Encoding::from("gzip"), Encoding::Gzip);
1133 assert_eq!(Encoding::from("br"), Encoding::Brotli);
1134 assert_eq!(Encoding::from("zstd"), Encoding::Zstd);
1135
1136 assert_eq!(core::convert::Into::<&str>::into(Encoding::None), "none");
1138 assert_eq!(core::convert::Into::<&str>::into(Encoding::Gzip), "gzip");
1139 assert_eq!(core::convert::Into::<&str>::into(Encoding::Brotli), "br");
1140 assert_eq!(core::convert::Into::<&str>::into(Encoding::Zstd), "zstd");
1141
1142 assert_eq!(Encoding::from(0), Encoding::None);
1144 assert_eq!(Encoding::from(1), Encoding::Gzip);
1145 assert_eq!(Encoding::from(2), Encoding::Brotli);
1146 assert_eq!(Encoding::from(3), Encoding::Zstd);
1147
1148 assert_eq!(u8::from(Encoding::None), 0);
1150 assert_eq!(u8::from(Encoding::Gzip), 1);
1151 assert_eq!(u8::from(Encoding::Brotli), 2);
1152 assert_eq!(u8::from(Encoding::Zstd), 3);
1153
1154 let json = serde_json::to_string(&Encoding::Gzip).unwrap();
1156 assert_eq!(json, "\"gzip\"");
1157 let encoding: Encoding = serde_json::from_str(&json).unwrap();
1158 assert_eq!(encoding, Encoding::Gzip);
1159
1160 let json = serde_json::to_string(&Encoding::Brotli).unwrap();
1162 assert_eq!(json, "\"br\"");
1163 let encoding: Encoding = serde_json::from_str(&json).unwrap();
1164 assert_eq!(encoding, Encoding::Brotli);
1165
1166 let json = serde_json::to_string(&Encoding::None).unwrap();
1168 assert_eq!(json, "\"none\"");
1169 let encoding: Encoding = serde_json::from_str(&json).unwrap();
1170 assert_eq!(encoding, Encoding::None);
1171
1172 let json = serde_json::to_string(&Encoding::Zstd).unwrap();
1174 assert_eq!(json, "\"zstd\"");
1175 let encoding: Encoding = serde_json::from_str(&json).unwrap();
1176 assert_eq!(encoding, Encoding::Zstd);
1177 }
1178
1179 #[test]
1181 fn test_scheme() {
1182 assert_eq!(Scheme::from("fzxy"), Scheme::Fzxy);
1184 assert_eq!(Scheme::from("tfzxy"), Scheme::Tfzxy);
1185 assert_eq!(Scheme::from("xyz"), Scheme::Xyz);
1186 assert_eq!(Scheme::from("txyz"), Scheme::Txyz);
1187 assert_eq!(Scheme::from("tms"), Scheme::Tms);
1188
1189 assert_eq!(core::convert::Into::<&str>::into(Scheme::Fzxy), "fzxy");
1191 assert_eq!(core::convert::Into::<&str>::into(Scheme::Tfzxy), "tfzxy");
1192 assert_eq!(core::convert::Into::<&str>::into(Scheme::Xyz), "xyz");
1193 assert_eq!(core::convert::Into::<&str>::into(Scheme::Txyz), "txyz");
1194 assert_eq!(core::convert::Into::<&str>::into(Scheme::Tms), "tms");
1195 }
1196
1197 #[test]
1198 fn test_tippecanoe_metadata() {
1199 let meta_str = r#"{
1200 "name": "test_fixture_1.pmtiles",
1201 "description": "test_fixture_1.pmtiles",
1202 "version": "2",
1203 "type": "overlay",
1204 "generator": "tippecanoe v2.5.0",
1205 "generator_options": "./tippecanoe -zg -o test_fixture_1.pmtiles --force",
1206 "vector_layers": [
1207 {
1208 "id": "test_fixture_1pmtiles",
1209 "description": "",
1210 "minzoom": 0,
1211 "maxzoom": 0,
1212 "fields": {}
1213 }
1214 ],
1215 "tilestats": {
1216 "layerCount": 1,
1217 "layers": [
1218 {
1219 "layer": "test_fixture_1pmtiles",
1220 "count": 1,
1221 "geometry": "Polygon",
1222 "attributeCount": 0,
1223 "attributes": []
1224 }
1225 ]
1226 }
1227 }"#;
1228
1229 let _meta: Metadata =
1230 serde_json::from_str(meta_str).unwrap_or_else(|e| panic!("ERROR: {}", e));
1231 }
1232
1233 #[test]
1234 fn test_mapbox_metadata() {
1235 let meta_str = r#"{
1236 "tilejson": "3.0.0",
1237 "name": "OpenStreetMap",
1238 "description": "A free editable map of the whole world.",
1239 "version": "1.0.0",
1240 "attribution": "(c) OpenStreetMap contributors, CC-BY-SA",
1241 "scheme": "xyz",
1242 "tiles": [
1243 "https://a.tile.custom-osm-tiles.org/{z}/{x}/{y}.mvt",
1244 "https://b.tile.custom-osm-tiles.org/{z}/{x}/{y}.mvt",
1245 "https://c.tile.custom-osm-tiles.org/{z}/{x}/{y}.mvt"
1246 ],
1247 "minzoom": 0,
1248 "maxzoom": 18,
1249 "bounds": [-180, -85, 180, 85],
1250 "fillzoom": 6,
1251 "something_custom": "this is my unique field",
1252 "vector_layers": [
1253 {
1254 "id": "telephone",
1255 "fields": {
1256 "phone_number": "the phone number",
1257 "payment": "how to pay"
1258 }
1259 },
1260 {
1261 "id": "bicycle_parking",
1262 "fields": {
1263 "type": "the type of bike parking",
1264 "year_installed": "the year the bike parking was installed"
1265 }
1266 },
1267 {
1268 "id": "showers",
1269 "fields": {
1270 "water_temperature": "the maximum water temperature",
1271 "wear_sandles": "whether you should wear sandles or not",
1272 "wheelchair": "is the shower wheelchair friendly?"
1273 }
1274 }
1275 ]
1276 }"#;
1277
1278 let meta_mapbox: MapboxTileJSONMetadata =
1279 serde_json::from_str(meta_str).unwrap_or_else(|e| panic!("ERROR: {}", e));
1280 let meta_new = meta_mapbox.to_metadata();
1281 assert_eq!(
1282 meta_new,
1283 Metadata {
1284 name: "OpenStreetMap".into(),
1285 description: "A free editable map of the whole world.".into(),
1286 version: "1.0.0".into(),
1287 scheme: Scheme::Xyz,
1288 type_: "vector".into(),
1289 encoding: "none".into(),
1290 extension: "pbf".into(),
1291 attribution: BTreeMap::new(),
1292 vector_layers: meta_mapbox.vector_layers.clone(),
1293 maxzoom: 18,
1294 minzoom: 0,
1295 center: Center { lat: 0.0, lon: 0.0, zoom: 0 },
1296 bounds: WMBounds::default(),
1297 faces: vec![Face::Face0],
1298 facesbounds: FaceBounds::default(),
1299 tilestats: TileStatsMetadata::default(),
1300 layers: LayersMetaData::default(),
1301 s2tilejson: "1.0.0".into(),
1302 },
1303 );
1304
1305 let meta_mapbox_from_unknown: UnknownMetadata =
1306 serde_json::from_str(meta_str).unwrap_or_else(|e| panic!("ERROR: {}", e));
1307 let meta_new = meta_mapbox_from_unknown.to_metadata();
1308 assert_eq!(
1309 meta_new,
1310 Metadata {
1311 name: "OpenStreetMap".into(),
1312 description: "A free editable map of the whole world.".into(),
1313 version: "1.0.0".into(),
1314 scheme: Scheme::Xyz,
1315 type_: "vector".into(),
1316 encoding: "none".into(),
1317 extension: "pbf".into(),
1318 attribution: BTreeMap::new(),
1319 vector_layers: meta_mapbox.vector_layers.clone(),
1320 maxzoom: 18,
1321 minzoom: 0,
1322 center: Center { lat: 0.0, lon: 0.0, zoom: 0 },
1323 bounds: WMBounds::default(),
1324 faces: vec![Face::Face0],
1325 facesbounds: FaceBounds::default(),
1326 tilestats: TileStatsMetadata::default(),
1327 layers: LayersMetaData::default(),
1328 s2tilejson: "1.0.0".into(),
1329 },
1330 );
1331 }
1332
1333 #[test]
1334 fn test_malformed_metadata() {
1335 let meta_str = r#"{
1336 "s2tilejson": "1.0.0",
1337 "bounds": [
1338 -180,
1339 -85,
1340 180,
1341 85
1342 ],
1343 "name": "Mapbox Satellite",
1344 "scheme": "xyz",
1345 "format": "zxy",
1346 "type": "raster",
1347 "extension": "webp",
1348 "encoding": "none",
1349 "minzoom": 0,
1350 "maxzoom": 3
1351 }
1352 "#;
1353
1354 let malformed_success: UnknownMetadata =
1355 serde_json::from_str(meta_str).unwrap_or_else(|e| panic!("ERROR: {}", e));
1356
1357 let meta: Metadata = malformed_success.to_metadata();
1358 assert_eq!(
1359 meta,
1360 Metadata {
1361 s2tilejson: "1.0.0".into(),
1362 version: "1.0.0".into(),
1363 name: "Mapbox Satellite".into(),
1364 scheme: Scheme::Xyz,
1365 description: "Built with s2maps-cli".into(),
1366 type_: SourceType::Vector,
1367 extension: "pbf".into(),
1368 encoding: Encoding::None,
1369 faces: vec![Face::Face0],
1370 bounds: BTreeMap::default(),
1371 facesbounds: FaceBounds {
1372 face0: BTreeMap::default(),
1373 face1: BTreeMap::default(),
1374 face2: BTreeMap::default(),
1375 face3: BTreeMap::default(),
1376 face4: BTreeMap::default(),
1377 face5: BTreeMap::default()
1378 },
1379 minzoom: 0,
1380 maxzoom: 3,
1381 center: Center { lon: 0.0, lat: 0.0, zoom: 0 },
1382 attribution: BTreeMap::default(),
1383 layers: BTreeMap::default(),
1384 tilestats: TileStatsMetadata {
1385 total: 0,
1386 total_0: 0,
1387 total_1: 0,
1388 total_2: 0,
1389 total_3: 0,
1390 total_4: 0,
1391 total_5: 0
1392 },
1393 vector_layers: vec![]
1394 }
1395 );
1396 }
1397}