1#![no_std]
2#![deny(missing_docs)]
3extern crate alloc;
6
7use s2json::{BBox, Face};
8
9use serde::{Deserialize, Deserializer, Serialize, Serializer};
10
11use alloc::borrow::ToOwned;
12use alloc::collections::BTreeMap;
13use alloc::collections::BTreeSet;
14use alloc::format;
15use alloc::string::String;
16use alloc::vec::Vec;
17
18pub type LonLatBounds = BBox<f64>;
20
21pub type TileBounds = BBox<u64>;
23
24#[derive(Copy, Clone, Debug, PartialEq)]
26pub enum DrawType {
27 Points = 1,
29 Lines = 2,
31 Polygons = 3,
33 Points3D = 4,
35 Lines3D = 5,
37 Polygons3D = 6,
39 Raster = 7,
41 Grid = 8,
43}
44impl From<DrawType> for u8 {
45 fn from(draw_type: DrawType) -> Self {
46 draw_type as u8
47 }
48}
49impl From<u8> for DrawType {
50 fn from(draw_type: u8) -> Self {
51 match draw_type {
52 1 => DrawType::Points,
53 2 => DrawType::Lines,
54 3 => DrawType::Polygons,
55 4 => DrawType::Points3D,
56 5 => DrawType::Lines3D,
57 6 => DrawType::Polygons3D,
58 7 => DrawType::Raster,
59 8 => DrawType::Grid,
60 _ => DrawType::Points,
61 }
62 }
63}
64impl Serialize for DrawType {
65 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
66 where
67 S: Serializer,
68 {
69 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, Clone, PartialEq)]
107#[serde(rename_all = "lowercase")]
108pub enum PrimitiveShape {
109 String,
111 U64,
113 I64,
115 F32,
117 F64,
119 Bool,
121 Null,
123}
124
125#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
127#[serde(untagged)]
128pub enum ShapePrimitiveType {
129 Primitive(PrimitiveShape),
131 NestedPrimitive(BTreeMap<String, PrimitiveShape>),
133}
134
135#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
139#[serde(untagged)]
140pub enum ShapeType {
141 Primitive(PrimitiveShape),
143 Array(Vec<ShapePrimitiveType>),
145 Nested(Shape),
147}
148
149pub type Shape = BTreeMap<String, ShapeType>;
151
152#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
154pub struct LayerMetaData {
155 #[serde(skip_serializing_if = "Option::is_none")]
157 pub description: Option<String>,
158 pub minzoom: u8,
160 pub maxzoom: u8,
162 pub draw_types: Vec<DrawType>,
164 pub shape: Shape,
166 #[serde(skip_serializing_if = "Option::is_none", rename = "mShape")]
168 pub m_shape: Option<Shape>,
169}
170
171pub type LayersMetaData = BTreeMap<String, LayerMetaData>;
173
174#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
176pub struct TileStatsMetadata {
177 #[serde(default)]
179 pub total: u64,
180 #[serde(rename = "0", default)]
182 pub total_0: u64,
183 #[serde(rename = "1", default)]
185 pub total_1: u64,
186 #[serde(rename = "2", default)]
188 pub total_2: u64,
189 #[serde(rename = "3", default)]
191 pub total_3: u64,
192 #[serde(rename = "4", default)]
194 pub total_4: u64,
195 #[serde(rename = "5", default)]
197 pub total_5: u64,
198}
199impl TileStatsMetadata {
200 pub fn get(&self, face: Face) -> u64 {
202 match face {
203 Face::Face0 => self.total_0,
204 Face::Face1 => self.total_1,
205 Face::Face2 => self.total_2,
206 Face::Face3 => self.total_3,
207 Face::Face4 => self.total_4,
208 Face::Face5 => self.total_5,
209 }
210 }
211
212 pub fn increment(&mut self, face: Face) {
214 match face {
215 Face::Face0 => self.total_0 += 1,
216 Face::Face1 => self.total_1 += 1,
217 Face::Face2 => self.total_2 += 1,
218 Face::Face3 => self.total_3 += 1,
219 Face::Face4 => self.total_4 += 1,
220 Face::Face5 => self.total_5 += 1,
221 }
222 self.total += 1;
223 }
224}
225
226pub type Attribution = BTreeMap<String, String>;
229
230#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
232pub struct FaceBounds {
233 #[serde(rename = "0")]
236 pub face0: BTreeMap<u8, TileBounds>,
237 #[serde(rename = "1")]
239 pub face1: BTreeMap<u8, TileBounds>,
240 #[serde(rename = "2")]
242 pub face2: BTreeMap<u8, TileBounds>,
243 #[serde(rename = "3")]
245 pub face3: BTreeMap<u8, TileBounds>,
246 #[serde(rename = "4")]
248 pub face4: BTreeMap<u8, TileBounds>,
249 #[serde(rename = "5")]
251 pub face5: BTreeMap<u8, TileBounds>,
252}
253impl FaceBounds {
254 pub fn get(&self, face: Face) -> &BTreeMap<u8, TileBounds> {
256 match face {
257 Face::Face0 => &self.face0,
258 Face::Face1 => &self.face1,
259 Face::Face2 => &self.face2,
260 Face::Face3 => &self.face3,
261 Face::Face4 => &self.face4,
262 Face::Face5 => &self.face5,
263 }
264 }
265
266 pub fn get_mut(&mut self, face: Face) -> &mut BTreeMap<u8, TileBounds> {
268 match face {
269 Face::Face0 => &mut self.face0,
270 Face::Face1 => &mut self.face1,
271 Face::Face2 => &mut self.face2,
272 Face::Face3 => &mut self.face3,
273 Face::Face4 => &mut self.face4,
274 Face::Face5 => &mut self.face5,
275 }
276 }
277}
278
279pub type WMBounds = BTreeMap<u8, TileBounds>;
282
283#[derive(Serialize, Debug, Default, Clone, PartialEq)]
285#[serde(rename_all = "lowercase")]
286pub enum SourceType {
287 #[default]
289 Vector,
290 Json,
292 Raster,
294 #[serde(rename = "raster-dem")]
296 RasterDem,
297 Grid,
299 Markers,
301 Unknown,
303}
304impl From<&str> for SourceType {
305 fn from(source_type: &str) -> Self {
306 match source_type {
307 "vector" => SourceType::Vector,
308 "json" => SourceType::Json,
309 "raster" => SourceType::Raster,
310 "raster-dem" => SourceType::RasterDem,
311 "grid" => SourceType::Grid,
312 "markers" => SourceType::Markers,
313 _ => SourceType::Unknown,
314 }
315 }
316}
317impl<'de> Deserialize<'de> for SourceType {
318 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
319 where
320 D: Deserializer<'de>,
321 {
322 let s: String = Deserialize::deserialize(deserializer)?;
324 Ok(SourceType::from(s.as_str()))
325 }
326}
327
328#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
330#[serde(rename_all = "lowercase")]
331pub enum Encoding {
332 #[default]
334 None = 0,
335 Gzip = 1,
337 #[serde(rename = "br")]
339 Brotli = 2,
340 Zstd = 3,
342}
343impl From<u8> for Encoding {
344 fn from(encoding: u8) -> Self {
345 match encoding {
346 1 => Encoding::Gzip,
347 2 => Encoding::Brotli,
348 3 => Encoding::Zstd,
349 _ => Encoding::None,
350 }
351 }
352}
353impl From<Encoding> for u8 {
354 fn from(encoding: Encoding) -> Self {
355 match encoding {
356 Encoding::Gzip => 1,
357 Encoding::Brotli => 2,
358 Encoding::Zstd => 3,
359 Encoding::None => 0,
360 }
361 }
362}
363impl From<Encoding> for &str {
364 fn from(encoding: Encoding) -> Self {
365 match encoding {
366 Encoding::Gzip => "gzip",
367 Encoding::Brotli => "br",
368 Encoding::Zstd => "zstd",
369 Encoding::None => "none",
370 }
371 }
372}
373impl From<&str> for Encoding {
374 fn from(encoding: &str) -> Self {
375 match encoding {
376 "gzip" => Encoding::Gzip,
377 "br" => Encoding::Brotli,
378 "zstd" => Encoding::Zstd,
379 _ => Encoding::None,
380 }
381 }
382}
383
384#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
386pub struct VectorLayer {
387 pub id: String,
389 #[serde(skip_serializing_if = "Option::is_none")]
391 pub description: Option<String>,
392 #[serde(skip_serializing_if = "Option::is_none")]
394 pub minzoom: Option<u8>,
395 #[serde(skip_serializing_if = "Option::is_none")]
397 pub maxzoom: Option<u8>,
398 pub fields: BTreeMap<String, String>,
400}
401
402#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
407#[serde(rename_all = "lowercase")]
408pub enum Scheme {
409 #[default]
411 Fzxy,
412 Tfzxy,
414 Xyz,
416 Txyz,
418 Tms,
420}
421impl From<&str> for Scheme {
422 fn from(scheme: &str) -> Self {
423 match scheme {
424 "fzxy" => Scheme::Fzxy,
425 "tfzxy" => Scheme::Tfzxy,
426 "xyz" => Scheme::Xyz,
427 "txyz" => Scheme::Txyz,
428 _ => Scheme::Tms,
429 }
430 }
431}
432impl From<Scheme> for &str {
433 fn from(scheme: Scheme) -> Self {
434 match scheme {
435 Scheme::Fzxy => "fzxy",
436 Scheme::Tfzxy => "tfzxy",
437 Scheme::Xyz => "xyz",
438 Scheme::Txyz => "txyz",
439 Scheme::Tms => "tms",
440 }
441 }
442}
443
444#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
446pub struct Center {
447 pub lon: f64,
449 pub lat: f64,
451 pub zoom: u8,
453}
454
455#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
457pub struct Metadata {
458 #[serde(default)]
460 pub s2tilejson: String,
461 #[serde(default)]
463 pub version: String,
464 #[serde(default)]
466 pub name: String,
467 #[serde(default)]
469 pub scheme: Scheme,
470 #[serde(default)]
472 pub description: String,
473 #[serde(rename = "type", default)]
475 pub type_: SourceType,
476 #[serde(default)]
478 pub extension: String,
479 #[serde(default)]
481 pub encoding: Encoding,
482 #[serde(default)]
484 pub faces: Vec<Face>,
485 #[serde(default)]
487 pub bounds: WMBounds,
488 #[serde(default)]
490 pub facesbounds: FaceBounds,
491 #[serde(default)]
493 pub minzoom: u8,
494 #[serde(default)]
496 pub maxzoom: u8,
497 #[serde(default)]
499 pub center: Center,
500 #[serde(default)]
502 pub attribution: Attribution,
503 #[serde(default)]
505 pub layers: LayersMetaData,
506 #[serde(default)]
508 pub tilestats: TileStatsMetadata,
509 #[serde(default)]
511 pub vector_layers: Vec<VectorLayer>,
512}
513impl Default for Metadata {
514 fn default() -> Self {
515 Self {
516 s2tilejson: "1.0.0".into(),
517 version: "1.0.0".into(),
518 name: "default".into(),
519 scheme: Scheme::default(),
520 description: "Built with s2maps-cli".into(),
521 type_: SourceType::default(),
522 extension: "pbf".into(),
523 encoding: Encoding::default(),
524 faces: Vec::new(),
525 bounds: WMBounds::default(),
526 facesbounds: FaceBounds::default(),
527 minzoom: 0,
528 maxzoom: 27,
529 center: Center::default(),
530 attribution: BTreeMap::new(),
531 layers: LayersMetaData::default(),
532 tilestats: TileStatsMetadata::default(),
533 vector_layers: Vec::new(),
534 }
535 }
536}
537
538#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
552pub struct MapboxTileJSONMetadata {
553 pub tilejson: String,
556 pub tiles: Vec<String>,
558 pub vector_layers: Vec<VectorLayer>,
560 pub attribution: Option<String>,
562 pub bounds: Option<BBox>,
564 pub center: Option<[f64; 3]>,
566 pub data: Option<Vec<String>>,
568 pub description: Option<String>,
570 pub fillzoom: Option<u8>,
572 pub grids: Option<Vec<String>>,
574 pub legend: Option<String>,
576 pub maxzoom: Option<u8>,
578 pub minzoom: Option<u8>,
580 pub name: Option<String>,
582 pub scheme: Option<Scheme>,
584 pub template: Option<String>,
586 pub version: Option<String>,
588}
589impl MapboxTileJSONMetadata {
590 pub fn to_metadata(&self) -> Metadata {
592 Metadata {
593 s2tilejson: "1.0.0".into(),
594 version: self.version.clone().unwrap_or("1.0.0".into()),
595 name: self.name.clone().unwrap_or("default".into()),
596 scheme: self.scheme.clone().unwrap_or_default(),
597 description: self.description.clone().unwrap_or("Built with s2maps-cli".into()),
598 type_: SourceType::default(),
599 extension: "pbf".into(),
600 faces: Vec::from([Face::Face0]),
601 bounds: WMBounds::default(),
602 facesbounds: FaceBounds::default(),
603 minzoom: self.minzoom.unwrap_or(0),
604 maxzoom: self.maxzoom.unwrap_or(27),
605 center: Center {
606 lon: self.center.unwrap_or([0.0, 0.0, 0.0])[0],
607 lat: self.center.unwrap_or([0.0, 0.0, 0.0])[1],
608 zoom: self.center.unwrap_or([0.0, 0.0, 0.0])[2] as u8,
609 },
610 attribution: BTreeMap::new(),
611 layers: LayersMetaData::default(),
612 tilestats: TileStatsMetadata::default(),
613 vector_layers: self.vector_layers.clone(),
614 encoding: Encoding::default(),
615 }
616 }
617}
618
619#[derive(Debug, Clone)]
621pub struct MetadataBuilder {
622 lon_lat_bounds: LonLatBounds,
623 faces: BTreeSet<Face>,
624 metadata: Metadata,
625}
626impl Default for MetadataBuilder {
627 fn default() -> Self {
628 MetadataBuilder {
629 lon_lat_bounds: BBox {
630 left: f64::INFINITY,
631 bottom: f64::INFINITY,
632 right: -f64::INFINITY,
633 top: -f64::INFINITY,
634 },
635 faces: BTreeSet::new(),
636 metadata: Metadata { minzoom: 30, maxzoom: 0, ..Metadata::default() },
637 }
638 }
639}
640impl MetadataBuilder {
641 pub fn commit(&mut self) -> Metadata {
643 self.update_center();
645 for face in &self.faces {
647 self.metadata.faces.push(*face);
648 }
649 self.metadata.to_owned()
651 }
652
653 pub fn set_name(&mut self, name: String) {
655 self.metadata.name = name;
656 }
657
658 pub fn set_scheme(&mut self, scheme: Scheme) {
660 self.metadata.scheme = scheme;
661 }
662
663 pub fn set_extension(&mut self, extension: String) {
665 self.metadata.extension = extension;
666 }
667
668 pub fn set_type(&mut self, type_: SourceType) {
670 self.metadata.type_ = type_;
671 }
672
673 pub fn set_version(&mut self, version: String) {
675 self.metadata.version = version;
676 }
677
678 pub fn set_description(&mut self, description: String) {
680 self.metadata.description = description;
681 }
682
683 pub fn set_encoding(&mut self, encoding: Encoding) {
685 self.metadata.encoding = encoding;
686 }
687
688 pub fn add_attribution(&mut self, display_name: &str, href: &str) {
690 self.metadata.attribution.insert(display_name.into(), href.into());
691 }
692
693 pub fn add_layer(&mut self, name: &str, layer: &LayerMetaData) {
695 if self.metadata.layers.entry(name.into()).or_insert(layer.clone()).eq(&layer) {
697 self.metadata.vector_layers.push(VectorLayer {
699 id: name.into(), description: layer.description.clone(),
701 minzoom: Some(layer.minzoom),
702 maxzoom: Some(layer.maxzoom),
703 fields: BTreeMap::new(),
704 });
705 }
706 if layer.minzoom < self.metadata.minzoom {
708 self.metadata.minzoom = layer.minzoom;
709 }
710 if layer.maxzoom > self.metadata.maxzoom {
711 self.metadata.maxzoom = layer.maxzoom;
712 }
713 }
714
715 pub fn add_tile_wm(&mut self, zoom: u8, x: u32, y: u32, ll_bounds: &LonLatBounds) {
717 self.metadata.tilestats.total += 1;
718 self.faces.insert(Face::Face0);
719 self.add_bounds_wm(zoom, x, y);
720 self.update_lon_lat_bounds(ll_bounds);
721 }
722
723 pub fn add_tile_s2(&mut self, face: Face, zoom: u8, x: u32, y: u32, ll_bounds: &LonLatBounds) {
725 self.metadata.tilestats.increment(face);
726 self.faces.insert(face);
727 self.add_bounds_s2(face, zoom, x, y);
728 self.update_lon_lat_bounds(ll_bounds);
729 }
730
731 fn update_center(&mut self) {
733 let Metadata { minzoom, maxzoom, .. } = self.metadata;
734 let BBox { left, bottom, right, top } = self.lon_lat_bounds;
735 self.metadata.center.lon = (left + right) / 2.0;
736 self.metadata.center.lat = (bottom + top) / 2.0;
737 self.metadata.center.zoom = (minzoom + maxzoom) >> 1;
738 }
739
740 fn add_bounds_wm(&mut self, zoom: u8, x: u32, y: u32) {
742 let x = x as u64;
743 let y = y as u64;
744 let bbox = self.metadata.bounds.entry(zoom).or_insert(BBox {
745 left: u64::MAX,
746 bottom: u64::MAX,
747 right: 0,
748 top: 0,
749 });
750
751 bbox.left = bbox.left.min(x);
752 bbox.bottom = bbox.bottom.min(y);
753 bbox.right = bbox.right.max(x);
754 bbox.top = bbox.top.max(y);
755 }
756
757 fn add_bounds_s2(&mut self, face: Face, zoom: u8, x: u32, y: u32) {
759 let x = x as u64;
760 let y = y as u64;
761 let bbox = self.metadata.facesbounds.get_mut(face).entry(zoom).or_insert(BBox {
762 left: u64::MAX,
763 bottom: u64::MAX,
764 right: 0,
765 top: 0,
766 });
767
768 bbox.left = bbox.left.min(x);
769 bbox.bottom = bbox.bottom.min(y);
770 bbox.right = bbox.right.max(x);
771 bbox.top = bbox.top.max(y);
772 }
773
774 fn update_lon_lat_bounds(&mut self, ll_bounds: &LonLatBounds) {
776 self.lon_lat_bounds.left = ll_bounds.left.min(self.lon_lat_bounds.left);
777 self.lon_lat_bounds.bottom = ll_bounds.bottom.min(self.lon_lat_bounds.bottom);
778 self.lon_lat_bounds.right = ll_bounds.right.max(self.lon_lat_bounds.right);
779 self.lon_lat_bounds.top = ll_bounds.top.max(self.lon_lat_bounds.top);
780 }
781}
782
783#[cfg(test)]
784mod tests {
785 use super::*;
786 use alloc::vec;
787
788 #[test]
789 fn it_works() {
790 let mut meta_builder = MetadataBuilder::default();
791
792 meta_builder.set_name("OSM".into());
794 meta_builder.set_description("A free editable map of the whole world.".into());
795 meta_builder.set_version("1.0.0".into());
796 meta_builder.set_scheme("fzxy".into()); meta_builder.set_type("vector".into()); meta_builder.set_encoding("none".into()); meta_builder.set_extension("pbf".into());
800 meta_builder.add_attribution("OpenStreetMap", "https://www.openstreetmap.org/copyright/");
801
802 let shape_str = r#"
804 {
805 "class": "string",
806 "offset": "f64",
807 "info": {
808 "name": "string",
809 "value": "i64"
810 }
811 }
812 "#;
813 let shape: Shape =
814 serde_json::from_str(shape_str).unwrap_or_else(|e| panic!("ERROR: {}", e));
815 let layer = LayerMetaData {
816 minzoom: 0,
817 maxzoom: 13,
818 description: Some("water_lines".into()),
819 draw_types: Vec::from(&[DrawType::Lines]),
820 shape: shape.clone(),
821 m_shape: None,
822 };
823 meta_builder.add_layer("water_lines", &layer);
824
825 meta_builder.add_tile_wm(
828 0,
829 0,
830 0,
831 &LonLatBounds { left: -60.0, bottom: -20.0, right: 5.0, top: 60.0 },
832 );
833 meta_builder.add_tile_s2(
835 Face::Face1,
836 5,
837 22,
838 37,
839 &LonLatBounds { left: -120.0, bottom: -7.0, right: 44.0, top: 72.0 },
840 );
841
842 let resulting_metadata: Metadata = meta_builder.commit();
844
845 assert_eq!(
846 resulting_metadata,
847 Metadata {
848 name: "OSM".into(),
849 description: "A free editable map of the whole world.".into(),
850 version: "1.0.0".into(),
851 scheme: "fzxy".into(),
852 type_: "vector".into(),
853 encoding: "none".into(),
854 extension: "pbf".into(),
855 attribution: BTreeMap::from([(
856 "OpenStreetMap".into(),
857 "https://www.openstreetmap.org/copyright/".into()
858 ),]),
859 bounds: BTreeMap::from([(0, TileBounds { left: 0, bottom: 0, right: 0, top: 0 }),]),
860 faces: Vec::from(&[Face::Face0, Face::Face1]),
861 facesbounds: FaceBounds {
862 face0: BTreeMap::new(),
863 face1: BTreeMap::from([(
864 5,
865 TileBounds { left: 22, bottom: 37, right: 22, top: 37 }
866 ),]),
867 face2: BTreeMap::new(),
868 face3: BTreeMap::new(),
869 face4: BTreeMap::new(),
870 face5: BTreeMap::new(),
871 },
872 minzoom: 0,
873 maxzoom: 13,
874 center: Center { lon: -38.0, lat: 26.0, zoom: 6 },
875 tilestats: TileStatsMetadata {
876 total: 2,
877 total_0: 0,
878 total_1: 1,
879 total_2: 0,
880 total_3: 0,
881 total_4: 0,
882 total_5: 0,
883 },
884 layers: BTreeMap::from([(
885 "water_lines".into(),
886 LayerMetaData {
887 description: Some("water_lines".into()),
888 minzoom: 0,
889 maxzoom: 13,
890 draw_types: Vec::from(&[DrawType::Lines]),
891 shape: BTreeMap::from([
892 ("class".into(), ShapeType::Primitive(PrimitiveShape::String)),
893 ("offset".into(), ShapeType::Primitive(PrimitiveShape::F64)),
894 (
895 "info".into(),
896 ShapeType::Nested(BTreeMap::from([
897 ("name".into(), ShapeType::Primitive(PrimitiveShape::String)),
898 ("value".into(), ShapeType::Primitive(PrimitiveShape::I64)),
899 ]))
900 ),
901 ]),
902 m_shape: None,
903 }
904 )]),
905 s2tilejson: "1.0.0".into(),
906 vector_layers: Vec::from([VectorLayer {
907 id: "water_lines".into(),
908 description: Some("water_lines".into()),
909 minzoom: Some(0),
910 maxzoom: Some(13),
911 fields: BTreeMap::new()
912 }]),
913 }
914 );
915
916 let meta_str = serde_json::to_string(&resulting_metadata).unwrap();
917
918 assert_eq!(meta_str, "{\"s2tilejson\":\"1.0.0\",\"version\":\"1.0.0\",\"name\":\"OSM\",\"scheme\":\"fzxy\",\"description\":\"A free editable map of the whole world.\",\"type\":\"vector\",\"extension\":\"pbf\",\"encoding\":\"none\",\"faces\":[0,1],\"bounds\":{\"0\":[0,0,0,0]},\"facesbounds\":{\"0\":{},\"1\":{\"5\":[22,37,22,37]},\"2\":{},\"3\":{},\"4\":{},\"5\":{}},\"minzoom\":0,\"maxzoom\":13,\"center\":{\"lon\":-38.0,\"lat\":26.0,\"zoom\":6},\"attribution\":{\"OpenStreetMap\":\"https://www.openstreetmap.org/copyright/\"},\"layers\":{\"water_lines\":{\"description\":\"water_lines\",\"minzoom\":0,\"maxzoom\":13,\"draw_types\":[2],\"shape\":{\"class\":\"string\",\"info\":{\"name\":\"string\",\"value\":\"i64\"},\"offset\":\"f64\"}}},\"tilestats\":{\"total\":2,\"0\":0,\"1\":1,\"2\":0,\"3\":0,\"4\":0,\"5\":0},\"vector_layers\":[{\"id\":\"water_lines\",\"description\":\"water_lines\",\"minzoom\":0,\"maxzoom\":13,\"fields\":{}}]}");
919
920 let meta_reparsed: Metadata =
921 serde_json::from_str(&meta_str).unwrap_or_else(|e| panic!("ERROR: {}", e));
922 assert_eq!(meta_reparsed, resulting_metadata);
923 }
924
925 #[test]
926 fn test_face() {
927 assert_eq!(Face::Face0, Face::from(0));
928 assert_eq!(Face::Face1, Face::from(1));
929 assert_eq!(Face::Face2, Face::from(2));
930 assert_eq!(Face::Face3, Face::from(3));
931 assert_eq!(Face::Face4, Face::from(4));
932 assert_eq!(Face::Face5, Face::from(5));
933
934 assert_eq!(0, u8::from(Face::Face0));
935 assert_eq!(1, u8::from(Face::Face1));
936 assert_eq!(2, u8::from(Face::Face2));
937 assert_eq!(3, u8::from(Face::Face3));
938 assert_eq!(4, u8::from(Face::Face4));
939 assert_eq!(5, u8::from(Face::Face5));
940 }
941
942 #[test]
943 fn test_bbox() {
944 let bbox: BBox = BBox { left: 0.0, bottom: 0.0, right: 0.0, top: 0.0 };
945 let json = serde_json::to_string(&bbox).unwrap();
947 assert_eq!(json, r#"[0.0,0.0,0.0,0.0]"#);
948 let bbox2: BBox = serde_json::from_str(&json).unwrap();
949 assert_eq!(bbox, bbox2);
950 }
951
952 #[test]
954 fn test_tilestats() {
955 let mut tilestats = TileStatsMetadata {
956 total: 2,
957 total_0: 0,
958 total_1: 1,
959 total_2: 0,
960 total_3: 0,
961 total_4: 0,
962 total_5: 0,
963 };
964 let json = serde_json::to_string(&tilestats).unwrap();
966 assert_eq!(json, r#"{"total":2,"0":0,"1":1,"2":0,"3":0,"4":0,"5":0}"#);
967 let tilestats2: TileStatsMetadata = serde_json::from_str(&json).unwrap();
968 assert_eq!(tilestats, tilestats2);
969
970 assert_eq!(tilestats.get(0.into()), 0);
972 tilestats.increment(0.into());
974 assert_eq!(tilestats.get(0.into()), 1);
975
976 assert_eq!(tilestats.get(1.into()), 1);
978 tilestats.increment(1.into());
980 assert_eq!(tilestats.get(1.into()), 2);
981
982 assert_eq!(tilestats.get(2.into()), 0);
984 tilestats.increment(2.into());
986 assert_eq!(tilestats.get(2.into()), 1);
987
988 assert_eq!(tilestats.get(3.into()), 0);
990 tilestats.increment(3.into());
992 assert_eq!(tilestats.get(3.into()), 1);
993
994 assert_eq!(tilestats.get(4.into()), 0);
996 tilestats.increment(4.into());
998 assert_eq!(tilestats.get(4.into()), 1);
999
1000 assert_eq!(tilestats.get(5.into()), 0);
1002 tilestats.increment(5.into());
1004 assert_eq!(tilestats.get(5.into()), 1);
1005 }
1006
1007 #[test]
1009 fn test_facebounds() {
1010 let mut facebounds = FaceBounds::default();
1011 let face0 = facebounds.get_mut(0.into());
1013 face0.insert(0, TileBounds { left: 0, bottom: 0, right: 0, top: 0 });
1014 let face1 = facebounds.get_mut(1.into());
1016 face1.insert(0, TileBounds { left: 0, bottom: 0, right: 1, top: 1 });
1017 let face2 = facebounds.get_mut(2.into());
1019 face2.insert(0, TileBounds { left: 0, bottom: 0, right: 2, top: 2 });
1020 let face3 = facebounds.get_mut(3.into());
1022 face3.insert(0, TileBounds { left: 0, bottom: 0, right: 3, top: 3 });
1023 let face4 = facebounds.get_mut(4.into());
1025 face4.insert(0, TileBounds { left: 0, bottom: 0, right: 4, top: 4 });
1026 let face5 = facebounds.get_mut(5.into());
1028 face5.insert(0, TileBounds { left: 0, bottom: 0, right: 5, top: 5 });
1029
1030 assert_eq!(
1033 facebounds.get(0.into()).get(&0).unwrap(),
1034 &TileBounds { left: 0, bottom: 0, right: 0, top: 0 }
1035 );
1036 assert_eq!(
1038 facebounds.get(1.into()).get(&0).unwrap(),
1039 &TileBounds { left: 0, bottom: 0, right: 1, top: 1 }
1040 );
1041 assert_eq!(
1043 facebounds.get(2.into()).get(&0).unwrap(),
1044 &TileBounds { left: 0, bottom: 0, right: 2, top: 2 }
1045 );
1046 assert_eq!(
1048 facebounds.get(3.into()).get(&0).unwrap(),
1049 &TileBounds { left: 0, bottom: 0, right: 3, top: 3 }
1050 );
1051 assert_eq!(
1053 facebounds.get(4.into()).get(&0).unwrap(),
1054 &TileBounds { left: 0, bottom: 0, right: 4, top: 4 }
1055 );
1056 assert_eq!(
1058 facebounds.get(5.into()).get(&0).unwrap(),
1059 &TileBounds { left: 0, bottom: 0, right: 5, top: 5 }
1060 );
1061
1062 let json = serde_json::to_string(&facebounds).unwrap();
1064 assert_eq!(
1065 json,
1066 "{\"0\":{\"0\":[0,0,0,0]},\"1\":{\"0\":[0,0,1,1]},\"2\":{\"0\":[0,0,2,2]},\"3\":{\"0\"\
1067 :[0,0,3,3]},\"4\":{\"0\":[0,0,4,4]},\"5\":{\"0\":[0,0,5,5]}}"
1068 );
1069 let facebounds2 = serde_json::from_str(&json).unwrap();
1070 assert_eq!(facebounds, facebounds2);
1071 }
1072
1073 #[test]
1075 fn test_drawtype() {
1076 assert_eq!(DrawType::from(1), DrawType::Points);
1077 assert_eq!(DrawType::from(2), DrawType::Lines);
1078 assert_eq!(DrawType::from(3), DrawType::Polygons);
1079 assert_eq!(DrawType::from(4), DrawType::Points3D);
1080 assert_eq!(DrawType::from(5), DrawType::Lines3D);
1081 assert_eq!(DrawType::from(6), DrawType::Polygons3D);
1082 assert_eq!(DrawType::from(7), DrawType::Raster);
1083 assert_eq!(DrawType::from(8), DrawType::Grid);
1084
1085 assert_eq!(1, u8::from(DrawType::Points));
1086 assert_eq!(2, u8::from(DrawType::Lines));
1087 assert_eq!(3, u8::from(DrawType::Polygons));
1088 assert_eq!(4, u8::from(DrawType::Points3D));
1089 assert_eq!(5, u8::from(DrawType::Lines3D));
1090 assert_eq!(6, u8::from(DrawType::Polygons3D));
1091 assert_eq!(7, u8::from(DrawType::Raster));
1092 assert_eq!(8, u8::from(DrawType::Grid));
1093
1094 let json = serde_json::to_string(&DrawType::Points).unwrap();
1096 assert_eq!(json, "1");
1097 let drawtype: DrawType = serde_json::from_str(&json).unwrap();
1098 assert_eq!(drawtype, DrawType::Points);
1099
1100 let drawtype: DrawType = serde_json::from_str("2").unwrap();
1101 assert_eq!(drawtype, DrawType::Lines);
1102
1103 let drawtype: DrawType = serde_json::from_str("3").unwrap();
1104 assert_eq!(drawtype, DrawType::Polygons);
1105
1106 let drawtype: DrawType = serde_json::from_str("4").unwrap();
1107 assert_eq!(drawtype, DrawType::Points3D);
1108
1109 let drawtype: DrawType = serde_json::from_str("5").unwrap();
1110 assert_eq!(drawtype, DrawType::Lines3D);
1111
1112 let drawtype: DrawType = serde_json::from_str("6").unwrap();
1113 assert_eq!(drawtype, DrawType::Polygons3D);
1114
1115 let drawtype: DrawType = serde_json::from_str("7").unwrap();
1116 assert_eq!(drawtype, DrawType::Raster);
1117
1118 let drawtype: DrawType = serde_json::from_str("8").unwrap();
1119 assert_eq!(drawtype, DrawType::Grid);
1120
1121 assert!(serde_json::from_str::<DrawType>("9").is_err());
1122 }
1123
1124 #[test]
1126 fn test_sourcetype() {
1127 assert_eq!(SourceType::from("vector"), SourceType::Vector);
1129 assert_eq!(SourceType::from("json"), SourceType::Json);
1130 assert_eq!(SourceType::from("raster"), SourceType::Raster);
1131 assert_eq!(SourceType::from("raster-dem"), SourceType::RasterDem);
1132 assert_eq!(SourceType::from("grid"), SourceType::Grid);
1133 assert_eq!(SourceType::from("markers"), SourceType::Markers);
1134 assert_eq!(SourceType::from("overlay"), SourceType::Unknown);
1135
1136 let json = serde_json::to_string(&SourceType::Vector).unwrap();
1138 assert_eq!(json, "\"vector\"");
1139 let sourcetype: SourceType = serde_json::from_str(&json).unwrap();
1140 assert_eq!(sourcetype, SourceType::Vector);
1141
1142 let json = serde_json::to_string(&SourceType::Json).unwrap();
1144 assert_eq!(json, "\"json\"");
1145 let sourcetype: SourceType = serde_json::from_str(&json).unwrap();
1146 assert_eq!(sourcetype, SourceType::Json);
1147
1148 let json = serde_json::to_string(&SourceType::Raster).unwrap();
1150 assert_eq!(json, "\"raster\"");
1151 let sourcetype: SourceType = serde_json::from_str(&json).unwrap();
1152 assert_eq!(sourcetype, SourceType::Raster);
1153
1154 let json = serde_json::to_string(&SourceType::RasterDem).unwrap();
1156 assert_eq!(json, "\"raster-dem\"");
1157 let sourcetype: SourceType = serde_json::from_str(&json).unwrap();
1158 assert_eq!(sourcetype, SourceType::RasterDem);
1159
1160 let json = serde_json::to_string(&SourceType::Grid).unwrap();
1162 assert_eq!(json, "\"grid\"");
1163 let sourcetype: SourceType = serde_json::from_str(&json).unwrap();
1164 assert_eq!(sourcetype, SourceType::Grid);
1165
1166 let json = serde_json::to_string(&SourceType::Markers).unwrap();
1168 assert_eq!(json, "\"markers\"");
1169 let sourcetype: SourceType = serde_json::from_str(&json).unwrap();
1170 assert_eq!(sourcetype, SourceType::Markers);
1171
1172 let json = serde_json::to_string(&SourceType::Unknown).unwrap();
1174 assert_eq!(json, "\"unknown\"");
1175 let sourcetype: SourceType = serde_json::from_str(r#""overlay""#).unwrap();
1176 assert_eq!(sourcetype, SourceType::Unknown);
1177 }
1178
1179 #[test]
1181 fn test_encoding() {
1182 assert_eq!(Encoding::from("none"), Encoding::None);
1184 assert_eq!(Encoding::from("gzip"), Encoding::Gzip);
1185 assert_eq!(Encoding::from("br"), Encoding::Brotli);
1186 assert_eq!(Encoding::from("zstd"), Encoding::Zstd);
1187
1188 assert_eq!(core::convert::Into::<&str>::into(Encoding::None), "none");
1190 assert_eq!(core::convert::Into::<&str>::into(Encoding::Gzip), "gzip");
1191 assert_eq!(core::convert::Into::<&str>::into(Encoding::Brotli), "br");
1192 assert_eq!(core::convert::Into::<&str>::into(Encoding::Zstd), "zstd");
1193
1194 assert_eq!(Encoding::from(0), Encoding::None);
1196 assert_eq!(Encoding::from(1), Encoding::Gzip);
1197 assert_eq!(Encoding::from(2), Encoding::Brotli);
1198 assert_eq!(Encoding::from(3), Encoding::Zstd);
1199
1200 assert_eq!(u8::from(Encoding::None), 0);
1202 assert_eq!(u8::from(Encoding::Gzip), 1);
1203 assert_eq!(u8::from(Encoding::Brotli), 2);
1204 assert_eq!(u8::from(Encoding::Zstd), 3);
1205
1206 let json = serde_json::to_string(&Encoding::Gzip).unwrap();
1208 assert_eq!(json, "\"gzip\"");
1209 let encoding: Encoding = serde_json::from_str(&json).unwrap();
1210 assert_eq!(encoding, Encoding::Gzip);
1211
1212 let json = serde_json::to_string(&Encoding::Brotli).unwrap();
1214 assert_eq!(json, "\"br\"");
1215 let encoding: Encoding = serde_json::from_str(&json).unwrap();
1216 assert_eq!(encoding, Encoding::Brotli);
1217
1218 let json = serde_json::to_string(&Encoding::None).unwrap();
1220 assert_eq!(json, "\"none\"");
1221 let encoding: Encoding = serde_json::from_str(&json).unwrap();
1222 assert_eq!(encoding, Encoding::None);
1223
1224 let json = serde_json::to_string(&Encoding::Zstd).unwrap();
1226 assert_eq!(json, "\"zstd\"");
1227 let encoding: Encoding = serde_json::from_str(&json).unwrap();
1228 assert_eq!(encoding, Encoding::Zstd);
1229 }
1230
1231 #[test]
1233 fn test_scheme() {
1234 assert_eq!(Scheme::from("fzxy"), Scheme::Fzxy);
1236 assert_eq!(Scheme::from("tfzxy"), Scheme::Tfzxy);
1237 assert_eq!(Scheme::from("xyz"), Scheme::Xyz);
1238 assert_eq!(Scheme::from("txyz"), Scheme::Txyz);
1239 assert_eq!(Scheme::from("tms"), Scheme::Tms);
1240
1241 assert_eq!(core::convert::Into::<&str>::into(Scheme::Fzxy), "fzxy");
1243 assert_eq!(core::convert::Into::<&str>::into(Scheme::Tfzxy), "tfzxy");
1244 assert_eq!(core::convert::Into::<&str>::into(Scheme::Xyz), "xyz");
1245 assert_eq!(core::convert::Into::<&str>::into(Scheme::Txyz), "txyz");
1246 assert_eq!(core::convert::Into::<&str>::into(Scheme::Tms), "tms");
1247 }
1248
1249 #[test]
1250 fn test_tippecanoe_metadata() {
1251 let meta_str = r#"{
1252 "name": "test_fixture_1.pmtiles",
1253 "description": "test_fixture_1.pmtiles",
1254 "version": "2",
1255 "type": "overlay",
1256 "generator": "tippecanoe v2.5.0",
1257 "generator_options": "./tippecanoe -zg -o test_fixture_1.pmtiles --force",
1258 "vector_layers": [
1259 {
1260 "id": "test_fixture_1pmtiles",
1261 "description": "",
1262 "minzoom": 0,
1263 "maxzoom": 0,
1264 "fields": {}
1265 }
1266 ],
1267 "tilestats": {
1268 "layerCount": 1,
1269 "layers": [
1270 {
1271 "layer": "test_fixture_1pmtiles",
1272 "count": 1,
1273 "geometry": "Polygon",
1274 "attributeCount": 0,
1275 "attributes": []
1276 }
1277 ]
1278 }
1279 }"#;
1280
1281 let _meta: Metadata =
1282 serde_json::from_str(meta_str).unwrap_or_else(|e| panic!("ERROR: {}", e));
1283 }
1284
1285 #[test]
1286 fn test_mapbox_metadata() {
1287 let meta_str = r#"{
1288 "tilejson": "3.0.0",
1289 "name": "OpenStreetMap",
1290 "description": "A free editable map of the whole world.",
1291 "version": "1.0.0",
1292 "attribution": "(c) OpenStreetMap contributors, CC-BY-SA",
1293 "scheme": "xyz",
1294 "tiles": [
1295 "https://a.tile.custom-osm-tiles.org/{z}/{x}/{y}.mvt",
1296 "https://b.tile.custom-osm-tiles.org/{z}/{x}/{y}.mvt",
1297 "https://c.tile.custom-osm-tiles.org/{z}/{x}/{y}.mvt"
1298 ],
1299 "minzoom": 0,
1300 "maxzoom": 18,
1301 "bounds": [-180, -85, 180, 85],
1302 "fillzoom": 6,
1303 "something_custom": "this is my unique field",
1304 "vector_layers": [
1305 {
1306 "id": "telephone",
1307 "fields": {
1308 "phone_number": "the phone number",
1309 "payment": "how to pay"
1310 }
1311 },
1312 {
1313 "id": "bicycle_parking",
1314 "fields": {
1315 "type": "the type of bike parking",
1316 "year_installed": "the year the bike parking was installed"
1317 }
1318 },
1319 {
1320 "id": "showers",
1321 "fields": {
1322 "water_temperature": "the maximum water temperature",
1323 "wear_sandles": "whether you should wear sandles or not",
1324 "wheelchair": "is the shower wheelchair friendly?"
1325 }
1326 }
1327 ]
1328 }"#;
1329
1330 let meta_mapbox: MapboxTileJSONMetadata =
1331 serde_json::from_str(meta_str).unwrap_or_else(|e| panic!("ERROR: {}", e));
1332 let meta_new = meta_mapbox.to_metadata();
1333 assert_eq!(
1334 meta_new,
1335 Metadata {
1336 name: "OpenStreetMap".into(),
1337 description: "A free editable map of the whole world.".into(),
1338 version: "1.0.0".into(),
1339 scheme: Scheme::Xyz,
1340 type_: "vector".into(),
1341 encoding: "none".into(),
1342 extension: "pbf".into(),
1343 attribution: BTreeMap::new(),
1344 vector_layers: meta_mapbox.vector_layers.clone(),
1345 maxzoom: 18,
1346 minzoom: 0,
1347 center: Center { lat: 0.0, lon: 0.0, zoom: 0 },
1348 bounds: WMBounds::default(),
1349 faces: vec![Face::Face0],
1350 facesbounds: FaceBounds::default(),
1351 tilestats: TileStatsMetadata::default(),
1352 layers: LayersMetaData::default(),
1353 s2tilejson: "1.0.0".into(),
1354 },
1355 );
1356 }
1357}