1#![no_std]
2#![deny(missing_docs)]
3extern 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#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
21#[repr(u8)] pub enum Face {
23 Face0 = 0,
25 Face1 = 1,
27 Face2 = 2,
29 Face3 = 3,
31 Face4 = 4,
33 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#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
82pub struct BBox<T = f64> {
83 pub left: T,
85 pub bottom: T,
87 pub right: T,
89 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
150pub type LonLatBounds = BBox<f64>;
152
153pub type TileBounds = BBox<u64>;
155
156#[derive(Copy, Clone, Debug, PartialEq)]
158pub enum DrawType {
159 Points = 1,
161 Lines = 2,
163 Polygons = 3,
165 Points3D = 4,
167 Lines3D = 5,
169 Polygons3D = 6,
171 Raster = 7,
173 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 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 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#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
239#[serde(rename_all = "lowercase")]
240pub enum PrimitiveShape {
241 String,
243 U64,
245 I64,
247 F32,
249 F64,
251 Bool,
253 Null,
255}
256
257#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
259#[serde(untagged)]
260pub enum ShapePrimitiveType {
261 Primitive(PrimitiveShape),
263 NestedPrimitive(BTreeMap<String, PrimitiveShape>),
265}
266
267#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
271#[serde(untagged)]
272pub enum ShapeType {
273 Primitive(PrimitiveShape),
275 Array(Vec<ShapePrimitiveType>),
277 Nested(Shape),
279}
280
281pub type Shape = BTreeMap<String, ShapeType>;
283
284#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
286pub struct LayerMetaData {
287 #[serde(skip_serializing_if = "Option::is_none")]
289 pub description: Option<String>,
290 pub minzoom: u8,
292 pub maxzoom: u8,
294 pub draw_types: Vec<DrawType>,
296 pub shape: Shape,
298 #[serde(skip_serializing_if = "Option::is_none", rename = "mShape")]
300 pub m_shape: Option<Shape>,
301}
302
303pub type LayersMetaData = BTreeMap<String, LayerMetaData>;
305
306#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
308pub struct TileStatsMetadata {
309 #[serde(default)]
311 pub total: u64,
312 #[serde(rename = "0", default)]
314 pub total_0: u64,
315 #[serde(rename = "1", default)]
317 pub total_1: u64,
318 #[serde(rename = "2", default)]
320 pub total_2: u64,
321 #[serde(rename = "3", default)]
323 pub total_3: u64,
324 #[serde(rename = "4", default)]
326 pub total_4: u64,
327 #[serde(rename = "5", default)]
329 pub total_5: u64,
330}
331impl TileStatsMetadata {
332 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 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
358pub type Attribution = BTreeMap<String, String>;
361
362#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
364pub struct FaceBounds {
365 #[serde(rename = "0")]
368 pub face0: BTreeMap<u8, TileBounds>,
369 #[serde(rename = "1")]
371 pub face1: BTreeMap<u8, TileBounds>,
372 #[serde(rename = "2")]
374 pub face2: BTreeMap<u8, TileBounds>,
375 #[serde(rename = "3")]
377 pub face3: BTreeMap<u8, TileBounds>,
378 #[serde(rename = "4")]
380 pub face4: BTreeMap<u8, TileBounds>,
381 #[serde(rename = "5")]
383 pub face5: BTreeMap<u8, TileBounds>,
384}
385impl FaceBounds {
386 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 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
411pub type WMBounds = BTreeMap<u8, TileBounds>;
414
415#[derive(Serialize, Debug, Default, Clone, PartialEq)]
417#[serde(rename_all = "lowercase")]
418pub enum SourceType {
419 #[default]
421 Vector,
422 Json,
424 Raster,
426 #[serde(rename = "raster-dem")]
428 RasterDem,
429 Grid,
431 Markers,
433 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 let s: String = Deserialize::deserialize(deserializer)?;
456 Ok(SourceType::from(s.as_str()))
457 }
458}
459
460#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
462#[serde(rename_all = "lowercase")]
463pub enum Encoding {
464 #[default]
466 None = 0,
467 Gzip = 1,
469 #[serde(rename = "br")]
471 Brotli = 2,
472 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#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
518pub struct VectorLayer {
519 pub id: String,
521 #[serde(skip_serializing_if = "Option::is_none")]
523 pub description: Option<String>,
524 #[serde(skip_serializing_if = "Option::is_none")]
526 pub minzoom: Option<u8>,
527 #[serde(skip_serializing_if = "Option::is_none")]
529 pub maxzoom: Option<u8>,
530 pub fields: BTreeMap<String, String>,
532}
533
534#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
539#[serde(rename_all = "lowercase")]
540pub enum Scheme {
541 #[default]
543 Fzxy,
544 Tfzxy,
546 Xyz,
548 Txyz,
550 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#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
578pub struct Center {
579 pub lon: f64,
581 pub lat: f64,
583 pub zoom: u8,
585}
586
587#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
589pub struct Metadata {
590 #[serde(default)]
592 pub s2tilejson: String,
593 #[serde(default)]
595 pub version: String,
596 #[serde(default)]
598 pub name: String,
599 #[serde(default)]
601 pub scheme: Scheme,
602 #[serde(default)]
604 pub description: String,
605 #[serde(rename = "type", default)]
607 pub type_: SourceType,
608 #[serde(default)]
610 pub extension: String,
611 #[serde(default)]
613 pub encoding: Encoding,
614 #[serde(default)]
616 pub faces: Vec<Face>,
617 #[serde(default)]
619 pub bounds: WMBounds,
620 #[serde(default)]
622 pub facesbounds: FaceBounds,
623 #[serde(default)]
625 pub minzoom: u8,
626 #[serde(default)]
628 pub maxzoom: u8,
629 #[serde(default)]
631 pub center: Center,
632 #[serde(default)]
634 pub attribution: Attribution,
635 #[serde(default)]
637 pub layers: LayersMetaData,
638 #[serde(default)]
640 pub tilestats: TileStatsMetadata,
641 #[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#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
684pub struct MapboxTileJSONMetadata {
685 pub tilejson: String,
688 pub tiles: Vec<String>,
690 pub vector_layers: Vec<VectorLayer>,
692 pub attribution: Option<String>,
694 pub bounds: Option<BBox>,
696 pub center: Option<[f64; 3]>,
698 pub data: Option<Vec<String>>,
700 pub description: Option<String>,
702 pub fillzoom: Option<u8>,
704 pub grids: Option<Vec<String>>,
706 pub legend: Option<String>,
708 pub maxzoom: Option<u8>,
710 pub minzoom: Option<u8>,
712 pub name: Option<String>,
714 pub scheme: Option<Scheme>,
716 pub template: Option<String>,
718 pub version: Option<String>,
720}
721impl MapboxTileJSONMetadata {
722 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#[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 pub fn commit(&mut self) -> Metadata {
775 self.update_center();
777 for face in &self.faces {
779 self.metadata.faces.push(*face);
780 }
781 self.metadata.to_owned()
783 }
784
785 pub fn set_name(&mut self, name: String) {
787 self.metadata.name = name;
788 }
789
790 pub fn set_scheme(&mut self, scheme: Scheme) {
792 self.metadata.scheme = scheme;
793 }
794
795 pub fn set_extension(&mut self, extension: String) {
797 self.metadata.extension = extension;
798 }
799
800 pub fn set_type(&mut self, type_: SourceType) {
802 self.metadata.type_ = type_;
803 }
804
805 pub fn set_version(&mut self, version: String) {
807 self.metadata.version = version;
808 }
809
810 pub fn set_description(&mut self, description: String) {
812 self.metadata.description = description;
813 }
814
815 pub fn set_encoding(&mut self, encoding: Encoding) {
817 self.metadata.encoding = encoding;
818 }
819
820 pub fn add_attribution(&mut self, display_name: &str, href: &str) {
822 self.metadata.attribution.insert(display_name.into(), href.into());
823 }
824
825 pub fn add_layer(&mut self, name: &str, layer: &LayerMetaData) {
827 if self.metadata.layers.entry(name.into()).or_insert(layer.clone()).eq(&layer) {
829 self.metadata.vector_layers.push(VectorLayer {
831 id: name.into(), description: layer.description.clone(),
833 minzoom: Some(layer.minzoom),
834 maxzoom: Some(layer.maxzoom),
835 fields: BTreeMap::new(),
836 });
837 }
838 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 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 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 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 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 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 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 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()); meta_builder.set_type("vector".into()); meta_builder.set_encoding("none".into()); meta_builder.set_extension("pbf".into());
932 meta_builder.add_attribution("OpenStreetMap", "https://www.openstreetmap.org/copyright/");
933
934 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 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 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 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 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 #[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 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 assert_eq!(tilestats.get(0.into()), 0);
1104 tilestats.increment(0.into());
1106 assert_eq!(tilestats.get(0.into()), 1);
1107
1108 assert_eq!(tilestats.get(1.into()), 1);
1110 tilestats.increment(1.into());
1112 assert_eq!(tilestats.get(1.into()), 2);
1113
1114 assert_eq!(tilestats.get(2.into()), 0);
1116 tilestats.increment(2.into());
1118 assert_eq!(tilestats.get(2.into()), 1);
1119
1120 assert_eq!(tilestats.get(3.into()), 0);
1122 tilestats.increment(3.into());
1124 assert_eq!(tilestats.get(3.into()), 1);
1125
1126 assert_eq!(tilestats.get(4.into()), 0);
1128 tilestats.increment(4.into());
1130 assert_eq!(tilestats.get(4.into()), 1);
1131
1132 assert_eq!(tilestats.get(5.into()), 0);
1134 tilestats.increment(5.into());
1136 assert_eq!(tilestats.get(5.into()), 1);
1137 }
1138
1139 #[test]
1141 fn test_facebounds() {
1142 let mut facebounds = FaceBounds::default();
1143 let face0 = facebounds.get_mut(0.into());
1145 face0.insert(0, TileBounds { left: 0, bottom: 0, right: 0, top: 0 });
1146 let face1 = facebounds.get_mut(1.into());
1148 face1.insert(0, TileBounds { left: 0, bottom: 0, right: 1, top: 1 });
1149 let face2 = facebounds.get_mut(2.into());
1151 face2.insert(0, TileBounds { left: 0, bottom: 0, right: 2, top: 2 });
1152 let face3 = facebounds.get_mut(3.into());
1154 face3.insert(0, TileBounds { left: 0, bottom: 0, right: 3, top: 3 });
1155 let face4 = facebounds.get_mut(4.into());
1157 face4.insert(0, TileBounds { left: 0, bottom: 0, right: 4, top: 4 });
1158 let face5 = facebounds.get_mut(5.into());
1160 face5.insert(0, TileBounds { left: 0, bottom: 0, right: 5, top: 5 });
1161
1162 assert_eq!(
1165 facebounds.get(0.into()).get(&0).unwrap(),
1166 &TileBounds { left: 0, bottom: 0, right: 0, top: 0 }
1167 );
1168 assert_eq!(
1170 facebounds.get(1.into()).get(&0).unwrap(),
1171 &TileBounds { left: 0, bottom: 0, right: 1, top: 1 }
1172 );
1173 assert_eq!(
1175 facebounds.get(2.into()).get(&0).unwrap(),
1176 &TileBounds { left: 0, bottom: 0, right: 2, top: 2 }
1177 );
1178 assert_eq!(
1180 facebounds.get(3.into()).get(&0).unwrap(),
1181 &TileBounds { left: 0, bottom: 0, right: 3, top: 3 }
1182 );
1183 assert_eq!(
1185 facebounds.get(4.into()).get(&0).unwrap(),
1186 &TileBounds { left: 0, bottom: 0, right: 4, top: 4 }
1187 );
1188 assert_eq!(
1190 facebounds.get(5.into()).get(&0).unwrap(),
1191 &TileBounds { left: 0, bottom: 0, right: 5, top: 5 }
1192 );
1193
1194 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 #[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 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 #[test]
1258 fn test_sourcetype() {
1259 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 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 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 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 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 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 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 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 #[test]
1313 fn test_encoding() {
1314 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 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 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 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 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 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 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 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 #[test]
1365 fn test_scheme() {
1366 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 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}