Skip to main content

mlt_core/
layer_writer.rs

1//! A layer written one feature at a time into columns, then encoded without building a
2//! [`TileLayer`](crate::TileLayer) or any value per feature.
3
4use std::ops::Range;
5
6use bitvec::vec::BitVec;
7use fast_mvt::{MvtFeatureBuilder, MvtGeomType, MvtLayerBuilder, MvtTileBuilder};
8use geo_types::Coord;
9
10use crate::decoder::{GeometryType, GeometryValues};
11use crate::encoder::{EncoderConfig, LayerSource, encode_layer};
12use crate::mvt::write_mvt_layer;
13use crate::tile::{
14    ColumnRole, Extent, PropKind, PropertyKey, reject_taken_name, validate_layer_name,
15};
16use crate::{MltError, MltResult, PropValueRef};
17
18/// A layer written feature by feature, straight from borrowed values, and encoded exactly as the
19/// same features in a [`TileLayer`](crate::TileLayer) would be.
20///
21/// Names and string values are borrowed for `'a`.
22/// [`LayerWriter::reset`] keeps the buffers for the next layer.
23/// A [`PropertyKey`] means nothing after [`LayerWriter::reset`].
24///
25/// ```
26/// use mlt_core::encoder::EncoderConfig;
27/// use mlt_core::geo_types::Coord;
28/// use mlt_core::{GeometryType, LayerWriter, PropKind};
29///
30/// let mut layer = LayerWriter::new("pois", 4096)?;
31/// let name = layer.add_property("name", PropKind::Str)?;
32/// let mut feature = layer.feature(GeometryType::Point);
33/// feature.id(Some(1)).points([Coord { x: 10, y: 20 }])?.property(name, "Cafe")?;
34/// feature.finish()?;
35/// let mlt = layer.encode(EncoderConfig::default())?;
36/// # Ok::<(), mlt_core::MltError>(())
37/// ```
38#[derive(Debug, Clone)]
39pub struct LayerWriter<'a> {
40    name: &'a str,
41    extent: Extent,
42    property_names: Vec<&'a str>,
43    property_kinds: Vec<PropKind>,
44    columns: Vec<Column<'a>>,
45    /// Columns of an earlier layer, kept for their buffers.
46    spare_columns: Vec<Column<'a>>,
47    ids: Vec<Option<u64>>,
48    geometry: Geometries,
49}
50
51/// One value or none per feature. Strings are kept as references, and every other kind as the
52/// bits of its value.
53#[derive(Debug, Clone)]
54struct Column<'a> {
55    kind: PropKind,
56    values: ColumnValues<'a>,
57    present: BitVec<u8>,
58}
59
60#[derive(Debug, Clone)]
61enum ColumnValues<'a> {
62    Scalars(Vec<u64>),
63    Strs(Vec<&'a str>),
64}
65
66impl ColumnValues<'_> {
67    fn new(kind: PropKind) -> Self {
68        if kind == PropKind::Str {
69            Self::Strs(Vec::new())
70        } else {
71            Self::Scalars(Vec::new())
72        }
73    }
74}
75
76impl<'a> Column<'a> {
77    fn new(kind: PropKind) -> Self {
78        Self {
79            kind,
80            values: ColumnValues::new(kind),
81            present: BitVec::new(),
82        }
83    }
84
85    /// Empties the column and makes it hold values of `kind`.
86    fn restart(&mut self, kind: PropKind) {
87        self.kind = kind;
88        self.clear();
89        if (kind == PropKind::Str) != matches!(self.values, ColumnValues::Strs(_)) {
90            self.values = ColumnValues::new(kind);
91        }
92    }
93
94    /// Makes the column hold exactly `len` features, the ones it had or none.
95    fn resize(&mut self, len: usize) {
96        match &mut self.values {
97            ColumnValues::Scalars(bits) => bits.resize(len, 0),
98            ColumnValues::Strs(strs) => strs.resize(len, ""),
99        }
100        self.present.resize(len, false);
101    }
102
103    fn clear(&mut self) {
104        self.truncate(0);
105    }
106
107    /// Sets the value of `feature`, replacing the one it already has.
108    fn set(&mut self, feature: usize, value: PropValueRef<'a>) {
109        if feature >= self.present.len() {
110            self.resize(feature + 1);
111        }
112        match (&mut self.values, scalar_bits(value)) {
113            (ColumnValues::Scalars(bits), Ok(value)) => bits[feature] = value,
114            (ColumnValues::Strs(strs), Err(text)) => strs[feature] = text,
115            _ => unreachable!("the writer checked the value is of the column's kind"),
116        }
117        self.present.set(feature, true);
118    }
119
120    /// Drops every feature from `len` on.
121    fn truncate(&mut self, len: usize) {
122        match &mut self.values {
123            ColumnValues::Scalars(bits) => bits.truncate(len),
124            ColumnValues::Strs(strs) => strs.truncate(len),
125        }
126        self.present.truncate(len);
127    }
128
129    fn get(&self, feature: usize) -> Option<PropValueRef<'_>> {
130        if !self.present[feature] {
131            return None;
132        }
133        match &self.values {
134            ColumnValues::Scalars(bits) => value_of_bits(self.kind, bits[feature]),
135            ColumnValues::Strs(strs) => Some(PropValueRef::Str(strs[feature])),
136        }
137    }
138}
139
140/// Every feature's geometry as groups of parts of coordinates, each level holding the cumulative
141/// end of its children.
142#[derive(Debug, Clone, Default)]
143struct Geometries {
144    types: Vec<GeometryType>,
145    feature_ends: Vec<u32>,
146    group_ends: Vec<u32>,
147    part_ends: Vec<u32>,
148    coords: Vec<Coord<i32>>,
149}
150
151impl<'a> LayerWriter<'a> {
152    pub fn new(name: &'a str, extent: u32) -> MltResult<Self> {
153        validate_layer_name(name)?;
154        Ok(Self {
155            name,
156            extent: Extent::new(extent)?,
157            property_names: Vec::new(),
158            property_kinds: Vec::new(),
159            columns: Vec::new(),
160            spare_columns: Vec::new(),
161            ids: Vec::new(),
162            geometry: Geometries::default(),
163        })
164    }
165
166    /// Starts over as an empty layer, keeping the buffers of this one.
167    pub fn reset(&mut self, name: &'a str, extent: u32) -> MltResult<()> {
168        validate_layer_name(name)?;
169        self.extent = Extent::new(extent)?;
170        self.name = name;
171        self.property_kinds.clear();
172        self.property_names.clear();
173        self.spare_columns.append(&mut self.columns);
174        self.ids.clear();
175        self.geometry.clear();
176        Ok(())
177    }
178
179    /// Adds a property column, which the features written so far have no value in.
180    pub fn add_property(&mut self, name: &'a str, kind: PropKind) -> MltResult<PropertyKey> {
181        let taken = self
182            .property_names
183            .iter()
184            .map(|&n| (n, ColumnRole::Property));
185        reject_taken_name(name, ColumnRole::Property, taken)?;
186        let mut column = self
187            .spare_columns
188            .pop()
189            .unwrap_or_else(|| Column::new(kind));
190        column.restart(kind);
191        column.resize(self.feature_count());
192        self.columns.push(column);
193        self.property_names.push(name);
194        self.property_kinds.push(kind);
195        Ok(PropertyKey::new(self.property_names.len() - 1))
196    }
197
198    /// Starts a feature of `geometry_type`. It joins the layer when [`FeatureWriter::finish`]
199    /// succeeds, and is discarded otherwise.
200    pub fn feature<'w>(&'w mut self, geometry_type: GeometryType) -> FeatureWriter<'w, 'a> {
201        FeatureWriter {
202            start: self.geometry.start(),
203            layer: self,
204            geometry_type,
205            id: None,
206            finished: false,
207        }
208    }
209
210    #[must_use]
211    pub fn feature_count(&self) -> usize {
212        self.ids.len()
213    }
214
215    /// Encodes the layer as MLT, choosing every encoding as [`TileLayer::encode`] does.
216    ///
217    /// [`TileLayer::encode`]: crate::TileLayer::encode
218    pub fn encode(&self, cfg: EncoderConfig) -> MltResult<Vec<u8>> {
219        encode_layer(self, cfg)
220    }
221
222    /// Adds the layer to an MVT tile, as [`tile_layers_to_mvt`] would.
223    ///
224    /// [`tile_layers_to_mvt`]: crate::mvt::tile_layers_to_mvt
225    pub fn write_mvt(&self, tile: MvtTileBuilder) -> MltResult<MvtTileBuilder> {
226        write_mvt_layer(self, tile)
227    }
228}
229
230/// A feature being written into a [`LayerWriter`].
231#[must_use = "call .finish() to add the feature to the layer"]
232pub struct FeatureWriter<'w, 'a> {
233    layer: &'w mut LayerWriter<'a>,
234    geometry_type: GeometryType,
235    id: Option<u64>,
236    /// Where this feature's geometry starts, to check its shape and to discard it.
237    start: FeatureStart,
238    finished: bool,
239}
240
241impl<'a> FeatureWriter<'_, 'a> {
242    pub fn id(&mut self, id: Option<u64>) -> &mut Self {
243        self.id = id;
244        self
245    }
246
247    /// Sets the feature's value in the `key` column, which must be of the value's kind.
248    pub fn property(
249        &mut self,
250        key: PropertyKey,
251        value: impl Into<PropValueRef<'a>>,
252    ) -> MltResult<&mut Self> {
253        let value = value.into();
254        let index = key.index();
255        let layer = &mut *self.layer;
256        let Some(&expected) = layer.property_kinds.get(index) else {
257            return Err(MltError::UnknownProperty {
258                index,
259                count: layer.property_kinds.len(),
260            });
261        };
262        let actual = value.kind();
263        if actual != expected {
264            return Err(MltError::PropertyKindMismatch {
265                index,
266                expected,
267                actual,
268            });
269        }
270        layer.columns[index].set(layer.ids.len(), value);
271        Ok(self)
272    }
273
274    /// Adds points to a [`GeometryType::Point`] (which holds exactly one) or
275    /// [`GeometryType::MultiPoint`] feature.
276    pub fn points(&mut self, coords: impl IntoIterator<Item = Coord<i32>>) -> MltResult<&mut Self> {
277        self.check_family(Family::Points, "points")?;
278        let geometry = &mut self.layer.geometry;
279        if geometry.group_ends.len() == self.start.groups {
280            geometry.begin_group()?;
281            geometry.begin_part()?;
282        }
283        geometry.extend(coords)?;
284        Ok(self)
285    }
286
287    /// Adds a line to a [`GeometryType::LineString`] (which holds exactly one) or
288    /// [`GeometryType::MultiLineString`] feature.
289    pub fn line(&mut self, coords: impl IntoIterator<Item = Coord<i32>>) -> MltResult<&mut Self> {
290        self.check_family(Family::Lines, "lines")?;
291        let geometry = &mut self.layer.geometry;
292        if geometry.group_ends.len() == self.start.groups {
293            geometry.begin_group()?;
294        }
295        geometry.begin_part()?;
296        geometry.extend(coords)?;
297        Ok(self)
298    }
299
300    /// Starts a polygon of a [`GeometryType::Polygon`] (which holds exactly one) or
301    /// [`GeometryType::MultiPolygon`] feature with its exterior ring, which may be open or closed.
302    pub fn exterior_ring(
303        &mut self,
304        coords: impl IntoIterator<Item = Coord<i32>>,
305    ) -> MltResult<&mut Self> {
306        self.check_family(Family::Polygons, "rings")?;
307        self.layer.geometry.begin_group()?;
308        self.ring(coords)
309    }
310
311    /// Adds a hole to the polygon the last [`Self::exterior_ring`] started, which may be open or
312    /// closed.
313    pub fn hole(&mut self, coords: impl IntoIterator<Item = Coord<i32>>) -> MltResult<&mut Self> {
314        self.check_family(Family::Polygons, "rings")?;
315        if self.layer.geometry.group_ends.len() == self.start.groups {
316            return Err(MltError::InvalidFeatureGeometry(
317                self.geometry_type,
318                "starts with a hole instead of an exterior ring",
319            ));
320        }
321        self.ring(coords)
322    }
323
324    fn ring(&mut self, coords: impl IntoIterator<Item = Coord<i32>>) -> MltResult<&mut Self> {
325        let geometry = &mut self.layer.geometry;
326        geometry.begin_part()?;
327        geometry.extend(coords)?;
328        Ok(self)
329    }
330
331    fn check_family(&self, family: Family, what: &'static str) -> MltResult<()> {
332        if Family::of(self.geometry_type).0 == family {
333            Ok(())
334        } else {
335            Err(MltError::InvalidFeatureGeometry(self.geometry_type, what))
336        }
337    }
338
339    /// Adds the feature to the layer, or discards it if its geometry is not of its type.
340    pub fn finish(mut self) -> MltResult<()> {
341        let layer = &mut *self.layer;
342        let geometry = &layer.geometry;
343        let invalid = match Family::of(self.geometry_type) {
344            (_, true) => None,
345            (Family::Points, false) => (geometry.coords.len() - self.start.coords != 1)
346                .then_some("needs exactly one point"),
347            (Family::Lines, false) => (geometry.part_ends.len() - self.start.parts != 1)
348                .then_some("needs exactly one line"),
349            (Family::Polygons, false) => (geometry.group_ends.len() - self.start.groups != 1)
350                .then_some("needs exactly one polygon"),
351        };
352        if let Some(reason) = invalid {
353            return Err(MltError::InvalidFeatureGeometry(self.geometry_type, reason));
354        }
355        layer.geometry.finish_feature(self.geometry_type)?;
356        let len = layer.ids.len() + 1;
357        for column in &mut layer.columns {
358            column.resize(len);
359        }
360        layer.ids.push(self.id);
361        self.finished = true;
362        Ok(())
363    }
364}
365
366impl Drop for FeatureWriter<'_, '_> {
367    fn drop(&mut self) {
368        if self.finished {
369            return;
370        }
371        let layer = &mut *self.layer;
372        let feature = layer.ids.len();
373        for column in &mut layer.columns {
374            column.truncate(feature);
375        }
376        layer.geometry.rewind(self.start);
377    }
378}
379
380/// The kind of parts a geometry type is made of, and whether it repeats them.
381#[derive(Clone, Copy, PartialEq, Eq)]
382enum Family {
383    Points,
384    Lines,
385    Polygons,
386}
387
388impl Family {
389    fn of(geometry_type: GeometryType) -> (Self, bool) {
390        match geometry_type {
391            GeometryType::Point => (Self::Points, false),
392            GeometryType::MultiPoint => (Self::Points, true),
393            GeometryType::LineString => (Self::Lines, false),
394            GeometryType::MultiLineString => (Self::Lines, true),
395            GeometryType::Polygon => (Self::Polygons, false),
396            GeometryType::MultiPolygon => (Self::Polygons, true),
397        }
398    }
399}
400
401/// How much of each [`Geometries`] level was in use when a feature started.
402#[derive(Debug, Clone, Copy)]
403struct FeatureStart {
404    groups: usize,
405    parts: usize,
406    coords: usize,
407}
408
409impl Geometries {
410    fn clear(&mut self) {
411        self.rewind(FeatureStart {
412            groups: 0,
413            parts: 0,
414            coords: 0,
415        });
416        self.types.clear();
417        self.feature_ends.clear();
418    }
419
420    fn start(&self) -> FeatureStart {
421        FeatureStart {
422            groups: self.group_ends.len(),
423            parts: self.part_ends.len(),
424            coords: self.coords.len(),
425        }
426    }
427
428    fn rewind(&mut self, start: FeatureStart) {
429        self.group_ends.truncate(start.groups);
430        self.part_ends.truncate(start.parts);
431        self.coords.truncate(start.coords);
432    }
433
434    fn begin_group(&mut self) -> MltResult<()> {
435        self.group_ends.push(len32(self.part_ends.len())?);
436        Ok(())
437    }
438
439    fn begin_part(&mut self) -> MltResult<()> {
440        self.part_ends.push(len32(self.coords.len())?);
441        *self
442            .group_ends
443            .last_mut()
444            .expect("a part belongs to a group") += 1;
445        Ok(())
446    }
447
448    fn extend(&mut self, coords: impl IntoIterator<Item = Coord<i32>>) -> MltResult<()> {
449        self.coords.extend(coords);
450        *self
451            .part_ends
452            .last_mut()
453            .expect("coordinates belong to a part") = len32(self.coords.len())?;
454        Ok(())
455    }
456
457    fn finish_feature(&mut self, geometry_type: GeometryType) -> MltResult<()> {
458        self.feature_ends.push(len32(self.group_ends.len())?);
459        self.types.push(geometry_type);
460        Ok(())
461    }
462
463    fn groups(&self, feature: usize) -> Range<usize> {
464        item_range(&self.feature_ends, feature)
465    }
466
467    /// The parts of consecutive `groups`.
468    fn parts(&self, groups: Range<usize>) -> Range<usize> {
469        if groups.is_empty() {
470            return 0..0;
471        }
472        item_range(&self.group_ends, groups.start).start..self.group_ends[groups.end - 1] as usize
473    }
474
475    fn part(&self, part: usize) -> &[Coord<i32>] {
476        &self.coords[item_range(&self.part_ends, part)]
477    }
478
479    fn feature_coords(&self, feature: usize) -> &[Coord<i32>] {
480        let parts = self.parts(self.groups(feature));
481        if parts.is_empty() {
482            return &[];
483        }
484        &self.coords
485            [item_range(&self.part_ends, parts.start).start..self.part_ends[parts.end - 1] as usize]
486    }
487}
488
489impl LayerSource for LayerWriter<'_> {
490    fn name(&self) -> &str {
491        self.name
492    }
493
494    fn extent(&self) -> Extent {
495        self.extent
496    }
497
498    fn feature_count(&self) -> usize {
499        Self::feature_count(self)
500    }
501
502    fn property_count(&self) -> usize {
503        self.property_names.len()
504    }
505
506    fn property_name(&self, column: usize) -> &str {
507        self.property_names[column]
508    }
509
510    fn property_kinds(&self) -> &[PropKind] {
511        &self.property_kinds
512    }
513
514    fn id(&self, feature: usize) -> Option<u64> {
515        self.ids[feature]
516    }
517
518    fn property(&self, feature: usize, column: usize) -> Option<PropValueRef<'_>> {
519        self.columns[column].get(feature)
520    }
521
522    fn coords(&self) -> impl Iterator<Item = Coord<i32>> + '_ {
523        self.geometry.coords.iter().copied()
524    }
525
526    fn first_coord(&self, feature: usize) -> Option<Coord<i32>> {
527        self.geometry.feature_coords(feature).first().copied()
528    }
529
530    fn coord_count(&self, feature: usize) -> usize {
531        self.geometry.feature_coords(feature).len()
532    }
533
534    fn push_geometry(&self, feature: usize, values: &mut GeometryValues) {
535        let geometry = &self.geometry;
536        let groups = geometry.groups(feature);
537        let parts = geometry.parts(groups.clone());
538        let part = |p| geometry.part(p);
539        match geometry.types[feature] {
540            GeometryType::Point => values.push_point(geometry.feature_coords(feature)[0]),
541            GeometryType::MultiPoint => {
542                values.push_multi_point(geometry.feature_coords(feature).iter().copied());
543            }
544            GeometryType::LineString => values.push_linestring(part(parts.start)),
545            GeometryType::MultiLineString => values.push_multi_linestring(parts.map(part)),
546            GeometryType::Polygon => values.push_polygon(parts.map(part)),
547            GeometryType::MultiPolygon => values
548                .push_multi_polygon(groups.map(|group| geometry.parts(group..group + 1).map(part))),
549        }
550    }
551
552    fn mvt_feature(&self, feature: usize, layer: MvtLayerBuilder) -> MltResult<MvtFeatureBuilder> {
553        let geometry = &self.geometry;
554        let groups = geometry.groups(feature);
555        let part = |p| geometry.part(p).iter().copied();
556        Ok(match Family::of(geometry.types[feature]).0 {
557            Family::Points => {
558                let mut out = layer.feature_of(MvtGeomType::Point);
559                out.points(geometry.feature_coords(feature).iter().copied())?;
560                out
561            }
562            Family::Lines => {
563                let mut out = layer.feature_of(MvtGeomType::LineString);
564                for p in geometry.parts(groups) {
565                    out.line(part(p))?;
566                }
567                out
568            }
569            Family::Polygons => {
570                let mut out = layer.feature_of(MvtGeomType::Polygon);
571                for group in groups {
572                    let rings = geometry.parts(group..group + 1);
573                    for p in rings.clone() {
574                        out.ring(part(p), p == rings.start)?;
575                    }
576                }
577                out
578            }
579        })
580    }
581}
582
583/// The bits of a scalar `value`, or its text.
584fn scalar_bits(value: PropValueRef<'_>) -> Result<u64, &str> {
585    Ok(match value {
586        PropValueRef::Bool(v) => u64::from(v),
587        PropValueRef::I8(v) => i64::from(v).cast_unsigned(),
588        PropValueRef::U8(v) => u64::from(v),
589        PropValueRef::I32(v) => i64::from(v).cast_unsigned(),
590        PropValueRef::U32(v) => u64::from(v),
591        PropValueRef::I64(v) => v.cast_unsigned(),
592        PropValueRef::U64(v) => v,
593        PropValueRef::F32(v) => u64::from(v.to_bits()),
594        PropValueRef::F64(v) => v.to_bits(),
595        PropValueRef::Str(text) => return Err(text),
596    })
597}
598
599#[expect(
600    clippy::cast_possible_truncation,
601    reason = "the bits were stored from a value of `kind`"
602)]
603/// The value of a scalar `kind` stored as `bits`, or [`None`] for a string kind.
604fn value_of_bits(kind: PropKind, bits: u64) -> Option<PropValueRef<'static>> {
605    Some(match kind {
606        PropKind::Bool => PropValueRef::Bool(bits != 0),
607        PropKind::I8 => PropValueRef::I8(bits.cast_signed() as i8),
608        PropKind::U8 => PropValueRef::U8(bits as u8),
609        PropKind::I32 => PropValueRef::I32(bits.cast_signed() as i32),
610        PropKind::U32 => PropValueRef::U32(bits as u32),
611        PropKind::I64 => PropValueRef::I64(bits.cast_signed()),
612        PropKind::U64 => PropValueRef::U64(bits),
613        PropKind::F32 => PropValueRef::F32(f32::from_bits(bits as u32)),
614        PropKind::F64 => PropValueRef::F64(f64::from_bits(bits)),
615        PropKind::Str => return None,
616    })
617}
618
619/// The range of item `index` among cumulative `ends`.
620fn item_range(ends: &[u32], index: usize) -> Range<usize> {
621    let start = index.checked_sub(1).map_or(0, |prev| ends[prev] as usize);
622    start..ends[index] as usize
623}
624
625/// A layer holds at most `u32::MAX` vertices.
626fn len32(len: usize) -> MltResult<u32> {
627    u32::try_from(len).map_err(|_| MltError::IntegerOverflow)
628}
629
630#[cfg(test)]
631mod tests {
632    use geo_types::{Geometry, Point};
633
634    use super::*;
635    use crate::{PropValue, TileLayer};
636
637    fn c(x: i32, y: i32) -> Coord<i32> {
638        Coord { x, y }
639    }
640
641    fn encode(writer: &LayerWriter<'_>) -> Vec<u8> {
642        writer.encode(EncoderConfig::default()).expect("encode")
643    }
644
645    fn two_points(writer: &mut LayerWriter<'static>) -> MltResult<()> {
646        writer.reset("points", 4096)?;
647        let name = writer.add_property("name", PropKind::Str)?;
648        let rank = writer.add_property("rank", PropKind::U32)?;
649        let mut feature = writer.feature(GeometryType::Point);
650        feature.id(Some(1)).points([c(1, 2)])?;
651        feature.property(name, "a")?.property(rank, 7_u32)?;
652        feature.finish()?;
653        let mut feature = writer.feature(GeometryType::Point);
654        feature.points([c(3, 4)])?.property(name, "b")?;
655        feature.finish()
656    }
657
658    #[test]
659    fn rejects_extra_points_in_a_point() {
660        let mut writer = LayerWriter::new("l", 4096).unwrap();
661        let mut feature = writer.feature(GeometryType::Point);
662        feature.points([c(1, 1), c(2, 2)]).unwrap();
663        insta::assert_snapshot!(feature.finish().unwrap_err(), @"a Point feature needs exactly one point");
664        assert_eq!(writer.feature_count(), 0);
665    }
666
667    #[test]
668    fn rejects_empty_line_string() {
669        let mut writer = LayerWriter::new("l", 4096).unwrap();
670        insta::assert_snapshot!(
671            writer.feature(GeometryType::LineString).finish().unwrap_err(),
672            @"a LineString feature needs exactly one line"
673        );
674        assert_eq!(writer.feature_count(), 0);
675    }
676
677    #[test]
678    fn rejects_hole_before_exterior_ring() {
679        let mut writer = LayerWriter::new("l", 4096).unwrap();
680        let mut feature = writer.feature(GeometryType::Polygon);
681        insta::assert_snapshot!(feature.hole([c(0, 0)]).err().unwrap(), @"a Polygon feature starts with a hole instead of an exterior ring");
682    }
683
684    #[test]
685    fn rejects_line_in_polygon() {
686        let mut writer = LayerWriter::new("l", 4096).unwrap();
687        let mut feature = writer.feature(GeometryType::Polygon);
688        insta::assert_snapshot!(feature.line([c(0, 0)]).err().unwrap(), @"a Polygon feature lines");
689    }
690
691    #[test]
692    fn rejects_empty_polygon() {
693        let mut writer = LayerWriter::new("l", 4096).unwrap();
694        insta::assert_snapshot!(
695            writer.feature(GeometryType::Polygon).finish().unwrap_err(),
696            @"a Polygon feature needs exactly one polygon"
697        );
698        assert_eq!(writer.feature_count(), 0);
699    }
700
701    #[test]
702    fn accepts_empty_multi_polygon() {
703        let mut writer = LayerWriter::new("l", 4096).unwrap();
704        writer.feature(GeometryType::MultiPolygon).finish().unwrap();
705        assert_eq!(writer.feature_count(), 1);
706    }
707
708    #[test]
709    fn rejects_value_of_another_kind() {
710        let mut writer = LayerWriter::new("l", 4096).unwrap();
711        let rank = writer.add_property("rank", PropKind::U32).unwrap();
712        let mut feature = writer.feature(GeometryType::Point);
713        insta::assert_snapshot!(feature.property(rank, 7_i32).err().unwrap(), @"property 0 kind mismatch: expected U32, got I32");
714    }
715
716    #[test]
717    fn rejects_unknown_property_key() {
718        let mut writer = LayerWriter::new("l", 4096).unwrap();
719        writer.add_property("rank", PropKind::U32).unwrap();
720        let mut feature = writer.feature(GeometryType::Point);
721        insta::assert_snapshot!(
722            feature.property(PropertyKey::new(1), 7_u32).err().unwrap(),
723            @"property 1 does not exist: the layer has 1"
724        );
725    }
726
727    #[test]
728    fn discards_unfinished_features() {
729        let mut expected = LayerWriter::new("l", 4096).unwrap();
730        two_points(&mut expected).unwrap();
731        let mut writer = LayerWriter::new("l", 4096).unwrap();
732        two_points(&mut writer).unwrap();
733        let mut feature = writer.feature(GeometryType::MultiPoint);
734        feature
735            .points([c(9, 9)])
736            .unwrap()
737            .property(PropertyKey::new(0), "gone")
738            .unwrap();
739        drop(feature);
740        assert_eq!(encode(&writer), encode(&expected));
741    }
742
743    #[test]
744    fn reset_writer_encodes_like_a_new_one() {
745        let mut fresh = LayerWriter::new("x", 4096).unwrap();
746        two_points(&mut fresh).unwrap();
747        let mut reused = LayerWriter::new("other", 512).unwrap();
748        reused.add_property("unrelated", PropKind::F64).unwrap();
749        reused.add_property("text", PropKind::Str).unwrap();
750        let mut feature = reused.feature(GeometryType::LineString);
751        feature.line([c(0, 0), c(5, 5)]).unwrap();
752        feature
753            .property(PropertyKey::new(1), "longer text")
754            .unwrap();
755        feature.finish().unwrap();
756        two_points(&mut reused).unwrap();
757        assert_eq!(encode(&reused), encode(&fresh));
758    }
759
760    #[test]
761    fn column_added_after_features_encodes_like_tile_layer() {
762        let mut writer = LayerWriter::new("l", 4096).unwrap();
763        let mut feature = writer.feature(GeometryType::Point);
764        feature.points([c(1, 1)]).unwrap();
765        feature.finish().unwrap();
766        let flag = writer.add_property("flag", PropKind::Bool).unwrap();
767        let mut feature = writer.feature(GeometryType::Point);
768        feature
769            .points([c(2, 2)])
770            .unwrap()
771            .property(flag, true)
772            .unwrap();
773        feature.finish().unwrap();
774
775        let mut layer = TileLayer::builder("l", 4096).unwrap();
776        layer
777            .feature(Geometry::Point(Point::new(1, 1)))
778            .finish()
779            .unwrap();
780        let flag = layer.add_property("flag", PropKind::Bool).unwrap();
781        let mut feature = layer.feature(Geometry::Point(Point::new(2, 2)));
782        feature.property(flag, PropValue::Bool(Some(true))).unwrap();
783        feature.finish().unwrap();
784        let layer = layer.finish();
785        assert_eq!(
786            encode(&writer),
787            layer.encode(EncoderConfig::default()).unwrap()
788        );
789    }
790
791    #[test]
792    fn repeated_property_keeps_the_last_value() {
793        let mut writer = LayerWriter::new("l", 4096).unwrap();
794        let flag = writer.add_property("flag", PropKind::Bool).unwrap();
795        let mut feature = writer.feature(GeometryType::Point);
796        feature
797            .points([c(2, 2)])
798            .unwrap()
799            .property(flag, false)
800            .unwrap()
801            .property(flag, true)
802            .unwrap();
803        feature.finish().unwrap();
804
805        let mut layer = TileLayer::builder("l", 4096).unwrap();
806        let flag = layer.add_property("flag", PropKind::Bool).unwrap();
807        let mut feature = layer.feature(Geometry::Point(Point::new(2, 2)));
808        feature.property(flag, PropValue::Bool(Some(true))).unwrap();
809        feature.finish().unwrap();
810        let layer = layer.finish();
811        assert_eq!(
812            encode(&writer),
813            layer.encode(EncoderConfig::default()).unwrap()
814        );
815    }
816}