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