use crate::{Coord, CoordNum, Line, Point, Triangle};
use alloc::vec;
use alloc::vec::Vec;
use core::iter::FromIterator;
use core::ops::{Index, IndexMut};
use core::slice::SliceIndex;
#[derive(Eq, PartialEq, Clone, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct LineString<T: CoordNum = f64>(pub Vec<Coord<T>>);
#[derive(Debug)]
pub struct PointsIter<'a, T: CoordNum + 'a>(::core::slice::Iter<'a, Coord<T>>);
impl<T: CoordNum> Iterator for PointsIter<'_, T> {
type Item = Point<T>;
fn next(&mut self) -> Option<Self::Item> {
self.0.next().map(|c| Point::from(*c))
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.0.size_hint()
}
}
impl<T: CoordNum> ExactSizeIterator for PointsIter<'_, T> {
fn len(&self) -> usize {
self.0.len()
}
}
impl<T: CoordNum> DoubleEndedIterator for PointsIter<'_, T> {
fn next_back(&mut self) -> Option<Self::Item> {
self.0.next_back().map(|c| Point::from(*c))
}
}
#[derive(Debug)]
pub struct CoordinatesIter<'a, T: CoordNum + 'a>(::core::slice::Iter<'a, Coord<T>>);
impl<'a, T: CoordNum> Iterator for CoordinatesIter<'a, T> {
type Item = &'a Coord<T>;
fn next(&mut self) -> Option<Self::Item> {
self.0.next()
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.0.size_hint()
}
}
impl<T: CoordNum> ExactSizeIterator for CoordinatesIter<'_, T> {
fn len(&self) -> usize {
self.0.len()
}
}
impl<T: CoordNum> DoubleEndedIterator for CoordinatesIter<'_, T> {
fn next_back(&mut self) -> Option<Self::Item> {
self.0.next_back()
}
}
impl<T: CoordNum> LineString<T> {
pub fn new(value: Vec<Coord<T>>) -> Self {
Self(value)
}
pub fn empty() -> Self {
Self::new(Vec::new())
}
#[deprecated(note = "Use points() instead")]
pub fn points_iter(&self) -> PointsIter<'_, T> {
PointsIter(self.0.iter())
}
pub fn points(&self) -> PointsIter<'_, T> {
PointsIter(self.0.iter())
}
pub fn coords(&self) -> impl DoubleEndedIterator<Item = &Coord<T>> {
self.0.iter()
}
pub fn coords_mut(&mut self) -> impl DoubleEndedIterator<Item = &mut Coord<T>> {
self.0.iter_mut()
}
pub fn into_points(self) -> Vec<Point<T>> {
self.0.into_iter().map(Point::from).collect()
}
pub fn into_inner(self) -> Vec<Coord<T>> {
self.0
}
pub fn lines(&'_ self) -> impl ExactSizeIterator<Item = Line<T>> + '_ {
self.0.windows(2).map(|w| {
unsafe { Line::new(*w.get_unchecked(0), *w.get_unchecked(1)) }
})
}
pub fn rev_lines(&'_ self) -> impl ExactSizeIterator<Item = Line<T>> + '_ {
self.0.windows(2).rev().map(|w| {
unsafe { Line::new(*w.get_unchecked(1), *w.get_unchecked(0)) }
})
}
pub fn triangles(&'_ self) -> impl ExactSizeIterator<Item = Triangle<T>> + '_ {
self.0.windows(3).map(|w| {
unsafe {
Triangle::new(
*w.get_unchecked(0),
*w.get_unchecked(1),
*w.get_unchecked(2),
)
}
})
}
pub fn close(&mut self) {
if !self.is_closed() {
debug_assert!(!self.0.is_empty());
self.0.push(self.0[0]);
}
}
#[deprecated(note = "Use geo::CoordsIter::coords_count instead")]
pub fn num_coords(&self) -> usize {
self.0.len()
}
pub fn is_closed(&self) -> bool {
self.0.first() == self.0.last()
}
}
impl<T: CoordNum, IC: Into<Coord<T>>> From<Vec<IC>> for LineString<T> {
fn from(v: Vec<IC>) -> Self {
Self(v.into_iter().map(|c| c.into()).collect())
}
}
impl<T: CoordNum> From<Line<T>> for LineString<T> {
fn from(line: Line<T>) -> Self {
LineString::from(&line)
}
}
impl<T: CoordNum> From<&Line<T>> for LineString<T> {
fn from(line: &Line<T>) -> Self {
Self(vec![line.start, line.end])
}
}
impl<T: CoordNum, IC: Into<Coord<T>>> FromIterator<IC> for LineString<T> {
fn from_iter<I: IntoIterator<Item = IC>>(iter: I) -> Self {
Self(iter.into_iter().map(|c| c.into()).collect())
}
}
impl<T: CoordNum> IntoIterator for LineString<T> {
type Item = Coord<T>;
type IntoIter = ::alloc::vec::IntoIter<Coord<T>>;
fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
}
}
impl<'a, T: CoordNum> IntoIterator for &'a LineString<T> {
type Item = &'a Coord<T>;
type IntoIter = CoordinatesIter<'a, T>;
fn into_iter(self) -> Self::IntoIter {
CoordinatesIter(self.0.iter())
}
}
impl<'a, T: CoordNum> IntoIterator for &'a mut LineString<T> {
type Item = &'a mut Coord<T>;
type IntoIter = ::core::slice::IterMut<'a, Coord<T>>;
fn into_iter(self) -> ::core::slice::IterMut<'a, Coord<T>> {
self.0.iter_mut()
}
}
impl<T: CoordNum, I: SliceIndex<[Coord<T>]>> Index<I> for LineString<T> {
type Output = I::Output;
fn index(&self, index: I) -> &I::Output {
self.0.index(index)
}
}
impl<T: CoordNum, I: SliceIndex<[Coord<T>]>> IndexMut<I> for LineString<T> {
fn index_mut(&mut self, index: I) -> &mut I::Output {
self.0.index_mut(index)
}
}
#[cfg(any(feature = "approx", test))]
mod approx_integration {
use super::*;
use approx::{AbsDiffEq, RelativeEq, UlpsEq};
impl<T> RelativeEq for LineString<T>
where
T: CoordNum + RelativeEq<Epsilon = T>,
{
#[inline]
fn default_max_relative() -> Self::Epsilon {
T::default_max_relative()
}
fn relative_eq(
&self,
other: &Self,
epsilon: Self::Epsilon,
max_relative: Self::Epsilon,
) -> bool {
if self.0.len() != other.0.len() {
return false;
}
let points_zipper = self.points().zip(other.points());
for (lhs, rhs) in points_zipper {
if lhs.relative_ne(&rhs, epsilon, max_relative) {
return false;
}
}
true
}
}
impl<T> AbsDiffEq for LineString<T>
where
T: CoordNum + AbsDiffEq<Epsilon = T>,
{
type Epsilon = T;
#[inline]
fn default_epsilon() -> Self::Epsilon {
T::default_epsilon()
}
fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool {
if self.0.len() != other.0.len() {
return false;
}
let mut points_zipper = self.points().zip(other.points());
points_zipper.all(|(lhs, rhs)| lhs.abs_diff_eq(&rhs, epsilon))
}
}
impl<T> UlpsEq for LineString<T>
where
T: CoordNum + UlpsEq<Epsilon = T>,
{
fn default_max_ulps() -> u32 {
T::default_max_ulps()
}
fn ulps_eq(&self, other: &Self, epsilon: Self::Epsilon, max_ulps: u32) -> bool {
if self.0.len() != other.0.len() {
return false;
}
let mut points_zipper = self.points().zip(other.points());
points_zipper.all(|(lhs, rhs)| lhs.ulps_eq(&rhs, epsilon, max_ulps))
}
}
}
#[cfg(any(
feature = "rstar_0_8",
feature = "rstar_0_9",
feature = "rstar_0_10",
feature = "rstar_0_11",
feature = "rstar_0_12"
))]
macro_rules! impl_rstar_line_string {
($rstar:ident) => {
impl<T> ::$rstar::RTreeObject for LineString<T>
where
T: ::num_traits::Float + ::$rstar::RTreeNum,
{
type Envelope = ::$rstar::AABB<Point<T>>;
fn envelope(&self) -> Self::Envelope {
use num_traits::Bounded;
let bounding_rect = crate::private_utils::line_string_bounding_rect(self);
match bounding_rect {
None => ::$rstar::AABB::from_corners(
Point::new(Bounded::min_value(), Bounded::min_value()),
Point::new(Bounded::max_value(), Bounded::max_value()),
),
Some(b) => ::$rstar::AABB::from_corners(
Point::new(b.min().x, b.min().y),
Point::new(b.max().x, b.max().y),
),
}
}
}
impl<T> ::$rstar::PointDistance for LineString<T>
where
T: ::num_traits::Float + ::$rstar::RTreeNum,
{
fn distance_2(&self, point: &Point<T>) -> T {
let d = crate::private_utils::point_line_string_euclidean_distance(*point, self);
if d == T::zero() {
d
} else {
d.powi(2)
}
}
}
};
}
#[cfg(feature = "rstar_0_8")]
impl_rstar_line_string!(rstar_0_8);
#[cfg(feature = "rstar_0_9")]
impl_rstar_line_string!(rstar_0_9);
#[cfg(feature = "rstar_0_10")]
impl_rstar_line_string!(rstar_0_10);
#[cfg(feature = "rstar_0_11")]
impl_rstar_line_string!(rstar_0_11);
#[cfg(feature = "rstar_0_12")]
impl_rstar_line_string!(rstar_0_12);
#[cfg(test)]
mod test {
use super::*;
use crate::{coord, wkt};
use approx::{AbsDiffEq, RelativeEq};
#[test]
fn test_exact_size() {
let first = coord! { x: 0., y: 0. };
let ls = LineString::new(vec![first, coord! { x: 10., y: 0. }]);
for c in (&ls).into_iter().rev().skip(1).rev() {
assert_eq!(&first, c);
}
for p in ls.points().rev().skip(1).rev() {
assert_eq!(Point::from(first), p);
}
}
#[test]
fn test_abs_diff_eq() {
let delta = 1e-6;
let coords = vec![(0., 0.), (5., 0.), (10., 10.)];
let ls: LineString<f32> = coords.into_iter().collect();
let coords_x = vec![(0., 0.), (5. + delta, 0.), (10., 10.)];
let ls_x: LineString<f32> = coords_x.into_iter().collect();
assert!(ls.abs_diff_eq(&ls_x, 1e-2));
assert!(ls.abs_diff_ne(&ls_x, 1e-12));
let coords_y = vec![(0., 0.), (5., 0. + delta), (10., 10.)];
let ls_y: LineString<f32> = coords_y.into_iter().collect();
assert!(ls.abs_diff_eq(&ls_y, 1e-2));
assert!(ls.abs_diff_ne(&ls_y, 1e-12));
let coords_x = vec![(0., 0.), (5., 0.)];
let ls_under: LineString<f32> = coords_x.into_iter().collect();
assert!(ls.abs_diff_ne(&ls_under, 1.));
let coords_x = vec![(0., 0.), (5., 0.), (10., 10.), (10., 100.)];
let ls_oversized: LineString<f32> = coords_x.into_iter().collect();
assert!(ls.abs_diff_ne(&ls_oversized, 1.));
}
#[test]
fn test_relative_eq() {
let delta = 1e-6;
let coords = vec![(0., 0.), (5., 0.), (10., 10.)];
let ls: LineString<f32> = coords.into_iter().collect();
let coords_x = vec![(0., 0.), (5. + delta, 0.), (10., 10.)];
let ls_x: LineString<f32> = coords_x.into_iter().collect();
assert!(ls.relative_eq(&ls_x, 1e-2, 1e-2));
assert!(ls.relative_ne(&ls_x, 1e-12, 1e-12));
let coords_y = vec![(0., 0.), (5., 0. + delta), (10., 10.)];
let ls_y: LineString<f32> = coords_y.into_iter().collect();
assert!(ls.relative_eq(&ls_y, 1e-2, 1e-2));
assert!(ls.relative_ne(&ls_y, 1e-12, 1e-12));
let coords_x = vec![(0., 0.), (5., 0.)];
let ls_under: LineString<f32> = coords_x.into_iter().collect();
assert!(ls.relative_ne(&ls_under, 1., 1.));
let coords_x = vec![(0., 0.), (5., 0.), (10., 10.), (10., 100.)];
let ls_oversized: LineString<f32> = coords_x.into_iter().collect();
assert!(ls.relative_ne(&ls_oversized, 1., 1.));
}
#[test]
fn should_be_built_from_line() {
let start = coord! { x: 0, y: 0 };
let end = coord! { x: 10, y: 10 };
let line = Line::new(start, end);
let expected = LineString::new(vec![start, end]);
assert_eq!(expected, LineString::from(line));
let start = coord! { x: 10., y: 0.5 };
let end = coord! { x: 10000., y: 10.4 };
let line = Line::new(start, end);
let expected = LineString::new(vec![start, end]);
assert_eq!(expected, LineString::from(line));
}
#[test]
fn empty() {
let empty = LineString::<f64>::empty();
let empty_2 = wkt! { LINESTRING EMPTY };
assert_eq!(empty, empty_2);
}
#[test]
fn test_indexing() {
let mut ls = wkt! { LINESTRING(0. 0., 1. 1., 2. 2.) };
assert_eq!(ls[0], coord! { x: 0., y: 0. });
assert_eq!(ls[1], coord! { x: 1., y: 1. });
ls[1] = coord! { x: 100., y: 100. };
assert_eq!(ls[1], coord! { x: 100., y: 100. });
assert_eq!(
ls[0..2],
[coord! { x: 0., y: 0. }, coord! { x: 100., y: 100. }]
);
}
#[test]
#[should_panic]
fn test_indexing_out_of_bounds() {
let ls = wkt! { LINESTRING(0. 0., 1. 1.) };
let _ = ls[2];
}
}