Skip to main content

geometry_trait/
multi.rs

1//! The three "iter of a single geometry" concepts:
2//! [`MultiPoint`], [`MultiLinestring`], and [`MultiPolygon`].
3//!
4//! Mirrors `doc/concept/multi_point.qbk`, `doc/concept/multi_linestring.qbk`,
5//! and `doc/concept/multi_polygon.qbk`; the canonical models live in
6//! `boost/geometry/geometries/multi_point.hpp`,
7//! `boost/geometry/geometries/multi_linestring.hpp`, and
8//! `boost/geometry/geometries/multi_polygon.hpp` respectively. All three
9//! C++ models derive from `Container<Item, Allocator<Item>>` and are
10//! consumed through `boost::range`; the Rust counterparts surface the
11//! item sequence as a `…s()` iterator returned via RPITIT, matching the
12//! pattern already used by [`crate::Linestring`], [`crate::Ring`], and
13//! [`crate::Polygon`].
14
15use crate::geometry::Geometry;
16use crate::linestring::Linestring;
17use crate::point::Point;
18use crate::polygon::Polygon;
19use geometry_tag::{MultiLinestringTag, MultiPointTag, MultiPolygonTag};
20
21/// A multi-point — a collection of points belonging to each other.
22///
23/// Mirrors the `MultiPoint` concept (`doc/concept/multi_point.qbk`); the
24/// canonical model is `boost::geometry::model::multi_point` in
25/// `boost/geometry/geometries/multi_point.hpp`, which derives from
26/// `std::vector<Point>` and asserts `concepts::Point<Point>` on its
27/// element type. The Rust port mirrors that assertion by bounding
28/// [`MultiPoint::ItemPoint`] on the [`Point`] concept.
29///
30/// As with [`crate::Linestring`], the element sequence is exposed via
31/// RPITIT so each impl can hand back whatever iterator its underlying
32/// container provides. The `ExactSizeIterator` bound mirrors the
33/// random-access shape `boost::range` relies on for the C++ model:
34/// callers can ask for `.len()` without consuming the source.
35///
36/// # Examples
37///
38/// ```
39/// use geometry_trait::MultiPoint;
40/// fn count<M: MultiPoint>(m: &M) -> usize { m.points().len() }
41/// ```
42pub trait MultiPoint: Geometry<Kind = MultiPointTag> {
43    /// The element point type.
44    ///
45    /// Mirrors the `Point` template parameter on
46    /// `boost::geometry::model::multi_point<Point, …>`
47    /// (`boost/geometry/geometries/multi_point.hpp`). The Rust side
48    /// does not require `ItemPoint == Self::Point`: the C++ model
49    /// has no `Self::Point` projection of its own (its
50    /// `point_type<MP>::type` is read from the element), so any
51    /// [`Point`] is admissible here.
52    type ItemPoint: Point;
53
54    /// The points of this multi-point, in declared order.
55    ///
56    /// Plays the role of `boost::begin(mp)` / `boost::end(mp)` from
57    /// `boost/geometry/geometries/multi_point.hpp` when read together.
58    fn points(&self) -> impl ExactSizeIterator<Item = &Self::ItemPoint>;
59}
60
61/// A multi-linestring — a collection of linestrings belonging to each
62/// other (e.g. a highway with interruptions).
63///
64/// Mirrors the `MultiLinestring` concept
65/// (`doc/concept/multi_linestring.qbk`); the canonical model is
66/// `boost::geometry::model::multi_linestring` in
67/// `boost/geometry/geometries/multi_linestring.hpp`, which derives
68/// from `std::vector<LineString>` and asserts
69/// `concepts::Linestring<LineString>` on its element type. The Rust
70/// port mirrors that assertion by bounding
71/// [`MultiLinestring::ItemLinestring`] on the [`Linestring`] concept,
72/// and additionally pins `ItemLinestring::Point = Self::Point` so the
73/// multi's `point_type<MLS>` projection (read through any element)
74/// stays consistent across the collection.
75///
76/// # Examples
77///
78/// ```
79/// use geometry_trait::MultiLinestring;
80/// fn count<M: MultiLinestring>(m: &M) -> usize { m.linestrings().len() }
81/// ```
82pub trait MultiLinestring: Geometry<Kind = MultiLinestringTag> {
83    /// The element linestring type.
84    ///
85    /// Mirrors the `LineString` template parameter on
86    /// `boost::geometry::model::multi_linestring<LineString, …>`
87    /// (`boost/geometry/geometries/multi_linestring.hpp`).
88    type ItemLinestring: Linestring<Point = Self::Point>;
89
90    /// The linestrings of this multi-linestring, in declared order.
91    ///
92    /// Plays the role of `boost::begin(mls)` / `boost::end(mls)` from
93    /// `boost/geometry/geometries/multi_linestring.hpp` when read
94    /// together.
95    fn linestrings(&self) -> impl ExactSizeIterator<Item = &Self::ItemLinestring>;
96}
97
98/// A multi-polygon — a collection of polygons belonging to each other
99/// (e.g. Hawaii).
100///
101/// Mirrors the `MultiPolygon` concept (`doc/concept/multi_polygon.qbk`);
102/// the canonical model is `boost::geometry::model::multi_polygon` in
103/// `boost/geometry/geometries/multi_polygon.hpp`, which derives from
104/// `std::vector<Polygon>` and asserts `concepts::Polygon<Polygon>` on
105/// its element type. The Rust port mirrors that assertion by bounding
106/// [`MultiPolygon::ItemPolygon`] on the [`Polygon`] concept, and
107/// additionally pins `ItemPolygon::Point = Self::Point` so the multi's
108/// `point_type<MPG>` projection stays consistent across the collection.
109///
110/// # Examples
111///
112/// ```
113/// use geometry_trait::MultiPolygon;
114/// fn count<M: MultiPolygon>(m: &M) -> usize { m.polygons().len() }
115/// ```
116pub trait MultiPolygon: Geometry<Kind = MultiPolygonTag> {
117    /// The element polygon type.
118    ///
119    /// Mirrors the `Polygon` template parameter on
120    /// `boost::geometry::model::multi_polygon<Polygon, …>`
121    /// (`boost/geometry/geometries/multi_polygon.hpp`).
122    type ItemPolygon: Polygon<Point = Self::Point>;
123
124    /// The polygons of this multi-polygon, in declared order.
125    ///
126    /// Plays the role of `boost::begin(mpg)` / `boost::end(mpg)` from
127    /// `boost/geometry/geometries/multi_polygon.hpp` when read
128    /// together.
129    fn polygons(&self) -> impl ExactSizeIterator<Item = &Self::ItemPolygon>;
130}
131
132#[cfg(test)]
133mod tests {
134    extern crate alloc;
135
136    use super::*;
137    use crate::point::PointMut;
138    use crate::ring::Ring;
139    use alloc::vec;
140    use alloc::vec::Vec;
141    use geometry_cs::Cartesian;
142    use geometry_tag::{LinestringTag, PointTag, PolygonTag, RingTag};
143
144    // --- Witnesses: any T satisfying the trait is accepted. These
145    // are static, compile-time checks; they never run. ---
146    fn accepts_mp<M: MultiPoint>() {}
147    fn accepts_mls<M: MultiLinestring>() {}
148    fn accepts_mpg<M: MultiPolygon>() {}
149
150    // --- Shared 2D Cartesian point used by every test impl below. ---
151    #[derive(Clone)]
152    struct Xy(f64, f64);
153
154    impl Geometry for Xy {
155        type Kind = PointTag;
156        type Point = Self;
157    }
158
159    impl Point for Xy {
160        type Scalar = f64;
161        type Cs = Cartesian;
162        const DIM: usize = 2;
163
164        fn get<const D: usize>(&self) -> f64 {
165            if D == 0 { self.0 } else { self.1 }
166        }
167    }
168
169    impl PointMut for Xy {
170        fn set<const D: usize>(&mut self, v: f64) {
171            if D == 0 {
172                self.0 = v;
173            } else {
174                self.1 = v;
175            }
176        }
177    }
178
179    // --- Vec-backed MultiPoint. ---
180    struct VMp(Vec<Xy>);
181
182    impl Geometry for VMp {
183        type Kind = MultiPointTag;
184        type Point = Xy;
185    }
186
187    impl MultiPoint for VMp {
188        type ItemPoint = Xy;
189
190        fn points(&self) -> impl ExactSizeIterator<Item = &Xy> {
191            self.0.iter()
192        }
193    }
194
195    #[test]
196    fn vec_backed_multipoint_satisfies_trait() {
197        let mp = VMp(vec![Xy(0.0, 0.0), Xy(1.0, 2.0), Xy(3.0, 4.0)]);
198        accepts_mp::<VMp>();
199        assert_eq!(mp.points().len(), 3);
200        let xs: Vec<f64> = mp.points().map(Xy::get::<0>).collect();
201        assert_eq!(xs, vec![0.0, 1.0, 3.0]);
202    }
203
204    // --- Vec-backed MultiLinestring. ---
205    struct VLs(Vec<Xy>);
206
207    impl Geometry for VLs {
208        type Kind = LinestringTag;
209        type Point = Xy;
210    }
211
212    impl Linestring for VLs {
213        fn points(&self) -> impl ExactSizeIterator<Item = &Xy> + Clone {
214            self.0.iter()
215        }
216    }
217
218    struct VMls(Vec<VLs>);
219
220    impl Geometry for VMls {
221        type Kind = MultiLinestringTag;
222        type Point = Xy;
223    }
224
225    impl MultiLinestring for VMls {
226        type ItemLinestring = VLs;
227
228        fn linestrings(&self) -> impl ExactSizeIterator<Item = &VLs> {
229            self.0.iter()
230        }
231    }
232
233    #[test]
234    fn vec_backed_multilinestring_satisfies_trait() {
235        let mls = VMls(vec![
236            VLs(vec![Xy(0.0, 0.0), Xy(1.0, 1.0)]),
237            VLs(vec![Xy(2.0, 2.0), Xy(3.0, 3.0), Xy(4.0, 4.0)]),
238        ]);
239        accepts_mls::<VMls>();
240        assert_eq!(mls.linestrings().len(), 2);
241        let counts: Vec<usize> = mls.linestrings().map(|ls| ls.points().count()).collect();
242        assert_eq!(counts, vec![2, 3]);
243    }
244
245    // --- Vec-backed MultiPolygon. ---
246    struct VRing(Vec<Xy>);
247
248    impl Geometry for VRing {
249        type Kind = RingTag;
250        type Point = Xy;
251    }
252
253    impl Ring for VRing {
254        fn points(&self) -> impl ExactSizeIterator<Item = &Xy> + Clone {
255            self.0.iter()
256        }
257    }
258
259    struct VPoly {
260        outer: VRing,
261        inners: Vec<VRing>,
262    }
263
264    impl Geometry for VPoly {
265        type Kind = PolygonTag;
266        type Point = Xy;
267    }
268
269    impl Polygon for VPoly {
270        type Ring = VRing;
271
272        fn exterior(&self) -> &VRing {
273            &self.outer
274        }
275
276        fn interiors(&self) -> impl ExactSizeIterator<Item = &VRing> {
277            self.inners.iter()
278        }
279    }
280
281    struct VMpg(Vec<VPoly>);
282
283    impl Geometry for VMpg {
284        type Kind = MultiPolygonTag;
285        type Point = Xy;
286    }
287
288    impl MultiPolygon for VMpg {
289        type ItemPolygon = VPoly;
290
291        fn polygons(&self) -> impl ExactSizeIterator<Item = &VPoly> {
292            self.0.iter()
293        }
294    }
295
296    #[test]
297    fn vec_backed_multipolygon_satisfies_trait() {
298        let mpg = VMpg(vec![
299            VPoly {
300                outer: VRing(vec![
301                    Xy(0.0, 0.0),
302                    Xy(1.0, 0.0),
303                    Xy(1.0, 1.0),
304                    Xy(0.0, 1.0),
305                    Xy(0.0, 0.0),
306                ]),
307                inners: vec![],
308            },
309            VPoly {
310                outer: VRing(vec![
311                    Xy(2.0, 2.0),
312                    Xy(3.0, 2.0),
313                    Xy(3.0, 3.0),
314                    Xy(2.0, 3.0),
315                    Xy(2.0, 2.0),
316                ]),
317                inners: vec![VRing(vec![
318                    Xy(2.25, 2.25),
319                    Xy(2.75, 2.25),
320                    Xy(2.75, 2.75),
321                    Xy(2.25, 2.75),
322                    Xy(2.25, 2.25),
323                ])],
324            },
325        ]);
326        accepts_mpg::<VMpg>();
327        assert_eq!(mpg.polygons().len(), 2);
328        let inner_counts: Vec<usize> = mpg.polygons().map(|p| p.interiors().count()).collect();
329        assert_eq!(inner_counts, vec![0, 1]);
330    }
331}