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}