geo_types/geometry/line_string.rs
1use crate::{Coord, CoordNum, Line, Point, Triangle};
2use alloc::vec;
3use alloc::vec::Vec;
4use core::iter::FromIterator;
5use core::ops::{Index, IndexMut};
6
7/// An ordered collection of [`Coord`]s, representing a path between locations.
8/// To be valid, a `LineString` must be empty, or have two or more coords.
9///
10/// # Semantics
11///
12/// 1. A [`LineString`] is _closed_ if it is empty, **or** if the first and last coordinates are the same.
13/// 2. The _boundary_ of a [`LineString`] is either:
14/// - **empty** if it is _closed_ (see **1**) **or**
15/// - contains the **start** and **end** coordinates.
16/// 3. The _interior_ is the (infinite) set of all coordinates along the [`LineString`], _not including_ the boundary.
17/// 4. A [`LineString`] is _simple_ if it does not intersect except **optionally** at the first and last coordinates (in which case it is also _closed_, see **1**).
18/// 5. A _simple_ **and** _closed_ [`LineString`] is a `LinearRing` as defined in the OGC-SFA (but is not defined as a separate type in this crate).
19///
20/// # Validity
21///
22/// A [`LineString`] is valid if it is either empty or
23/// contains 2 or more coordinates.
24///
25/// Further, a closed [`LineString`] **must not** self-intersect. Note that its
26/// validity is **not** enforced, and operations and
27/// predicates are **undefined** on invalid `LineString`s.
28///
29/// # Examples
30/// ## Creation
31///
32/// Create a [`LineString`] by calling it directly:
33///
34/// ```
35/// use geo_types::{coord, LineString};
36///
37/// let line_string = LineString::new(vec![
38/// coord! { x: 0., y: 0. },
39/// coord! { x: 10., y: 0. },
40/// ]);
41/// ```
42///
43/// Create a [`LineString`] with the [`line_string!`][`crate::line_string!`] macro:
44///
45/// ```
46/// use geo_types::line_string;
47///
48/// let line_string = line_string![
49/// (x: 0., y: 0.),
50/// (x: 10., y: 0.),
51/// ];
52/// ```
53///
54/// By converting from a [`Vec`] of coordinate-like things:
55///
56/// ```
57/// use geo_types::LineString;
58///
59/// let line_string: LineString<f32> = vec![(0., 0.), (10., 0.)].into();
60/// ```
61///
62/// ```
63/// use geo_types::LineString;
64///
65/// let line_string: LineString = vec![[0., 0.], [10., 0.]].into();
66/// ```
67//
68/// Or by `collect`ing from a [`Coord`] iterator
69///
70/// ```
71/// use geo_types::{coord, LineString};
72///
73/// let mut coords_iter =
74/// vec![coord! { x: 0., y: 0. }, coord! { x: 10., y: 0. }].into_iter();
75///
76/// let line_string: LineString<f32> = coords_iter.collect();
77/// ```
78///
79/// ## Iteration
80/// [`LineString`] provides five iterators: [`coords`](LineString::coords), [`coords_mut`](LineString::coords_mut), [`points`](LineString::points), [`lines`](LineString::lines), and [`triangles`](LineString::triangles):
81///
82/// ```
83/// use geo_types::{coord, LineString};
84///
85/// let line_string = LineString::new(vec![
86/// coord! { x: 0., y: 0. },
87/// coord! { x: 10., y: 0. },
88/// ]);
89///
90/// line_string.coords().for_each(|coord| println!("{:?}", coord));
91///
92/// for point in line_string.points() {
93/// println!("Point x = {}, y = {}", point.x(), point.y());
94/// }
95/// ```
96///
97/// Note that its [`IntoIterator`] impl yields [`Coord`]s when looping:
98///
99/// ```
100/// use geo_types::{coord, LineString};
101///
102/// let line_string = LineString::new(vec![
103/// coord! { x: 0., y: 0. },
104/// coord! { x: 10., y: 0. },
105/// ]);
106///
107/// for coord in &line_string {
108/// println!("Coordinate x = {}, y = {}", coord.x, coord.y);
109/// }
110///
111/// for coord in line_string {
112/// println!("Coordinate x = {}, y = {}", coord.x, coord.y);
113/// }
114///
115/// ```
116/// ## Decomposition
117///
118/// You can decompose a [`LineString`] into a [`Vec`] of [`Coord`]s or [`Point`]s:
119/// ```
120/// use geo_types::{coord, LineString, Point};
121///
122/// let line_string = LineString::new(vec![
123/// coord! { x: 0., y: 0. },
124/// coord! { x: 10., y: 0. },
125/// ]);
126///
127/// let coordinate_vec = line_string.clone().into_inner();
128/// let point_vec = line_string.clone().into_points();
129///
130/// ```
131
132#[derive(Eq, PartialEq, Clone, Hash)]
133#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
134pub struct LineString<T: CoordNum = f64>(pub Vec<Coord<T>>);
135
136/// A [`Point`] iterator returned by the `points` method
137#[derive(Debug)]
138pub struct PointsIter<'a, T: CoordNum + 'a>(::core::slice::Iter<'a, Coord<T>>);
139
140impl<T: CoordNum> Iterator for PointsIter<'_, T> {
141 type Item = Point<T>;
142
143 fn next(&mut self) -> Option<Self::Item> {
144 self.0.next().map(|c| Point::from(*c))
145 }
146
147 fn size_hint(&self) -> (usize, Option<usize>) {
148 self.0.size_hint()
149 }
150}
151
152impl<T: CoordNum> ExactSizeIterator for PointsIter<'_, T> {
153 fn len(&self) -> usize {
154 self.0.len()
155 }
156}
157
158impl<T: CoordNum> DoubleEndedIterator for PointsIter<'_, T> {
159 fn next_back(&mut self) -> Option<Self::Item> {
160 self.0.next_back().map(|c| Point::from(*c))
161 }
162}
163
164/// A [`Coord`] iterator used by the `into_iter` method on a [`LineString`]
165#[derive(Debug)]
166pub struct CoordinatesIter<'a, T: CoordNum + 'a>(::core::slice::Iter<'a, Coord<T>>);
167
168impl<'a, T: CoordNum> Iterator for CoordinatesIter<'a, T> {
169 type Item = &'a Coord<T>;
170
171 fn next(&mut self) -> Option<Self::Item> {
172 self.0.next()
173 }
174
175 fn size_hint(&self) -> (usize, Option<usize>) {
176 self.0.size_hint()
177 }
178}
179
180impl<T: CoordNum> ExactSizeIterator for CoordinatesIter<'_, T> {
181 fn len(&self) -> usize {
182 self.0.len()
183 }
184}
185
186impl<T: CoordNum> DoubleEndedIterator for CoordinatesIter<'_, T> {
187 fn next_back(&mut self) -> Option<Self::Item> {
188 self.0.next_back()
189 }
190}
191
192impl<T: CoordNum> LineString<T> {
193 /// Instantiate Self from the raw content value
194 pub fn new(value: Vec<Coord<T>>) -> Self {
195 Self(value)
196 }
197
198 /// Return an iterator yielding the coordinates of a [`LineString`] as [`Point`]s
199 #[deprecated(note = "Use points() instead")]
200 pub fn points_iter(&self) -> PointsIter<T> {
201 PointsIter(self.0.iter())
202 }
203
204 /// Return an iterator yielding the coordinates of a [`LineString`] as [`Point`]s
205 pub fn points(&self) -> PointsIter<T> {
206 PointsIter(self.0.iter())
207 }
208
209 /// Return an iterator yielding the members of a [`LineString`] as [`Coord`]s
210 pub fn coords(&self) -> impl DoubleEndedIterator<Item = &Coord<T>> {
211 self.0.iter()
212 }
213
214 /// Return an iterator yielding the coordinates of a [`LineString`] as mutable [`Coord`]s
215 pub fn coords_mut(&mut self) -> impl DoubleEndedIterator<Item = &mut Coord<T>> {
216 self.0.iter_mut()
217 }
218
219 /// Return the coordinates of a [`LineString`] as a [`Vec`] of [`Point`]s
220 pub fn into_points(self) -> Vec<Point<T>> {
221 self.0.into_iter().map(Point::from).collect()
222 }
223
224 /// Return the coordinates of a [`LineString`] as a [`Vec`] of [`Coord`]s
225 pub fn into_inner(self) -> Vec<Coord<T>> {
226 self.0
227 }
228
229 /// Return an iterator yielding one [`Line`] for each line segment
230 /// in the [`LineString`].
231 ///
232 /// # Examples
233 ///
234 /// ```
235 /// use geo_types::{wkt, Line, LineString};
236 ///
237 /// let line_string = wkt!(LINESTRING(0 0,5 0,7 9));
238 /// let mut lines = line_string.lines();
239 ///
240 /// assert_eq!(
241 /// Some(Line::new((0, 0), (5, 0))),
242 /// lines.next()
243 /// );
244 /// assert_eq!(
245 /// Some(Line::new((5, 0), (7, 9))),
246 /// lines.next()
247 /// );
248 /// assert!(lines.next().is_none());
249 /// ```
250 pub fn lines(&'_ self) -> impl ExactSizeIterator<Item = Line<T>> + '_ {
251 self.0.windows(2).map(|w| {
252 // slice::windows(N) is guaranteed to yield a slice with exactly N elements
253 unsafe { Line::new(*w.get_unchecked(0), *w.get_unchecked(1)) }
254 })
255 }
256
257 /// Return an iterator yielding one [`Line`] for each line segment in the [`LineString`],
258 /// starting from the **end** point of the LineString, working towards the start.
259 ///
260 /// Note: This is like [`Self::lines`], but the sequence **and** the orientation of
261 /// segments are reversed.
262 ///
263 /// # Examples
264 ///
265 /// ```
266 /// use geo_types::{wkt, Line, LineString};
267 ///
268 /// let line_string = wkt!(LINESTRING(0 0,5 0,7 9));
269 /// let mut lines = line_string.rev_lines();
270 ///
271 /// assert_eq!(
272 /// Some(Line::new((7, 9), (5, 0))),
273 /// lines.next()
274 /// );
275 /// assert_eq!(
276 /// Some(Line::new((5, 0), (0, 0))),
277 /// lines.next()
278 /// );
279 /// assert!(lines.next().is_none());
280 /// ```
281 pub fn rev_lines(&'_ self) -> impl ExactSizeIterator<Item = Line<T>> + '_ {
282 self.0.windows(2).rev().map(|w| {
283 // slice::windows(N) is guaranteed to yield a slice with exactly N elements
284 unsafe { Line::new(*w.get_unchecked(1), *w.get_unchecked(0)) }
285 })
286 }
287
288 /// An iterator which yields the coordinates of a [`LineString`] as [Triangle]s
289 pub fn triangles(&'_ self) -> impl ExactSizeIterator<Item = Triangle<T>> + '_ {
290 self.0.windows(3).map(|w| {
291 // slice::windows(N) is guaranteed to yield a slice with exactly N elements
292 unsafe {
293 Triangle::new(
294 *w.get_unchecked(0),
295 *w.get_unchecked(1),
296 *w.get_unchecked(2),
297 )
298 }
299 })
300 }
301
302 /// Close the [`LineString`]. Specifically, if the [`LineString`] has at least one [`Coord`], and
303 /// the value of the first [`Coord`] **does not** equal the value of the last [`Coord`], then a
304 /// new [`Coord`] is added to the end with the value of the first [`Coord`].
305 pub fn close(&mut self) {
306 if !self.is_closed() {
307 // by definition, we treat empty LineString's as closed.
308 debug_assert!(!self.0.is_empty());
309 self.0.push(self.0[0]);
310 }
311 }
312
313 /// Return the number of coordinates in the [`LineString`].
314 ///
315 /// # Examples
316 ///
317 /// ```
318 /// use geo_types::LineString;
319 ///
320 /// let mut coords = vec![(0., 0.), (5., 0.), (7., 9.)];
321 /// let line_string: LineString<f32> = coords.into_iter().collect();
322 ///
323 /// # #[allow(deprecated)]
324 /// # {
325 /// assert_eq!(3, line_string.num_coords());
326 /// # }
327 /// ```
328 #[deprecated(note = "Use geo::CoordsIter::coords_count instead")]
329 pub fn num_coords(&self) -> usize {
330 self.0.len()
331 }
332
333 /// Checks if the linestring is closed; i.e. it is
334 /// either empty or, the first and last points are the
335 /// same.
336 ///
337 /// # Examples
338 ///
339 /// ```
340 /// use geo_types::LineString;
341 ///
342 /// let mut coords = vec![(0., 0.), (5., 0.), (0., 0.)];
343 /// let line_string: LineString<f32> = coords.into_iter().collect();
344 /// assert!(line_string.is_closed());
345 /// ```
346 ///
347 /// Note that we diverge from some libraries ([JTS](https://locationtech.github.io/jts/javadoc/org/locationtech/jts/geom/LinearRing.html) et al), which have a `LinearRing` type,
348 /// separate from [`LineString`]. Those libraries treat an empty `LinearRing` as **closed** by
349 /// definition, while treating an empty `LineString` as **open**. Since we don't have a separate
350 /// `LinearRing` type, and use a [`LineString`] in its place, we adopt the JTS `LinearRing` `is_closed`
351 /// behavior in all places: that is, **we consider an empty [`LineString`] as closed**.
352 ///
353 /// This is expected when used in the context of a [`Polygon.exterior`](crate::Polygon::exterior) and elsewhere; And there
354 /// seems to be no reason to maintain the separate behavior for [`LineString`]s used in
355 /// non-`LinearRing` contexts.
356 pub fn is_closed(&self) -> bool {
357 self.0.first() == self.0.last()
358 }
359}
360
361/// Turn a [`Vec`] of [`Point`]-like objects into a [`LineString`].
362impl<T: CoordNum, IC: Into<Coord<T>>> From<Vec<IC>> for LineString<T> {
363 fn from(v: Vec<IC>) -> Self {
364 Self(v.into_iter().map(|c| c.into()).collect())
365 }
366}
367
368impl<T: CoordNum> From<Line<T>> for LineString<T> {
369 fn from(line: Line<T>) -> Self {
370 LineString::from(&line)
371 }
372}
373
374impl<T: CoordNum> From<&Line<T>> for LineString<T> {
375 fn from(line: &Line<T>) -> Self {
376 Self(vec![line.start, line.end])
377 }
378}
379
380/// Turn an iterator of [`Point`]-like objects into a [`LineString`].
381impl<T: CoordNum, IC: Into<Coord<T>>> FromIterator<IC> for LineString<T> {
382 fn from_iter<I: IntoIterator<Item = IC>>(iter: I) -> Self {
383 Self(iter.into_iter().map(|c| c.into()).collect())
384 }
385}
386
387/// Iterate over all the [`Coord`]s in this [`LineString`].
388impl<T: CoordNum> IntoIterator for LineString<T> {
389 type Item = Coord<T>;
390 type IntoIter = ::alloc::vec::IntoIter<Coord<T>>;
391
392 fn into_iter(self) -> Self::IntoIter {
393 self.0.into_iter()
394 }
395}
396
397impl<'a, T: CoordNum> IntoIterator for &'a LineString<T> {
398 type Item = &'a Coord<T>;
399 type IntoIter = CoordinatesIter<'a, T>;
400
401 fn into_iter(self) -> Self::IntoIter {
402 CoordinatesIter(self.0.iter())
403 }
404}
405
406/// Mutably iterate over all the [`Coord`]s in this [`LineString`]
407impl<'a, T: CoordNum> IntoIterator for &'a mut LineString<T> {
408 type Item = &'a mut Coord<T>;
409 type IntoIter = ::core::slice::IterMut<'a, Coord<T>>;
410
411 fn into_iter(self) -> ::core::slice::IterMut<'a, Coord<T>> {
412 self.0.iter_mut()
413 }
414}
415
416impl<T: CoordNum> Index<usize> for LineString<T> {
417 type Output = Coord<T>;
418
419 fn index(&self, index: usize) -> &Coord<T> {
420 self.0.index(index)
421 }
422}
423
424impl<T: CoordNum> IndexMut<usize> for LineString<T> {
425 fn index_mut(&mut self, index: usize) -> &mut Coord<T> {
426 self.0.index_mut(index)
427 }
428}
429
430#[cfg(any(feature = "approx", test))]
431mod approx_integration {
432 use super::*;
433 use approx::{AbsDiffEq, RelativeEq, UlpsEq};
434
435 impl<T> RelativeEq for LineString<T>
436 where
437 T: CoordNum + RelativeEq<Epsilon = T>,
438 {
439 #[inline]
440 fn default_max_relative() -> Self::Epsilon {
441 T::default_max_relative()
442 }
443
444 /// Equality assertion within a relative limit.
445 ///
446 /// # Examples
447 ///
448 /// ```
449 /// use geo_types::LineString;
450 ///
451 /// let mut coords_a = vec![(0., 0.), (5., 0.), (7., 9.)];
452 /// let a: LineString<f32> = coords_a.into_iter().collect();
453 ///
454 /// let mut coords_b = vec![(0., 0.), (5., 0.), (7.001, 9.)];
455 /// let b: LineString<f32> = coords_b.into_iter().collect();
456 ///
457 /// approx::assert_relative_eq!(a, b, max_relative=0.1)
458 /// ```
459 ///
460 fn relative_eq(
461 &self,
462 other: &Self,
463 epsilon: Self::Epsilon,
464 max_relative: Self::Epsilon,
465 ) -> bool {
466 if self.0.len() != other.0.len() {
467 return false;
468 }
469
470 let points_zipper = self.points().zip(other.points());
471 for (lhs, rhs) in points_zipper {
472 if lhs.relative_ne(&rhs, epsilon, max_relative) {
473 return false;
474 }
475 }
476
477 true
478 }
479 }
480
481 impl<T> AbsDiffEq for LineString<T>
482 where
483 T: CoordNum + AbsDiffEq<Epsilon = T>,
484 {
485 type Epsilon = T;
486
487 #[inline]
488 fn default_epsilon() -> Self::Epsilon {
489 T::default_epsilon()
490 }
491
492 /// Equality assertion with an absolute limit.
493 ///
494 /// # Examples
495 ///
496 /// ```
497 /// use geo_types::LineString;
498 ///
499 /// let mut coords_a = vec![(0., 0.), (5., 0.), (7., 9.)];
500 /// let a: LineString<f32> = coords_a.into_iter().collect();
501 ///
502 /// let mut coords_b = vec![(0., 0.), (5., 0.), (7.001, 9.)];
503 /// let b: LineString<f32> = coords_b.into_iter().collect();
504 ///
505 /// approx::assert_relative_eq!(a, b, epsilon=0.1)
506 /// ```
507 fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool {
508 if self.0.len() != other.0.len() {
509 return false;
510 }
511 let mut points_zipper = self.points().zip(other.points());
512 points_zipper.all(|(lhs, rhs)| lhs.abs_diff_eq(&rhs, epsilon))
513 }
514 }
515
516 impl<T> UlpsEq for LineString<T>
517 where
518 T: CoordNum + UlpsEq<Epsilon = T>,
519 {
520 fn default_max_ulps() -> u32 {
521 T::default_max_ulps()
522 }
523
524 fn ulps_eq(&self, other: &Self, epsilon: Self::Epsilon, max_ulps: u32) -> bool {
525 if self.0.len() != other.0.len() {
526 return false;
527 }
528 let mut points_zipper = self.points().zip(other.points());
529 points_zipper.all(|(lhs, rhs)| lhs.ulps_eq(&rhs, epsilon, max_ulps))
530 }
531 }
532}
533
534#[cfg(any(
535 feature = "rstar_0_8",
536 feature = "rstar_0_9",
537 feature = "rstar_0_10",
538 feature = "rstar_0_11",
539 feature = "rstar_0_12"
540))]
541macro_rules! impl_rstar_line_string {
542 ($rstar:ident) => {
543 impl<T> ::$rstar::RTreeObject for LineString<T>
544 where
545 T: ::num_traits::Float + ::$rstar::RTreeNum,
546 {
547 type Envelope = ::$rstar::AABB<Point<T>>;
548
549 fn envelope(&self) -> Self::Envelope {
550 use num_traits::Bounded;
551 let bounding_rect = crate::private_utils::line_string_bounding_rect(self);
552 match bounding_rect {
553 None => ::$rstar::AABB::from_corners(
554 Point::new(Bounded::min_value(), Bounded::min_value()),
555 Point::new(Bounded::max_value(), Bounded::max_value()),
556 ),
557 Some(b) => ::$rstar::AABB::from_corners(
558 Point::new(b.min().x, b.min().y),
559 Point::new(b.max().x, b.max().y),
560 ),
561 }
562 }
563 }
564
565 impl<T> ::$rstar::PointDistance for LineString<T>
566 where
567 T: ::num_traits::Float + ::$rstar::RTreeNum,
568 {
569 fn distance_2(&self, point: &Point<T>) -> T {
570 let d = crate::private_utils::point_line_string_euclidean_distance(*point, self);
571 if d == T::zero() {
572 d
573 } else {
574 d.powi(2)
575 }
576 }
577 }
578 };
579}
580
581#[cfg(feature = "rstar_0_8")]
582impl_rstar_line_string!(rstar_0_8);
583
584#[cfg(feature = "rstar_0_9")]
585impl_rstar_line_string!(rstar_0_9);
586
587#[cfg(feature = "rstar_0_10")]
588impl_rstar_line_string!(rstar_0_10);
589
590#[cfg(feature = "rstar_0_11")]
591impl_rstar_line_string!(rstar_0_11);
592
593#[cfg(feature = "rstar_0_12")]
594impl_rstar_line_string!(rstar_0_12);
595
596#[cfg(test)]
597mod test {
598 use super::*;
599 use crate::coord;
600 use approx::{AbsDiffEq, RelativeEq};
601
602 #[test]
603 fn test_exact_size() {
604 // see https://github.com/georust/geo/issues/762
605 let first = coord! { x: 0., y: 0. };
606 let ls = LineString::new(vec![first, coord! { x: 10., y: 0. }]);
607
608 // reference to force the `impl IntoIterator for &LineString` impl, giving a `CoordinatesIter`
609 for c in (&ls).into_iter().rev().skip(1).rev() {
610 assert_eq!(&first, c);
611 }
612 for p in ls.points().rev().skip(1).rev() {
613 assert_eq!(Point::from(first), p);
614 }
615 }
616
617 #[test]
618 fn test_abs_diff_eq() {
619 let delta = 1e-6;
620
621 let coords = vec![(0., 0.), (5., 0.), (10., 10.)];
622 let ls: LineString<f32> = coords.into_iter().collect();
623
624 let coords_x = vec![(0., 0.), (5. + delta, 0.), (10., 10.)];
625 let ls_x: LineString<f32> = coords_x.into_iter().collect();
626 assert!(ls.abs_diff_eq(&ls_x, 1e-2));
627 assert!(ls.abs_diff_ne(&ls_x, 1e-12));
628
629 let coords_y = vec![(0., 0.), (5., 0. + delta), (10., 10.)];
630 let ls_y: LineString<f32> = coords_y.into_iter().collect();
631 assert!(ls.abs_diff_eq(&ls_y, 1e-2));
632 assert!(ls.abs_diff_ne(&ls_y, 1e-12));
633
634 // Undersized, but otherwise equal.
635 let coords_x = vec![(0., 0.), (5., 0.)];
636 let ls_under: LineString<f32> = coords_x.into_iter().collect();
637 assert!(ls.abs_diff_ne(&ls_under, 1.));
638
639 // Oversized, but otherwise equal.
640 let coords_x = vec![(0., 0.), (5., 0.), (10., 10.), (10., 100.)];
641 let ls_oversized: LineString<f32> = coords_x.into_iter().collect();
642 assert!(ls.abs_diff_ne(&ls_oversized, 1.));
643 }
644
645 #[test]
646 fn test_relative_eq() {
647 let delta = 1e-6;
648
649 let coords = vec![(0., 0.), (5., 0.), (10., 10.)];
650 let ls: LineString<f32> = coords.into_iter().collect();
651
652 let coords_x = vec![(0., 0.), (5. + delta, 0.), (10., 10.)];
653 let ls_x: LineString<f32> = coords_x.into_iter().collect();
654 assert!(ls.relative_eq(&ls_x, 1e-2, 1e-2));
655 assert!(ls.relative_ne(&ls_x, 1e-12, 1e-12));
656
657 let coords_y = vec![(0., 0.), (5., 0. + delta), (10., 10.)];
658 let ls_y: LineString<f32> = coords_y.into_iter().collect();
659 assert!(ls.relative_eq(&ls_y, 1e-2, 1e-2));
660 assert!(ls.relative_ne(&ls_y, 1e-12, 1e-12));
661
662 // Undersized, but otherwise equal.
663 let coords_x = vec![(0., 0.), (5., 0.)];
664 let ls_under: LineString<f32> = coords_x.into_iter().collect();
665 assert!(ls.relative_ne(&ls_under, 1., 1.));
666
667 // Oversized, but otherwise equal.
668 let coords_x = vec![(0., 0.), (5., 0.), (10., 10.), (10., 100.)];
669 let ls_oversized: LineString<f32> = coords_x.into_iter().collect();
670 assert!(ls.relative_ne(&ls_oversized, 1., 1.));
671 }
672
673 #[test]
674 fn should_be_built_from_line() {
675 let start = coord! { x: 0, y: 0 };
676 let end = coord! { x: 10, y: 10 };
677 let line = Line::new(start, end);
678 let expected = LineString::new(vec![start, end]);
679
680 assert_eq!(expected, LineString::from(line));
681
682 let start = coord! { x: 10., y: 0.5 };
683 let end = coord! { x: 10000., y: 10.4 };
684 let line = Line::new(start, end);
685 let expected = LineString::new(vec![start, end]);
686
687 assert_eq!(expected, LineString::from(line));
688 }
689}