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#[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 pub fn new(value: Vec<LineString<T>>) -> Self {
44 Self(value)
45 }
46
47 pub fn is_closed(&self) -> bool {
68 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 #[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 #[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 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}