Skip to main content

mlt_core/decoder/
tile.rs

1//! Row-oriented "source form" for the optimizer.
2//!
3//! [`TileLayer`] holds one [`TileFeature`] per map feature, each owning
4//! its geometry as a [`geo_types::Geometry<i32>`] and its property values as a
5//! plain `Vec<PropValue>`.  This is the working form used throughout the
6//! optimizer and sorting pipeline: it is cheap to clone, trivially sortable,
7//! and free from any encoded/decoded duality.
8
9use crate::decoder::{
10    GeometryValues, Layer01, ParsedLayer01, ParsedProperty, PropValue, PropValueRef, TileFeature,
11    TileLayer,
12};
13use crate::errors::AsMltError as _;
14use crate::{Decoder, LendingIterator, MltResult};
15
16impl ParsedLayer01<'_> {
17    /// Returns the decoded geometry buffer for this layer.
18    ///
19    /// Provides access to the columnar geometry arrays (vertex buffer, offset arrays, geometry
20    /// types) for advanced use cases such as building typed arrays for WebAssembly or
21    /// performing spatial indexing. For iterating feature geometries as `geo_types` values,
22    /// prefer [`iter_features`](Self::iter_features) instead.
23    #[must_use]
24    pub fn geometry_values(&self) -> &GeometryValues {
25        &self.geometry
26    }
27
28    /// Decode and convert into a row-oriented [`TileLayer`], charging every
29    /// heap allocation against `dec`.
30    pub fn into_tile(self, dec: &mut Decoder) -> MltResult<TileLayer> {
31        // Extract owned/copied fields before borrowing self for the feature iterator.
32        let name = self.name.to_string();
33        let extent = self.extent;
34        let names: Vec<String> = self.iterate_prop_names().map(|n| n.to_string()).collect();
35        let col_nulls = typed_nulls(&self.properties);
36        let mut features = dec.alloc::<TileFeature>(self.feature_count())?;
37        let mut feat_iter = self.iter_features();
38        while let Some(feat) = feat_iter.next() {
39            let feat = feat?;
40            let mut values = dec.alloc::<PropValue>(names.len())?;
41            for (col_idx, value) in feat.iter_all_properties().enumerate() {
42                values.push(match value {
43                    Some(v) => prop_value_from_ref(v),
44                    None => col_nulls[col_idx].clone(),
45                });
46            }
47
48            charge_str_props(dec, &values)?;
49
50            features.push(TileFeature {
51                id: feat.id,
52                geometry: feat.geometry,
53                properties: values,
54            });
55        }
56
57        Ok(TileLayer {
58            name,
59            extent,
60            property_names: names,
61            features,
62        })
63    }
64
65    #[must_use]
66    pub fn feature_count(&self) -> usize {
67        self.geometry.vector_types.len()
68    }
69}
70
71impl Layer01<'_> {
72    /// Decode and convert into a row-oriented [`TileLayer`]
73    pub fn into_tile(self, dec: &mut Decoder) -> MltResult<TileLayer> {
74        self.decode_all(dec)?.into_tile(dec)
75    }
76}
77
78/// Convert a [`PropValueRef`] (as yielded by [`crate::FeatureRef::iter_all_properties`])
79/// into an owned [`PropValue`].
80fn prop_value_from_ref(value: PropValueRef<'_>) -> PropValue {
81    match value {
82        PropValueRef::Bool(v) => PropValue::Bool(Some(v)),
83        PropValueRef::I8(v) => PropValue::I8(Some(v)),
84        PropValueRef::U8(v) => PropValue::U8(Some(v)),
85        PropValueRef::I32(v) => PropValue::I32(Some(v)),
86        PropValueRef::U32(v) => PropValue::U32(Some(v)),
87        PropValueRef::I64(v) => PropValue::I64(Some(v)),
88        PropValueRef::U64(v) => PropValue::U64(Some(v)),
89        PropValueRef::F32(v) => PropValue::F32(Some(v)),
90        PropValueRef::F64(v) => PropValue::F64(Some(v)),
91        PropValueRef::Str(s) => PropValue::Str(Some(s.to_string())),
92    }
93}
94
95/// Build a flat list of typed null [`PropValue`]s, one per logical column position
96/// as yielded by [`crate::FeatureRef::iter_all_properties`].
97///
98/// Each scalar column contributes one entry with its specific null variant (e.g.
99/// `PropValue::Bool(None)`).  A `SharedDict` column expands to one `PropValue::Str(None)`
100/// entry per sub-item.
101fn typed_nulls(properties: &[ParsedProperty<'_>]) -> Vec<PropValue> {
102    use ParsedProperty as PP;
103    use PropValue as PV;
104    let mut nulls = Vec::new();
105    for prop in properties {
106        match prop {
107            PP::Bool(_) => nulls.push(PV::Bool(None)),
108            PP::I8(_) => nulls.push(PV::I8(None)),
109            PP::U8(_) => nulls.push(PV::U8(None)),
110            PP::I32(_) => nulls.push(PV::I32(None)),
111            PP::U32(_) => nulls.push(PV::U32(None)),
112            PP::I64(_) => nulls.push(PV::I64(None)),
113            PP::U64(_) => nulls.push(PV::U64(None)),
114            PP::F32(_) => nulls.push(PV::F32(None)),
115            PP::F64(_) => nulls.push(PV::F64(None)),
116            PP::Str(_) => nulls.push(PV::Str(None)),
117            PP::SharedDict(d) => {
118                for _ in &d.items {
119                    nulls.push(PV::Str(None));
120                }
121            }
122        }
123    }
124    nulls
125}
126
127/// Charge `dec` for the heap bytes of owned `String` values inside `PropValue::Str`.
128fn charge_str_props(dec: &mut Decoder, props: &[PropValue]) -> MltResult<()> {
129    let str_bytes = props
130        .iter()
131        .filter_map(|p| {
132            if let PropValue::Str(Some(s)) = p {
133                Some(s.len())
134            } else {
135                None
136            }
137        })
138        .try_fold(0u32, |acc, n| {
139            acc.checked_add(u32::try_from(n).or_overflow()?)
140                .or_overflow()
141        })?;
142    if str_bytes > 0 {
143        dec.consume(str_bytes)?;
144    }
145    Ok(())
146}