• 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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

149
    fn load_points_3d(&mut self) -> VectorPoints3D {
3✔
150
        panic!("unexpected geometry type")
3✔
151
    }
152

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

162
    /// an array of 3D lines. The offsets will be set to 0
163
    fn load_lines_3d(&mut self) -> VectorLines3DWithOffset {
3✔
164
        panic!("unexpected geometry type")
3✔
165
    }
166

167
    /// an array of polys
168
    fn load_polys(&mut self) -> Vec<VectorLinesWithOffset> {
×
169
        match self.load_geometry() {
×
170
            VectorGeometry::VectorPolys(polys) => polys,
×
171
            _ => panic!("unexpected geometry type"),
×
172
        }
173
    }
×
174

175
    /// an array of 3D polys
176
    fn load_polys_3d(&mut self) -> Vec<VectorLines3DWithOffset> {
×
177
        panic!("unexpected geometry type")
×
178
    }
179

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

6✔
204
        (geometry, indices)
6✔
205
    }
6✔
206

207
    /// load the geometry
208
    fn load_geometry(&mut self) -> VectorGeometry {
84✔
209
        if let Some(geometry) = &self.geometry {
84✔
210
            return geometry.clone();
48✔
211
        }
36✔
212

36✔
213
        let mut pbf = self.pbf.borrow_mut();
36✔
214
        pbf.set_pos(self.geometry_index);
36✔
215

36✔
216
        let end: usize = pbf.read_varint::<usize>() + pbf.get_pos();
36✔
217
        let mut cmd: usize = 1;
36✔
218
        let mut length: isize = 0;
36✔
219
        let mut x: i32 = 0;
36✔
220
        let mut y: i32 = 0;
36✔
221

36✔
222
        let mut points: VectorPoints = vec![];
36✔
223
        let mut lines: VectorLinesWithOffset = vec![];
36✔
224
        let mut polys: Vec<VectorLinesWithOffset> = vec![];
36✔
225

226
        while pbf.get_pos() < end {
258✔
227
            if length <= 0 {
222✔
228
                let cmd_len: usize = pbf.read_varint();
144✔
229
                cmd = cmd_len & 0x7;
144✔
230
                length = (cmd_len >> 3) as isize;
144✔
231
            }
144✔
232

233
            length -= 1;
222✔
234

222✔
235
            if cmd == 1 || cmd == 2 {
222✔
236
                x += pbf.read_s_varint::<i32>();
177✔
237
                y += pbf.read_s_varint::<i32>();
177✔
238

177✔
239
                if cmd == 1 {
177✔
240
                    // moveTo
241
                    if !points.is_empty() && self.r#type != FeatureType::Point {
66✔
242
                        lines.push((&points[..]).into());
3✔
243
                        points = vec![];
3✔
244
                    }
63✔
245
                }
111✔
246
                points.push(Point::new(x, y));
177✔
247
            } else if cmd == 4 {
45✔
248
                // close poly
249
                if !points.is_empty() {
9✔
250
                    lines.push((&points[..]).into());
×
251
                }
9✔
252
                polys.push(lines);
9✔
253
                lines = vec![];
9✔
254
                points = vec![];
9✔
255
            } else if cmd == 7 {
36✔
256
                // close path
257
                if !points.is_empty() {
36✔
258
                    points.push(points[0].clone());
36✔
259
                    lines.push((&points[..]).into());
36✔
260
                    points = vec![];
36✔
261
                }
36✔
262
            } else {
263
                panic!("unknown cmd: {}", cmd);
×
264
            }
265
        }
266

267
        let geometry = if self.r#type == FeatureType::Point {
36✔
268
            VectorGeometry::VectorPoints(points)
12✔
269
        } else {
270
            if !points.is_empty() {
24✔
271
                lines.push(VectorLineWithOffset::new(0.0, points.clone()));
6✔
272
            }
18✔
273
            if self.r#type == FeatureType::Line {
24✔
274
                VectorGeometry::VectorLines(lines)
6✔
275
            } else if (self.r#type == FeatureType::MultiPolygon
18✔
276
                || self.r#type == FeatureType::Polygon)
12✔
277
                && !self.is_s2
18✔
278
            {
279
                VectorGeometry::VectorPolys(classify_rings(&lines))
12✔
280
            } else {
281
                VectorGeometry::VectorPolys(polys)
6✔
282
            }
283
        };
284

285
        self.geometry = Some(geometry.clone());
36✔
286
        geometry
36✔
287
    }
84✔
288

289
    /// load the indices
290
    fn read_indices(&mut self) -> Vec<u32> {
30✔
291
        if let Some(indices) = &self.indices {
30✔
292
            return indices.clone();
6✔
293
        } else if self.indices_index.is_none() {
24✔
294
            return vec![];
18✔
295
        }
6✔
296

6✔
297
        let mut pbf = self.pbf.borrow_mut();
6✔
298
        pbf.set_pos(self.indices_index.unwrap());
6✔
299

6✔
300
        let mut curr: i32 = 0;
6✔
301
        let end = pbf.read_varint::<usize>() + pbf.get_pos();
6✔
302
        // build indices
6✔
303
        let mut indices: Vec<u32> = vec![];
6✔
304
        while pbf.get_pos() < end {
21✔
305
            curr += pbf.read_s_varint::<i32>();
15✔
306
            indices.push(curr as u32);
15✔
307
        }
15✔
308

309
        self.indices = Some(indices.clone());
6✔
310
        indices
6✔
311
    }
30✔
312

313
    /// Add tessellation data to the geometry
314
    fn add_tessellation(&mut self, geometry: &mut Vec<f64>, multiplier: f64) {
18✔
315
        if self.tessellation_index.is_none() {
18✔
316
            return;
6✔
317
        }
12✔
318

12✔
319
        let mut pbf = self.pbf.borrow_mut();
12✔
320
        pbf.set_pos(self.tessellation_index.unwrap());
12✔
321

12✔
322
        let end = pbf.read_varint::<usize>() + pbf.get_pos();
12✔
323
        let mut x = 0;
12✔
324
        let mut y = 0;
12✔
325
        while pbf.get_pos() < end {
30✔
326
            x += pbf.read_s_varint::<i32>();
18✔
327
            y += pbf.read_s_varint::<i32>();
18✔
328
            geometry.push(x as f64 * multiplier);
18✔
329
            geometry.push(y as f64 * multiplier);
18✔
330
        }
18✔
331
    }
18✔
332

333
    /// Add 3D tessellation data to the geometry
334
    fn add_tessellation_3d(&mut self, _geometry: &mut Vec<f64>, _multiplier: f64) {
3✔
335
        panic!("unexpected geometry type")
3✔
336
    }
337
}
338
impl ProtoRead for MapboxVectorFeature {
339
    fn read(&mut self, tag: u64, pb: &mut Protobuf) {
141✔
340
        if self.is_s2 {
141✔
341
            match tag {
87✔
342
                15 => self.id = Some(pb.read_varint::<u64>()),
12✔
343
                1 => {
344
                    let end = pb.get_pos() + pb.read_varint::<usize>();
21✔
345

346
                    while pb.get_pos() < end {
84✔
347
                        let key = &self.keys.borrow()[pb.read_varint::<usize>()];
63✔
348
                        let value = &self.values.borrow()[pb.read_varint::<usize>()];
63✔
349

63✔
350
                        self.properties.insert(key.clone(), value.clone());
63✔
351
                    }
63✔
352
                }
353
                2 => self.r#type = pb.read_varint::<FeatureType>(),
21✔
354
                3 => self.geometry_index = pb.get_pos(),
21✔
355
                4 => self.indices_index = Some(pb.get_pos()),
6✔
356
                5 => self.tessellation_index = Some(pb.get_pos()),
6✔
357

358
                _ => panic!("unknown tag: {}", tag),
×
359
            }
360
        } else {
361
            match tag {
54✔
362
                1 => self.id = Some(pb.read_varint::<u64>()),
6✔
363
                2 => {
364
                    let end = pb.get_pos() + pb.read_varint::<usize>();
12✔
365

366
                    while pb.get_pos() < end {
12✔
367
                        let key = &self.keys.borrow()[pb.read_varint::<usize>()];
×
368
                        let value = &self.values.borrow()[pb.read_varint::<usize>()];
×
369

×
370
                        self.properties.insert(key.clone(), value.clone());
×
371
                    }
×
372
                }
373
                3 => self.r#type = pb.read_varint::<FeatureType>(),
18✔
374
                4 => self.geometry_index = pb.get_pos(),
18✔
375
                5 => self.indices_index = Some(pb.get_pos()),
×
376
                6 => self.tessellation_index = Some(pb.get_pos()),
×
377

378
                _ => panic!("unknown tag: {}", tag),
×
379
            }
380
        }
381
    }
141✔
382
}
383

384
fn classify_rings(rings: &VectorLinesWithOffset) -> Vec<VectorLinesWithOffset> {
12✔
385
    let mut polygons: Vec<VectorLinesWithOffset> = vec![];
12✔
386
    let mut polygon: VectorLinesWithOffset = vec![];
12✔
387
    let mut ccw: Option<bool> = None;
12✔
388

12✔
389
    let mut i: usize = 0;
12✔
390
    while i < rings.len() {
36✔
391
        let area = signed_area(&rings[i].geometry);
24✔
392
        if area == 0 {
24✔
393
            continue;
×
394
        }
24✔
395
        if ccw.is_none() {
24✔
396
            ccw = Some(area < 0);
12✔
397
        }
12✔
398

399
        if ccw.is_some() && ccw.unwrap() == (area < 0) {
24✔
400
            // outer poly ring
401
            if !polygon.is_empty() {
24✔
402
                polygons.push(polygon.clone());
12✔
403
                polygon = vec![];
12✔
404
            }
12✔
405
            polygon.push(rings[i].clone());
24✔
406
        } else {
×
407
            // inner poly ring (hole)
×
408
            polygon.push(rings[i].clone());
×
409
        }
×
410

411
        i += 1
24✔
412
    }
413
    if !polygon.is_empty() {
12✔
414
        polygons.push(polygon.clone());
12✔
415
    }
12✔
416

417
    polygons
12✔
418
}
12✔
419

420
fn signed_area(ring: &[Point]) -> i32 {
24✔
421
    let mut sum: i32 = 0;
24✔
422
    let mut i: usize = 0;
24✔
423
    let mut j = ring.len() - 1;
24✔
424
    while i < ring.len() {
144✔
425
        let p1 = &ring[i];
120✔
426
        let p2 = &ring[j];
120✔
427
        sum += (p2.x - p1.x) * (p1.y + p2.y);
120✔
428

120✔
429
        j = i;
120✔
430
        i += 1;
120✔
431
    }
120✔
432

433
    sum
24✔
434
}
24✔
435

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

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

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

504
    pbf.take()
21✔
505
}
21✔
506

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

515
    for (key, value) in properties.iter() {
63✔
516
        let key_length = keys.len();
63✔
517
        let key_index = keys.entry(key.clone()).or_insert(key_length);
63✔
518
        pbf.write_varint(*key_index);
63✔
519
        let value_length = values.len();
63✔
520
        let value_index = values.entry(value.clone()).or_insert(value_length);
63✔
521
        pbf.write_varint(*value_index);
63✔
522
    }
63✔
523

524
    pbf.take()
21✔
525
}
21✔
526

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

6✔
531
    let mut curr: i32 = 0;
6✔
532
    for index in indices {
21✔
533
        let d_curr = (*index as i32) - curr;
15✔
534
        pbf.write_varint(zigzag(d_curr));
15✔
535
        curr += d_curr;
15✔
536
    }
15✔
537

538
    pbf.take()
6✔
539
}
6✔
540

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

555
    pbf.take()
6✔
556
}
6✔
557

558
/// write the geometry to a protobuffer using the S2 Specification
559
fn write_geometry(feature: &BaseVectorFeature, mapbox_support: bool) -> Vec<u8> {
21✔
560
    use BaseVectorFeature::*;
561
    let mut pbf = Protobuf::new();
21✔
562
    match feature {
21✔
563
        BaseVectorPointsFeature(points) => write_geometry_points(&points.geometry, &mut pbf),
9✔
564
        BaseVectorLinesFeature(lines) => write_geometry_lines(&lines.geometry, &mut pbf),
6✔
565
        BaseVectorPolysFeature(polys) => {
6✔
566
            write_geometry_polys(&polys.geometry, &mut pbf, mapbox_support)
6✔
567
        }
568

569
        _ => panic!("unknown feature type: {:?}", feature.get_type()),
×
570
    };
571
    pbf.take()
21✔
572
}
21✔
573

574
/// write the points geometry to a protobuffer using the S2 Specification
575
fn write_geometry_points(points: &[Point], pbf: &mut Protobuf) {
9✔
576
    let mut x = 0;
9✔
577
    let mut y = 0;
9✔
578

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

593
/// write the lines geometry to a protobuffer using the S2 Specification
594
fn write_geometry_lines(lines: &[VectorLineWithOffset], pbf: &mut Protobuf) {
6✔
595
    let mut x = 0;
6✔
596
    let mut y = 0;
6✔
597

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

610
            let point = &line_geo[i];
12✔
611
            let dx = point.x - x;
12✔
612
            let dy = point.y - y;
12✔
613
            pbf.write_varint(zigzag(dx));
12✔
614
            pbf.write_varint(zigzag(dy));
12✔
615
            x += dx;
12✔
616
            y += dy;
12✔
617

12✔
618
            i += 1;
12✔
619
        }
620
    }
621
}
6✔
622

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

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

644
                let point = &ring_geo[i];
48✔
645
                let dx = point.x - x;
48✔
646
                let dy = point.y - y;
48✔
647
                pbf.write_varint(zigzag(dx));
48✔
648
                pbf.write_varint(zigzag(dy));
48✔
649
                x += dx;
48✔
650
                y += dy;
48✔
651

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