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

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

02 Apr 2025 03:18PM UTC coverage: 98.957% (+0.1%) from 98.854%
#59

push

Mr Martian
fix naming conventions for tessellation; fix fields starting with 0; add support to convert s2json::VectorFeature to BaseVectorFeature

1071 of 1073 new or added lines in 22 files covered. (99.81%)

1 existing line in 1 file now uncovered.

9389 of 9488 relevant lines covered (98.96%)

66.76 hits per line

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

95.62
/rust/mapbox/vector_feature.rs
1
use crate::{
2
    base::{BaseVectorFeature, TessellationWrapper},
3
    command_encode,
4
    open::FeatureType as OpenFeatureType,
5
    zigzag, Point, VectorFeatureMethods, VectorGeometry, VectorLineWithOffset,
6
    VectorLines3DWithOffset, VectorLinesWithOffset, VectorPoints, VectorPoints3D,
7
};
8
use alloc::{collections::BTreeMap, rc::Rc, string::String, vec, vec::Vec};
9
use core::cell::RefCell;
10
use pbf::{BitCast, ProtoRead, Protobuf};
11
use s2json::{MapboxProperties, PrimitiveValue, Properties, BBOX};
12

13
/// Mapbox specification for a Feature
14
#[derive(Debug)]
15
pub struct MapboxVectorFeature {
16
    /// the id of the feature
17
    pub id: Option<u64>,
18
    /// the version of the vector tile
19
    pub version: u16,
20
    /// the properties
21
    pub properties: MapboxProperties,
22
    /// the extent
23
    pub extent: usize,
24
    /// the feature type
25
    pub r#type: FeatureType,
26
    /// whether the feature is using the S2 spec. This isn't used by most tooling and was replaced by
27
    /// the open spec
28
    pub is_s2: bool,
29
    indices_index: Option<usize>,
30
    indices: Option<Vec<u32>>,
31
    geometry_index: usize,
32
    geometry: Option<VectorGeometry>,
33
    tessellation_index: Option<usize>,
34
    keys: Rc<RefCell<Vec<String>>>,
35
    values: Rc<RefCell<Vec<PrimitiveValue>>>,
36
    pbf: Rc<RefCell<Protobuf>>,
37
}
38
impl MapboxVectorFeature {
39
    /// Create a new MapboxVectorFeature
40
    pub fn new(
39✔
41
        pbf: Rc<RefCell<Protobuf>>,
39✔
42
        is_s2: bool,
39✔
43
        extent: usize,
39✔
44
        version: u16,
39✔
45
        keys: Rc<RefCell<Vec<String>>>,
39✔
46
        values: Rc<RefCell<Vec<PrimitiveValue>>>,
39✔
47
    ) -> MapboxVectorFeature {
39✔
48
        MapboxVectorFeature {
39✔
49
            id: None,
39✔
50
            version,
39✔
51
            properties: MapboxProperties::new(),
39✔
52
            extent,
39✔
53
            r#type: FeatureType::Point,
39✔
54
            is_s2,
39✔
55
            // tmp pbf until after reading in attributes
39✔
56
            indices_index: None,
39✔
57
            indices: None,
39✔
58
            geometry_index: 0,
39✔
59
            geometry: None,
39✔
60
            tessellation_index: None,
39✔
61
            keys,
39✔
62
            values,
39✔
63
            pbf,
39✔
64
        }
39✔
65
    }
39✔
66
}
67
impl VectorFeatureMethods for MapboxVectorFeature {
68
    /// get the feature id
69
    fn id(&self) -> Option<u64> {
18✔
70
        self.id
18✔
71
    }
18✔
72

73
    /// get the feature version
74
    fn version(&self) -> u16 {
18✔
75
        self.version
18✔
76
    }
18✔
77

78
    /// get the feature properties
79
    fn properties(&self) -> Properties {
18✔
80
        (&self.properties).into()
18✔
81
    }
18✔
82

83
    /// get the feature extent
84
    fn extent(&self) -> usize {
18✔
85
        self.extent
18✔
86
    }
18✔
87

88
    /// get the feature type
89
    fn get_type(&self) -> OpenFeatureType {
18✔
90
        (&self.r#type).into()
18✔
91
    }
18✔
92

93
    /// get the bbox
94
    fn bbox(&self) -> Option<BBOX> {
18✔
95
        None
18✔
96
    }
18✔
97

98
    /// whether the feature has m values
99
    fn has_m_values(&self) -> bool {
18✔
100
        false
18✔
101
    }
18✔
102

103
    /// whether the feature is a points type
104
    fn is_points(&self) -> bool {
18✔
105
        self.r#type == FeatureType::Point
18✔
106
    }
18✔
107

108
    /// whether the feature is a line type
109
    fn is_lines(&self) -> bool {
18✔
110
        self.r#type == FeatureType::Line
18✔
111
    }
18✔
112

113
    /// whether the feature is a polygon type
114
    fn is_polygons(&self) -> bool {
18✔
115
        self.r#type == FeatureType::Polygon || self.r#type == FeatureType::MultiPolygon
18✔
116
    }
18✔
117

118
    /// whether the feature is a points 3D type
119
    fn is_points_3d(&self) -> bool {
18✔
120
        false
18✔
121
    }
18✔
122

123
    /// whether the feature is a line 3D type
124
    fn is_lines_3d(&self) -> bool {
18✔
125
        false
18✔
126
    }
18✔
127

128
    /// whether the feature is a polygon 3D type
129
    fn is_polygons_3d(&self) -> bool {
18✔
130
        false
18✔
131
    }
18✔
132

133
    /// regardless of the type, we return a flattend point array
134
    fn load_points(&mut self) -> VectorPoints {
18✔
135
        match self.load_geometry() {
18✔
136
            VectorGeometry::VectorPoints(p) => p,
6✔
137
            VectorGeometry::VectorLines(lines) => {
6✔
138
                lines.iter().flat_map(|p| p.geometry.clone()).collect()
9✔
139
            }
140
            VectorGeometry::VectorPolys(polys) => polys
6✔
141
                .iter()
6✔
142
                .flat_map(|p| p.iter().flat_map(|p| p.geometry[..p.geometry.len() - 1].to_vec()))
12✔
143
                .collect(),
6✔
144
            #[tarpaulin::skip]
145
            _ => panic!("unexpected geometry type"),
×
146
        }
147
    }
18✔
148

149
    #[tarpaulin::skip]
150
    fn load_points_3d(&mut self) -> VectorPoints3D {
3✔
151
        panic!("unexpected geometry type")
3✔
152
    }
153

154
    /// an array of lines. The offsets will be set to 0
155
    fn load_lines(&mut self) -> VectorLinesWithOffset {
15✔
156
        match self.load_geometry() {
15✔
157
            VectorGeometry::VectorLines(lines) => lines,
6✔
158
            VectorGeometry::VectorPolys(polys) => polys.iter().flat_map(|p| p.clone()).collect(),
9✔
159
            #[tarpaulin::skip]
160
            _ => panic!("unexpected geometry type"),
3✔
161
        }
162
    }
12✔
163

164
    /// an array of 3D lines. The offsets will be set to 0
165
    #[tarpaulin::skip]
166
    fn load_lines_3d(&mut self) -> VectorLines3DWithOffset {
3✔
167
        panic!("unexpected geometry type")
3✔
168
    }
169

170
    /// (flattened geometry & tesslation if applicable, indices)
171
    fn load_geometry_flat(&mut self) -> (Vec<f64>, Vec<u32>) {
9✔
172
        // build a multiplier
9✔
173
        let multiplier: f64 = 1.0 / self.extent as f64;
9✔
174
        // grab the geometry, flatten it, and mutate to an f64
175
        let mut geometry: Vec<f64> = match self.load_geometry() {
9✔
176
            VectorGeometry::VectorPolys(polys) => polys
6✔
177
                .iter()
6✔
178
                .flat_map(|p| {
9✔
179
                    p.iter().flat_map(|p| {
12✔
180
                        p.geometry
12✔
181
                            .clone()
12✔
182
                            .into_iter()
12✔
183
                            .flat_map(|p| vec![p.x as f64 * multiplier, p.y as f64 * multiplier])
60✔
184
                    })
12✔
185
                })
9✔
186
                .collect(),
6✔
187
            #[tarpaulin::skip]
188
            _ => panic!("unexpected geometry type"),
3✔
189
        };
190
        // if a poly, check if we should load indices
191
        let indices = self.read_indices();
6✔
192
        // if a poly, check if we should load tessellation
6✔
193
        self.add_tessellation(&mut geometry, multiplier);
6✔
194

6✔
195
        (geometry, indices)
6✔
196
    }
6✔
197

198
    /// load the geometry
199
    fn load_geometry(&mut self) -> VectorGeometry {
84✔
200
        if let Some(geometry) = &self.geometry {
84✔
201
            return geometry.clone();
48✔
202
        }
36✔
203

36✔
204
        let mut pbf = self.pbf.borrow_mut();
36✔
205
        pbf.set_pos(self.geometry_index);
36✔
206

36✔
207
        let end: usize = pbf.read_varint::<usize>() + pbf.get_pos();
36✔
208
        let mut cmd: usize = 1;
36✔
209
        let mut length: isize = 0;
36✔
210
        let mut x: i32 = 0;
36✔
211
        let mut y: i32 = 0;
36✔
212

36✔
213
        let mut points: VectorPoints = vec![];
36✔
214
        let mut lines: VectorLinesWithOffset = vec![];
36✔
215
        let mut polys: Vec<VectorLinesWithOffset> = vec![];
36✔
216

217
        while pbf.get_pos() < end {
258✔
218
            if length <= 0 {
222✔
219
                let cmd_len: usize = pbf.read_varint();
144✔
220
                cmd = cmd_len & 0x7;
144✔
221
                length = (cmd_len >> 3) as isize;
144✔
222
            }
144✔
223

224
            length -= 1;
222✔
225

222✔
226
            if cmd == 1 || cmd == 2 {
222✔
227
                x += pbf.read_s_varint::<i32>();
177✔
228
                y += pbf.read_s_varint::<i32>();
177✔
229

177✔
230
                if cmd == 1 {
177✔
231
                    // moveTo
232
                    if !points.is_empty() && self.r#type != FeatureType::Point {
66✔
233
                        lines.push((&points[..]).into());
3✔
234
                        points = vec![];
3✔
235
                    }
63✔
236
                }
111✔
237
                points.push(Point::new(x, y));
177✔
238
            } else if cmd == 4 {
45✔
239
                // close poly
240
                if !points.is_empty() {
9✔
241
                    lines.push((&points[..]).into());
×
242
                }
9✔
243
                polys.push(lines);
9✔
244
                lines = vec![];
9✔
245
                points = vec![];
9✔
246
            } else if cmd == 7 {
36✔
247
                // close path
248
                if !points.is_empty() {
36✔
249
                    points.push(points[0].clone());
36✔
250
                    lines.push((&points[..]).into());
36✔
251
                    points = vec![];
36✔
252
                }
36✔
253
            } else {
254
                #[tarpaulin::skip]
255
                panic!("unknown cmd: {}", cmd);
×
256
            }
257
        }
258

259
        let geometry = if self.r#type == FeatureType::Point {
36✔
260
            VectorGeometry::VectorPoints(points)
12✔
261
        } else {
262
            if !points.is_empty() {
24✔
263
                lines.push(VectorLineWithOffset::new(0.0, points.clone()));
6✔
264
            }
18✔
265
            if self.r#type == FeatureType::Line {
24✔
266
                VectorGeometry::VectorLines(lines)
6✔
267
            } else if (self.r#type == FeatureType::MultiPolygon
18✔
268
                || self.r#type == FeatureType::Polygon)
12✔
269
                && !self.is_s2
18✔
270
            {
271
                VectorGeometry::VectorPolys(classify_rings(&lines))
12✔
272
            } else {
273
                VectorGeometry::VectorPolys(polys)
6✔
274
            }
275
        };
276

277
        self.geometry = Some(geometry.clone());
36✔
278
        geometry
36✔
279
    }
84✔
280

281
    /// load the indices
282
    fn read_indices(&mut self) -> Vec<u32> {
30✔
283
        if let Some(indices) = &self.indices {
30✔
284
            return indices.clone();
6✔
285
        } else if self.indices_index.is_none() {
24✔
286
            return vec![];
18✔
287
        }
6✔
288

6✔
289
        let mut pbf = self.pbf.borrow_mut();
6✔
290
        pbf.set_pos(self.indices_index.unwrap());
6✔
291

6✔
292
        let mut curr: i32 = 0;
6✔
293
        let end = pbf.read_varint::<usize>() + pbf.get_pos();
6✔
294
        // build indices
6✔
295
        let mut indices: Vec<u32> = vec![];
6✔
296
        while pbf.get_pos() < end {
21✔
297
            curr += pbf.read_s_varint::<i32>();
15✔
298
            indices.push(curr as u32);
15✔
299
        }
15✔
300

301
        self.indices = Some(indices.clone());
6✔
302
        indices
6✔
303
    }
30✔
304

305
    /// Add tessellation data to the geometry
306
    fn add_tessellation(&mut self, geometry: &mut Vec<f64>, multiplier: f64) {
18✔
307
        if self.tessellation_index.is_none() {
18✔
308
            return;
6✔
309
        }
12✔
310

12✔
311
        let mut pbf = self.pbf.borrow_mut();
12✔
312
        pbf.set_pos(self.tessellation_index.unwrap());
12✔
313

12✔
314
        let end = pbf.read_varint::<usize>() + pbf.get_pos();
12✔
315
        let mut x = 0;
12✔
316
        let mut y = 0;
12✔
317
        while pbf.get_pos() < end {
30✔
318
            x += pbf.read_s_varint::<i32>();
18✔
319
            y += pbf.read_s_varint::<i32>();
18✔
320
            geometry.push(x as f64 * multiplier);
18✔
321
            geometry.push(y as f64 * multiplier);
18✔
322
        }
18✔
323
    }
18✔
324

325
    /// Add 3D tessellation data to the geometry
326
    #[tarpaulin::skip]
327
    fn add_tessellation_3d(&mut self, _geometry: &mut Vec<f64>, _multiplier: f64) {
3✔
328
        panic!("unexpected geometry type")
3✔
329
    }
330
}
331
impl ProtoRead for MapboxVectorFeature {
332
    fn read(&mut self, tag: u64, pb: &mut Protobuf) {
141✔
333
        if self.is_s2 {
141✔
334
            match tag {
87✔
335
                15 => self.id = Some(pb.read_varint::<u64>()),
12✔
336
                1 => {
337
                    let end = pb.get_pos() + pb.read_varint::<usize>();
21✔
338

339
                    while pb.get_pos() < end {
84✔
340
                        let key = &self.keys.borrow()[pb.read_varint::<usize>()];
63✔
341
                        let value = &self.values.borrow()[pb.read_varint::<usize>()];
63✔
342

63✔
343
                        self.properties.insert(key.clone(), value.clone());
63✔
344
                    }
63✔
345
                }
346
                2 => self.r#type = pb.read_varint::<FeatureType>(),
21✔
347
                3 => self.geometry_index = pb.get_pos(),
21✔
348
                4 => self.indices_index = Some(pb.get_pos()),
6✔
349
                5 => self.tessellation_index = Some(pb.get_pos()),
6✔
350
                #[tarpaulin::skip]
351
                _ => panic!("unknown tag: {}", tag),
×
352
            }
353
        } else {
354
            match tag {
54✔
355
                1 => self.id = Some(pb.read_varint::<u64>()),
6✔
356
                2 => {
357
                    let end = pb.get_pos() + pb.read_varint::<usize>();
12✔
358

359
                    while pb.get_pos() < end {
12✔
360
                        let key = &self.keys.borrow()[pb.read_varint::<usize>()];
×
361
                        let value = &self.values.borrow()[pb.read_varint::<usize>()];
×
362

×
363
                        self.properties.insert(key.clone(), value.clone());
×
364
                    }
×
365
                }
366
                3 => self.r#type = pb.read_varint::<FeatureType>(),
18✔
367
                4 => self.geometry_index = pb.get_pos(),
18✔
368
                5 => self.indices_index = Some(pb.get_pos()),
×
NEW
369
                6 => self.tessellation_index = Some(pb.get_pos()),
×
370
                #[tarpaulin::skip]
371
                _ => panic!("unknown tag: {}", tag),
×
372
            }
373
        }
374
    }
141✔
375
}
376

377
fn classify_rings(rings: &VectorLinesWithOffset) -> Vec<VectorLinesWithOffset> {
12✔
378
    let mut polygons: Vec<VectorLinesWithOffset> = vec![];
12✔
379
    let mut polygon: VectorLinesWithOffset = vec![];
12✔
380
    let mut ccw: Option<bool> = None;
12✔
381

12✔
382
    let mut i: usize = 0;
12✔
383
    while i < rings.len() {
36✔
384
        let area = signed_area(&rings[i].geometry);
24✔
385
        if area == 0 {
24✔
386
            continue;
×
387
        }
24✔
388
        if ccw.is_none() {
24✔
389
            ccw = Some(area < 0);
12✔
390
        }
12✔
391

392
        if ccw.is_some() && ccw.unwrap() == (area < 0) {
24✔
393
            // outer poly ring
394
            if !polygon.is_empty() {
24✔
395
                polygons.push(polygon.clone());
12✔
396
                polygon = vec![];
12✔
397
            }
12✔
398
            polygon.push(rings[i].clone());
24✔
399
        } else {
×
400
            // inner poly ring (hole)
×
401
            polygon.push(rings[i].clone());
×
402
        }
×
403

404
        i += 1
24✔
405
    }
406
    if !polygon.is_empty() {
12✔
407
        polygons.push(polygon.clone());
12✔
408
    }
12✔
409

410
    polygons
12✔
411
}
12✔
412

413
fn signed_area(ring: &[Point]) -> i32 {
24✔
414
    let mut sum: i32 = 0;
24✔
415
    let mut i: usize = 0;
24✔
416
    let mut j = ring.len() - 1;
24✔
417
    while i < ring.len() {
144✔
418
        let p1 = &ring[i];
120✔
419
        let p2 = &ring[j];
120✔
420
        sum += (p2.x - p1.x) * (p1.y + p2.y);
120✔
421

120✔
422
        j = i;
120✔
423
        i += 1;
120✔
424
    }
120✔
425

426
    sum
24✔
427
}
24✔
428

429
/// Mapbox Vector Feature types.
430
#[derive(Debug, Clone, PartialEq)]
431
pub enum FeatureType {
432
    /// Point Feature
433
    Point = 1,
434
    /// Line Feature
435
    Line = 2,
436
    /// Polygon Feature
437
    Polygon = 3,
438
    /// MultiPolygon Feature
439
    MultiPolygon = 4,
440
}
441
impl From<OpenFeatureType> for FeatureType {
442
    fn from(value: OpenFeatureType) -> Self {
21✔
443
        match value {
21✔
444
            OpenFeatureType::Points => FeatureType::Point,
9✔
445
            OpenFeatureType::Lines => FeatureType::Line,
6✔
446
            OpenFeatureType::Polygons => FeatureType::MultiPolygon,
6✔
447
            #[tarpaulin::skip]
448
            _ => panic!("unknown value: {:?}", value),
×
449
        }
450
    }
21✔
451
}
452
impl BitCast for FeatureType {
453
    fn to_u64(&self) -> u64 {
21✔
454
        (*self).clone() as u64
21✔
455
    }
21✔
456
    fn from_u64(value: u64) -> Self {
39✔
457
        match value {
39✔
458
            1 => FeatureType::Point,
15✔
459
            2 => FeatureType::Line,
6✔
460
            3 => FeatureType::Polygon,
12✔
461
            4 => FeatureType::MultiPolygon,
6✔
462
            #[tarpaulin::skip]
463
            _ => panic!("unknown value: {}", value),
×
464
        }
465
    }
39✔
466
}
467

468
/// Write a feature to a protobuffer using the S2 Specification
469
pub fn write_feature(
21✔
470
    feature: &BaseVectorFeature,
21✔
471
    keys: &mut BTreeMap<String, usize>,
21✔
472
    values: &mut BTreeMap<PrimitiveValue, usize>,
21✔
473
    mapbox_support: bool,
21✔
474
) -> Vec<u8> {
21✔
475
    let mut pbf = Protobuf::new();
21✔
476

21✔
477
    let properties: MapboxProperties = feature.properties().clone().into();
21✔
478
    if let Some(id) = feature.id() {
21✔
479
        pbf.write_varint_field(if mapbox_support { 1 } else { 15 }, id);
12✔
480
    }
9✔
481
    pbf.write_bytes_field(
21✔
482
        if mapbox_support { 2 } else { 1 },
21✔
483
        &write_properties(&properties, keys, values),
21✔
484
    );
21✔
485
    let _type: FeatureType = feature.get_type().into();
21✔
486
    pbf.write_varint_field(if mapbox_support { 3 } else { 2 }, _type);
21✔
487
    // Geometry
21✔
488
    let written = write_geometry(feature, mapbox_support);
21✔
489
    pbf.write_bytes_field(if mapbox_support { 4 } else { 3 }, &written);
21✔
490
    // Indices
491
    if let Some(indices) = feature.indices() {
21✔
492
        pbf.write_bytes_field(if mapbox_support { 5 } else { 4 }, &write_indices(&indices));
6✔
493
    }
15✔
494
    // Tessellation
495
    if let Some(TessellationWrapper::Tessellation(tess)) = feature.tessellation() {
21✔
496
        pbf.write_bytes_field(if mapbox_support { 6 } else { 5 }, &write_tessellation(&tess));
6✔
497
    }
15✔
498

499
    pbf.take()
21✔
500
}
21✔
501

502
/// Write a properties to a protobuffer using the S2 Specification
503
fn write_properties(
21✔
504
    properties: &MapboxProperties,
21✔
505
    keys: &mut BTreeMap<String, usize>,
21✔
506
    values: &mut BTreeMap<PrimitiveValue, usize>,
21✔
507
) -> Vec<u8> {
21✔
508
    let mut pbf = Protobuf::new();
21✔
509

510
    for (key, value) in properties.iter() {
63✔
511
        let key_length = keys.len();
63✔
512
        let key_index = keys.entry(key.clone()).or_insert(key_length);
63✔
513
        pbf.write_varint(*key_index);
63✔
514
        let value_length = values.len();
63✔
515
        let value_index = values.entry(value.clone()).or_insert(value_length);
63✔
516
        pbf.write_varint(*value_index);
63✔
517
    }
63✔
518

519
    pbf.take()
21✔
520
}
21✔
521

522
/// write the indices to a protobuffer using the S2 Specification
523
fn write_indices(indices: &[u32]) -> Vec<u8> {
6✔
524
    let mut pbf = Protobuf::new();
6✔
525

6✔
526
    let mut curr: i32 = 0;
6✔
527
    for index in indices {
21✔
528
        let d_curr = (*index as i32) - curr;
15✔
529
        pbf.write_varint(zigzag(d_curr));
15✔
530
        curr += d_curr;
15✔
531
    }
15✔
532

533
    pbf.take()
6✔
534
}
6✔
535

536
/// write the tessellation to a protobuffer using the S2 Specification
537
fn write_tessellation(geometry: &[Point]) -> Vec<u8> {
6✔
538
    let mut pbf = Protobuf::new();
6✔
539
    let mut x = 0;
6✔
540
    let mut y = 0;
6✔
541
    for point in geometry {
15✔
542
        let dx = point.x - x;
9✔
543
        let dy = point.y - y;
9✔
544
        pbf.write_varint(zigzag(dx));
9✔
545
        pbf.write_varint(zigzag(dy));
9✔
546
        x += dx;
9✔
547
        y += dy;
9✔
548
    }
9✔
549

550
    pbf.take()
6✔
551
}
6✔
552

553
/// write the geometry to a protobuffer using the S2 Specification
554
fn write_geometry(feature: &BaseVectorFeature, mapbox_support: bool) -> Vec<u8> {
21✔
555
    use BaseVectorFeature::*;
556
    let mut pbf = Protobuf::new();
21✔
557
    match feature {
21✔
558
        BaseVectorPointsFeature(points) => write_geometry_points(&points.geometry, &mut pbf),
9✔
559
        BaseVectorLinesFeature(lines) => write_geometry_lines(&lines.geometry, &mut pbf),
6✔
560
        BaseVectorPolysFeature(polys) => {
6✔
561
            write_geometry_polys(&polys.geometry, &mut pbf, mapbox_support)
6✔
562
        }
563
        #[tarpaulin::skip]
564
        _ => panic!("unknown feature type: {:?}", feature.get_type()),
×
565
    };
566
    pbf.take()
21✔
567
}
21✔
568

569
/// write the points geometry to a protobuffer using the S2 Specification
570
fn write_geometry_points(points: &[Point], pbf: &mut Protobuf) {
9✔
571
    let mut x = 0;
9✔
572
    let mut y = 0;
9✔
573

574
    for point in points {
21✔
575
        // move
12✔
576
        pbf.write_varint(command_encode(1, 1)); // moveto
12✔
577
                                                // store
12✔
578
        let dx = point.x - x;
12✔
579
        let dy = point.y - y;
12✔
580
        pbf.write_varint(zigzag(dx));
12✔
581
        pbf.write_varint(zigzag(dy));
12✔
582
        // update position
12✔
583
        x += dx;
12✔
584
        y += dy;
12✔
585
    }
12✔
586
}
9✔
587

588
/// write the lines geometry to a protobuffer using the S2 Specification
589
fn write_geometry_lines(lines: &[VectorLineWithOffset], pbf: &mut Protobuf) {
6✔
590
    let mut x = 0;
6✔
591
    let mut y = 0;
6✔
592

593
    for line in lines {
15✔
594
        let line_geo = &line.geometry;
9✔
595
        pbf.write_varint(command_encode(1, 1)); // moveto
9✔
596
                                                // do not write polygon closing path as lineto
9✔
597
        let line_count = line_geo.len();
9✔
598
        let mut i = 0;
9✔
599
        while i < line_count {
21✔
600
            if i == 1 {
12✔
601
                pbf.write_varint(command_encode(2, (line_count - 1).try_into().unwrap()));
3✔
602
                // lineto
3✔
603
            }
9✔
604

605
            let point = &line_geo[i];
12✔
606
            let dx = point.x - x;
12✔
607
            let dy = point.y - y;
12✔
608
            pbf.write_varint(zigzag(dx));
12✔
609
            pbf.write_varint(zigzag(dy));
12✔
610
            x += dx;
12✔
611
            y += dy;
12✔
612

12✔
613
            i += 1;
12✔
614
        }
615
    }
616
}
6✔
617

618
/// write the polys geometry to a protobuffer using the S2 Specification
619
fn write_geometry_polys(
6✔
620
    polys: &[Vec<VectorLineWithOffset>],
6✔
621
    pbf: &mut Protobuf,
6✔
622
    mapbox_support: bool,
6✔
623
) {
6✔
624
    let mut x = 0;
6✔
625
    let mut y = 0;
6✔
626

627
    for poly in polys {
15✔
628
        for ring in poly {
21✔
629
            let ring_geo = &ring.geometry;
12✔
630
            pbf.write_varint(command_encode(1, 1)); // moveto
12✔
631
            let line_count = ring_geo.len() - 1;
12✔
632
            let mut i = 0;
12✔
633
            while i < line_count {
60✔
634
                if i == 1 {
48✔
635
                    pbf.write_varint(command_encode(2, (line_count - 1).try_into().unwrap()));
12✔
636
                    // lineto
12✔
637
                }
36✔
638

639
                let point = &ring_geo[i];
48✔
640
                let dx = point.x - x;
48✔
641
                let dy = point.y - y;
48✔
642
                pbf.write_varint(zigzag(dx));
48✔
643
                pbf.write_varint(zigzag(dy));
48✔
644
                x += dx;
48✔
645
                y += dy;
48✔
646

48✔
647
                i += 1;
48✔
648
            }
649
            pbf.write_varint(command_encode(7, 1)); // ClosePath
12✔
650
        }
651
        // ClosePolygon (Mapbox does not support so close path if not supported)
652
        pbf.write_varint(command_encode(if mapbox_support { 7 } else { 4 }, 1));
9✔
653
    }
654
}
6✔
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