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