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}