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    /// Is this GeometryCollection empty
112    pub fn is_empty(&self) -> bool {
113        self.0.is_empty()
114    }
115}
116
117/// **DO NOT USE!** Deprecated since 0.7.5.
118///
119/// Use `GeometryCollection::from(vec![geom])` instead.
120impl<T: CoordNum, IG: Into<Geometry<T>>> From<IG> for GeometryCollection<T> {
121    fn from(x: IG) -> Self {
122        Self(vec![x.into()])
123    }
124}
125
126impl<T: CoordNum, IG: Into<Geometry<T>>> From<Vec<IG>> for GeometryCollection<T> {
127    fn from(geoms: Vec<IG>) -> Self {
128        let geoms: Vec<Geometry<_>> = geoms.into_iter().map(Into::into).collect();
129        Self(geoms)
130    }
131}
132
133/// Collect Geometries (or what can be converted to a Geometry) into a GeometryCollection
134impl<T: CoordNum, IG: Into<Geometry<T>>> FromIterator<IG> for GeometryCollection<T> {
135    fn from_iter<I: IntoIterator<Item = IG>>(iter: I) -> Self {
136        Self(iter.into_iter().map(|g| g.into()).collect())
137    }
138}
139
140impl<T: CoordNum, I: SliceIndex<[Geometry<T>]>> Index<I> for GeometryCollection<T> {
141    type Output = I::Output;
142
143    fn index(&self, index: I) -> &I::Output {
144        self.0.index(index)
145    }
146}
147
148impl<T: CoordNum, I: SliceIndex<[Geometry<T>]>> IndexMut<I> for GeometryCollection<T> {
149    fn index_mut(&mut self, index: I) -> &mut I::Output {
150        self.0.index_mut(index)
151    }
152}
153
154// structure helper for consuming iterator
155#[derive(Debug)]
156pub struct IntoIteratorHelper<T: CoordNum> {
157    iter: ::alloc::vec::IntoIter<Geometry<T>>,
158}
159
160// implement the IntoIterator trait for a consuming iterator. Iteration will
161// consume the GeometryCollection
162impl<T: CoordNum> IntoIterator for GeometryCollection<T> {
163    type Item = Geometry<T>;
164    type IntoIter = IntoIteratorHelper<T>;
165
166    // note that into_iter() is consuming self
167    fn into_iter(self) -> Self::IntoIter {
168        IntoIteratorHelper {
169            iter: self.0.into_iter(),
170        }
171    }
172}
173
174// implement Iterator trait for the helper struct, to be used by adapters
175impl<T: CoordNum> Iterator for IntoIteratorHelper<T> {
176    type Item = Geometry<T>;
177
178    // just return the reference
179    fn next(&mut self) -> Option<Self::Item> {
180        self.iter.next()
181    }
182}
183
184// structure helper for non-consuming iterator
185#[derive(Debug)]
186pub struct IterHelper<'a, T: CoordNum> {
187    iter: ::core::slice::Iter<'a, Geometry<T>>,
188}
189
190// implement the IntoIterator trait for a non-consuming iterator. Iteration will
191// borrow the GeometryCollection
192impl<'a, T: CoordNum> IntoIterator for &'a GeometryCollection<T> {
193    type Item = &'a Geometry<T>;
194    type IntoIter = IterHelper<'a, T>;
195
196    // note that into_iter() is consuming self
197    fn into_iter(self) -> Self::IntoIter {
198        IterHelper {
199            iter: self.0.iter(),
200        }
201    }
202}
203
204// implement the Iterator trait for the helper struct, to be used by adapters
205impl<'a, T: CoordNum> Iterator for IterHelper<'a, T> {
206    type Item = &'a Geometry<T>;
207
208    // just return the str reference
209    fn next(&mut self) -> Option<Self::Item> {
210        self.iter.next()
211    }
212}
213
214// structure helper for mutable non-consuming iterator
215#[derive(Debug)]
216pub struct IterMutHelper<'a, T: CoordNum> {
217    iter: ::core::slice::IterMut<'a, Geometry<T>>,
218}
219
220// implement the IntoIterator trait for a mutable non-consuming iterator. Iteration will
221// mutably borrow the GeometryCollection
222impl<'a, T: CoordNum> IntoIterator for &'a mut GeometryCollection<T> {
223    type Item = &'a mut Geometry<T>;
224    type IntoIter = IterMutHelper<'a, T>;
225
226    // note that into_iter() is consuming self
227    fn into_iter(self) -> Self::IntoIter {
228        IterMutHelper {
229            iter: self.0.iter_mut(),
230        }
231    }
232}
233
234// implement the Iterator trait for the helper struct, to be used by adapters
235impl<'a, T: CoordNum> Iterator for IterMutHelper<'a, T> {
236    type Item = &'a mut Geometry<T>;
237
238    // just return the str reference
239    fn next(&mut self) -> Option<Self::Item> {
240        self.iter.next()
241    }
242}
243
244impl<'a, T: CoordNum> GeometryCollection<T> {
245    pub fn iter(&'a self) -> IterHelper<'a, T> {
246        self.into_iter()
247    }
248
249    pub fn iter_mut(&'a mut self) -> IterMutHelper<'a, T> {
250        self.into_iter()
251    }
252}
253
254#[cfg(any(feature = "approx", test))]
255mod approx_integration {
256    use super::*;
257    use approx::{AbsDiffEq, RelativeEq, UlpsEq};
258
259    impl<T> RelativeEq for GeometryCollection<T>
260    where
261        T: CoordNum + RelativeEq<Epsilon = T>,
262    {
263        #[inline]
264        fn default_max_relative() -> Self::Epsilon {
265            T::default_max_relative()
266        }
267
268        /// Equality assertion within a relative limit.
269        ///
270        /// # Examples
271        ///
272        /// ```
273        /// use geo_types::{GeometryCollection, point};
274        ///
275        /// let a = GeometryCollection::new_from(vec![point![x: 1.0, y: 2.0].into()]);
276        /// let b = GeometryCollection::new_from(vec![point![x: 1.0, y: 2.01].into()]);
277        ///
278        /// approx::assert_relative_eq!(a, b, max_relative=0.1);
279        /// approx::assert_relative_ne!(a, b, max_relative=0.0001);
280        /// ```
281        #[inline]
282        fn relative_eq(
283            &self,
284            other: &Self,
285            epsilon: Self::Epsilon,
286            max_relative: Self::Epsilon,
287        ) -> bool {
288            if self.0.len() != other.0.len() {
289                return false;
290            }
291
292            self.iter()
293                .zip(other.iter())
294                .all(|(lhs, rhs)| lhs.relative_eq(rhs, epsilon, max_relative))
295        }
296    }
297
298    impl<T> AbsDiffEq for GeometryCollection<T>
299    where
300        T: CoordNum + AbsDiffEq<Epsilon = T>,
301    {
302        type Epsilon = T;
303
304        #[inline]
305        fn default_epsilon() -> Self::Epsilon {
306            T::default_epsilon()
307        }
308
309        /// Equality assertion with an absolute limit.
310        ///
311        /// # Examples
312        ///
313        /// ```
314        /// use geo_types::{GeometryCollection, point};
315        ///
316        /// let a = GeometryCollection::new_from(vec![point![x: 0.0, y: 0.0].into()]);
317        /// let b = GeometryCollection::new_from(vec![point![x: 0.0, y: 0.1].into()]);
318        ///
319        /// approx::abs_diff_eq!(a, b, epsilon=0.1);
320        /// approx::abs_diff_ne!(a, b, epsilon=0.001);
321        /// ```
322        #[inline]
323        fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool {
324            if self.0.len() != other.0.len() {
325                return false;
326            }
327
328            self.into_iter()
329                .zip(other)
330                .all(|(lhs, rhs)| lhs.abs_diff_eq(rhs, epsilon))
331        }
332    }
333
334    impl<T> UlpsEq for GeometryCollection<T>
335    where
336        T: CoordNum + UlpsEq<Epsilon = T>,
337    {
338        fn default_max_ulps() -> u32 {
339            T::default_max_ulps()
340        }
341
342        fn ulps_eq(&self, other: &Self, epsilon: Self::Epsilon, max_ulps: u32) -> bool {
343            if self.0.len() != other.0.len() {
344                return false;
345            }
346            self.into_iter()
347                .zip(other)
348                .all(|(lhs, rhs)| lhs.ulps_eq(rhs, epsilon, max_ulps))
349        }
350    }
351}
352
353#[cfg(test)]
354mod tests {
355    use alloc::vec;
356
357    use crate::{point, wkt, GeometryCollection, Point};
358
359    #[test]
360    fn from_vec() {
361        let gc = GeometryCollection::from(vec![Point::new(1i32, 2)]);
362        let p = Point::try_from(gc[0].clone()).unwrap();
363        assert_eq!(p.y(), 2);
364    }
365
366    #[test]
367    fn empty() {
368        let empty = GeometryCollection::<f64>::empty();
369        let empty_2 = wkt! { GEOMETRYCOLLECTION EMPTY };
370        assert_eq!(empty, empty_2);
371    }
372
373    #[test]
374    fn test_indexing() {
375        let mut gc = wkt! { GEOMETRYCOLLECTION(POINT(0. 0.), POINT(1. 1.), POINT(2. 2.)) };
376
377        // Index
378        assert_eq!(gc[0], point! { x: 0., y: 0. }.into());
379        assert_eq!(gc[1], point! { x: 1., y: 1. }.into());
380
381        // IndexMut
382        gc[1] = point! { x: 100., y: 100. }.into();
383        assert_eq!(gc[1], point! { x: 100., y: 100. }.into());
384
385        // Range
386        assert_eq!(
387            gc[0..2],
388            [
389                point! { x: 0., y: 0. }.into(),
390                point! { x: 100., y: 100. }.into()
391            ]
392        );
393    }
394
395    #[test]
396    #[should_panic]
397    fn test_indexing_out_of_bounds() {
398        let gc = wkt! { GEOMETRYCOLLECTION(POINT(0. 0.), POINT(1. 1.)) };
399        let _ = gc[2];
400    }
401}