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}