geometry_trait/collection.rs
1//! The [`GeometryCollection`] concept — a sequence of geometries.
2//!
3//! Mirrors `boost::geometry::model::geometry_collection` in
4//! `boost/geometry/geometries/geometry_collection.hpp` and the
5//! `geometry_collection_tag` marker in
6//! `boost/geometry/core/tags.hpp` (`struct geometry_collection_tag :
7//! multi_tag {};`). The C++ model derives from
8//! `Container<DynamicGeometry, Allocator<DynamicGeometry>>` and is
9//! consumed through `boost::range`; this Rust port surfaces the
10//! element sequence via an RPITIT `items()` iterator, matching the
11//! pattern already used by the multi-* traits.
12//!
13//! **Scope:** v1 only supplies the trait surface. Heterogeneous
14//! dynamic dispatch (a `dyn`/`enum`-based story that would let one
15//! collection mix Points, Linestrings, Polygons, …) is intentionally
16//! deferred — see `specs/FUTURE_ITERATIONS.md` §1.3 (`DynGeometry`
17//! variant) and §1.4 (heterogeneous `GeometryCollection`). The
18//! [`GeometryCollection::Item`] associated type is therefore bound on
19//! [`Geometry`] alone, so today's impls are expected to be
20//! homogeneous in practice (e.g. `Vec<Polygon<P>>`).
21
22use crate::geometry::Geometry;
23use geometry_tag::GeometryCollectionTag;
24
25/// A collection of geometries belonging to each other.
26///
27/// Mirrors the `GeometryCollection` concept; the canonical C++ model
28/// is `boost::geometry::model::geometry_collection` in
29/// `boost/geometry/geometries/geometry_collection.hpp`, tagged with
30/// `geometry_collection_tag` (`boost/geometry/core/tags.hpp`).
31///
32/// In v1 the [`Item`](Self::Item) associated type is bound only on
33/// [`Geometry`] — every concrete impl is homogeneous (a single
34/// element type for the whole collection). Heterogeneous dispatch is
35/// intentionally deferred to a later iteration; see
36/// `specs/FUTURE_ITERATIONS.md` §1.3 (`DynGeometry` variant) and
37/// §1.4 (heterogeneous `GeometryCollection`).
38///
39/// # Examples
40///
41/// ```
42/// use geometry_trait::GeometryCollection;
43/// fn count<C: GeometryCollection>(c: &C) -> usize { c.items().len() }
44/// ```
45pub trait GeometryCollection: Geometry<Kind = GeometryCollectionTag> {
46 /// The element geometry type.
47 ///
48 /// Mirrors the `DynamicGeometry` template parameter on
49 /// `boost::geometry::model::geometry_collection<DynamicGeometry, …>`
50 /// (`boost/geometry/geometries/geometry_collection.hpp`). In v1
51 /// the bound is just [`Geometry`]; once §1.4 lands this will
52 /// tighten to a dynamic-geometry concept.
53 type Item: Geometry;
54
55 /// The items of this collection, in declared order.
56 ///
57 /// Plays the role of `boost::begin(gc)` / `boost::end(gc)` from
58 /// `boost/geometry/geometries/geometry_collection.hpp` when read
59 /// together.
60 fn items(&self) -> impl ExactSizeIterator<Item = &Self::Item>;
61}
62
63#[cfg(test)]
64mod tests {
65 extern crate alloc;
66
67 use super::*;
68 use crate::point::{Point, PointMut};
69 use alloc::vec;
70 use alloc::vec::Vec;
71 use geometry_cs::Cartesian;
72 use geometry_tag::PointTag;
73
74 // Witness: any T satisfying the trait is accepted. Static,
75 // compile-time check; never runs.
76 fn accepts_gc<G: GeometryCollection>() {}
77
78 // Shared 2D Cartesian point used by the test impl below.
79 struct Xy(f64, f64);
80
81 impl Geometry for Xy {
82 type Kind = PointTag;
83 type Point = Self;
84 }
85
86 impl Point for Xy {
87 type Scalar = f64;
88 type Cs = Cartesian;
89 const DIM: usize = 2;
90
91 fn get<const D: usize>(&self) -> f64 {
92 if D == 0 { self.0 } else { self.1 }
93 }
94 }
95
96 impl PointMut for Xy {
97 fn set<const D: usize>(&mut self, v: f64) {
98 if D == 0 {
99 self.0 = v;
100 } else {
101 self.1 = v;
102 }
103 }
104 }
105
106 // Homogeneous Vec-backed collection of points.
107 struct ManyPoints(Vec<Xy>);
108
109 impl Geometry for ManyPoints {
110 type Kind = GeometryCollectionTag;
111 type Point = Xy;
112 }
113
114 impl GeometryCollection for ManyPoints {
115 type Item = Xy;
116
117 fn items(&self) -> impl ExactSizeIterator<Item = &Xy> {
118 self.0.iter()
119 }
120 }
121
122 #[test]
123 fn collection_iterates_all_items() {
124 let m = ManyPoints(vec![Xy(0.0, 0.0), Xy(1.0, 1.0), Xy(2.0, 2.0)]);
125 accepts_gc::<ManyPoints>();
126 assert_eq!(m.items().len(), 3);
127 let xs: Vec<f64> = m.items().map(Xy::get::<0>).collect();
128 assert_eq!(xs, vec![0.0, 1.0, 2.0]);
129 }
130}