1#![no_std]
2#![forbid(unsafe_code)]
3#![deny(missing_docs)]
4#![cfg_attr(docsrs, feature(doc_cfg))]
5
6extern crate alloc;
79
80use alloc::{
81 borrow::ToOwned,
82 boxed::Box,
83 collections::{BTreeMap, BTreeSet},
84 format,
85 string::String,
86 vec::Vec,
87};
88pub use s2json::*;
89use serde::{Deserialize, Deserializer, Serialize, Serializer};
90
91pub type LonLatBounds = BBox<f64>;
93
94pub type TileBounds = BBox<u64>;
96
97#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
99pub enum DrawType {
100 Points = 1,
102 Lines = 2,
104 Polygons = 3,
106 Points3D = 4,
108 Lines3D = 5,
110 Polygons3D = 6,
112 Raster = 7,
114 Grid = 8,
116}
117impl From<DrawType> for u8 {
118 fn from(draw_type: DrawType) -> Self {
119 draw_type as u8
120 }
121}
122impl From<u8> for DrawType {
123 fn from(draw_type: u8) -> Self {
124 match draw_type {
125 2 => DrawType::Lines,
126 3 => DrawType::Polygons,
127 4 => DrawType::Points3D,
128 5 => DrawType::Lines3D,
129 6 => DrawType::Polygons3D,
130 7 => DrawType::Raster,
131 8 => DrawType::Grid,
132 _ => DrawType::Points, }
134 }
135}
136impl<D: MValueCompatible> From<&VectorGeometry<D>> for DrawType {
137 fn from(geometry: &VectorGeometry<D>) -> DrawType {
138 match geometry {
139 VectorGeometry::Point(p) => {
140 if p.is_3d {
141 DrawType::Points3D
142 } else {
143 DrawType::Points
144 }
145 }
146 VectorGeometry::MultiPoint(mp) => {
147 if mp.is_3d {
148 DrawType::Points3D
149 } else {
150 DrawType::Points
151 }
152 }
153 VectorGeometry::LineString(l) => {
154 if l.is_3d {
155 DrawType::Lines3D
156 } else {
157 DrawType::Lines
158 }
159 }
160 VectorGeometry::MultiLineString(ml) => {
161 if ml.is_3d {
162 DrawType::Lines3D
163 } else {
164 DrawType::Lines
165 }
166 }
167 VectorGeometry::Polygon(p) => {
168 if p.is_3d {
169 DrawType::Polygons3D
170 } else {
171 DrawType::Polygons
172 }
173 }
174 VectorGeometry::MultiPolygon(mp) => {
175 if mp.is_3d {
176 DrawType::Polygons3D
177 } else {
178 DrawType::Polygons
179 }
180 }
181 }
182 }
183}
184impl<M: Clone, P: MValueCompatible, D: MValueCompatible> From<&VectorFeature<M, P, D>>
185 for DrawType
186{
187 fn from(feature: &VectorFeature<M, P, D>) -> DrawType {
188 DrawType::from(&feature.geometry)
189 }
190}
191impl Serialize for DrawType {
192 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
193 where
194 S: Serializer,
195 {
196 serializer.serialize_u8(*self as u8)
198 }
199}
200
201impl<'de> Deserialize<'de> for DrawType {
202 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
203 where
204 D: Deserializer<'de>,
205 {
206 let value: u8 = Deserialize::deserialize(deserializer)?;
208 match value {
209 1 => Ok(DrawType::Points),
210 2 => Ok(DrawType::Lines),
211 3 => Ok(DrawType::Polygons),
212 4 => Ok(DrawType::Points3D),
213 5 => Ok(DrawType::Lines3D),
214 6 => Ok(DrawType::Polygons3D),
215 7 => Ok(DrawType::Raster),
216 8 => Ok(DrawType::Grid),
217 _ => Err(serde::de::Error::custom(format!("unknown DrawType variant: {value}"))),
218 }
219 }
220}
221
222#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
224pub struct LayerMetaData {
225 #[serde(skip_serializing_if = "Option::is_none")]
227 pub description: Option<String>,
228 pub minzoom: u8,
230 pub maxzoom: u8,
232 pub draw_types: Vec<DrawType>,
234 pub shape: Shape,
236 #[serde(skip_serializing_if = "Option::is_none", rename = "mShape")]
238 pub m_shape: Option<Shape>,
239}
240
241pub type LayersMetaData = BTreeMap<String, LayerMetaData>;
243
244#[derive(Serialize, Deserialize, Debug, Default, Clone, Copy, PartialEq)]
246pub struct TileStatsMetadata {
247 #[serde(default)]
249 pub total: u64,
250 #[serde(rename = "0", default)]
252 pub total_0: u64,
253 #[serde(rename = "1", default)]
255 pub total_1: u64,
256 #[serde(rename = "2", default)]
258 pub total_2: u64,
259 #[serde(rename = "3", default)]
261 pub total_3: u64,
262 #[serde(rename = "4", default)]
264 pub total_4: u64,
265 #[serde(rename = "5", default)]
267 pub total_5: u64,
268 #[serde(rename = "6", default)]
270 pub total_wm: u64,
271}
272impl TileStatsMetadata {
273 pub fn get(&self, face: Face) -> u64 {
275 match face {
276 Face::Face0 => self.total_0,
277 Face::Face1 => self.total_1,
278 Face::Face2 => self.total_2,
279 Face::Face3 => self.total_3,
280 Face::Face4 => self.total_4,
281 Face::Face5 => self.total_5,
282 Face::WM => self.total_wm,
283 }
284 }
285
286 pub fn increment(&mut self, face: Face) {
288 match face {
289 Face::Face0 => self.total_0 += 1,
290 Face::Face1 => self.total_1 += 1,
291 Face::Face2 => self.total_2 += 1,
292 Face::Face3 => self.total_3 += 1,
293 Face::Face4 => self.total_4 += 1,
294 Face::Face5 => self.total_5 += 1,
295 Face::WM => self.total_wm += 1,
296 }
297 self.total += 1;
298 }
299}
300
301#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
303pub struct FaceBounds {
304 #[serde(rename = "0")]
307 pub face0: BTreeMap<u8, TileBounds>,
308 #[serde(rename = "1")]
310 pub face1: BTreeMap<u8, TileBounds>,
311 #[serde(rename = "2")]
313 pub face2: BTreeMap<u8, TileBounds>,
314 #[serde(rename = "3")]
316 pub face3: BTreeMap<u8, TileBounds>,
317 #[serde(rename = "4")]
319 pub face4: BTreeMap<u8, TileBounds>,
320 #[serde(rename = "5")]
322 pub face5: BTreeMap<u8, TileBounds>,
323 #[serde(rename = "6")]
325 pub wm: BTreeMap<u8, TileBounds>,
326}
327impl FaceBounds {
328 pub fn get(&self, face: Face) -> &BTreeMap<u8, TileBounds> {
330 match face {
331 Face::Face0 => &self.face0,
332 Face::Face1 => &self.face1,
333 Face::Face2 => &self.face2,
334 Face::Face3 => &self.face3,
335 Face::Face4 => &self.face4,
336 Face::Face5 => &self.face5,
337 Face::WM => &self.wm,
338 }
339 }
340
341 pub fn get_mut(&mut self, face: Face) -> &mut BTreeMap<u8, TileBounds> {
343 match face {
344 Face::Face0 => &mut self.face0,
345 Face::Face1 => &mut self.face1,
346 Face::Face2 => &mut self.face2,
347 Face::Face3 => &mut self.face3,
348 Face::Face4 => &mut self.face4,
349 Face::Face5 => &mut self.face5,
350 Face::WM => &mut self.wm,
351 }
352 }
353}
354
355pub type WMBounds = BTreeMap<u8, TileBounds>;
358
359#[derive(Serialize, Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
361#[serde(rename_all = "lowercase")]
362pub enum SourceType {
363 #[default]
365 Vector,
366 Json,
368 Raster,
370 #[serde(rename = "raster-dem")]
372 RasterDem,
373 Grid,
375 Markers,
377 Unknown,
379}
380impl From<&str> for SourceType {
381 fn from(source_type: &str) -> Self {
382 match source_type {
383 "vector" => SourceType::Vector,
384 "json" => SourceType::Json,
385 "raster" => SourceType::Raster,
386 "raster-dem" => SourceType::RasterDem,
387 "grid" => SourceType::Grid,
388 "markers" => SourceType::Markers,
389 _ => SourceType::Unknown,
390 }
391 }
392}
393impl<'de> Deserialize<'de> for SourceType {
394 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
395 where
396 D: Deserializer<'de>,
397 {
398 let s: String = Deserialize::deserialize(deserializer)?;
400 Ok(SourceType::from(s.as_str()))
401 }
402}
403
404#[derive(Serialize, Deserialize, Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
406#[serde(rename_all = "lowercase")]
407pub enum Encoding {
408 #[default]
410 None = 0,
411 Gzip = 1,
413 #[serde(rename = "br")]
415 Brotli = 2,
416 Zstd = 3,
418}
419impl From<u8> for Encoding {
420 fn from(encoding: u8) -> Self {
421 match encoding {
422 1 => Encoding::Gzip,
423 2 => Encoding::Brotli,
424 3 => Encoding::Zstd,
425 _ => Encoding::None,
426 }
427 }
428}
429impl From<Encoding> for u8 {
430 fn from(encoding: Encoding) -> Self {
431 match encoding {
432 Encoding::Gzip => 1,
433 Encoding::Brotli => 2,
434 Encoding::Zstd => 3,
435 Encoding::None => 0,
436 }
437 }
438}
439impl From<Encoding> for &str {
440 fn from(encoding: Encoding) -> Self {
441 match encoding {
442 Encoding::Gzip => "gzip",
443 Encoding::Brotli => "br",
444 Encoding::Zstd => "zstd",
445 Encoding::None => "none",
446 }
447 }
448}
449impl From<&str> for Encoding {
450 fn from(encoding: &str) -> Self {
451 match encoding {
452 "gzip" => Encoding::Gzip,
453 "br" => Encoding::Brotli,
454 "zstd" => Encoding::Zstd,
455 _ => Encoding::None,
456 }
457 }
458}
459
460#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
462pub struct VectorLayer {
463 pub id: String,
465 #[serde(skip_serializing_if = "Option::is_none")]
467 pub description: Option<String>,
468 #[serde(skip_serializing_if = "Option::is_none")]
470 pub minzoom: Option<u8>,
471 #[serde(skip_serializing_if = "Option::is_none")]
473 pub maxzoom: Option<u8>,
474 pub fields: BTreeMap<String, String>,
476}
477
478#[derive(Serialize, Deserialize, Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
483#[serde(rename_all = "lowercase")]
484pub enum Scheme {
485 #[default]
487 Fzxy,
488 Tfzxy,
490 Xyz,
492 Txyz,
494 Tms,
496}
497impl From<&str> for Scheme {
498 fn from(scheme: &str) -> Self {
499 match scheme {
500 "fzxy" => Scheme::Fzxy,
501 "tfzxy" => Scheme::Tfzxy,
502 "xyz" => Scheme::Xyz,
503 "txyz" => Scheme::Txyz,
504 _ => Scheme::Tms,
505 }
506 }
507}
508impl From<Scheme> for &str {
509 fn from(scheme: Scheme) -> Self {
510 match scheme {
511 Scheme::Fzxy => "fzxy",
512 Scheme::Tfzxy => "tfzxy",
513 Scheme::Xyz => "xyz",
514 Scheme::Txyz => "txyz",
515 Scheme::Tms => "tms",
516 }
517 }
518}
519
520#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
522pub struct Center {
523 pub lon: f64,
525 pub lat: f64,
527 pub zoom: u8,
529}
530
531#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
545#[serde(default)]
546pub struct Metadata {
547 pub s2tilejson: String,
549 pub version: String,
551 pub name: String,
553 pub scheme: Scheme,
555 pub description: String,
557 #[serde(rename = "type")]
559 pub r#type: SourceType,
560 pub extension: String,
562 pub encoding: Encoding,
564 pub faces: Vec<Face>,
566 pub bounds: LonLatBounds,
568 pub wmbounds: WMBounds,
570 pub s2bounds: FaceBounds,
572 pub minzoom: u8,
574 pub maxzoom: u8,
576 pub centerpoint: Center,
578 pub attributions: Attributions,
580 pub layers: LayersMetaData,
582 pub tilestats: TileStatsMetadata,
584 pub vector_layers: Vec<VectorLayer>,
586 #[serde(skip_serializing_if = "Option::is_none")]
588 pub interval: Option<i64>,
589
590 pub tilejson: Option<String>,
593 #[serde(skip_serializing_if = "Option::is_none")]
595 pub tiles: Option<Vec<String>>,
596 #[serde(skip_serializing_if = "Option::is_none")]
598 pub attribution: Option<String>,
599 #[serde(skip_serializing_if = "Option::is_none")]
601 pub fillzoom: Option<u8>,
602 #[serde(skip_serializing_if = "Option::is_none")]
604 pub center: Option<[f64; 3]>,
605 #[serde(skip_serializing_if = "Option::is_none")]
607 pub data: Option<Vec<String>>,
608 #[serde(skip_serializing_if = "Option::is_none")]
610 pub grids: Option<Vec<String>>,
611 #[serde(skip_serializing_if = "Option::is_none")]
613 pub legend: Option<String>,
614 #[serde(skip_serializing_if = "Option::is_none")]
616 pub template: Option<String>,
617}
618impl Default for Metadata {
619 fn default() -> Self {
620 Self {
621 s2tilejson: "1.0.0".into(),
622 version: "1.0.0".into(),
623 name: "default".into(),
624 scheme: Scheme::default(),
625 description: "Built with s2maps-cli".into(),
626 r#type: SourceType::default(),
627 extension: "pbf".into(),
628 encoding: Encoding::default(),
629 faces: Vec::default(),
630 bounds: BBox::new(-180.0, -90.0, 180.0, 90.0),
631 wmbounds: WMBounds::default(),
632 s2bounds: FaceBounds::default(),
633 minzoom: 0,
634 maxzoom: 27,
635 centerpoint: Center::default(),
636 attributions: Attributions::new(),
637 layers: LayersMetaData::default(),
638 tilestats: TileStatsMetadata::default(),
639 vector_layers: Vec::new(),
640 interval: None,
641 attribution: None,
642 fillzoom: None,
643 center: None,
644 data: None,
645 grids: None,
646 legend: None,
647 template: None,
648 tilejson: None,
649 tiles: None,
650 }
651 }
652}
653
654#[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq)]
669#[serde(default)]
670pub struct MapboxTileJSONMetadata {
671 pub tilejson: String,
673 pub tiles: Vec<String>,
675 pub vector_layers: Vec<VectorLayer>,
677 #[serde(skip_serializing_if = "Option::is_none")]
679 pub attribution: Option<String>,
680 #[serde(skip_serializing_if = "Option::is_none")]
682 pub bounds: Option<LonLatBounds>,
683 #[serde(skip_serializing_if = "Option::is_none")]
685 pub center: Option<[f64; 3]>,
686 #[serde(skip_serializing_if = "Option::is_none")]
688 pub data: Option<Vec<String>>,
689 #[serde(skip_serializing_if = "Option::is_none")]
691 pub description: Option<String>,
692 #[serde(skip_serializing_if = "Option::is_none")]
694 pub fillzoom: Option<u8>,
695 #[serde(skip_serializing_if = "Option::is_none")]
697 pub grids: Option<Vec<String>>,
698 #[serde(skip_serializing_if = "Option::is_none")]
700 pub legend: Option<String>,
701 #[serde(skip_serializing_if = "Option::is_none")]
703 pub maxzoom: Option<u8>,
704 #[serde(skip_serializing_if = "Option::is_none")]
706 pub minzoom: Option<u8>,
707 #[serde(skip_serializing_if = "Option::is_none")]
709 pub name: Option<String>,
710 #[serde(skip_serializing_if = "Option::is_none")]
712 pub scheme: Option<Scheme>,
713 #[serde(skip_serializing_if = "Option::is_none")]
715 pub template: Option<String>,
716 #[serde(skip_serializing_if = "Option::is_none")]
718 pub version: Option<String>,
719 #[serde(skip_serializing_if = "Option::is_none")]
722 pub r#type: Option<SourceType>,
723 #[serde(skip_serializing_if = "Option::is_none")]
725 pub extension: Option<String>,
726 #[serde(skip_serializing_if = "Option::is_none")]
728 pub encoding: Option<Encoding>,
729}
730impl MapboxTileJSONMetadata {
731 pub fn to_metadata(&self) -> Metadata {
733 let [lon, lat, zoom] = self.center.unwrap_or([0.0, 0.0, 0.0]);
734 Metadata {
735 s2tilejson: "1.0.0".into(),
736 version: self.version.clone().unwrap_or("1.0.0".into()),
737 name: self.name.clone().unwrap_or("default".into()),
738 scheme: self.scheme.unwrap_or_default(),
739 description: self.description.clone().unwrap_or("Built with s2maps-cli".into()),
740 r#type: self.r#type.unwrap_or_default(),
741 extension: self.extension.clone().unwrap_or("pbf".into()),
742 faces: Vec::from([Face::Face0]),
743 bounds: self.bounds.unwrap_or_default(),
744 minzoom: self.minzoom.unwrap_or(0),
745 maxzoom: self.maxzoom.unwrap_or(27),
746 centerpoint: Center { lon, lat, zoom: zoom as u8 },
747 center: Some([lon, lat, zoom]),
748 attributions: extract_link_info(self.attribution.as_ref().unwrap_or(&"".into()))
749 .unwrap_or_default(),
750 vector_layers: self.vector_layers.clone(),
751 encoding: self.encoding.unwrap_or(Encoding::None),
752 attribution: self.attribution.clone(),
753 tiles: Some(self.tiles.clone()),
754 data: self.data.clone(),
755 grids: self.grids.clone(),
756 legend: self.legend.clone(),
757 template: self.template.clone(),
758 fillzoom: self.fillzoom,
759 ..Default::default()
760 }
761 }
762}
763
764#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
766#[serde(untagged)]
767pub enum UnknownMetadata {
768 Metadata(Box<Metadata>),
770 Mapbox(Box<MapboxTileJSONMetadata>),
772}
773impl UnknownMetadata {
774 pub fn to_metadata(&self) -> Metadata {
776 match self {
777 UnknownMetadata::Metadata(m) => *m.clone(),
778 UnknownMetadata::Mapbox(m) => m.to_metadata(),
779 }
780 }
781}
782
783#[derive(Debug, Clone)]
807pub struct MetadataBuilder {
808 lon_lat_bounds: LonLatBounds,
809 faces: BTreeSet<Face>,
810 metadata: Metadata,
811}
812impl Default for MetadataBuilder {
813 fn default() -> Self {
814 MetadataBuilder {
815 lon_lat_bounds: BBox {
816 left: f64::INFINITY,
817 bottom: f64::INFINITY,
818 right: -f64::INFINITY,
819 top: -f64::INFINITY,
820 },
821 faces: BTreeSet::new(),
822 metadata: Metadata { minzoom: 30, maxzoom: 0, ..Metadata::default() },
823 }
824 }
825}
826impl MetadataBuilder {
827 pub fn commit(&mut self) -> Metadata {
829 self.update_center();
831 self.metadata.bounds = self.lon_lat_bounds;
833 for face in &self.faces {
835 self.metadata.faces.push(*face);
836 }
837 self.metadata.to_owned()
839 }
840
841 pub fn set_name(&mut self, name: String) {
843 self.metadata.name = name;
844 }
845
846 pub fn set_scheme(&mut self, scheme: Scheme) {
848 self.metadata.scheme = scheme;
849 }
850
851 pub fn set_extension(&mut self, extension: String) {
853 self.metadata.extension = extension;
854 }
855
856 pub fn set_type(&mut self, r#type: SourceType) {
858 self.metadata.r#type = r#type;
859 }
860
861 pub fn set_version(&mut self, version: String) {
863 self.metadata.version = version;
864 }
865
866 pub fn set_description(&mut self, description: String) {
868 self.metadata.description = description;
869 }
870
871 pub fn set_encoding(&mut self, encoding: Encoding) {
873 self.metadata.encoding = encoding;
874 }
875
876 pub fn add_attribution(&mut self, display_name: &str, href: &str) {
878 self.metadata.attributions.insert(display_name.into(), href.into());
879 }
880
881 pub fn add_layer(&mut self, name: &str, layer: &LayerMetaData) {
883 if self.metadata.layers.entry(name.into()).or_insert(layer.clone()).eq(&layer) {
885 self.metadata.vector_layers.push(VectorLayer {
887 id: name.into(), description: layer.description.clone(),
889 minzoom: Some(layer.minzoom),
890 maxzoom: Some(layer.maxzoom),
891 fields: BTreeMap::new(),
892 });
893 }
894 if layer.minzoom < self.metadata.minzoom {
896 self.metadata.minzoom = layer.minzoom;
897 }
898 if layer.maxzoom > self.metadata.maxzoom {
899 self.metadata.maxzoom = layer.maxzoom;
900 }
901 }
902
903 pub fn add_tile_wm(&mut self, zoom: u8, x: u32, y: u32, ll_bounds: &LonLatBounds) {
905 self.metadata.tilestats.total += 1;
906 self.faces.insert(Face::Face0);
907 self.add_bounds_wm(zoom, x, y);
908 self.update_lon_lat_bounds(ll_bounds);
909 }
910
911 pub fn add_tile_s2(&mut self, face: Face, zoom: u8, x: u32, y: u32, ll_bounds: &LonLatBounds) {
913 self.metadata.tilestats.increment(face);
914 self.faces.insert(face);
915 self.add_bounds_s2(face, zoom, x, y);
916 self.update_lon_lat_bounds(ll_bounds);
917 }
918
919 fn update_center(&mut self) {
921 let Metadata { minzoom, maxzoom, .. } = self.metadata;
922 let BBox { left, bottom, right, top } = self.lon_lat_bounds;
923 self.metadata.centerpoint.lon = (left + right) / 2.0;
924 self.metadata.centerpoint.lat = (bottom + top) / 2.0;
925 self.metadata.centerpoint.zoom = (minzoom + maxzoom) >> 1;
926 }
927
928 fn add_bounds_wm(&mut self, zoom: u8, x: u32, y: u32) {
930 let x = x as u64;
931 let y = y as u64;
932 let bbox = self.metadata.wmbounds.entry(zoom).or_insert(BBox {
933 left: u64::MAX,
934 bottom: u64::MAX,
935 right: 0,
936 top: 0,
937 });
938
939 bbox.left = bbox.left.min(x);
940 bbox.bottom = bbox.bottom.min(y);
941 bbox.right = bbox.right.max(x);
942 bbox.top = bbox.top.max(y);
943 }
944
945 fn add_bounds_s2(&mut self, face: Face, zoom: u8, x: u32, y: u32) {
947 let x = x as u64;
948 let y = y as u64;
949 let bbox = self.metadata.s2bounds.get_mut(face).entry(zoom).or_insert(BBox {
950 left: u64::MAX,
951 bottom: u64::MAX,
952 right: 0,
953 top: 0,
954 });
955
956 bbox.left = bbox.left.min(x);
957 bbox.bottom = bbox.bottom.min(y);
958 bbox.right = bbox.right.max(x);
959 bbox.top = bbox.top.max(y);
960 }
961
962 fn update_lon_lat_bounds(&mut self, ll_bounds: &LonLatBounds) {
964 self.lon_lat_bounds.left = ll_bounds.left.min(self.lon_lat_bounds.left);
965 self.lon_lat_bounds.bottom = ll_bounds.bottom.min(self.lon_lat_bounds.bottom);
966 self.lon_lat_bounds.right = ll_bounds.right.max(self.lon_lat_bounds.right);
967 self.lon_lat_bounds.top = ll_bounds.top.max(self.lon_lat_bounds.top);
968 }
969}
970
971fn extract_link_info(html_string: &str) -> Option<Attributions> {
973 let href_start = html_string.find("href='")?;
975 let href_value_start = href_start + "href='".len();
976 let href_end = html_string[href_value_start..].find("'")?;
977 let href_value = &html_string[href_value_start..href_value_start + href_end];
978
979 let text_start = html_string.find(">")?;
981 let text_value_start = text_start + 1;
982 let text_end = html_string.find("</a>")?;
983 let text_value = &html_string[text_value_start..text_end];
984
985 let mut map = Attributions::new();
986 map.insert(text_value.into(), href_value.into());
987
988 Some(map)
989}
990
991#[cfg(test)]
992mod tests {
993 use super::*;
994 use alloc::vec;
995 use s2json::{PrimitiveShape, ShapeType};
996
997 #[test]
998 fn it_works() {
999 let mut meta_builder = MetadataBuilder::default();
1000
1001 meta_builder.set_name("OSM".into());
1003 meta_builder.set_description("A free editable map of the whole world.".into());
1004 meta_builder.set_version("1.0.0".into());
1005 meta_builder.set_scheme("fzxy".into()); meta_builder.set_type("vector".into()); meta_builder.set_encoding("none".into()); meta_builder.set_extension("pbf".into());
1009 meta_builder.add_attribution("OpenStreetMap", "https://www.openstreetmap.org/copyright/");
1010
1011 let shape_str = r#"
1013 {
1014 "class": "string",
1015 "offset": "f64",
1016 "info": {
1017 "name": "string",
1018 "value": "i64"
1019 }
1020 }
1021 "#;
1022 let shape: Shape = serde_json::from_str(shape_str).unwrap_or_else(|e| panic!("ERROR: {e}"));
1023 let layer = LayerMetaData {
1024 minzoom: 0,
1025 maxzoom: 13,
1026 description: Some("water_lines".into()),
1027 draw_types: Vec::from(&[DrawType::Lines]),
1028 shape: shape.clone(),
1029 m_shape: None,
1030 };
1031 meta_builder.add_layer("water_lines", &layer);
1032
1033 meta_builder.add_tile_wm(
1036 0,
1037 0,
1038 0,
1039 &LonLatBounds { left: -60.0, bottom: -20.0, right: 5.0, top: 60.0 },
1040 );
1041 meta_builder.add_tile_s2(
1043 Face::Face1,
1044 5,
1045 22,
1046 37,
1047 &LonLatBounds { left: -120.0, bottom: -7.0, right: 44.0, top: 72.0 },
1048 );
1049
1050 let resulting_metadata: Metadata = meta_builder.commit();
1052
1053 assert_eq!(
1054 resulting_metadata,
1055 Metadata {
1056 name: "OSM".into(),
1057 description: "A free editable map of the whole world.".into(),
1058 version: "1.0.0".into(),
1059 scheme: "fzxy".into(),
1060 r#type: "vector".into(),
1061 encoding: "none".into(),
1062 extension: "pbf".into(),
1063 attributions: Attributions::from([(
1064 "OpenStreetMap".into(),
1065 "https://www.openstreetmap.org/copyright/".into()
1066 ),]),
1067 wmbounds: BTreeMap::from([(
1068 0,
1069 TileBounds { left: 0, bottom: 0, right: 0, top: 0 }
1070 ),]),
1071 faces: Vec::from(&[Face::Face0, Face::Face1]),
1072 bounds: BBox { left: -120.0, bottom: -20.0, right: 44.0, top: 72.0 },
1073 s2bounds: FaceBounds {
1074 face0: BTreeMap::new(),
1075 face1: BTreeMap::from([(
1076 5,
1077 TileBounds { left: 22, bottom: 37, right: 22, top: 37 }
1078 ),]),
1079 face2: BTreeMap::new(),
1080 face3: BTreeMap::new(),
1081 face4: BTreeMap::new(),
1082 face5: BTreeMap::new(),
1083 wm: BTreeMap::new(),
1084 },
1085 minzoom: 0,
1086 maxzoom: 13,
1087 centerpoint: Center { lon: -38.0, lat: 26.0, zoom: 6 },
1089 tilestats: TileStatsMetadata {
1090 total: 2,
1091 total_0: 0,
1092 total_1: 1,
1093 total_2: 0,
1094 total_3: 0,
1095 total_4: 0,
1096 total_5: 0,
1097 total_wm: 0,
1098 },
1099 layers: BTreeMap::from([(
1100 "water_lines".into(),
1101 LayerMetaData {
1102 description: Some("water_lines".into()),
1103 minzoom: 0,
1104 maxzoom: 13,
1105 draw_types: Vec::from(&[DrawType::Lines]),
1106 shape: Shape::from([
1107 ("class".into(), ShapeType::Primitive(PrimitiveShape::String)),
1108 ("offset".into(), ShapeType::Primitive(PrimitiveShape::F64)),
1109 (
1110 "info".into(),
1111 ShapeType::Nested(Shape::from([
1112 ("name".into(), ShapeType::Primitive(PrimitiveShape::String)),
1113 ("value".into(), ShapeType::Primitive(PrimitiveShape::I64)),
1114 ]))
1115 ),
1116 ]),
1117 m_shape: None,
1118 }
1119 )]),
1120 s2tilejson: "1.0.0".into(),
1121 vector_layers: Vec::from([VectorLayer {
1122 id: "water_lines".into(),
1123 description: Some("water_lines".into()),
1124 minzoom: Some(0),
1125 maxzoom: Some(13),
1126 fields: BTreeMap::new()
1127 }]),
1128 ..Default::default()
1129 }
1130 );
1131
1132 let meta_str = serde_json::to_string(&resulting_metadata).unwrap();
1133
1134 assert_eq!(
1135 meta_str,
1136 "{\"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\":{},\"6\":{}},\"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,\"6\":0},\"vector_layers\":[{\"id\":\"water_lines\",\"description\":\"water_lines\",\"minzoom\":0,\"maxzoom\":13,\"fields\":{}}],\"tilejson\":null}"
1137 );
1138
1139 let meta_reparsed: Metadata =
1140 serde_json::from_str(&meta_str).unwrap_or_else(|e| panic!("ERROR: {e}"));
1141 assert_eq!(meta_reparsed, resulting_metadata);
1142 }
1143
1144 #[test]
1145 fn test_face() {
1146 assert_eq!(Face::Face0, Face::from(0));
1147 assert_eq!(Face::Face1, Face::from(1));
1148 assert_eq!(Face::Face2, Face::from(2));
1149 assert_eq!(Face::Face3, Face::from(3));
1150 assert_eq!(Face::Face4, Face::from(4));
1151 assert_eq!(Face::Face5, Face::from(5));
1152
1153 assert_eq!(0, u8::from(Face::Face0));
1154 assert_eq!(1, u8::from(Face::Face1));
1155 assert_eq!(2, u8::from(Face::Face2));
1156 assert_eq!(3, u8::from(Face::Face3));
1157 assert_eq!(4, u8::from(Face::Face4));
1158 assert_eq!(5, u8::from(Face::Face5));
1159 }
1160
1161 #[test]
1162 fn test_bbox() {
1163 let bbox: BBox = BBox { left: 0.0, bottom: 0.0, right: 0.0, top: 0.0 };
1164 let json = serde_json::to_string(&bbox).unwrap();
1166 assert_eq!(json, r#"[0.0,0.0,0.0,0.0]"#);
1167 let bbox2: BBox = serde_json::from_str(&json).unwrap();
1168 assert_eq!(bbox, bbox2);
1169 }
1170
1171 #[test]
1173 fn test_tilestats() {
1174 let mut tilestats = TileStatsMetadata {
1175 total: 2,
1176 total_0: 0,
1177 total_1: 1,
1178 total_2: 0,
1179 total_3: 0,
1180 total_4: 0,
1181 total_5: 0,
1182 total_wm: 0,
1183 };
1184 let json = serde_json::to_string(&tilestats).unwrap();
1186 assert_eq!(json, r#"{"total":2,"0":0,"1":1,"2":0,"3":0,"4":0,"5":0,"6":0}"#);
1187 let tilestats2: TileStatsMetadata = serde_json::from_str(&json).unwrap();
1188 assert_eq!(tilestats, tilestats2);
1189
1190 assert_eq!(tilestats.get(0.into()), 0);
1192 tilestats.increment(0.into());
1194 assert_eq!(tilestats.get(0.into()), 1);
1195
1196 assert_eq!(tilestats.get(1.into()), 1);
1198 tilestats.increment(1.into());
1200 assert_eq!(tilestats.get(1.into()), 2);
1201
1202 assert_eq!(tilestats.get(2.into()), 0);
1204 tilestats.increment(2.into());
1206 assert_eq!(tilestats.get(2.into()), 1);
1207
1208 assert_eq!(tilestats.get(3.into()), 0);
1210 tilestats.increment(3.into());
1212 assert_eq!(tilestats.get(3.into()), 1);
1213
1214 assert_eq!(tilestats.get(4.into()), 0);
1216 tilestats.increment(4.into());
1218 assert_eq!(tilestats.get(4.into()), 1);
1219
1220 assert_eq!(tilestats.get(5.into()), 0);
1222 tilestats.increment(5.into());
1224 assert_eq!(tilestats.get(5.into()), 1);
1225 }
1226
1227 #[test]
1229 fn test_facebounds() {
1230 let mut facebounds = FaceBounds::default();
1231 let face0 = facebounds.get_mut(0.into());
1233 face0.insert(0, TileBounds { left: 0, bottom: 0, right: 0, top: 0 });
1234 let face1 = facebounds.get_mut(1.into());
1236 face1.insert(0, TileBounds { left: 0, bottom: 0, right: 1, top: 1 });
1237 let face2 = facebounds.get_mut(2.into());
1239 face2.insert(0, TileBounds { left: 0, bottom: 0, right: 2, top: 2 });
1240 let face3 = facebounds.get_mut(3.into());
1242 face3.insert(0, TileBounds { left: 0, bottom: 0, right: 3, top: 3 });
1243 let face4 = facebounds.get_mut(4.into());
1245 face4.insert(0, TileBounds { left: 0, bottom: 0, right: 4, top: 4 });
1246 let face5 = facebounds.get_mut(5.into());
1248 face5.insert(0, TileBounds { left: 0, bottom: 0, right: 5, top: 5 });
1249
1250 assert_eq!(
1253 facebounds.get(0.into()).get(&0).unwrap(),
1254 &TileBounds { left: 0, bottom: 0, right: 0, top: 0 }
1255 );
1256 assert_eq!(
1258 facebounds.get(1.into()).get(&0).unwrap(),
1259 &TileBounds { left: 0, bottom: 0, right: 1, top: 1 }
1260 );
1261 assert_eq!(
1263 facebounds.get(2.into()).get(&0).unwrap(),
1264 &TileBounds { left: 0, bottom: 0, right: 2, top: 2 }
1265 );
1266 assert_eq!(
1268 facebounds.get(3.into()).get(&0).unwrap(),
1269 &TileBounds { left: 0, bottom: 0, right: 3, top: 3 }
1270 );
1271 assert_eq!(
1273 facebounds.get(4.into()).get(&0).unwrap(),
1274 &TileBounds { left: 0, bottom: 0, right: 4, top: 4 }
1275 );
1276 assert_eq!(
1278 facebounds.get(5.into()).get(&0).unwrap(),
1279 &TileBounds { left: 0, bottom: 0, right: 5, top: 5 }
1280 );
1281
1282 let json = serde_json::to_string(&facebounds).unwrap();
1284 assert_eq!(
1285 json,
1286 "{\"0\":{\"0\":[0,0,0,0]},\"1\":{\"0\":[0,0,1,1]},\"2\":{\"0\":[0,0,2,2]},\"3\":{\"0\"\
1287 :[0,0,3,3]},\"4\":{\"0\":[0,0,4,4]},\"5\":{\"0\":[0,0,5,5]},\"6\":{}}"
1288 );
1289 let facebounds2 = serde_json::from_str(&json).unwrap();
1290 assert_eq!(facebounds, facebounds2);
1291 }
1292
1293 #[test]
1295 fn test_drawtype() {
1296 assert_eq!(DrawType::from(1), DrawType::Points);
1297 assert_eq!(DrawType::from(2), DrawType::Lines);
1298 assert_eq!(DrawType::from(3), DrawType::Polygons);
1299 assert_eq!(DrawType::from(4), DrawType::Points3D);
1300 assert_eq!(DrawType::from(5), DrawType::Lines3D);
1301 assert_eq!(DrawType::from(6), DrawType::Polygons3D);
1302 assert_eq!(DrawType::from(7), DrawType::Raster);
1303 assert_eq!(DrawType::from(8), DrawType::Grid);
1304
1305 assert_eq!(1, u8::from(DrawType::Points));
1306 assert_eq!(2, u8::from(DrawType::Lines));
1307 assert_eq!(3, u8::from(DrawType::Polygons));
1308 assert_eq!(4, u8::from(DrawType::Points3D));
1309 assert_eq!(5, u8::from(DrawType::Lines3D));
1310 assert_eq!(6, u8::from(DrawType::Polygons3D));
1311 assert_eq!(7, u8::from(DrawType::Raster));
1312 assert_eq!(8, u8::from(DrawType::Grid));
1313
1314 let json = serde_json::to_string(&DrawType::Points).unwrap();
1316 assert_eq!(json, "1");
1317 let drawtype: DrawType = serde_json::from_str(&json).unwrap();
1318 assert_eq!(drawtype, DrawType::Points);
1319
1320 let drawtype: DrawType = serde_json::from_str("2").unwrap();
1321 assert_eq!(drawtype, DrawType::Lines);
1322
1323 let drawtype: DrawType = serde_json::from_str("3").unwrap();
1324 assert_eq!(drawtype, DrawType::Polygons);
1325
1326 let drawtype: DrawType = serde_json::from_str("4").unwrap();
1327 assert_eq!(drawtype, DrawType::Points3D);
1328
1329 let drawtype: DrawType = serde_json::from_str("5").unwrap();
1330 assert_eq!(drawtype, DrawType::Lines3D);
1331
1332 let drawtype: DrawType = serde_json::from_str("6").unwrap();
1333 assert_eq!(drawtype, DrawType::Polygons3D);
1334
1335 let drawtype: DrawType = serde_json::from_str("7").unwrap();
1336 assert_eq!(drawtype, DrawType::Raster);
1337
1338 let drawtype: DrawType = serde_json::from_str("8").unwrap();
1339 assert_eq!(drawtype, DrawType::Grid);
1340
1341 assert!(serde_json::from_str::<DrawType>("9").is_err());
1342 }
1343
1344 #[test]
1346 fn test_sourcetype() {
1347 assert_eq!(SourceType::from("vector"), SourceType::Vector);
1349 assert_eq!(SourceType::from("json"), SourceType::Json);
1350 assert_eq!(SourceType::from("raster"), SourceType::Raster);
1351 assert_eq!(SourceType::from("raster-dem"), SourceType::RasterDem);
1352 assert_eq!(SourceType::from("grid"), SourceType::Grid);
1353 assert_eq!(SourceType::from("markers"), SourceType::Markers);
1354 assert_eq!(SourceType::from("overlay"), SourceType::Unknown);
1355
1356 let json = serde_json::to_string(&SourceType::Vector).unwrap();
1358 assert_eq!(json, "\"vector\"");
1359 let sourcetype: SourceType = serde_json::from_str(&json).unwrap();
1360 assert_eq!(sourcetype, SourceType::Vector);
1361
1362 let json = serde_json::to_string(&SourceType::Json).unwrap();
1364 assert_eq!(json, "\"json\"");
1365 let sourcetype: SourceType = serde_json::from_str(&json).unwrap();
1366 assert_eq!(sourcetype, SourceType::Json);
1367
1368 let json = serde_json::to_string(&SourceType::Raster).unwrap();
1370 assert_eq!(json, "\"raster\"");
1371 let sourcetype: SourceType = serde_json::from_str(&json).unwrap();
1372 assert_eq!(sourcetype, SourceType::Raster);
1373
1374 let json = serde_json::to_string(&SourceType::RasterDem).unwrap();
1376 assert_eq!(json, "\"raster-dem\"");
1377 let sourcetype: SourceType = serde_json::from_str(&json).unwrap();
1378 assert_eq!(sourcetype, SourceType::RasterDem);
1379
1380 let json = serde_json::to_string(&SourceType::Grid).unwrap();
1382 assert_eq!(json, "\"grid\"");
1383 let sourcetype: SourceType = serde_json::from_str(&json).unwrap();
1384 assert_eq!(sourcetype, SourceType::Grid);
1385
1386 let json = serde_json::to_string(&SourceType::Markers).unwrap();
1388 assert_eq!(json, "\"markers\"");
1389 let sourcetype: SourceType = serde_json::from_str(&json).unwrap();
1390 assert_eq!(sourcetype, SourceType::Markers);
1391
1392 let json = serde_json::to_string(&SourceType::Unknown).unwrap();
1394 assert_eq!(json, "\"unknown\"");
1395 let sourcetype: SourceType = serde_json::from_str(r#""overlay""#).unwrap();
1396 assert_eq!(sourcetype, SourceType::Unknown);
1397 }
1398
1399 #[test]
1401 fn test_encoding() {
1402 assert_eq!(Encoding::from("none"), Encoding::None);
1404 assert_eq!(Encoding::from("gzip"), Encoding::Gzip);
1405 assert_eq!(Encoding::from("br"), Encoding::Brotli);
1406 assert_eq!(Encoding::from("zstd"), Encoding::Zstd);
1407
1408 assert_eq!(core::convert::Into::<&str>::into(Encoding::None), "none");
1410 assert_eq!(core::convert::Into::<&str>::into(Encoding::Gzip), "gzip");
1411 assert_eq!(core::convert::Into::<&str>::into(Encoding::Brotli), "br");
1412 assert_eq!(core::convert::Into::<&str>::into(Encoding::Zstd), "zstd");
1413
1414 assert_eq!(Encoding::from(0), Encoding::None);
1416 assert_eq!(Encoding::from(1), Encoding::Gzip);
1417 assert_eq!(Encoding::from(2), Encoding::Brotli);
1418 assert_eq!(Encoding::from(3), Encoding::Zstd);
1419
1420 assert_eq!(u8::from(Encoding::None), 0);
1422 assert_eq!(u8::from(Encoding::Gzip), 1);
1423 assert_eq!(u8::from(Encoding::Brotli), 2);
1424 assert_eq!(u8::from(Encoding::Zstd), 3);
1425
1426 let json = serde_json::to_string(&Encoding::Gzip).unwrap();
1428 assert_eq!(json, "\"gzip\"");
1429 let encoding: Encoding = serde_json::from_str(&json).unwrap();
1430 assert_eq!(encoding, Encoding::Gzip);
1431
1432 let json = serde_json::to_string(&Encoding::Brotli).unwrap();
1434 assert_eq!(json, "\"br\"");
1435 let encoding: Encoding = serde_json::from_str(&json).unwrap();
1436 assert_eq!(encoding, Encoding::Brotli);
1437
1438 let json = serde_json::to_string(&Encoding::None).unwrap();
1440 assert_eq!(json, "\"none\"");
1441 let encoding: Encoding = serde_json::from_str(&json).unwrap();
1442 assert_eq!(encoding, Encoding::None);
1443
1444 let json = serde_json::to_string(&Encoding::Zstd).unwrap();
1446 assert_eq!(json, "\"zstd\"");
1447 let encoding: Encoding = serde_json::from_str(&json).unwrap();
1448 assert_eq!(encoding, Encoding::Zstd);
1449 }
1450
1451 #[test]
1453 fn test_scheme() {
1454 assert_eq!(Scheme::from("fzxy"), Scheme::Fzxy);
1456 assert_eq!(Scheme::from("tfzxy"), Scheme::Tfzxy);
1457 assert_eq!(Scheme::from("xyz"), Scheme::Xyz);
1458 assert_eq!(Scheme::from("txyz"), Scheme::Txyz);
1459 assert_eq!(Scheme::from("tms"), Scheme::Tms);
1460
1461 assert_eq!(core::convert::Into::<&str>::into(Scheme::Fzxy), "fzxy");
1463 assert_eq!(core::convert::Into::<&str>::into(Scheme::Tfzxy), "tfzxy");
1464 assert_eq!(core::convert::Into::<&str>::into(Scheme::Xyz), "xyz");
1465 assert_eq!(core::convert::Into::<&str>::into(Scheme::Txyz), "txyz");
1466 assert_eq!(core::convert::Into::<&str>::into(Scheme::Tms), "tms");
1467 }
1468
1469 #[test]
1470 fn test_tippecanoe_metadata() {
1471 let meta_str = r#"{
1472 "name": "test_fixture_1.pmtiles",
1473 "description": "test_fixture_1.pmtiles",
1474 "version": "2",
1475 "type": "overlay",
1476 "generator": "tippecanoe v2.5.0",
1477 "generator_options": "./tippecanoe -zg -o test_fixture_1.pmtiles --force",
1478 "vector_layers": [
1479 {
1480 "id": "test_fixture_1pmtiles",
1481 "description": "",
1482 "minzoom": 0,
1483 "maxzoom": 0,
1484 "fields": {}
1485 }
1486 ],
1487 "tilestats": {
1488 "layerCount": 1,
1489 "layers": [
1490 {
1491 "layer": "test_fixture_1pmtiles",
1492 "count": 1,
1493 "geometry": "Polygon",
1494 "attributeCount": 0,
1495 "attributes": []
1496 }
1497 ]
1498 }
1499 }"#;
1500
1501 let _meta: Metadata =
1502 serde_json::from_str(meta_str).unwrap_or_else(|e| panic!("ERROR: {e}"));
1503 }
1504
1505 #[test]
1506 fn test_mapbox_metadata() {
1507 let meta_str = r#"{
1508 "tilejson": "3.0.0",
1509 "name": "OpenStreetMap",
1510 "description": "A free editable map of the whole world.",
1511 "version": "1.0.0",
1512 "attribution": "<a href='https://openstreetmap.org'>OSM contributors</a>",
1513 "scheme": "xyz",
1514 "tiles": [
1515 "https://a.tile.custom-osm-tiles.org/{z}/{x}/{y}.mvt",
1516 "https://b.tile.custom-osm-tiles.org/{z}/{x}/{y}.mvt",
1517 "https://c.tile.custom-osm-tiles.org/{z}/{x}/{y}.mvt"
1518 ],
1519 "minzoom": 0,
1520 "maxzoom": 18,
1521 "bounds": [-180, -85, 180, 85],
1522 "fillzoom": 6,
1523 "something_custom": "this is my unique field",
1524 "vector_layers": [
1525 {
1526 "id": "telephone",
1527 "fields": {
1528 "phone_number": "the phone number",
1529 "payment": "how to pay"
1530 }
1531 },
1532 {
1533 "id": "bicycle_parking",
1534 "fields": {
1535 "type": "the type of bike parking",
1536 "year_installed": "the year the bike parking was installed"
1537 }
1538 },
1539 {
1540 "id": "showers",
1541 "fields": {
1542 "water_temperature": "the maximum water temperature",
1543 "wear_sandles": "whether you should wear sandles or not",
1544 "wheelchair": "is the shower wheelchair friendly?"
1545 }
1546 }
1547 ]
1548 }"#;
1549
1550 let meta_mapbox: MapboxTileJSONMetadata =
1551 serde_json::from_str(meta_str).unwrap_or_else(|e| panic!("ERROR: {e}"));
1552 let meta_new = meta_mapbox.to_metadata();
1553 assert_eq!(
1554 meta_new,
1555 Metadata {
1556 name: "OpenStreetMap".into(),
1557 description: "A free editable map of the whole world.".into(),
1558 version: "1.0.0".into(),
1559 scheme: Scheme::Xyz,
1560 r#type: "vector".into(),
1561 encoding: Encoding::None, extension: "pbf".into(),
1563 attributions: Attributions::from([(
1564 "OSM contributors".into(),
1565 "https://openstreetmap.org".into()
1566 )]),
1567 bounds: BBox::new(-180., -85., 180., 85.),
1568 vector_layers: meta_mapbox.vector_layers.clone(),
1569 maxzoom: 18,
1570 minzoom: 0,
1571 centerpoint: Center { lat: 0.0, lon: 0.0, zoom: 0 },
1572 wmbounds: WMBounds::default(),
1573 faces: vec![Face::Face0],
1574 s2bounds: FaceBounds::default(),
1575 tilestats: TileStatsMetadata::default(),
1576 layers: LayersMetaData::default(),
1577 s2tilejson: "1.0.0".into(),
1578 attribution: Some(
1579 "<a href='https://openstreetmap.org'>OSM contributors</a>".into()
1580 ),
1581 tiles: Some(meta_mapbox.tiles.clone()),
1582 fillzoom: meta_mapbox.fillzoom,
1583 center: Some([0.0, 0.0, 0.0]),
1584 ..Default::default()
1585 },
1586 );
1587
1588 let meta_mapbox_from_unknown: UnknownMetadata =
1589 serde_json::from_str(meta_str).unwrap_or_else(|e| panic!("ERROR: {e}"));
1590 let meta_new = meta_mapbox_from_unknown.to_metadata();
1591 assert_eq!(
1592 meta_new,
1593 Metadata {
1594 name: "OpenStreetMap".into(),
1595 description: "A free editable map of the whole world.".into(),
1596 version: "1.0.0".into(),
1597 scheme: Scheme::Xyz,
1598 r#type: "vector".into(),
1599 encoding: Encoding::None, extension: "pbf".into(),
1601 attributions: Attributions::default(),
1602 bounds: BBox::new(-180., -85., 180., 85.),
1603 vector_layers: meta_mapbox.vector_layers.clone(),
1604 maxzoom: 18,
1605 minzoom: 0,
1606 centerpoint: Center { lat: 0.0, lon: 0.0, zoom: 0 },
1607 wmbounds: WMBounds::default(),
1608 faces: vec![],
1609 s2bounds: FaceBounds::default(),
1610 tilestats: TileStatsMetadata::default(),
1611 layers: LayersMetaData::default(),
1612 s2tilejson: "1.0.0".into(),
1613 attribution: Some(
1614 "<a href='https://openstreetmap.org'>OSM contributors</a>".into()
1615 ),
1616 tiles: Some(meta_mapbox.tiles.clone()),
1617 fillzoom: meta_mapbox.fillzoom,
1618 center: None,
1619 tilejson: Some("3.0.0".into()),
1620 ..Default::default()
1621 },
1622 );
1623 }
1624
1625 #[test]
1626 fn test_malformed_metadata() {
1627 let meta_str = r#"{
1628 "s2tilejson": "1.0.0",
1629 "bounds": [
1630 -180,
1631 -85,
1632 180,
1633 85
1634 ],
1635 "name": "Mapbox Satellite",
1636 "scheme": "xyz",
1637 "format": "zxy",
1638 "type": "raster",
1639 "extension": "webp",
1640 "encoding": "gzip",
1641 "minzoom": 0,
1642 "maxzoom": 3
1643 }
1644 "#;
1645
1646 let malformed_success: UnknownMetadata =
1647 serde_json::from_str(meta_str).unwrap_or_else(|e| panic!("ERROR: {e}"));
1648
1649 let meta: Metadata = malformed_success.to_metadata();
1650 assert_eq!(
1651 meta,
1652 Metadata {
1653 s2tilejson: "1.0.0".into(),
1654 version: "1.0.0".into(),
1655 name: "Mapbox Satellite".into(),
1656 scheme: Scheme::Xyz,
1657 description: "Built with s2maps-cli".into(),
1658 r#type: SourceType::Raster,
1659 extension: "webp".into(),
1660 encoding: Encoding::Gzip,
1661 faces: vec![],
1662 bounds: BBox::new(-180., -85., 180., 85.),
1663 wmbounds: BTreeMap::default(),
1664 s2bounds: FaceBounds {
1665 face0: BTreeMap::default(),
1666 face1: BTreeMap::default(),
1667 face2: BTreeMap::default(),
1668 face3: BTreeMap::default(),
1669 face4: BTreeMap::default(),
1670 face5: BTreeMap::default(),
1671 wm: BTreeMap::default(),
1672 },
1673 minzoom: 0,
1674 maxzoom: 3,
1675 centerpoint: Center { lon: 0.0, lat: 0.0, zoom: 0 },
1676 attributions: Attributions::default(),
1677 layers: BTreeMap::default(),
1678 tilestats: TileStatsMetadata {
1679 total: 0,
1680 total_0: 0,
1681 total_1: 0,
1682 total_2: 0,
1683 total_3: 0,
1684 total_4: 0,
1685 total_5: 0,
1686 total_wm: 0
1687 },
1688 vector_layers: vec![],
1689 ..Default::default()
1690 }
1691 );
1692 }
1693
1694 #[test]
1695 fn test_faces() {
1696 let meta = Metadata {
1697 faces: vec![Face::Face0, Face::Face1, Face::Face4, Face::Face5, Face::WM],
1698 ..Default::default()
1699 };
1700
1701 let to_string: String = serde_json::to_string(&meta).unwrap();
1702 let from_string: Metadata = serde_json::from_str(&to_string).unwrap();
1703 assert_eq!(meta, from_string);
1704
1705 let from_string_unknown: UnknownMetadata = serde_json::from_str(&to_string).unwrap();
1706 let from_string: Metadata = from_string_unknown.to_metadata();
1707 assert_eq!(meta, from_string);
1708 }
1709
1710 #[test]
1711 fn test_vector_feature_to_draw_type() {
1712 let feature: VectorFeature<(), Properties, MValue> = VectorFeature {
1714 geometry: VectorGeometry::new_point(VectorPoint::from_xy(0., 0.), None),
1715 ..Default::default()
1716 };
1717 assert_eq!(DrawType::Points, (&feature).into());
1718 let feature: VectorFeature<(), Properties, MValue> = VectorFeature {
1719 geometry: VectorGeometry::new_multipoint(vec![VectorPoint::from_xy(0., 0.)], None),
1720 ..Default::default()
1721 };
1722 assert_eq!(DrawType::Points, (&feature).into());
1723 let feature: VectorFeature<(), Properties, MValue> = VectorFeature {
1725 geometry: VectorGeometry::new_point(VectorPoint::from_xyz(0., 0., 1.0), None),
1726 ..Default::default()
1727 };
1728 assert_eq!(DrawType::Points3D, (&feature).into());
1729 let feature: VectorFeature<(), Properties, MValue> = VectorFeature {
1730 geometry: VectorGeometry::new_multipoint(
1731 vec![VectorPoint::from_xyz(0., 0., 1.0)],
1732 None,
1733 ),
1734 ..Default::default()
1735 };
1736 assert_eq!(DrawType::Points3D, (&feature).into());
1737 let feature: VectorFeature<(), Properties, MValue> = VectorFeature {
1739 geometry: VectorGeometry::new_linestring(vec![VectorPoint::from_xy(0., 0.)], None),
1740 ..Default::default()
1741 };
1742 assert_eq!(DrawType::Lines, (&feature).into());
1743 let feature: VectorFeature<(), Properties, MValue> = VectorFeature {
1744 geometry: VectorGeometry::new_multilinestring(
1745 vec![vec![VectorPoint::from_xy(0., 0.)]],
1746 None,
1747 ),
1748 ..Default::default()
1749 };
1750 assert_eq!(DrawType::Lines, (&feature).into());
1751 let feature: VectorFeature<(), Properties, MValue> = VectorFeature {
1753 geometry: VectorGeometry::new_linestring(
1754 vec![VectorPoint::from_xyz(0., 0., 1.0)],
1755 None,
1756 ),
1757 ..Default::default()
1758 };
1759 assert_eq!(DrawType::Lines3D, (&feature).into());
1760 let feature: VectorFeature<(), Properties, MValue> = VectorFeature {
1761 geometry: VectorGeometry::new_multilinestring(
1762 vec![vec![VectorPoint::from_xyz(0., 0., 1.0)]],
1763 None,
1764 ),
1765 ..Default::default()
1766 };
1767 assert_eq!(DrawType::Lines3D, (&feature).into());
1768 let feature: VectorFeature<(), Properties, MValue> = VectorFeature {
1770 geometry: VectorGeometry::new_polygon(vec![vec![VectorPoint::from_xy(0., 0.)]], None),
1771 ..Default::default()
1772 };
1773 assert_eq!(DrawType::Polygons, (&feature).into());
1774 let feature: VectorFeature<(), Properties, MValue> = VectorFeature {
1775 geometry: VectorGeometry::new_multipolygon(
1776 vec![vec![vec![VectorPoint::from_xy(0., 0.)]]],
1777 None,
1778 ),
1779 ..Default::default()
1780 };
1781 assert_eq!(DrawType::Polygons, (&feature).into());
1782 let feature: VectorFeature<(), Properties, MValue> = VectorFeature {
1784 geometry: VectorGeometry::new_polygon(
1785 vec![vec![VectorPoint::from_xyz(0., 0., 1.0)]],
1786 None,
1787 ),
1788 ..Default::default()
1789 };
1790 assert_eq!(DrawType::Polygons3D, (&feature).into());
1791 let feature: VectorFeature<(), Properties, MValue> = VectorFeature {
1792 geometry: VectorGeometry::new_multipolygon(
1793 vec![vec![vec![VectorPoint::from_xyz(0., 0., 1.0)]]],
1794 None,
1795 ),
1796 ..Default::default()
1797 };
1798 assert_eq!(DrawType::Polygons3D, (&feature).into());
1799 }
1800}