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}