Skip to main content

geometry_model/
multi.rs

1//! Default `model::Multi{Point, Linestring, Polygon}` — homogeneous
2//! collections of a single geometry kind, each stored in a `Vec`.
3//!
4//! Mirrors `boost::geometry::model::multi_point`
5//! (`boost/geometry/geometries/multi_point.hpp:52-100`),
6//! `boost::geometry::model::multi_linestring`
7//! (`boost/geometry/geometries/multi_linestring.hpp:48-92`), and
8//! `boost::geometry::model::multi_polygon`
9//! (`boost/geometry/geometries/multi_polygon.hpp:48-92`), together with
10//! the `traits::tag` specialisations the same headers provide at
11//! `multi_point.hpp:101-115`, `multi_linestring.hpp:100-115`, and
12//! `multi_polygon.hpp:100-115`. The Rust port collapses each of those
13//! specialisations into the two trait impls below ([`Geometry`] and
14//! the matching `MultiXxxTrait`), keyed off the generic element type.
15//!
16//! All three C++ models derive from `Container<Item, Allocator<Item>>`
17//! so any `boost::range` algorithm sees the underlying storage
18//! directly; the Rust port stores a `Vec<Item>` and surfaces it
19//! through the `…s()` iterator the matching concept requires.
20
21use alloc::vec::Vec;
22
23use geometry_tag::{MultiLinestringTag, MultiPointTag, MultiPolygonTag};
24use geometry_trait::{
25    Geometry, Linestring as LinestringTrait, MultiLinestring as MultiLinestringTrait,
26    MultiPoint as MultiPointTrait, MultiPolygon as MultiPolygonTrait, Point as PointTrait,
27    Polygon as PolygonTrait,
28};
29
30/// Default `MultiPoint` — a `Vec<P>` newtype.
31///
32/// Mirrors `boost::geometry::model::multi_point<Point>` from
33/// `boost/geometry/geometries/multi_point.hpp:52-100`. The
34/// `#[repr(transparent)]` annotation guarantees the in-memory layout
35/// matches the underlying `Vec<P>`, mirroring the invariant Boost
36/// relies on by inheriting publicly from `std::vector<Point>`
37/// (`boost/geometry/geometries/multi_point.hpp:58`).
38#[derive(Debug, Clone, PartialEq)]
39#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
40#[repr(transparent)]
41pub struct MultiPoint<P: PointTrait>(pub Vec<P>);
42
43impl<P: PointTrait> MultiPoint<P> {
44    /// Construct an empty multi-point.
45    ///
46    /// Mirrors the default `multi_point()` constructor at
47    /// `boost/geometry/geometries/multi_point.hpp:66-68`.
48    #[must_use]
49    pub const fn new() -> Self {
50        Self(Vec::new())
51    }
52
53    /// Wrap an existing `Vec<P>` as a multi-point without copying.
54    ///
55    /// Rust analogue of the iterator-range `multi_point(Iterator,
56    /// Iterator)` constructor at
57    /// `boost/geometry/geometries/multi_point.hpp:71-74`; the `Vec`
58    /// is moved in rather than copied element-by-element.
59    #[must_use]
60    pub const fn from_vec(v: Vec<P>) -> Self {
61        Self(v)
62    }
63
64    /// Append a point to the back of the multi-point.
65    ///
66    /// Plays the role of `std::vector::push_back` on the inherited
67    /// `base_type` in `boost/geometry/geometries/multi_point.hpp:58-62`.
68    pub fn push(&mut self, p: P) {
69        self.0.push(p);
70    }
71}
72
73impl<P: PointTrait> Default for MultiPoint<P> {
74    #[inline]
75    fn default() -> Self {
76        Self::new()
77    }
78}
79
80/// Tag this concrete type as a `MultiPoint`. Mirrors the
81/// `traits::tag<model::multi_point<...>>` specialisation at
82/// `boost/geometry/geometries/multi_point.hpp:101-115`.
83impl<P: PointTrait> Geometry for MultiPoint<P> {
84    type Kind = MultiPointTag;
85    type Point = P;
86}
87
88/// Wire the `MultiPoint` concept up. The `points()` iterator hands back
89/// `self.0.iter()` — `slice::Iter` is already `ExactSizeIterator`.
90/// Plays the role of `boost::begin(mp)` / `boost::end(mp)` on
91/// `model::multi_point` (`boost/geometry/geometries/multi_point.hpp:58-62`).
92impl<P: PointTrait> MultiPointTrait for MultiPoint<P> {
93    type ItemPoint = P;
94
95    fn points(&self) -> impl ExactSizeIterator<Item = &P> {
96        self.0.iter()
97    }
98}
99
100/// Default `MultiLinestring` — a `Vec<L>` newtype where `L` is any
101/// concrete [`LinestringTrait`] implementor.
102///
103/// Mirrors `boost::geometry::model::multi_linestring<LineString>` from
104/// `boost/geometry/geometries/multi_linestring.hpp:48-92`. The
105/// `#[repr(transparent)]` annotation guarantees the in-memory layout
106/// matches the underlying `Vec<L>`, mirroring the invariant Boost
107/// relies on by inheriting publicly from `std::vector<LineString>`
108/// (`boost/geometry/geometries/multi_linestring.hpp:55`).
109#[derive(Debug, Clone, PartialEq)]
110#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
111#[repr(transparent)]
112pub struct MultiLinestring<L: LinestringTrait>(pub Vec<L>);
113
114impl<L: LinestringTrait> MultiLinestring<L> {
115    /// Construct an empty multi-linestring.
116    ///
117    /// Mirrors the default `multi_linestring()` constructor at
118    /// `boost/geometry/geometries/multi_linestring.hpp:63-65`.
119    #[must_use]
120    pub const fn new() -> Self {
121        Self(Vec::new())
122    }
123
124    /// Wrap an existing `Vec<L>` as a multi-linestring without copying.
125    ///
126    /// Rust analogue of the iterator-range
127    /// `multi_linestring(Iterator, Iterator)` constructor at
128    /// `boost/geometry/geometries/multi_linestring.hpp:68-71`.
129    #[must_use]
130    pub const fn from_vec(v: Vec<L>) -> Self {
131        Self(v)
132    }
133
134    /// Append a linestring to the back of the multi-linestring.
135    ///
136    /// Plays the role of `std::vector::push_back` on the inherited
137    /// `base_type` in
138    /// `boost/geometry/geometries/multi_linestring.hpp:55-59`.
139    pub fn push(&mut self, ls: L) {
140        self.0.push(ls);
141    }
142}
143
144impl<L: LinestringTrait> Default for MultiLinestring<L> {
145    #[inline]
146    fn default() -> Self {
147        Self::new()
148    }
149}
150
151/// Tag this concrete type as a `MultiLinestring`. Mirrors the
152/// `traits::tag<model::multi_linestring<...>>` specialisation at
153/// `boost/geometry/geometries/multi_linestring.hpp:100-115`.
154impl<L: LinestringTrait> Geometry for MultiLinestring<L> {
155    type Kind = MultiLinestringTag;
156    type Point = L::Point;
157}
158
159/// Wire the `MultiLinestring` concept up. The `linestrings()` iterator
160/// hands back `self.0.iter()` — `slice::Iter` is already
161/// `ExactSizeIterator`. Plays the role of `boost::begin(mls)` /
162/// `boost::end(mls)` on `model::multi_linestring`
163/// (`boost/geometry/geometries/multi_linestring.hpp:55-59`).
164impl<L: LinestringTrait> MultiLinestringTrait for MultiLinestring<L> {
165    type ItemLinestring = L;
166
167    fn linestrings(&self) -> impl ExactSizeIterator<Item = &L> {
168        self.0.iter()
169    }
170}
171
172/// Default `MultiPolygon` — a `Vec<Pg>` newtype where `Pg` is any
173/// concrete [`PolygonTrait`] implementor.
174///
175/// Mirrors `boost::geometry::model::multi_polygon<Polygon>` from
176/// `boost/geometry/geometries/multi_polygon.hpp:48-92`. The
177/// `#[repr(transparent)]` annotation guarantees the in-memory layout
178/// matches the underlying `Vec<Pg>`, mirroring the invariant Boost
179/// relies on by inheriting publicly from `std::vector<Polygon>`
180/// (`boost/geometry/geometries/multi_polygon.hpp:55`).
181#[derive(Debug, Clone, PartialEq)]
182#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
183#[repr(transparent)]
184pub struct MultiPolygon<Pg: PolygonTrait>(pub Vec<Pg>);
185
186impl<Pg: PolygonTrait> MultiPolygon<Pg> {
187    /// Construct an empty multi-polygon.
188    ///
189    /// Mirrors the default `multi_polygon()` constructor at
190    /// `boost/geometry/geometries/multi_polygon.hpp:63-65`.
191    #[must_use]
192    pub const fn new() -> Self {
193        Self(Vec::new())
194    }
195
196    /// Wrap an existing `Vec<Pg>` as a multi-polygon without copying.
197    ///
198    /// Rust analogue of the iterator-range
199    /// `multi_polygon(Iterator, Iterator)` constructor at
200    /// `boost/geometry/geometries/multi_polygon.hpp:68-71`.
201    #[must_use]
202    pub const fn from_vec(v: Vec<Pg>) -> Self {
203        Self(v)
204    }
205
206    /// Append a polygon to the back of the multi-polygon.
207    ///
208    /// Plays the role of `std::vector::push_back` on the inherited
209    /// `base_type` in
210    /// `boost/geometry/geometries/multi_polygon.hpp:55-59`.
211    pub fn push(&mut self, pg: Pg) {
212        self.0.push(pg);
213    }
214}
215
216impl<Pg: PolygonTrait> Default for MultiPolygon<Pg> {
217    #[inline]
218    fn default() -> Self {
219        Self::new()
220    }
221}
222
223/// Tag this concrete type as a `MultiPolygon`. Mirrors the
224/// `traits::tag<model::multi_polygon<...>>` specialisation at
225/// `boost/geometry/geometries/multi_polygon.hpp:100-115`.
226impl<Pg: PolygonTrait> Geometry for MultiPolygon<Pg> {
227    type Kind = MultiPolygonTag;
228    type Point = Pg::Point;
229}
230
231/// Wire the `MultiPolygon` concept up. The `polygons()` iterator hands
232/// back `self.0.iter()` — `slice::Iter` is already
233/// `ExactSizeIterator`. Plays the role of `boost::begin(mpg)` /
234/// `boost::end(mpg)` on `model::multi_polygon`
235/// (`boost/geometry/geometries/multi_polygon.hpp:55-59`).
236impl<Pg: PolygonTrait> MultiPolygonTrait for MultiPolygon<Pg> {
237    type ItemPolygon = Pg;
238
239    fn polygons(&self) -> impl ExactSizeIterator<Item = &Pg> {
240        self.0.iter()
241    }
242}
243
244#[cfg(test)]
245mod tests {
246    //! Construction + tag-resolution + concept-check witnesses for
247    //! [`MultiPoint`], [`MultiLinestring`], and [`MultiPolygon`].
248    //! Mirrors `boost/geometry/test/core/tag.cpp` (the multi arms) and
249    //! the iteration smoke tests in
250    //! `boost/geometry/test/geometries/multi_*.cpp`.
251
252    use super::{MultiLinestring, MultiPoint, MultiPolygon};
253    use crate::linestring::Linestring;
254    use crate::point::Point2D;
255    use crate::polygon::Polygon;
256    use crate::ring::Ring;
257    use alloc::vec;
258    use alloc::vec::Vec;
259    use geometry_cs::Cartesian;
260    use geometry_tag::{MultiLinestringTag, MultiPointTag, MultiPolygonTag};
261    use geometry_trait::{
262        Geometry, MultiLinestring as MultiLinestringTrait, MultiPoint as MultiPointTrait,
263        MultiPolygon as MultiPolygonTrait, Point as _, check_multi_linestring, check_multi_point,
264        check_multi_polygon,
265    };
266
267    type P = Point2D<f64, Cartesian>;
268
269    // ---- MultiPoint ---------------------------------------------------
270
271    #[test]
272    fn multipoint_iterates_in_declared_order() {
273        let mut mp = MultiPoint::<P>::new();
274        mp.push(Point2D::new(0.0, 0.0));
275        mp.push(Point2D::new(1.0, 2.0));
276        mp.push(Point2D::new(3.0, 4.0));
277        assert_eq!(mp.points().count(), 3);
278        let xs: Vec<u64> = mp.points().map(|p| p.get::<0>().to_bits()).collect();
279        assert_eq!(
280            xs,
281            vec![0.0_f64.to_bits(), 1.0_f64.to_bits(), 3.0_f64.to_bits()]
282        );
283    }
284
285    #[test]
286    fn multipoint_from_vec_reports_exact_size() {
287        let mp = MultiPoint::<P>::from_vec(vec![Point2D::new(0.0, 0.0), Point2D::new(1.0, 1.0)]);
288        assert_eq!(mp.points().len(), 2);
289    }
290
291    #[test]
292    fn multipoint_kind_is_multi_point_tag() {
293        fn k<T: Geometry<Kind = MultiPointTag>>() {}
294        k::<MultiPoint<P>>();
295    }
296
297    #[test]
298    fn multipoint_satisfies_concept() {
299        check_multi_point::<MultiPoint<P>>();
300    }
301
302    // ---- MultiLinestring ----------------------------------------------
303
304    #[test]
305    fn multilinestring_iterates_in_declared_order() {
306        let mut mls = MultiLinestring::<Linestring<P>>::new();
307        mls.push(Linestring::from_vec(vec![
308            Point2D::new(0.0, 0.0),
309            Point2D::new(1.0, 1.0),
310        ]));
311        mls.push(Linestring::from_vec(vec![
312            Point2D::new(2.0, 2.0),
313            Point2D::new(3.0, 3.0),
314            Point2D::new(4.0, 4.0),
315        ]));
316        assert_eq!(mls.linestrings().count(), 2);
317    }
318
319    #[test]
320    fn multilinestring_kind_is_multi_linestring_tag() {
321        fn k<T: Geometry<Kind = MultiLinestringTag>>() {}
322        k::<MultiLinestring<Linestring<P>>>();
323    }
324
325    #[test]
326    fn multilinestring_satisfies_concept() {
327        check_multi_linestring::<MultiLinestring<Linestring<P>>>();
328    }
329
330    // ---- MultiPolygon -------------------------------------------------
331
332    fn unit_square() -> Ring<P> {
333        Ring::from_vec(vec![
334            Point2D::new(0.0, 0.0),
335            Point2D::new(1.0, 0.0),
336            Point2D::new(1.0, 1.0),
337            Point2D::new(0.0, 1.0),
338            Point2D::new(0.0, 0.0),
339        ])
340    }
341
342    #[test]
343    fn multipolygon_iterates_in_declared_order() {
344        let mut mpg = MultiPolygon::<Polygon<P>>::new();
345        mpg.push(Polygon::new(unit_square()));
346        mpg.push(Polygon::new(unit_square()));
347        assert_eq!(mpg.polygons().count(), 2);
348    }
349
350    #[test]
351    fn multipolygon_kind_is_multi_polygon_tag() {
352        fn k<T: Geometry<Kind = MultiPolygonTag>>() {}
353        k::<MultiPolygon<Polygon<P>>>();
354    }
355
356    #[test]
357    fn multipolygon_satisfies_concept() {
358        check_multi_polygon::<MultiPolygon<Polygon<P>>>();
359    }
360}