1#[cfg(feature = "unstable-v2")]
9use std::collections::BTreeMap;
10use std::num::NonZeroU32;
11
12use geo_types::{Coord, Geometry, LineString};
13#[cfg(feature = "unstable-v2")]
14use num_traits::ToPrimitive as _;
15
16use crate::{MltError, MltResult};
17
18#[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#[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 pub const MIN_EXPONENT: i8 = -3;
55 pub const MAX_EXPONENT: i8 = 4;
57 pub const BASE_METRES: f64 = -10_000.0;
59
60 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 pub(crate) fn code(self) -> u8 {
76 (self.0 - Self::MIN_EXPONENT).cast_unsigned()
77 }
78
79 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 #[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 #[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 #[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 fn power(self) -> f64 {
124 10f64.powi(self.0.unsigned_abs().into())
125 }
126}
127
128#[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#[derive(Debug, Clone, Copy, PartialEq, Eq, strum::Display)]
147pub enum ColumnRole {
148 #[strum(serialize = "property")]
150 Property,
151 #[cfg(feature = "unstable-v2")]
153 #[strum(serialize = "m-value")]
154 MValue,
155 #[cfg(feature = "unstable-v2")]
157 #[strum(serialize = "nested")]
158 Nested,
159}
160
161#[derive(Debug, Clone, PartialEq)]
167pub struct TileLayer {
168 pub(crate) name: String,
169 pub(crate) extent: Extent,
170 pub(crate) property_names: Vec<String>,
172 pub(crate) property_kinds: Vec<PropKind>,
174 #[cfg(feature = "unstable-v2")]
176 pub(crate) m_value_names: Vec<String>,
177 #[cfg(feature = "unstable-v2")]
179 pub(crate) m_value_kinds: Vec<PropKind>,
180 #[cfg(feature = "unstable-v2")]
182 pub(crate) nested_names: Vec<String>,
183 #[cfg(feature = "unstable-v2")]
185 pub(crate) nested_kinds: Vec<NestedKind>,
186 #[cfg(feature = "unstable-v2")]
188 pub(crate) z_step: Option<ZStep>,
189 pub(crate) features: Vec<TileFeature>,
190}
191
192#[derive(Debug, Clone, PartialEq)]
194pub struct TileFeature {
195 pub(crate) id: Option<u64>,
196 pub(crate) geometry: Geometry<i32>,
198 pub(crate) properties: Vec<PropValue>,
201 #[cfg(feature = "unstable-v2")]
205 pub(crate) m_values: Vec<MValue>,
206 #[cfg(feature = "unstable-v2")]
208 pub(crate) nested: Vec<NestedValue>,
209 #[cfg(feature = "unstable-v2")]
211 pub(crate) z: Vec<i32>,
212}
213
214#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
215pub struct PropertyKey(usize);
216
217impl PropertyKey {
218 pub(crate) const fn new(index: usize) -> Self {
219 Self(index)
220 }
221
222 #[must_use]
223 pub fn index(self) -> usize {
224 self.0
225 }
226}
227
228#[cfg(feature = "unstable-v2")]
230#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
231pub struct MValueKey(usize);
232
233#[cfg(feature = "unstable-v2")]
234impl MValueKey {
235 #[must_use]
236 pub fn index(self) -> usize {
237 self.0
238 }
239}
240
241#[cfg(feature = "unstable-v2")]
243#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
244pub struct NestedKey(usize);
245
246#[cfg(feature = "unstable-v2")]
247impl NestedKey {
248 #[must_use]
249 pub fn index(self) -> usize {
250 self.0
251 }
252}
253
254#[cfg(feature = "unstable-v2")]
256pub(crate) const MAX_NESTED_DEPTH: usize = 8;
257
258#[cfg(feature = "unstable-v2")]
260#[derive(Debug, Clone, PartialEq, Eq)]
261pub enum NestedKind {
262 Leaf(PropKind),
263 List(Box<Self>),
264 Map(BTreeMap<String, Self>),
266}
267
268#[cfg(feature = "unstable-v2")]
269impl NestedKind {
270 #[must_use]
272 pub fn map<K: Into<String>>(fields: impl IntoIterator<Item = (K, Self)>) -> Self {
273 Self::Map(fields.into_iter().map(|(k, v)| (k.into(), v)).collect())
274 }
275
276 #[must_use]
278 pub fn list(element: Self) -> Self {
279 Self::List(Box::new(element))
280 }
281
282 #[must_use]
284 pub fn depth(&self) -> usize {
285 1 + match self {
286 Self::Leaf(_) => 0,
287 Self::List(element) => element.depth(),
288 Self::Map(fields) => fields.values().map(Self::depth).max().unwrap_or(0),
289 }
290 }
291
292 #[must_use]
294 pub fn null_value(&self) -> NestedValue {
295 match self {
296 Self::Leaf(kind) => NestedValue::Leaf(PropValue::null(*kind)),
297 Self::List(_) => NestedValue::List(None),
298 Self::Map(_) => NestedValue::Map(None),
299 }
300 }
301
302 #[must_use]
306 pub fn accepts(&self, value: &NestedValue) -> bool {
307 match (self, value) {
308 (Self::Leaf(kind), NestedValue::Leaf(value)) => value.kind() == *kind,
309 (Self::List(_), NestedValue::List(None)) | (Self::Map(_), NestedValue::Map(None)) => {
310 true
311 }
312 (Self::List(element), NestedValue::List(Some(items))) => {
313 items.iter().all(|item| element.accepts(item))
314 }
315 (Self::Map(fields), NestedValue::Map(Some(entries))) => entries
316 .iter()
317 .all(|(key, value)| fields.get(key).is_some_and(|kind| kind.accepts(value))),
318 _ => false,
319 }
320 }
321
322 fn has_empty_map(&self) -> bool {
324 match self {
325 Self::Leaf(_) => false,
326 Self::List(element) => element.has_empty_map(),
327 Self::Map(fields) => fields.is_empty() || fields.values().any(Self::has_empty_map),
328 }
329 }
330}
331
332#[cfg(feature = "unstable-v2")]
336#[derive(Debug, Clone, PartialEq)]
337pub enum NestedValue {
338 Leaf(PropValue),
339 List(Option<Vec<Self>>),
340 Map(Option<BTreeMap<String, Self>>),
341}
342
343#[cfg(feature = "unstable-v2")]
344impl NestedValue {
345 #[must_use]
347 pub fn map<K: Into<String>>(entries: impl IntoIterator<Item = (K, Self)>) -> Self {
348 Self::Map(Some(
349 entries.into_iter().map(|(k, v)| (k.into(), v)).collect(),
350 ))
351 }
352
353 #[must_use]
355 pub fn list(items: impl IntoIterator<Item = Self>) -> Self {
356 Self::List(Some(items.into_iter().collect()))
357 }
358
359 #[must_use]
361 pub fn is_null(&self) -> bool {
362 match self {
363 Self::Leaf(value) => value.is_null(),
364 Self::List(items) => items.is_none(),
365 Self::Map(entries) => entries.is_none(),
366 }
367 }
368
369 #[must_use]
371 pub(crate) fn heap_bytes(&self) -> usize {
372 match self {
373 Self::Leaf(PropValue::Str(Some(value))) => value.len(),
374 Self::Leaf(_) => size_of::<PropValue>(),
375 Self::List(items) => items.iter().flatten().map(Self::heap_bytes).sum::<usize>(),
376 Self::Map(entries) => entries
377 .iter()
378 .flatten()
379 .map(|(key, value)| key.len() + value.heap_bytes())
380 .sum(),
381 }
382 }
383
384 fn same_shape(&self, other: &Self) -> bool {
387 matches!(
388 (self, other),
389 (Self::Leaf(_), Self::Leaf(_))
390 | (Self::List(_), Self::List(_))
391 | (Self::Map(_), Self::Map(_))
392 )
393 }
394}
395
396impl TileLayer {
397 pub fn new(name: impl Into<String>, extent: u32) -> MltResult<Self> {
398 Self::with_capacity(name, extent, 0)
399 }
400
401 pub fn with_capacity(name: impl Into<String>, extent: u32, features: usize) -> MltResult<Self> {
402 let name = name.into();
403 validate_layer_name(&name)?;
404 let extent = Extent::new(extent)?;
405 Ok(Self {
406 name,
407 extent,
408 property_names: Vec::new(),
409 property_kinds: Vec::new(),
410 #[cfg(feature = "unstable-v2")]
411 m_value_names: Vec::new(),
412 #[cfg(feature = "unstable-v2")]
413 m_value_kinds: Vec::new(),
414 #[cfg(feature = "unstable-v2")]
415 nested_names: Vec::new(),
416 #[cfg(feature = "unstable-v2")]
417 nested_kinds: Vec::new(),
418 #[cfg(feature = "unstable-v2")]
419 z_step: None,
420 features: Vec::with_capacity(features),
421 })
422 }
423
424 pub(crate) fn from_parts(
425 name: impl Into<String>,
426 extent: u32,
427 property_names: Vec<String>,
428 features: Vec<TileFeature>,
429 ) -> MltResult<Self> {
430 let name = name.into();
431 validate_layer_name(&name)?;
432 let extent = Extent::new(extent)?;
433 validate_unique_names(&property_names, ColumnRole::Property)?;
434 let property_kinds = infer_kinds::<PropValue>(&property_names, &features)?;
435 let layer = Self {
436 name,
437 extent,
438 property_names,
439 property_kinds,
440 #[cfg(feature = "unstable-v2")]
441 m_value_names: Vec::new(),
442 #[cfg(feature = "unstable-v2")]
443 m_value_kinds: Vec::new(),
444 #[cfg(feature = "unstable-v2")]
445 nested_names: Vec::new(),
446 #[cfg(feature = "unstable-v2")]
447 nested_kinds: Vec::new(),
448 #[cfg(feature = "unstable-v2")]
449 z_step: None,
450 features,
451 };
452 Ok(layer)
453 }
454
455 #[must_use]
456 pub fn name(&self) -> &str {
457 &self.name
458 }
459
460 #[must_use]
461 pub fn extent(&self) -> Extent {
462 self.extent
463 }
464
465 #[must_use]
466 pub fn property_names(&self) -> &[String] {
467 &self.property_names
468 }
469
470 #[must_use]
471 pub fn property_kinds(&self) -> &[PropKind] {
472 &self.property_kinds
473 }
474
475 #[must_use]
476 pub fn features(&self) -> &[TileFeature] {
477 &self.features
478 }
479
480 #[must_use]
481 pub fn feature_count(&self) -> usize {
482 self.features.len()
483 }
484
485 fn reject_name_in_use(&self, name: &str, role: ColumnRole) -> MltResult<()> {
487 let taken = self
488 .property_names
489 .iter()
490 .map(|n| (n.as_str(), ColumnRole::Property));
491 #[cfg(feature = "unstable-v2")]
492 let taken = taken
493 .chain(
494 self.m_value_names
495 .iter()
496 .map(|n| (n.as_str(), ColumnRole::MValue)),
497 )
498 .chain(
499 self.nested_names
500 .iter()
501 .map(|n| (n.as_str(), ColumnRole::Nested)),
502 );
503 reject_taken_name(name, role, taken)
504 }
505
506 pub fn add_property(
507 &mut self,
508 name: impl Into<String>,
509 kind: PropKind,
510 ) -> MltResult<PropertyKey> {
511 let name = name.into();
512 self.reject_name_in_use(&name, ColumnRole::Property)?;
513 for feature in &mut self.features {
514 feature.properties.push(PropValue::null(kind));
515 }
516 self.property_names.push(name);
517 self.property_kinds.push(kind);
518 Ok(PropertyKey(self.property_names.len() - 1))
519 }
520
521 #[cfg(feature = "unstable-v2")]
522 #[must_use]
523 pub fn m_value_names(&self) -> &[String] {
524 &self.m_value_names
525 }
526
527 #[cfg(feature = "unstable-v2")]
528 #[must_use]
529 pub fn m_value_kinds(&self) -> &[PropKind] {
530 &self.m_value_kinds
531 }
532
533 #[cfg(feature = "unstable-v2")]
535 pub fn add_m_value(&mut self, name: impl Into<String>, kind: PropKind) -> MltResult<MValueKey> {
536 let name = name.into();
537 self.reject_name_in_use(&name, ColumnRole::MValue)?;
538 for feature in &mut self.features {
539 feature.m_values.push(MValue::null(kind));
540 }
541 self.m_value_names.push(name);
542 self.m_value_kinds.push(kind);
543 Ok(MValueKey(self.m_value_names.len() - 1))
544 }
545
546 #[cfg(feature = "unstable-v2")]
549 pub(crate) fn with_m_value_names(mut self, names: Vec<String>) -> MltResult<Self> {
550 validate_unique_names(&names, ColumnRole::MValue)?;
551 for name in &names {
552 self.reject_name_in_use(name, ColumnRole::MValue)?;
553 }
554 self.m_value_kinds = infer_kinds::<MValue>(&names, &self.features)?;
555 self.m_value_names = names;
556 for feature in &self.features {
557 self.validate_m_values(feature)?;
558 }
559 Ok(self)
560 }
561
562 #[cfg(feature = "unstable-v2")]
563 #[must_use]
564 pub fn nested_names(&self) -> &[String] {
565 &self.nested_names
566 }
567
568 #[cfg(feature = "unstable-v2")]
569 #[must_use]
570 pub fn nested_kinds(&self) -> &[NestedKind] {
571 &self.nested_kinds
572 }
573
574 #[cfg(feature = "unstable-v2")]
576 #[must_use]
577 pub fn z_step(&self) -> Option<ZStep> {
578 self.z_step
579 }
580
581 #[cfg(feature = "unstable-v2")]
585 pub fn set_z_step(&mut self, step: ZStep) -> MltResult<()> {
586 if !self.features.is_empty() {
587 return Err(MltError::ZStepOnNonEmptyLayer(self.name.clone()));
588 }
589 self.z_step = Some(step);
590 Ok(())
591 }
592
593 #[cfg(feature = "unstable-v2")]
595 pub(crate) fn with_z_step(mut self, step: Option<ZStep>) -> MltResult<Self> {
596 self.z_step = step;
597 for feature in &self.features {
598 self.validate_z(feature)?;
599 }
600 Ok(self)
601 }
602
603 #[cfg(feature = "unstable-v2")]
607 pub fn add_nested(
608 &mut self,
609 name: impl Into<String>,
610 kind: NestedKind,
611 ) -> MltResult<NestedKey> {
612 let name = name.into();
613 self.reject_name_in_use(&name, ColumnRole::Nested)?;
614 validate_nested_kind(&name, &kind)?;
615 for feature in &mut self.features {
616 feature.nested.push(kind.null_value());
617 }
618 self.nested_names.push(name);
619 self.nested_kinds.push(kind);
620 Ok(NestedKey(self.nested_names.len() - 1))
621 }
622
623 #[cfg(feature = "unstable-v2")]
626 pub(crate) fn with_nested(
627 mut self,
628 names: Vec<String>,
629 kinds: Vec<NestedKind>,
630 ) -> MltResult<Self> {
631 validate_unique_names(&names, ColumnRole::Nested)?;
632 for name in &names {
633 self.reject_name_in_use(name, ColumnRole::Nested)?;
634 }
635 for (name, kind) in names.iter().zip(&kinds) {
636 validate_nested_kind(name, kind)?;
637 }
638 self.nested_names = names;
639 self.nested_kinds = kinds;
640 for feature in &self.features {
641 self.validate_nested(feature)?;
642 }
643 Ok(self)
644 }
645
646 pub fn push_feature(&mut self, feature: TileFeature) -> MltResult<()> {
647 self.validate_feature(&feature)?;
648 self.features.push(feature);
649 Ok(())
650 }
651
652 pub fn builder(name: impl Into<String>, extent: u32) -> MltResult<TileLayerBuilder> {
653 Ok(TileLayerBuilder {
654 layer: Self::new(name, extent)?,
655 })
656 }
657
658 #[must_use]
660 pub fn builder_like(&self) -> TileLayerBuilder {
661 let layer = Self {
662 name: self.name.clone(),
663 extent: self.extent,
664 property_names: Vec::new(),
665 property_kinds: Vec::new(),
666 #[cfg(feature = "unstable-v2")]
667 m_value_names: Vec::new(),
668 #[cfg(feature = "unstable-v2")]
669 m_value_kinds: Vec::new(),
670 #[cfg(feature = "unstable-v2")]
671 nested_names: Vec::new(),
672 #[cfg(feature = "unstable-v2")]
673 nested_kinds: Vec::new(),
674 #[cfg(feature = "unstable-v2")]
675 z_step: self.z_step,
676 features: Vec::new(),
677 };
678 TileLayerBuilder { layer }
679 }
680
681 fn validate_feature(&self, feature: &TileFeature) -> MltResult<()> {
682 validate_kinds::<PropValue>(&self.property_kinds, feature)?;
683 #[cfg(feature = "unstable-v2")]
684 self.validate_m_values(feature)?;
685 #[cfg(feature = "unstable-v2")]
686 self.validate_nested(feature)?;
687 #[cfg(feature = "unstable-v2")]
688 self.validate_z(feature)?;
689 Ok(())
690 }
691
692 #[cfg(feature = "unstable-v2")]
694 fn validate_z(&self, feature: &TileFeature) -> MltResult<()> {
695 let expected = if self.z_step.is_some() {
696 feature.vertex_count()
697 } else {
698 0
699 };
700 if feature.z.len() == expected {
701 Ok(())
702 } else {
703 Err(MltError::ZVertexCountMismatch {
704 expected,
705 actual: feature.z.len(),
706 })
707 }
708 }
709
710 #[cfg(feature = "unstable-v2")]
713 fn validate_nested(&self, feature: &TileFeature) -> MltResult<()> {
714 if feature.nested.len() != self.nested_kinds.len() {
715 return Err(MltError::NestedColumnCountMismatch {
716 expected: self.nested_kinds.len(),
717 actual: feature.nested.len(),
718 });
719 }
720 for (index, (value, kind)) in feature.nested.iter().zip(&self.nested_kinds).enumerate() {
721 if !kind.accepts(value) {
722 return Err(MltError::NestedValueMismatch {
723 index,
724 name: self.nested_names[index].clone(),
725 });
726 }
727 }
728 Ok(())
729 }
730
731 #[cfg(feature = "unstable-v2")]
734 fn validate_m_values(&self, feature: &TileFeature) -> MltResult<()> {
735 validate_kinds::<MValue>(&self.m_value_kinds, feature)?;
736 let vertices = feature.vertex_count();
737 for (index, m_value) in feature.m_values.iter().enumerate() {
738 if let Some(len) = m_value.count()
739 && len != vertices
740 {
741 return Err(MltError::MValueVertexCountMismatch {
742 index,
743 expected: vertices,
744 actual: len,
745 });
746 }
747 }
748 Ok(())
749 }
750}
751
752impl TileFeature {
753 #[must_use]
754 pub fn new(geometry: Geometry<i32>) -> Self {
755 Self {
756 id: None,
757 geometry,
758 properties: Vec::new(),
759 #[cfg(feature = "unstable-v2")]
760 m_values: Vec::new(),
761 #[cfg(feature = "unstable-v2")]
762 nested: Vec::new(),
763 #[cfg(feature = "unstable-v2")]
764 z: Vec::new(),
765 }
766 }
767
768 #[must_use]
769 pub fn with_id(geometry: Geometry<i32>, id: u64) -> Self {
770 Self {
771 id: Some(id),
772 geometry,
773 properties: Vec::new(),
774 #[cfg(feature = "unstable-v2")]
775 m_values: Vec::new(),
776 #[cfg(feature = "unstable-v2")]
777 nested: Vec::new(),
778 #[cfg(feature = "unstable-v2")]
779 z: Vec::new(),
780 }
781 }
782
783 #[must_use]
784 pub fn id(&self) -> Option<u64> {
785 self.id
786 }
787
788 #[must_use]
789 pub fn geometry(&self) -> &Geometry<i32> {
790 &self.geometry
791 }
792
793 #[must_use]
794 pub fn properties(&self) -> &[PropValue] {
795 &self.properties
796 }
797
798 #[cfg(feature = "unstable-v2")]
803 #[must_use]
804 pub fn vertex_count(&self) -> usize {
805 stored_vertex_count(&self.geometry)
806 }
807
808 #[cfg(feature = "unstable-v2")]
809 #[must_use]
810 pub fn m_values(&self) -> &[MValue] {
811 &self.m_values
812 }
813
814 #[cfg(feature = "unstable-v2")]
816 #[must_use]
817 pub fn z(&self) -> &[i32] {
818 &self.z
819 }
820
821 #[cfg(feature = "unstable-v2")]
823 pub fn set_z(&mut self, z: Vec<i32>) -> MltResult<()> {
824 let expected = self.vertex_count();
825 if z.len() != expected {
826 return Err(MltError::ZVertexCountMismatch {
827 expected,
828 actual: z.len(),
829 });
830 }
831 self.z = z;
832 Ok(())
833 }
834
835 #[cfg(feature = "unstable-v2")]
838 pub fn set_m_value(&mut self, key: MValueKey, value: MValue) -> MltResult<()> {
839 let vertices = self.vertex_count();
840 let Some(slot) = self.m_values.get_mut(key.index()) else {
841 return Err(MltError::UnknownMValueKey {
842 index: key.index(),
843 columns: self.m_values.len(),
844 });
845 };
846 let expected = slot.kind();
847 let actual = value.kind();
848 if actual != expected {
849 return Err(MltError::MValueKindMismatch {
850 index: key.index(),
851 expected,
852 actual,
853 });
854 }
855 if let Some(len) = value.count()
856 && len != vertices
857 {
858 return Err(MltError::MValueVertexCountMismatch {
859 index: key.index(),
860 expected: vertices,
861 actual: len,
862 });
863 }
864 *slot = value;
865 Ok(())
866 }
867
868 #[cfg(feature = "unstable-v2")]
869 #[must_use]
870 pub fn nested(&self) -> &[NestedValue] {
871 &self.nested
872 }
873
874 #[cfg(feature = "unstable-v2")]
880 pub fn set_nested(&mut self, key: NestedKey, value: NestedValue) -> MltResult<()> {
881 let columns = self.nested.len();
882 let Some(slot) = self.nested.get_mut(key.index()) else {
883 return Err(MltError::UnknownNestedKey {
884 index: key.index(),
885 columns,
886 });
887 };
888 if !slot.same_shape(&value) {
889 return Err(MltError::NestedShapeMismatch { index: key.index() });
890 }
891 *slot = value;
892 Ok(())
893 }
894
895 pub fn set_property(&mut self, key: PropertyKey, value: PropValue) -> MltResult<()> {
896 let Some(prop) = self.properties.get_mut(key.index()) else {
897 return Err(MltError::UnknownProperty {
898 index: key.index(),
899 count: self.properties.len(),
900 });
901 };
902 let expected = PropKind::from(&*prop);
903 let actual = PropKind::from(&value);
904 if actual != expected {
905 return Err(MltError::PropertyKindMismatch {
906 index: key.index(),
907 expected,
908 actual,
909 });
910 }
911 *prop = value;
912 Ok(())
913 }
914}
915
916pub struct TileLayerBuilder {
917 layer: TileLayer,
918}
919
920impl TileLayerBuilder {
921 pub fn add_property(
922 &mut self,
923 name: impl Into<String>,
924 kind: PropKind,
925 ) -> MltResult<PropertyKey> {
926 self.layer.add_property(name, kind)
927 }
928
929 #[cfg(feature = "unstable-v2")]
930 pub fn add_m_value(&mut self, name: impl Into<String>, kind: PropKind) -> MltResult<MValueKey> {
931 self.layer.add_m_value(name, kind)
932 }
933
934 #[cfg(feature = "unstable-v2")]
935 pub fn add_nested(
936 &mut self,
937 name: impl Into<String>,
938 kind: NestedKind,
939 ) -> MltResult<NestedKey> {
940 self.layer.add_nested(name, kind)
941 }
942
943 #[cfg(feature = "unstable-v2")]
944 pub fn set_z_step(&mut self, step: ZStep) -> MltResult<()> {
945 self.layer.set_z_step(step)
946 }
947
948 pub fn feature(&mut self, geometry: Geometry<i32>) -> TileFeatureBuilder<'_> {
949 let properties = self
950 .layer
951 .property_kinds
952 .iter()
953 .copied()
954 .map(PropValue::null)
955 .collect();
956 #[cfg(feature = "unstable-v2")]
957 let m_values = self
958 .layer
959 .m_value_kinds
960 .iter()
961 .copied()
962 .map(MValue::null)
963 .collect();
964 #[cfg(feature = "unstable-v2")]
965 let nested = self
966 .layer
967 .nested_kinds
968 .iter()
969 .map(NestedKind::null_value)
970 .collect();
971 TileFeatureBuilder {
972 layer: self,
973 feature: TileFeature {
974 id: None,
975 geometry,
976 properties,
977 #[cfg(feature = "unstable-v2")]
978 m_values,
979 #[cfg(feature = "unstable-v2")]
980 nested,
981 #[cfg(feature = "unstable-v2")]
982 z: Vec::new(),
983 },
984 }
985 }
986
987 pub fn add_properties_like(&mut self, layer: &TileLayer) -> MltResult<Vec<PropertyKey>> {
989 (layer.property_names.iter().zip(&layer.property_kinds))
990 .map(|(name, &kind)| self.add_property(name, kind))
991 .collect()
992 }
993
994 #[cfg(feature = "unstable-v2")]
996 pub fn add_m_values_like(&mut self, layer: &TileLayer) -> MltResult<Vec<MValueKey>> {
997 (layer.m_value_names.iter().zip(&layer.m_value_kinds))
998 .map(|(name, &kind)| self.add_m_value(name, kind))
999 .collect()
1000 }
1001
1002 #[cfg(feature = "unstable-v2")]
1004 pub fn add_nested_like(&mut self, layer: &TileLayer) -> MltResult<Vec<NestedKey>> {
1005 (layer.nested_names.iter().zip(&layer.nested_kinds))
1006 .map(|(name, kind)| self.add_nested(name, kind.clone()))
1007 .collect()
1008 }
1009
1010 pub fn feature_like(&mut self, feature: &TileFeature) -> TileFeatureBuilder<'_> {
1012 let mut row = self.feature(feature.geometry.clone());
1013 row.feature.id = feature.id;
1014 #[cfg(feature = "unstable-v2")]
1015 row.feature.z.clone_from(&feature.z);
1016 row
1017 }
1018
1019 pub fn push_feature(&mut self, feature: TileFeature) -> MltResult<()> {
1020 self.layer.push_feature(feature)
1021 }
1022
1023 #[must_use]
1024 pub fn finish(self) -> TileLayer {
1025 self.layer
1026 }
1027}
1028
1029pub struct TileFeatureBuilder<'a> {
1030 layer: &'a mut TileLayerBuilder,
1031 feature: TileFeature,
1032}
1033
1034impl TileFeatureBuilder<'_> {
1035 pub fn id(&mut self, id: Option<u64>) -> &mut Self {
1036 self.feature.id = id;
1037 self
1038 }
1039
1040 pub fn property(&mut self, key: PropertyKey, value: PropValue) -> MltResult<&mut Self> {
1041 self.feature.set_property(key, value)?;
1042 Ok(self)
1043 }
1044
1045 #[cfg(feature = "unstable-v2")]
1046 pub fn m_value(&mut self, key: MValueKey, value: MValue) -> MltResult<&mut Self> {
1047 self.feature.set_m_value(key, value)?;
1048 Ok(self)
1049 }
1050
1051 #[cfg(feature = "unstable-v2")]
1052 pub fn nested(&mut self, key: NestedKey, value: NestedValue) -> MltResult<&mut Self> {
1053 self.feature.set_nested(key, value)?;
1054 Ok(self)
1055 }
1056
1057 #[cfg(feature = "unstable-v2")]
1058 pub fn z(&mut self, z: Vec<i32>) -> MltResult<&mut Self> {
1059 self.feature.set_z(z)?;
1060 Ok(self)
1061 }
1062
1063 pub fn finish(self) -> MltResult<()> {
1064 self.layer.push_feature(self.feature)
1065 }
1066}
1067
1068#[derive(Debug, Clone, PartialEq)]
1076pub enum PropValue {
1077 Bool(Option<bool>),
1078 I8(Option<i8>),
1079 U8(Option<u8>),
1080 I32(Option<i32>),
1081 U32(Option<u32>),
1082 I64(Option<i64>),
1083 U64(Option<u64>),
1084 F32(Option<f32>),
1085 F64(Option<f64>),
1086 Str(Option<String>),
1087}
1088
1089impl PropValue {
1090 #[must_use]
1091 pub fn kind(&self) -> PropKind {
1092 self.into()
1093 }
1094}
1095
1096#[cfg(feature = "unstable-v2")]
1101#[derive(Debug, Clone, PartialEq)]
1102pub enum MValue {
1103 Bool(Option<Vec<bool>>),
1104 I8(Option<Vec<i8>>),
1105 U8(Option<Vec<u8>>),
1106 I32(Option<Vec<i32>>),
1107 U32(Option<Vec<u32>>),
1108 I64(Option<Vec<i64>>),
1109 U64(Option<Vec<u64>>),
1110 F32(Option<Vec<f32>>),
1111 F64(Option<Vec<f64>>),
1112 Str(Option<Vec<String>>),
1113}
1114
1115#[cfg(feature = "unstable-v2")]
1116impl MValue {
1117 #[must_use]
1118 pub fn is_null(&self) -> bool {
1119 self.count().is_none()
1120 }
1121}
1122
1123#[derive(Debug, Clone, Copy, PartialEq)]
1128pub enum PropValueRef<'a> {
1129 Bool(bool),
1130 I8(i8),
1131 U8(u8),
1132 I32(i32),
1133 U32(u32),
1134 I64(i64),
1135 U64(u64),
1136 F32(f32),
1137 F64(f64),
1138 Str(&'a str),
1139}
1140
1141impl From<bool> for PropValueRef<'_> {
1142 fn from(v: bool) -> Self {
1143 Self::Bool(v)
1144 }
1145}
1146impl From<i8> for PropValueRef<'_> {
1147 fn from(v: i8) -> Self {
1148 Self::I8(v)
1149 }
1150}
1151impl From<u8> for PropValueRef<'_> {
1152 fn from(v: u8) -> Self {
1153 Self::U8(v)
1154 }
1155}
1156impl From<i32> for PropValueRef<'_> {
1157 fn from(v: i32) -> Self {
1158 Self::I32(v)
1159 }
1160}
1161impl From<u32> for PropValueRef<'_> {
1162 fn from(v: u32) -> Self {
1163 Self::U32(v)
1164 }
1165}
1166impl From<i64> for PropValueRef<'_> {
1167 fn from(v: i64) -> Self {
1168 Self::I64(v)
1169 }
1170}
1171impl From<u64> for PropValueRef<'_> {
1172 fn from(v: u64) -> Self {
1173 Self::U64(v)
1174 }
1175}
1176impl From<f32> for PropValueRef<'_> {
1177 fn from(v: f32) -> Self {
1178 Self::F32(v)
1179 }
1180}
1181impl From<f64> for PropValueRef<'_> {
1182 fn from(v: f64) -> Self {
1183 Self::F64(v)
1184 }
1185}
1186
1187impl<'a> From<&'a str> for PropValueRef<'a> {
1188 fn from(v: &'a str) -> Self {
1189 Self::Str(v)
1190 }
1191}
1192
1193macro_rules! kind_mappings {
1194 (
1195 scalar { $($sv:ident),* $(,)? }
1196 string { $($gv:ident),* $(,)? }
1197 ) => {
1198 #[derive(Debug, Clone, Copy, PartialEq, Eq, strum::IntoStaticStr)]
1200 #[strum(serialize_all = "lowercase")]
1201 pub enum PropKind {
1202 $($sv,)*
1203 $($gv,)*
1204 }
1205
1206 impl From<&PropValue> for PropKind {
1207 fn from(prop: &PropValue) -> Self {
1208 match prop {
1209 $(PropValue::$sv(_) => Self::$sv,)*
1210 $(PropValue::$gv(_) => Self::$gv,)*
1211 }
1212 }
1213 }
1214
1215 impl PropValue {
1216 #[must_use]
1217 pub fn is_null(&self) -> bool {
1218 match self {
1219 $(Self::$sv(v) => v.is_none(),)*
1220 $(Self::$gv(v) => v.is_none(),)*
1221 }
1222 }
1223
1224 #[must_use]
1226 pub fn null(kind: PropKind) -> Self {
1227 match kind {
1228 $(PropKind::$sv => Self::$sv(None),)*
1229 $(PropKind::$gv => Self::$gv(None),)*
1230 }
1231 }
1232
1233 #[inline]
1235 #[must_use]
1236 pub fn value_ref(&self) -> Option<PropValueRef<'_>> {
1237 match self {
1238 $(Self::$sv(v) => v.map(PropValueRef::$sv),)*
1239 $(Self::$gv(v) => v.as_deref().map(PropValueRef::$gv),)*
1240 }
1241 }
1242 }
1243
1244 impl PropValueRef<'_> {
1245 #[inline]
1247 #[must_use]
1248 pub fn kind(self) -> PropKind {
1249 match self {
1250 $(Self::$sv(_) => PropKind::$sv,)*
1251 $(Self::$gv(_) => PropKind::$gv,)*
1252 }
1253 }
1254 }
1255
1256 impl From<PropValueRef<'_>> for PropValue {
1257 fn from(value: PropValueRef<'_>) -> Self {
1258 match value {
1259 $(PropValueRef::$sv(v) => Self::$sv(Some(v)),)*
1260 $(PropValueRef::$gv(v) => Self::$gv(Some(v.to_owned())),)*
1261 }
1262 }
1263 }
1264
1265 #[cfg(feature = "unstable-v2")]
1266 impl MValue {
1267 #[must_use]
1268 pub fn kind(&self) -> PropKind {
1269 match self {
1270 $(Self::$sv(_) => PropKind::$sv,)*
1271 $(Self::$gv(_) => PropKind::$gv,)*
1272 }
1273 }
1274
1275 #[must_use]
1277 pub fn count(&self) -> Option<usize> {
1278 match self {
1279 $(Self::$sv(v) => v.as_ref().map(Vec::len),)*
1280 $(Self::$gv(v) => v.as_ref().map(Vec::len),)*
1281 }
1282 }
1283
1284 #[must_use]
1286 pub fn null(kind: PropKind) -> Self {
1287 match kind {
1288 $(PropKind::$sv => Self::$sv(None),)*
1289 $(PropKind::$gv => Self::$gv(None),)*
1290 }
1291 }
1292 }
1293 };
1294}
1295
1296with_kinds!(kind_mappings);
1297
1298impl PropKind {
1299 fn is_signed_int(self) -> bool {
1300 matches!(self, Self::I8 | Self::I32 | Self::I64)
1301 }
1302
1303 fn is_unsigned_int(self) -> bool {
1304 matches!(self, Self::U8 | Self::U32 | Self::U64)
1305 }
1306
1307 fn is_float(self) -> bool {
1308 matches!(self, Self::F32 | Self::F64)
1309 }
1310
1311 #[must_use]
1317 pub fn merge(self, other: Self) -> Self {
1318 let int = |k: Self| k.is_signed_int() || k.is_unsigned_int();
1319 if self == other {
1320 self
1321 } else if int(self) && int(other) {
1322 if self.is_unsigned_int() && other.is_unsigned_int() {
1323 Self::U64
1324 } else {
1325 Self::I64
1326 }
1327 } else if (self.is_float() || int(self)) && (other.is_float() || int(other)) {
1328 Self::F64
1329 } else {
1330 Self::Str
1331 }
1332 }
1333}
1334
1335impl PropValueRef<'_> {
1336 fn as_int(self) -> Option<i128> {
1337 match self {
1338 Self::I8(v) => Some(i128::from(v)),
1339 Self::U8(v) => Some(i128::from(v)),
1340 Self::I32(v) => Some(i128::from(v)),
1341 Self::U32(v) => Some(i128::from(v)),
1342 Self::I64(v) => Some(i128::from(v)),
1343 Self::U64(v) => Some(i128::from(v)),
1344 Self::Bool(_) | Self::F32(_) | Self::F64(_) | Self::Str(_) => None,
1345 }
1346 }
1347
1348 #[must_use]
1354 pub fn coerce(self, kind: PropKind) -> PropValue {
1355 if self.kind() == kind {
1356 return self.into();
1357 }
1358 let int = self.as_int();
1359 #[expect(
1360 clippy::cast_precision_loss,
1361 reason = "integers in a float column are taken as floats"
1362 )]
1363 match kind {
1364 PropKind::I8 => PropValue::I8(int.and_then(|v| i8::try_from(v).ok())),
1365 PropKind::U8 => PropValue::U8(int.and_then(|v| u8::try_from(v).ok())),
1366 PropKind::I32 => PropValue::I32(int.and_then(|v| i32::try_from(v).ok())),
1367 PropKind::U32 => PropValue::U32(int.and_then(|v| u32::try_from(v).ok())),
1368 PropKind::I64 => PropValue::I64(int.and_then(|v| i64::try_from(v).ok())),
1369 PropKind::U64 => PropValue::U64(int.and_then(|v| u64::try_from(v).ok())),
1370 PropKind::F64 => PropValue::F64(match self {
1371 Self::F32(v) => Some(f64::from(v)),
1372 Self::F64(v) => Some(v),
1373 Self::Bool(_) | Self::Str(_) => None,
1374 Self::I8(_)
1375 | Self::U8(_)
1376 | Self::I32(_)
1377 | Self::U32(_)
1378 | Self::I64(_)
1379 | Self::U64(_) => int.map(|v| v as f64),
1380 }),
1381 PropKind::Str => PropValue::Str(Some(match self {
1382 Self::Bool(v) => v.to_string(),
1383 Self::I8(v) => v.to_string(),
1384 Self::U8(v) => v.to_string(),
1385 Self::I32(v) => v.to_string(),
1386 Self::U32(v) => v.to_string(),
1387 Self::I64(v) => v.to_string(),
1388 Self::U64(v) => v.to_string(),
1389 Self::F32(v) => v.to_string(),
1390 Self::F64(v) => v.to_string(),
1391 Self::Str(v) => v.to_string(),
1392 })),
1393 PropKind::Bool | PropKind::F32 => PropValue::null(kind),
1394 }
1395 }
1396}
1397
1398pub(crate) fn stored_ring_len(ring: &LineString<i32>) -> usize {
1400 stored_len(&ring.0)
1401}
1402
1403pub(crate) fn stored_len(coords: &[Coord<i32>]) -> usize {
1405 if coords.len() > 1 && coords.last() == coords.first() {
1406 coords.len() - 1
1407 } else {
1408 coords.len()
1409 }
1410}
1411
1412#[cfg(feature = "unstable-v2")]
1415pub(crate) fn stored_vertex_count(geom: &Geometry<i32>) -> usize {
1416 let polygon = |poly: &geo_types::Polygon<i32>| {
1417 std::iter::once(poly.exterior())
1418 .chain(poly.interiors())
1419 .map(stored_ring_len)
1420 .sum::<usize>()
1421 };
1422 match geom {
1423 Geometry::Point(_) => 1,
1424 Geometry::Line(_) => 2,
1425 Geometry::LineString(ls) => ls.0.len(),
1426 Geometry::Polygon(p) => polygon(p),
1427 Geometry::MultiPoint(mp) => mp.0.len(),
1428 Geometry::MultiLineString(mls) => mls.iter().map(|ls| ls.0.len()).sum(),
1429 Geometry::MultiPolygon(mp) => mp.iter().map(polygon).sum(),
1430 Geometry::Triangle(t) => polygon(&t.to_polygon()),
1431 Geometry::Rect(r) => polygon(&r.to_polygon()),
1432 Geometry::GeometryCollection(gc) => gc.iter().map(stored_vertex_count).sum(),
1433 }
1434}
1435
1436#[cfg(feature = "unstable-v2")]
1439fn validate_nested_kind(name: &str, kind: &NestedKind) -> MltResult<()> {
1440 if matches!(kind, NestedKind::Leaf(_)) {
1441 return Err(MltError::NestedRootIsLeaf(name.to_string()));
1442 }
1443 if kind.has_empty_map() {
1444 return Err(MltError::EmptyStructNode);
1445 }
1446 let depth = kind.depth();
1447 if depth > MAX_NESTED_DEPTH {
1448 return Err(MltError::NestedTooDeep(depth));
1449 }
1450 Ok(())
1451}
1452
1453pub(crate) fn validate_layer_name(name: &str) -> MltResult<()> {
1454 if name.is_empty() {
1455 Err(MltError::MissingLayerName)
1456 } else {
1457 Ok(())
1458 }
1459}
1460
1461pub(crate) fn reject_taken_name<'a>(
1465 name: &str,
1466 role: ColumnRole,
1467 taken: impl IntoIterator<Item = (&'a str, ColumnRole)>,
1468) -> MltResult<()> {
1469 for (seen, taken_by) in taken {
1470 if seen == name {
1471 return Err(MltError::DuplicateColumnName {
1472 name: name.to_string(),
1473 role,
1474 taken_by,
1475 });
1476 }
1477 }
1478 Ok(())
1479}
1480
1481fn validate_unique_names(names: &[String], role: ColumnRole) -> MltResult<()> {
1483 for (i, name) in names.iter().enumerate() {
1486 reject_taken_name(name, role, names[..i].iter().map(|n| (n.as_str(), role)))?;
1487 }
1488 Ok(())
1489}
1490
1491trait FeatureColumn: Sized {
1493 fn of(feature: &TileFeature) -> &[Self];
1495 fn column_kind(&self) -> PropKind;
1496 fn count_mismatch(expected: usize, actual: usize) -> MltError;
1498 fn kind_mismatch(index: usize, expected: PropKind, actual: PropKind) -> MltError;
1500}
1501
1502impl FeatureColumn for PropValue {
1503 fn of(feature: &TileFeature) -> &[Self] {
1504 &feature.properties
1505 }
1506
1507 fn column_kind(&self) -> PropKind {
1508 PropKind::from(self)
1509 }
1510
1511 fn count_mismatch(expected: usize, actual: usize) -> MltError {
1512 MltError::PropertyLengthMismatch { expected, actual }
1513 }
1514
1515 fn kind_mismatch(index: usize, expected: PropKind, actual: PropKind) -> MltError {
1516 MltError::PropertyKindMismatch {
1517 index,
1518 expected,
1519 actual,
1520 }
1521 }
1522}
1523
1524#[cfg(feature = "unstable-v2")]
1525impl FeatureColumn for MValue {
1526 fn of(feature: &TileFeature) -> &[Self] {
1527 &feature.m_values
1528 }
1529
1530 fn column_kind(&self) -> PropKind {
1531 self.kind()
1532 }
1533
1534 fn count_mismatch(expected: usize, actual: usize) -> MltError {
1535 MltError::MValueColumnCountMismatch { expected, actual }
1536 }
1537
1538 fn kind_mismatch(index: usize, expected: PropKind, actual: PropKind) -> MltError {
1539 MltError::MValueKindMismatch {
1540 index,
1541 expected,
1542 actual,
1543 }
1544 }
1545}
1546
1547fn infer_kinds<T: FeatureColumn>(
1551 names: &[String],
1552 features: &[TileFeature],
1553) -> MltResult<Vec<PropKind>> {
1554 let mut kinds = vec![None; names.len()];
1555 for feature in features {
1556 let values = T::of(feature);
1557 if values.len() != names.len() {
1558 return Err(T::count_mismatch(names.len(), values.len()));
1559 }
1560 for (index, value) in values.iter().enumerate() {
1561 let actual = value.column_kind();
1562 match kinds[index] {
1563 Some(expected) if expected != actual => {
1564 return Err(T::kind_mismatch(index, expected, actual));
1565 }
1566 None => kinds[index] = Some(actual),
1567 _ => {}
1568 }
1569 }
1570 }
1571 Ok(kinds
1572 .into_iter()
1573 .map(|kind| kind.unwrap_or(PropKind::Str))
1574 .collect())
1575}
1576
1577fn validate_kinds<T: FeatureColumn>(kinds: &[PropKind], feature: &TileFeature) -> MltResult<()> {
1579 let values = T::of(feature);
1580 if values.len() != kinds.len() {
1581 return Err(T::count_mismatch(kinds.len(), values.len()));
1582 }
1583 for (index, (value, expected)) in values.iter().zip(kinds).enumerate() {
1584 let actual = value.column_kind();
1585 if actual != *expected {
1586 return Err(T::kind_mismatch(index, *expected, actual));
1587 }
1588 }
1589 Ok(())
1590}
1591
1592#[cfg(test)]
1593mod tests {
1594 use geo_types::{Geometry, Point};
1595 use rstest::rstest;
1596
1597 use super::*;
1598
1599 #[rstest]
1600 #[case::same_kind(PropKind::I32, PropKind::I32, PropKind::I32)]
1601 #[case::signed_widths(PropKind::I8, PropKind::I32, PropKind::I64)]
1602 #[case::unsigned_widths(PropKind::U8, PropKind::U32, PropKind::U64)]
1603 #[case::signed_with_unsigned(PropKind::I64, PropKind::U64, PropKind::I64)]
1604 #[case::small_signed_with_unsigned(PropKind::I8, PropKind::U8, PropKind::I64)]
1605 #[case::floats(PropKind::F32, PropKind::F64, PropKind::F64)]
1606 #[case::int_with_float(PropKind::U8, PropKind::F32, PropKind::F64)]
1607 #[case::bool_with_int(PropKind::Bool, PropKind::I64, PropKind::Str)]
1608 #[case::str_with_float(PropKind::Str, PropKind::F64, PropKind::Str)]
1609 fn merge_kinds(#[case] a: PropKind, #[case] b: PropKind, #[case] expected: PropKind) {
1610 assert_eq!(a.merge(b), expected);
1611 assert_eq!(b.merge(a), expected);
1612 }
1613
1614 #[rstest]
1615 #[case::same_kind(PropValueRef::I32(-3), PropKind::I32, PropValue::I32(Some(-3)))]
1616 #[case::widen_signed(PropValueRef::I8(-3), PropKind::I64, PropValue::I64(Some(-3)))]
1617 #[case::unsigned_into_signed(PropValueRef::U64(7), PropKind::I64, PropValue::I64(Some(7)))]
1618 #[case::u64_above_i64_max(PropValueRef::U64(u64::MAX), PropKind::I64, PropValue::I64(None))]
1619 #[case::negative_into_u64(PropValueRef::I64(-1), PropKind::U64, PropValue::U64(None))]
1620 #[case::narrowing_out_of_range(PropValueRef::I32(300), PropKind::U8, PropValue::U8(None))]
1621 #[case::f32_into_f64(PropValueRef::F32(0.5), PropKind::F64, PropValue::F64(Some(0.5)))]
1622 #[case::int_into_f64(PropValueRef::I64(-2), PropKind::F64, PropValue::F64(Some(-2.0)))]
1623 #[case::u64_into_f64(PropValueRef::U64(1 << 63), PropKind::F64, PropValue::F64(Some(9.223_372_036_854_776e18)))]
1624 #[case::bool_into_str(PropValueRef::Bool(true), PropKind::Str, PropValue::Str(Some("true".into())))]
1625 #[case::int_into_str(PropValueRef::I64(-12), PropKind::Str, PropValue::Str(Some("-12".into())))]
1626 #[case::f32_into_str(PropValueRef::F32(1.5), PropKind::Str, PropValue::Str(Some("1.5".into())))]
1627 #[case::str_into_str(PropValueRef::Str("a"), PropKind::Str, PropValue::Str(Some("a".into())))]
1628 #[case::str_into_int(PropValueRef::Str("1"), PropKind::I64, PropValue::I64(None))]
1629 #[case::f64_into_f32(PropValueRef::F64(1.0), PropKind::F32, PropValue::F32(None))]
1630 fn coerce_to_kind(
1631 #[case] value: PropValueRef<'_>,
1632 #[case] kind: PropKind,
1633 #[case] expected: PropValue,
1634 ) {
1635 assert_eq!(value.coerce(kind), expected);
1636 }
1637
1638 #[rstest]
1639 #[case::bool(PropValueRef::Bool(true), PropKind::Bool, PropValue::Bool(Some(true)))]
1640 #[case::u8(PropValueRef::U8(1), PropKind::U8, PropValue::U8(Some(1)))]
1641 #[case::str(PropValueRef::Str("x"), PropKind::Str, PropValue::Str(Some("x".into())))]
1642 fn ref_into_owned_keeps_kind(
1643 #[case] value: PropValueRef<'_>,
1644 #[case] kind: PropKind,
1645 #[case] expected: PropValue,
1646 ) {
1647 assert_eq!(value.kind(), kind);
1648 assert_eq!(PropValue::from(value), expected);
1649 }
1650
1651 fn point_feature(properties: Vec<PropValue>) -> TileFeature {
1652 TileFeature {
1653 id: None,
1654 geometry: Geometry::Point(Point::new(0, 0)),
1655 properties,
1656 #[cfg(feature = "unstable-v2")]
1657 m_values: Vec::new(),
1658 #[cfg(feature = "unstable-v2")]
1659 nested: Vec::new(),
1660 #[cfg(feature = "unstable-v2")]
1661 z: Vec::new(),
1662 }
1663 }
1664
1665 #[test]
1666 fn tile_layer_constructor_rejects_empty_name() {
1667 assert!(matches!(
1668 TileLayer::new("", 4096),
1669 Err(MltError::MissingLayerName)
1670 ));
1671 }
1672
1673 #[test]
1674 fn tile_layer_constructor_rejects_zero_extent() {
1675 assert!(matches!(
1676 TileLayer::new("layer", 0),
1677 Err(MltError::InvalidExtent(0))
1678 ));
1679 }
1680
1681 #[test]
1682 fn add_property_rejects_duplicate_names() {
1683 let mut layer = TileLayer::new("layer", 4096).unwrap();
1684 layer.add_property("name", PropKind::Str).unwrap();
1685 assert_eq!(
1686 layer
1687 .add_property("name", PropKind::Str)
1688 .unwrap_err()
1689 .to_string(),
1690 "duplicate column name name: the property column repeats the property column"
1691 );
1692 }
1693
1694 #[test]
1695 fn from_parts_allows_empty_property_name() {
1696 assert!(TileLayer::from_parts("layer", 4096, vec![String::new()], vec![]).is_ok());
1698 }
1699
1700 #[test]
1701 fn from_parts_rejects_duplicate_property_name() {
1702 assert_eq!(
1703 TileLayer::from_parts("layer", 4096, vec!["dup".into(), "dup".into()], vec![])
1704 .unwrap_err()
1705 .to_string(),
1706 "duplicate column name dup: the property column repeats the property column"
1707 );
1708 }
1709
1710 #[test]
1711 fn push_feature_validates_property_count() {
1712 let mut layer = TileLayer::new("layer", 4096).unwrap();
1713 layer.add_property("name", PropKind::Str).unwrap();
1714 assert!(matches!(
1715 layer.push_feature(point_feature(vec![])),
1716 Err(MltError::PropertyLengthMismatch {
1717 expected: 1,
1718 actual: 0
1719 })
1720 ));
1721 }
1722
1723 #[test]
1724 fn push_feature_validates_property_kind() {
1725 let mut layer = TileLayer::new("layer", 4096).unwrap();
1726 layer.add_property("flag", PropKind::Bool).unwrap();
1727 layer
1728 .push_feature(point_feature(vec![PropValue::Bool(Some(true))]))
1729 .unwrap();
1730 assert!(matches!(
1731 layer.push_feature(point_feature(vec![PropValue::I32(Some(1))])),
1732 Err(MltError::PropertyKindMismatch {
1733 index: 0,
1734 expected: PropKind::Bool,
1735 actual: PropKind::I32,
1736 })
1737 ));
1738 }
1739
1740 #[test]
1741 fn declared_property_kind_is_enforced_for_first_feature() {
1742 let mut layer = TileLayer::new("layer", 4096).unwrap();
1743 layer.add_property("flag", PropKind::Bool).unwrap();
1744 assert!(matches!(
1745 layer.push_feature(point_feature(vec![PropValue::I32(Some(1))])),
1746 Err(MltError::PropertyKindMismatch {
1747 index: 0,
1748 expected: PropKind::Bool,
1749 actual: PropKind::I32,
1750 })
1751 ));
1752 }
1753
1754 #[cfg(feature = "unstable-v2")]
1755 #[test]
1756 fn push_feature_validates_m_value_count() {
1757 let mut layer = TileLayer::new("layer", 4096).unwrap();
1758 layer.add_m_value("m", PropKind::I32).unwrap();
1759 assert!(matches!(
1760 layer.push_feature(point_feature(vec![])),
1761 Err(MltError::MValueColumnCountMismatch {
1762 expected: 1,
1763 actual: 0
1764 })
1765 ));
1766 }
1767
1768 #[test]
1769 fn with_id_is_new_with_that_id() {
1770 let geometry = Geometry::Point(Point::new(1, 2));
1771 let mut expected = TileFeature::new(geometry.clone());
1772 expected.id = Some(7);
1773 assert_eq!(TileFeature::with_id(geometry, 7), expected);
1774 }
1775
1776 #[cfg(feature = "unstable-v2")]
1777 #[test]
1778 fn push_feature_validates_one_value_per_vertex() {
1779 let mut layer = TileLayer::new("layer", 4096).unwrap();
1780 layer.add_m_value("m", PropKind::I32).unwrap();
1781 let mut feature = point_feature(vec![]);
1782 feature.m_values = vec![MValue::I32(Some(vec![1, 2]))];
1783 assert!(matches!(
1784 layer.push_feature(feature),
1785 Err(MltError::MValueVertexCountMismatch {
1786 index: 0,
1787 expected: 1,
1788 actual: 2,
1789 })
1790 ));
1791 }
1792
1793 #[cfg(feature = "unstable-v2")]
1794 #[test]
1795 fn a_polygon_does_not_count_its_closing_vertices() {
1796 let ring = |pts: &[(i32, i32)]| {
1797 let mut ls = LineString::new(pts.iter().map(|&(x, y)| Coord { x, y }).collect());
1798 ls.close();
1799 ls
1800 };
1801 let poly = geo_types::Polygon::new(
1802 ring(&[(0, 0), (0, 4), (4, 4), (4, 0)]),
1803 vec![ring(&[(1, 1), (1, 2), (2, 2)])],
1804 );
1805 let feature = TileFeature::new(Geometry::Polygon(poly));
1806 assert_eq!(feature.vertex_count(), 7);
1807 }
1808
1809 #[cfg(feature = "unstable-v2")]
1810 #[test]
1811 fn a_shape_counts_its_root_as_the_first_level() {
1812 let kind = NestedKind::list(NestedKind::map([("a", NestedKind::Leaf(PropKind::I32))]));
1813 assert_eq!(kind.depth(), 3);
1814 }
1815
1816 #[cfg(feature = "unstable-v2")]
1817 #[test]
1818 fn a_map_may_leave_out_a_field_but_not_add_one() {
1819 let kind = NestedKind::map([
1820 ("a", NestedKind::Leaf(PropKind::I32)),
1821 ("b", NestedKind::Leaf(PropKind::Str)),
1822 ]);
1823 assert!(kind.accepts(&NestedValue::map([(
1824 "a",
1825 NestedValue::Leaf(PropValue::I32(Some(1)))
1826 )])));
1827 assert!(!kind.accepts(&NestedValue::map([(
1828 "c",
1829 NestedValue::Leaf(PropValue::I32(Some(1)))
1830 )])));
1831 assert!(!kind.accepts(&NestedValue::map([(
1832 "a",
1833 NestedValue::Leaf(PropValue::Str(Some("x".into())))
1834 )])));
1835 }
1836
1837 #[cfg(feature = "unstable-v2")]
1838 fn list_kind() -> NestedKind {
1839 NestedKind::list(NestedKind::Leaf(PropKind::I32))
1840 }
1841
1842 #[cfg(feature = "unstable-v2")]
1843 #[test]
1844 fn add_m_value_rejects_a_name_a_property_has() {
1845 let mut layer = TileLayer::new("layer", 4096).unwrap();
1846 layer.add_property("n", PropKind::I32).unwrap();
1847 assert_eq!(
1848 layer
1849 .add_m_value("n", PropKind::I32)
1850 .unwrap_err()
1851 .to_string(),
1852 "duplicate column name n: the m-value column repeats the property column"
1853 );
1854 }
1855
1856 #[cfg(feature = "unstable-v2")]
1857 #[test]
1858 fn add_m_value_rejects_a_name_a_nested_column_has() {
1859 let mut layer = TileLayer::new("layer", 4096).unwrap();
1860 layer.add_nested("n", list_kind()).unwrap();
1861 assert_eq!(
1862 layer
1863 .add_m_value("n", PropKind::I32)
1864 .unwrap_err()
1865 .to_string(),
1866 "duplicate column name n: the m-value column repeats the nested column"
1867 );
1868 }
1869
1870 #[cfg(feature = "unstable-v2")]
1871 #[test]
1872 fn add_m_value_rejects_duplicate_names() {
1873 let mut layer = TileLayer::new("layer", 4096).unwrap();
1874 layer.add_m_value("n", PropKind::I32).unwrap();
1875 assert_eq!(
1876 layer
1877 .add_m_value("n", PropKind::I32)
1878 .unwrap_err()
1879 .to_string(),
1880 "duplicate column name n: the m-value column repeats the m-value column"
1881 );
1882 }
1883
1884 #[cfg(feature = "unstable-v2")]
1885 #[test]
1886 fn add_property_rejects_a_name_an_m_value_has() {
1887 let mut layer = TileLayer::new("layer", 4096).unwrap();
1888 layer.add_m_value("n", PropKind::I32).unwrap();
1889 assert_eq!(
1890 layer
1891 .add_property("n", PropKind::I32)
1892 .unwrap_err()
1893 .to_string(),
1894 "duplicate column name n: the property column repeats the m-value column"
1895 );
1896 }
1897
1898 #[cfg(feature = "unstable-v2")]
1899 #[test]
1900 fn add_property_rejects_a_name_a_nested_column_has() {
1901 let mut layer = TileLayer::new("layer", 4096).unwrap();
1902 layer.add_nested("n", list_kind()).unwrap();
1903 assert_eq!(
1904 layer
1905 .add_property("n", PropKind::I32)
1906 .unwrap_err()
1907 .to_string(),
1908 "duplicate column name n: the property column repeats the nested column"
1909 );
1910 }
1911
1912 #[cfg(feature = "unstable-v2")]
1913 #[test]
1914 fn add_nested_rejects_a_name_a_property_has() {
1915 let mut layer = TileLayer::new("layer", 4096).unwrap();
1916 layer.add_property("n", PropKind::I32).unwrap();
1917 assert_eq!(
1918 layer.add_nested("n", list_kind()).unwrap_err().to_string(),
1919 "duplicate column name n: the nested column repeats the property column"
1920 );
1921 }
1922
1923 #[cfg(feature = "unstable-v2")]
1924 #[test]
1925 fn add_nested_rejects_a_name_an_m_value_already_has() {
1926 let mut layer = TileLayer::new("layer", 4096).unwrap();
1927 layer.add_m_value("n", PropKind::I32).unwrap();
1928 assert_eq!(
1929 layer.add_nested("n", list_kind()).unwrap_err().to_string(),
1930 "duplicate column name n: the nested column repeats the m-value column"
1931 );
1932 }
1933
1934 #[cfg(feature = "unstable-v2")]
1935 #[test]
1936 fn add_nested_rejects_duplicate_names() {
1937 let mut layer = TileLayer::new("layer", 4096).unwrap();
1938 layer.add_nested("n", list_kind()).unwrap();
1939 assert_eq!(
1940 layer.add_nested("n", list_kind()).unwrap_err().to_string(),
1941 "duplicate column name n: the nested column repeats the nested column"
1942 );
1943 }
1944
1945 #[cfg(feature = "unstable-v2")]
1946 #[test]
1947 fn set_nested_rejects_a_value_of_another_shape() {
1948 let mut layer = TileLayer::new("layer", 4096).unwrap();
1949 let key = layer
1950 .add_nested("n", NestedKind::list(NestedKind::Leaf(PropKind::I32)))
1951 .unwrap();
1952 let mut feature = point_feature(vec![]);
1953 feature.nested = vec![NestedValue::List(None)];
1954 assert_eq!(
1955 feature
1956 .set_nested(key, NestedValue::Map(None))
1957 .unwrap_err()
1958 .to_string(),
1959 "nested column 0 was given a value of another shape"
1960 );
1961 }
1962
1963 #[test]
1964 fn builder_uses_declared_property_kind_for_defaults() {
1965 let mut builder = TileLayer::builder("layer", 4096).unwrap();
1966 let flag = builder.add_property("flag", PropKind::Bool).unwrap();
1967 let mut feature = builder.feature(Geometry::Point(Point::new(0, 0)));
1968 feature.property(flag, PropValue::Bool(Some(true))).unwrap();
1969 feature.finish().unwrap();
1970 let layer = builder.finish();
1971
1972 assert_eq!(
1973 layer.features()[0].properties()[0],
1974 PropValue::Bool(Some(true))
1975 );
1976 }
1977}