Skip to main content

mlt_core/
tile.rs

1//! Owned, row-oriented tile model.
2//!
3//! This is the representation tiles are built in and converted to/from:
4//! it is independent of the wire format, and is shared by the encoder, the MVT and
5//! `GeoJSON` converters, and the language bindings.
6//! The decoder's columnar types live in [`crate::decoder`].
7
8#[cfg(feature = "unstable-v2")]
9use std::collections::BTreeMap;
10use std::num::NonZeroU32;
11
12use geo_types::{Coord, Geometry, LineString};
13#[cfg(feature = "unstable-v2")]
14use num_traits::ToPrimitive as _;
15
16use crate::{MltError, MltResult};
17
18/// Non-zero tile extent.
19///
20/// Use [`Extent::new`] to validate raw integer input before storing it in
21/// owned row or staged layer structures.
22#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
23pub struct Extent(NonZeroU32);
24
25impl Extent {
26    pub fn new(value: u32) -> MltResult<Self> {
27        NonZeroU32::new(value)
28            .map(Self)
29            .ok_or(MltError::InvalidExtent(value))
30    }
31
32    #[must_use]
33    pub fn get(self) -> u32 {
34        self.0.get()
35    }
36}
37
38impl From<Extent> for NonZeroU32 {
39    fn from(value: Extent) -> Self {
40        value.0
41    }
42}
43
44/// The vertical grid a layer's z coordinates lie on, a power-of-ten step in metres.
45///
46/// A z of `0` sits at Terrain-RGB's base of -10000 m, so a coordinate's elevation is `-10000 + z * step`.
47#[cfg(feature = "unstable-v2")]
48#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
49pub struct ZStep(i8);
50
51#[cfg(feature = "unstable-v2")]
52impl ZStep {
53    /// The finest step, 10^-3 m.
54    pub const MIN_EXPONENT: i8 = -3;
55    /// The coarsest step, 10^4 m.
56    pub const MAX_EXPONENT: i8 = 4;
57    /// The elevation of `z = 0`, in metres.
58    pub const BASE_METRES: f64 = -10_000.0;
59
60    /// A step of `10^exponent` metres.
61    pub fn new(exponent: i8) -> MltResult<Self> {
62        if (Self::MIN_EXPONENT..=Self::MAX_EXPONENT).contains(&exponent) {
63            Ok(Self(exponent))
64        } else {
65            Err(MltError::InvalidZStep(exponent))
66        }
67    }
68
69    #[must_use]
70    pub fn exponent(self) -> i8 {
71        self.0
72    }
73
74    /// The byte a vertex stream stores this step as, `exponent + 3`.
75    pub(crate) fn code(self) -> u8 {
76        (self.0 - Self::MIN_EXPONENT).cast_unsigned()
77    }
78
79    /// The step a vertex stream's byte names.
80    pub(crate) fn from_code(code: u8) -> MltResult<Self> {
81        i8::try_from(code)
82            .ok()
83            .and_then(|c| Self::new(c + Self::MIN_EXPONENT).ok())
84            .ok_or(MltError::InvalidZStepCode(code))
85    }
86
87    /// The step in metres.
88    #[must_use]
89    pub fn metres(self) -> f64 {
90        if self.0 < 0 {
91            1.0 / self.power()
92        } else {
93            self.power()
94        }
95    }
96
97    /// The elevation of `z`, in metres.
98    ///
99    /// A fine step moves the base onto the grid and divides by an exact integer last, so `1_001_234` at `10^-2` reads as `12.34`.
100    #[must_use]
101    pub fn elevation(self, z: i32) -> f64 {
102        let power = self.power();
103        if self.0 < 0 {
104            (f64::from(z) + Self::BASE_METRES * power) / power
105        } else {
106            Self::BASE_METRES + f64::from(z) * power
107        }
108    }
109
110    /// The grid value nearest `elevation` metres, or [`None`] when it falls outside `i32`.
111    #[must_use]
112    pub fn z(self, elevation: f64) -> Option<i32> {
113        let power = self.power();
114        let z = if self.0 < 0 {
115            elevation * power - Self::BASE_METRES * power
116        } else {
117            (elevation - Self::BASE_METRES) / power
118        };
119        z.round().to_i32()
120    }
121
122    /// `10^|exponent|`, an integer `f64` holds exactly.
123    fn power(self) -> f64 {
124        10f64.powi(self.0.unsigned_abs().into())
125    }
126}
127
128/// The step as the spec's table names it, e.g. `1 dm`.
129#[cfg(feature = "unstable-v2")]
130impl std::fmt::Display for ZStep {
131    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
132        f.write_str(match self.0 {
133            -3 => "1 mm",
134            -2 => "1 cm",
135            -1 => "1 dm",
136            0 => "1 m",
137            1 => "10 m",
138            2 => "100 m",
139            3 => "1 km",
140            _ => "10 km",
141        })
142    }
143}
144
145/// The kind of column a name belongs to, which a layer's names are unique across.
146#[derive(Debug, Clone, Copy, PartialEq, Eq, strum::Display)]
147pub enum ColumnRole {
148    /// An ordinary column of values, a shared dictionary's children included.
149    #[strum(serialize = "property")]
150    Property,
151    /// A vertex-scoped column of the m-value section.
152    #[cfg(feature = "unstable-v2")]
153    #[strum(serialize = "m-value")]
154    MValue,
155    /// A shredded column of maps or lists.
156    #[cfg(feature = "unstable-v2")]
157    #[strum(serialize = "nested")]
158    Nested,
159}
160
161/// Row-oriented working form for the optimizer.
162///
163/// All features are stored as a flat [`Vec<TileFeature>`] so that sorting is
164/// a single `sort_by_cached_key` call.  The `property_names` vec is parallel
165/// to every `TileFeature::properties` slice in this layer.
166#[derive(Debug, Clone, PartialEq)]
167pub struct TileLayer {
168    pub(crate) name: String,
169    pub(crate) extent: Extent,
170    /// Column names, parallel to `TileFeature::properties`.
171    pub(crate) property_names: Vec<String>,
172    /// Column types, parallel to `TileFeature::properties`.
173    pub(crate) property_kinds: Vec<PropKind>,
174    /// Vertex-scoped column names, parallel to `TileFeature::m_values`.
175    #[cfg(feature = "unstable-v2")]
176    pub(crate) m_value_names: Vec<String>,
177    /// Vertex-scoped column types, parallel to `TileFeature::m_values`.
178    #[cfg(feature = "unstable-v2")]
179    pub(crate) m_value_kinds: Vec<PropKind>,
180    /// Nested column names, parallel to `TileFeature::nested`.
181    #[cfg(feature = "unstable-v2")]
182    pub(crate) nested_names: Vec<String>,
183    /// Nested column shapes, parallel to `TileFeature::nested`.
184    #[cfg(feature = "unstable-v2")]
185    pub(crate) nested_kinds: Vec<NestedKind>,
186    /// The grid of every feature's `TileFeature::z`, or [`None`] for a flat layer.
187    #[cfg(feature = "unstable-v2")]
188    pub(crate) z_step: Option<ZStep>,
189    pub(crate) features: Vec<TileFeature>,
190}
191
192/// A single map feature in row form.
193#[derive(Debug, Clone, PartialEq)]
194pub struct TileFeature {
195    pub(crate) id: Option<u64>,
196    /// Geometry as a [`geo_types`] form
197    pub(crate) geometry: Geometry<i32>,
198    /// One value per property column, in the same order as
199    /// [`TileLayer::property_names`].
200    pub(crate) properties: Vec<PropValue>,
201    /// One entry per m-value column, in the same order as
202    /// [`TileLayer::m_value_names`], each holding one value per vertex of this
203    /// feature's geometry or none at all.
204    #[cfg(feature = "unstable-v2")]
205    pub(crate) m_values: Vec<MValue>,
206    /// One value per nested column, in the same order as [`TileLayer::nested_names`].
207    #[cfg(feature = "unstable-v2")]
208    pub(crate) nested: Vec<NestedValue>,
209    /// One z per vertex of the geometry on the layer's [`ZStep`] grid, or none in a flat layer.
210    #[cfg(feature = "unstable-v2")]
211    pub(crate) z: Vec<i32>,
212}
213
214#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
215pub struct PropertyKey(usize);
216
217impl PropertyKey {
218    pub(crate) const fn new(index: usize) -> Self {
219        Self(index)
220    }
221
222    #[must_use]
223    pub fn index(self) -> usize {
224        self.0
225    }
226}
227
228/// Handle to one of a layer's m-value columns, as [`PropertyKey`] is to a property column.
229#[cfg(feature = "unstable-v2")]
230#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
231pub struct MValueKey(usize);
232
233#[cfg(feature = "unstable-v2")]
234impl MValueKey {
235    #[must_use]
236    pub fn index(self) -> usize {
237        self.0
238    }
239}
240
241/// Handle to one of a layer's nested columns, as [`PropertyKey`] is to a property column.
242#[cfg(feature = "unstable-v2")]
243#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
244pub struct NestedKey(usize);
245
246#[cfg(feature = "unstable-v2")]
247impl NestedKey {
248    #[must_use]
249    pub fn index(self) -> usize {
250        self.0
251    }
252}
253
254/// How deep a nested column may go, counting its root as the first level.
255#[cfg(feature = "unstable-v2")]
256pub(crate) const MAX_NESTED_DEPTH: usize = 8;
257
258/// The shape of one nested column, the union of every value it holds.
259#[cfg(feature = "unstable-v2")]
260#[derive(Debug, Clone, PartialEq, Eq)]
261pub enum NestedKind {
262    Leaf(PropKind),
263    List(Box<Self>),
264    /// String keys, each with its own kind, so a heterogeneous object is one of these.
265    Map(BTreeMap<String, Self>),
266}
267
268#[cfg(feature = "unstable-v2")]
269impl NestedKind {
270    /// A map of the given fields, the shape a struct of them shreds into.
271    #[must_use]
272    pub fn map<K: Into<String>>(fields: impl IntoIterator<Item = (K, Self)>) -> Self {
273        Self::Map(fields.into_iter().map(|(k, v)| (k.into(), v)).collect())
274    }
275
276    /// A list of `element`.
277    #[must_use]
278    pub fn list(element: Self) -> Self {
279        Self::List(Box::new(element))
280    }
281
282    /// How many levels this shape has, counting itself as the first.
283    #[must_use]
284    pub fn depth(&self) -> usize {
285        1 + match self {
286            Self::Leaf(_) => 0,
287            Self::List(element) => element.depth(),
288            Self::Map(fields) => fields.values().map(Self::depth).max().unwrap_or(0),
289        }
290    }
291
292    /// The value a feature that carries nothing for this column holds.
293    #[must_use]
294    pub fn null_value(&self) -> NestedValue {
295        match self {
296            Self::Leaf(kind) => NestedValue::Leaf(PropValue::null(*kind)),
297            Self::List(_) => NestedValue::List(None),
298            Self::Map(_) => NestedValue::Map(None),
299        }
300    }
301
302    /// Whether `value` is one this shape describes.
303    ///
304    /// A map may leave a field out, which is how a null field is written.
305    #[must_use]
306    pub fn accepts(&self, value: &NestedValue) -> bool {
307        match (self, value) {
308            (Self::Leaf(kind), NestedValue::Leaf(value)) => value.kind() == *kind,
309            (Self::List(_), NestedValue::List(None)) | (Self::Map(_), NestedValue::Map(None)) => {
310                true
311            }
312            (Self::List(element), NestedValue::List(Some(items))) => {
313                items.iter().all(|item| element.accepts(item))
314            }
315            (Self::Map(fields), NestedValue::Map(Some(entries))) => entries
316                .iter()
317                .all(|(key, value)| fields.get(key).is_some_and(|kind| kind.accepts(value))),
318            _ => false,
319        }
320    }
321
322    /// Whether a shape with no fields, which no struct node can hold, sits anywhere in this one.
323    fn has_empty_map(&self) -> bool {
324        match self {
325            Self::Leaf(_) => false,
326            Self::List(element) => element.has_empty_map(),
327            Self::Map(fields) => fields.is_empty() || fields.values().any(Self::has_empty_map),
328        }
329    }
330}
331
332/// One feature's value for one nested column.
333///
334/// A map leaves out the keys it has no value for, so a null field is an absent one.
335#[cfg(feature = "unstable-v2")]
336#[derive(Debug, Clone, PartialEq)]
337pub enum NestedValue {
338    Leaf(PropValue),
339    List(Option<Vec<Self>>),
340    Map(Option<BTreeMap<String, Self>>),
341}
342
343#[cfg(feature = "unstable-v2")]
344impl NestedValue {
345    /// A map of the given entries.
346    #[must_use]
347    pub fn map<K: Into<String>>(entries: impl IntoIterator<Item = (K, Self)>) -> Self {
348        Self::Map(Some(
349            entries.into_iter().map(|(k, v)| (k.into(), v)).collect(),
350        ))
351    }
352
353    /// A list of the given items.
354    #[must_use]
355    pub fn list(items: impl IntoIterator<Item = Self>) -> Self {
356        Self::List(Some(items.into_iter().collect()))
357    }
358
359    /// Whether this carries nothing at all, which is how a nested value is null.
360    #[must_use]
361    pub fn is_null(&self) -> bool {
362        match self {
363            Self::Leaf(value) => value.is_null(),
364            Self::List(items) => items.is_none(),
365            Self::Map(entries) => entries.is_none(),
366        }
367    }
368
369    /// The heap bytes this value holds, which the decoder charges its budget for.
370    #[must_use]
371    pub(crate) fn heap_bytes(&self) -> usize {
372        match self {
373            Self::Leaf(PropValue::Str(Some(value))) => value.len(),
374            Self::Leaf(_) => size_of::<PropValue>(),
375            Self::List(items) => items.iter().flatten().map(Self::heap_bytes).sum::<usize>(),
376            Self::Map(entries) => entries
377                .iter()
378                .flatten()
379                .map(|(key, value)| key.len() + value.heap_bytes())
380                .sum(),
381        }
382    }
383
384    /// Whether the two are the same kind of value, which is all a feature can check
385    /// without the column's shape.
386    fn same_shape(&self, other: &Self) -> bool {
387        matches!(
388            (self, other),
389            (Self::Leaf(_), Self::Leaf(_))
390                | (Self::List(_), Self::List(_))
391                | (Self::Map(_), Self::Map(_))
392        )
393    }
394}
395
396impl TileLayer {
397    pub fn new(name: impl Into<String>, extent: u32) -> MltResult<Self> {
398        Self::with_capacity(name, extent, 0)
399    }
400
401    pub fn with_capacity(name: impl Into<String>, extent: u32, features: usize) -> MltResult<Self> {
402        let name = name.into();
403        validate_layer_name(&name)?;
404        let extent = Extent::new(extent)?;
405        Ok(Self {
406            name,
407            extent,
408            property_names: Vec::new(),
409            property_kinds: Vec::new(),
410            #[cfg(feature = "unstable-v2")]
411            m_value_names: Vec::new(),
412            #[cfg(feature = "unstable-v2")]
413            m_value_kinds: Vec::new(),
414            #[cfg(feature = "unstable-v2")]
415            nested_names: Vec::new(),
416            #[cfg(feature = "unstable-v2")]
417            nested_kinds: Vec::new(),
418            #[cfg(feature = "unstable-v2")]
419            z_step: None,
420            features: Vec::with_capacity(features),
421        })
422    }
423
424    pub(crate) fn from_parts(
425        name: impl Into<String>,
426        extent: u32,
427        property_names: Vec<String>,
428        features: Vec<TileFeature>,
429    ) -> MltResult<Self> {
430        let name = name.into();
431        validate_layer_name(&name)?;
432        let extent = Extent::new(extent)?;
433        validate_unique_names(&property_names, ColumnRole::Property)?;
434        let property_kinds = infer_kinds::<PropValue>(&property_names, &features)?;
435        let layer = Self {
436            name,
437            extent,
438            property_names,
439            property_kinds,
440            #[cfg(feature = "unstable-v2")]
441            m_value_names: Vec::new(),
442            #[cfg(feature = "unstable-v2")]
443            m_value_kinds: Vec::new(),
444            #[cfg(feature = "unstable-v2")]
445            nested_names: Vec::new(),
446            #[cfg(feature = "unstable-v2")]
447            nested_kinds: Vec::new(),
448            #[cfg(feature = "unstable-v2")]
449            z_step: None,
450            features,
451        };
452        Ok(layer)
453    }
454
455    #[must_use]
456    pub fn name(&self) -> &str {
457        &self.name
458    }
459
460    #[must_use]
461    pub fn extent(&self) -> Extent {
462        self.extent
463    }
464
465    #[must_use]
466    pub fn property_names(&self) -> &[String] {
467        &self.property_names
468    }
469
470    #[must_use]
471    pub fn property_kinds(&self) -> &[PropKind] {
472        &self.property_kinds
473    }
474
475    #[must_use]
476    pub fn features(&self) -> &[TileFeature] {
477        &self.features
478    }
479
480    #[must_use]
481    pub fn feature_count(&self) -> usize {
482        self.features.len()
483    }
484
485    /// Reject a column name any column of this layer already holds.
486    fn reject_name_in_use(&self, name: &str, role: ColumnRole) -> MltResult<()> {
487        let taken = self
488            .property_names
489            .iter()
490            .map(|n| (n.as_str(), ColumnRole::Property));
491        #[cfg(feature = "unstable-v2")]
492        let taken = taken
493            .chain(
494                self.m_value_names
495                    .iter()
496                    .map(|n| (n.as_str(), ColumnRole::MValue)),
497            )
498            .chain(
499                self.nested_names
500                    .iter()
501                    .map(|n| (n.as_str(), ColumnRole::Nested)),
502            );
503        reject_taken_name(name, role, taken)
504    }
505
506    pub fn add_property(
507        &mut self,
508        name: impl Into<String>,
509        kind: PropKind,
510    ) -> MltResult<PropertyKey> {
511        let name = name.into();
512        self.reject_name_in_use(&name, ColumnRole::Property)?;
513        for feature in &mut self.features {
514            feature.properties.push(PropValue::null(kind));
515        }
516        self.property_names.push(name);
517        self.property_kinds.push(kind);
518        Ok(PropertyKey(self.property_names.len() - 1))
519    }
520
521    #[cfg(feature = "unstable-v2")]
522    #[must_use]
523    pub fn m_value_names(&self) -> &[String] {
524        &self.m_value_names
525    }
526
527    #[cfg(feature = "unstable-v2")]
528    #[must_use]
529    pub fn m_value_kinds(&self) -> &[PropKind] {
530        &self.m_value_kinds
531    }
532
533    /// Declare a vertex-scoped column, whose values every feature then holds or does not.
534    #[cfg(feature = "unstable-v2")]
535    pub fn add_m_value(&mut self, name: impl Into<String>, kind: PropKind) -> MltResult<MValueKey> {
536        let name = name.into();
537        self.reject_name_in_use(&name, ColumnRole::MValue)?;
538        for feature in &mut self.features {
539            feature.m_values.push(MValue::null(kind));
540        }
541        self.m_value_names.push(name);
542        self.m_value_kinds.push(kind);
543        Ok(MValueKey(self.m_value_names.len() - 1))
544    }
545
546    /// Name the vertex-scoped columns the features already carry, as
547    /// [`Self::from_parts`] names the property columns.
548    #[cfg(feature = "unstable-v2")]
549    pub(crate) fn with_m_value_names(mut self, names: Vec<String>) -> MltResult<Self> {
550        validate_unique_names(&names, ColumnRole::MValue)?;
551        for name in &names {
552            self.reject_name_in_use(name, ColumnRole::MValue)?;
553        }
554        self.m_value_kinds = infer_kinds::<MValue>(&names, &self.features)?;
555        self.m_value_names = names;
556        for feature in &self.features {
557            self.validate_m_values(feature)?;
558        }
559        Ok(self)
560    }
561
562    #[cfg(feature = "unstable-v2")]
563    #[must_use]
564    pub fn nested_names(&self) -> &[String] {
565        &self.nested_names
566    }
567
568    #[cfg(feature = "unstable-v2")]
569    #[must_use]
570    pub fn nested_kinds(&self) -> &[NestedKind] {
571        &self.nested_kinds
572    }
573
574    /// The grid the features' z coordinates lie on, or [`None`] for a flat layer.
575    #[cfg(feature = "unstable-v2")]
576    #[must_use]
577    pub fn z_step(&self) -> Option<ZStep> {
578        self.z_step
579    }
580
581    /// Give the layer z coordinates on `step`, which every feature pushed after this must carry.
582    ///
583    /// A layer that already holds features is rejected, since they carry none.
584    #[cfg(feature = "unstable-v2")]
585    pub fn set_z_step(&mut self, step: ZStep) -> MltResult<()> {
586        if !self.features.is_empty() {
587            return Err(MltError::ZStepOnNonEmptyLayer(self.name.clone()));
588        }
589        self.z_step = Some(step);
590        Ok(())
591    }
592
593    /// Put the features, which already carry their z coordinates, on `step`.
594    #[cfg(feature = "unstable-v2")]
595    pub(crate) fn with_z_step(mut self, step: Option<ZStep>) -> MltResult<Self> {
596        self.z_step = step;
597        for feature in &self.features {
598            self.validate_z(feature)?;
599        }
600        Ok(self)
601    }
602
603    /// Declare a nested column of the given shape, which every feature then holds a value of.
604    ///
605    /// A scalar root is an ordinary property column, so [`NestedKind::Leaf`] is rejected here.
606    #[cfg(feature = "unstable-v2")]
607    pub fn add_nested(
608        &mut self,
609        name: impl Into<String>,
610        kind: NestedKind,
611    ) -> MltResult<NestedKey> {
612        let name = name.into();
613        self.reject_name_in_use(&name, ColumnRole::Nested)?;
614        validate_nested_kind(&name, &kind)?;
615        for feature in &mut self.features {
616            feature.nested.push(kind.null_value());
617        }
618        self.nested_names.push(name);
619        self.nested_kinds.push(kind);
620        Ok(NestedKey(self.nested_names.len() - 1))
621    }
622
623    /// Name and shape the nested columns the features already carry, as
624    /// [`Self::from_parts`] names the property columns.
625    #[cfg(feature = "unstable-v2")]
626    pub(crate) fn with_nested(
627        mut self,
628        names: Vec<String>,
629        kinds: Vec<NestedKind>,
630    ) -> MltResult<Self> {
631        validate_unique_names(&names, ColumnRole::Nested)?;
632        for name in &names {
633            self.reject_name_in_use(name, ColumnRole::Nested)?;
634        }
635        for (name, kind) in names.iter().zip(&kinds) {
636            validate_nested_kind(name, kind)?;
637        }
638        self.nested_names = names;
639        self.nested_kinds = kinds;
640        for feature in &self.features {
641            self.validate_nested(feature)?;
642        }
643        Ok(self)
644    }
645
646    pub fn push_feature(&mut self, feature: TileFeature) -> MltResult<()> {
647        self.validate_feature(&feature)?;
648        self.features.push(feature);
649        Ok(())
650    }
651
652    pub fn builder(name: impl Into<String>, extent: u32) -> MltResult<TileLayerBuilder> {
653        Ok(TileLayerBuilder {
654            layer: Self::new(name, extent)?,
655        })
656    }
657
658    /// A builder for a layer with the name, extent and z step of this one, and no columns or features.
659    #[must_use]
660    pub fn builder_like(&self) -> TileLayerBuilder {
661        let layer = Self {
662            name: self.name.clone(),
663            extent: self.extent,
664            property_names: Vec::new(),
665            property_kinds: Vec::new(),
666            #[cfg(feature = "unstable-v2")]
667            m_value_names: Vec::new(),
668            #[cfg(feature = "unstable-v2")]
669            m_value_kinds: Vec::new(),
670            #[cfg(feature = "unstable-v2")]
671            nested_names: Vec::new(),
672            #[cfg(feature = "unstable-v2")]
673            nested_kinds: Vec::new(),
674            #[cfg(feature = "unstable-v2")]
675            z_step: self.z_step,
676            features: Vec::new(),
677        };
678        TileLayerBuilder { layer }
679    }
680
681    fn validate_feature(&self, feature: &TileFeature) -> MltResult<()> {
682        validate_kinds::<PropValue>(&self.property_kinds, feature)?;
683        #[cfg(feature = "unstable-v2")]
684        self.validate_m_values(feature)?;
685        #[cfg(feature = "unstable-v2")]
686        self.validate_nested(feature)?;
687        #[cfg(feature = "unstable-v2")]
688        self.validate_z(feature)?;
689        Ok(())
690    }
691
692    /// Check a feature's z coordinates against the layer: one per vertex on a z layer, none on a flat one.
693    #[cfg(feature = "unstable-v2")]
694    fn validate_z(&self, feature: &TileFeature) -> MltResult<()> {
695        let expected = if self.z_step.is_some() {
696            feature.vertex_count()
697        } else {
698            0
699        };
700        if feature.z.len() == expected {
701            Ok(())
702        } else {
703            Err(MltError::ZVertexCountMismatch {
704                expected,
705                actual: feature.z.len(),
706            })
707        }
708    }
709
710    /// Check a feature's nested values against the layer's columns: one value per
711    /// column, of the shape the column declared.
712    #[cfg(feature = "unstable-v2")]
713    fn validate_nested(&self, feature: &TileFeature) -> MltResult<()> {
714        if feature.nested.len() != self.nested_kinds.len() {
715            return Err(MltError::NestedColumnCountMismatch {
716                expected: self.nested_kinds.len(),
717                actual: feature.nested.len(),
718            });
719        }
720        for (index, (value, kind)) in feature.nested.iter().zip(&self.nested_kinds).enumerate() {
721            if !kind.accepts(value) {
722                return Err(MltError::NestedValueMismatch {
723                    index,
724                    name: self.nested_names[index].clone(),
725                });
726            }
727        }
728        Ok(())
729    }
730
731    /// Check a feature's m-values against the layer's columns: one entry per
732    /// column, of the column's kind, holding one value per vertex it has.
733    #[cfg(feature = "unstable-v2")]
734    fn validate_m_values(&self, feature: &TileFeature) -> MltResult<()> {
735        validate_kinds::<MValue>(&self.m_value_kinds, feature)?;
736        let vertices = feature.vertex_count();
737        for (index, m_value) in feature.m_values.iter().enumerate() {
738            if let Some(len) = m_value.count()
739                && len != vertices
740            {
741                return Err(MltError::MValueVertexCountMismatch {
742                    index,
743                    expected: vertices,
744                    actual: len,
745                });
746            }
747        }
748        Ok(())
749    }
750}
751
752impl TileFeature {
753    #[must_use]
754    pub fn new(geometry: Geometry<i32>) -> Self {
755        Self {
756            id: None,
757            geometry,
758            properties: Vec::new(),
759            #[cfg(feature = "unstable-v2")]
760            m_values: Vec::new(),
761            #[cfg(feature = "unstable-v2")]
762            nested: Vec::new(),
763            #[cfg(feature = "unstable-v2")]
764            z: Vec::new(),
765        }
766    }
767
768    #[must_use]
769    pub fn with_id(geometry: Geometry<i32>, id: u64) -> Self {
770        Self {
771            id: Some(id),
772            geometry,
773            properties: Vec::new(),
774            #[cfg(feature = "unstable-v2")]
775            m_values: Vec::new(),
776            #[cfg(feature = "unstable-v2")]
777            nested: Vec::new(),
778            #[cfg(feature = "unstable-v2")]
779            z: Vec::new(),
780        }
781    }
782
783    #[must_use]
784    pub fn id(&self) -> Option<u64> {
785        self.id
786    }
787
788    #[must_use]
789    pub fn geometry(&self) -> &Geometry<i32> {
790        &self.geometry
791    }
792
793    #[must_use]
794    pub fn properties(&self) -> &[PropValue] {
795        &self.properties
796    }
797
798    /// How many vertices this feature stores, which is how many values each of
799    /// its m-values holds.
800    ///
801    /// A polygon ring's closing vertex is not stored, so it is not counted.
802    #[cfg(feature = "unstable-v2")]
803    #[must_use]
804    pub fn vertex_count(&self) -> usize {
805        stored_vertex_count(&self.geometry)
806    }
807
808    #[cfg(feature = "unstable-v2")]
809    #[must_use]
810    pub fn m_values(&self) -> &[MValue] {
811        &self.m_values
812    }
813
814    /// One z per vertex, in the order [`Self::vertex_count`] counts them, or none in a flat layer.
815    #[cfg(feature = "unstable-v2")]
816    #[must_use]
817    pub fn z(&self) -> &[i32] {
818        &self.z
819    }
820
821    /// Set this feature's z coordinates, one per vertex it stores.
822    #[cfg(feature = "unstable-v2")]
823    pub fn set_z(&mut self, z: Vec<i32>) -> MltResult<()> {
824        let expected = self.vertex_count();
825        if z.len() != expected {
826            return Err(MltError::ZVertexCountMismatch {
827                expected,
828                actual: z.len(),
829            });
830        }
831        self.z = z;
832        Ok(())
833    }
834
835    /// Set this feature's values for one m-value column, which must hold one
836    /// value per vertex unless the feature has none at all.
837    #[cfg(feature = "unstable-v2")]
838    pub fn set_m_value(&mut self, key: MValueKey, value: MValue) -> MltResult<()> {
839        let vertices = self.vertex_count();
840        let Some(slot) = self.m_values.get_mut(key.index()) else {
841            return Err(MltError::UnknownMValueKey {
842                index: key.index(),
843                columns: self.m_values.len(),
844            });
845        };
846        let expected = slot.kind();
847        let actual = value.kind();
848        if actual != expected {
849            return Err(MltError::MValueKindMismatch {
850                index: key.index(),
851                expected,
852                actual,
853            });
854        }
855        if let Some(len) = value.count()
856            && len != vertices
857        {
858            return Err(MltError::MValueVertexCountMismatch {
859                index: key.index(),
860                expected: vertices,
861                actual: len,
862            });
863        }
864        *slot = value;
865        Ok(())
866    }
867
868    #[cfg(feature = "unstable-v2")]
869    #[must_use]
870    pub fn nested(&self) -> &[NestedValue] {
871        &self.nested
872    }
873
874    /// Set this feature's value for one nested column, which must be the kind of
875    /// value the column holds.
876    ///
877    /// Only the layer knows the column's full shape, so that is checked when the
878    /// feature is pushed.
879    #[cfg(feature = "unstable-v2")]
880    pub fn set_nested(&mut self, key: NestedKey, value: NestedValue) -> MltResult<()> {
881        let columns = self.nested.len();
882        let Some(slot) = self.nested.get_mut(key.index()) else {
883            return Err(MltError::UnknownNestedKey {
884                index: key.index(),
885                columns,
886            });
887        };
888        if !slot.same_shape(&value) {
889            return Err(MltError::NestedShapeMismatch { index: key.index() });
890        }
891        *slot = value;
892        Ok(())
893    }
894
895    pub fn set_property(&mut self, key: PropertyKey, value: PropValue) -> MltResult<()> {
896        let Some(prop) = self.properties.get_mut(key.index()) else {
897            return Err(MltError::UnknownProperty {
898                index: key.index(),
899                count: self.properties.len(),
900            });
901        };
902        let expected = PropKind::from(&*prop);
903        let actual = PropKind::from(&value);
904        if actual != expected {
905            return Err(MltError::PropertyKindMismatch {
906                index: key.index(),
907                expected,
908                actual,
909            });
910        }
911        *prop = value;
912        Ok(())
913    }
914}
915
916pub struct TileLayerBuilder {
917    layer: TileLayer,
918}
919
920impl TileLayerBuilder {
921    pub fn add_property(
922        &mut self,
923        name: impl Into<String>,
924        kind: PropKind,
925    ) -> MltResult<PropertyKey> {
926        self.layer.add_property(name, kind)
927    }
928
929    #[cfg(feature = "unstable-v2")]
930    pub fn add_m_value(&mut self, name: impl Into<String>, kind: PropKind) -> MltResult<MValueKey> {
931        self.layer.add_m_value(name, kind)
932    }
933
934    #[cfg(feature = "unstable-v2")]
935    pub fn add_nested(
936        &mut self,
937        name: impl Into<String>,
938        kind: NestedKind,
939    ) -> MltResult<NestedKey> {
940        self.layer.add_nested(name, kind)
941    }
942
943    #[cfg(feature = "unstable-v2")]
944    pub fn set_z_step(&mut self, step: ZStep) -> MltResult<()> {
945        self.layer.set_z_step(step)
946    }
947
948    pub fn feature(&mut self, geometry: Geometry<i32>) -> TileFeatureBuilder<'_> {
949        let properties = self
950            .layer
951            .property_kinds
952            .iter()
953            .copied()
954            .map(PropValue::null)
955            .collect();
956        #[cfg(feature = "unstable-v2")]
957        let m_values = self
958            .layer
959            .m_value_kinds
960            .iter()
961            .copied()
962            .map(MValue::null)
963            .collect();
964        #[cfg(feature = "unstable-v2")]
965        let nested = self
966            .layer
967            .nested_kinds
968            .iter()
969            .map(NestedKind::null_value)
970            .collect();
971        TileFeatureBuilder {
972            layer: self,
973            feature: TileFeature {
974                id: None,
975                geometry,
976                properties,
977                #[cfg(feature = "unstable-v2")]
978                m_values,
979                #[cfg(feature = "unstable-v2")]
980                nested,
981                #[cfg(feature = "unstable-v2")]
982                z: Vec::new(),
983            },
984        }
985    }
986
987    /// Declare every property column of `layer`, in its order.
988    pub fn add_properties_like(&mut self, layer: &TileLayer) -> MltResult<Vec<PropertyKey>> {
989        (layer.property_names.iter().zip(&layer.property_kinds))
990            .map(|(name, &kind)| self.add_property(name, kind))
991            .collect()
992    }
993
994    /// Declare every m-value column of `layer`, in its order.
995    #[cfg(feature = "unstable-v2")]
996    pub fn add_m_values_like(&mut self, layer: &TileLayer) -> MltResult<Vec<MValueKey>> {
997        (layer.m_value_names.iter().zip(&layer.m_value_kinds))
998            .map(|(name, &kind)| self.add_m_value(name, kind))
999            .collect()
1000    }
1001
1002    /// Declare every nested column of `layer`, in its order.
1003    #[cfg(feature = "unstable-v2")]
1004    pub fn add_nested_like(&mut self, layer: &TileLayer) -> MltResult<Vec<NestedKey>> {
1005        (layer.nested_names.iter().zip(&layer.nested_kinds))
1006            .map(|(name, kind)| self.add_nested(name, kind.clone()))
1007            .collect()
1008    }
1009
1010    /// A feature with the geometry, id and z of `feature`, and no values yet.
1011    pub fn feature_like(&mut self, feature: &TileFeature) -> TileFeatureBuilder<'_> {
1012        let mut row = self.feature(feature.geometry.clone());
1013        row.feature.id = feature.id;
1014        #[cfg(feature = "unstable-v2")]
1015        row.feature.z.clone_from(&feature.z);
1016        row
1017    }
1018
1019    pub fn push_feature(&mut self, feature: TileFeature) -> MltResult<()> {
1020        self.layer.push_feature(feature)
1021    }
1022
1023    #[must_use]
1024    pub fn finish(self) -> TileLayer {
1025        self.layer
1026    }
1027}
1028
1029pub struct TileFeatureBuilder<'a> {
1030    layer: &'a mut TileLayerBuilder,
1031    feature: TileFeature,
1032}
1033
1034impl TileFeatureBuilder<'_> {
1035    pub fn id(&mut self, id: Option<u64>) -> &mut Self {
1036        self.feature.id = id;
1037        self
1038    }
1039
1040    pub fn property(&mut self, key: PropertyKey, value: PropValue) -> MltResult<&mut Self> {
1041        self.feature.set_property(key, value)?;
1042        Ok(self)
1043    }
1044
1045    #[cfg(feature = "unstable-v2")]
1046    pub fn m_value(&mut self, key: MValueKey, value: MValue) -> MltResult<&mut Self> {
1047        self.feature.set_m_value(key, value)?;
1048        Ok(self)
1049    }
1050
1051    #[cfg(feature = "unstable-v2")]
1052    pub fn nested(&mut self, key: NestedKey, value: NestedValue) -> MltResult<&mut Self> {
1053        self.feature.set_nested(key, value)?;
1054        Ok(self)
1055    }
1056
1057    #[cfg(feature = "unstable-v2")]
1058    pub fn z(&mut self, z: Vec<i32>) -> MltResult<&mut Self> {
1059        self.feature.set_z(z)?;
1060        Ok(self)
1061    }
1062
1063    pub fn finish(self) -> MltResult<()> {
1064        self.layer.push_feature(self.feature)
1065    }
1066}
1067
1068/// A single typed value for one property of one feature.
1069///
1070/// Mirrors the scalar variants of `ParsedProperty` at the per-feature
1071/// level. `SharedDict` items are flattened: each sub-field becomes its own
1072/// `PropValue::Str` entry in `TileFeature::properties`, with the
1073/// corresponding entry in `TileLayer::property_names` set to
1074/// `"prefix:suffix"`.
1075#[derive(Debug, Clone, PartialEq)]
1076pub enum PropValue {
1077    Bool(Option<bool>),
1078    I8(Option<i8>),
1079    U8(Option<u8>),
1080    I32(Option<i32>),
1081    U32(Option<u32>),
1082    I64(Option<i64>),
1083    U64(Option<u64>),
1084    F32(Option<f32>),
1085    F64(Option<f64>),
1086    Str(Option<String>),
1087}
1088
1089impl PropValue {
1090    #[must_use]
1091    pub fn kind(&self) -> PropKind {
1092        self.into()
1093    }
1094}
1095
1096/// A feature's values for one vertex-scoped column: one per vertex, or none at all.
1097///
1098/// The option is around the whole vector because a null is a feature's, not a
1099/// vertex's: a feature either measures every one of its vertices or none of them.
1100#[cfg(feature = "unstable-v2")]
1101#[derive(Debug, Clone, PartialEq)]
1102pub enum MValue {
1103    Bool(Option<Vec<bool>>),
1104    I8(Option<Vec<i8>>),
1105    U8(Option<Vec<u8>>),
1106    I32(Option<Vec<i32>>),
1107    U32(Option<Vec<u32>>),
1108    I64(Option<Vec<i64>>),
1109    U64(Option<Vec<u64>>),
1110    F32(Option<Vec<f32>>),
1111    F64(Option<Vec<f64>>),
1112    Str(Option<Vec<String>>),
1113}
1114
1115#[cfg(feature = "unstable-v2")]
1116impl MValue {
1117    #[must_use]
1118    pub fn is_null(&self) -> bool {
1119        self.count().is_none()
1120    }
1121}
1122
1123/// A borrowed, non-null per-feature property value.
1124///
1125/// Nullability is lifted to [`ColumnRef`](crate::decoder::ColumnRef): only non-null values appear in
1126/// [`FeatureRef::iter_properties`](crate::decoder::FeatureRef::iter_properties).
1127#[derive(Debug, Clone, Copy, PartialEq)]
1128pub enum PropValueRef<'a> {
1129    Bool(bool),
1130    I8(i8),
1131    U8(u8),
1132    I32(i32),
1133    U32(u32),
1134    I64(i64),
1135    U64(u64),
1136    F32(f32),
1137    F64(f64),
1138    Str(&'a str),
1139}
1140
1141impl From<bool> for PropValueRef<'_> {
1142    fn from(v: bool) -> Self {
1143        Self::Bool(v)
1144    }
1145}
1146impl From<i8> for PropValueRef<'_> {
1147    fn from(v: i8) -> Self {
1148        Self::I8(v)
1149    }
1150}
1151impl From<u8> for PropValueRef<'_> {
1152    fn from(v: u8) -> Self {
1153        Self::U8(v)
1154    }
1155}
1156impl From<i32> for PropValueRef<'_> {
1157    fn from(v: i32) -> Self {
1158        Self::I32(v)
1159    }
1160}
1161impl From<u32> for PropValueRef<'_> {
1162    fn from(v: u32) -> Self {
1163        Self::U32(v)
1164    }
1165}
1166impl From<i64> for PropValueRef<'_> {
1167    fn from(v: i64) -> Self {
1168        Self::I64(v)
1169    }
1170}
1171impl From<u64> for PropValueRef<'_> {
1172    fn from(v: u64) -> Self {
1173        Self::U64(v)
1174    }
1175}
1176impl From<f32> for PropValueRef<'_> {
1177    fn from(v: f32) -> Self {
1178        Self::F32(v)
1179    }
1180}
1181impl From<f64> for PropValueRef<'_> {
1182    fn from(v: f64) -> Self {
1183        Self::F64(v)
1184    }
1185}
1186
1187impl<'a> From<&'a str> for PropValueRef<'a> {
1188    fn from(v: &'a str) -> Self {
1189        Self::Str(v)
1190    }
1191}
1192
1193macro_rules! kind_mappings {
1194    (
1195        scalar { $($sv:ident),* $(,)? }
1196        string { $($gv:ident),* $(,)? }
1197    ) => {
1198        /// The data type of one property or m-value column.
1199        #[derive(Debug, Clone, Copy, PartialEq, Eq, strum::IntoStaticStr)]
1200        #[strum(serialize_all = "lowercase")]
1201        pub enum PropKind {
1202            $($sv,)*
1203            $($gv,)*
1204        }
1205
1206        impl From<&PropValue> for PropKind {
1207            fn from(prop: &PropValue) -> Self {
1208                match prop {
1209                    $(PropValue::$sv(_) => Self::$sv,)*
1210                    $(PropValue::$gv(_) => Self::$gv,)*
1211                }
1212            }
1213        }
1214
1215        impl PropValue {
1216            #[must_use]
1217            pub fn is_null(&self) -> bool {
1218                match self {
1219                    $(Self::$sv(v) => v.is_none(),)*
1220                    $(Self::$gv(v) => v.is_none(),)*
1221                }
1222            }
1223
1224            /// The null value of `kind`.
1225            #[must_use]
1226            pub fn null(kind: PropKind) -> Self {
1227                match kind {
1228                    $(PropKind::$sv => Self::$sv(None),)*
1229                    $(PropKind::$gv => Self::$gv(None),)*
1230                }
1231            }
1232
1233            /// The value, borrowed, or [`None`] for a null.
1234            #[inline]
1235            #[must_use]
1236            pub fn value_ref(&self) -> Option<PropValueRef<'_>> {
1237                match self {
1238                    $(Self::$sv(v) => v.map(PropValueRef::$sv),)*
1239                    $(Self::$gv(v) => v.as_deref().map(PropValueRef::$gv),)*
1240                }
1241            }
1242        }
1243
1244        impl PropValueRef<'_> {
1245            #[inline]
1246            pub(crate) fn kind(&self) -> PropKind {
1247                match self {
1248                    $(Self::$sv(_) => PropKind::$sv,)*
1249                    $(Self::$gv(_) => PropKind::$gv,)*
1250                }
1251            }
1252        }
1253
1254        #[cfg(feature = "unstable-v2")]
1255        impl MValue {
1256            #[must_use]
1257            pub fn kind(&self) -> PropKind {
1258                match self {
1259                    $(Self::$sv(_) => PropKind::$sv,)*
1260                    $(Self::$gv(_) => PropKind::$gv,)*
1261                }
1262            }
1263
1264            /// How many values this holds, or [`None`] when the feature carries none.
1265            #[must_use]
1266            pub fn count(&self) -> Option<usize> {
1267                match self {
1268                    $(Self::$sv(v) => v.as_ref().map(Vec::len),)*
1269                    $(Self::$gv(v) => v.as_ref().map(Vec::len),)*
1270                }
1271            }
1272
1273            /// The empty value of `kind`, which is what a feature with no values holds.
1274            #[must_use]
1275            pub fn null(kind: PropKind) -> Self {
1276                match kind {
1277                    $(PropKind::$sv => Self::$sv(None),)*
1278                    $(PropKind::$gv => Self::$gv(None),)*
1279                }
1280            }
1281        }
1282    };
1283}
1284
1285with_kinds!(kind_mappings);
1286
1287/// How many of a ring's coordinates MLT stores, which is all of them but a closing one.
1288pub(crate) fn stored_ring_len(ring: &LineString<i32>) -> usize {
1289    stored_len(&ring.0)
1290}
1291
1292/// [`stored_ring_len`] of a ring given as its coordinates.
1293pub(crate) fn stored_len(coords: &[Coord<i32>]) -> usize {
1294    if coords.len() > 1 && coords.last() == coords.first() {
1295        coords.len() - 1
1296    } else {
1297        coords.len()
1298    }
1299}
1300
1301/// How many vertices a geometry contributes to the layer's vertex sequence, which
1302/// is what an m-value column holds one value per.
1303#[cfg(feature = "unstable-v2")]
1304pub(crate) fn stored_vertex_count(geom: &Geometry<i32>) -> usize {
1305    let polygon = |poly: &geo_types::Polygon<i32>| {
1306        std::iter::once(poly.exterior())
1307            .chain(poly.interiors())
1308            .map(stored_ring_len)
1309            .sum::<usize>()
1310    };
1311    match geom {
1312        Geometry::Point(_) => 1,
1313        Geometry::Line(_) => 2,
1314        Geometry::LineString(ls) => ls.0.len(),
1315        Geometry::Polygon(p) => polygon(p),
1316        Geometry::MultiPoint(mp) => mp.0.len(),
1317        Geometry::MultiLineString(mls) => mls.iter().map(|ls| ls.0.len()).sum(),
1318        Geometry::MultiPolygon(mp) => mp.iter().map(polygon).sum(),
1319        Geometry::Triangle(t) => polygon(&t.to_polygon()),
1320        Geometry::Rect(r) => polygon(&r.to_polygon()),
1321        Geometry::GeometryCollection(gc) => gc.iter().map(stored_vertex_count).sum(),
1322    }
1323}
1324
1325/// Check a nested column's shape: an interior root, no empty field set, and no
1326/// more levels than the wire allows.
1327#[cfg(feature = "unstable-v2")]
1328fn validate_nested_kind(name: &str, kind: &NestedKind) -> MltResult<()> {
1329    if matches!(kind, NestedKind::Leaf(_)) {
1330        return Err(MltError::NestedRootIsLeaf(name.to_string()));
1331    }
1332    if kind.has_empty_map() {
1333        return Err(MltError::EmptyStructNode);
1334    }
1335    let depth = kind.depth();
1336    if depth > MAX_NESTED_DEPTH {
1337        return Err(MltError::NestedTooDeep(depth));
1338    }
1339    Ok(())
1340}
1341
1342pub(crate) fn validate_layer_name(name: &str) -> MltResult<()> {
1343    if name.is_empty() {
1344        Err(MltError::MissingLayerName)
1345    } else {
1346        Ok(())
1347    }
1348}
1349
1350/// Reject a column name one of `taken` already holds, naming the kind of column that holds it.
1351///
1352/// A layer has one namespace of column names, so this is the only rule every named column obeys.
1353pub(crate) fn reject_taken_name<'a>(
1354    name: &str,
1355    role: ColumnRole,
1356    taken: impl IntoIterator<Item = (&'a str, ColumnRole)>,
1357) -> MltResult<()> {
1358    for (seen, taken_by) in taken {
1359        if seen == name {
1360            return Err(MltError::DuplicateColumnName {
1361                name: name.to_string(),
1362                role,
1363                taken_by,
1364            });
1365        }
1366    }
1367    Ok(())
1368}
1369
1370/// Reject a name repeated within one list of column names.
1371fn validate_unique_names(names: &[String], role: ColumnRole) -> MltResult<()> {
1372    // Linear scan, not a HashSet: column counts are small, so this skips a per-layer alloc.
1373    // Empty names are allowed; real MVT tiles contain them.
1374    for (i, name) in names.iter().enumerate() {
1375        reject_taken_name(name, role, names[..i].iter().map(|n| (n.as_str(), role)))?;
1376    }
1377    Ok(())
1378}
1379
1380/// One feature's values for one kind of column, so the checks over them are written once.
1381trait FeatureColumn: Sized {
1382    /// This kind of column's values, one per column of the layer.
1383    fn of(feature: &TileFeature) -> &[Self];
1384    fn column_kind(&self) -> PropKind;
1385    /// A feature holding a different number of values than the layer has columns.
1386    fn count_mismatch(expected: usize, actual: usize) -> MltError;
1387    /// A value of a different kind than its column.
1388    fn kind_mismatch(index: usize, expected: PropKind, actual: PropKind) -> MltError;
1389}
1390
1391impl FeatureColumn for PropValue {
1392    fn of(feature: &TileFeature) -> &[Self] {
1393        &feature.properties
1394    }
1395
1396    fn column_kind(&self) -> PropKind {
1397        PropKind::from(self)
1398    }
1399
1400    fn count_mismatch(expected: usize, actual: usize) -> MltError {
1401        MltError::PropertyLengthMismatch { expected, actual }
1402    }
1403
1404    fn kind_mismatch(index: usize, expected: PropKind, actual: PropKind) -> MltError {
1405        MltError::PropertyKindMismatch {
1406            index,
1407            expected,
1408            actual,
1409        }
1410    }
1411}
1412
1413#[cfg(feature = "unstable-v2")]
1414impl FeatureColumn for MValue {
1415    fn of(feature: &TileFeature) -> &[Self] {
1416        &feature.m_values
1417    }
1418
1419    fn column_kind(&self) -> PropKind {
1420        self.kind()
1421    }
1422
1423    fn count_mismatch(expected: usize, actual: usize) -> MltError {
1424        MltError::MValueColumnCountMismatch { expected, actual }
1425    }
1426
1427    fn kind_mismatch(index: usize, expected: PropKind, actual: PropKind) -> MltError {
1428        MltError::MValueKindMismatch {
1429            index,
1430            expected,
1431            actual,
1432        }
1433    }
1434}
1435
1436/// The kind of each column, taken from the features that carry values for it.
1437///
1438/// A column no feature carries a value for is [`PropKind::Str`].
1439fn infer_kinds<T: FeatureColumn>(
1440    names: &[String],
1441    features: &[TileFeature],
1442) -> MltResult<Vec<PropKind>> {
1443    let mut kinds = vec![None; names.len()];
1444    for feature in features {
1445        let values = T::of(feature);
1446        if values.len() != names.len() {
1447            return Err(T::count_mismatch(names.len(), values.len()));
1448        }
1449        for (index, value) in values.iter().enumerate() {
1450            let actual = value.column_kind();
1451            match kinds[index] {
1452                Some(expected) if expected != actual => {
1453                    return Err(T::kind_mismatch(index, expected, actual));
1454                }
1455                None => kinds[index] = Some(actual),
1456                _ => {}
1457            }
1458        }
1459    }
1460    Ok(kinds
1461        .into_iter()
1462        .map(|kind| kind.unwrap_or(PropKind::Str))
1463        .collect())
1464}
1465
1466/// Check a feature's values for one kind of column: one per column, of the column's kind.
1467fn validate_kinds<T: FeatureColumn>(kinds: &[PropKind], feature: &TileFeature) -> MltResult<()> {
1468    let values = T::of(feature);
1469    if values.len() != kinds.len() {
1470        return Err(T::count_mismatch(kinds.len(), values.len()));
1471    }
1472    for (index, (value, expected)) in values.iter().zip(kinds).enumerate() {
1473        let actual = value.column_kind();
1474        if actual != *expected {
1475            return Err(T::kind_mismatch(index, *expected, actual));
1476        }
1477    }
1478    Ok(())
1479}
1480
1481#[cfg(test)]
1482mod tests {
1483    use geo_types::{Geometry, Point};
1484
1485    use super::*;
1486
1487    fn point_feature(properties: Vec<PropValue>) -> TileFeature {
1488        TileFeature {
1489            id: None,
1490            geometry: Geometry::Point(Point::new(0, 0)),
1491            properties,
1492            #[cfg(feature = "unstable-v2")]
1493            m_values: Vec::new(),
1494            #[cfg(feature = "unstable-v2")]
1495            nested: Vec::new(),
1496            #[cfg(feature = "unstable-v2")]
1497            z: Vec::new(),
1498        }
1499    }
1500
1501    #[test]
1502    fn tile_layer_constructor_rejects_empty_name() {
1503        assert!(matches!(
1504            TileLayer::new("", 4096),
1505            Err(MltError::MissingLayerName)
1506        ));
1507    }
1508
1509    #[test]
1510    fn tile_layer_constructor_rejects_zero_extent() {
1511        assert!(matches!(
1512            TileLayer::new("layer", 0),
1513            Err(MltError::InvalidExtent(0))
1514        ));
1515    }
1516
1517    #[test]
1518    fn add_property_rejects_duplicate_names() {
1519        let mut layer = TileLayer::new("layer", 4096).unwrap();
1520        layer.add_property("name", PropKind::Str).unwrap();
1521        assert_eq!(
1522            layer
1523                .add_property("name", PropKind::Str)
1524                .unwrap_err()
1525                .to_string(),
1526            "duplicate column name name: the property column repeats the property column"
1527        );
1528    }
1529
1530    #[test]
1531    fn from_parts_allows_empty_property_name() {
1532        // Real MVT tiles contain empty keys.
1533        assert!(TileLayer::from_parts("layer", 4096, vec![String::new()], vec![]).is_ok());
1534    }
1535
1536    #[test]
1537    fn from_parts_rejects_duplicate_property_name() {
1538        assert_eq!(
1539            TileLayer::from_parts("layer", 4096, vec!["dup".into(), "dup".into()], vec![])
1540                .unwrap_err()
1541                .to_string(),
1542            "duplicate column name dup: the property column repeats the property column"
1543        );
1544    }
1545
1546    #[test]
1547    fn push_feature_validates_property_count() {
1548        let mut layer = TileLayer::new("layer", 4096).unwrap();
1549        layer.add_property("name", PropKind::Str).unwrap();
1550        assert!(matches!(
1551            layer.push_feature(point_feature(vec![])),
1552            Err(MltError::PropertyLengthMismatch {
1553                expected: 1,
1554                actual: 0
1555            })
1556        ));
1557    }
1558
1559    #[test]
1560    fn push_feature_validates_property_kind() {
1561        let mut layer = TileLayer::new("layer", 4096).unwrap();
1562        layer.add_property("flag", PropKind::Bool).unwrap();
1563        layer
1564            .push_feature(point_feature(vec![PropValue::Bool(Some(true))]))
1565            .unwrap();
1566        assert!(matches!(
1567            layer.push_feature(point_feature(vec![PropValue::I32(Some(1))])),
1568            Err(MltError::PropertyKindMismatch {
1569                index: 0,
1570                expected: PropKind::Bool,
1571                actual: PropKind::I32,
1572            })
1573        ));
1574    }
1575
1576    #[test]
1577    fn declared_property_kind_is_enforced_for_first_feature() {
1578        let mut layer = TileLayer::new("layer", 4096).unwrap();
1579        layer.add_property("flag", PropKind::Bool).unwrap();
1580        assert!(matches!(
1581            layer.push_feature(point_feature(vec![PropValue::I32(Some(1))])),
1582            Err(MltError::PropertyKindMismatch {
1583                index: 0,
1584                expected: PropKind::Bool,
1585                actual: PropKind::I32,
1586            })
1587        ));
1588    }
1589
1590    #[cfg(feature = "unstable-v2")]
1591    #[test]
1592    fn push_feature_validates_m_value_count() {
1593        let mut layer = TileLayer::new("layer", 4096).unwrap();
1594        layer.add_m_value("m", PropKind::I32).unwrap();
1595        assert!(matches!(
1596            layer.push_feature(point_feature(vec![])),
1597            Err(MltError::MValueColumnCountMismatch {
1598                expected: 1,
1599                actual: 0
1600            })
1601        ));
1602    }
1603
1604    #[test]
1605    fn with_id_is_new_with_that_id() {
1606        let geometry = Geometry::Point(Point::new(1, 2));
1607        let mut expected = TileFeature::new(geometry.clone());
1608        expected.id = Some(7);
1609        assert_eq!(TileFeature::with_id(geometry, 7), expected);
1610    }
1611
1612    #[cfg(feature = "unstable-v2")]
1613    #[test]
1614    fn push_feature_validates_one_value_per_vertex() {
1615        let mut layer = TileLayer::new("layer", 4096).unwrap();
1616        layer.add_m_value("m", PropKind::I32).unwrap();
1617        let mut feature = point_feature(vec![]);
1618        feature.m_values = vec![MValue::I32(Some(vec![1, 2]))];
1619        assert!(matches!(
1620            layer.push_feature(feature),
1621            Err(MltError::MValueVertexCountMismatch {
1622                index: 0,
1623                expected: 1,
1624                actual: 2,
1625            })
1626        ));
1627    }
1628
1629    #[cfg(feature = "unstable-v2")]
1630    #[test]
1631    fn a_polygon_does_not_count_its_closing_vertices() {
1632        let ring = |pts: &[(i32, i32)]| {
1633            let mut ls = LineString::new(pts.iter().map(|&(x, y)| Coord { x, y }).collect());
1634            ls.close();
1635            ls
1636        };
1637        let poly = geo_types::Polygon::new(
1638            ring(&[(0, 0), (0, 4), (4, 4), (4, 0)]),
1639            vec![ring(&[(1, 1), (1, 2), (2, 2)])],
1640        );
1641        let feature = TileFeature::new(Geometry::Polygon(poly));
1642        assert_eq!(feature.vertex_count(), 7);
1643    }
1644
1645    #[cfg(feature = "unstable-v2")]
1646    #[test]
1647    fn a_shape_counts_its_root_as_the_first_level() {
1648        let kind = NestedKind::list(NestedKind::map([("a", NestedKind::Leaf(PropKind::I32))]));
1649        assert_eq!(kind.depth(), 3);
1650    }
1651
1652    #[cfg(feature = "unstable-v2")]
1653    #[test]
1654    fn a_map_may_leave_out_a_field_but_not_add_one() {
1655        let kind = NestedKind::map([
1656            ("a", NestedKind::Leaf(PropKind::I32)),
1657            ("b", NestedKind::Leaf(PropKind::Str)),
1658        ]);
1659        assert!(kind.accepts(&NestedValue::map([(
1660            "a",
1661            NestedValue::Leaf(PropValue::I32(Some(1)))
1662        )])));
1663        assert!(!kind.accepts(&NestedValue::map([(
1664            "c",
1665            NestedValue::Leaf(PropValue::I32(Some(1)))
1666        )])));
1667        assert!(!kind.accepts(&NestedValue::map([(
1668            "a",
1669            NestedValue::Leaf(PropValue::Str(Some("x".into())))
1670        )])));
1671    }
1672
1673    #[cfg(feature = "unstable-v2")]
1674    fn list_kind() -> NestedKind {
1675        NestedKind::list(NestedKind::Leaf(PropKind::I32))
1676    }
1677
1678    #[cfg(feature = "unstable-v2")]
1679    #[test]
1680    fn add_m_value_rejects_a_name_a_property_has() {
1681        let mut layer = TileLayer::new("layer", 4096).unwrap();
1682        layer.add_property("n", PropKind::I32).unwrap();
1683        assert_eq!(
1684            layer
1685                .add_m_value("n", PropKind::I32)
1686                .unwrap_err()
1687                .to_string(),
1688            "duplicate column name n: the m-value column repeats the property column"
1689        );
1690    }
1691
1692    #[cfg(feature = "unstable-v2")]
1693    #[test]
1694    fn add_m_value_rejects_a_name_a_nested_column_has() {
1695        let mut layer = TileLayer::new("layer", 4096).unwrap();
1696        layer.add_nested("n", list_kind()).unwrap();
1697        assert_eq!(
1698            layer
1699                .add_m_value("n", PropKind::I32)
1700                .unwrap_err()
1701                .to_string(),
1702            "duplicate column name n: the m-value column repeats the nested column"
1703        );
1704    }
1705
1706    #[cfg(feature = "unstable-v2")]
1707    #[test]
1708    fn add_m_value_rejects_duplicate_names() {
1709        let mut layer = TileLayer::new("layer", 4096).unwrap();
1710        layer.add_m_value("n", PropKind::I32).unwrap();
1711        assert_eq!(
1712            layer
1713                .add_m_value("n", PropKind::I32)
1714                .unwrap_err()
1715                .to_string(),
1716            "duplicate column name n: the m-value column repeats the m-value column"
1717        );
1718    }
1719
1720    #[cfg(feature = "unstable-v2")]
1721    #[test]
1722    fn add_property_rejects_a_name_an_m_value_has() {
1723        let mut layer = TileLayer::new("layer", 4096).unwrap();
1724        layer.add_m_value("n", PropKind::I32).unwrap();
1725        assert_eq!(
1726            layer
1727                .add_property("n", PropKind::I32)
1728                .unwrap_err()
1729                .to_string(),
1730            "duplicate column name n: the property column repeats the m-value column"
1731        );
1732    }
1733
1734    #[cfg(feature = "unstable-v2")]
1735    #[test]
1736    fn add_property_rejects_a_name_a_nested_column_has() {
1737        let mut layer = TileLayer::new("layer", 4096).unwrap();
1738        layer.add_nested("n", list_kind()).unwrap();
1739        assert_eq!(
1740            layer
1741                .add_property("n", PropKind::I32)
1742                .unwrap_err()
1743                .to_string(),
1744            "duplicate column name n: the property column repeats the nested column"
1745        );
1746    }
1747
1748    #[cfg(feature = "unstable-v2")]
1749    #[test]
1750    fn add_nested_rejects_a_name_a_property_has() {
1751        let mut layer = TileLayer::new("layer", 4096).unwrap();
1752        layer.add_property("n", PropKind::I32).unwrap();
1753        assert_eq!(
1754            layer.add_nested("n", list_kind()).unwrap_err().to_string(),
1755            "duplicate column name n: the nested column repeats the property column"
1756        );
1757    }
1758
1759    #[cfg(feature = "unstable-v2")]
1760    #[test]
1761    fn add_nested_rejects_a_name_an_m_value_already_has() {
1762        let mut layer = TileLayer::new("layer", 4096).unwrap();
1763        layer.add_m_value("n", PropKind::I32).unwrap();
1764        assert_eq!(
1765            layer.add_nested("n", list_kind()).unwrap_err().to_string(),
1766            "duplicate column name n: the nested column repeats the m-value column"
1767        );
1768    }
1769
1770    #[cfg(feature = "unstable-v2")]
1771    #[test]
1772    fn add_nested_rejects_duplicate_names() {
1773        let mut layer = TileLayer::new("layer", 4096).unwrap();
1774        layer.add_nested("n", list_kind()).unwrap();
1775        assert_eq!(
1776            layer.add_nested("n", list_kind()).unwrap_err().to_string(),
1777            "duplicate column name n: the nested column repeats the nested column"
1778        );
1779    }
1780
1781    #[cfg(feature = "unstable-v2")]
1782    #[test]
1783    fn set_nested_rejects_a_value_of_another_shape() {
1784        let mut layer = TileLayer::new("layer", 4096).unwrap();
1785        let key = layer
1786            .add_nested("n", NestedKind::list(NestedKind::Leaf(PropKind::I32)))
1787            .unwrap();
1788        let mut feature = point_feature(vec![]);
1789        feature.nested = vec![NestedValue::List(None)];
1790        assert_eq!(
1791            feature
1792                .set_nested(key, NestedValue::Map(None))
1793                .unwrap_err()
1794                .to_string(),
1795            "nested column 0 was given a value of another shape"
1796        );
1797    }
1798
1799    #[test]
1800    fn builder_uses_declared_property_kind_for_defaults() {
1801        let mut builder = TileLayer::builder("layer", 4096).unwrap();
1802        let flag = builder.add_property("flag", PropKind::Bool).unwrap();
1803        let mut feature = builder.feature(Geometry::Point(Point::new(0, 0)));
1804        feature.property(flag, PropValue::Bool(Some(true))).unwrap();
1805        feature.finish().unwrap();
1806        let layer = builder.finish();
1807
1808        assert_eq!(
1809            layer.features()[0].properties()[0],
1810            PropValue::Bool(Some(true))
1811        );
1812    }
1813}