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

Open-S2 / open-vector-tile / #67

03 May 2025 04:21AM UTC coverage: 98.815% (+0.1%) from 98.678%
#67

push

Mr Martian
migrate to stable

9510 of 9624 relevant lines covered (98.82%)

68.44 hits per line

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

97.34
/rust/base/vector_feature.rs
1
use crate::{
2
    Point, Point3D, VectorFeatureMethods, VectorGeometry, VectorLines3DWithOffset,
3
    VectorLinesWithOffset, VectorPoints, VectorPoints3D,
4
    mapbox::MapboxVectorFeature,
5
    open::{ColumnCacheWriter, FeatureType, encode_value},
6
    weave_2d, weave_3d, zigzag,
7
};
8
use alloc::vec::Vec;
9
use libm::round;
10
use s2json::{BBOX, BBox, BBox3D, LineStringMValues, Properties, Shape, Value};
11

12
/// Vector Feature functions that are common to all vector features
13
pub trait VectorFeature {
14
    /// Get the type of the vector feature
15
    fn get_type(&self) -> FeatureType;
16
    /// Get the properties of the vector feature
17
    fn properties(&self) -> &Properties;
18
    /// true if the feature has BBox
19
    fn has_bbox(&self) -> bool;
20
    /// true if the feature has offsets
21
    fn has_offsets(&self) -> bool;
22
    /// true if the feature has M values
23
    fn has_m_values(&self) -> bool;
24
    /// Get the geometry of the feature
25
    fn load_geometry(&self) -> VectorGeometry;
26
    /// Get the M values of the feature
27
    fn m_values(&self) -> Option<LineStringMValues>;
28
    /// Encode the feature to cache
29
    fn encode_to_cache(&self, cache: &mut ColumnCacheWriter, m_shape: Option<&Shape>) -> usize;
30
}
31

32
//? Points & Points3D
33

34
/// Base Vector Points Feature
35
#[derive(Default, Debug, Clone, PartialEq)]
36
pub struct BaseVectorPointsFeature {
37
    /// Unique ID
38
    pub id: Option<u64>,
39
    /// Geometry
40
    pub geometry: VectorPoints,
41
    /// Properties
42
    pub properties: Properties,
43
    /// BBox
44
    pub bbox: Option<BBox>,
45
}
46
impl BaseVectorPointsFeature {
47
    /// Create a new BaseVectorPointsFeature
48
    pub fn new(
33✔
49
        id: Option<u64>,
33✔
50
        geometry: VectorPoints,
33✔
51
        properties: Properties,
33✔
52
        bbox: Option<BBox>,
33✔
53
    ) -> Self {
33✔
54
        Self { id, geometry, properties, bbox }
33✔
55
    }
33✔
56
}
57
impl VectorFeature for BaseVectorPointsFeature {
58
    /// Get the type of the feature
59
    fn get_type(&self) -> FeatureType {
39✔
60
        FeatureType::Points
39✔
61
    }
39✔
62

63
    /// Get the properties of the feature
64
    fn properties(&self) -> &Properties {
51✔
65
        &self.properties
51✔
66
    }
51✔
67

68
    /// true if the feature has BBox
69
    fn has_bbox(&self) -> bool {
6✔
70
        self.bbox.is_some()
6✔
71
    }
6✔
72

73
    /// Points do not have this feature, so return false
74
    fn has_offsets(&self) -> bool {
6✔
75
        false
6✔
76
    }
6✔
77

78
    /// Points do not have this feature, so return false
79
    fn has_m_values(&self) -> bool {
63✔
80
        self.geometry.iter().any(|g| g.m.is_some())
63✔
81
    }
63✔
82

83
    fn load_geometry(&self) -> VectorGeometry {
6✔
84
        VectorGeometry::VectorPoints(self.geometry.clone())
6✔
85
    }
6✔
86

87
    fn m_values(&self) -> Option<LineStringMValues> {
42✔
88
        if !self.has_m_values() {
42✔
89
            return None;
6✔
90
        }
36✔
91
        Some(
36✔
92
            self.geometry
36✔
93
                .iter()
36✔
94
                .map(|g| {
63✔
95
                    // grab the m values, if they exist otherwise return default
63✔
96
                    g.m.clone().unwrap_or_default()
63✔
97
                })
63✔
98
                .collect(),
36✔
99
        )
36✔
100
    }
42✔
101

102
    fn encode_to_cache(&self, cache: &mut ColumnCacheWriter, m_shape: Option<&Shape>) -> usize {
21✔
103
        let geometry = &self.geometry;
21✔
104
        if geometry.len() == 1 {
21✔
105
            let point = &geometry[0];
12✔
106
            weave_2d(zigzag(point.x) as u16, zigzag(point.y) as u16) as usize
12✔
107
        } else {
108
            let mut indices: Vec<u32> = Vec::new();
9✔
109
            indices.push(cache.add_points(geometry.to_vec()) as u32);
9✔
110
            // store the mvalues indexes if they exist
111
            if let (Some(m_values), Some(shape)) = (self.m_values(), m_shape) {
9✔
112
                for m in m_values {
27✔
113
                    indices.push(encode_value(&m, shape, cache) as u32);
18✔
114
                }
18✔
115
            }
×
116
            cache.add_indices(indices)
9✔
117
        }
118
    }
21✔
119
}
120
/// Base Vector Points Feature
121
#[derive(Default, Debug, Clone, PartialEq)]
122
pub struct BaseVectorPoints3DFeature {
123
    /// Unique ID
124
    pub id: Option<u64>,
125
    /// Geometry
126
    pub geometry: VectorPoints3D,
127
    /// Properties
128
    pub properties: Properties,
129
    /// BBox
130
    pub bbox: Option<BBox3D>,
131
}
132
impl BaseVectorPoints3DFeature {
133
    /// Create a new BaseVectorPoints3DFeature
134
    pub fn new(
18✔
135
        id: Option<u64>,
18✔
136
        geometry: VectorPoints3D,
18✔
137
        properties: Properties,
18✔
138
        bbox: Option<BBox3D>,
18✔
139
    ) -> Self {
18✔
140
        Self { id, geometry, properties, bbox }
18✔
141
    }
18✔
142
}
143
impl VectorFeature for BaseVectorPoints3DFeature {
144
    /// Get the type of the feature
145
    fn get_type(&self) -> FeatureType {
30✔
146
        FeatureType::Points3D
30✔
147
    }
30✔
148

149
    /// Get the properties of the feature
150
    fn properties(&self) -> &Properties {
30✔
151
        &self.properties
30✔
152
    }
30✔
153

154
    /// true if the feature has BBox
155
    fn has_bbox(&self) -> bool {
6✔
156
        self.bbox.is_some()
6✔
157
    }
6✔
158

159
    /// Points do not have this feature, so return false
160
    fn has_offsets(&self) -> bool {
6✔
161
        false
6✔
162
    }
6✔
163

164
    /// Points do not have this feature, so return false
165
    fn has_m_values(&self) -> bool {
51✔
166
        self.geometry.iter().any(|g| g.m.is_some())
51✔
167
    }
51✔
168

169
    fn load_geometry(&self) -> VectorGeometry {
6✔
170
        VectorGeometry::VectorPoints3D(self.geometry.clone())
6✔
171
    }
6✔
172

173
    fn m_values(&self) -> Option<LineStringMValues> {
30✔
174
        if !self.has_m_values() {
30✔
175
            return None;
6✔
176
        }
24✔
177
        Some(self.geometry.iter().map(|g| g.m.clone().unwrap_or_default()).collect())
42✔
178
    }
30✔
179

180
    fn encode_to_cache(&self, cache: &mut ColumnCacheWriter, m_shape: Option<&Shape>) -> usize {
21✔
181
        let geometry = &self.geometry;
21✔
182
        if geometry.len() == 1 {
21✔
183
            let point = &geometry[0];
12✔
184
            weave_3d(zigzag(point.x) as u16, zigzag(point.y) as u16, zigzag(point.z) as u16)
12✔
185
                as usize
12✔
186
        } else {
187
            let mut indices: Vec<u32> = Vec::new();
9✔
188
            indices.push(cache.add_points_3d(geometry.to_vec()) as u32);
9✔
189
            // store the mvalues indexes if they exist
190
            if let (Some(m_values), Some(shape)) = (self.m_values(), m_shape) {
9✔
191
                for m in m_values {
27✔
192
                    indices.push(encode_value(&m, shape, cache) as u32);
18✔
193
                }
18✔
194
            }
×
195
            cache.add_indices(indices)
9✔
196
        }
197
    }
21✔
198
}
199

200
//? Lines & Lines3D
201

202
/// Base Vector Line Feature
203
#[derive(Default, Debug, Clone, PartialEq)]
204
pub struct BaseVectorLinesFeature {
205
    /// Unique ID
206
    pub id: Option<u64>,
207
    /// Geometry
208
    pub geometry: VectorLinesWithOffset,
209
    /// Properties
210
    pub properties: Properties,
211
    /// BBox
212
    pub bbox: Option<BBox>,
213
}
214
impl BaseVectorLinesFeature {
215
    /// Create a new BaseVectorLinesFeature
216
    pub fn new(
21✔
217
        id: Option<u64>,
21✔
218
        geometry: VectorLinesWithOffset,
21✔
219
        properties: Properties,
21✔
220
        bbox: Option<BBox>,
21✔
221
    ) -> Self {
21✔
222
        Self { id, geometry, properties, bbox }
21✔
223
    }
21✔
224
}
225
impl VectorFeature for BaseVectorLinesFeature {
226
    /// Get the type of the feature
227
    fn get_type(&self) -> FeatureType {
30✔
228
        FeatureType::Lines
30✔
229
    }
30✔
230

231
    /// Get the properties of the feature
232
    fn properties(&self) -> &Properties {
36✔
233
        &self.properties
36✔
234
    }
36✔
235

236
    /// true if the feature has BBox
237
    fn has_bbox(&self) -> bool {
6✔
238
        self.bbox.is_some()
6✔
239
    }
6✔
240

241
    /// Points do not have this feature, so return false
242
    fn has_offsets(&self) -> bool {
18✔
243
        self.geometry.iter().any(|g| g.has_offset())
24✔
244
    }
18✔
245

246
    /// Points do not have this feature, so return false
247
    fn has_m_values(&self) -> bool {
69✔
248
        self.geometry.iter().any(|g| g.has_m_values())
69✔
249
    }
69✔
250

251
    fn load_geometry(&self) -> VectorGeometry {
6✔
252
        VectorGeometry::VectorLines(self.geometry.to_vec())
6✔
253
    }
6✔
254

255
    fn m_values(&self) -> Option<LineStringMValues> {
24✔
256
        if !self.has_m_values() {
24✔
257
            return None;
6✔
258
        }
18✔
259
        Some(self.geometry.iter().flat_map(|g| g.m_values().unwrap_or_default()).collect())
30✔
260
    }
24✔
261

262
    fn encode_to_cache(&self, cache: &mut ColumnCacheWriter, m_shape: Option<&Shape>) -> usize {
18✔
263
        let geometry = &self.geometry;
18✔
264
        let mut indices: Vec<u32> = Vec::new();
18✔
265
        if geometry.len() != 1 {
18✔
266
            indices.push(geometry.len() as u32)
9✔
267
        }
9✔
268
        for line in geometry {
45✔
269
            if line.has_offset() {
27✔
270
                indices.push(encode_offset(line.offset));
12✔
271
            }
15✔
272
            indices.push(cache.add_points(line.geometry.clone()) as u32);
27✔
273
            // store the mvalues indexes if they exist
27✔
274
            if self.has_m_values() {
27✔
275
                if let (Some(m_values), Some(shape)) = (line.m_values(), m_shape) {
21✔
276
                    for m in m_values {
48✔
277
                        indices.push(encode_value(&m, shape, cache) as u32);
30✔
278
                    }
30✔
279
                } else if let (None, Some(shape)) = (line.m_values(), m_shape) {
3✔
280
                    for _ in 0..line.geometry.len() {
6✔
281
                        indices.push(encode_value(&Value::default(), shape, cache) as u32);
6✔
282
                    }
6✔
283
                }
×
284
            }
6✔
285
        }
286
        cache.add_indices(indices)
18✔
287
    }
18✔
288
}
289

290
/// Base Vector Line 3D Feature
291
#[derive(Default, Debug, Clone, PartialEq)]
292
pub struct BaseVectorLines3DFeature {
293
    /// Unique ID
294
    pub id: Option<u64>,
295
    /// Geometry
296
    pub geometry: VectorLines3DWithOffset,
297
    /// Properties
298
    pub properties: Properties,
299
    /// BBox
300
    pub bbox: Option<BBox3D>,
301
}
302
impl BaseVectorLines3DFeature {
303
    /// Create a new BaseVectorLines3DFeature
304
    pub fn new(
15✔
305
        id: Option<u64>,
15✔
306
        geometry: VectorLines3DWithOffset,
15✔
307
        properties: Properties,
15✔
308
        bbox: Option<BBox3D>,
15✔
309
    ) -> Self {
15✔
310
        Self { id, geometry, properties, bbox }
15✔
311
    }
15✔
312
}
313
impl VectorFeature for BaseVectorLines3DFeature {
314
    /// Get the type of the feature
315
    fn get_type(&self) -> FeatureType {
24✔
316
        FeatureType::Lines3D
24✔
317
    }
24✔
318

319
    /// Get the properties of the feature
320
    fn properties(&self) -> &Properties {
24✔
321
        &self.properties
24✔
322
    }
24✔
323

324
    /// true if the feature has BBox
325
    fn has_bbox(&self) -> bool {
6✔
326
        self.bbox.is_some()
6✔
327
    }
6✔
328

329
    /// Points do not have this feature, so return false
330
    fn has_offsets(&self) -> bool {
18✔
331
        self.geometry.iter().any(|g| g.has_offset())
24✔
332
    }
18✔
333

334
    /// Points do not have this feature, so return false
335
    fn has_m_values(&self) -> bool {
63✔
336
        self.geometry.iter().any(|g| g.has_m_values())
63✔
337
    }
63✔
338

339
    fn load_geometry(&self) -> VectorGeometry {
6✔
340
        VectorGeometry::VectorLines3D(self.geometry.to_vec())
6✔
341
    }
6✔
342

343
    fn m_values(&self) -> Option<LineStringMValues> {
18✔
344
        if !self.has_m_values() {
18✔
345
            return None;
6✔
346
        }
12✔
347
        Some(self.geometry.iter().flat_map(|g| g.m_values().unwrap_or_default()).collect())
21✔
348
    }
18✔
349

350
    fn encode_to_cache(&self, cache: &mut ColumnCacheWriter, m_shape: Option<&Shape>) -> usize {
18✔
351
        let geometry = &self.geometry;
18✔
352
        let mut indices: Vec<u32> = Vec::new();
18✔
353
        if geometry.len() != 1 {
18✔
354
            indices.push(geometry.len() as u32)
9✔
355
        }
9✔
356
        for line in geometry {
45✔
357
            if line.has_offset() {
27✔
358
                indices.push(encode_offset(line.offset));
12✔
359
            }
15✔
360
            indices.push(cache.add_points_3d(line.geometry.clone()) as u32);
27✔
361
            // store the mvalues indexes if they exist
27✔
362
            if self.has_m_values() {
27✔
363
                if let (Some(m_values), Some(shape)) = (line.m_values(), m_shape) {
21✔
364
                    for m in m_values {
48✔
365
                        indices.push(encode_value(&m, shape, cache) as u32);
30✔
366
                    }
30✔
367
                } else if let (None, Some(shape)) = (line.m_values(), m_shape) {
3✔
368
                    for _ in 0..line.geometry.len() {
6✔
369
                        indices.push(encode_value(&Value::default(), shape, cache) as u32);
6✔
370
                    }
6✔
371
                }
×
372
            }
6✔
373
        }
374
        cache.add_indices(indices)
18✔
375
    }
18✔
376
}
377

378
//? Polygons & Polygons3D
379

380
/// Base Vector Polygon Feature
381
#[derive(Default, Debug, Clone, PartialEq)]
382
pub struct BaseVectorPolysFeature {
383
    /// Unique ID
384
    pub id: Option<u64>,
385
    /// Geometry
386
    pub geometry: Vec<VectorLinesWithOffset>,
387
    /// Properties
388
    pub properties: Properties,
389
    /// BBox
390
    pub bbox: Option<BBox>,
391
    /// Tessellation
392
    pub tessellation: Vec<Point>,
393
    /// Indices
394
    pub indices: Vec<u32>,
395
}
396
impl BaseVectorPolysFeature {
397
    /// Create a new BaseVectorPolysFeature
398
    pub fn new(
27✔
399
        id: Option<u64>,
27✔
400
        geometry: Vec<VectorLinesWithOffset>,
27✔
401
        properties: Properties,
27✔
402
        bbox: Option<BBox>,
27✔
403
        indices: Vec<u32>,
27✔
404
        tessellation: Vec<Point>,
27✔
405
    ) -> Self {
27✔
406
        Self { id, geometry, properties, bbox, indices, tessellation }
27✔
407
    }
27✔
408
}
409
impl VectorFeature for BaseVectorPolysFeature {
410
    /// Get the type of the feature
411
    fn get_type(&self) -> FeatureType {
30✔
412
        FeatureType::Polygons
30✔
413
    }
30✔
414

415
    /// Get the properties of the feature
416
    fn properties(&self) -> &Properties {
42✔
417
        &self.properties
42✔
418
    }
42✔
419

420
    /// true if the feature has BBox
421
    fn has_bbox(&self) -> bool {
6✔
422
        self.bbox.is_some()
6✔
423
    }
6✔
424

425
    /// Points do not have this feature, so return false
426
    fn has_offsets(&self) -> bool {
18✔
427
        self.geometry.iter().any(|g| g.iter().any(|l| l.has_offset()))
30✔
428
    }
18✔
429

430
    /// Points do not have this feature, so return false
431
    fn has_m_values(&self) -> bool {
96✔
432
        self.geometry.iter().any(|g| g.iter().any(|l| l.has_m_values()))
126✔
433
    }
96✔
434

435
    fn load_geometry(&self) -> VectorGeometry {
12✔
436
        VectorGeometry::VectorPolys(self.geometry.iter().map(|line| line.to_vec()).collect())
21✔
437
    }
12✔
438

439
    fn m_values(&self) -> Option<LineStringMValues> {
30✔
440
        if !self.has_m_values() {
30✔
441
            return None;
12✔
442
        }
18✔
443
        Some(
18✔
444
            self.geometry
18✔
445
                .iter()
18✔
446
                .flat_map(|g| g.iter().flat_map(|l| l.m_values().unwrap_or_default()))
48✔
447
                .collect(),
18✔
448
        )
18✔
449
    }
30✔
450

451
    fn encode_to_cache(&self, cache: &mut ColumnCacheWriter, m_shape: Option<&Shape>) -> usize {
18✔
452
        let geometry = &self.geometry;
18✔
453
        let mut indices: Vec<u32> = Vec::new();
18✔
454
        if geometry.len() != 1 {
18✔
455
            indices.push(geometry.len() as u32)
9✔
456
        }
9✔
457
        for poly in geometry {
45✔
458
            indices.push(poly.len() as u32);
27✔
459
            for line in poly {
75✔
460
                if line.has_offset() {
48✔
461
                    indices.push(encode_offset(line.offset));
27✔
462
                }
27✔
463
                indices.push(cache.add_points(line.geometry.clone()) as u32);
48✔
464
                // store the mvalues indexes if they exist
48✔
465
                if self.has_m_values() {
48✔
466
                    if let (Some(m_values), Some(shape)) = (line.m_values(), m_shape) {
36✔
467
                        for m in m_values {
90✔
468
                            indices.push(encode_value(&m, shape, cache) as u32);
69✔
469
                        }
69✔
470
                    } else if let (None, Some(shape)) = (line.m_values(), m_shape) {
15✔
471
                        for _ in 0..line.geometry.len() {
39✔
472
                            indices.push(encode_value(&Value::default(), shape, cache) as u32);
39✔
473
                        }
39✔
474
                    }
×
475
                }
12✔
476
            }
477
        }
478
        cache.add_indices(indices)
18✔
479
    }
18✔
480
}
481

482
/// Base Vector Polygon Feature
483
#[derive(Default, Debug, Clone, PartialEq)]
484
pub struct BaseVectorPolys3DFeature {
485
    /// Unique ID
486
    pub id: Option<u64>,
487
    /// Geometry
488
    pub geometry: Vec<VectorLines3DWithOffset>,
489
    /// Properties
490
    pub properties: Properties,
491
    /// BBox
492
    pub bbox: Option<BBox3D>,
493
    /// Tessellation
494
    pub tessellation: Vec<Point3D>,
495
    /// Indices
496
    pub indices: Vec<u32>,
497
}
498
impl BaseVectorPolys3DFeature {
499
    /// Create a new BaseVectorPolys3DFeature
500
    pub fn new(
15✔
501
        id: Option<u64>,
15✔
502
        geometry: Vec<VectorLines3DWithOffset>,
15✔
503
        properties: Properties,
15✔
504
        bbox: Option<BBox3D>,
15✔
505
        indices: Vec<u32>,
15✔
506
        tessellation: Vec<Point3D>,
15✔
507
    ) -> Self {
15✔
508
        Self { id, geometry, properties, bbox, indices, tessellation }
15✔
509
    }
15✔
510
}
511
impl VectorFeature for BaseVectorPolys3DFeature {
512
    /// Get the type of the feature
513
    fn get_type(&self) -> FeatureType {
24✔
514
        FeatureType::Polygons3D
24✔
515
    }
24✔
516

517
    /// Get the properties of the feature
518
    fn properties(&self) -> &Properties {
24✔
519
        &self.properties
24✔
520
    }
24✔
521

522
    /// true if the feature has BBox
523
    fn has_bbox(&self) -> bool {
6✔
524
        self.bbox.is_some()
6✔
525
    }
6✔
526

527
    /// Points do not have this feature, so return false
528
    fn has_offsets(&self) -> bool {
18✔
529
        self.geometry.iter().any(|g| g.iter().any(|l| l.has_offset()))
30✔
530
    }
18✔
531

532
    /// Points do not have this feature, so return false
533
    fn has_m_values(&self) -> bool {
84✔
534
        self.geometry.iter().any(|g| g.iter().any(|l| l.has_m_values()))
108✔
535
    }
84✔
536

537
    fn load_geometry(&self) -> VectorGeometry {
6✔
538
        VectorGeometry::VectorPolys3D(self.geometry.iter().map(|line| line.to_vec()).collect())
9✔
539
    }
6✔
540

541
    fn m_values(&self) -> Option<LineStringMValues> {
18✔
542
        if !self.has_m_values() {
18✔
543
            return None;
6✔
544
        }
12✔
545
        Some(
12✔
546
            self.geometry
12✔
547
                .iter()
12✔
548
                .flat_map(|g| g.iter().flat_map(|l| l.m_values().unwrap_or_default()))
36✔
549
                .collect(),
12✔
550
        )
12✔
551
    }
18✔
552

553
    fn encode_to_cache(&self, cache: &mut ColumnCacheWriter, m_shape: Option<&Shape>) -> usize {
18✔
554
        let geometry = &self.geometry;
18✔
555
        let mut indices: Vec<u32> = Vec::new();
18✔
556
        if geometry.len() != 1 {
18✔
557
            indices.push(geometry.len() as u32)
9✔
558
        }
9✔
559
        for poly in geometry {
45✔
560
            indices.push(poly.len() as u32);
27✔
561
            for line in poly {
75✔
562
                if line.has_offset() {
48✔
563
                    indices.push(encode_offset(line.offset));
27✔
564
                }
27✔
565
                indices.push(cache.add_points_3d(line.geometry.clone()) as u32);
48✔
566
                // store the mvalues indexes if they exist
48✔
567
                if self.has_m_values() {
48✔
568
                    if let (Some(m_values), Some(shape)) = (line.m_values(), m_shape) {
36✔
569
                        for m in m_values {
90✔
570
                            indices.push(encode_value(&m, shape, cache) as u32);
69✔
571
                        }
69✔
572
                    } else if let (None, Some(shape)) = (line.m_values(), m_shape) {
15✔
573
                        for _ in 0..line.geometry.len() {
39✔
574
                            indices.push(encode_value(&Value::default(), shape, cache) as u32);
39✔
575
                        }
39✔
576
                    }
×
577
                }
12✔
578
            }
579
        }
580
        cache.add_indices(indices)
18✔
581
    }
18✔
582
}
583

584
/// Tessellation Wrapper to handle both 2D and 3D cases
585
#[derive(Debug, Clone, PartialEq)]
586
pub enum TessellationWrapper {
587
    /// 2D tessellation
588
    Tessellation(Vec<Point>),
589
    /// 3D tessellation
590
    Tessellation3D(Vec<Point3D>),
591
}
592
impl TessellationWrapper {
593
    /// check the length of the tessellation
594
    pub fn len(&self) -> usize {
12✔
595
        match self {
12✔
596
            TessellationWrapper::Tessellation(points) => points.len(),
6✔
597
            TessellationWrapper::Tessellation3D(points) => points.len(),
6✔
598
        }
599
    }
12✔
600

601
    /// check if the tessellation is empty
602
    pub fn is_empty(&self) -> bool {
24✔
603
        match self {
24✔
604
            TessellationWrapper::Tessellation(points) => points.is_empty(),
12✔
605
            TessellationWrapper::Tessellation3D(points) => points.is_empty(),
12✔
606
        }
607
    }
24✔
608
}
609

610
/// A type that encompasses all vector tile feature types
611
#[derive(Debug, Clone, PartialEq)]
612
pub enum BaseVectorFeature {
613
    /// Points
614
    BaseVectorPointsFeature(BaseVectorPointsFeature),
615
    /// Lines
616
    BaseVectorLinesFeature(BaseVectorLinesFeature),
617
    /// Polygons
618
    BaseVectorPolysFeature(BaseVectorPolysFeature),
619
    /// 3D Points
620
    BaseVectorPoints3DFeature(BaseVectorPoints3DFeature),
621
    /// 3D Lines
622
    BaseVectorLines3DFeature(BaseVectorLines3DFeature),
623
    /// 3D Polygons
624
    BaseVectorPolys3DFeature(BaseVectorPolys3DFeature),
625
}
626
impl BaseVectorFeature {
627
    /// check if the feature geometry has a single length
628
    pub fn single(&self) -> bool {
78✔
629
        match self {
78✔
630
            BaseVectorFeature::BaseVectorPointsFeature(f) => f.geometry.len() == 1,
15✔
631
            BaseVectorFeature::BaseVectorLinesFeature(f) => f.geometry.len() == 1,
12✔
632
            BaseVectorFeature::BaseVectorPolysFeature(f) => f.geometry.len() == 1,
12✔
633
            BaseVectorFeature::BaseVectorPoints3DFeature(f) => f.geometry.len() == 1,
15✔
634
            BaseVectorFeature::BaseVectorLines3DFeature(f) => f.geometry.len() == 1,
12✔
635
            BaseVectorFeature::BaseVectorPolys3DFeature(f) => f.geometry.len() == 1,
12✔
636
        }
637
    }
78✔
638

639
    /// get the feature properties
640
    pub fn properties(&self) -> &Properties {
171✔
641
        match self {
171✔
642
            BaseVectorFeature::BaseVectorPointsFeature(f) => f.properties(),
45✔
643
            BaseVectorFeature::BaseVectorLinesFeature(f) => f.properties(),
30✔
644
            BaseVectorFeature::BaseVectorPolysFeature(f) => f.properties(),
36✔
645
            BaseVectorFeature::BaseVectorPoints3DFeature(f) => f.properties(),
24✔
646
            BaseVectorFeature::BaseVectorLines3DFeature(f) => f.properties(),
18✔
647
            BaseVectorFeature::BaseVectorPolys3DFeature(f) => f.properties(),
18✔
648
        }
649
    }
171✔
650

651
    /// check if the feature has m values
652
    pub fn has_m_values(&self) -> bool {
78✔
653
        match self {
78✔
654
            BaseVectorFeature::BaseVectorPointsFeature(f) => f.has_m_values(),
15✔
655
            BaseVectorFeature::BaseVectorLinesFeature(f) => f.has_m_values(),
12✔
656
            BaseVectorFeature::BaseVectorPolysFeature(f) => f.has_m_values(),
12✔
657
            BaseVectorFeature::BaseVectorPoints3DFeature(f) => f.has_m_values(),
15✔
658
            BaseVectorFeature::BaseVectorLines3DFeature(f) => f.has_m_values(),
12✔
659
            BaseVectorFeature::BaseVectorPolys3DFeature(f) => f.has_m_values(),
12✔
660
        }
661
    }
78✔
662

663
    /// get the feature m values
664
    pub fn m_values(&self) -> Option<LineStringMValues> {
108✔
665
        match self {
108✔
666
            BaseVectorFeature::BaseVectorPointsFeature(f) => f.m_values(),
27✔
667
            BaseVectorFeature::BaseVectorLinesFeature(f) => f.m_values(),
18✔
668
            BaseVectorFeature::BaseVectorPolysFeature(f) => f.m_values(),
24✔
669
            BaseVectorFeature::BaseVectorPoints3DFeature(f) => f.m_values(),
15✔
670
            BaseVectorFeature::BaseVectorLines3DFeature(f) => f.m_values(),
12✔
671
            BaseVectorFeature::BaseVectorPolys3DFeature(f) => f.m_values(),
12✔
672
        }
673
    }
108✔
674

675
    /// get the feature type
676
    pub fn get_type(&self) -> FeatureType {
141✔
677
        match self {
141✔
678
            BaseVectorFeature::BaseVectorPointsFeature(f) => f.get_type(),
33✔
679
            BaseVectorFeature::BaseVectorLinesFeature(f) => f.get_type(),
24✔
680
            BaseVectorFeature::BaseVectorPolysFeature(f) => f.get_type(),
24✔
681
            BaseVectorFeature::BaseVectorPoints3DFeature(f) => f.get_type(),
24✔
682
            BaseVectorFeature::BaseVectorLines3DFeature(f) => f.get_type(),
18✔
683
            BaseVectorFeature::BaseVectorPolys3DFeature(f) => f.get_type(),
18✔
684
        }
685
    }
141✔
686

687
    /// get the feature id
688
    pub fn id(&self) -> Option<u64> {
99✔
689
        match self {
99✔
690
            BaseVectorFeature::BaseVectorPointsFeature(f) => f.id,
24✔
691
            BaseVectorFeature::BaseVectorLinesFeature(f) => f.id,
18✔
692
            BaseVectorFeature::BaseVectorPolysFeature(f) => f.id,
18✔
693
            BaseVectorFeature::BaseVectorPoints3DFeature(f) => f.id,
15✔
694
            BaseVectorFeature::BaseVectorLines3DFeature(f) => f.id,
12✔
695
            BaseVectorFeature::BaseVectorPolys3DFeature(f) => f.id,
12✔
696
        }
697
    }
99✔
698

699
    /// get the feature indices
700
    pub fn indices(&self) -> Option<Vec<u32>> {
105✔
701
        match self {
105✔
702
            BaseVectorFeature::BaseVectorPolysFeature(f) => Some(f.indices.clone()),
21✔
703
            BaseVectorFeature::BaseVectorPolys3DFeature(f) => Some(f.indices.clone()),
15✔
704
            _ => None,
69✔
705
        }
706
    }
105✔
707

708
    /// get the feature tessellation
709
    pub fn tessellation(&self) -> Option<TessellationWrapper> {
99✔
710
        match self {
99✔
711
            BaseVectorFeature::BaseVectorPolysFeature(f) => {
18✔
712
                Some(TessellationWrapper::Tessellation(f.tessellation.clone()))
18✔
713
            }
714
            BaseVectorFeature::BaseVectorPolys3DFeature(f) => {
12✔
715
                Some(TessellationWrapper::Tessellation3D(f.tessellation.clone()))
12✔
716
            }
717
            _ => None,
69✔
718
        }
719
    }
99✔
720

721
    /// get the feature bbox
722
    pub fn bbox(&self) -> Option<BBOX> {
78✔
723
        match self {
78✔
724
            BaseVectorFeature::BaseVectorPointsFeature(f) => f.bbox.map(BBOX::BBox),
15✔
725
            BaseVectorFeature::BaseVectorLinesFeature(f) => f.bbox.map(BBOX::BBox),
12✔
726
            BaseVectorFeature::BaseVectorPolysFeature(f) => f.bbox.map(BBOX::BBox),
12✔
727
            BaseVectorFeature::BaseVectorPoints3DFeature(f) => f.bbox.map(BBOX::BBox3D),
15✔
728
            BaseVectorFeature::BaseVectorLines3DFeature(f) => f.bbox.map(BBOX::BBox3D),
12✔
729
            BaseVectorFeature::BaseVectorPolys3DFeature(f) => f.bbox.map(BBOX::BBox3D),
12✔
730
        }
731
    }
78✔
732

733
    /// check if the feature has offsets
734
    pub fn has_offsets(&self) -> bool {
78✔
735
        match self {
78✔
736
            BaseVectorFeature::BaseVectorLinesFeature(f) => f.has_offsets(),
12✔
737
            BaseVectorFeature::BaseVectorLines3DFeature(f) => f.has_offsets(),
12✔
738
            BaseVectorFeature::BaseVectorPolysFeature(f) => f.has_offsets(),
12✔
739
            BaseVectorFeature::BaseVectorPolys3DFeature(f) => f.has_offsets(),
12✔
740
            _ => false,
30✔
741
        }
742
    }
78✔
743

744
    /// encode the feature to cache
745
    pub fn encode_to_cache(&self, cache: &mut ColumnCacheWriter, m_shape: Option<&Shape>) -> usize {
78✔
746
        match self {
78✔
747
            BaseVectorFeature::BaseVectorPointsFeature(f) => f.encode_to_cache(cache, m_shape),
15✔
748
            BaseVectorFeature::BaseVectorLinesFeature(f) => f.encode_to_cache(cache, m_shape),
12✔
749
            BaseVectorFeature::BaseVectorPolysFeature(f) => f.encode_to_cache(cache, m_shape),
12✔
750
            BaseVectorFeature::BaseVectorPoints3DFeature(f) => f.encode_to_cache(cache, m_shape),
15✔
751
            BaseVectorFeature::BaseVectorLines3DFeature(f) => f.encode_to_cache(cache, m_shape),
12✔
752
            BaseVectorFeature::BaseVectorPolys3DFeature(f) => f.encode_to_cache(cache, m_shape),
12✔
753
        }
754
    }
78✔
755
}
756
impl From<&mut MapboxVectorFeature> for BaseVectorFeature {
757
    fn from(mvt: &mut MapboxVectorFeature) -> Self {
6✔
758
        let id = mvt.id;
6✔
759
        let properties: Properties = (&mvt.properties).into();
6✔
760
        let indices = mvt.read_indices();
6✔
761
        let mut tessellation_floats: Vec<f64> = Vec::new();
6✔
762
        mvt.add_tessellation(&mut tessellation_floats, 1.0);
6✔
763
        // convert an flat array of f64 to groups of 2 making a Point (convert to a Vec<Point>)
6✔
764
        let tessellation = tess_to_points(tessellation_floats);
6✔
765

6✔
766
        match mvt.load_geometry() {
6✔
767
            VectorGeometry::VectorPoints(geo) => BaseVectorFeature::BaseVectorPointsFeature(
×
768
                BaseVectorPointsFeature::new(id, geo, properties, None),
×
769
            ),
×
770
            VectorGeometry::VectorLines(geo) => BaseVectorFeature::BaseVectorLinesFeature(
×
771
                BaseVectorLinesFeature::new(id, geo, properties, None),
×
772
            ),
×
773
            VectorGeometry::VectorPolys(geo) => BaseVectorFeature::BaseVectorPolysFeature(
6✔
774
                BaseVectorPolysFeature::new(id, geo, properties, None, indices, tessellation),
6✔
775
            ),
6✔
776
            _ => panic!("unexpected geometry type"),
×
777
        }
778
    }
6✔
779
}
780

781
/// Taking input tesselation data, migrate it back to a Point
782
pub fn tess_to_points(tess: Vec<f64>) -> Vec<Point> {
12✔
783
    tess.chunks(2).map(|chunk| Point::new(round(chunk[0]) as i32, round(chunk[1]) as i32)).collect()
12✔
784
}
12✔
785

786
/// Taking input tesselation 3D data, migrate back to a Point3D
787
pub fn tess_to_points_3d(tess: Vec<f64>) -> Vec<Point3D> {
6✔
788
    tess.chunks(3)
6✔
789
        .map(|chunk| {
12✔
790
            Point3D::new(round(chunk[0]) as i32, round(chunk[1]) as i32, round(chunk[2]) as i32)
12✔
791
        })
12✔
792
        .collect()
6✔
793
}
6✔
794

795
/// Encode offset values into a signed integer to reduce byte cost without too much loss
796
pub fn encode_offset(offset: f64) -> u32 {
87✔
797
    round(offset * 1_000.0) as u32
87✔
798
}
87✔
799

800
/// Decode offset from a signed integer into a float or double
801
pub fn decode_offset(offset: u32) -> f64 {
39✔
802
    (offset as f64) / 1_000.0
39✔
803
}
39✔
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

© 2026 Coveralls, Inc