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