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 empty() -> Self {
49 Self::new(Vec::new())
50 }
51
52 pub fn is_closed(&self) -> bool {
73 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 #[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 #[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 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}