open_vector_tile/mapbox/
vector_feature.rs

1use 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};
9use alloc::{collections::BTreeMap, rc::Rc, string::String, vec, vec::Vec};
10use core::cell::RefCell;
11use pbf::{BitCast, ProtoRead, Protobuf};
12use s2json::{BBOX, MapboxProperties, PrimitiveValue, Properties};
13
14/// Mapbox specification for a Feature
15#[derive(Debug)]
16pub 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}
39impl MapboxVectorFeature {
40    /// Create a new MapboxVectorFeature
41    pub fn new(
42        pbf: Rc<RefCell<Protobuf>>,
43        is_s2: bool,
44        extent: usize,
45        version: u16,
46        keys: Rc<RefCell<Vec<String>>>,
47        values: Rc<RefCell<Vec<PrimitiveValue>>>,
48    ) -> MapboxVectorFeature {
49        MapboxVectorFeature {
50            id: None,
51            version,
52            properties: MapboxProperties::new(),
53            extent,
54            r#type: FeatureType::Point,
55            is_s2,
56            // tmp pbf until after reading in attributes
57            indices_index: None,
58            indices: None,
59            geometry_index: 0,
60            geometry: None,
61            tessellation_index: None,
62            keys,
63            values,
64            pbf,
65        }
66    }
67}
68impl VectorFeatureMethods for MapboxVectorFeature {
69    /// get the feature id
70    fn id(&self) -> Option<u64> {
71        self.id
72    }
73
74    /// get the feature version
75    fn version(&self) -> u16 {
76        self.version
77    }
78
79    /// get the feature properties
80    fn properties(&self) -> Properties {
81        (&self.properties).into()
82    }
83
84    /// get the feature extent
85    fn extent(&self) -> usize {
86        self.extent
87    }
88
89    /// get the feature type
90    fn get_type(&self) -> OpenFeatureType {
91        (&self.r#type).into()
92    }
93
94    /// get the bbox
95    fn bbox(&self) -> Option<BBOX> {
96        None
97    }
98
99    /// whether the feature has m values
100    fn has_m_values(&self) -> bool {
101        false
102    }
103
104    /// whether the feature is a points type
105    fn is_points(&self) -> bool {
106        self.r#type == FeatureType::Point
107    }
108
109    /// whether the feature is a line type
110    fn is_lines(&self) -> bool {
111        self.r#type == FeatureType::Line
112    }
113
114    /// whether the feature is a polygon type
115    fn is_polygons(&self) -> bool {
116        self.r#type == FeatureType::Polygon || self.r#type == FeatureType::MultiPolygon
117    }
118
119    /// whether the feature is a points 3D type
120    fn is_points_3d(&self) -> bool {
121        false
122    }
123
124    /// whether the feature is a line 3D type
125    fn is_lines_3d(&self) -> bool {
126        false
127    }
128
129    /// whether the feature is a polygon 3D type
130    fn is_polygons_3d(&self) -> bool {
131        false
132    }
133
134    /// regardless of the type, we return a flattend point array
135    fn load_points(&mut self) -> VectorPoints {
136        match self.load_geometry() {
137            VectorGeometry::VectorPoints(p) => p,
138            VectorGeometry::VectorLines(lines) => {
139                lines.iter().flat_map(|p| p.geometry.clone()).collect()
140            }
141            VectorGeometry::VectorPolys(polys) => polys
142                .iter()
143                .flat_map(|p| p.iter().flat_map(|p| p.geometry[..p.geometry.len() - 1].to_vec()))
144                .collect(),
145            _ => panic!("unexpected geometry type"),
146        }
147    }
148
149    fn load_points_3d(&mut self) -> VectorPoints3D {
150        panic!("unexpected geometry type")
151    }
152
153    /// an array of lines. The offsets will be set to 0
154    fn load_lines(&mut self) -> VectorLinesWithOffset {
155        match self.load_geometry() {
156            VectorGeometry::VectorLines(lines) => lines,
157            VectorGeometry::VectorPolys(polys) => polys.iter().flat_map(|p| p.clone()).collect(),
158            _ => panic!("unexpected geometry type"),
159        }
160    }
161
162    /// an array of 3D lines. The offsets will be set to 0
163    fn load_lines_3d(&mut self) -> VectorLines3DWithOffset {
164        panic!("unexpected geometry type")
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>) {
182        // build a multiplier
183        let multiplier: f64 = 1.0 / self.extent as f64;
184        // grab the geometry, flatten it, and mutate to an f64
185        let mut geometry: Vec<f64> = match self.load_geometry() {
186            VectorGeometry::VectorPolys(polys) => polys
187                .iter()
188                .flat_map(|p| {
189                    p.iter().flat_map(|p| {
190                        p.geometry
191                            .clone()
192                            .into_iter()
193                            .flat_map(|p| vec![p.x as f64 * multiplier, p.y as f64 * multiplier])
194                    })
195                })
196                .collect(),
197            _ => panic!("unexpected geometry type"),
198        };
199        // if a poly, check if we should load indices
200        let indices = self.read_indices();
201        // if a poly, check if we should load tessellation
202        self.add_tessellation(&mut geometry, multiplier);
203
204        (geometry, indices)
205    }
206
207    /// load the geometry
208    fn load_geometry(&mut self) -> VectorGeometry {
209        if let Some(geometry) = &self.geometry {
210            return geometry.clone();
211        }
212
213        let mut pbf = self.pbf.borrow_mut();
214        pbf.set_pos(self.geometry_index);
215
216        let end: usize = pbf.read_varint::<usize>() + pbf.get_pos();
217        let mut cmd: usize = 1;
218        let mut length: isize = 0;
219        let mut x: i32 = 0;
220        let mut y: i32 = 0;
221
222        let mut points: VectorPoints = vec![];
223        let mut lines: VectorLinesWithOffset = vec![];
224        let mut polys: Vec<VectorLinesWithOffset> = vec![];
225
226        while pbf.get_pos() < end {
227            if length <= 0 {
228                let cmd_len: usize = pbf.read_varint();
229                cmd = cmd_len & 0x7;
230                length = (cmd_len >> 3) as isize;
231            }
232
233            length -= 1;
234
235            if cmd == 1 || cmd == 2 {
236                x += pbf.read_s_varint::<i32>();
237                y += pbf.read_s_varint::<i32>();
238
239                if cmd == 1 {
240                    // moveTo
241                    if !points.is_empty() && self.r#type != FeatureType::Point {
242                        lines.push((&points[..]).into());
243                        points = vec![];
244                    }
245                }
246                points.push(Point::new(x, y));
247            } else if cmd == 4 {
248                // close poly
249                if !points.is_empty() {
250                    lines.push((&points[..]).into());
251                }
252                polys.push(lines);
253                lines = vec![];
254                points = vec![];
255            } else if cmd == 7 {
256                // close path
257                if !points.is_empty() {
258                    points.push(points[0].clone());
259                    lines.push((&points[..]).into());
260                    points = vec![];
261                }
262            } else {
263                panic!("unknown cmd: {}", cmd);
264            }
265        }
266
267        let geometry = if self.r#type == FeatureType::Point {
268            VectorGeometry::VectorPoints(points)
269        } else {
270            if !points.is_empty() {
271                lines.push(VectorLineWithOffset::new(0.0, points.clone()));
272            }
273            if self.r#type == FeatureType::Line {
274                VectorGeometry::VectorLines(lines)
275            } else if (self.r#type == FeatureType::MultiPolygon
276                || self.r#type == FeatureType::Polygon)
277                && !self.is_s2
278            {
279                VectorGeometry::VectorPolys(classify_rings(&lines))
280            } else {
281                VectorGeometry::VectorPolys(polys)
282            }
283        };
284
285        self.geometry = Some(geometry.clone());
286        geometry
287    }
288
289    /// load the indices
290    fn read_indices(&mut self) -> Vec<u32> {
291        if let Some(indices) = &self.indices {
292            return indices.clone();
293        } else if self.indices_index.is_none() {
294            return vec![];
295        }
296
297        let mut pbf = self.pbf.borrow_mut();
298        pbf.set_pos(self.indices_index.unwrap());
299
300        let mut curr: i32 = 0;
301        let end = pbf.read_varint::<usize>() + pbf.get_pos();
302        // build indices
303        let mut indices: Vec<u32> = vec![];
304        while pbf.get_pos() < end {
305            curr += pbf.read_s_varint::<i32>();
306            indices.push(curr as u32);
307        }
308
309        self.indices = Some(indices.clone());
310        indices
311    }
312
313    /// Add tessellation data to the geometry
314    fn add_tessellation(&mut self, geometry: &mut Vec<f64>, multiplier: f64) {
315        if self.tessellation_index.is_none() {
316            return;
317        }
318
319        let mut pbf = self.pbf.borrow_mut();
320        pbf.set_pos(self.tessellation_index.unwrap());
321
322        let end = pbf.read_varint::<usize>() + pbf.get_pos();
323        let mut x = 0;
324        let mut y = 0;
325        while pbf.get_pos() < end {
326            x += pbf.read_s_varint::<i32>();
327            y += pbf.read_s_varint::<i32>();
328            geometry.push(x as f64 * multiplier);
329            geometry.push(y as f64 * multiplier);
330        }
331    }
332
333    /// Add 3D tessellation data to the geometry
334    fn add_tessellation_3d(&mut self, _geometry: &mut Vec<f64>, _multiplier: f64) {
335        panic!("unexpected geometry type")
336    }
337}
338impl ProtoRead for MapboxVectorFeature {
339    fn read(&mut self, tag: u64, pb: &mut Protobuf) {
340        if self.is_s2 {
341            match tag {
342                15 => self.id = Some(pb.read_varint::<u64>()),
343                1 => {
344                    let end = pb.get_pos() + pb.read_varint::<usize>();
345
346                    while pb.get_pos() < end {
347                        let key = &self.keys.borrow()[pb.read_varint::<usize>()];
348                        let value = &self.values.borrow()[pb.read_varint::<usize>()];
349
350                        self.properties.insert(key.clone(), value.clone());
351                    }
352                }
353                2 => self.r#type = pb.read_varint::<FeatureType>(),
354                3 => self.geometry_index = pb.get_pos(),
355                4 => self.indices_index = Some(pb.get_pos()),
356                5 => self.tessellation_index = Some(pb.get_pos()),
357
358                _ => panic!("unknown tag: {}", tag),
359            }
360        } else {
361            match tag {
362                1 => self.id = Some(pb.read_varint::<u64>()),
363                2 => {
364                    let end = pb.get_pos() + pb.read_varint::<usize>();
365
366                    while pb.get_pos() < end {
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>(),
374                4 => self.geometry_index = pb.get_pos(),
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    }
382}
383
384fn classify_rings(rings: &VectorLinesWithOffset) -> Vec<VectorLinesWithOffset> {
385    let mut polygons: Vec<VectorLinesWithOffset> = vec![];
386    let mut polygon: VectorLinesWithOffset = vec![];
387    let mut ccw: Option<bool> = None;
388
389    let mut i: usize = 0;
390    while i < rings.len() {
391        let area = signed_area(&rings[i].geometry);
392        if area == 0 {
393            continue;
394        }
395        if ccw.is_none() {
396            ccw = Some(area < 0);
397        }
398
399        if ccw.is_some() && ccw.unwrap() == (area < 0) {
400            // outer poly ring
401            if !polygon.is_empty() {
402                polygons.push(polygon.clone());
403                polygon = vec![];
404            }
405            polygon.push(rings[i].clone());
406        } else {
407            // inner poly ring (hole)
408            polygon.push(rings[i].clone());
409        }
410
411        i += 1
412    }
413    if !polygon.is_empty() {
414        polygons.push(polygon.clone());
415    }
416
417    polygons
418}
419
420fn signed_area(ring: &[Point]) -> i32 {
421    let mut sum: i32 = 0;
422    let mut i: usize = 0;
423    let mut j = ring.len() - 1;
424    while i < ring.len() {
425        let p1 = &ring[i];
426        let p2 = &ring[j];
427        sum += (p2.x - p1.x) * (p1.y + p2.y);
428
429        j = i;
430        i += 1;
431    }
432
433    sum
434}
435
436/// Mapbox Vector Feature types.
437#[derive(Debug, Clone, PartialEq)]
438pub 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}
448impl From<OpenFeatureType> for FeatureType {
449    fn from(value: OpenFeatureType) -> Self {
450        match value {
451            OpenFeatureType::Points => FeatureType::Point,
452            OpenFeatureType::Lines => FeatureType::Line,
453            OpenFeatureType::Polygons => FeatureType::MultiPolygon,
454            _ => panic!("unknown value: {:?}", value),
455        }
456    }
457}
458impl BitCast for FeatureType {
459    fn to_u64(&self) -> u64 {
460        (*self).clone() as u64
461    }
462    fn from_u64(value: u64) -> Self {
463        match value {
464            1 => FeatureType::Point,
465            2 => FeatureType::Line,
466            3 => FeatureType::Polygon,
467            4 => FeatureType::MultiPolygon,
468            _ => panic!("unknown value: {}", value),
469        }
470    }
471}
472
473/// Write a feature to a protobuffer using the S2 Specification
474pub fn write_feature(
475    feature: &BaseVectorFeature,
476    keys: &mut BTreeMap<String, usize>,
477    values: &mut BTreeMap<PrimitiveValue, usize>,
478    mapbox_support: bool,
479) -> Vec<u8> {
480    let mut pbf = Protobuf::new();
481
482    let properties: MapboxProperties = feature.properties().clone().into();
483    if let Some(id) = feature.id() {
484        pbf.write_varint_field(if mapbox_support { 1 } else { 15 }, id);
485    }
486    pbf.write_bytes_field(
487        if mapbox_support { 2 } else { 1 },
488        &write_properties(&properties, keys, values),
489    );
490    let _type: FeatureType = feature.get_type().into();
491    pbf.write_varint_field(if mapbox_support { 3 } else { 2 }, _type);
492    // Geometry
493    let written = write_geometry(feature, mapbox_support);
494    pbf.write_bytes_field(if mapbox_support { 4 } else { 3 }, &written);
495    // Indices
496    if let Some(indices) = feature.indices() {
497        pbf.write_bytes_field(if mapbox_support { 5 } else { 4 }, &write_indices(&indices));
498    }
499    // Tessellation
500    if let Some(TessellationWrapper::Tessellation(tess)) = feature.tessellation() {
501        pbf.write_bytes_field(if mapbox_support { 6 } else { 5 }, &write_tessellation(&tess));
502    }
503
504    pbf.take()
505}
506
507/// Write a properties to a protobuffer using the S2 Specification
508fn write_properties(
509    properties: &MapboxProperties,
510    keys: &mut BTreeMap<String, usize>,
511    values: &mut BTreeMap<PrimitiveValue, usize>,
512) -> Vec<u8> {
513    let mut pbf = Protobuf::new();
514
515    for (key, value) in properties.iter() {
516        let key_length = keys.len();
517        let key_index = keys.entry(key.clone()).or_insert(key_length);
518        pbf.write_varint(*key_index);
519        let value_length = values.len();
520        let value_index = values.entry(value.clone()).or_insert(value_length);
521        pbf.write_varint(*value_index);
522    }
523
524    pbf.take()
525}
526
527/// write the indices to a protobuffer using the S2 Specification
528fn write_indices(indices: &[u32]) -> Vec<u8> {
529    let mut pbf = Protobuf::new();
530
531    let mut curr: i32 = 0;
532    for index in indices {
533        let d_curr = (*index as i32) - curr;
534        pbf.write_varint(zigzag(d_curr));
535        curr += d_curr;
536    }
537
538    pbf.take()
539}
540
541/// write the tessellation to a protobuffer using the S2 Specification
542fn write_tessellation(geometry: &[Point]) -> Vec<u8> {
543    let mut pbf = Protobuf::new();
544    let mut x = 0;
545    let mut y = 0;
546    for point in geometry {
547        let dx = point.x - x;
548        let dy = point.y - y;
549        pbf.write_varint(zigzag(dx));
550        pbf.write_varint(zigzag(dy));
551        x += dx;
552        y += dy;
553    }
554
555    pbf.take()
556}
557
558/// write the geometry to a protobuffer using the S2 Specification
559fn write_geometry(feature: &BaseVectorFeature, mapbox_support: bool) -> Vec<u8> {
560    use BaseVectorFeature::*;
561    let mut pbf = Protobuf::new();
562    match feature {
563        BaseVectorPointsFeature(points) => write_geometry_points(&points.geometry, &mut pbf),
564        BaseVectorLinesFeature(lines) => write_geometry_lines(&lines.geometry, &mut pbf),
565        BaseVectorPolysFeature(polys) => {
566            write_geometry_polys(&polys.geometry, &mut pbf, mapbox_support)
567        }
568
569        _ => panic!("unknown feature type: {:?}", feature.get_type()),
570    };
571    pbf.take()
572}
573
574/// write the points geometry to a protobuffer using the S2 Specification
575fn write_geometry_points(points: &[Point], pbf: &mut Protobuf) {
576    let mut x = 0;
577    let mut y = 0;
578
579    for point in points {
580        // move
581        pbf.write_varint(command_encode(1, 1)); // moveto
582        // store
583        let dx = point.x - x;
584        let dy = point.y - y;
585        pbf.write_varint(zigzag(dx));
586        pbf.write_varint(zigzag(dy));
587        // update position
588        x += dx;
589        y += dy;
590    }
591}
592
593/// write the lines geometry to a protobuffer using the S2 Specification
594fn write_geometry_lines(lines: &[VectorLineWithOffset], pbf: &mut Protobuf) {
595    let mut x = 0;
596    let mut y = 0;
597
598    for line in lines {
599        let line_geo = &line.geometry;
600        pbf.write_varint(command_encode(1, 1)); // moveto
601        // do not write polygon closing path as lineto
602        let line_count = line_geo.len();
603        let mut i = 0;
604        while i < line_count {
605            if i == 1 {
606                pbf.write_varint(command_encode(2, (line_count - 1).try_into().unwrap()));
607                // lineto
608            }
609
610            let point = &line_geo[i];
611            let dx = point.x - x;
612            let dy = point.y - y;
613            pbf.write_varint(zigzag(dx));
614            pbf.write_varint(zigzag(dy));
615            x += dx;
616            y += dy;
617
618            i += 1;
619        }
620    }
621}
622
623/// write the polys geometry to a protobuffer using the S2 Specification
624fn write_geometry_polys(
625    polys: &[Vec<VectorLineWithOffset>],
626    pbf: &mut Protobuf,
627    mapbox_support: bool,
628) {
629    let mut x = 0;
630    let mut y = 0;
631
632    for poly in polys {
633        for ring in poly {
634            let ring_geo = &ring.geometry;
635            pbf.write_varint(command_encode(1, 1)); // moveto
636            let line_count = ring_geo.len() - 1;
637            let mut i = 0;
638            while i < line_count {
639                if i == 1 {
640                    pbf.write_varint(command_encode(2, (line_count - 1).try_into().unwrap()));
641                    // lineto
642                }
643
644                let point = &ring_geo[i];
645                let dx = point.x - x;
646                let dy = point.y - y;
647                pbf.write_varint(zigzag(dx));
648                pbf.write_varint(zigzag(dy));
649                x += dx;
650                y += dy;
651
652                i += 1;
653            }
654            pbf.write_varint(command_encode(7, 1)); // ClosePath
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));
658    }
659}