open_vector_tile/open/
vector_feature.rs

1use super::decode_value;
2use crate::{
3    Point, Point3D, VectorFeatureMethods, VectorGeometry, VectorLine3DWithOffset,
4    VectorLineWithOffset, VectorLines3DWithOffset, VectorLinesWithOffset, VectorPoints,
5    VectorPoints3D,
6    base::{BaseVectorFeature, TessellationWrapper, decode_offset},
7    mapbox::FeatureType as MapboxFeatureType,
8    open::{ColumnCacheReader, ColumnCacheWriter, encode_value},
9    unweave_2d, unweave_3d, zagzig,
10};
11use alloc::{rc::Rc, vec, vec::Vec};
12use core::cell::RefCell;
13use pbf::{BitCast, Protobuf};
14use s2json::{BBOX, Properties, Shape};
15use serde::{Deserialize, Serialize};
16
17/// Extent guide for how the geometry data is stored
18#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
19pub enum Extent {
20    /// 512x512
21    Extent512 = 512,
22    /// 1024x1024
23    Extent1024 = 1_024,
24    /// 2048x2048
25    Extent2048 = 2_048,
26    /// 4096x4096 (default)
27    #[default]
28    Extent4096 = 4_096,
29    /// 8_192x8_192
30    Extent8192 = 8_192,
31    /// 16_384x16_384
32    Extent16384 = 16_384,
33}
34impl BitCast for Extent {
35    fn to_u64(&self) -> u64 {
36        match self {
37            Extent::Extent512 => 0,
38            Extent::Extent1024 => 1,
39            Extent::Extent2048 => 2,
40            Extent::Extent4096 => 3,
41            Extent::Extent8192 => 4,
42            Extent::Extent16384 => 5,
43        }
44    }
45
46    fn from_u64(value: u64) -> Self {
47        match value {
48            1 => Extent::Extent1024,
49            2 => Extent::Extent2048,
50            3 => Extent::Extent4096,
51            4 => Extent::Extent8192,
52            5 => Extent::Extent16384,
53            _ => Extent::Extent512,
54        }
55    }
56}
57impl From<usize> for Extent {
58    fn from(extent: usize) -> Self {
59        match extent {
60            512 => Extent::Extent512,
61            1_024 => Extent::Extent1024,
62            2_048 => Extent::Extent2048,
63            4_096 => Extent::Extent4096,
64            8_192 => Extent::Extent8192,
65            16_384 => Extent::Extent16384,
66            _ => Extent::Extent512,
67        }
68    }
69}
70impl From<Extent> for usize {
71    fn from(extent: Extent) -> Self {
72        extent as i32 as usize
73    }
74}
75impl From<Extent> for f64 {
76    fn from(extent: Extent) -> Self {
77        extent as i32 as f64
78    }
79}
80
81/// Open Vector Tile Feature types.
82#[derive(Default, Debug, Copy, Clone, PartialEq, PartialOrd, Ord, Eq)]
83pub enum FeatureType {
84    /// Points Feature
85    #[default]
86    Points = 1,
87    /// Lines Feature
88    Lines = 2,
89    /// Polygons Feature
90    Polygons = 3,
91    /// Points3D Feature
92    Points3D = 4,
93    /// Lines3D Feature
94    Lines3D = 5,
95    /// Polygons3D Feature
96    Polygons3D = 6,
97}
98impl BitCast for FeatureType {
99    fn to_u64(&self) -> u64 {
100        (*self) as u64
101    }
102    fn from_u64(value: u64) -> Self {
103        match value {
104            1 => FeatureType::Points,
105            2 => FeatureType::Lines,
106            3 => FeatureType::Polygons,
107            4 => FeatureType::Points3D,
108            5 => FeatureType::Lines3D,
109            6 => FeatureType::Polygons3D,
110            _ => panic!("unknown value: {}", value),
111        }
112    }
113}
114impl From<&MapboxFeatureType> for FeatureType {
115    fn from(mft: &MapboxFeatureType) -> Self {
116        match mft {
117            MapboxFeatureType::Point => FeatureType::Points,
118            MapboxFeatureType::Line => FeatureType::Lines,
119            MapboxFeatureType::Polygon => FeatureType::Polygons,
120            MapboxFeatureType::MultiPolygon => FeatureType::Polygons,
121        }
122    }
123}
124
125/// Open Vector Tile Feature specification
126#[derive(Debug)]
127pub struct OpenVectorFeature {
128    /// the id of the feature
129    pub id: Option<u64>,
130    /// the properties of the feature
131    pub properties: Properties,
132    /// the type of the feature
133    pub r#type: FeatureType,
134    cache: Rc<RefCell<ColumnCacheReader>>,
135    m_shape: Shape,
136    extent: Extent,
137    geometry_indices: Vec<u32>,
138    geometry: Option<VectorGeometry>,
139    single: bool,
140    bbox_index: Option<usize>,
141    has_offsets: bool,
142    has_m_values: bool,
143    indices_index: Option<usize>,
144    tessellation_index: Option<usize>,
145}
146impl OpenVectorFeature {
147    fn _load_geometry_points(&mut self) -> VectorPoints {
148        let mut cache = self.cache.borrow_mut();
149
150        let mut index_pos = 0;
151        let geometry_index = self.geometry_indices[index_pos];
152        index_pos += 1;
153        if self.single {
154            let (a, b) = unweave_2d(geometry_index);
155            vec![Point::new(zagzig(a as u32), zagzig(b as u32))]
156        } else {
157            let mut geometry = cache.get_points(geometry_index as usize);
158
159            if self.has_m_values {
160                let length = geometry.len();
161                geometry.iter_mut().take(length).for_each(|p| {
162                    let value_index = self.geometry_indices[index_pos];
163                    p.m = Some(decode_value(value_index as usize, &self.m_shape, &mut cache));
164                    index_pos += 1;
165                });
166            }
167
168            geometry
169        }
170    }
171
172    fn _load_geometry_points_3d(&mut self) -> VectorPoints3D {
173        let mut cache = self.cache.borrow_mut();
174
175        let mut index_pos = 0;
176        let geometry_index = self.geometry_indices[index_pos];
177        index_pos += 1;
178        if self.single {
179            let (a, b, c) = unweave_3d(geometry_index as u64);
180            vec![Point3D::new(zagzig(a), zagzig(b), zagzig(c))]
181        } else {
182            let mut geometry = cache.get_points_3d(geometry_index as usize);
183
184            if self.has_m_values {
185                let length = geometry.len();
186                geometry.iter_mut().take(length).for_each(|p| {
187                    let value_index = self.geometry_indices[index_pos];
188                    p.m = Some(decode_value(value_index as usize, &self.m_shape, &mut cache));
189                    index_pos += 1;
190                });
191            }
192
193            geometry
194        }
195    }
196
197    fn _load_geometry_lines(&mut self) -> VectorLinesWithOffset {
198        let mut cache = self.cache.borrow_mut();
199
200        let mut res: VectorLinesWithOffset = vec![];
201
202        let mut index_pos = 0;
203        let mut line_count = 1;
204        if !self.single {
205            line_count = self.geometry_indices[index_pos];
206            index_pos += 1;
207        };
208        for _ in 0..line_count {
209            // get offset if it exists
210            let mut offset = 0.0;
211            if self.has_offsets {
212                offset = decode_offset(self.geometry_indices[index_pos]);
213                index_pos += 1;
214            }
215            // get geometry
216            let mut geometry = cache.get_points(self.geometry_indices[index_pos] as usize);
217            index_pos += 1;
218            // inject m values if they exist
219            if self.has_m_values {
220                let length = geometry.len();
221                geometry.iter_mut().take(length).for_each(|p| {
222                    let value_index = self.geometry_indices[index_pos];
223                    p.m = Some(decode_value(value_index as usize, &self.m_shape, &mut cache));
224                    index_pos += 1;
225                });
226            }
227            res.push(VectorLineWithOffset::new(offset, geometry));
228        }
229
230        res
231    }
232
233    fn _load_geometry_lines_3d(&mut self) -> VectorLines3DWithOffset {
234        let mut cache = self.cache.borrow_mut();
235
236        let mut res: VectorLines3DWithOffset = vec![];
237
238        let mut index_pos = 0;
239        let mut line_count = 1;
240        if !self.single {
241            line_count = self.geometry_indices[index_pos];
242            index_pos += 1;
243        };
244        for _ in 0..line_count {
245            // get offset if it exists
246            let mut offset = 0.0;
247            if self.has_offsets {
248                offset = decode_offset(self.geometry_indices[index_pos]);
249                index_pos += 1;
250            }
251            // get geometry
252            let mut geometry = cache.get_points_3d(self.geometry_indices[index_pos] as usize);
253            index_pos += 1;
254            // inject m values if they exist
255            if self.has_m_values {
256                let length = geometry.len();
257                geometry.iter_mut().take(length).for_each(|p| {
258                    let value_index = self.geometry_indices[index_pos];
259                    p.m = Some(decode_value(value_index as usize, &self.m_shape, &mut cache));
260                    index_pos += 1;
261                });
262            }
263            res.push(VectorLine3DWithOffset::new(offset, geometry));
264        }
265
266        res
267    }
268
269    fn _load_geometry_polys(&mut self) -> Vec<VectorLinesWithOffset> {
270        let mut cache = self.cache.borrow_mut();
271
272        let mut res: Vec<VectorLinesWithOffset> = vec![];
273
274        let mut index_pos = 0;
275        let mut poly_count = 1;
276        if !self.single {
277            poly_count = self.geometry_indices[index_pos];
278            index_pos += 1;
279        };
280        for _ in 0..poly_count {
281            let line_count = self.geometry_indices[index_pos];
282            index_pos += 1;
283            let mut lines: VectorLinesWithOffset = vec![];
284            for _ in 0..line_count {
285                // get offset if it exists
286                let mut offset = 0.0;
287                if self.has_offsets {
288                    offset = decode_offset(self.geometry_indices[index_pos]);
289                    index_pos += 1;
290                }
291                // get geometry
292                let mut geometry = cache.get_points(self.geometry_indices[index_pos] as usize);
293                index_pos += 1;
294                // inject m values if they exist
295                if self.has_m_values {
296                    let length = geometry.len();
297                    geometry.iter_mut().take(length).for_each(|p| {
298                        let value_index = self.geometry_indices[index_pos];
299                        p.m = Some(decode_value(value_index as usize, &self.m_shape, &mut cache));
300                        index_pos += 1;
301                    });
302                }
303                lines.push(VectorLineWithOffset::new(offset, geometry));
304            }
305            res.push(lines);
306        }
307
308        res
309    }
310
311    fn _load_geometry_polys_3d(&mut self) -> Vec<VectorLines3DWithOffset> {
312        let mut cache = self.cache.borrow_mut();
313
314        let mut res: Vec<VectorLines3DWithOffset> = vec![];
315
316        let mut index_pos = 0;
317        let mut poly_count = 1;
318        if !self.single {
319            poly_count = self.geometry_indices[index_pos];
320            index_pos += 1;
321        };
322        for _ in 0..poly_count {
323            let line_count = self.geometry_indices[index_pos];
324            index_pos += 1;
325            let mut lines: VectorLines3DWithOffset = vec![];
326            for _ in 0..line_count {
327                // get offset if it exists
328                let mut offset = 0.0;
329                if self.has_offsets {
330                    offset = decode_offset(self.geometry_indices[index_pos]);
331                    index_pos += 1;
332                }
333                // get geometry
334                let mut geometry = cache.get_points_3d(self.geometry_indices[index_pos] as usize);
335                index_pos += 1;
336                // inject m values if they exist
337                if self.has_m_values {
338                    let length = geometry.len();
339                    geometry.iter_mut().take(length).for_each(|p| {
340                        let value_index = self.geometry_indices[index_pos];
341                        p.m = Some(decode_value(value_index as usize, &self.m_shape, &mut cache));
342                        index_pos += 1;
343                    });
344                }
345                lines.push(VectorLine3DWithOffset::new(offset, geometry));
346            }
347            res.push(lines);
348        }
349
350        res
351    }
352}
353impl VectorFeatureMethods for OpenVectorFeature {
354    /// get the id of the feature
355    fn id(&self) -> Option<u64> {
356        self.id
357    }
358
359    /// get the version of the feature
360    fn version(&self) -> u16 {
361        1
362    }
363
364    /// get the extent of the feature
365    fn extent(&self) -> usize {
366        self.extent.into()
367    }
368
369    fn properties(&self) -> Properties {
370        self.properties.clone()
371    }
372
373    /// Create a new OpenVectorFeature
374    fn get_type(&self) -> FeatureType {
375        self.r#type
376    }
377
378    /// get the bbox of the feature
379    fn bbox(&self) -> Option<BBOX> {
380        if let Some(index) = self.bbox_index {
381            let mut cache = self.cache.borrow_mut();
382            Some(cache.get_bbox(index))
383        } else {
384            None
385        }
386    }
387
388    /// whether the feature has m values
389    fn has_m_values(&self) -> bool {
390        self.has_m_values
391    }
392
393    /// whether the feature is a points type
394    fn is_points(&self) -> bool {
395        self.r#type == FeatureType::Points
396    }
397
398    /// whether the feature is a line type
399    fn is_lines(&self) -> bool {
400        self.r#type == FeatureType::Lines
401    }
402
403    /// whether the feature is a polygon type
404    fn is_polygons(&self) -> bool {
405        self.r#type == FeatureType::Polygons
406    }
407
408    /// whether the feature is a points 3D type
409    fn is_points_3d(&self) -> bool {
410        self.r#type == FeatureType::Points3D
411    }
412
413    /// whether the feature is a line 3D type
414    fn is_lines_3d(&self) -> bool {
415        self.r#type == FeatureType::Lines3D
416    }
417
418    /// whether the feature is a polygon 3D type
419    fn is_polygons_3d(&self) -> bool {
420        self.r#type == FeatureType::Polygons3D
421    }
422
423    /// regardless of the type, we return a flattend point array
424    fn load_points(&mut self) -> VectorPoints {
425        match self.load_geometry() {
426            VectorGeometry::VectorPoints(p) => p,
427            VectorGeometry::VectorLines(lines) => {
428                lines.iter().flat_map(|p| p.geometry.clone()).collect()
429            }
430            VectorGeometry::VectorPolys(polys) => polys
431                .iter()
432                .flat_map(|p| p.iter().flat_map(|p| p.geometry[..p.geometry.len() - 1].to_vec()))
433                .collect(),
434            _ => {
435                panic!("unexpected geometry type")
436            }
437        }
438    }
439
440    /// regardless of the type, we return a flattend point array
441    fn load_points_3d(&mut self) -> VectorPoints3D {
442        match self.load_geometry() {
443            VectorGeometry::VectorPoints3D(p) => p,
444            VectorGeometry::VectorLines3D(lines) => {
445                lines.iter().flat_map(|p| p.geometry.clone()).collect()
446            }
447            VectorGeometry::VectorPolys3D(polys) => polys
448                .iter()
449                .flat_map(|p| p.iter().flat_map(|p| p.geometry[..p.geometry.len() - 1].to_vec()))
450                .collect(),
451            _ => {
452                panic!("unexpected geometry type")
453            }
454        }
455    }
456
457    /// an array of lines. The offsets will be set to 0
458    fn load_lines(&mut self) -> VectorLinesWithOffset {
459        match self.load_geometry() {
460            VectorGeometry::VectorLines(lines) => lines,
461            VectorGeometry::VectorPolys(polys) => polys.iter().flat_map(|p| p.clone()).collect(),
462            _ => {
463                panic!("unexpected geometry type")
464            }
465        }
466    }
467
468    /// an array of lines. The offsets will be set to 0
469    fn load_lines_3d(&mut self) -> VectorLines3DWithOffset {
470        match self.load_geometry() {
471            VectorGeometry::VectorLines3D(lines) => lines,
472            VectorGeometry::VectorPolys3D(polys) => polys.iter().flat_map(|p| p.clone()).collect(),
473            _ => {
474                panic!("unexpected geometry type")
475            }
476        }
477    }
478
479    /// an array of polys
480    fn load_polys(&mut self) -> Vec<VectorLinesWithOffset> {
481        match self.load_geometry() {
482            VectorGeometry::VectorPolys(polys) => polys,
483            _ => {
484                panic!("unexpected geometry type")
485            }
486        }
487    }
488
489    /// an array of 3d polys
490    fn load_polys_3d(&mut self) -> Vec<VectorLines3DWithOffset> {
491        match self.load_geometry() {
492            VectorGeometry::VectorPolys3D(polys) => polys,
493            _ => {
494                panic!("unexpected geometry type")
495            }
496        }
497    }
498
499    /// returns the indices of the geometry
500    fn read_indices(&mut self) -> Vec<u32> {
501        if self.indices_index.is_none() {
502            return vec![];
503        }
504        let mut cache = self.cache.borrow_mut();
505        cache.get_indices(self.indices_index.unwrap())
506    }
507
508    /// Add tessellation data to the geometry
509    fn add_tessellation(&mut self, geometry: &mut Vec<f64>, multiplier: f64) {
510        let Some(tessellation_index) = self.tessellation_index else {
511            return;
512        };
513        let mut cache = self.cache.borrow_mut();
514        let data = cache.get_points(tessellation_index);
515        for point in data {
516            geometry.push(point.x as f64 * multiplier);
517            geometry.push(point.y as f64 * multiplier);
518        }
519    }
520
521    /// Add 3D tessellation data to the geometry
522    fn add_tessellation_3d(&mut self, geometry: &mut Vec<f64>, multiplier: f64) {
523        let Some(tessellation_index) = self.tessellation_index else {
524            return;
525        };
526        let mut cache = self.cache.borrow_mut();
527        let data = cache.get_points_3d(tessellation_index);
528        for point in data {
529            geometry.push(point.x as f64 * multiplier);
530            geometry.push(point.y as f64 * multiplier);
531            geometry.push(point.z as f64 * multiplier);
532        }
533    }
534
535    /// (flattened geometry & tesslation if applicable, indices)
536    fn load_geometry_flat(&mut self) -> (Vec<f64>, Vec<u32>) {
537        // build a multiplier
538        let multiplier: f64 = 1.0 / f64::from(self.extent);
539        // grab the geometry, flatten it, and mutate to an f64
540        let geometry: Vec<f64> = match self.load_geometry() {
541            VectorGeometry::VectorPolys(polys) => {
542                let mut geo = polys
543                    .iter()
544                    .flat_map(|p| {
545                        p.iter().flat_map(|p| {
546                            p.geometry.clone().into_iter().flat_map(|p| {
547                                vec![p.x as f64 * multiplier, p.y as f64 * multiplier]
548                            })
549                        })
550                    })
551                    .collect();
552                self.add_tessellation(&mut geo, multiplier);
553                geo
554            }
555            VectorGeometry::VectorPolys3D(polys) => {
556                let mut geo = polys
557                    .iter()
558                    .flat_map(|p| {
559                        p.iter().flat_map(|p| {
560                            p.geometry.clone().into_iter().flat_map(|p| {
561                                vec![p.x as f64 * multiplier, p.y as f64 * multiplier]
562                            })
563                        })
564                    })
565                    .collect();
566                self.add_tessellation_3d(&mut geo, multiplier);
567                geo
568            }
569            _ => {
570                panic!("unexpected geometry type")
571            }
572        };
573        // if a poly, check if we should load indices
574        let indices = self.read_indices();
575
576        (geometry, indices)
577    }
578
579    /// load the geometry
580    fn load_geometry(&mut self) -> VectorGeometry {
581        if let Some(geometry) = &self.geometry {
582            return geometry.clone();
583        }
584
585        self.geometry = Some(match self.r#type {
586            FeatureType::Points => VectorGeometry::VectorPoints(self._load_geometry_points()),
587            FeatureType::Points3D => {
588                VectorGeometry::VectorPoints3D(self._load_geometry_points_3d())
589            }
590            FeatureType::Lines => VectorGeometry::VectorLines(self._load_geometry_lines()),
591            FeatureType::Lines3D => VectorGeometry::VectorLines3D(self._load_geometry_lines_3d()),
592            FeatureType::Polygons => VectorGeometry::VectorPolys(self._load_geometry_polys()),
593            FeatureType::Polygons3D => {
594                VectorGeometry::VectorPolys3D(self._load_geometry_polys_3d())
595            }
596        });
597
598        self.load_geometry()
599    }
600}
601
602/// Read a single feature given the encoded data
603pub fn read_feature(
604    data: Vec<u8>,
605    extent: Extent,
606    cache: Rc<RefCell<ColumnCacheReader>>,
607    shape: &Shape,
608    m_shape: Shape,
609) -> OpenVectorFeature {
610    let mut pbf = Protobuf::from_input(data);
611    // pull in the type
612    let r#type: FeatureType = pbf.read_varint();
613    // next the flags
614    let flags: u8 = pbf.read_varint();
615    // read the id if it exists
616    let id: Option<u64> = if flags & 1 > 0 { Some(pbf.read_varint()) } else { None };
617    let has_bbox = flags & (1 << 1) > 0;
618    let has_offsets = (flags & (1 << 2)) > 0;
619    let has_indices = flags & (1 << 3) > 0;
620    let has_tessellation = flags & (1 << 4) > 0;
621    let has_m_values = flags & (1 << 5) > 0;
622    let single = flags & (1 << 6) > 0;
623    // read the properties
624    let value_index: usize = pbf.read_varint();
625    let properties = decode_value(value_index, shape, &mut cache.borrow_mut());
626    // if type is 1 or 4, read geometry as a single index, otherwise as an array
627    let mut geometry_indices: Vec<u32> = vec![];
628    let mut indices_index: Option<usize> = None;
629    let mut tessellation_index: Option<usize> = None;
630    if r#type == FeatureType::Points || r#type == FeatureType::Points3D {
631        if single {
632            geometry_indices.push(pbf.read_varint())
633        } else {
634            geometry_indices = cache.borrow_mut().get_indices(pbf.read_varint());
635        }
636    } else {
637        geometry_indices = cache.borrow_mut().get_indices(pbf.read_varint());
638    }
639    // read indices and tessellation if they exist
640    if r#type == FeatureType::Polygons || r#type == FeatureType::Polygons3D {
641        if has_indices {
642            indices_index = Some(pbf.read_varint());
643        }
644        if has_tessellation {
645            tessellation_index = Some(pbf.read_varint());
646        }
647    }
648    let bbox_index = if has_bbox { Some(pbf.read_varint()) } else { None };
649
650    OpenVectorFeature {
651        id,
652        properties,
653        r#type,
654        cache,
655        m_shape,
656        extent,
657        geometry_indices,
658        geometry: None,
659        single,
660        bbox_index,
661        has_offsets,
662        has_m_values,
663        indices_index,
664        tessellation_index,
665    }
666}
667
668/// Write a single feature to the column cache and return the encoding indexes for lookup
669pub fn write_feature(
670    feature: &BaseVectorFeature,
671    shape: &Shape,
672    m_shape: Option<&Shape>,
673    cache: &mut ColumnCacheWriter,
674) -> Vec<u8> {
675    // write id, type, properties, bbox, geometry, indices, tessellation, mValues
676    let mut pbf = Protobuf::new();
677    // type is just stored as a varint
678    pbf.write_varint(feature.get_type());
679    // store flags if each one exists or not into a single byte
680    let id = feature.id();
681    let has_id: bool = id.is_some();
682    let indices = feature.indices();
683    let has_indices = indices.is_some() && !indices.unwrap().is_empty();
684    let tessellation = feature.tessellation();
685    let has_tessellation = tessellation.is_some() && !tessellation.as_ref().unwrap().is_empty();
686    let has_offsets = feature.has_offsets();
687    let bbox = feature.bbox();
688    let has_bbox = bbox.is_some();
689    let has_m_values = feature.has_m_values();
690    let single = feature.single();
691    let mut flags: u8 = 0;
692    if has_id {
693        flags += 1;
694    }
695    if has_bbox {
696        flags += 1 << 1;
697    }
698    if has_offsets {
699        flags += 1 << 2;
700    }
701    if has_indices {
702        flags += 1 << 3;
703    }
704    if has_tessellation {
705        flags += 1 << 4;
706    }
707    if has_m_values {
708        flags += 1 << 5;
709    }
710    if single {
711        flags += 1 << 6;
712    }
713    pbf.write_varint(flags);
714    // id is stored in unsigned column
715    if has_id {
716        pbf.write_varint(id.unwrap());
717    }
718    // index to values column
719    let value_index = encode_value(feature.properties(), shape, cache);
720    pbf.write_varint(value_index);
721    // geometry
722    let stored_geo = feature.encode_to_cache(cache, m_shape);
723    pbf.write_varint(stored_geo);
724    // indices
725    if has_indices {
726        pbf.write_varint(cache.add_indices(feature.indices().unwrap()));
727    }
728    // tessellation
729    if has_tessellation {
730        match tessellation.unwrap() {
731            TessellationWrapper::Tessellation(t) => pbf.write_varint(cache.add_points(t)),
732            TessellationWrapper::Tessellation3D(t) => pbf.write_varint(cache.add_points_3d(t)),
733        }
734    }
735    // bbox is stored in double column.
736    if has_bbox {
737        pbf.write_varint(cache.add_bbox(bbox.unwrap()));
738    }
739
740    pbf.take()
741}