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    /// Returns a LineString with the given coordinates
194    pub fn new(value: Vec<Coord<T>>) -> Self {
195        Self(value)
196    }
197
198    /// Returns an empty LineString
199    pub fn empty() -> Self {
200        Self::new(Vec::new())
201    }
202
203    /// Return an iterator yielding the coordinates of a [`LineString`] as [`Point`]s
204    #[deprecated(note = "Use points() instead")]
205    pub fn points_iter(&self) -> PointsIter<T> {
206        PointsIter(self.0.iter())
207    }
208
209    /// Return an iterator yielding the coordinates of a [`LineString`] as [`Point`]s
210    pub fn points(&self) -> PointsIter<T> {
211        PointsIter(self.0.iter())
212    }
213
214    /// Return an iterator yielding the members of a [`LineString`] as [`Coord`]s
215    pub fn coords(&self) -> impl DoubleEndedIterator<Item = &Coord<T>> {
216        self.0.iter()
217    }
218
219    /// Return an iterator yielding the coordinates of a [`LineString`] as mutable [`Coord`]s
220    pub fn coords_mut(&mut self) -> impl DoubleEndedIterator<Item = &mut Coord<T>> {
221        self.0.iter_mut()
222    }
223
224    /// Return the coordinates of a [`LineString`] as a [`Vec`] of [`Point`]s
225    pub fn into_points(self) -> Vec<Point<T>> {
226        self.0.into_iter().map(Point::from).collect()
227    }
228
229    /// Return the coordinates of a [`LineString`] as a [`Vec`] of [`Coord`]s
230    pub fn into_inner(self) -> Vec<Coord<T>> {
231        self.0
232    }
233
234    /// Return an iterator yielding one [`Line`] for each line segment
235    /// in the [`LineString`].
236    ///
237    /// # Examples
238    ///
239    /// ```
240    /// use geo_types::{wkt, Line, LineString};
241    ///
242    /// let line_string = wkt!(LINESTRING(0 0,5 0,7 9));
243    /// let mut lines = line_string.lines();
244    ///
245    /// assert_eq!(
246    ///     Some(Line::new((0, 0), (5, 0))),
247    ///     lines.next()
248    /// );
249    /// assert_eq!(
250    ///     Some(Line::new((5, 0), (7, 9))),
251    ///     lines.next()
252    /// );
253    /// assert!(lines.next().is_none());
254    /// ```
255    pub fn lines(&'_ self) -> impl ExactSizeIterator<Item = Line<T>> + '_ {
256        self.0.windows(2).map(|w| {
257            // slice::windows(N) is guaranteed to yield a slice with exactly N elements
258            unsafe { Line::new(*w.get_unchecked(0), *w.get_unchecked(1)) }
259        })
260    }
261
262    /// Return an iterator yielding one [`Line`] for each line segment in the [`LineString`],
263    /// starting from the **end** point of the LineString, working towards the start.
264    ///
265    /// Note: This is like [`Self::lines`], but the sequence **and** the orientation of
266    /// segments are reversed.
267    ///
268    /// # Examples
269    ///
270    /// ```
271    /// use geo_types::{wkt, Line, LineString};
272    ///
273    /// let line_string = wkt!(LINESTRING(0 0,5 0,7 9));
274    /// let mut lines = line_string.rev_lines();
275    ///
276    /// assert_eq!(
277    ///     Some(Line::new((7, 9), (5, 0))),
278    ///     lines.next()
279    /// );
280    /// assert_eq!(
281    ///     Some(Line::new((5, 0), (0, 0))),
282    ///     lines.next()
283    /// );
284    /// assert!(lines.next().is_none());
285    /// ```
286    pub fn rev_lines(&'_ self) -> impl ExactSizeIterator<Item = Line<T>> + '_ {
287        self.0.windows(2).rev().map(|w| {
288            // slice::windows(N) is guaranteed to yield a slice with exactly N elements
289            unsafe { Line::new(*w.get_unchecked(1), *w.get_unchecked(0)) }
290        })
291    }
292
293    /// An iterator which yields the coordinates of a [`LineString`] as [Triangle]s
294    pub fn triangles(&'_ self) -> impl ExactSizeIterator<Item = Triangle<T>> + '_ {
295        self.0.windows(3).map(|w| {
296            // slice::windows(N) is guaranteed to yield a slice with exactly N elements
297            unsafe {
298                Triangle::new(
299                    *w.get_unchecked(0),
300                    *w.get_unchecked(1),
301                    *w.get_unchecked(2),
302                )
303            }
304        })
305    }
306
307    /// Close the [`LineString`]. Specifically, if the [`LineString`] has at least one [`Coord`], and
308    /// the value of the first [`Coord`] **does not** equal the value of the last [`Coord`], then a
309    /// new [`Coord`] is added to the end with the value of the first [`Coord`].
310    pub fn close(&mut self) {
311        if !self.is_closed() {
312            // by definition, we treat empty LineString's as closed.
313            debug_assert!(!self.0.is_empty());
314            self.0.push(self.0[0]);
315        }
316    }
317
318    /// Return the number of coordinates in the [`LineString`].
319    ///
320    /// # Examples
321    ///
322    /// ```
323    /// use geo_types::LineString;
324    ///
325    /// let mut coords = vec![(0., 0.), (5., 0.), (7., 9.)];
326    /// let line_string: LineString<f32> = coords.into_iter().collect();
327    ///
328    /// # #[allow(deprecated)]
329    /// # {
330    /// assert_eq!(3, line_string.num_coords());
331    /// # }
332    /// ```
333    #[deprecated(note = "Use geo::CoordsIter::coords_count instead")]
334    pub fn num_coords(&self) -> usize {
335        self.0.len()
336    }
337
338    /// Checks if the linestring is closed; i.e. it is
339    /// either empty or, the first and last points are the
340    /// same.
341    ///
342    /// # Examples
343    ///
344    /// ```
345    /// use geo_types::LineString;
346    ///
347    /// let mut coords = vec![(0., 0.), (5., 0.), (0., 0.)];
348    /// let line_string: LineString<f32> = coords.into_iter().collect();
349    /// assert!(line_string.is_closed());
350    /// ```
351    ///
352    /// 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,
353    /// separate from [`LineString`]. Those libraries treat an empty `LinearRing` as **closed** by
354    /// definition, while treating an empty `LineString` as **open**. Since we don't have a separate
355    /// `LinearRing` type, and use a [`LineString`] in its place, we adopt the JTS `LinearRing` `is_closed`
356    /// behavior in all places: that is, **we consider an empty [`LineString`] as closed**.
357    ///
358    /// This is expected when used in the context of a [`Polygon.exterior`](crate::Polygon::exterior) and elsewhere; And there
359    /// seems to be no reason to maintain the separate behavior for [`LineString`]s used in
360    /// non-`LinearRing` contexts.
361    pub fn is_closed(&self) -> bool {
362        self.0.first() == self.0.last()
363    }
364}
365
366/// Turn a [`Vec`] of [`Point`]-like objects into a [`LineString`].
367impl<T: CoordNum, IC: Into<Coord<T>>> From<Vec<IC>> for LineString<T> {
368    fn from(v: Vec<IC>) -> Self {
369        Self(v.into_iter().map(|c| c.into()).collect())
370    }
371}
372
373impl<T: CoordNum> From<Line<T>> for LineString<T> {
374    fn from(line: Line<T>) -> Self {
375        LineString::from(&line)
376    }
377}
378
379impl<T: CoordNum> From<&Line<T>> for LineString<T> {
380    fn from(line: &Line<T>) -> Self {
381        Self(vec![line.start, line.end])
382    }
383}
384
385/// Turn an iterator of [`Point`]-like objects into a [`LineString`].
386impl<T: CoordNum, IC: Into<Coord<T>>> FromIterator<IC> for LineString<T> {
387    fn from_iter<I: IntoIterator<Item = IC>>(iter: I) -> Self {
388        Self(iter.into_iter().map(|c| c.into()).collect())
389    }
390}
391
392/// Iterate over all the [`Coord`]s in this [`LineString`].
393impl<T: CoordNum> IntoIterator for LineString<T> {
394    type Item = Coord<T>;
395    type IntoIter = ::alloc::vec::IntoIter<Coord<T>>;
396
397    fn into_iter(self) -> Self::IntoIter {
398        self.0.into_iter()
399    }
400}
401
402impl<'a, T: CoordNum> IntoIterator for &'a LineString<T> {
403    type Item = &'a Coord<T>;
404    type IntoIter = CoordinatesIter<'a, T>;
405
406    fn into_iter(self) -> Self::IntoIter {
407        CoordinatesIter(self.0.iter())
408    }
409}
410
411/// Mutably iterate over all the [`Coord`]s in this [`LineString`]
412impl<'a, T: CoordNum> IntoIterator for &'a mut LineString<T> {
413    type Item = &'a mut Coord<T>;
414    type IntoIter = ::core::slice::IterMut<'a, Coord<T>>;
415
416    fn into_iter(self) -> ::core::slice::IterMut<'a, Coord<T>> {
417        self.0.iter_mut()
418    }
419}
420
421impl<T: CoordNum> Index<usize> for LineString<T> {
422    type Output = Coord<T>;
423
424    fn index(&self, index: usize) -> &Coord<T> {
425        self.0.index(index)
426    }
427}
428
429impl<T: CoordNum> IndexMut<usize> for LineString<T> {
430    fn index_mut(&mut self, index: usize) -> &mut Coord<T> {
431        self.0.index_mut(index)
432    }
433}
434
435#[cfg(any(feature = "approx", test))]
436mod approx_integration {
437    use super::*;
438    use approx::{AbsDiffEq, RelativeEq, UlpsEq};
439
440    impl<T> RelativeEq for LineString<T>
441    where
442        T: CoordNum + RelativeEq<Epsilon = T>,
443    {
444        #[inline]
445        fn default_max_relative() -> Self::Epsilon {
446            T::default_max_relative()
447        }
448
449        /// Equality assertion within a relative limit.
450        ///
451        /// # Examples
452        ///
453        /// ```
454        /// use geo_types::LineString;
455        ///
456        /// let mut coords_a = vec![(0., 0.), (5., 0.), (7., 9.)];
457        /// let a: LineString<f32> = coords_a.into_iter().collect();
458        ///
459        /// let mut coords_b = vec![(0., 0.), (5., 0.), (7.001, 9.)];
460        /// let b: LineString<f32> = coords_b.into_iter().collect();
461        ///
462        /// approx::assert_relative_eq!(a, b, max_relative=0.1)
463        /// ```
464        ///
465        fn relative_eq(
466            &self,
467            other: &Self,
468            epsilon: Self::Epsilon,
469            max_relative: Self::Epsilon,
470        ) -> bool {
471            if self.0.len() != other.0.len() {
472                return false;
473            }
474
475            let points_zipper = self.points().zip(other.points());
476            for (lhs, rhs) in points_zipper {
477                if lhs.relative_ne(&rhs, epsilon, max_relative) {
478                    return false;
479                }
480            }
481
482            true
483        }
484    }
485
486    impl<T> AbsDiffEq for LineString<T>
487    where
488        T: CoordNum + AbsDiffEq<Epsilon = T>,
489    {
490        type Epsilon = T;
491
492        #[inline]
493        fn default_epsilon() -> Self::Epsilon {
494            T::default_epsilon()
495        }
496
497        /// Equality assertion with an absolute limit.
498        ///
499        /// # Examples
500        ///
501        /// ```
502        /// use geo_types::LineString;
503        ///
504        /// let mut coords_a = vec![(0., 0.), (5., 0.), (7., 9.)];
505        /// let a: LineString<f32> = coords_a.into_iter().collect();
506        ///
507        /// let mut coords_b = vec![(0., 0.), (5., 0.), (7.001, 9.)];
508        /// let b: LineString<f32> = coords_b.into_iter().collect();
509        ///
510        /// approx::assert_relative_eq!(a, b, epsilon=0.1)
511        /// ```
512        fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool {
513            if self.0.len() != other.0.len() {
514                return false;
515            }
516            let mut points_zipper = self.points().zip(other.points());
517            points_zipper.all(|(lhs, rhs)| lhs.abs_diff_eq(&rhs, epsilon))
518        }
519    }
520
521    impl<T> UlpsEq for LineString<T>
522    where
523        T: CoordNum + UlpsEq<Epsilon = T>,
524    {
525        fn default_max_ulps() -> u32 {
526            T::default_max_ulps()
527        }
528
529        fn ulps_eq(&self, other: &Self, epsilon: Self::Epsilon, max_ulps: u32) -> bool {
530            if self.0.len() != other.0.len() {
531                return false;
532            }
533            let mut points_zipper = self.points().zip(other.points());
534            points_zipper.all(|(lhs, rhs)| lhs.ulps_eq(&rhs, epsilon, max_ulps))
535        }
536    }
537}
538
539#[cfg(any(
540    feature = "rstar_0_8",
541    feature = "rstar_0_9",
542    feature = "rstar_0_10",
543    feature = "rstar_0_11",
544    feature = "rstar_0_12"
545))]
546macro_rules! impl_rstar_line_string {
547    ($rstar:ident) => {
548        impl<T> ::$rstar::RTreeObject for LineString<T>
549        where
550            T: ::num_traits::Float + ::$rstar::RTreeNum,
551        {
552            type Envelope = ::$rstar::AABB<Point<T>>;
553
554            fn envelope(&self) -> Self::Envelope {
555                use num_traits::Bounded;
556                let bounding_rect = crate::private_utils::line_string_bounding_rect(self);
557                match bounding_rect {
558                    None => ::$rstar::AABB::from_corners(
559                        Point::new(Bounded::min_value(), Bounded::min_value()),
560                        Point::new(Bounded::max_value(), Bounded::max_value()),
561                    ),
562                    Some(b) => ::$rstar::AABB::from_corners(
563                        Point::new(b.min().x, b.min().y),
564                        Point::new(b.max().x, b.max().y),
565                    ),
566                }
567            }
568        }
569
570        impl<T> ::$rstar::PointDistance for LineString<T>
571        where
572            T: ::num_traits::Float + ::$rstar::RTreeNum,
573        {
574            fn distance_2(&self, point: &Point<T>) -> T {
575                let d = crate::private_utils::point_line_string_euclidean_distance(*point, self);
576                if d == T::zero() {
577                    d
578                } else {
579                    d.powi(2)
580                }
581            }
582        }
583    };
584}
585
586#[cfg(feature = "rstar_0_8")]
587impl_rstar_line_string!(rstar_0_8);
588
589#[cfg(feature = "rstar_0_9")]
590impl_rstar_line_string!(rstar_0_9);
591
592#[cfg(feature = "rstar_0_10")]
593impl_rstar_line_string!(rstar_0_10);
594
595#[cfg(feature = "rstar_0_11")]
596impl_rstar_line_string!(rstar_0_11);
597
598#[cfg(feature = "rstar_0_12")]
599impl_rstar_line_string!(rstar_0_12);
600
601#[cfg(test)]
602mod test {
603    use super::*;
604    use crate::{coord, wkt};
605    use approx::{AbsDiffEq, RelativeEq};
606
607    #[test]
608    fn test_exact_size() {
609        // see https://github.com/georust/geo/issues/762
610        let first = coord! { x: 0., y: 0. };
611        let ls = LineString::new(vec![first, coord! { x: 10., y: 0. }]);
612
613        // reference to force the `impl IntoIterator for &LineString` impl, giving a `CoordinatesIter`
614        for c in (&ls).into_iter().rev().skip(1).rev() {
615            assert_eq!(&first, c);
616        }
617        for p in ls.points().rev().skip(1).rev() {
618            assert_eq!(Point::from(first), p);
619        }
620    }
621
622    #[test]
623    fn test_abs_diff_eq() {
624        let delta = 1e-6;
625
626        let coords = vec![(0., 0.), (5., 0.), (10., 10.)];
627        let ls: LineString<f32> = coords.into_iter().collect();
628
629        let coords_x = vec![(0., 0.), (5. + delta, 0.), (10., 10.)];
630        let ls_x: LineString<f32> = coords_x.into_iter().collect();
631        assert!(ls.abs_diff_eq(&ls_x, 1e-2));
632        assert!(ls.abs_diff_ne(&ls_x, 1e-12));
633
634        let coords_y = vec![(0., 0.), (5., 0. + delta), (10., 10.)];
635        let ls_y: LineString<f32> = coords_y.into_iter().collect();
636        assert!(ls.abs_diff_eq(&ls_y, 1e-2));
637        assert!(ls.abs_diff_ne(&ls_y, 1e-12));
638
639        // Undersized, but otherwise equal.
640        let coords_x = vec![(0., 0.), (5., 0.)];
641        let ls_under: LineString<f32> = coords_x.into_iter().collect();
642        assert!(ls.abs_diff_ne(&ls_under, 1.));
643
644        // Oversized, but otherwise equal.
645        let coords_x = vec![(0., 0.), (5., 0.), (10., 10.), (10., 100.)];
646        let ls_oversized: LineString<f32> = coords_x.into_iter().collect();
647        assert!(ls.abs_diff_ne(&ls_oversized, 1.));
648    }
649
650    #[test]
651    fn test_relative_eq() {
652        let delta = 1e-6;
653
654        let coords = vec![(0., 0.), (5., 0.), (10., 10.)];
655        let ls: LineString<f32> = coords.into_iter().collect();
656
657        let coords_x = vec![(0., 0.), (5. + delta, 0.), (10., 10.)];
658        let ls_x: LineString<f32> = coords_x.into_iter().collect();
659        assert!(ls.relative_eq(&ls_x, 1e-2, 1e-2));
660        assert!(ls.relative_ne(&ls_x, 1e-12, 1e-12));
661
662        let coords_y = vec![(0., 0.), (5., 0. + delta), (10., 10.)];
663        let ls_y: LineString<f32> = coords_y.into_iter().collect();
664        assert!(ls.relative_eq(&ls_y, 1e-2, 1e-2));
665        assert!(ls.relative_ne(&ls_y, 1e-12, 1e-12));
666
667        // Undersized, but otherwise equal.
668        let coords_x = vec![(0., 0.), (5., 0.)];
669        let ls_under: LineString<f32> = coords_x.into_iter().collect();
670        assert!(ls.relative_ne(&ls_under, 1., 1.));
671
672        // Oversized, but otherwise equal.
673        let coords_x = vec![(0., 0.), (5., 0.), (10., 10.), (10., 100.)];
674        let ls_oversized: LineString<f32> = coords_x.into_iter().collect();
675        assert!(ls.relative_ne(&ls_oversized, 1., 1.));
676    }
677
678    #[test]
679    fn should_be_built_from_line() {
680        let start = coord! { x: 0, y: 0 };
681        let end = coord! { x: 10, y: 10 };
682        let line = Line::new(start, end);
683        let expected = LineString::new(vec![start, end]);
684
685        assert_eq!(expected, LineString::from(line));
686
687        let start = coord! { x: 10., y: 0.5 };
688        let end = coord! { x: 10000., y: 10.4 };
689        let line = Line::new(start, end);
690        let expected = LineString::new(vec![start, end]);
691
692        assert_eq!(expected, LineString::from(line));
693    }
694
695    #[test]
696    fn empty() {
697        let empty = LineString::<f64>::empty();
698        let empty_2 = wkt! { LINESTRING EMPTY };
699        assert_eq!(empty, empty_2);
700    }
701}