1use 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#[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 spare_columns: Vec<Column<'a>>,
47 ids: Vec<Option<u64>>,
48 geometry: Geometries,
49}
50
51#[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 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 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 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 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#[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 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 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 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 pub fn encode(&self, cfg: EncoderConfig) -> MltResult<Vec<u8>> {
219 encode_layer(self, cfg)
220 }
221
222 pub fn write_mvt(&self, tile: MvtTileBuilder) -> MltResult<MvtTileBuilder> {
226 write_mvt_layer(self, tile)
227 }
228}
229
230#[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 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 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 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 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 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 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 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#[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#[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 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
583fn 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)]
603fn 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
619fn 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
625fn 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}