geo_types/geometry/geometry_collection.rs
1use crate::{CoordNum, Geometry};
2
3use alloc::vec;
4use alloc::vec::Vec;
5use core::iter::FromIterator;
6use core::ops::{Index, IndexMut};
7use core::slice::SliceIndex;
8
9/// A collection of [`Geometry`](enum.Geometry.html) types.
10///
11/// It can be created from a `Vec` of Geometries, or from an Iterator which yields Geometries.
12///
13/// Looping over this object yields its component **Geometry
14/// enum members** (_not_ the underlying geometry
15/// primitives), and it supports iteration and indexing as
16/// well as the various
17/// [`MapCoords`](algorithm/map_coords/index.html)
18/// functions, which _are_ directly applied to the
19/// underlying geometry primitives.
20///
21/// # Examples
22/// ## Looping
23///
24/// ```
25/// use std::convert::TryFrom;
26/// use geo_types::{Point, point, Geometry, GeometryCollection};
27/// let p = point!(x: 1.0, y: 1.0);
28/// let pe = Geometry::Point(p);
29/// let gc = GeometryCollection::new_from(vec![pe]);
30/// for geom in gc {
31/// println!("{:?}", Point::try_from(geom).unwrap().x());
32/// }
33/// ```
34/// ## Implements `iter()`
35///
36/// ```
37/// use std::convert::TryFrom;
38/// use geo_types::{Point, point, Geometry, GeometryCollection};
39/// let p = point!(x: 1.0, y: 1.0);
40/// let pe = Geometry::Point(p);
41/// let gc = GeometryCollection::new_from(vec![pe]);
42/// gc.iter().for_each(|geom| println!("{:?}", geom));
43/// ```
44///
45/// ## Mutable Iteration
46///
47/// ```
48/// use std::convert::TryFrom;
49/// use geo_types::{Point, point, Geometry, GeometryCollection};
50/// let p = point!(x: 1.0, y: 1.0);
51/// let pe = Geometry::Point(p);
52/// let mut gc = GeometryCollection::new_from(vec![pe]);
53/// gc.iter_mut().for_each(|geom| {
54/// if let Geometry::Point(p) = geom {
55/// p.set_x(0.2);
56/// }
57/// });
58/// let updated = gc[0].clone();
59/// assert_eq!(Point::try_from(updated).unwrap().x(), 0.2);
60/// ```
61///
62/// ## Indexing
63///
64/// ```
65/// use std::convert::TryFrom;
66/// use geo_types::{Point, point, Geometry, GeometryCollection};
67/// let p = point!(x: 1.0, y: 1.0);
68/// let pe = Geometry::Point(p);
69/// let gc = GeometryCollection::new_from(vec![pe]);
70/// println!("{:?}", gc[0]);
71/// ```
72///
73#[derive(Eq, PartialEq, Clone, Hash)]
74#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
75pub struct GeometryCollection<T: CoordNum = f64>(pub Vec<Geometry<T>>);
76
77// Implementing Default by hand because T does not have Default restriction
78// todo: consider adding Default as a CoordNum requirement
79impl<T: CoordNum> Default for GeometryCollection<T> {
80 fn default() -> Self {
81 Self(Vec::new())
82 }
83}
84
85impl<T: CoordNum> GeometryCollection<T> {
86 /// Return an empty GeometryCollection
87 #[deprecated(
88 note = "Will be replaced with a parametrized version in upcoming version. Use GeometryCollection::empty() instead"
89 )]
90 pub fn new() -> Self {
91 GeometryCollection::default()
92 }
93
94 /// DO NOT USE!
95 /// This fn will be renamed to `new` in the upcoming version.
96 /// This fn is not marked as deprecated because it would require extensive refactoring of the geo code.
97 pub fn new_from(value: Vec<Geometry<T>>) -> Self {
98 Self(value)
99 }
100
101 /// Returns an empty GeometryCollection
102 pub fn empty() -> Self {
103 Self(Vec::new())
104 }
105
106 /// Number of geometries in this GeometryCollection
107 pub fn len(&self) -> usize {
108 self.0.len()
109 }
110
111 /// Returns `true` if this `GeometryCollection` contains zero geometries.
112 ///
113 /// This is a purely structural check: it tests only whether the
114 /// underlying collection has no elements. It does **not** recurse into
115 /// the contained geometries, so a collection holding only empty
116 /// geometries (for example a single empty [`LineString`]) is **not**
117 /// considered empty by this method.
118 ///
119 /// If you need the dimensional notion of emptiness — where a collection
120 /// is empty when it has no geometries *or* all of its geometries are
121 /// themselves empty — use `geo::HasDimensions::is_empty` instead.
122 ///
123 /// # Examples
124 ///
125 /// ```
126 /// use geo_types::{GeometryCollection, LineString};
127 ///
128 /// // A collection with no geometries is empty.
129 /// let gc = GeometryCollection::<f64>::new_from(vec![]);
130 /// assert!(gc.is_empty());
131 ///
132 /// // A collection containing a single (empty) geometry is NOT empty:
133 /// // the element is present even though it has no coordinates.
134 /// let gc = GeometryCollection::<f64>::new_from(vec![
135 /// LineString::<f64>::new(vec![]).into(),
136 /// ]);
137 /// assert!(!gc.is_empty());
138 /// ```
139 ///
140 /// [`LineString`]: crate::LineString
141 pub fn is_empty(&self) -> bool {
142 self.0.is_empty()
143 }
144}
145
146/// **DO NOT USE!** Deprecated since 0.7.5.
147///
148/// Use `GeometryCollection::from(vec![geom])` instead.
149impl<T: CoordNum, IG: Into<Geometry<T>>> From<IG> for GeometryCollection<T> {
150 fn from(x: IG) -> Self {
151 Self(vec![x.into()])
152 }
153}
154
155impl<T: CoordNum, IG: Into<Geometry<T>>> From<Vec<IG>> for GeometryCollection<T> {
156 fn from(geoms: Vec<IG>) -> Self {
157 let geoms: Vec<Geometry<_>> = geoms.into_iter().map(Into::into).collect();
158 Self(geoms)
159 }
160}
161
162/// Collect Geometries (or what can be converted to a Geometry) into a GeometryCollection
163impl<T: CoordNum, IG: Into<Geometry<T>>> FromIterator<IG> for GeometryCollection<T> {
164 fn from_iter<I: IntoIterator<Item = IG>>(iter: I) -> Self {
165 Self(iter.into_iter().map(|g| g.into()).collect())
166 }
167}
168
169impl<T: CoordNum, I: SliceIndex<[Geometry<T>]>> Index<I> for GeometryCollection<T> {
170 type Output = I::Output;
171
172 fn index(&self, index: I) -> &I::Output {
173 self.0.index(index)
174 }
175}
176
177impl<T: CoordNum, I: SliceIndex<[Geometry<T>]>> IndexMut<I> for GeometryCollection<T> {
178 fn index_mut(&mut self, index: I) -> &mut I::Output {
179 self.0.index_mut(index)
180 }
181}
182
183// structure helper for consuming iterator
184#[derive(Debug)]
185pub struct IntoIteratorHelper<T: CoordNum> {
186 iter: ::alloc::vec::IntoIter<Geometry<T>>,
187}
188
189// implement the IntoIterator trait for a consuming iterator. Iteration will
190// consume the GeometryCollection
191impl<T: CoordNum> IntoIterator for GeometryCollection<T> {
192 type Item = Geometry<T>;
193 type IntoIter = IntoIteratorHelper<T>;
194
195 // note that into_iter() is consuming self
196 fn into_iter(self) -> Self::IntoIter {
197 IntoIteratorHelper {
198 iter: self.0.into_iter(),
199 }
200 }
201}
202
203// implement Iterator trait for the helper struct, to be used by adapters
204impl<T: CoordNum> Iterator for IntoIteratorHelper<T> {
205 type Item = Geometry<T>;
206
207 // just return the reference
208 fn next(&mut self) -> Option<Self::Item> {
209 self.iter.next()
210 }
211}
212
213// structure helper for non-consuming iterator
214#[derive(Debug)]
215pub struct IterHelper<'a, T: CoordNum> {
216 iter: ::core::slice::Iter<'a, Geometry<T>>,
217}
218
219// implement the IntoIterator trait for a non-consuming iterator. Iteration will
220// borrow the GeometryCollection
221impl<'a, T: CoordNum> IntoIterator for &'a GeometryCollection<T> {
222 type Item = &'a Geometry<T>;
223 type IntoIter = IterHelper<'a, T>;
224
225 // note that into_iter() is consuming self
226 fn into_iter(self) -> Self::IntoIter {
227 IterHelper {
228 iter: self.0.iter(),
229 }
230 }
231}
232
233// implement the Iterator trait for the helper struct, to be used by adapters
234impl<'a, T: CoordNum> Iterator for IterHelper<'a, T> {
235 type Item = &'a Geometry<T>;
236
237 // just return the str reference
238 fn next(&mut self) -> Option<Self::Item> {
239 self.iter.next()
240 }
241}
242
243// structure helper for mutable non-consuming iterator
244#[derive(Debug)]
245pub struct IterMutHelper<'a, T: CoordNum> {
246 iter: ::core::slice::IterMut<'a, Geometry<T>>,
247}
248
249// implement the IntoIterator trait for a mutable non-consuming iterator. Iteration will
250// mutably borrow the GeometryCollection
251impl<'a, T: CoordNum> IntoIterator for &'a mut GeometryCollection<T> {
252 type Item = &'a mut Geometry<T>;
253 type IntoIter = IterMutHelper<'a, T>;
254
255 // note that into_iter() is consuming self
256 fn into_iter(self) -> Self::IntoIter {
257 IterMutHelper {
258 iter: self.0.iter_mut(),
259 }
260 }
261}
262
263// implement the Iterator trait for the helper struct, to be used by adapters
264impl<'a, T: CoordNum> Iterator for IterMutHelper<'a, T> {
265 type Item = &'a mut Geometry<T>;
266
267 // just return the str reference
268 fn next(&mut self) -> Option<Self::Item> {
269 self.iter.next()
270 }
271}
272
273impl<'a, T: CoordNum> GeometryCollection<T> {
274 pub fn iter(&'a self) -> IterHelper<'a, T> {
275 self.into_iter()
276 }
277
278 pub fn iter_mut(&'a mut self) -> IterMutHelper<'a, T> {
279 self.into_iter()
280 }
281}
282
283#[cfg(any(feature = "approx", test))]
284mod approx_integration {
285 use super::*;
286 use approx::{AbsDiffEq, RelativeEq, UlpsEq};
287
288 impl<T> RelativeEq for GeometryCollection<T>
289 where
290 T: CoordNum + RelativeEq<Epsilon = T>,
291 {
292 #[inline]
293 fn default_max_relative() -> Self::Epsilon {
294 T::default_max_relative()
295 }
296
297 /// Equality assertion within a relative limit.
298 ///
299 /// # Examples
300 ///
301 /// ```
302 /// use geo_types::{GeometryCollection, point};
303 ///
304 /// let a = GeometryCollection::new_from(vec![point![x: 1.0, y: 2.0].into()]);
305 /// let b = GeometryCollection::new_from(vec![point![x: 1.0, y: 2.01].into()]);
306 ///
307 /// approx::assert_relative_eq!(a, b, max_relative=0.1);
308 /// approx::assert_relative_ne!(a, b, max_relative=0.0001);
309 /// ```
310 #[inline]
311 fn relative_eq(
312 &self,
313 other: &Self,
314 epsilon: Self::Epsilon,
315 max_relative: Self::Epsilon,
316 ) -> bool {
317 if self.0.len() != other.0.len() {
318 return false;
319 }
320
321 self.iter()
322 .zip(other.iter())
323 .all(|(lhs, rhs)| lhs.relative_eq(rhs, epsilon, max_relative))
324 }
325 }
326
327 impl<T> AbsDiffEq for GeometryCollection<T>
328 where
329 T: CoordNum + AbsDiffEq<Epsilon = T>,
330 {
331 type Epsilon = T;
332
333 #[inline]
334 fn default_epsilon() -> Self::Epsilon {
335 T::default_epsilon()
336 }
337
338 /// Equality assertion with an absolute limit.
339 ///
340 /// # Examples
341 ///
342 /// ```
343 /// use geo_types::{GeometryCollection, point};
344 ///
345 /// let a = GeometryCollection::new_from(vec![point![x: 0.0, y: 0.0].into()]);
346 /// let b = GeometryCollection::new_from(vec![point![x: 0.0, y: 0.1].into()]);
347 ///
348 /// approx::abs_diff_eq!(a, b, epsilon=0.1);
349 /// approx::abs_diff_ne!(a, b, epsilon=0.001);
350 /// ```
351 #[inline]
352 fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool {
353 if self.0.len() != other.0.len() {
354 return false;
355 }
356
357 self.into_iter()
358 .zip(other)
359 .all(|(lhs, rhs)| lhs.abs_diff_eq(rhs, epsilon))
360 }
361 }
362
363 impl<T> UlpsEq for GeometryCollection<T>
364 where
365 T: CoordNum + UlpsEq<Epsilon = T>,
366 {
367 fn default_max_ulps() -> u32 {
368 T::default_max_ulps()
369 }
370
371 fn ulps_eq(&self, other: &Self, epsilon: Self::Epsilon, max_ulps: u32) -> bool {
372 if self.0.len() != other.0.len() {
373 return false;
374 }
375 self.into_iter()
376 .zip(other)
377 .all(|(lhs, rhs)| lhs.ulps_eq(rhs, epsilon, max_ulps))
378 }
379 }
380}
381
382#[cfg(test)]
383mod tests {
384 use alloc::vec;
385
386 use crate::{point, wkt, GeometryCollection, Point};
387
388 #[test]
389 fn from_vec() {
390 let gc = GeometryCollection::from(vec![Point::new(1i32, 2)]);
391 let p = Point::try_from(gc[0].clone()).unwrap();
392 assert_eq!(p.y(), 2);
393 }
394
395 #[test]
396 fn empty() {
397 let empty = GeometryCollection::<f64>::empty();
398 let empty_2 = wkt! { GEOMETRYCOLLECTION EMPTY };
399 assert_eq!(empty, empty_2);
400 }
401
402 #[test]
403 fn test_indexing() {
404 let mut gc = wkt! { GEOMETRYCOLLECTION(POINT(0. 0.), POINT(1. 1.), POINT(2. 2.)) };
405
406 // Index
407 assert_eq!(gc[0], point! { x: 0., y: 0. }.into());
408 assert_eq!(gc[1], point! { x: 1., y: 1. }.into());
409
410 // IndexMut
411 gc[1] = point! { x: 100., y: 100. }.into();
412 assert_eq!(gc[1], point! { x: 100., y: 100. }.into());
413
414 // Range
415 assert_eq!(
416 gc[0..2],
417 [
418 point! { x: 0., y: 0. }.into(),
419 point! { x: 100., y: 100. }.into()
420 ]
421 );
422 }
423
424 #[test]
425 #[should_panic]
426 fn test_indexing_out_of_bounds() {
427 let gc = wkt! { GEOMETRYCOLLECTION(POINT(0. 0.), POINT(1. 1.)) };
428 let _ = gc[2];
429 }
430}