1#![no_std]
2#![forbid(unsafe_code)]
3#![deny(missing_docs)]
4extern crate alloc;
7
8use alloc::{
9 borrow::ToOwned,
10 boxed::Box,
11 collections::{BTreeMap, BTreeSet},
12 format,
13 string::String,
14 vec::Vec,
15};
16pub use s2json::*;
17use serde::{Deserialize, Deserializer, Serialize, Serializer};
18
19pub type LonLatBounds = BBox<f64>;
21
22pub type TileBounds = BBox<u64>;
24
25#[derive(Copy, Clone, Debug, PartialEq)]
27pub enum DrawType {
28 Points = 1,
30 Lines = 2,
32 Polygons = 3,
34 Points3D = 4,
36 Lines3D = 5,
38 Polygons3D = 6,
40 Raster = 7,
42 Grid = 8,
44}
45impl From<DrawType> for u8 {
46 fn from(draw_type: DrawType) -> Self {
47 draw_type as u8
48 }
49}
50impl From<u8> for DrawType {
51 fn from(draw_type: u8) -> Self {
52 match draw_type {
53 2 => DrawType::Lines,
54 3 => DrawType::Polygons,
55 4 => DrawType::Points3D,
56 5 => DrawType::Lines3D,
57 6 => DrawType::Polygons3D,
58 7 => DrawType::Raster,
59 8 => DrawType::Grid,
60 _ => DrawType::Points, }
62 }
63}
64impl Serialize for DrawType {
65 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
66 where
67 S: Serializer,
68 {
69 serializer.serialize_u8(*self as u8)
71 }
72}
73
74impl<'de> Deserialize<'de> for DrawType {
75 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
76 where
77 D: Deserializer<'de>,
78 {
79 let value: u8 = Deserialize::deserialize(deserializer)?;
81 match value {
82 1 => Ok(DrawType::Points),
83 2 => Ok(DrawType::Lines),
84 3 => Ok(DrawType::Polygons),
85 4 => Ok(DrawType::Points3D),
86 5 => Ok(DrawType::Lines3D),
87 6 => Ok(DrawType::Polygons3D),
88 7 => Ok(DrawType::Raster),
89 8 => Ok(DrawType::Grid),
90 _ => Err(serde::de::Error::custom(format!("unknown DrawType variant: {}", value))),
91 }
92 }
93}
94
95#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
97pub struct LayerMetaData {
98 #[serde(skip_serializing_if = "Option::is_none")]
100 pub description: Option<String>,
101 pub minzoom: u8,
103 pub maxzoom: u8,
105 pub draw_types: Vec<DrawType>,
107 pub shape: Shape,
109 #[serde(skip_serializing_if = "Option::is_none", rename = "mShape")]
111 pub m_shape: Option<Shape>,
112}
113
114pub type LayersMetaData = BTreeMap<String, LayerMetaData>;
116
117#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
119pub struct TileStatsMetadata {
120 #[serde(default)]
122 pub total: u64,
123 #[serde(rename = "0", default)]
125 pub total_0: u64,
126 #[serde(rename = "1", default)]
128 pub total_1: u64,
129 #[serde(rename = "2", default)]
131 pub total_2: u64,
132 #[serde(rename = "3", default)]
134 pub total_3: u64,
135 #[serde(rename = "4", default)]
137 pub total_4: u64,
138 #[serde(rename = "5", default)]
140 pub total_5: u64,
141}
142impl TileStatsMetadata {
143 pub fn get(&self, face: Face) -> u64 {
145 match face {
146 Face::Face0 => self.total_0,
147 Face::Face1 => self.total_1,
148 Face::Face2 => self.total_2,
149 Face::Face3 => self.total_3,
150 Face::Face4 => self.total_4,
151 Face::Face5 => self.total_5,
152 }
153 }
154
155 pub fn increment(&mut self, face: Face) {
157 match face {
158 Face::Face0 => self.total_0 += 1,
159 Face::Face1 => self.total_1 += 1,
160 Face::Face2 => self.total_2 += 1,
161 Face::Face3 => self.total_3 += 1,
162 Face::Face4 => self.total_4 += 1,
163 Face::Face5 => self.total_5 += 1,
164 }
165 self.total += 1;
166 }
167}
168
169pub type Attributions = BTreeMap<String, String>;
172
173#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
175pub struct FaceBounds {
176 #[serde(rename = "0")]
179 pub face0: BTreeMap<u8, TileBounds>,
180 #[serde(rename = "1")]
182 pub face1: BTreeMap<u8, TileBounds>,
183 #[serde(rename = "2")]
185 pub face2: BTreeMap<u8, TileBounds>,
186 #[serde(rename = "3")]
188 pub face3: BTreeMap<u8, TileBounds>,
189 #[serde(rename = "4")]
191 pub face4: BTreeMap<u8, TileBounds>,
192 #[serde(rename = "5")]
194 pub face5: BTreeMap<u8, TileBounds>,
195}
196impl FaceBounds {
197 pub fn get(&self, face: Face) -> &BTreeMap<u8, TileBounds> {
199 match face {
200 Face::Face0 => &self.face0,
201 Face::Face1 => &self.face1,
202 Face::Face2 => &self.face2,
203 Face::Face3 => &self.face3,
204 Face::Face4 => &self.face4,
205 Face::Face5 => &self.face5,
206 }
207 }
208
209 pub fn get_mut(&mut self, face: Face) -> &mut BTreeMap<u8, TileBounds> {
211 match face {
212 Face::Face0 => &mut self.face0,
213 Face::Face1 => &mut self.face1,
214 Face::Face2 => &mut self.face2,
215 Face::Face3 => &mut self.face3,
216 Face::Face4 => &mut self.face4,
217 Face::Face5 => &mut self.face5,
218 }
219 }
220}
221
222pub type WMBounds = BTreeMap<u8, TileBounds>;
225
226#[derive(Serialize, Debug, Default, Clone, PartialEq)]
228#[serde(rename_all = "lowercase")]
229pub enum SourceType {
230 #[default]
232 Vector,
233 Json,
235 Raster,
237 #[serde(rename = "raster-dem")]
239 RasterDem,
240 Grid,
242 Markers,
244 Unknown,
246}
247impl From<&str> for SourceType {
248 fn from(source_type: &str) -> Self {
249 match source_type {
250 "vector" => SourceType::Vector,
251 "json" => SourceType::Json,
252 "raster" => SourceType::Raster,
253 "raster-dem" => SourceType::RasterDem,
254 "grid" => SourceType::Grid,
255 "markers" => SourceType::Markers,
256 _ => SourceType::Unknown,
257 }
258 }
259}
260impl<'de> Deserialize<'de> for SourceType {
261 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
262 where
263 D: Deserializer<'de>,
264 {
265 let s: String = Deserialize::deserialize(deserializer)?;
267 Ok(SourceType::from(s.as_str()))
268 }
269}
270
271#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
273#[serde(rename_all = "lowercase")]
274pub enum Encoding {
275 #[default]
277 None = 0,
278 Gzip = 1,
280 #[serde(rename = "br")]
282 Brotli = 2,
283 Zstd = 3,
285}
286impl From<u8> for Encoding {
287 fn from(encoding: u8) -> Self {
288 match encoding {
289 1 => Encoding::Gzip,
290 2 => Encoding::Brotli,
291 3 => Encoding::Zstd,
292 _ => Encoding::None,
293 }
294 }
295}
296impl From<Encoding> for u8 {
297 fn from(encoding: Encoding) -> Self {
298 match encoding {
299 Encoding::Gzip => 1,
300 Encoding::Brotli => 2,
301 Encoding::Zstd => 3,
302 Encoding::None => 0,
303 }
304 }
305}
306impl From<Encoding> for &str {
307 fn from(encoding: Encoding) -> Self {
308 match encoding {
309 Encoding::Gzip => "gzip",
310 Encoding::Brotli => "br",
311 Encoding::Zstd => "zstd",
312 Encoding::None => "none",
313 }
314 }
315}
316impl From<&str> for Encoding {
317 fn from(encoding: &str) -> Self {
318 match encoding {
319 "gzip" => Encoding::Gzip,
320 "br" => Encoding::Brotli,
321 "zstd" => Encoding::Zstd,
322 _ => Encoding::None,
323 }
324 }
325}
326
327#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
329pub struct VectorLayer {
330 pub id: String,
332 #[serde(skip_serializing_if = "Option::is_none")]
334 pub description: Option<String>,
335 #[serde(skip_serializing_if = "Option::is_none")]
337 pub minzoom: Option<u8>,
338 #[serde(skip_serializing_if = "Option::is_none")]
340 pub maxzoom: Option<u8>,
341 pub fields: BTreeMap<String, String>,
343}
344
345#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
350#[serde(rename_all = "lowercase")]
351pub enum Scheme {
352 #[default]
354 Fzxy,
355 Tfzxy,
357 Xyz,
359 Txyz,
361 Tms,
363}
364impl From<&str> for Scheme {
365 fn from(scheme: &str) -> Self {
366 match scheme {
367 "fzxy" => Scheme::Fzxy,
368 "tfzxy" => Scheme::Tfzxy,
369 "xyz" => Scheme::Xyz,
370 "txyz" => Scheme::Txyz,
371 _ => Scheme::Tms,
372 }
373 }
374}
375impl From<Scheme> for &str {
376 fn from(scheme: Scheme) -> Self {
377 match scheme {
378 Scheme::Fzxy => "fzxy",
379 Scheme::Tfzxy => "tfzxy",
380 Scheme::Xyz => "xyz",
381 Scheme::Txyz => "txyz",
382 Scheme::Tms => "tms",
383 }
384 }
385}
386
387#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
389pub struct Center {
390 pub lon: f64,
392 pub lat: f64,
394 pub zoom: u8,
396}
397
398#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
400#[serde(default)]
401pub struct Metadata {
402 pub s2tilejson: String,
404 pub version: String,
406 pub name: String,
408 pub scheme: Scheme,
410 pub description: String,
412 #[serde(rename = "type")]
414 pub r#type: SourceType,
415 pub extension: String,
417 pub encoding: Encoding,
419 pub faces: Vec<Face>,
421 pub bounds: LonLatBounds,
423 pub wmbounds: WMBounds,
425 pub s2bounds: FaceBounds,
427 pub minzoom: u8,
429 pub maxzoom: u8,
431 pub centerpoint: Center,
433 pub attributions: Attributions,
435 pub layers: LayersMetaData,
437 pub tilestats: TileStatsMetadata,
439 pub vector_layers: Vec<VectorLayer>,
441
442 pub tilejson: Option<String>,
445 #[serde(skip_serializing_if = "Option::is_none")]
447 pub tiles: Option<Vec<String>>,
448 #[serde(skip_serializing_if = "Option::is_none")]
450 pub attribution: Option<String>,
451 #[serde(skip_serializing_if = "Option::is_none")]
453 pub fillzoom: Option<u8>,
454 #[serde(skip_serializing_if = "Option::is_none")]
456 pub center: Option<[f64; 3]>,
457 #[serde(skip_serializing_if = "Option::is_none")]
459 pub data: Option<Vec<String>>,
460 #[serde(skip_serializing_if = "Option::is_none")]
462 pub grids: Option<Vec<String>>,
463 #[serde(skip_serializing_if = "Option::is_none")]
465 pub legend: Option<String>,
466 #[serde(skip_serializing_if = "Option::is_none")]
468 pub template: Option<String>,
469}
470impl Default for Metadata {
471 fn default() -> Self {
472 Self {
473 s2tilejson: "1.0.0".into(),
474 version: "1.0.0".into(),
475 name: "default".into(),
476 scheme: Scheme::default(),
477 description: "Built with s2maps-cli".into(),
478 r#type: SourceType::default(),
479 extension: "pbf".into(),
480 encoding: Encoding::default(),
481 faces: Vec::default(),
482 bounds: BBox::new(-180.0, -90.0, 180.0, 90.0),
483 wmbounds: WMBounds::default(),
484 s2bounds: FaceBounds::default(),
485 minzoom: 0,
486 maxzoom: 27,
487 centerpoint: Center::default(),
488 attributions: BTreeMap::new(),
489 layers: LayersMetaData::default(),
490 tilestats: TileStatsMetadata::default(),
491 vector_layers: Vec::new(),
492 attribution: None,
493 fillzoom: None,
494 center: None,
495 data: None,
496 grids: None,
497 legend: None,
498 template: None,
499 tilejson: None,
500 tiles: None,
501 }
502 }
503}
504
505#[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq)]
519#[serde(default)]
520pub struct MapboxTileJSONMetadata {
521 pub tilejson: String,
523 pub tiles: Vec<String>,
525 pub vector_layers: Vec<VectorLayer>,
527 #[serde(skip_serializing_if = "Option::is_none")]
529 pub attribution: Option<String>,
530 #[serde(skip_serializing_if = "Option::is_none")]
532 pub bounds: Option<LonLatBounds>,
533 #[serde(skip_serializing_if = "Option::is_none")]
535 pub center: Option<[f64; 3]>,
536 #[serde(skip_serializing_if = "Option::is_none")]
538 pub data: Option<Vec<String>>,
539 #[serde(skip_serializing_if = "Option::is_none")]
541 pub description: Option<String>,
542 #[serde(skip_serializing_if = "Option::is_none")]
544 pub fillzoom: Option<u8>,
545 #[serde(skip_serializing_if = "Option::is_none")]
547 pub grids: Option<Vec<String>>,
548 #[serde(skip_serializing_if = "Option::is_none")]
550 pub legend: Option<String>,
551 #[serde(skip_serializing_if = "Option::is_none")]
553 pub maxzoom: Option<u8>,
554 #[serde(skip_serializing_if = "Option::is_none")]
556 pub minzoom: Option<u8>,
557 #[serde(skip_serializing_if = "Option::is_none")]
559 pub name: Option<String>,
560 #[serde(skip_serializing_if = "Option::is_none")]
562 pub scheme: Option<Scheme>,
563 #[serde(skip_serializing_if = "Option::is_none")]
565 pub template: Option<String>,
566 #[serde(skip_serializing_if = "Option::is_none")]
568 pub version: Option<String>,
569 #[serde(skip_serializing_if = "Option::is_none")]
572 pub r#type: Option<SourceType>,
573 #[serde(skip_serializing_if = "Option::is_none")]
575 pub extension: Option<String>,
576 #[serde(skip_serializing_if = "Option::is_none")]
578 pub encoding: Option<Encoding>,
579}
580impl MapboxTileJSONMetadata {
581 pub fn to_metadata(&self) -> Metadata {
583 let [lon, lat, zoom] = self.center.unwrap_or([0.0, 0.0, 0.0]);
584 Metadata {
585 s2tilejson: "1.0.0".into(),
586 version: self.version.clone().unwrap_or("1.0.0".into()),
587 name: self.name.clone().unwrap_or("default".into()),
588 scheme: self.scheme.clone().unwrap_or_default(),
589 description: self.description.clone().unwrap_or("Built with s2maps-cli".into()),
590 r#type: self.r#type.clone().unwrap_or_default(),
591 extension: self.extension.clone().unwrap_or("pbf".into()),
592 faces: Vec::from([Face::Face0]),
593 bounds: self.bounds.unwrap_or_default(),
594 minzoom: self.minzoom.unwrap_or(0),
595 maxzoom: self.maxzoom.unwrap_or(27),
596 centerpoint: Center { lon, lat, zoom: zoom as u8 },
597 center: Some([lon, lat, zoom]),
598 attributions: extract_link_info(self.attribution.as_ref().unwrap_or(&"".into()))
599 .unwrap_or_default(),
600 vector_layers: self.vector_layers.clone(),
601 encoding: self.encoding.clone().unwrap_or(Encoding::None),
602 attribution: self.attribution.clone(),
603 tiles: Some(self.tiles.clone()),
604 data: self.data.clone(),
605 grids: self.grids.clone(),
606 legend: self.legend.clone(),
607 template: self.template.clone(),
608 fillzoom: self.fillzoom,
609 ..Default::default()
610 }
611 }
612}
613
614#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
616#[serde(untagged)]
617pub enum UnknownMetadata {
618 Metadata(Box<Metadata>),
620 Mapbox(Box<MapboxTileJSONMetadata>),
622}
623impl UnknownMetadata {
624 pub fn to_metadata(&self) -> Metadata {
626 match self {
627 UnknownMetadata::Metadata(m) => *m.clone(),
628 UnknownMetadata::Mapbox(m) => m.to_metadata(),
629 }
630 }
631}
632
633#[derive(Debug, Clone)]
635pub struct MetadataBuilder {
636 lon_lat_bounds: LonLatBounds,
637 faces: BTreeSet<Face>,
638 metadata: Metadata,
639}
640impl Default for MetadataBuilder {
641 fn default() -> Self {
642 MetadataBuilder {
643 lon_lat_bounds: BBox {
644 left: f64::INFINITY,
645 bottom: f64::INFINITY,
646 right: -f64::INFINITY,
647 top: -f64::INFINITY,
648 },
649 faces: BTreeSet::new(),
650 metadata: Metadata { minzoom: 30, maxzoom: 0, ..Metadata::default() },
651 }
652 }
653}
654impl MetadataBuilder {
655 pub fn commit(&mut self) -> Metadata {
657 self.update_center();
659 self.metadata.bounds = self.lon_lat_bounds;
661 for face in &self.faces {
663 self.metadata.faces.push(*face);
664 }
665 self.metadata.to_owned()
667 }
668
669 pub fn set_name(&mut self, name: String) {
671 self.metadata.name = name;
672 }
673
674 pub fn set_scheme(&mut self, scheme: Scheme) {
676 self.metadata.scheme = scheme;
677 }
678
679 pub fn set_extension(&mut self, extension: String) {
681 self.metadata.extension = extension;
682 }
683
684 pub fn set_type(&mut self, r#type: SourceType) {
686 self.metadata.r#type = r#type;
687 }
688
689 pub fn set_version(&mut self, version: String) {
691 self.metadata.version = version;
692 }
693
694 pub fn set_description(&mut self, description: String) {
696 self.metadata.description = description;
697 }
698
699 pub fn set_encoding(&mut self, encoding: Encoding) {
701 self.metadata.encoding = encoding;
702 }
703
704 pub fn add_attribution(&mut self, display_name: &str, href: &str) {
706 self.metadata.attributions.insert(display_name.into(), href.into());
707 }
708
709 pub fn add_layer(&mut self, name: &str, layer: &LayerMetaData) {
711 if self.metadata.layers.entry(name.into()).or_insert(layer.clone()).eq(&layer) {
713 self.metadata.vector_layers.push(VectorLayer {
715 id: name.into(), description: layer.description.clone(),
717 minzoom: Some(layer.minzoom),
718 maxzoom: Some(layer.maxzoom),
719 fields: BTreeMap::new(),
720 });
721 }
722 if layer.minzoom < self.metadata.minzoom {
724 self.metadata.minzoom = layer.minzoom;
725 }
726 if layer.maxzoom > self.metadata.maxzoom {
727 self.metadata.maxzoom = layer.maxzoom;
728 }
729 }
730
731 pub fn add_tile_wm(&mut self, zoom: u8, x: u32, y: u32, ll_bounds: &LonLatBounds) {
733 self.metadata.tilestats.total += 1;
734 self.faces.insert(Face::Face0);
735 self.add_bounds_wm(zoom, x, y);
736 self.update_lon_lat_bounds(ll_bounds);
737 }
738
739 pub fn add_tile_s2(&mut self, face: Face, zoom: u8, x: u32, y: u32, ll_bounds: &LonLatBounds) {
741 self.metadata.tilestats.increment(face);
742 self.faces.insert(face);
743 self.add_bounds_s2(face, zoom, x, y);
744 self.update_lon_lat_bounds(ll_bounds);
745 }
746
747 fn update_center(&mut self) {
749 let Metadata { minzoom, maxzoom, .. } = self.metadata;
750 let BBox { left, bottom, right, top } = self.lon_lat_bounds;
751 self.metadata.centerpoint.lon = (left + right) / 2.0;
752 self.metadata.centerpoint.lat = (bottom + top) / 2.0;
753 self.metadata.centerpoint.zoom = (minzoom + maxzoom) >> 1;
754 }
755
756 fn add_bounds_wm(&mut self, zoom: u8, x: u32, y: u32) {
758 let x = x as u64;
759 let y = y as u64;
760 let bbox = self.metadata.wmbounds.entry(zoom).or_insert(BBox {
761 left: u64::MAX,
762 bottom: u64::MAX,
763 right: 0,
764 top: 0,
765 });
766
767 bbox.left = bbox.left.min(x);
768 bbox.bottom = bbox.bottom.min(y);
769 bbox.right = bbox.right.max(x);
770 bbox.top = bbox.top.max(y);
771 }
772
773 fn add_bounds_s2(&mut self, face: Face, zoom: u8, x: u32, y: u32) {
775 let x = x as u64;
776 let y = y as u64;
777 let bbox = self.metadata.s2bounds.get_mut(face).entry(zoom).or_insert(BBox {
778 left: u64::MAX,
779 bottom: u64::MAX,
780 right: 0,
781 top: 0,
782 });
783
784 bbox.left = bbox.left.min(x);
785 bbox.bottom = bbox.bottom.min(y);
786 bbox.right = bbox.right.max(x);
787 bbox.top = bbox.top.max(y);
788 }
789
790 fn update_lon_lat_bounds(&mut self, ll_bounds: &LonLatBounds) {
792 self.lon_lat_bounds.left = ll_bounds.left.min(self.lon_lat_bounds.left);
793 self.lon_lat_bounds.bottom = ll_bounds.bottom.min(self.lon_lat_bounds.bottom);
794 self.lon_lat_bounds.right = ll_bounds.right.max(self.lon_lat_bounds.right);
795 self.lon_lat_bounds.top = ll_bounds.top.max(self.lon_lat_bounds.top);
796 }
797}
798
799fn extract_link_info(html_string: &str) -> Option<Attributions> {
801 let href_start = html_string.find("href='")?;
803 let href_value_start = href_start + "href='".len();
804 let href_end = html_string[href_value_start..].find("'")?;
805 let href_value = &html_string[href_value_start..href_value_start + href_end];
806
807 let text_start = html_string.find(">")?;
809 let text_value_start = text_start + 1;
810 let text_end = html_string.find("</a>")?;
811 let text_value = &html_string[text_value_start..text_end];
812
813 let mut map = BTreeMap::new();
814 map.insert(text_value.into(), href_value.into());
815
816 Some(map)
817}
818
819#[cfg(test)]
820mod tests {
821 use super::*;
822 use alloc::vec;
823 use s2json::{PrimitiveShape, ShapeType};
824
825 #[test]
826 fn it_works() {
827 let mut meta_builder = MetadataBuilder::default();
828
829 meta_builder.set_name("OSM".into());
831 meta_builder.set_description("A free editable map of the whole world.".into());
832 meta_builder.set_version("1.0.0".into());
833 meta_builder.set_scheme("fzxy".into()); meta_builder.set_type("vector".into()); meta_builder.set_encoding("none".into()); meta_builder.set_extension("pbf".into());
837 meta_builder.add_attribution("OpenStreetMap", "https://www.openstreetmap.org/copyright/");
838
839 let shape_str = r#"
841 {
842 "class": "string",
843 "offset": "f64",
844 "info": {
845 "name": "string",
846 "value": "i64"
847 }
848 }
849 "#;
850 let shape: Shape =
851 serde_json::from_str(shape_str).unwrap_or_else(|e| panic!("ERROR: {}", e));
852 let layer = LayerMetaData {
853 minzoom: 0,
854 maxzoom: 13,
855 description: Some("water_lines".into()),
856 draw_types: Vec::from(&[DrawType::Lines]),
857 shape: shape.clone(),
858 m_shape: None,
859 };
860 meta_builder.add_layer("water_lines", &layer);
861
862 meta_builder.add_tile_wm(
865 0,
866 0,
867 0,
868 &LonLatBounds { left: -60.0, bottom: -20.0, right: 5.0, top: 60.0 },
869 );
870 meta_builder.add_tile_s2(
872 Face::Face1,
873 5,
874 22,
875 37,
876 &LonLatBounds { left: -120.0, bottom: -7.0, right: 44.0, top: 72.0 },
877 );
878
879 let resulting_metadata: Metadata = meta_builder.commit();
881
882 assert_eq!(
883 resulting_metadata,
884 Metadata {
885 name: "OSM".into(),
886 description: "A free editable map of the whole world.".into(),
887 version: "1.0.0".into(),
888 scheme: "fzxy".into(),
889 r#type: "vector".into(),
890 encoding: "none".into(),
891 extension: "pbf".into(),
892 attributions: BTreeMap::from([(
893 "OpenStreetMap".into(),
894 "https://www.openstreetmap.org/copyright/".into()
895 ),]),
896 wmbounds: BTreeMap::from([(
897 0,
898 TileBounds { left: 0, bottom: 0, right: 0, top: 0 }
899 ),]),
900 faces: Vec::from(&[Face::Face0, Face::Face1]),
901 bounds: BBox { left: -120.0, bottom: -20.0, right: 44.0, top: 72.0 },
902 s2bounds: FaceBounds {
903 face0: BTreeMap::new(),
904 face1: BTreeMap::from([(
905 5,
906 TileBounds { left: 22, bottom: 37, right: 22, top: 37 }
907 ),]),
908 face2: BTreeMap::new(),
909 face3: BTreeMap::new(),
910 face4: BTreeMap::new(),
911 face5: BTreeMap::new(),
912 },
913 minzoom: 0,
914 maxzoom: 13,
915 centerpoint: Center { lon: -38.0, lat: 26.0, zoom: 6 },
917 tilestats: TileStatsMetadata {
918 total: 2,
919 total_0: 0,
920 total_1: 1,
921 total_2: 0,
922 total_3: 0,
923 total_4: 0,
924 total_5: 0,
925 },
926 layers: BTreeMap::from([(
927 "water_lines".into(),
928 LayerMetaData {
929 description: Some("water_lines".into()),
930 minzoom: 0,
931 maxzoom: 13,
932 draw_types: Vec::from(&[DrawType::Lines]),
933 shape: Shape::from([
934 ("class".into(), ShapeType::Primitive(PrimitiveShape::String)),
935 ("offset".into(), ShapeType::Primitive(PrimitiveShape::F64)),
936 (
937 "info".into(),
938 ShapeType::Nested(Shape::from([
939 ("name".into(), ShapeType::Primitive(PrimitiveShape::String)),
940 ("value".into(), ShapeType::Primitive(PrimitiveShape::I64)),
941 ]))
942 ),
943 ]),
944 m_shape: None,
945 }
946 )]),
947 s2tilejson: "1.0.0".into(),
948 vector_layers: Vec::from([VectorLayer {
949 id: "water_lines".into(),
950 description: Some("water_lines".into()),
951 minzoom: Some(0),
952 maxzoom: Some(13),
953 fields: BTreeMap::new()
954 }]),
955 ..Default::default()
956 }
957 );
958
959 let meta_str = serde_json::to_string(&resulting_metadata).unwrap();
960
961 assert_eq!(
962 meta_str,
963 "{\"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\":[-120.0,-20.0,44.0,72.0],\"wmbounds\":{\"0\":[0,0,0,0]},\"s2bounds\":{\"0\":{},\"1\":{\"5\":[22,37,22,37]},\"2\":{},\"3\":{},\"4\":{},\"5\":{}},\"minzoom\":0,\"maxzoom\":13,\"centerpoint\":{\"lon\":-38.0,\"lat\":26.0,\"zoom\":6},\"attributions\":{\"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\":{}}],\"tilejson\":null}"
964 );
965
966 let meta_reparsed: Metadata =
967 serde_json::from_str(&meta_str).unwrap_or_else(|e| panic!("ERROR: {}", e));
968 assert_eq!(meta_reparsed, resulting_metadata);
969 }
970
971 #[test]
972 fn test_face() {
973 assert_eq!(Face::Face0, Face::from(0));
974 assert_eq!(Face::Face1, Face::from(1));
975 assert_eq!(Face::Face2, Face::from(2));
976 assert_eq!(Face::Face3, Face::from(3));
977 assert_eq!(Face::Face4, Face::from(4));
978 assert_eq!(Face::Face5, Face::from(5));
979
980 assert_eq!(0, u8::from(Face::Face0));
981 assert_eq!(1, u8::from(Face::Face1));
982 assert_eq!(2, u8::from(Face::Face2));
983 assert_eq!(3, u8::from(Face::Face3));
984 assert_eq!(4, u8::from(Face::Face4));
985 assert_eq!(5, u8::from(Face::Face5));
986 }
987
988 #[test]
989 fn test_bbox() {
990 let bbox: BBox = BBox { left: 0.0, bottom: 0.0, right: 0.0, top: 0.0 };
991 let json = serde_json::to_string(&bbox).unwrap();
993 assert_eq!(json, r#"[0.0,0.0,0.0,0.0]"#);
994 let bbox2: BBox = serde_json::from_str(&json).unwrap();
995 assert_eq!(bbox, bbox2);
996 }
997
998 #[test]
1000 fn test_tilestats() {
1001 let mut tilestats = TileStatsMetadata {
1002 total: 2,
1003 total_0: 0,
1004 total_1: 1,
1005 total_2: 0,
1006 total_3: 0,
1007 total_4: 0,
1008 total_5: 0,
1009 };
1010 let json = serde_json::to_string(&tilestats).unwrap();
1012 assert_eq!(json, r#"{"total":2,"0":0,"1":1,"2":0,"3":0,"4":0,"5":0}"#);
1013 let tilestats2: TileStatsMetadata = serde_json::from_str(&json).unwrap();
1014 assert_eq!(tilestats, tilestats2);
1015
1016 assert_eq!(tilestats.get(0.into()), 0);
1018 tilestats.increment(0.into());
1020 assert_eq!(tilestats.get(0.into()), 1);
1021
1022 assert_eq!(tilestats.get(1.into()), 1);
1024 tilestats.increment(1.into());
1026 assert_eq!(tilestats.get(1.into()), 2);
1027
1028 assert_eq!(tilestats.get(2.into()), 0);
1030 tilestats.increment(2.into());
1032 assert_eq!(tilestats.get(2.into()), 1);
1033
1034 assert_eq!(tilestats.get(3.into()), 0);
1036 tilestats.increment(3.into());
1038 assert_eq!(tilestats.get(3.into()), 1);
1039
1040 assert_eq!(tilestats.get(4.into()), 0);
1042 tilestats.increment(4.into());
1044 assert_eq!(tilestats.get(4.into()), 1);
1045
1046 assert_eq!(tilestats.get(5.into()), 0);
1048 tilestats.increment(5.into());
1050 assert_eq!(tilestats.get(5.into()), 1);
1051 }
1052
1053 #[test]
1055 fn test_facebounds() {
1056 let mut facebounds = FaceBounds::default();
1057 let face0 = facebounds.get_mut(0.into());
1059 face0.insert(0, TileBounds { left: 0, bottom: 0, right: 0, top: 0 });
1060 let face1 = facebounds.get_mut(1.into());
1062 face1.insert(0, TileBounds { left: 0, bottom: 0, right: 1, top: 1 });
1063 let face2 = facebounds.get_mut(2.into());
1065 face2.insert(0, TileBounds { left: 0, bottom: 0, right: 2, top: 2 });
1066 let face3 = facebounds.get_mut(3.into());
1068 face3.insert(0, TileBounds { left: 0, bottom: 0, right: 3, top: 3 });
1069 let face4 = facebounds.get_mut(4.into());
1071 face4.insert(0, TileBounds { left: 0, bottom: 0, right: 4, top: 4 });
1072 let face5 = facebounds.get_mut(5.into());
1074 face5.insert(0, TileBounds { left: 0, bottom: 0, right: 5, top: 5 });
1075
1076 assert_eq!(
1079 facebounds.get(0.into()).get(&0).unwrap(),
1080 &TileBounds { left: 0, bottom: 0, right: 0, top: 0 }
1081 );
1082 assert_eq!(
1084 facebounds.get(1.into()).get(&0).unwrap(),
1085 &TileBounds { left: 0, bottom: 0, right: 1, top: 1 }
1086 );
1087 assert_eq!(
1089 facebounds.get(2.into()).get(&0).unwrap(),
1090 &TileBounds { left: 0, bottom: 0, right: 2, top: 2 }
1091 );
1092 assert_eq!(
1094 facebounds.get(3.into()).get(&0).unwrap(),
1095 &TileBounds { left: 0, bottom: 0, right: 3, top: 3 }
1096 );
1097 assert_eq!(
1099 facebounds.get(4.into()).get(&0).unwrap(),
1100 &TileBounds { left: 0, bottom: 0, right: 4, top: 4 }
1101 );
1102 assert_eq!(
1104 facebounds.get(5.into()).get(&0).unwrap(),
1105 &TileBounds { left: 0, bottom: 0, right: 5, top: 5 }
1106 );
1107
1108 let json = serde_json::to_string(&facebounds).unwrap();
1110 assert_eq!(
1111 json,
1112 "{\"0\":{\"0\":[0,0,0,0]},\"1\":{\"0\":[0,0,1,1]},\"2\":{\"0\":[0,0,2,2]},\"3\":{\"0\"\
1113 :[0,0,3,3]},\"4\":{\"0\":[0,0,4,4]},\"5\":{\"0\":[0,0,5,5]}}"
1114 );
1115 let facebounds2 = serde_json::from_str(&json).unwrap();
1116 assert_eq!(facebounds, facebounds2);
1117 }
1118
1119 #[test]
1121 fn test_drawtype() {
1122 assert_eq!(DrawType::from(1), DrawType::Points);
1123 assert_eq!(DrawType::from(2), DrawType::Lines);
1124 assert_eq!(DrawType::from(3), DrawType::Polygons);
1125 assert_eq!(DrawType::from(4), DrawType::Points3D);
1126 assert_eq!(DrawType::from(5), DrawType::Lines3D);
1127 assert_eq!(DrawType::from(6), DrawType::Polygons3D);
1128 assert_eq!(DrawType::from(7), DrawType::Raster);
1129 assert_eq!(DrawType::from(8), DrawType::Grid);
1130
1131 assert_eq!(1, u8::from(DrawType::Points));
1132 assert_eq!(2, u8::from(DrawType::Lines));
1133 assert_eq!(3, u8::from(DrawType::Polygons));
1134 assert_eq!(4, u8::from(DrawType::Points3D));
1135 assert_eq!(5, u8::from(DrawType::Lines3D));
1136 assert_eq!(6, u8::from(DrawType::Polygons3D));
1137 assert_eq!(7, u8::from(DrawType::Raster));
1138 assert_eq!(8, u8::from(DrawType::Grid));
1139
1140 let json = serde_json::to_string(&DrawType::Points).unwrap();
1142 assert_eq!(json, "1");
1143 let drawtype: DrawType = serde_json::from_str(&json).unwrap();
1144 assert_eq!(drawtype, DrawType::Points);
1145
1146 let drawtype: DrawType = serde_json::from_str("2").unwrap();
1147 assert_eq!(drawtype, DrawType::Lines);
1148
1149 let drawtype: DrawType = serde_json::from_str("3").unwrap();
1150 assert_eq!(drawtype, DrawType::Polygons);
1151
1152 let drawtype: DrawType = serde_json::from_str("4").unwrap();
1153 assert_eq!(drawtype, DrawType::Points3D);
1154
1155 let drawtype: DrawType = serde_json::from_str("5").unwrap();
1156 assert_eq!(drawtype, DrawType::Lines3D);
1157
1158 let drawtype: DrawType = serde_json::from_str("6").unwrap();
1159 assert_eq!(drawtype, DrawType::Polygons3D);
1160
1161 let drawtype: DrawType = serde_json::from_str("7").unwrap();
1162 assert_eq!(drawtype, DrawType::Raster);
1163
1164 let drawtype: DrawType = serde_json::from_str("8").unwrap();
1165 assert_eq!(drawtype, DrawType::Grid);
1166
1167 assert!(serde_json::from_str::<DrawType>("9").is_err());
1168 }
1169
1170 #[test]
1172 fn test_sourcetype() {
1173 assert_eq!(SourceType::from("vector"), SourceType::Vector);
1175 assert_eq!(SourceType::from("json"), SourceType::Json);
1176 assert_eq!(SourceType::from("raster"), SourceType::Raster);
1177 assert_eq!(SourceType::from("raster-dem"), SourceType::RasterDem);
1178 assert_eq!(SourceType::from("grid"), SourceType::Grid);
1179 assert_eq!(SourceType::from("markers"), SourceType::Markers);
1180 assert_eq!(SourceType::from("overlay"), SourceType::Unknown);
1181
1182 let json = serde_json::to_string(&SourceType::Vector).unwrap();
1184 assert_eq!(json, "\"vector\"");
1185 let sourcetype: SourceType = serde_json::from_str(&json).unwrap();
1186 assert_eq!(sourcetype, SourceType::Vector);
1187
1188 let json = serde_json::to_string(&SourceType::Json).unwrap();
1190 assert_eq!(json, "\"json\"");
1191 let sourcetype: SourceType = serde_json::from_str(&json).unwrap();
1192 assert_eq!(sourcetype, SourceType::Json);
1193
1194 let json = serde_json::to_string(&SourceType::Raster).unwrap();
1196 assert_eq!(json, "\"raster\"");
1197 let sourcetype: SourceType = serde_json::from_str(&json).unwrap();
1198 assert_eq!(sourcetype, SourceType::Raster);
1199
1200 let json = serde_json::to_string(&SourceType::RasterDem).unwrap();
1202 assert_eq!(json, "\"raster-dem\"");
1203 let sourcetype: SourceType = serde_json::from_str(&json).unwrap();
1204 assert_eq!(sourcetype, SourceType::RasterDem);
1205
1206 let json = serde_json::to_string(&SourceType::Grid).unwrap();
1208 assert_eq!(json, "\"grid\"");
1209 let sourcetype: SourceType = serde_json::from_str(&json).unwrap();
1210 assert_eq!(sourcetype, SourceType::Grid);
1211
1212 let json = serde_json::to_string(&SourceType::Markers).unwrap();
1214 assert_eq!(json, "\"markers\"");
1215 let sourcetype: SourceType = serde_json::from_str(&json).unwrap();
1216 assert_eq!(sourcetype, SourceType::Markers);
1217
1218 let json = serde_json::to_string(&SourceType::Unknown).unwrap();
1220 assert_eq!(json, "\"unknown\"");
1221 let sourcetype: SourceType = serde_json::from_str(r#""overlay""#).unwrap();
1222 assert_eq!(sourcetype, SourceType::Unknown);
1223 }
1224
1225 #[test]
1227 fn test_encoding() {
1228 assert_eq!(Encoding::from("none"), Encoding::None);
1230 assert_eq!(Encoding::from("gzip"), Encoding::Gzip);
1231 assert_eq!(Encoding::from("br"), Encoding::Brotli);
1232 assert_eq!(Encoding::from("zstd"), Encoding::Zstd);
1233
1234 assert_eq!(core::convert::Into::<&str>::into(Encoding::None), "none");
1236 assert_eq!(core::convert::Into::<&str>::into(Encoding::Gzip), "gzip");
1237 assert_eq!(core::convert::Into::<&str>::into(Encoding::Brotli), "br");
1238 assert_eq!(core::convert::Into::<&str>::into(Encoding::Zstd), "zstd");
1239
1240 assert_eq!(Encoding::from(0), Encoding::None);
1242 assert_eq!(Encoding::from(1), Encoding::Gzip);
1243 assert_eq!(Encoding::from(2), Encoding::Brotli);
1244 assert_eq!(Encoding::from(3), Encoding::Zstd);
1245
1246 assert_eq!(u8::from(Encoding::None), 0);
1248 assert_eq!(u8::from(Encoding::Gzip), 1);
1249 assert_eq!(u8::from(Encoding::Brotli), 2);
1250 assert_eq!(u8::from(Encoding::Zstd), 3);
1251
1252 let json = serde_json::to_string(&Encoding::Gzip).unwrap();
1254 assert_eq!(json, "\"gzip\"");
1255 let encoding: Encoding = serde_json::from_str(&json).unwrap();
1256 assert_eq!(encoding, Encoding::Gzip);
1257
1258 let json = serde_json::to_string(&Encoding::Brotli).unwrap();
1260 assert_eq!(json, "\"br\"");
1261 let encoding: Encoding = serde_json::from_str(&json).unwrap();
1262 assert_eq!(encoding, Encoding::Brotli);
1263
1264 let json = serde_json::to_string(&Encoding::None).unwrap();
1266 assert_eq!(json, "\"none\"");
1267 let encoding: Encoding = serde_json::from_str(&json).unwrap();
1268 assert_eq!(encoding, Encoding::None);
1269
1270 let json = serde_json::to_string(&Encoding::Zstd).unwrap();
1272 assert_eq!(json, "\"zstd\"");
1273 let encoding: Encoding = serde_json::from_str(&json).unwrap();
1274 assert_eq!(encoding, Encoding::Zstd);
1275 }
1276
1277 #[test]
1279 fn test_scheme() {
1280 assert_eq!(Scheme::from("fzxy"), Scheme::Fzxy);
1282 assert_eq!(Scheme::from("tfzxy"), Scheme::Tfzxy);
1283 assert_eq!(Scheme::from("xyz"), Scheme::Xyz);
1284 assert_eq!(Scheme::from("txyz"), Scheme::Txyz);
1285 assert_eq!(Scheme::from("tms"), Scheme::Tms);
1286
1287 assert_eq!(core::convert::Into::<&str>::into(Scheme::Fzxy), "fzxy");
1289 assert_eq!(core::convert::Into::<&str>::into(Scheme::Tfzxy), "tfzxy");
1290 assert_eq!(core::convert::Into::<&str>::into(Scheme::Xyz), "xyz");
1291 assert_eq!(core::convert::Into::<&str>::into(Scheme::Txyz), "txyz");
1292 assert_eq!(core::convert::Into::<&str>::into(Scheme::Tms), "tms");
1293 }
1294
1295 #[test]
1296 fn test_tippecanoe_metadata() {
1297 let meta_str = r#"{
1298 "name": "test_fixture_1.pmtiles",
1299 "description": "test_fixture_1.pmtiles",
1300 "version": "2",
1301 "type": "overlay",
1302 "generator": "tippecanoe v2.5.0",
1303 "generator_options": "./tippecanoe -zg -o test_fixture_1.pmtiles --force",
1304 "vector_layers": [
1305 {
1306 "id": "test_fixture_1pmtiles",
1307 "description": "",
1308 "minzoom": 0,
1309 "maxzoom": 0,
1310 "fields": {}
1311 }
1312 ],
1313 "tilestats": {
1314 "layerCount": 1,
1315 "layers": [
1316 {
1317 "layer": "test_fixture_1pmtiles",
1318 "count": 1,
1319 "geometry": "Polygon",
1320 "attributeCount": 0,
1321 "attributes": []
1322 }
1323 ]
1324 }
1325 }"#;
1326
1327 let _meta: Metadata =
1328 serde_json::from_str(meta_str).unwrap_or_else(|e| panic!("ERROR: {}", e));
1329 }
1330
1331 #[test]
1332 fn test_mapbox_metadata() {
1333 let meta_str = r#"{
1334 "tilejson": "3.0.0",
1335 "name": "OpenStreetMap",
1336 "description": "A free editable map of the whole world.",
1337 "version": "1.0.0",
1338 "attribution": "<a href='https://openstreetmap.org'>OSM contributors</a>",
1339 "scheme": "xyz",
1340 "tiles": [
1341 "https://a.tile.custom-osm-tiles.org/{z}/{x}/{y}.mvt",
1342 "https://b.tile.custom-osm-tiles.org/{z}/{x}/{y}.mvt",
1343 "https://c.tile.custom-osm-tiles.org/{z}/{x}/{y}.mvt"
1344 ],
1345 "minzoom": 0,
1346 "maxzoom": 18,
1347 "bounds": [-180, -85, 180, 85],
1348 "fillzoom": 6,
1349 "something_custom": "this is my unique field",
1350 "vector_layers": [
1351 {
1352 "id": "telephone",
1353 "fields": {
1354 "phone_number": "the phone number",
1355 "payment": "how to pay"
1356 }
1357 },
1358 {
1359 "id": "bicycle_parking",
1360 "fields": {
1361 "type": "the type of bike parking",
1362 "year_installed": "the year the bike parking was installed"
1363 }
1364 },
1365 {
1366 "id": "showers",
1367 "fields": {
1368 "water_temperature": "the maximum water temperature",
1369 "wear_sandles": "whether you should wear sandles or not",
1370 "wheelchair": "is the shower wheelchair friendly?"
1371 }
1372 }
1373 ]
1374 }"#;
1375
1376 let meta_mapbox: MapboxTileJSONMetadata =
1377 serde_json::from_str(meta_str).unwrap_or_else(|e| panic!("ERROR: {}", e));
1378 let meta_new = meta_mapbox.to_metadata();
1379 assert_eq!(
1380 meta_new,
1381 Metadata {
1382 name: "OpenStreetMap".into(),
1383 description: "A free editable map of the whole world.".into(),
1384 version: "1.0.0".into(),
1385 scheme: Scheme::Xyz,
1386 r#type: "vector".into(),
1387 encoding: Encoding::None, extension: "pbf".into(),
1389 attributions: BTreeMap::from([(
1390 "OSM contributors".into(),
1391 "https://openstreetmap.org".into()
1392 )]),
1393 bounds: BBox::new(-180., -85., 180., 85.),
1394 vector_layers: meta_mapbox.vector_layers.clone(),
1395 maxzoom: 18,
1396 minzoom: 0,
1397 centerpoint: Center { lat: 0.0, lon: 0.0, zoom: 0 },
1398 wmbounds: WMBounds::default(),
1399 faces: vec![Face::Face0],
1400 s2bounds: FaceBounds::default(),
1401 tilestats: TileStatsMetadata::default(),
1402 layers: LayersMetaData::default(),
1403 s2tilejson: "1.0.0".into(),
1404 attribution: Some(
1405 "<a href='https://openstreetmap.org'>OSM contributors</a>".into()
1406 ),
1407 tiles: Some(meta_mapbox.tiles.clone()),
1408 fillzoom: meta_mapbox.fillzoom,
1409 center: Some([0.0, 0.0, 0.0]),
1410 ..Default::default()
1411 },
1412 );
1413
1414 let meta_mapbox_from_unknown: UnknownMetadata =
1415 serde_json::from_str(meta_str).unwrap_or_else(|e| panic!("ERROR: {}", e));
1416 let meta_new = meta_mapbox_from_unknown.to_metadata();
1417 assert_eq!(
1418 meta_new,
1419 Metadata {
1420 name: "OpenStreetMap".into(),
1421 description: "A free editable map of the whole world.".into(),
1422 version: "1.0.0".into(),
1423 scheme: Scheme::Xyz,
1424 r#type: "vector".into(),
1425 encoding: Encoding::None, extension: "pbf".into(),
1427 attributions: BTreeMap::default(),
1428 bounds: BBox::new(-180., -85., 180., 85.),
1429 vector_layers: meta_mapbox.vector_layers.clone(),
1430 maxzoom: 18,
1431 minzoom: 0,
1432 centerpoint: Center { lat: 0.0, lon: 0.0, zoom: 0 },
1433 wmbounds: WMBounds::default(),
1434 faces: vec![],
1435 s2bounds: FaceBounds::default(),
1436 tilestats: TileStatsMetadata::default(),
1437 layers: LayersMetaData::default(),
1438 s2tilejson: "1.0.0".into(),
1439 attribution: Some(
1440 "<a href='https://openstreetmap.org'>OSM contributors</a>".into()
1441 ),
1442 tiles: Some(meta_mapbox.tiles.clone()),
1443 fillzoom: meta_mapbox.fillzoom,
1444 center: None,
1445 tilejson: Some("3.0.0".into()),
1446 ..Default::default()
1447 },
1448 );
1449 }
1450
1451 #[test]
1452 fn test_malformed_metadata() {
1453 let meta_str = r#"{
1454 "s2tilejson": "1.0.0",
1455 "bounds": [
1456 -180,
1457 -85,
1458 180,
1459 85
1460 ],
1461 "name": "Mapbox Satellite",
1462 "scheme": "xyz",
1463 "format": "zxy",
1464 "type": "raster",
1465 "extension": "webp",
1466 "encoding": "gzip",
1467 "minzoom": 0,
1468 "maxzoom": 3
1469 }
1470 "#;
1471
1472 let malformed_success: UnknownMetadata =
1473 serde_json::from_str(meta_str).unwrap_or_else(|e| panic!("ERROR: {}", e));
1474
1475 let meta: Metadata = malformed_success.to_metadata();
1476 assert_eq!(
1477 meta,
1478 Metadata {
1479 s2tilejson: "1.0.0".into(),
1480 version: "1.0.0".into(),
1481 name: "Mapbox Satellite".into(),
1482 scheme: Scheme::Xyz,
1483 description: "Built with s2maps-cli".into(),
1484 r#type: SourceType::Raster,
1485 extension: "webp".into(),
1486 encoding: Encoding::Gzip,
1487 faces: vec![],
1488 bounds: BBox::new(-180., -85., 180., 85.),
1489 wmbounds: BTreeMap::default(),
1490 s2bounds: FaceBounds {
1491 face0: BTreeMap::default(),
1492 face1: BTreeMap::default(),
1493 face2: BTreeMap::default(),
1494 face3: BTreeMap::default(),
1495 face4: BTreeMap::default(),
1496 face5: BTreeMap::default()
1497 },
1498 minzoom: 0,
1499 maxzoom: 3,
1500 centerpoint: Center { lon: 0.0, lat: 0.0, zoom: 0 },
1501 attributions: BTreeMap::default(),
1502 layers: BTreeMap::default(),
1503 tilestats: TileStatsMetadata {
1504 total: 0,
1505 total_0: 0,
1506 total_1: 0,
1507 total_2: 0,
1508 total_3: 0,
1509 total_4: 0,
1510 total_5: 0
1511 },
1512 vector_layers: vec![],
1513 ..Default::default()
1514 }
1515 );
1516 }
1517
1518 #[test]
1519 fn test_faces() {
1520 let meta = Metadata {
1521 faces: vec![Face::Face0, Face::Face1, Face::Face4, Face::Face5],
1522 ..Default::default()
1523 };
1524
1525 let to_string: String = serde_json::to_string(&meta).unwrap();
1526 let from_string: Metadata = serde_json::from_str(&to_string).unwrap();
1527 assert_eq!(meta, from_string);
1528
1529 let from_string_unknown: UnknownMetadata = serde_json::from_str(&to_string).unwrap();
1530 let from_string: Metadata = from_string_unknown.to_metadata();
1531 assert_eq!(meta, from_string);
1532 }
1533}