Skip to main content

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