1#![no_std]
2#![forbid(unsafe_code)]
3#![deny(missing_docs)]
4extern crate alloc;
7
8use alloc::{
9 borrow::ToOwned,
10 collections::{BTreeMap, BTreeSet},
11 format,
12 string::String,
13 vec::Vec,
14};
15pub use s2json::*;
16use serde::{Deserialize, Deserializer, Serialize, Serializer};
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 2 => DrawType::Lines,
53 3 => DrawType::Polygons,
54 4 => DrawType::Points3D,
55 5 => DrawType::Lines3D,
56 6 => DrawType::Polygons3D,
57 7 => DrawType::Raster,
58 8 => DrawType::Grid,
59 _ => DrawType::Points, }
61 }
62}
63impl Serialize for DrawType {
64 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
65 where
66 S: Serializer,
67 {
68 serializer.serialize_u8(*self as u8)
70 }
71}
72
73impl<'de> Deserialize<'de> for DrawType {
74 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
75 where
76 D: Deserializer<'de>,
77 {
78 let value: u8 = Deserialize::deserialize(deserializer)?;
80 match value {
81 1 => Ok(DrawType::Points),
82 2 => Ok(DrawType::Lines),
83 3 => Ok(DrawType::Polygons),
84 4 => Ok(DrawType::Points3D),
85 5 => Ok(DrawType::Lines3D),
86 6 => Ok(DrawType::Polygons3D),
87 7 => Ok(DrawType::Raster),
88 8 => Ok(DrawType::Grid),
89 _ => Err(serde::de::Error::custom(format!("unknown DrawType variant: {}", value))),
90 }
91 }
92}
93
94#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
96pub struct LayerMetaData {
97 #[serde(skip_serializing_if = "Option::is_none")]
99 pub description: Option<String>,
100 pub minzoom: u8,
102 pub maxzoom: u8,
104 pub draw_types: Vec<DrawType>,
106 pub shape: Shape,
108 #[serde(skip_serializing_if = "Option::is_none", rename = "mShape")]
110 pub m_shape: Option<Shape>,
111}
112
113pub type LayersMetaData = BTreeMap<String, LayerMetaData>;
115
116#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
118pub struct TileStatsMetadata {
119 #[serde(default)]
121 pub total: u64,
122 #[serde(rename = "0", default)]
124 pub total_0: u64,
125 #[serde(rename = "1", default)]
127 pub total_1: u64,
128 #[serde(rename = "2", default)]
130 pub total_2: u64,
131 #[serde(rename = "3", default)]
133 pub total_3: u64,
134 #[serde(rename = "4", default)]
136 pub total_4: u64,
137 #[serde(rename = "5", default)]
139 pub total_5: u64,
140}
141impl TileStatsMetadata {
142 pub fn get(&self, face: Face) -> u64 {
144 match face {
145 Face::Face0 => self.total_0,
146 Face::Face1 => self.total_1,
147 Face::Face2 => self.total_2,
148 Face::Face3 => self.total_3,
149 Face::Face4 => self.total_4,
150 Face::Face5 => self.total_5,
151 }
152 }
153
154 pub fn increment(&mut self, face: Face) {
156 match face {
157 Face::Face0 => self.total_0 += 1,
158 Face::Face1 => self.total_1 += 1,
159 Face::Face2 => self.total_2 += 1,
160 Face::Face3 => self.total_3 += 1,
161 Face::Face4 => self.total_4 += 1,
162 Face::Face5 => self.total_5 += 1,
163 }
164 self.total += 1;
165 }
166}
167
168pub type Attribution = BTreeMap<String, String>;
171
172#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
174pub struct FaceBounds {
175 #[serde(rename = "0")]
178 pub face0: BTreeMap<u8, TileBounds>,
179 #[serde(rename = "1")]
181 pub face1: BTreeMap<u8, TileBounds>,
182 #[serde(rename = "2")]
184 pub face2: BTreeMap<u8, TileBounds>,
185 #[serde(rename = "3")]
187 pub face3: BTreeMap<u8, TileBounds>,
188 #[serde(rename = "4")]
190 pub face4: BTreeMap<u8, TileBounds>,
191 #[serde(rename = "5")]
193 pub face5: BTreeMap<u8, TileBounds>,
194}
195impl FaceBounds {
196 pub fn get(&self, face: Face) -> &BTreeMap<u8, TileBounds> {
198 match face {
199 Face::Face0 => &self.face0,
200 Face::Face1 => &self.face1,
201 Face::Face2 => &self.face2,
202 Face::Face3 => &self.face3,
203 Face::Face4 => &self.face4,
204 Face::Face5 => &self.face5,
205 }
206 }
207
208 pub fn get_mut(&mut self, face: Face) -> &mut BTreeMap<u8, TileBounds> {
210 match face {
211 Face::Face0 => &mut self.face0,
212 Face::Face1 => &mut self.face1,
213 Face::Face2 => &mut self.face2,
214 Face::Face3 => &mut self.face3,
215 Face::Face4 => &mut self.face4,
216 Face::Face5 => &mut self.face5,
217 }
218 }
219}
220
221pub type WMBounds = BTreeMap<u8, TileBounds>;
224
225#[derive(Serialize, Debug, Default, Clone, PartialEq)]
227#[serde(rename_all = "lowercase")]
228pub enum SourceType {
229 #[default]
231 Vector,
232 Json,
234 Raster,
236 #[serde(rename = "raster-dem")]
238 RasterDem,
239 Grid,
241 Markers,
243 Unknown,
245}
246impl From<&str> for SourceType {
247 fn from(source_type: &str) -> Self {
248 match source_type {
249 "vector" => SourceType::Vector,
250 "json" => SourceType::Json,
251 "raster" => SourceType::Raster,
252 "raster-dem" => SourceType::RasterDem,
253 "grid" => SourceType::Grid,
254 "markers" => SourceType::Markers,
255 _ => SourceType::Unknown,
256 }
257 }
258}
259impl<'de> Deserialize<'de> for SourceType {
260 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
261 where
262 D: Deserializer<'de>,
263 {
264 let s: String = Deserialize::deserialize(deserializer)?;
266 Ok(SourceType::from(s.as_str()))
267 }
268}
269
270#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
272#[serde(rename_all = "lowercase")]
273pub enum Encoding {
274 #[default]
276 None = 0,
277 Gzip = 1,
279 #[serde(rename = "br")]
281 Brotli = 2,
282 Zstd = 3,
284}
285impl From<u8> for Encoding {
286 fn from(encoding: u8) -> Self {
287 match encoding {
288 1 => Encoding::Gzip,
289 2 => Encoding::Brotli,
290 3 => Encoding::Zstd,
291 _ => Encoding::None,
292 }
293 }
294}
295impl From<Encoding> for u8 {
296 fn from(encoding: Encoding) -> Self {
297 match encoding {
298 Encoding::Gzip => 1,
299 Encoding::Brotli => 2,
300 Encoding::Zstd => 3,
301 Encoding::None => 0,
302 }
303 }
304}
305impl From<Encoding> for &str {
306 fn from(encoding: Encoding) -> Self {
307 match encoding {
308 Encoding::Gzip => "gzip",
309 Encoding::Brotli => "br",
310 Encoding::Zstd => "zstd",
311 Encoding::None => "none",
312 }
313 }
314}
315impl From<&str> for Encoding {
316 fn from(encoding: &str) -> Self {
317 match encoding {
318 "gzip" => Encoding::Gzip,
319 "br" => Encoding::Brotli,
320 "zstd" => Encoding::Zstd,
321 _ => Encoding::None,
322 }
323 }
324}
325
326#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
328pub struct VectorLayer {
329 pub id: String,
331 #[serde(skip_serializing_if = "Option::is_none")]
333 pub description: Option<String>,
334 #[serde(skip_serializing_if = "Option::is_none")]
336 pub minzoom: Option<u8>,
337 #[serde(skip_serializing_if = "Option::is_none")]
339 pub maxzoom: Option<u8>,
340 pub fields: BTreeMap<String, String>,
342}
343
344#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
349#[serde(rename_all = "lowercase")]
350pub enum Scheme {
351 #[default]
353 Fzxy,
354 Tfzxy,
356 Xyz,
358 Txyz,
360 Tms,
362}
363impl From<&str> for Scheme {
364 fn from(scheme: &str) -> Self {
365 match scheme {
366 "fzxy" => Scheme::Fzxy,
367 "tfzxy" => Scheme::Tfzxy,
368 "xyz" => Scheme::Xyz,
369 "txyz" => Scheme::Txyz,
370 _ => Scheme::Tms,
371 }
372 }
373}
374impl From<Scheme> for &str {
375 fn from(scheme: Scheme) -> Self {
376 match scheme {
377 Scheme::Fzxy => "fzxy",
378 Scheme::Tfzxy => "tfzxy",
379 Scheme::Xyz => "xyz",
380 Scheme::Txyz => "txyz",
381 Scheme::Tms => "tms",
382 }
383 }
384}
385
386#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
388pub struct Center {
389 pub lon: f64,
391 pub lat: f64,
393 pub zoom: u8,
395}
396
397#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
399pub struct Metadata {
400 #[serde(default)]
402 pub s2tilejson: String,
403 #[serde(default)]
405 pub version: String,
406 #[serde(default)]
408 pub name: String,
409 #[serde(default)]
411 pub scheme: Scheme,
412 #[serde(default)]
414 pub description: String,
415 #[serde(rename = "type", default)]
417 pub type_: SourceType,
418 #[serde(default)]
420 pub extension: String,
421 #[serde(default)]
423 pub encoding: Encoding,
424 #[serde(default)]
426 pub faces: Vec<Face>,
427 #[serde(default)]
429 pub bounds: WMBounds,
430 #[serde(default)]
432 pub facesbounds: FaceBounds,
433 #[serde(default)]
435 pub minzoom: u8,
436 #[serde(default)]
438 pub maxzoom: u8,
439 #[serde(default)]
441 pub center: Center,
442 #[serde(default)]
444 pub attribution: Attribution,
445 #[serde(default)]
447 pub layers: LayersMetaData,
448 #[serde(default)]
450 pub tilestats: TileStatsMetadata,
451 #[serde(default)]
453 pub vector_layers: Vec<VectorLayer>,
454}
455impl Default for Metadata {
456 fn default() -> Self {
457 Self {
458 s2tilejson: "1.0.0".into(),
459 version: "1.0.0".into(),
460 name: "default".into(),
461 scheme: Scheme::default(),
462 description: "Built with s2maps-cli".into(),
463 type_: SourceType::default(),
464 extension: "pbf".into(),
465 encoding: Encoding::default(),
466 faces: Vec::new(),
467 bounds: WMBounds::default(),
468 facesbounds: FaceBounds::default(),
469 minzoom: 0,
470 maxzoom: 27,
471 center: Center::default(),
472 attribution: BTreeMap::new(),
473 layers: LayersMetaData::default(),
474 tilestats: TileStatsMetadata::default(),
475 vector_layers: Vec::new(),
476 }
477 }
478}
479
480#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
494pub struct MapboxTileJSONMetadata {
495 pub tilejson: String,
498 pub tiles: Vec<String>,
500 pub vector_layers: Vec<VectorLayer>,
502 pub attribution: Option<String>,
504 pub bounds: Option<BBox>,
506 pub center: Option<[f64; 3]>,
508 pub data: Option<Vec<String>>,
510 pub description: Option<String>,
512 pub fillzoom: Option<u8>,
514 pub grids: Option<Vec<String>>,
516 pub legend: Option<String>,
518 pub maxzoom: Option<u8>,
520 pub minzoom: Option<u8>,
522 pub name: Option<String>,
524 pub scheme: Option<Scheme>,
526 pub template: Option<String>,
528 pub version: Option<String>,
530}
531impl MapboxTileJSONMetadata {
532 pub fn to_metadata(&self) -> Metadata {
534 Metadata {
535 s2tilejson: "1.0.0".into(),
536 version: self.version.clone().unwrap_or("1.0.0".into()),
537 name: self.name.clone().unwrap_or("default".into()),
538 scheme: self.scheme.clone().unwrap_or_default(),
539 description: self.description.clone().unwrap_or("Built with s2maps-cli".into()),
540 type_: SourceType::default(),
541 extension: "pbf".into(),
542 faces: Vec::from([Face::Face0]),
543 bounds: WMBounds::default(),
544 facesbounds: FaceBounds::default(),
545 minzoom: self.minzoom.unwrap_or(0),
546 maxzoom: self.maxzoom.unwrap_or(27),
547 center: Center {
548 lon: self.center.unwrap_or([0.0, 0.0, 0.0])[0],
549 lat: self.center.unwrap_or([0.0, 0.0, 0.0])[1],
550 zoom: self.center.unwrap_or([0.0, 0.0, 0.0])[2] as u8,
551 },
552 attribution: BTreeMap::new(),
553 layers: LayersMetaData::default(),
554 tilestats: TileStatsMetadata::default(),
555 vector_layers: self.vector_layers.clone(),
556 encoding: Encoding::default(),
557 }
558 }
559}
560
561#[derive(Debug, Clone)]
563pub struct MetadataBuilder {
564 lon_lat_bounds: LonLatBounds,
565 faces: BTreeSet<Face>,
566 metadata: Metadata,
567}
568impl Default for MetadataBuilder {
569 fn default() -> Self {
570 MetadataBuilder {
571 lon_lat_bounds: BBox {
572 left: f64::INFINITY,
573 bottom: f64::INFINITY,
574 right: -f64::INFINITY,
575 top: -f64::INFINITY,
576 },
577 faces: BTreeSet::new(),
578 metadata: Metadata { minzoom: 30, maxzoom: 0, ..Metadata::default() },
579 }
580 }
581}
582impl MetadataBuilder {
583 pub fn commit(&mut self) -> Metadata {
585 self.update_center();
587 for face in &self.faces {
589 self.metadata.faces.push(*face);
590 }
591 self.metadata.to_owned()
593 }
594
595 pub fn set_name(&mut self, name: String) {
597 self.metadata.name = name;
598 }
599
600 pub fn set_scheme(&mut self, scheme: Scheme) {
602 self.metadata.scheme = scheme;
603 }
604
605 pub fn set_extension(&mut self, extension: String) {
607 self.metadata.extension = extension;
608 }
609
610 pub fn set_type(&mut self, type_: SourceType) {
612 self.metadata.type_ = type_;
613 }
614
615 pub fn set_version(&mut self, version: String) {
617 self.metadata.version = version;
618 }
619
620 pub fn set_description(&mut self, description: String) {
622 self.metadata.description = description;
623 }
624
625 pub fn set_encoding(&mut self, encoding: Encoding) {
627 self.metadata.encoding = encoding;
628 }
629
630 pub fn add_attribution(&mut self, display_name: &str, href: &str) {
632 self.metadata.attribution.insert(display_name.into(), href.into());
633 }
634
635 pub fn add_layer(&mut self, name: &str, layer: &LayerMetaData) {
637 if self.metadata.layers.entry(name.into()).or_insert(layer.clone()).eq(&layer) {
639 self.metadata.vector_layers.push(VectorLayer {
641 id: name.into(), description: layer.description.clone(),
643 minzoom: Some(layer.minzoom),
644 maxzoom: Some(layer.maxzoom),
645 fields: BTreeMap::new(),
646 });
647 }
648 if layer.minzoom < self.metadata.minzoom {
650 self.metadata.minzoom = layer.minzoom;
651 }
652 if layer.maxzoom > self.metadata.maxzoom {
653 self.metadata.maxzoom = layer.maxzoom;
654 }
655 }
656
657 pub fn add_tile_wm(&mut self, zoom: u8, x: u32, y: u32, ll_bounds: &LonLatBounds) {
659 self.metadata.tilestats.total += 1;
660 self.faces.insert(Face::Face0);
661 self.add_bounds_wm(zoom, x, y);
662 self.update_lon_lat_bounds(ll_bounds);
663 }
664
665 pub fn add_tile_s2(&mut self, face: Face, zoom: u8, x: u32, y: u32, ll_bounds: &LonLatBounds) {
667 self.metadata.tilestats.increment(face);
668 self.faces.insert(face);
669 self.add_bounds_s2(face, zoom, x, y);
670 self.update_lon_lat_bounds(ll_bounds);
671 }
672
673 fn update_center(&mut self) {
675 let Metadata { minzoom, maxzoom, .. } = self.metadata;
676 let BBox { left, bottom, right, top } = self.lon_lat_bounds;
677 self.metadata.center.lon = (left + right) / 2.0;
678 self.metadata.center.lat = (bottom + top) / 2.0;
679 self.metadata.center.zoom = (minzoom + maxzoom) >> 1;
680 }
681
682 fn add_bounds_wm(&mut self, zoom: u8, x: u32, y: u32) {
684 let x = x as u64;
685 let y = y as u64;
686 let bbox = self.metadata.bounds.entry(zoom).or_insert(BBox {
687 left: u64::MAX,
688 bottom: u64::MAX,
689 right: 0,
690 top: 0,
691 });
692
693 bbox.left = bbox.left.min(x);
694 bbox.bottom = bbox.bottom.min(y);
695 bbox.right = bbox.right.max(x);
696 bbox.top = bbox.top.max(y);
697 }
698
699 fn add_bounds_s2(&mut self, face: Face, zoom: u8, x: u32, y: u32) {
701 let x = x as u64;
702 let y = y as u64;
703 let bbox = self.metadata.facesbounds.get_mut(face).entry(zoom).or_insert(BBox {
704 left: u64::MAX,
705 bottom: u64::MAX,
706 right: 0,
707 top: 0,
708 });
709
710 bbox.left = bbox.left.min(x);
711 bbox.bottom = bbox.bottom.min(y);
712 bbox.right = bbox.right.max(x);
713 bbox.top = bbox.top.max(y);
714 }
715
716 fn update_lon_lat_bounds(&mut self, ll_bounds: &LonLatBounds) {
718 self.lon_lat_bounds.left = ll_bounds.left.min(self.lon_lat_bounds.left);
719 self.lon_lat_bounds.bottom = ll_bounds.bottom.min(self.lon_lat_bounds.bottom);
720 self.lon_lat_bounds.right = ll_bounds.right.max(self.lon_lat_bounds.right);
721 self.lon_lat_bounds.top = ll_bounds.top.max(self.lon_lat_bounds.top);
722 }
723}
724
725#[cfg(test)]
726mod tests {
727 use super::*;
728 use alloc::vec;
729 use s2json::{PrimitiveShape, ShapeType};
730
731 #[test]
732 fn it_works() {
733 let mut meta_builder = MetadataBuilder::default();
734
735 meta_builder.set_name("OSM".into());
737 meta_builder.set_description("A free editable map of the whole world.".into());
738 meta_builder.set_version("1.0.0".into());
739 meta_builder.set_scheme("fzxy".into()); meta_builder.set_type("vector".into()); meta_builder.set_encoding("none".into()); meta_builder.set_extension("pbf".into());
743 meta_builder.add_attribution("OpenStreetMap", "https://www.openstreetmap.org/copyright/");
744
745 let shape_str = r#"
747 {
748 "class": "string",
749 "offset": "f64",
750 "info": {
751 "name": "string",
752 "value": "i64"
753 }
754 }
755 "#;
756 let shape: Shape =
757 serde_json::from_str(shape_str).unwrap_or_else(|e| panic!("ERROR: {}", e));
758 let layer = LayerMetaData {
759 minzoom: 0,
760 maxzoom: 13,
761 description: Some("water_lines".into()),
762 draw_types: Vec::from(&[DrawType::Lines]),
763 shape: shape.clone(),
764 m_shape: None,
765 };
766 meta_builder.add_layer("water_lines", &layer);
767
768 meta_builder.add_tile_wm(
771 0,
772 0,
773 0,
774 &LonLatBounds { left: -60.0, bottom: -20.0, right: 5.0, top: 60.0 },
775 );
776 meta_builder.add_tile_s2(
778 Face::Face1,
779 5,
780 22,
781 37,
782 &LonLatBounds { left: -120.0, bottom: -7.0, right: 44.0, top: 72.0 },
783 );
784
785 let resulting_metadata: Metadata = meta_builder.commit();
787
788 assert_eq!(
789 resulting_metadata,
790 Metadata {
791 name: "OSM".into(),
792 description: "A free editable map of the whole world.".into(),
793 version: "1.0.0".into(),
794 scheme: "fzxy".into(),
795 type_: "vector".into(),
796 encoding: "none".into(),
797 extension: "pbf".into(),
798 attribution: BTreeMap::from([(
799 "OpenStreetMap".into(),
800 "https://www.openstreetmap.org/copyright/".into()
801 ),]),
802 bounds: BTreeMap::from([(0, TileBounds { left: 0, bottom: 0, right: 0, top: 0 }),]),
803 faces: Vec::from(&[Face::Face0, Face::Face1]),
804 facesbounds: FaceBounds {
805 face0: BTreeMap::new(),
806 face1: BTreeMap::from([(
807 5,
808 TileBounds { left: 22, bottom: 37, right: 22, top: 37 }
809 ),]),
810 face2: BTreeMap::new(),
811 face3: BTreeMap::new(),
812 face4: BTreeMap::new(),
813 face5: BTreeMap::new(),
814 },
815 minzoom: 0,
816 maxzoom: 13,
817 center: Center { lon: -38.0, lat: 26.0, zoom: 6 },
818 tilestats: TileStatsMetadata {
819 total: 2,
820 total_0: 0,
821 total_1: 1,
822 total_2: 0,
823 total_3: 0,
824 total_4: 0,
825 total_5: 0,
826 },
827 layers: BTreeMap::from([(
828 "water_lines".into(),
829 LayerMetaData {
830 description: Some("water_lines".into()),
831 minzoom: 0,
832 maxzoom: 13,
833 draw_types: Vec::from(&[DrawType::Lines]),
834 shape: Shape::from([
835 ("class".into(), ShapeType::Primitive(PrimitiveShape::String)),
836 ("offset".into(), ShapeType::Primitive(PrimitiveShape::F64)),
837 (
838 "info".into(),
839 ShapeType::Nested(Shape::from([
840 ("name".into(), ShapeType::Primitive(PrimitiveShape::String)),
841 ("value".into(), ShapeType::Primitive(PrimitiveShape::I64)),
842 ]))
843 ),
844 ]),
845 m_shape: None,
846 }
847 )]),
848 s2tilejson: "1.0.0".into(),
849 vector_layers: Vec::from([VectorLayer {
850 id: "water_lines".into(),
851 description: Some("water_lines".into()),
852 minzoom: Some(0),
853 maxzoom: Some(13),
854 fields: BTreeMap::new()
855 }]),
856 }
857 );
858
859 let meta_str = serde_json::to_string(&resulting_metadata).unwrap();
860
861 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\":{}}]}");
862
863 let meta_reparsed: Metadata =
864 serde_json::from_str(&meta_str).unwrap_or_else(|e| panic!("ERROR: {}", e));
865 assert_eq!(meta_reparsed, resulting_metadata);
866 }
867
868 #[test]
869 fn test_face() {
870 assert_eq!(Face::Face0, Face::from(0));
871 assert_eq!(Face::Face1, Face::from(1));
872 assert_eq!(Face::Face2, Face::from(2));
873 assert_eq!(Face::Face3, Face::from(3));
874 assert_eq!(Face::Face4, Face::from(4));
875 assert_eq!(Face::Face5, Face::from(5));
876
877 assert_eq!(0, u8::from(Face::Face0));
878 assert_eq!(1, u8::from(Face::Face1));
879 assert_eq!(2, u8::from(Face::Face2));
880 assert_eq!(3, u8::from(Face::Face3));
881 assert_eq!(4, u8::from(Face::Face4));
882 assert_eq!(5, u8::from(Face::Face5));
883 }
884
885 #[test]
886 fn test_bbox() {
887 let bbox: BBox = BBox { left: 0.0, bottom: 0.0, right: 0.0, top: 0.0 };
888 let json = serde_json::to_string(&bbox).unwrap();
890 assert_eq!(json, r#"[0.0,0.0,0.0,0.0]"#);
891 let bbox2: BBox = serde_json::from_str(&json).unwrap();
892 assert_eq!(bbox, bbox2);
893 }
894
895 #[test]
897 fn test_tilestats() {
898 let mut tilestats = TileStatsMetadata {
899 total: 2,
900 total_0: 0,
901 total_1: 1,
902 total_2: 0,
903 total_3: 0,
904 total_4: 0,
905 total_5: 0,
906 };
907 let json = serde_json::to_string(&tilestats).unwrap();
909 assert_eq!(json, r#"{"total":2,"0":0,"1":1,"2":0,"3":0,"4":0,"5":0}"#);
910 let tilestats2: TileStatsMetadata = serde_json::from_str(&json).unwrap();
911 assert_eq!(tilestats, tilestats2);
912
913 assert_eq!(tilestats.get(0.into()), 0);
915 tilestats.increment(0.into());
917 assert_eq!(tilestats.get(0.into()), 1);
918
919 assert_eq!(tilestats.get(1.into()), 1);
921 tilestats.increment(1.into());
923 assert_eq!(tilestats.get(1.into()), 2);
924
925 assert_eq!(tilestats.get(2.into()), 0);
927 tilestats.increment(2.into());
929 assert_eq!(tilestats.get(2.into()), 1);
930
931 assert_eq!(tilestats.get(3.into()), 0);
933 tilestats.increment(3.into());
935 assert_eq!(tilestats.get(3.into()), 1);
936
937 assert_eq!(tilestats.get(4.into()), 0);
939 tilestats.increment(4.into());
941 assert_eq!(tilestats.get(4.into()), 1);
942
943 assert_eq!(tilestats.get(5.into()), 0);
945 tilestats.increment(5.into());
947 assert_eq!(tilestats.get(5.into()), 1);
948 }
949
950 #[test]
952 fn test_facebounds() {
953 let mut facebounds = FaceBounds::default();
954 let face0 = facebounds.get_mut(0.into());
956 face0.insert(0, TileBounds { left: 0, bottom: 0, right: 0, top: 0 });
957 let face1 = facebounds.get_mut(1.into());
959 face1.insert(0, TileBounds { left: 0, bottom: 0, right: 1, top: 1 });
960 let face2 = facebounds.get_mut(2.into());
962 face2.insert(0, TileBounds { left: 0, bottom: 0, right: 2, top: 2 });
963 let face3 = facebounds.get_mut(3.into());
965 face3.insert(0, TileBounds { left: 0, bottom: 0, right: 3, top: 3 });
966 let face4 = facebounds.get_mut(4.into());
968 face4.insert(0, TileBounds { left: 0, bottom: 0, right: 4, top: 4 });
969 let face5 = facebounds.get_mut(5.into());
971 face5.insert(0, TileBounds { left: 0, bottom: 0, right: 5, top: 5 });
972
973 assert_eq!(
976 facebounds.get(0.into()).get(&0).unwrap(),
977 &TileBounds { left: 0, bottom: 0, right: 0, top: 0 }
978 );
979 assert_eq!(
981 facebounds.get(1.into()).get(&0).unwrap(),
982 &TileBounds { left: 0, bottom: 0, right: 1, top: 1 }
983 );
984 assert_eq!(
986 facebounds.get(2.into()).get(&0).unwrap(),
987 &TileBounds { left: 0, bottom: 0, right: 2, top: 2 }
988 );
989 assert_eq!(
991 facebounds.get(3.into()).get(&0).unwrap(),
992 &TileBounds { left: 0, bottom: 0, right: 3, top: 3 }
993 );
994 assert_eq!(
996 facebounds.get(4.into()).get(&0).unwrap(),
997 &TileBounds { left: 0, bottom: 0, right: 4, top: 4 }
998 );
999 assert_eq!(
1001 facebounds.get(5.into()).get(&0).unwrap(),
1002 &TileBounds { left: 0, bottom: 0, right: 5, top: 5 }
1003 );
1004
1005 let json = serde_json::to_string(&facebounds).unwrap();
1007 assert_eq!(
1008 json,
1009 "{\"0\":{\"0\":[0,0,0,0]},\"1\":{\"0\":[0,0,1,1]},\"2\":{\"0\":[0,0,2,2]},\"3\":{\"0\"\
1010 :[0,0,3,3]},\"4\":{\"0\":[0,0,4,4]},\"5\":{\"0\":[0,0,5,5]}}"
1011 );
1012 let facebounds2 = serde_json::from_str(&json).unwrap();
1013 assert_eq!(facebounds, facebounds2);
1014 }
1015
1016 #[test]
1018 fn test_drawtype() {
1019 assert_eq!(DrawType::from(1), DrawType::Points);
1020 assert_eq!(DrawType::from(2), DrawType::Lines);
1021 assert_eq!(DrawType::from(3), DrawType::Polygons);
1022 assert_eq!(DrawType::from(4), DrawType::Points3D);
1023 assert_eq!(DrawType::from(5), DrawType::Lines3D);
1024 assert_eq!(DrawType::from(6), DrawType::Polygons3D);
1025 assert_eq!(DrawType::from(7), DrawType::Raster);
1026 assert_eq!(DrawType::from(8), DrawType::Grid);
1027
1028 assert_eq!(1, u8::from(DrawType::Points));
1029 assert_eq!(2, u8::from(DrawType::Lines));
1030 assert_eq!(3, u8::from(DrawType::Polygons));
1031 assert_eq!(4, u8::from(DrawType::Points3D));
1032 assert_eq!(5, u8::from(DrawType::Lines3D));
1033 assert_eq!(6, u8::from(DrawType::Polygons3D));
1034 assert_eq!(7, u8::from(DrawType::Raster));
1035 assert_eq!(8, u8::from(DrawType::Grid));
1036
1037 let json = serde_json::to_string(&DrawType::Points).unwrap();
1039 assert_eq!(json, "1");
1040 let drawtype: DrawType = serde_json::from_str(&json).unwrap();
1041 assert_eq!(drawtype, DrawType::Points);
1042
1043 let drawtype: DrawType = serde_json::from_str("2").unwrap();
1044 assert_eq!(drawtype, DrawType::Lines);
1045
1046 let drawtype: DrawType = serde_json::from_str("3").unwrap();
1047 assert_eq!(drawtype, DrawType::Polygons);
1048
1049 let drawtype: DrawType = serde_json::from_str("4").unwrap();
1050 assert_eq!(drawtype, DrawType::Points3D);
1051
1052 let drawtype: DrawType = serde_json::from_str("5").unwrap();
1053 assert_eq!(drawtype, DrawType::Lines3D);
1054
1055 let drawtype: DrawType = serde_json::from_str("6").unwrap();
1056 assert_eq!(drawtype, DrawType::Polygons3D);
1057
1058 let drawtype: DrawType = serde_json::from_str("7").unwrap();
1059 assert_eq!(drawtype, DrawType::Raster);
1060
1061 let drawtype: DrawType = serde_json::from_str("8").unwrap();
1062 assert_eq!(drawtype, DrawType::Grid);
1063
1064 assert!(serde_json::from_str::<DrawType>("9").is_err());
1065 }
1066
1067 #[test]
1069 fn test_sourcetype() {
1070 assert_eq!(SourceType::from("vector"), SourceType::Vector);
1072 assert_eq!(SourceType::from("json"), SourceType::Json);
1073 assert_eq!(SourceType::from("raster"), SourceType::Raster);
1074 assert_eq!(SourceType::from("raster-dem"), SourceType::RasterDem);
1075 assert_eq!(SourceType::from("grid"), SourceType::Grid);
1076 assert_eq!(SourceType::from("markers"), SourceType::Markers);
1077 assert_eq!(SourceType::from("overlay"), SourceType::Unknown);
1078
1079 let json = serde_json::to_string(&SourceType::Vector).unwrap();
1081 assert_eq!(json, "\"vector\"");
1082 let sourcetype: SourceType = serde_json::from_str(&json).unwrap();
1083 assert_eq!(sourcetype, SourceType::Vector);
1084
1085 let json = serde_json::to_string(&SourceType::Json).unwrap();
1087 assert_eq!(json, "\"json\"");
1088 let sourcetype: SourceType = serde_json::from_str(&json).unwrap();
1089 assert_eq!(sourcetype, SourceType::Json);
1090
1091 let json = serde_json::to_string(&SourceType::Raster).unwrap();
1093 assert_eq!(json, "\"raster\"");
1094 let sourcetype: SourceType = serde_json::from_str(&json).unwrap();
1095 assert_eq!(sourcetype, SourceType::Raster);
1096
1097 let json = serde_json::to_string(&SourceType::RasterDem).unwrap();
1099 assert_eq!(json, "\"raster-dem\"");
1100 let sourcetype: SourceType = serde_json::from_str(&json).unwrap();
1101 assert_eq!(sourcetype, SourceType::RasterDem);
1102
1103 let json = serde_json::to_string(&SourceType::Grid).unwrap();
1105 assert_eq!(json, "\"grid\"");
1106 let sourcetype: SourceType = serde_json::from_str(&json).unwrap();
1107 assert_eq!(sourcetype, SourceType::Grid);
1108
1109 let json = serde_json::to_string(&SourceType::Markers).unwrap();
1111 assert_eq!(json, "\"markers\"");
1112 let sourcetype: SourceType = serde_json::from_str(&json).unwrap();
1113 assert_eq!(sourcetype, SourceType::Markers);
1114
1115 let json = serde_json::to_string(&SourceType::Unknown).unwrap();
1117 assert_eq!(json, "\"unknown\"");
1118 let sourcetype: SourceType = serde_json::from_str(r#""overlay""#).unwrap();
1119 assert_eq!(sourcetype, SourceType::Unknown);
1120 }
1121
1122 #[test]
1124 fn test_encoding() {
1125 assert_eq!(Encoding::from("none"), Encoding::None);
1127 assert_eq!(Encoding::from("gzip"), Encoding::Gzip);
1128 assert_eq!(Encoding::from("br"), Encoding::Brotli);
1129 assert_eq!(Encoding::from("zstd"), Encoding::Zstd);
1130
1131 assert_eq!(core::convert::Into::<&str>::into(Encoding::None), "none");
1133 assert_eq!(core::convert::Into::<&str>::into(Encoding::Gzip), "gzip");
1134 assert_eq!(core::convert::Into::<&str>::into(Encoding::Brotli), "br");
1135 assert_eq!(core::convert::Into::<&str>::into(Encoding::Zstd), "zstd");
1136
1137 assert_eq!(Encoding::from(0), Encoding::None);
1139 assert_eq!(Encoding::from(1), Encoding::Gzip);
1140 assert_eq!(Encoding::from(2), Encoding::Brotli);
1141 assert_eq!(Encoding::from(3), Encoding::Zstd);
1142
1143 assert_eq!(u8::from(Encoding::None), 0);
1145 assert_eq!(u8::from(Encoding::Gzip), 1);
1146 assert_eq!(u8::from(Encoding::Brotli), 2);
1147 assert_eq!(u8::from(Encoding::Zstd), 3);
1148
1149 let json = serde_json::to_string(&Encoding::Gzip).unwrap();
1151 assert_eq!(json, "\"gzip\"");
1152 let encoding: Encoding = serde_json::from_str(&json).unwrap();
1153 assert_eq!(encoding, Encoding::Gzip);
1154
1155 let json = serde_json::to_string(&Encoding::Brotli).unwrap();
1157 assert_eq!(json, "\"br\"");
1158 let encoding: Encoding = serde_json::from_str(&json).unwrap();
1159 assert_eq!(encoding, Encoding::Brotli);
1160
1161 let json = serde_json::to_string(&Encoding::None).unwrap();
1163 assert_eq!(json, "\"none\"");
1164 let encoding: Encoding = serde_json::from_str(&json).unwrap();
1165 assert_eq!(encoding, Encoding::None);
1166
1167 let json = serde_json::to_string(&Encoding::Zstd).unwrap();
1169 assert_eq!(json, "\"zstd\"");
1170 let encoding: Encoding = serde_json::from_str(&json).unwrap();
1171 assert_eq!(encoding, Encoding::Zstd);
1172 }
1173
1174 #[test]
1176 fn test_scheme() {
1177 assert_eq!(Scheme::from("fzxy"), Scheme::Fzxy);
1179 assert_eq!(Scheme::from("tfzxy"), Scheme::Tfzxy);
1180 assert_eq!(Scheme::from("xyz"), Scheme::Xyz);
1181 assert_eq!(Scheme::from("txyz"), Scheme::Txyz);
1182 assert_eq!(Scheme::from("tms"), Scheme::Tms);
1183
1184 assert_eq!(core::convert::Into::<&str>::into(Scheme::Fzxy), "fzxy");
1186 assert_eq!(core::convert::Into::<&str>::into(Scheme::Tfzxy), "tfzxy");
1187 assert_eq!(core::convert::Into::<&str>::into(Scheme::Xyz), "xyz");
1188 assert_eq!(core::convert::Into::<&str>::into(Scheme::Txyz), "txyz");
1189 assert_eq!(core::convert::Into::<&str>::into(Scheme::Tms), "tms");
1190 }
1191
1192 #[test]
1193 fn test_tippecanoe_metadata() {
1194 let meta_str = r#"{
1195 "name": "test_fixture_1.pmtiles",
1196 "description": "test_fixture_1.pmtiles",
1197 "version": "2",
1198 "type": "overlay",
1199 "generator": "tippecanoe v2.5.0",
1200 "generator_options": "./tippecanoe -zg -o test_fixture_1.pmtiles --force",
1201 "vector_layers": [
1202 {
1203 "id": "test_fixture_1pmtiles",
1204 "description": "",
1205 "minzoom": 0,
1206 "maxzoom": 0,
1207 "fields": {}
1208 }
1209 ],
1210 "tilestats": {
1211 "layerCount": 1,
1212 "layers": [
1213 {
1214 "layer": "test_fixture_1pmtiles",
1215 "count": 1,
1216 "geometry": "Polygon",
1217 "attributeCount": 0,
1218 "attributes": []
1219 }
1220 ]
1221 }
1222 }"#;
1223
1224 let _meta: Metadata =
1225 serde_json::from_str(meta_str).unwrap_or_else(|e| panic!("ERROR: {}", e));
1226 }
1227
1228 #[test]
1229 fn test_mapbox_metadata() {
1230 let meta_str = r#"{
1231 "tilejson": "3.0.0",
1232 "name": "OpenStreetMap",
1233 "description": "A free editable map of the whole world.",
1234 "version": "1.0.0",
1235 "attribution": "(c) OpenStreetMap contributors, CC-BY-SA",
1236 "scheme": "xyz",
1237 "tiles": [
1238 "https://a.tile.custom-osm-tiles.org/{z}/{x}/{y}.mvt",
1239 "https://b.tile.custom-osm-tiles.org/{z}/{x}/{y}.mvt",
1240 "https://c.tile.custom-osm-tiles.org/{z}/{x}/{y}.mvt"
1241 ],
1242 "minzoom": 0,
1243 "maxzoom": 18,
1244 "bounds": [-180, -85, 180, 85],
1245 "fillzoom": 6,
1246 "something_custom": "this is my unique field",
1247 "vector_layers": [
1248 {
1249 "id": "telephone",
1250 "fields": {
1251 "phone_number": "the phone number",
1252 "payment": "how to pay"
1253 }
1254 },
1255 {
1256 "id": "bicycle_parking",
1257 "fields": {
1258 "type": "the type of bike parking",
1259 "year_installed": "the year the bike parking was installed"
1260 }
1261 },
1262 {
1263 "id": "showers",
1264 "fields": {
1265 "water_temperature": "the maximum water temperature",
1266 "wear_sandles": "whether you should wear sandles or not",
1267 "wheelchair": "is the shower wheelchair friendly?"
1268 }
1269 }
1270 ]
1271 }"#;
1272
1273 let meta_mapbox: MapboxTileJSONMetadata =
1274 serde_json::from_str(meta_str).unwrap_or_else(|e| panic!("ERROR: {}", e));
1275 let meta_new = meta_mapbox.to_metadata();
1276 assert_eq!(
1277 meta_new,
1278 Metadata {
1279 name: "OpenStreetMap".into(),
1280 description: "A free editable map of the whole world.".into(),
1281 version: "1.0.0".into(),
1282 scheme: Scheme::Xyz,
1283 type_: "vector".into(),
1284 encoding: "none".into(),
1285 extension: "pbf".into(),
1286 attribution: BTreeMap::new(),
1287 vector_layers: meta_mapbox.vector_layers.clone(),
1288 maxzoom: 18,
1289 minzoom: 0,
1290 center: Center { lat: 0.0, lon: 0.0, zoom: 0 },
1291 bounds: WMBounds::default(),
1292 faces: vec![Face::Face0],
1293 facesbounds: FaceBounds::default(),
1294 tilestats: TileStatsMetadata::default(),
1295 layers: LayersMetaData::default(),
1296 s2tilejson: "1.0.0".into(),
1297 },
1298 );
1299 }
1300}