Skip to main content

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}