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#[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 pub fn new(value: Vec<LineString<T>>) -> Self {
46 Self(value)
47 }
48
49 pub fn empty() -> Self {
51 Self::new(Vec::new())
52 }
53
54 pub fn is_closed(&self) -> bool {
75 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 #[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 #[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 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 assert_eq!(mls[0], wkt! { LINESTRING(0. 0., 1. 1.) });
370 assert_eq!(mls[1], wkt! { LINESTRING(2. 2., 3. 3.) });
371
372 mls[1] = wkt! { LINESTRING(100. 100., 101. 101.) };
374 assert_eq!(mls[1], wkt! { LINESTRING(100. 100., 101. 101.) });
375
376 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}