geo_types/geometry/
multi_line_string.rs

1use crate::{CoordNum, LineString};
2
3use alloc::vec;
4use alloc::vec::Vec;
5#[cfg(any(feature = "approx", test))]
6use core::iter::FromIterator;
7#[cfg(feature = "multithreading")]
8use rayon::prelude::*;
9
10/// A collection of
11/// [`LineString`s](line_string/struct.LineString.html). Can
12/// be created from a `Vec` of `LineString`s or from an
13/// Iterator which yields `LineString`s. Iterating over this
14/// object yields the component `LineString`s.
15///
16/// # Semantics
17///
18/// The _boundary_ of a `MultiLineString` is obtained by
19/// applying the “mod 2” union rule: A `Point` is in the
20/// boundary of a `MultiLineString` if it is in the
21/// boundaries of an odd number of elements of the
22/// `MultiLineString`.
23///
24/// The _interior_ of a `MultiLineString` is the union of
25/// the interior, and boundary of the constituent
26/// `LineString`s, _except_ for the boundary as defined
27/// above. In other words, it is the set difference of the
28/// boundary from the union of the interior and boundary of
29/// the constituents.
30///
31/// A `MultiLineString` is _simple_ if and only if all of
32/// its elements are simple and the only intersections
33/// between any two elements occur at `Point`s that are on
34/// the boundaries of both elements. A `MultiLineString` is
35/// _closed_ if all of its elements are closed. The boundary
36/// of a closed `MultiLineString` is always empty.
37#[derive(Eq, PartialEq, Clone, Hash)]
38#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
39pub struct MultiLineString<T: CoordNum = f64>(pub Vec<LineString<T>>);
40
41impl<T: CoordNum> MultiLineString<T> {
42    /// Instantiate Self from the raw content value
43    pub fn new(value: Vec<LineString<T>>) -> Self {
44        Self(value)
45    }
46
47    /// True if the MultiLineString is empty or if all of its LineStrings are closed - see
48    /// [`LineString::is_closed`].
49    ///
50    /// # Examples
51    ///
52    /// ```
53    /// use geo_types::{MultiLineString, LineString, line_string};
54    ///
55    /// let open_line_string: LineString<f32> = line_string![(x: 0., y: 0.), (x: 5., y: 0.)];
56    /// assert!(!MultiLineString::new(vec![open_line_string.clone()]).is_closed());
57    ///
58    /// let closed_line_string: LineString<f32> = line_string![(x: 0., y: 0.), (x: 5., y: 0.), (x: 0., y: 0.)];
59    /// assert!(MultiLineString::new(vec![closed_line_string.clone()]).is_closed());
60    ///
61    /// // MultiLineString is not closed if *any* of it's LineStrings are not closed
62    /// assert!(!MultiLineString::new(vec![open_line_string, closed_line_string]).is_closed());
63    ///
64    /// // An empty MultiLineString is closed
65    /// assert!(MultiLineString::<f32>::new(vec![]).is_closed());
66    /// ```
67    pub fn is_closed(&self) -> bool {
68        // Note: Unlike JTS et al, we consider an empty MultiLineString as closed.
69        self.iter().all(LineString::is_closed)
70    }
71}
72
73impl<T: CoordNum, ILS: Into<LineString<T>>> From<ILS> for MultiLineString<T> {
74    fn from(ls: ILS) -> Self {
75        Self(vec![ls.into()])
76    }
77}
78
79impl<T: CoordNum, ILS: Into<LineString<T>>> FromIterator<ILS> for MultiLineString<T> {
80    fn from_iter<I: IntoIterator<Item = ILS>>(iter: I) -> Self {
81        Self(iter.into_iter().map(|ls| ls.into()).collect())
82    }
83}
84
85impl<T: CoordNum> IntoIterator for MultiLineString<T> {
86    type Item = LineString<T>;
87    type IntoIter = ::alloc::vec::IntoIter<LineString<T>>;
88
89    fn into_iter(self) -> Self::IntoIter {
90        self.0.into_iter()
91    }
92}
93
94impl<'a, T: CoordNum> IntoIterator for &'a MultiLineString<T> {
95    type Item = &'a LineString<T>;
96    type IntoIter = ::alloc::slice::Iter<'a, LineString<T>>;
97
98    fn into_iter(self) -> Self::IntoIter {
99        (self.0).iter()
100    }
101}
102
103impl<'a, T: CoordNum> IntoIterator for &'a mut MultiLineString<T> {
104    type Item = &'a mut LineString<T>;
105    type IntoIter = ::alloc::slice::IterMut<'a, LineString<T>>;
106
107    fn into_iter(self) -> Self::IntoIter {
108        (self.0).iter_mut()
109    }
110}
111
112impl<T: CoordNum> MultiLineString<T> {
113    pub fn iter(&self) -> impl Iterator<Item = &LineString<T>> {
114        self.0.iter()
115    }
116
117    pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut LineString<T>> {
118        self.0.iter_mut()
119    }
120}
121
122#[cfg(feature = "multithreading")]
123impl<T: CoordNum + Send> IntoParallelIterator for MultiLineString<T> {
124    type Item = LineString<T>;
125    type Iter = rayon::vec::IntoIter<LineString<T>>;
126
127    fn into_par_iter(self) -> Self::Iter {
128        self.0.into_par_iter()
129    }
130}
131
132#[cfg(feature = "multithreading")]
133impl<'a, T: CoordNum + Sync> IntoParallelIterator for &'a MultiLineString<T> {
134    type Item = &'a LineString<T>;
135    type Iter = rayon::slice::Iter<'a, LineString<T>>;
136
137    fn into_par_iter(self) -> Self::Iter {
138        self.0.par_iter()
139    }
140}
141
142#[cfg(feature = "multithreading")]
143impl<'a, T: CoordNum + Send + Sync> IntoParallelIterator for &'a mut MultiLineString<T> {
144    type Item = &'a mut LineString<T>;
145    type Iter = rayon::slice::IterMut<'a, LineString<T>>;
146
147    fn into_par_iter(self) -> Self::Iter {
148        self.0.par_iter_mut()
149    }
150}
151
152#[cfg(any(feature = "approx", test))]
153mod approx_integration {
154    use super::*;
155    use approx::{AbsDiffEq, RelativeEq, UlpsEq};
156
157    impl<T> RelativeEq for MultiLineString<T>
158    where
159        T: CoordNum + RelativeEq<Epsilon = T>,
160    {
161        #[inline]
162        fn default_max_relative() -> Self::Epsilon {
163            T::default_max_relative()
164        }
165
166        /// Equality assertion within a relative limit.
167        ///
168        /// # Examples
169        ///
170        /// ```
171        /// use geo_types::{MultiLineString, line_string};
172        ///
173        /// let a = MultiLineString::new(vec![line_string![(x: 0., y: 0.), (x: 10., y: 10.)]]);
174        /// let b = MultiLineString::new(vec![line_string![(x: 0., y: 0.), (x: 10.01, y: 10.)]]);
175        ///
176        /// approx::assert_relative_eq!(a, b, max_relative=0.1);
177        /// approx::assert_relative_ne!(a, b, max_relative=0.0001);
178        /// ```
179        #[inline]
180        fn relative_eq(
181            &self,
182            other: &Self,
183            epsilon: Self::Epsilon,
184            max_relative: Self::Epsilon,
185        ) -> bool {
186            if self.0.len() != other.0.len() {
187                return false;
188            }
189
190            let mut mp_zipper = self.iter().zip(other.iter());
191            mp_zipper.all(|(lhs, rhs)| lhs.relative_eq(rhs, epsilon, max_relative))
192        }
193    }
194
195    impl<T> AbsDiffEq for MultiLineString<T>
196    where
197        T: CoordNum + AbsDiffEq<Epsilon = T>,
198    {
199        type Epsilon = T;
200
201        #[inline]
202        fn default_epsilon() -> Self::Epsilon {
203            T::default_epsilon()
204        }
205
206        /// Equality assertion with an absolute limit.
207        ///
208        /// # Examples
209        ///
210        /// ```
211        /// use geo_types::{MultiLineString, line_string};
212        ///
213        /// let a = MultiLineString::new(vec![line_string![(x: 0., y: 0.), (x: 10., y: 10.)]]);
214        /// let b = MultiLineString::new(vec![line_string![(x: 0., y: 0.), (x: 10.01, y: 10.)]]);
215        ///
216        /// approx::abs_diff_eq!(a, b, epsilon=0.1);
217        /// approx::abs_diff_ne!(a, b, epsilon=0.001);
218        /// ```
219        #[inline]
220        fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool {
221            if self.0.len() != other.0.len() {
222                return false;
223            }
224
225            self.into_iter()
226                .zip(other)
227                .all(|(lhs, rhs)| lhs.abs_diff_eq(rhs, epsilon))
228        }
229    }
230
231    impl<T> UlpsEq for MultiLineString<T>
232    where
233        T: CoordNum + UlpsEq<Epsilon = T>,
234    {
235        fn default_max_ulps() -> u32 {
236            T::default_max_ulps()
237        }
238
239        fn ulps_eq(&self, other: &Self, epsilon: Self::Epsilon, max_ulps: u32) -> bool {
240            if self.0.len() != other.0.len() {
241                return false;
242            }
243            self.into_iter()
244                .zip(other)
245                .all(|(lhs, rhs)| lhs.ulps_eq(rhs, epsilon, max_ulps))
246        }
247    }
248}
249
250#[cfg(test)]
251mod test {
252    use super::*;
253    use crate::{line_string, wkt};
254
255    #[cfg(feature = "multithreading")]
256    #[test]
257    fn test_multithreading_linestring() {
258        let multi: MultiLineString<i32> = wkt! {
259            MULTILINESTRING((0 0,2 0,1 2,0 0), (10 10,12 10,11 12,10 10))
260        };
261        let mut multimut: MultiLineString<i32> = wkt! {
262            MULTILINESTRING((0 0,2 0,1 2,0 0), (10 10,12 10,11 12,10 10))
263        };
264        multi.par_iter().for_each(|_p| ());
265        multimut.par_iter_mut().for_each(|_p| ());
266        let _ = &multi.into_par_iter().for_each(|_p| ());
267        let _ = &mut multimut.par_iter_mut().for_each(|_p| ());
268    }
269
270    #[test]
271    fn test_iter() {
272        let multi: MultiLineString<i32> = wkt! {
273            MULTILINESTRING((0 0,2 0,1 2,0 0), (10 10,12 10,11 12,10 10))
274        };
275
276        let mut first = true;
277        for p in &multi {
278            if first {
279                assert_eq!(p, &wkt! { LINESTRING(0 0,2 0,1 2,0 0) });
280                first = false;
281            } else {
282                assert_eq!(p, &wkt! { LINESTRING(10 10,12 10,11 12,10 10) });
283            }
284        }
285
286        // Do it again to prove that `multi` wasn't `moved`.
287        first = true;
288        for p in &multi {
289            if first {
290                assert_eq!(p, &wkt! { LINESTRING(0 0,2 0,1 2,0 0) });
291                first = false;
292            } else {
293                assert_eq!(p, &wkt! { LINESTRING(10 10,12 10,11 12,10 10) });
294            }
295        }
296    }
297
298    #[test]
299    fn test_iter_mut() {
300        let mut multi = MultiLineString::new(vec![
301            line_string![(x: 0, y: 0), (x: 2, y: 0), (x: 1, y: 2), (x:0, y:0)],
302            line_string![(x: 10, y: 10), (x: 12, y: 10), (x: 11, y: 12), (x:10, y:10)],
303        ]);
304
305        for line_string in &mut multi {
306            for coord in line_string {
307                coord.x += 1;
308                coord.y += 1;
309            }
310        }
311
312        for line_string in multi.iter_mut() {
313            for coord in line_string {
314                coord.x += 1;
315                coord.y += 1;
316            }
317        }
318
319        let mut first = true;
320        for p in &multi {
321            if first {
322                assert_eq!(
323                    p,
324                    &line_string![(x: 2, y: 2), (x: 4, y: 2), (x: 3, y: 4), (x:2, y:2)]
325                );
326                first = false;
327            } else {
328                assert_eq!(
329                    p,
330                    &line_string![(x: 12, y: 12), (x: 14, y: 12), (x: 13, y: 14), (x:12, y:12)]
331                );
332            }
333        }
334    }
335}