• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Open-S2 / s2-tilejson / #16

12 Aug 2024 07:45PM UTC coverage: 99.402%. Remained the same
#16

push

Mr Martian
improve formatting; add formatting check to actions

997 of 1003 relevant lines covered (99.4%)

17.38 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

99.32
/rust/lib.rs
1
#![no_std]
2
#![deny(missing_docs)]
3
//! The `s2-tilejson` Rust crate... TODO
4

5
extern crate alloc;
6

7
use serde::de::{self, SeqAccess, Visitor};
8
use serde::ser::SerializeTuple;
9
use serde::{Deserialize, Deserializer, Serialize, Serializer};
10

11
use alloc::borrow::ToOwned;
12
use alloc::collections::BTreeMap;
13
use alloc::collections::BTreeSet;
14
use alloc::fmt;
15
use alloc::format;
16
use alloc::string::String;
17
use alloc::vec::Vec;
18

19
/// S2 Face
20
#[derive(Serialize, Deserialize, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
×
21
pub enum Face {
22
    /// Face 0
23
    Face0 = 0,
24
    /// Face 1
25
    Face1 = 1,
26
    /// Face 2
27
    Face2 = 2,
28
    /// Face 3
29
    Face3 = 3,
30
    /// Face 4
31
    Face4 = 4,
32
    /// Face 5
33
    Face5 = 5,
34
}
35
impl From<Face> for u8 {
36
    fn from(face: Face) -> Self {
6✔
37
        face as u8
6✔
38
    }
6✔
39
}
40
impl From<u8> for Face {
41
    fn from(face: u8) -> Self {
36✔
42
        match face {
36✔
43
            1 => Face::Face1,
6✔
44
            2 => Face::Face2,
6✔
45
            3 => Face::Face3,
6✔
46
            4 => Face::Face4,
6✔
47
            5 => Face::Face5,
6✔
48
            _ => Face::Face0,
6✔
49
        }
50
    }
36✔
51
}
52

53
/// The Bounding box, whether the tile bounds or lon-lat bounds or whatever.
54
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
55
pub struct BBox<T = f64> {
56
    /// left most point; Also represents the left-most longitude
57
    pub left: T,
58
    /// bottom most point; Also represents the bottom-most latitude
59
    pub bottom: T,
60
    /// right most point; Also represents the right-most longitude
61
    pub right: T,
62
    /// top most point; Also represents the top-most latitude
63
    pub top: T,
64
}
65
impl<T> Serialize for BBox<T>
66
where
67
    T: Serialize + Copy,
68
{
69
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
7✔
70
    where
7✔
71
        S: Serializer,
7✔
72
    {
7✔
73
        let mut seq = serializer.serialize_tuple(4)?;
7✔
74
        seq.serialize_element(&self.left)?;
7✔
75
        seq.serialize_element(&self.bottom)?;
7✔
76
        seq.serialize_element(&self.right)?;
7✔
77
        seq.serialize_element(&self.top)?;
7✔
78
        seq.end()
7✔
79
    }
7✔
80
}
81

82
impl<'de, T> Deserialize<'de> for BBox<T>
83
where
84
    T: Deserialize<'de> + Copy,
85
{
86
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
7✔
87
    where
7✔
88
        D: Deserializer<'de>,
7✔
89
    {
7✔
90
        struct BBoxVisitor<T> {
7✔
91
            marker: core::marker::PhantomData<T>,
7✔
92
        }
7✔
93

7✔
94
        impl<'de, T> Visitor<'de> for BBoxVisitor<T>
7✔
95
        where
7✔
96
            T: Deserialize<'de> + Copy,
7✔
97
        {
7✔
98
            type Value = BBox<T>;
7✔
99

7✔
100
            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
7✔
101
                formatter.write_str("a sequence of four numbers")
×
102
            }
×
103

7✔
104
            fn visit_seq<V>(self, mut seq: V) -> Result<BBox<T>, V::Error>
7✔
105
            where
7✔
106
                V: SeqAccess<'de>,
7✔
107
            {
7✔
108
                let left = seq
7✔
109
                    .next_element()?
7✔
110
                    .ok_or_else(|| de::Error::invalid_length(0, &self))?;
7✔
111
                let bottom = seq
7✔
112
                    .next_element()?
7✔
113
                    .ok_or_else(|| de::Error::invalid_length(1, &self))?;
7✔
114
                let right = seq
7✔
115
                    .next_element()?
7✔
116
                    .ok_or_else(|| de::Error::invalid_length(2, &self))?;
7✔
117
                let top = seq
7✔
118
                    .next_element()?
7✔
119
                    .ok_or_else(|| de::Error::invalid_length(3, &self))?;
7✔
120
                Ok(BBox {
7✔
121
                    left,
7✔
122
                    bottom,
7✔
123
                    right,
7✔
124
                    top,
7✔
125
                })
7✔
126
            }
7✔
127
        }
7✔
128

7✔
129
        deserializer.deserialize_tuple(
7✔
130
            4,
7✔
131
            BBoxVisitor {
7✔
132
                marker: core::marker::PhantomData,
7✔
133
            },
7✔
134
        )
7✔
135
    }
7✔
136
}
137

138
/// Use bounds as floating point numbers for longitude and latitude
139
pub type LonLatBounds = BBox<f64>;
140

141
/// Use bounds as u64 for the tile index range
142
pub type TileBounds = BBox<u64>;
143

144
/// 1: points, 2: lines, 3: polys, 4: points3D, 5: lines3D, 6: polys3D
145
#[derive(Copy, Clone, Debug, PartialEq)]
146
pub enum DrawType {
147
    /// Collection of points
148
    Points = 1,
149
    /// Collection of lines
150
    Lines = 2,
151
    /// Collection of polygons
152
    Polygons = 3,
153
    /// Collection of 3D points
154
    Points3D = 4,
155
    /// Collection of 3D lines
156
    Lines3D = 5,
157
    /// Collection of 3D polygons
158
    Polygons3D = 6,
159
}
160
impl From<DrawType> for u8 {
161
    fn from(draw_type: DrawType) -> Self {
6✔
162
        draw_type as u8
6✔
163
    }
6✔
164
}
165
impl From<u8> for DrawType {
166
    fn from(draw_type: u8) -> Self {
7✔
167
        match draw_type {
7✔
168
            1 => DrawType::Points,
1✔
169
            2 => DrawType::Lines,
1✔
170
            3 => DrawType::Polygons,
1✔
171
            4 => DrawType::Points3D,
1✔
172
            5 => DrawType::Lines3D,
1✔
173
            6 => DrawType::Polygons3D,
1✔
174
            _ => DrawType::Points,
1✔
175
        }
176
    }
7✔
177
}
178
impl Serialize for DrawType {
179
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1✔
180
    where
1✔
181
        S: Serializer,
1✔
182
    {
1✔
183
        // Serialize as u8
1✔
184
        serializer.serialize_u8(*self as u8)
1✔
185
    }
1✔
186
}
187

188
impl<'de> Deserialize<'de> for DrawType {
189
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
7✔
190
    where
7✔
191
        D: Deserializer<'de>,
7✔
192
    {
7✔
193
        // Deserialize from u8 or string
194
        let value: u8 = Deserialize::deserialize(deserializer)?;
7✔
195
        match value {
7✔
196
            1 => Ok(DrawType::Points),
1✔
197
            2 => Ok(DrawType::Lines),
1✔
198
            3 => Ok(DrawType::Polygons),
1✔
199
            4 => Ok(DrawType::Points3D),
1✔
200
            5 => Ok(DrawType::Lines3D),
1✔
201
            6 => Ok(DrawType::Polygons3D),
1✔
202
            _ => Err(serde::de::Error::custom(format!(
1✔
203
                "unknown DrawType variant: {}",
1✔
204
                value
1✔
205
            ))),
1✔
206
        }
207
    }
7✔
208
}
209

210
// Shapes exist solely to deconstruct and rebuild objects.
211
//
212
// Shape limitations:
213
// - all keys are strings.
214
// - all values are either:
215
// - - primitive types: strings, numbers (f32, f64, u64, i64), true, false, or null
216
// - - sub types: an array of a shape or a nested object which is itself a shape
217
// - - if the sub type is an array, ensure all elements are of the same type
218
// The interfaces below help describe how shapes are built by the user.
219

220
/// Primitive types that can be found in a shape
221
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
8✔
222
#[serde(rename_all = "lowercase")]
223
pub enum PrimitiveShape {
224
    /// String type utf8 encoded
225
    String,
226
    /// unsigned 64 bit integer
227
    U64,
228
    /// signed 64 bit integer
229
    I64,
230
    /// floating point number
231
    F32,
232
    /// double precision floating point number
233
    F64,
234
    /// boolean
235
    Bool,
236
    /// null
237
    Null,
238
}
239

240
/// Arrays may contain either a primitive or an object whose values are primitives
241
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
242
#[serde(untagged)]
243
pub enum ShapePrimitiveType {
244
    /// Primitive type
245
    Primitive(PrimitiveShape),
246
    /// Nested shape that can only contain primitives
247
    NestedPrimitive(BTreeMap<String, PrimitiveShape>),
248
}
249

250
/// Shape types that can be found in a shapes object.
251
/// Either a primitive, an array containing any type, or a nested shape.
252
/// If the type is an array, all elements must be the same type
253
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
254
#[serde(untagged)]
255
pub enum ShapeType {
256
    /// Primitive type
257
    Primitive(PrimitiveShape),
258
    /// Nested shape that can only contain primitives
259
    Array(Vec<ShapePrimitiveType>),
260
    /// Nested shape
261
    Nested(Shape),
262
}
263

264
/// The Shape Object
265
pub type Shape = BTreeMap<String, ShapeType>;
266

267
/// Each layer has metadata associated with it. Defined as blueprints pre-construction of vector data.
268
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
×
269
pub struct LayerMetaData {
270
    /// The description of the layer
271
    #[serde(skip_serializing_if = "Option::is_none")]
272
    pub description: Option<String>,
273
    /// the lowest zoom level at which the layer is available
274
    pub minzoom: u8,
275
    /// the highest zoom level at which the layer is available
276
    pub maxzoom: u8,
277
    /// The draw types that can be found in this layer
278
    pub draw_types: Vec<DrawType>,
279
    /// The shape that can be found in this layer
280
    pub shape: Shape,
281
    /// The shape used inside features that can be found in this layer
282
    #[serde(skip_serializing_if = "Option::is_none", rename = "mShape")]
283
    pub m_shape: Option<Shape>,
284
}
285

286
/// Each layer has metadata associated with it. Defined as blueprints pre-construction of vector data.
287
pub type LayersMetaData = BTreeMap<String, LayerMetaData>;
288

289
/// Tilestats is simply a tracker to see where most of the tiles live
290
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
11✔
291
pub struct TileStatsMetadata {
292
    /// total number of tiles
293
    #[serde(default)]
294
    pub total: u64,
295
    /// number of tiles for face 0
296
    #[serde(rename = "0", default)]
297
    pub total_0: u64,
298
    /// number of tiles for face 1
299
    #[serde(rename = "1", default)]
300
    pub total_1: u64,
301
    /// number of tiles for face 2
302
    #[serde(rename = "2", default)]
303
    pub total_2: u64,
304
    /// number of tiles for face 3
305
    #[serde(rename = "3", default)]
306
    pub total_3: u64,
307
    /// number of tiles for face 4
308
    #[serde(rename = "4", default)]
309
    pub total_4: u64,
310
    /// number of tiles for face 5
311
    #[serde(rename = "5", default)]
312
    pub total_5: u64,
313
}
314
impl TileStatsMetadata {
315
    /// Access the total number of tiles for a given face
316
    pub fn get(&self, face: Face) -> u64 {
12✔
317
        match face {
12✔
318
            Face::Face0 => self.total_0,
2✔
319
            Face::Face1 => self.total_1,
2✔
320
            Face::Face2 => self.total_2,
2✔
321
            Face::Face3 => self.total_3,
2✔
322
            Face::Face4 => self.total_4,
2✔
323
            Face::Face5 => self.total_5,
2✔
324
        }
325
    }
12✔
326

327
    /// Increment the total number of tiles for a given face and also the grand total
328
    pub fn increment(&mut self, face: Face) {
7✔
329
        match face {
7✔
330
            Face::Face0 => self.total_0 += 1,
1✔
331
            Face::Face1 => self.total_1 += 1,
2✔
332
            Face::Face2 => self.total_2 += 1,
1✔
333
            Face::Face3 => self.total_3 += 1,
1✔
334
            Face::Face4 => self.total_4 += 1,
1✔
335
            Face::Face5 => self.total_5 += 1,
1✔
336
        }
337
        self.total += 1;
7✔
338
    }
7✔
339
}
340

341
/// Attribution data is stored in an object.
342
/// The key is the name of the attribution, and the value is the link
343
pub type Attribution = BTreeMap<String, String>;
344

345
/// Track the S2 tile bounds of each face and zoom
346
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
7✔
347
pub struct FaceBounds {
348
    // facesbounds[face][zoom] = [...]
349
    /// Tile bounds for face 0 at each zoom
350
    #[serde(rename = "0")]
351
    pub face0: BTreeMap<u8, TileBounds>,
352
    /// Tile bounds for face 1 at each zoom
353
    #[serde(rename = "1")]
354
    pub face1: BTreeMap<u8, TileBounds>,
355
    /// Tile bounds for face 2 at each zoom
356
    #[serde(rename = "2")]
357
    pub face2: BTreeMap<u8, TileBounds>,
358
    /// Tile bounds for face 3 at each zoom
359
    #[serde(rename = "3")]
360
    pub face3: BTreeMap<u8, TileBounds>,
361
    /// Tile bounds for face 4 at each zoom
362
    #[serde(rename = "4")]
363
    pub face4: BTreeMap<u8, TileBounds>,
364
    /// Tile bounds for face 5 at each zoom
365
    #[serde(rename = "5")]
366
    pub face5: BTreeMap<u8, TileBounds>,
367
}
368
impl FaceBounds {
369
    /// Access the tile bounds for a given face and zoom
370
    pub fn get(&self, face: Face) -> &BTreeMap<u8, TileBounds> {
6✔
371
        match face {
6✔
372
            Face::Face0 => &self.face0,
1✔
373
            Face::Face1 => &self.face1,
1✔
374
            Face::Face2 => &self.face2,
1✔
375
            Face::Face3 => &self.face3,
1✔
376
            Face::Face4 => &self.face4,
1✔
377
            Face::Face5 => &self.face5,
1✔
378
        }
379
    }
6✔
380

381
    /// Access the mutable tile bounds for a given face and zoom
382
    pub fn get_mut(&mut self, face: Face) -> &mut BTreeMap<u8, TileBounds> {
7✔
383
        match face {
7✔
384
            Face::Face0 => &mut self.face0,
1✔
385
            Face::Face1 => &mut self.face1,
2✔
386
            Face::Face2 => &mut self.face2,
1✔
387
            Face::Face3 => &mut self.face3,
1✔
388
            Face::Face4 => &mut self.face4,
1✔
389
            Face::Face5 => &mut self.face5,
1✔
390
        }
391
    }
7✔
392
}
393

394
/// Track the WM tile bounds of each zoom
395
/// `[zoom: number]: BBox`
396
pub type WMBounds = BTreeMap<u8, TileBounds>;
397

398
/// Check the source type of the layer
399
#[derive(Serialize, Debug, Default, Clone, PartialEq)]
400
#[serde(rename_all = "lowercase")]
401
pub enum SourceType {
402
    /// Vector data
403
    #[default]
404
    Vector,
405
    /// Json data
406
    Json,
407
    /// Raster data
408
    Raster,
409
    /// Raster DEM data
410
    #[serde(rename = "raster-dem")]
411
    RasterDem,
412
    /// Sensor data
413
    Sensor,
414
    /// Marker data
415
    Markers,
416
    /// Unknown source type
417
    Unknown,
418
}
419
impl From<&str> for SourceType {
420
    fn from(source_type: &str) -> Self {
17✔
421
        match source_type {
17✔
422
            "vector" => SourceType::Vector,
17✔
423
            "json" => SourceType::Json,
13✔
424
            "raster" => SourceType::Raster,
11✔
425
            "raster-dem" => SourceType::RasterDem,
9✔
426
            "sensor" => SourceType::Sensor,
7✔
427
            "markers" => SourceType::Markers,
5✔
428
            _ => SourceType::Unknown,
3✔
429
        }
430
    }
17✔
431
}
432
impl<'de> Deserialize<'de> for SourceType {
433
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
8✔
434
    where
8✔
435
        D: Deserializer<'de>,
8✔
436
    {
8✔
437
        // Deserialize from a string
438
        let s: String = Deserialize::deserialize(deserializer)?;
8✔
439
        Ok(SourceType::from(s.as_str()))
8✔
440
    }
8✔
441
}
442

443
/// Store the encoding of the data
444
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
8✔
445
#[serde(rename_all = "lowercase")]
446
pub enum Encoding {
447
    /// No encoding
448
    #[default]
449
    None = 0,
450
    /// Gzip encoding
451
    Gzip = 1,
452
    /// Brotli encoding
453
    #[serde(rename = "br")]
454
    Brotli = 2,
455
    /// Zstd encoding
456
    Zstd = 3,
457
}
458
impl From<u8> for Encoding {
459
    fn from(encoding: u8) -> Self {
4✔
460
        match encoding {
4✔
461
            1 => Encoding::Gzip,
1✔
462
            2 => Encoding::Brotli,
1✔
463
            3 => Encoding::Zstd,
1✔
464
            _ => Encoding::None,
1✔
465
        }
466
    }
4✔
467
}
468
impl From<Encoding> for u8 {
469
    fn from(encoding: Encoding) -> Self {
4✔
470
        match encoding {
4✔
471
            Encoding::Gzip => 1,
1✔
472
            Encoding::Brotli => 2,
1✔
473
            Encoding::Zstd => 3,
1✔
474
            Encoding::None => 0,
1✔
475
        }
476
    }
4✔
477
}
478
impl From<Encoding> for &str {
479
    fn from(encoding: Encoding) -> Self {
4✔
480
        match encoding {
4✔
481
            Encoding::Gzip => "gzip",
1✔
482
            Encoding::Brotli => "br",
1✔
483
            Encoding::Zstd => "zstd",
1✔
484
            Encoding::None => "none",
1✔
485
        }
486
    }
4✔
487
}
488
impl From<&str> for Encoding {
489
    fn from(encoding: &str) -> Self {
6✔
490
        match encoding {
6✔
491
            "gzip" => Encoding::Gzip,
6✔
492
            "br" => Encoding::Brotli,
5✔
493
            "zstd" => Encoding::Zstd,
4✔
494
            _ => Encoding::None,
3✔
495
        }
496
    }
6✔
497
}
498

499
/// Old spec tracks basic vector data
500
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
6✔
501
pub struct VectorLayer {
502
    /// The id of the layer
503
    pub id: String,
504
    /// The description of the layer
505
    #[serde(skip_serializing_if = "Option::is_none")]
506
    pub description: Option<String>,
507
    /// The min zoom of the layer
508
    #[serde(skip_serializing_if = "Option::is_none")]
509
    pub minzoom: Option<u8>,
510
    /// The max zoom of the layer
511
    #[serde(skip_serializing_if = "Option::is_none")]
512
    pub maxzoom: Option<u8>,
513
    /// Information about each field property
514
    pub fields: BTreeMap<String, String>,
515
}
516

517
/// Default S2 tile scheme is `fzxy`
518
/// Default Web Mercator tile scheme is `xyz`
519
/// Adding a t prefix to the scheme will change the request to be time sensitive
520
/// TMS is an oudated version that is not supported by s2maps-gpu
521
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
×
522
#[serde(rename_all = "lowercase")]
523
pub enum Scheme {
524
    /// The default scheme with faces (S2)
525
    #[default]
526
    Fzxy,
527
    /// The time sensitive scheme with faces (S2)
528
    Tfzxy,
529
    /// The basic scheme (Web Mercator)
530
    Xyz,
531
    /// The time sensitive basic scheme (Web Mercator)
532
    Txyz,
533
    /// The TMS scheme
534
    Tms,
535
}
536
impl From<&str> for Scheme {
537
    fn from(scheme: &str) -> Self {
7✔
538
        match scheme {
7✔
539
            "fzxy" => Scheme::Fzxy,
7✔
540
            "tfzxy" => Scheme::Tfzxy,
4✔
541
            "xyz" => Scheme::Xyz,
3✔
542
            "txyz" => Scheme::Txyz,
2✔
543
            _ => Scheme::Tms,
1✔
544
        }
545
    }
7✔
546
}
547
impl From<Scheme> for &str {
548
    fn from(scheme: Scheme) -> Self {
5✔
549
        match scheme {
5✔
550
            Scheme::Fzxy => "fzxy",
1✔
551
            Scheme::Tfzxy => "tfzxy",
1✔
552
            Scheme::Xyz => "xyz",
1✔
553
            Scheme::Txyz => "txyz",
1✔
554
            Scheme::Tms => "tms",
1✔
555
        }
556
    }
5✔
557
}
558

559
/// Store where the center of the data lives
560
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
×
561
pub struct Center {
562
    /// The longitude of the center
563
    pub lon: f64,
564
    /// The latitude of the center
565
    pub lat: f64,
566
    /// The zoom of the center
567
    pub zoom: u8,
568
}
569

570
/// Metadata for the tile data
571
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
9✔
572
pub struct Metadata {
573
    /// The version of the s2-tilejson spec
574
    #[serde(default)]
575
    pub s2tilejson: String,
576
    /// The version of the data
577
    #[serde(default)]
578
    pub version: String,
579
    /// The name of the data
580
    #[serde(default)]
581
    pub name: String,
582
    /// The scheme of the data
583
    #[serde(default)]
584
    pub scheme: Scheme,
585
    /// The description of the data
586
    #[serde(default)]
587
    pub description: String,
588
    /// The type of the data
589
    #[serde(rename = "type", default)]
590
    pub type_: SourceType,
591
    /// The extension to use when requesting a tile
592
    #[serde(default)]
593
    pub extension: String,
594
    /// The encoding of the data
595
    #[serde(default)]
596
    pub encoding: Encoding,
597
    /// List of faces that have data
598
    #[serde(default)]
599
    pub faces: Vec<Face>,
600
    /// WM Tile fetching bounds. Helpful to not make unecessary requests for tiles we know don't exist
601
    #[serde(default)]
602
    pub bounds: WMBounds,
603
    /// S2 Tile fetching bounds. Helpful to not make unecessary requests for tiles we know don't exist
604
    #[serde(default)]
605
    pub facesbounds: FaceBounds,
606
    /// minzoom at which to request tiles. [default=0]
607
    #[serde(default)]
608
    pub minzoom: u8,
609
    /// maxzoom at which to request tiles. [default=27]
610
    #[serde(default)]
611
    pub maxzoom: u8,
612
    /// The center of the data
613
    #[serde(default)]
614
    pub center: Center,
615
    /// { ['human readable string']: 'href' }
616
    #[serde(default)]
617
    pub attribution: Attribution,
618
    /// Track layer metadata
619
    #[serde(default)]
620
    pub layers: LayersMetaData,
621
    /// Track tile stats for each face and total overall
622
    #[serde(default)]
623
    pub tilestats: TileStatsMetadata,
624
    /// Old spec, track basic layer metadata
625
    #[serde(default)]
626
    pub vector_layers: Vec<VectorLayer>,
627
}
628
impl Default for Metadata {
629
    fn default() -> Self {
1✔
630
        Self {
1✔
631
            s2tilejson: "1.0.0".into(),
1✔
632
            version: "1.0.0".into(),
1✔
633
            name: "default".into(),
1✔
634
            scheme: Scheme::default(),
1✔
635
            description: "Built with s2maps-cli".into(),
1✔
636
            type_: SourceType::default(),
1✔
637
            extension: "pbf".into(),
1✔
638
            encoding: Encoding::default(),
1✔
639
            faces: Vec::new(),
1✔
640
            bounds: WMBounds::default(),
1✔
641
            facesbounds: FaceBounds::default(),
1✔
642
            minzoom: 0,
1✔
643
            maxzoom: 27,
1✔
644
            center: Center::default(),
1✔
645
            attribution: BTreeMap::new(),
1✔
646
            layers: LayersMetaData::default(),
1✔
647
            tilestats: TileStatsMetadata::default(),
1✔
648
            vector_layers: Vec::new(),
1✔
649
        }
1✔
650
    }
1✔
651
}
652

653
/// Builder for the metadata
654
#[derive(Debug, Clone)]
655
pub struct MetadataBuilder {
656
    lon_lat_bounds: LonLatBounds,
657
    faces: BTreeSet<Face>,
658
    metadata: Metadata,
659
}
660
impl Default for MetadataBuilder {
661
    fn default() -> Self {
1✔
662
        MetadataBuilder {
1✔
663
            lon_lat_bounds: BBox {
1✔
664
                left: f64::INFINITY,
1✔
665
                bottom: f64::INFINITY,
1✔
666
                right: -f64::INFINITY,
1✔
667
                top: -f64::INFINITY,
1✔
668
            },
1✔
669
            faces: BTreeSet::new(),
1✔
670
            metadata: Metadata {
1✔
671
                minzoom: 30,
1✔
672
                maxzoom: 0,
1✔
673
                ..Metadata::default()
1✔
674
            },
1✔
675
        }
1✔
676
    }
1✔
677
}
678
impl MetadataBuilder {
679
    /// Commit the metadata and take ownership
680
    pub fn commit(&mut self) -> Metadata {
1✔
681
        // set the center
1✔
682
        self.update_center();
1✔
683
        // set the faces
684
        for face in &self.faces {
3✔
685
            self.metadata.faces.push(*face);
2✔
686
        }
2✔
687
        // return the result
688
        self.metadata.to_owned()
1✔
689
    }
1✔
690

691
    /// Set the name
692
    pub fn set_name(&mut self, name: String) {
1✔
693
        self.metadata.name = name;
1✔
694
    }
1✔
695

696
    /// Set the scheme of the data. [default=fzxy]
697
    pub fn set_scheme(&mut self, scheme: Scheme) {
1✔
698
        self.metadata.scheme = scheme;
1✔
699
    }
1✔
700

701
    /// Set the extension of the data. [default=pbf]
702
    pub fn set_extension(&mut self, extension: String) {
1✔
703
        self.metadata.extension = extension;
1✔
704
    }
1✔
705

706
    /// Set the type of the data. [default=vector]
707
    pub fn set_type(&mut self, type_: SourceType) {
1✔
708
        self.metadata.type_ = type_;
1✔
709
    }
1✔
710

711
    /// Set the version of the data
712
    pub fn set_version(&mut self, version: String) {
1✔
713
        self.metadata.version = version;
1✔
714
    }
1✔
715

716
    /// Set the description of the data
717
    pub fn set_description(&mut self, description: String) {
1✔
718
        self.metadata.description = description;
1✔
719
    }
1✔
720

721
    /// Set the encoding of the data. [default=none]
722
    pub fn set_encoding(&mut self, encoding: Encoding) {
1✔
723
        self.metadata.encoding = encoding;
1✔
724
    }
1✔
725

726
    /// add an attribution
727
    pub fn add_attribution(&mut self, display_name: &str, href: &str) {
1✔
728
        self.metadata
1✔
729
            .attribution
1✔
730
            .insert(display_name.into(), href.into());
1✔
731
    }
1✔
732

733
    /// Add the layer metadata
734
    pub fn add_layer(&mut self, name: &str, layer: &LayerMetaData) {
1✔
735
        // Only insert if the key does not exist
1✔
736
        if self
1✔
737
            .metadata
1✔
738
            .layers
1✔
739
            .entry(name.into())
1✔
740
            .or_insert(layer.clone())
1✔
741
            .eq(&layer)
1✔
742
        {
1✔
743
            // Also add to vector_layers only if the key was not present and the insert was successful
1✔
744
            self.metadata.vector_layers.push(VectorLayer {
1✔
745
                id: name.into(), // No need to clone again; we use the moved value
1✔
746
                description: layer.description.clone(),
1✔
747
                minzoom: Some(layer.minzoom),
1✔
748
                maxzoom: Some(layer.maxzoom),
1✔
749
                fields: BTreeMap::new(),
1✔
750
            });
1✔
751
        }
1✔
752
        // update minzoom and maxzoom
753
        if layer.minzoom < self.metadata.minzoom {
1✔
754
            self.metadata.minzoom = layer.minzoom;
1✔
755
        }
1✔
756
        if layer.maxzoom > self.metadata.maxzoom {
1✔
757
            self.metadata.maxzoom = layer.maxzoom;
1✔
758
        }
1✔
759
    }
1✔
760

761
    /// Add the WM tile metadata
762
    pub fn add_tile_wm(&mut self, zoom: u8, x: u32, y: u32, ll_bounds: &LonLatBounds) {
1✔
763
        self.metadata.tilestats.total += 1;
1✔
764
        self.faces.insert(Face::Face0);
1✔
765
        self.add_bounds_wm(zoom, x, y);
1✔
766
        self.update_lon_lat_bounds(ll_bounds);
1✔
767
    }
1✔
768

769
    /// Add the S2 tile metadata
770
    pub fn add_tile_s2(&mut self, face: Face, zoom: u8, x: u32, y: u32, ll_bounds: &LonLatBounds) {
1✔
771
        self.metadata.tilestats.increment(face);
1✔
772
        self.faces.insert(face);
1✔
773
        self.add_bounds_s2(face, zoom, x, y);
1✔
774
        self.update_lon_lat_bounds(ll_bounds);
1✔
775
    }
1✔
776

777
    /// Update the center now that all tiles have been added
778
    fn update_center(&mut self) {
1✔
779
        let Metadata {
1✔
780
            minzoom, maxzoom, ..
1✔
781
        } = self.metadata;
1✔
782
        let BBox {
1✔
783
            left,
1✔
784
            bottom,
1✔
785
            right,
1✔
786
            top,
1✔
787
        } = self.lon_lat_bounds;
1✔
788
        self.metadata.center.lon = (left + right) / 2.0;
1✔
789
        self.metadata.center.lat = (bottom + top) / 2.0;
1✔
790
        self.metadata.center.zoom = (minzoom + maxzoom) >> 1;
1✔
791
    }
1✔
792

793
    /// Add the bounds of the tile for WM data
794
    fn add_bounds_wm(&mut self, zoom: u8, x: u32, y: u32) {
1✔
795
        let x = x as u64;
1✔
796
        let y = y as u64;
1✔
797
        let bbox = self.metadata.bounds.entry(zoom).or_insert(BBox {
1✔
798
            left: u64::MAX,
1✔
799
            bottom: u64::MAX,
1✔
800
            right: 0,
1✔
801
            top: 0,
1✔
802
        });
1✔
803

1✔
804
        bbox.left = bbox.left.min(x);
1✔
805
        bbox.bottom = bbox.bottom.min(y);
1✔
806
        bbox.right = bbox.right.max(x);
1✔
807
        bbox.top = bbox.top.max(y);
1✔
808
    }
1✔
809

810
    /// Add the bounds of the tile for S2 data
811
    fn add_bounds_s2(&mut self, face: Face, zoom: u8, x: u32, y: u32) {
1✔
812
        let x = x as u64;
1✔
813
        let y = y as u64;
1✔
814
        let bbox = self
1✔
815
            .metadata
1✔
816
            .facesbounds
1✔
817
            .get_mut(face)
1✔
818
            .entry(zoom)
1✔
819
            .or_insert(BBox {
1✔
820
                left: u64::MAX,
1✔
821
                bottom: u64::MAX,
1✔
822
                right: 0,
1✔
823
                top: 0,
1✔
824
            });
1✔
825

1✔
826
        bbox.left = bbox.left.min(x);
1✔
827
        bbox.bottom = bbox.bottom.min(y);
1✔
828
        bbox.right = bbox.right.max(x);
1✔
829
        bbox.top = bbox.top.max(y);
1✔
830
    }
1✔
831

832
    /// Update the lon-lat bounds so eventually we can find the center point of the data
833
    fn update_lon_lat_bounds(&mut self, ll_bounds: &LonLatBounds) {
2✔
834
        self.lon_lat_bounds.left = ll_bounds.left.min(self.lon_lat_bounds.left);
2✔
835
        self.lon_lat_bounds.bottom = ll_bounds.bottom.min(self.lon_lat_bounds.bottom);
2✔
836
        self.lon_lat_bounds.right = ll_bounds.right.max(self.lon_lat_bounds.right);
2✔
837
        self.lon_lat_bounds.top = ll_bounds.top.max(self.lon_lat_bounds.top);
2✔
838
    }
2✔
839
}
840

841
#[cfg(test)]
842
mod tests {
843
    use super::*;
844

845
    #[test]
846
    fn it_works() {
1✔
847
        let mut meta_builder = MetadataBuilder::default();
1✔
848

1✔
849
        // on initial use be sure to update basic metadata:
1✔
850
        meta_builder.set_name("OSM".into());
1✔
851
        meta_builder.set_description("A free editable map of the whole world.".into());
1✔
852
        meta_builder.set_version("1.0.0".into());
1✔
853
        meta_builder.set_scheme("fzxy".into()); // 'fzxy' | 'tfzxy' | 'xyz' | 'txyz' | 'tms'
1✔
854
        meta_builder.set_type("vector".into()); // 'vector' | 'json' | 'raster' | 'raster-dem' | 'sensor' | 'markers'
1✔
855
        meta_builder.set_encoding("none".into()); // 'gz' | 'br' | 'none'
1✔
856
        meta_builder.set_extension("pbf".into());
1✔
857
        meta_builder.add_attribution("OpenStreetMap", "https://www.openstreetmap.org/copyright/");
1✔
858

1✔
859
        // Vector Specific: add layers based on how you want to parse data from a source:
1✔
860
        let shape_str = r#"
1✔
861
        {
1✔
862
            "class": "string",
1✔
863
            "offset": "f64",
1✔
864
            "info": {
1✔
865
                "name": "string",
1✔
866
                "value": "i64"
1✔
867
            }
1✔
868
        }
1✔
869
        "#;
1✔
870
        let shape: Shape =
1✔
871
            serde_json::from_str(shape_str).unwrap_or_else(|e| panic!("ERROR: {}", e));
1✔
872
        let layer = LayerMetaData {
1✔
873
            minzoom: 0,
1✔
874
            maxzoom: 13,
1✔
875
            description: Some("water_lines".into()),
1✔
876
            draw_types: Vec::from(&[DrawType::Lines]),
1✔
877
            shape: shape.clone(),
1✔
878
            m_shape: None,
1✔
879
        };
1✔
880
        meta_builder.add_layer("water_lines", &layer);
1✔
881

1✔
882
        // as you build tiles, add the tiles metadata:
1✔
883
        // WM:
1✔
884
        meta_builder.add_tile_wm(
1✔
885
            0,
1✔
886
            0,
1✔
887
            0,
1✔
888
            &LonLatBounds {
1✔
889
                left: -60.0,
1✔
890
                bottom: -20.0,
1✔
891
                right: 5.0,
1✔
892
                top: 60.0,
1✔
893
            },
1✔
894
        );
1✔
895
        // S2:
1✔
896
        meta_builder.add_tile_s2(
1✔
897
            Face::Face1,
1✔
898
            5,
1✔
899
            22,
1✔
900
            37,
1✔
901
            &LonLatBounds {
1✔
902
                left: -120.0,
1✔
903
                bottom: -7.0,
1✔
904
                right: 44.0,
1✔
905
                top: 72.0,
1✔
906
            },
1✔
907
        );
1✔
908

1✔
909
        // finally to get the resulting metadata:
1✔
910
        let resulting_metadata: Metadata = meta_builder.commit();
1✔
911

1✔
912
        assert_eq!(
1✔
913
            resulting_metadata,
1✔
914
            Metadata {
1✔
915
                name: "OSM".into(),
1✔
916
                description: "A free editable map of the whole world.".into(),
1✔
917
                version: "1.0.0".into(),
1✔
918
                scheme: "fzxy".into(),
1✔
919
                type_: "vector".into(),
1✔
920
                encoding: "none".into(),
1✔
921
                extension: "pbf".into(),
1✔
922
                attribution: BTreeMap::from([(
1✔
923
                    "OpenStreetMap".into(),
1✔
924
                    "https://www.openstreetmap.org/copyright/".into()
1✔
925
                ),]),
1✔
926
                bounds: BTreeMap::from([(
1✔
927
                    0,
1✔
928
                    TileBounds {
1✔
929
                        left: 0,
1✔
930
                        bottom: 0,
1✔
931
                        right: 0,
1✔
932
                        top: 0
1✔
933
                    }
1✔
934
                ),]),
1✔
935
                faces: Vec::from(&[Face::Face0, Face::Face1]),
1✔
936
                facesbounds: FaceBounds {
1✔
937
                    face0: BTreeMap::new(),
1✔
938
                    face1: BTreeMap::from([(
1✔
939
                        5,
1✔
940
                        TileBounds {
1✔
941
                            left: 22,
1✔
942
                            bottom: 37,
1✔
943
                            right: 22,
1✔
944
                            top: 37
1✔
945
                        }
1✔
946
                    ),]),
1✔
947
                    face2: BTreeMap::new(),
1✔
948
                    face3: BTreeMap::new(),
1✔
949
                    face4: BTreeMap::new(),
1✔
950
                    face5: BTreeMap::new(),
1✔
951
                },
1✔
952
                minzoom: 0,
1✔
953
                maxzoom: 13,
1✔
954
                center: Center {
1✔
955
                    lon: -38.0,
1✔
956
                    lat: 26.0,
1✔
957
                    zoom: 6
1✔
958
                },
1✔
959
                tilestats: TileStatsMetadata {
1✔
960
                    total: 2,
1✔
961
                    total_0: 0,
1✔
962
                    total_1: 1,
1✔
963
                    total_2: 0,
1✔
964
                    total_3: 0,
1✔
965
                    total_4: 0,
1✔
966
                    total_5: 0,
1✔
967
                },
1✔
968
                layers: BTreeMap::from([(
1✔
969
                    "water_lines".into(),
1✔
970
                    LayerMetaData {
1✔
971
                        description: Some("water_lines".into()),
1✔
972
                        minzoom: 0,
1✔
973
                        maxzoom: 13,
1✔
974
                        draw_types: Vec::from(&[DrawType::Lines]),
1✔
975
                        shape: BTreeMap::from([
1✔
976
                            ("class".into(), ShapeType::Primitive(PrimitiveShape::String)),
1✔
977
                            ("offset".into(), ShapeType::Primitive(PrimitiveShape::F64)),
1✔
978
                            (
1✔
979
                                "info".into(),
1✔
980
                                ShapeType::Nested(BTreeMap::from([
1✔
981
                                    ("name".into(), ShapeType::Primitive(PrimitiveShape::String)),
1✔
982
                                    ("value".into(), ShapeType::Primitive(PrimitiveShape::I64)),
1✔
983
                                ]))
1✔
984
                            ),
1✔
985
                        ]),
1✔
986
                        m_shape: None,
1✔
987
                    }
1✔
988
                )]),
1✔
989
                s2tilejson: "1.0.0".into(),
1✔
990
                vector_layers: Vec::from([VectorLayer {
1✔
991
                    id: "water_lines".into(),
1✔
992
                    description: Some("water_lines".into()),
1✔
993
                    minzoom: Some(0),
1✔
994
                    maxzoom: Some(13),
1✔
995
                    fields: BTreeMap::new()
1✔
996
                }]),
1✔
997
            }
1✔
998
        );
1✔
999
    }
1✔
1000

1001
    #[test]
1002
    fn test_face() {
1✔
1003
        assert_eq!(Face::Face0, Face::from(0));
1✔
1004
        assert_eq!(Face::Face1, Face::from(1));
1✔
1005
        assert_eq!(Face::Face2, Face::from(2));
1✔
1006
        assert_eq!(Face::Face3, Face::from(3));
1✔
1007
        assert_eq!(Face::Face4, Face::from(4));
1✔
1008
        assert_eq!(Face::Face5, Face::from(5));
1✔
1009

1010
        assert_eq!(0, u8::from(Face::Face0));
1✔
1011
        assert_eq!(1, u8::from(Face::Face1));
1✔
1012
        assert_eq!(2, u8::from(Face::Face2));
1✔
1013
        assert_eq!(3, u8::from(Face::Face3));
1✔
1014
        assert_eq!(4, u8::from(Face::Face4));
1✔
1015
        assert_eq!(5, u8::from(Face::Face5));
1✔
1016
    }
1✔
1017

1018
    #[test]
1019
    fn test_bbox() {
1✔
1020
        let bbox: BBox = BBox {
1✔
1021
            left: 0.0,
1✔
1022
            bottom: 0.0,
1✔
1023
            right: 0.0,
1✔
1024
            top: 0.0,
1✔
1025
        };
1✔
1026
        // serialize to JSON and back
1✔
1027
        let json = serde_json::to_string(&bbox).unwrap();
1✔
1028
        assert_eq!(json, r#"[0.0,0.0,0.0,0.0]"#);
1✔
1029
        let bbox2: BBox = serde_json::from_str(&json).unwrap();
1✔
1030
        assert_eq!(bbox, bbox2);
1✔
1031
    }
1✔
1032

1033
    // TileStatsMetadata
1034
    #[test]
1035
    fn test_tilestats() {
1✔
1036
        let mut tilestats = TileStatsMetadata {
1✔
1037
            total: 2,
1✔
1038
            total_0: 0,
1✔
1039
            total_1: 1,
1✔
1040
            total_2: 0,
1✔
1041
            total_3: 0,
1✔
1042
            total_4: 0,
1✔
1043
            total_5: 0,
1✔
1044
        };
1✔
1045
        // serialize to JSON and back
1✔
1046
        let json = serde_json::to_string(&tilestats).unwrap();
1✔
1047
        assert_eq!(json, r#"{"total":2,"0":0,"1":1,"2":0,"3":0,"4":0,"5":0}"#);
1✔
1048
        let tilestats2: TileStatsMetadata = serde_json::from_str(&json).unwrap();
1✔
1049
        assert_eq!(tilestats, tilestats2);
1✔
1050

1051
        // get0
1052
        assert_eq!(tilestats.get(0.into()), 0);
1✔
1053
        // increment0
1054
        tilestats.increment(0.into());
1✔
1055
        assert_eq!(tilestats.get(0.into()), 1);
1✔
1056

1057
        // get 1
1058
        assert_eq!(tilestats.get(1.into()), 1);
1✔
1059
        // increment 1
1060
        tilestats.increment(1.into());
1✔
1061
        assert_eq!(tilestats.get(1.into()), 2);
1✔
1062

1063
        // get 2
1064
        assert_eq!(tilestats.get(2.into()), 0);
1✔
1065
        // increment 2
1066
        tilestats.increment(2.into());
1✔
1067
        assert_eq!(tilestats.get(2.into()), 1);
1✔
1068

1069
        // get 3
1070
        assert_eq!(tilestats.get(3.into()), 0);
1✔
1071
        // increment 3
1072
        tilestats.increment(3.into());
1✔
1073
        assert_eq!(tilestats.get(3.into()), 1);
1✔
1074

1075
        // get 4
1076
        assert_eq!(tilestats.get(4.into()), 0);
1✔
1077
        // increment 4
1078
        tilestats.increment(4.into());
1✔
1079
        assert_eq!(tilestats.get(4.into()), 1);
1✔
1080

1081
        // get 5
1082
        assert_eq!(tilestats.get(5.into()), 0);
1✔
1083
        // increment 5
1084
        tilestats.increment(5.into());
1✔
1085
        assert_eq!(tilestats.get(5.into()), 1);
1✔
1086
    }
1✔
1087

1088
    // FaceBounds
1089
    #[test]
1090
    fn test_facebounds() {
1✔
1091
        let mut facebounds = FaceBounds::default();
1✔
1092
        // get mut
1✔
1093
        let face0 = facebounds.get_mut(0.into());
1✔
1094
        face0.insert(
1✔
1095
            0,
1✔
1096
            TileBounds {
1✔
1097
                left: 0,
1✔
1098
                bottom: 0,
1✔
1099
                right: 0,
1✔
1100
                top: 0,
1✔
1101
            },
1✔
1102
        );
1✔
1103
        // get mut 1
1✔
1104
        let face1 = facebounds.get_mut(1.into());
1✔
1105
        face1.insert(
1✔
1106
            0,
1✔
1107
            TileBounds {
1✔
1108
                left: 0,
1✔
1109
                bottom: 0,
1✔
1110
                right: 1,
1✔
1111
                top: 1,
1✔
1112
            },
1✔
1113
        );
1✔
1114
        // get mut 2
1✔
1115
        let face2 = facebounds.get_mut(2.into());
1✔
1116
        face2.insert(
1✔
1117
            0,
1✔
1118
            TileBounds {
1✔
1119
                left: 0,
1✔
1120
                bottom: 0,
1✔
1121
                right: 2,
1✔
1122
                top: 2,
1✔
1123
            },
1✔
1124
        );
1✔
1125
        // get mut 3
1✔
1126
        let face3 = facebounds.get_mut(3.into());
1✔
1127
        face3.insert(
1✔
1128
            0,
1✔
1129
            TileBounds {
1✔
1130
                left: 0,
1✔
1131
                bottom: 0,
1✔
1132
                right: 3,
1✔
1133
                top: 3,
1✔
1134
            },
1✔
1135
        );
1✔
1136
        // get mut 4
1✔
1137
        let face4 = facebounds.get_mut(4.into());
1✔
1138
        face4.insert(
1✔
1139
            0,
1✔
1140
            TileBounds {
1✔
1141
                left: 0,
1✔
1142
                bottom: 0,
1✔
1143
                right: 4,
1✔
1144
                top: 4,
1✔
1145
            },
1✔
1146
        );
1✔
1147
        // get mut 5
1✔
1148
        let face5 = facebounds.get_mut(5.into());
1✔
1149
        face5.insert(
1✔
1150
            0,
1✔
1151
            TileBounds {
1✔
1152
                left: 0,
1✔
1153
                bottom: 0,
1✔
1154
                right: 5,
1✔
1155
                top: 5,
1✔
1156
            },
1✔
1157
        );
1✔
1158

1✔
1159
        // now get for all 5:
1✔
1160
        // get 0
1✔
1161
        assert_eq!(
1✔
1162
            facebounds.get(0.into()).get(&0).unwrap(),
1✔
1163
            &TileBounds {
1✔
1164
                left: 0,
1✔
1165
                bottom: 0,
1✔
1166
                right: 0,
1✔
1167
                top: 0
1✔
1168
            }
1✔
1169
        );
1✔
1170
        // get 1
1171
        assert_eq!(
1✔
1172
            facebounds.get(1.into()).get(&0).unwrap(),
1✔
1173
            &TileBounds {
1✔
1174
                left: 0,
1✔
1175
                bottom: 0,
1✔
1176
                right: 1,
1✔
1177
                top: 1
1✔
1178
            }
1✔
1179
        );
1✔
1180
        // get 2
1181
        assert_eq!(
1✔
1182
            facebounds.get(2.into()).get(&0).unwrap(),
1✔
1183
            &TileBounds {
1✔
1184
                left: 0,
1✔
1185
                bottom: 0,
1✔
1186
                right: 2,
1✔
1187
                top: 2
1✔
1188
            }
1✔
1189
        );
1✔
1190
        // get 3
1191
        assert_eq!(
1✔
1192
            facebounds.get(3.into()).get(&0).unwrap(),
1✔
1193
            &TileBounds {
1✔
1194
                left: 0,
1✔
1195
                bottom: 0,
1✔
1196
                right: 3,
1✔
1197
                top: 3
1✔
1198
            }
1✔
1199
        );
1✔
1200
        // get 4
1201
        assert_eq!(
1✔
1202
            facebounds.get(4.into()).get(&0).unwrap(),
1✔
1203
            &TileBounds {
1✔
1204
                left: 0,
1✔
1205
                bottom: 0,
1✔
1206
                right: 4,
1✔
1207
                top: 4
1✔
1208
            }
1✔
1209
        );
1✔
1210
        // get 5
1211
        assert_eq!(
1✔
1212
            facebounds.get(5.into()).get(&0).unwrap(),
1✔
1213
            &TileBounds {
1✔
1214
                left: 0,
1✔
1215
                bottom: 0,
1✔
1216
                right: 5,
1✔
1217
                top: 5
1✔
1218
            }
1✔
1219
        );
1✔
1220

1221
        // serialize to JSON and back
1222
        let json = serde_json::to_string(&facebounds).unwrap();
1✔
1223
        assert_eq!(json, "{\"0\":{\"0\":[0,0,0,0]},\"1\":{\"0\":[0,0,1,1]},\"2\":{\"0\":[0,0,2,2]},\"3\":{\"0\":[0,0,3,3]},\"4\":{\"0\":[0,0,4,4]},\"5\":{\"0\":[0,0,5,5]}}");
1✔
1224
        let facebounds2 = serde_json::from_str(&json).unwrap();
1✔
1225
        assert_eq!(facebounds, facebounds2);
1✔
1226
    }
1✔
1227

1228
    // DrawType
1229
    #[test]
1230
    fn test_drawtype() {
1✔
1231
        assert_eq!(DrawType::from(1), DrawType::Points);
1✔
1232
        assert_eq!(DrawType::from(2), DrawType::Lines);
1✔
1233
        assert_eq!(DrawType::from(3), DrawType::Polygons);
1✔
1234
        assert_eq!(DrawType::from(4), DrawType::Points3D);
1✔
1235
        assert_eq!(DrawType::from(5), DrawType::Lines3D);
1✔
1236
        assert_eq!(DrawType::from(6), DrawType::Polygons3D);
1✔
1237
        assert_eq!(DrawType::from(7), DrawType::Points);
1✔
1238

1239
        assert_eq!(1, u8::from(DrawType::Points));
1✔
1240
        assert_eq!(2, u8::from(DrawType::Lines));
1✔
1241
        assert_eq!(3, u8::from(DrawType::Polygons));
1✔
1242
        assert_eq!(4, u8::from(DrawType::Points3D));
1✔
1243
        assert_eq!(5, u8::from(DrawType::Lines3D));
1✔
1244
        assert_eq!(6, u8::from(DrawType::Polygons3D));
1✔
1245

1246
        // check json is the number value
1247
        let json = serde_json::to_string(&DrawType::Points).unwrap();
1✔
1248
        assert_eq!(json, "1");
1✔
1249
        let drawtype: DrawType = serde_json::from_str(&json).unwrap();
1✔
1250
        assert_eq!(drawtype, DrawType::Points);
1✔
1251

1252
        let drawtype: DrawType = serde_json::from_str("2").unwrap();
1✔
1253
        assert_eq!(drawtype, DrawType::Lines);
1✔
1254

1255
        let drawtype: DrawType = serde_json::from_str("3").unwrap();
1✔
1256
        assert_eq!(drawtype, DrawType::Polygons);
1✔
1257

1258
        let drawtype: DrawType = serde_json::from_str("4").unwrap();
1✔
1259
        assert_eq!(drawtype, DrawType::Points3D);
1✔
1260

1261
        let drawtype: DrawType = serde_json::from_str("5").unwrap();
1✔
1262
        assert_eq!(drawtype, DrawType::Lines3D);
1✔
1263

1264
        let drawtype: DrawType = serde_json::from_str("6").unwrap();
1✔
1265
        assert_eq!(drawtype, DrawType::Polygons3D);
1✔
1266

1267
        assert!(serde_json::from_str::<DrawType>("7").is_err());
1✔
1268
    }
1✔
1269

1270
    // SourceType
1271
    #[test]
1272
    fn test_sourcetype() {
1✔
1273
        // from string
1✔
1274
        assert_eq!(SourceType::from("vector"), SourceType::Vector);
1✔
1275
        assert_eq!(SourceType::from("json"), SourceType::Json);
1✔
1276
        assert_eq!(SourceType::from("raster"), SourceType::Raster);
1✔
1277
        assert_eq!(SourceType::from("raster-dem"), SourceType::RasterDem);
1✔
1278
        assert_eq!(SourceType::from("sensor"), SourceType::Sensor);
1✔
1279
        assert_eq!(SourceType::from("markers"), SourceType::Markers);
1✔
1280
        assert_eq!(SourceType::from("overlay"), SourceType::Unknown);
1✔
1281

1282
        // json vector
1283
        let json = serde_json::to_string(&SourceType::Vector).unwrap();
1✔
1284
        assert_eq!(json, "\"vector\"");
1✔
1285
        let sourcetype: SourceType = serde_json::from_str(&json).unwrap();
1✔
1286
        assert_eq!(sourcetype, SourceType::Vector);
1✔
1287

1288
        // json json
1289
        let json = serde_json::to_string(&SourceType::Json).unwrap();
1✔
1290
        assert_eq!(json, "\"json\"");
1✔
1291
        let sourcetype: SourceType = serde_json::from_str(&json).unwrap();
1✔
1292
        assert_eq!(sourcetype, SourceType::Json);
1✔
1293

1294
        // json raster
1295
        let json = serde_json::to_string(&SourceType::Raster).unwrap();
1✔
1296
        assert_eq!(json, "\"raster\"");
1✔
1297
        let sourcetype: SourceType = serde_json::from_str(&json).unwrap();
1✔
1298
        assert_eq!(sourcetype, SourceType::Raster);
1✔
1299

1300
        // json raster-dem
1301
        let json = serde_json::to_string(&SourceType::RasterDem).unwrap();
1✔
1302
        assert_eq!(json, "\"raster-dem\"");
1✔
1303
        let sourcetype: SourceType = serde_json::from_str(&json).unwrap();
1✔
1304
        assert_eq!(sourcetype, SourceType::RasterDem);
1✔
1305

1306
        // json sensor
1307
        let json = serde_json::to_string(&SourceType::Sensor).unwrap();
1✔
1308
        assert_eq!(json, "\"sensor\"");
1✔
1309
        let sourcetype: SourceType = serde_json::from_str(&json).unwrap();
1✔
1310
        assert_eq!(sourcetype, SourceType::Sensor);
1✔
1311

1312
        // json markers
1313
        let json = serde_json::to_string(&SourceType::Markers).unwrap();
1✔
1314
        assert_eq!(json, "\"markers\"");
1✔
1315
        let sourcetype: SourceType = serde_json::from_str(&json).unwrap();
1✔
1316
        assert_eq!(sourcetype, SourceType::Markers);
1✔
1317

1318
        // json unknown
1319
        let json = serde_json::to_string(&SourceType::Unknown).unwrap();
1✔
1320
        assert_eq!(json, "\"unknown\"");
1✔
1321
        let sourcetype: SourceType = serde_json::from_str(r#""overlay""#).unwrap();
1✔
1322
        assert_eq!(sourcetype, SourceType::Unknown);
1✔
1323
    }
1✔
1324

1325
    // Encoding
1326
    #[test]
1327
    fn test_encoding() {
1✔
1328
        // from string
1✔
1329
        assert_eq!(Encoding::from("none"), Encoding::None);
1✔
1330
        assert_eq!(Encoding::from("gzip"), Encoding::Gzip);
1✔
1331
        assert_eq!(Encoding::from("br"), Encoding::Brotli);
1✔
1332
        assert_eq!(Encoding::from("zstd"), Encoding::Zstd);
1✔
1333

1334
        // to string
1335
        assert_eq!(core::convert::Into::<&str>::into(Encoding::None), "none");
1✔
1336
        assert_eq!(core::convert::Into::<&str>::into(Encoding::Gzip), "gzip");
1✔
1337
        assert_eq!(core::convert::Into::<&str>::into(Encoding::Brotli), "br");
1✔
1338
        assert_eq!(core::convert::Into::<&str>::into(Encoding::Zstd), "zstd");
1✔
1339

1340
        // from u8
1341
        assert_eq!(Encoding::from(0), Encoding::None);
1✔
1342
        assert_eq!(Encoding::from(1), Encoding::Gzip);
1✔
1343
        assert_eq!(Encoding::from(2), Encoding::Brotli);
1✔
1344
        assert_eq!(Encoding::from(3), Encoding::Zstd);
1✔
1345

1346
        // to u8
1347
        assert_eq!(u8::from(Encoding::None), 0);
1✔
1348
        assert_eq!(u8::from(Encoding::Gzip), 1);
1✔
1349
        assert_eq!(u8::from(Encoding::Brotli), 2);
1✔
1350
        assert_eq!(u8::from(Encoding::Zstd), 3);
1✔
1351

1352
        // json gzip
1353
        let json = serde_json::to_string(&Encoding::Gzip).unwrap();
1✔
1354
        assert_eq!(json, "\"gzip\"");
1✔
1355
        let encoding: Encoding = serde_json::from_str(&json).unwrap();
1✔
1356
        assert_eq!(encoding, Encoding::Gzip);
1✔
1357

1358
        // json br
1359
        let json = serde_json::to_string(&Encoding::Brotli).unwrap();
1✔
1360
        assert_eq!(json, "\"br\"");
1✔
1361
        let encoding: Encoding = serde_json::from_str(&json).unwrap();
1✔
1362
        assert_eq!(encoding, Encoding::Brotli);
1✔
1363

1364
        // json none
1365
        let json = serde_json::to_string(&Encoding::None).unwrap();
1✔
1366
        assert_eq!(json, "\"none\"");
1✔
1367
        let encoding: Encoding = serde_json::from_str(&json).unwrap();
1✔
1368
        assert_eq!(encoding, Encoding::None);
1✔
1369

1370
        // json zstd
1371
        let json = serde_json::to_string(&Encoding::Zstd).unwrap();
1✔
1372
        assert_eq!(json, "\"zstd\"");
1✔
1373
        let encoding: Encoding = serde_json::from_str(&json).unwrap();
1✔
1374
        assert_eq!(encoding, Encoding::Zstd);
1✔
1375
    }
1✔
1376

1377
    // Scheme
1378
    #[test]
1379
    fn test_scheme() {
1✔
1380
        // from string
1✔
1381
        assert_eq!(Scheme::from("fzxy"), Scheme::Fzxy);
1✔
1382
        assert_eq!(Scheme::from("tfzxy"), Scheme::Tfzxy);
1✔
1383
        assert_eq!(Scheme::from("xyz"), Scheme::Xyz);
1✔
1384
        assert_eq!(Scheme::from("txyz"), Scheme::Txyz);
1✔
1385
        assert_eq!(Scheme::from("tms"), Scheme::Tms);
1✔
1386

1387
        // to string
1388
        assert_eq!(core::convert::Into::<&str>::into(Scheme::Fzxy), "fzxy");
1✔
1389
        assert_eq!(core::convert::Into::<&str>::into(Scheme::Tfzxy), "tfzxy");
1✔
1390
        assert_eq!(core::convert::Into::<&str>::into(Scheme::Xyz), "xyz");
1✔
1391
        assert_eq!(core::convert::Into::<&str>::into(Scheme::Txyz), "txyz");
1✔
1392
        assert_eq!(core::convert::Into::<&str>::into(Scheme::Tms), "tms");
1✔
1393
    }
1✔
1394

1395
    #[test]
1396
    fn test_tippecanoe_metadata() {
1✔
1397
        let meta_str = r#"{
1✔
1398
            "name": "test_fixture_1.pmtiles",
1✔
1399
            "description": "test_fixture_1.pmtiles",
1✔
1400
            "version": "2",
1✔
1401
            "type": "overlay",
1✔
1402
            "generator": "tippecanoe v2.5.0",
1✔
1403
            "generator_options": "./tippecanoe -zg -o test_fixture_1.pmtiles --force",
1✔
1404
            "vector_layers": [
1✔
1405
                {
1✔
1406
                    "id": "test_fixture_1pmtiles",
1✔
1407
                    "description": "",
1✔
1408
                    "minzoom": 0,
1✔
1409
                    "maxzoom": 0,
1✔
1410
                    "fields": {}
1✔
1411
                }
1✔
1412
            ],
1✔
1413
            "tilestats": {
1✔
1414
                "layerCount": 1,
1✔
1415
                "layers": [
1✔
1416
                    {
1✔
1417
                        "layer": "test_fixture_1pmtiles",
1✔
1418
                        "count": 1,
1✔
1419
                        "geometry": "Polygon",
1✔
1420
                        "attributeCount": 0,
1✔
1421
                        "attributes": []
1✔
1422
                    }
1✔
1423
                ]
1✔
1424
            }
1✔
1425
        }"#;
1✔
1426

1✔
1427
        let _meta: Metadata =
1✔
1428
            serde_json::from_str(meta_str).unwrap_or_else(|e| panic!("ERROR: {}", e));
1✔
1429
    }
1✔
1430
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc