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