Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

relative_eq() #567

Merged
merged 19 commits into from
Jan 4, 2021
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
946397d
Added num_coords() method to MultiPoint. To Point, Line, LineString a…
martinfrances107 Dec 11, 2020
65f6100
Removed debug.
martinfrances107 Dec 11, 2020
4ab3599
Removed commented out code.
martinfrances107 Dec 16, 2020
daf5570
Removed commented out code.
martinfrances107 Dec 16, 2020
719c32c
line_string.rs abs_diff_eq: simplify by using .all()
martinfrances107 Dec 16, 2020
6c23693
multi_point.rs: Simplify using .all()
martinfrances107 Dec 16, 2020
3eaec49
multi_point.rs abs_diff_eq() now handles unequal sizes
martinfrances107 Dec 16, 2020
1dca719
point.rs Simplify RelativeEq for Point
martinfrances107 Dec 16, 2020
edb4ff1
lines.rs Simplify "AbsDiffEq for Line<T>" to operate of coords not po…
martinfrances107 Dec 16, 2020
5aa011c
line_string.rs - abs_diff_eq() Now rejects line_strings with unequal…
martinfrances107 Dec 16, 2020
efb112a
Removed type constraint on RelativeEq and AbsDiffEq that the type mus…
martinfrances107 Dec 17, 2020
8ba2b0b
ran cargo fmt
martinfrances107 Dec 17, 2020
26a6a2d
Added new feature relative_eq
martinfrances107 Dec 20, 2020
c21a552
After rebase, drop the use of the deprecated num_coords() method.
martinfrances107 Dec 22, 2020
bfb2746
Fixed dev-dependency issue.
martinfrances107 Dec 24, 2020
d347afd
Minor Tidy: Removed multipoint.rs num_coords(). Fixed doctest code an…
martinfrances107 Dec 30, 2020
89d4742
Rename `relative_eq` feature to `approx` and make optional
michaelkirk Dec 30, 2020
fde7395
changelog
michaelkirk Jan 1, 2021
e90ee64
Merge pull request #1 from georust/mkirk/approx-feature-juggling
martinfrances107 Jan 2, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions geo-types/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ keywords = ["gis", "geo", "geography", "geospatial"]
description = "Geospatial primitive data types"
edition = "2018"

[features]
relative_eq = []

[dependencies]
approx = "0.4.0"
num-traits = "0.2"
Expand Down
15 changes: 11 additions & 4 deletions geo-types/src/coordinate.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
use crate::{CoordinateType, Point};
#[cfg(test)]
use approx::{AbsDiffEq, RelativeEq, UlpsEq};

#[cfg(any(feature = "relative_eq", test))]
use approx::{AbsDiffEq, RelativeEq};

#[cfg(test)]
use approx::UlpsEq;
/// A lightweight struct used to store coordinates on the 2-dimensional
/// Cartesian plane.
///
Expand Down Expand Up @@ -240,31 +243,35 @@ impl<T: CoordinateType> Zero for Coordinate<T> {
}
}

#[cfg(test)]
#[cfg(feature = "relative_eq")]
impl<T: CoordinateType + AbsDiffEq> AbsDiffEq for Coordinate<T>
where
T::Epsilon: Copy,
{
type Epsilon = T::Epsilon;

#[inline]
fn default_epsilon() -> T::Epsilon {
T::default_epsilon()
}

#[inline]
fn abs_diff_eq(&self, other: &Self, epsilon: T::Epsilon) -> bool {
T::abs_diff_eq(&self.x, &other.x, epsilon) && T::abs_diff_eq(&self.y, &other.y, epsilon)
}
}

#[cfg(test)]
#[cfg(feature = "relative_eq")]
impl<T: CoordinateType + RelativeEq> RelativeEq for Coordinate<T>
where
T::Epsilon: Copy,
{
#[inline]
fn default_max_relative() -> T::Epsilon {
T::default_max_relative()
}

#[inline]
fn relative_eq(&self, other: &Self, epsilon: T::Epsilon, max_relative: T::Epsilon) -> bool {
T::relative_eq(&self.x, &other.x, epsilon, max_relative)
&& T::relative_eq(&self.y, &other.y, epsilon, max_relative)
Expand Down
143 changes: 143 additions & 0 deletions geo-types/src/line.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
use crate::{Coordinate, CoordinateType, Point};
#[cfg(feature = "relative_eq")]
use approx::AbsDiffEq;
#[cfg(feature = "relative_eq")]
use approx::RelativeEq;

/// A line segment made up of exactly two
/// [`Coordinate`s](struct.Coordinate.html).
Expand Down Expand Up @@ -164,6 +168,66 @@ impl<T: CoordinateType> From<[(T, T); 2]> for Line<T> {
Line::new(coord[0], coord[1])
}
}
#[cfg(feature = "relative_eq")]
impl<T> RelativeEq for Line<T>
where
T: AbsDiffEq<Epsilon = T> + CoordinateType + RelativeEq,
{
#[inline]
fn default_max_relative() -> Self::Epsilon {
T::default_max_relative()
}

/// Equality assertion within a relative limit.
///
/// # Examples
///
/// ```
/// use geo_types::{Coordinate, Line};
///
/// let a = Line::new(Coordinate { x: 0., y: 0. }, Coordinate { x: 1., y: 1. });
/// let b = Line::new(Coordinate { x: 0., y: 0. }, Coordinate { x: 1.001, y: 1. });
///
/// approx::assert_relative_eq!(a, b, max_relative=0.1)
/// ```
#[inline]
fn relative_eq(
&self,
other: &Self,
epsilon: Self::Epsilon,
max_relative: Self::Epsilon,
) -> bool {
self.start.relative_eq(&other.start, epsilon, max_relative)
&& self.end.relative_eq(&other.end, epsilon, max_relative)
}
}

#[cfg(feature = "relative_eq")]
impl<T: AbsDiffEq<Epsilon = T> + CoordinateType> AbsDiffEq for Line<T> {
type Epsilon = T;

#[inline]
fn default_epsilon() -> Self::Epsilon {
T::default_epsilon()
}

/// Equality assertion within a relative limit.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment seems like its for relative_eq not abs_diff_eq

///
/// # Examples
///
/// ```
/// use geo_types::{Coordinate, Line};
///
/// let a = Line::new(Coordinate { x: 0., y: 0. }, Coordinate { x: 1., y: 1. });
/// let b = Line::new(Coordinate { x: 0., y: 0. }, Coordinate { x: 1.001, y: 1. });
///
/// approx::assert_relative_eq!(a, b, epsilon=0.1)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

assert_abs_diff_eq

/// ```
#[inline]
fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool {
self.start.abs_diff_eq(&other.start, epsilon) && self.end.abs_diff_eq(&other.end, epsilon)
}
}

#[cfg(feature = "rstar")]
impl<T> ::rstar::RTreeObject for Line<T>
Expand All @@ -188,3 +252,82 @@ where
d.powi(2)
}
}

#[cfg(test)]
mod test {
use super::*;

#[test]
fn test_abs_diff_eq() {
let delta = 1e-6;
let line = Line::new(Coordinate { x: 0., y: 0. }, Coordinate { x: 1., y: 1. });
let line_start_x = Line::new(
Point(Coordinate {
x: 0. + delta,
y: 0.,
}),
Point(Coordinate { x: 1., y: 1. }),
);
assert!(line.abs_diff_eq(&line_start_x, 1e-2));
assert!(line.abs_diff_ne(&line_start_x, 1e-12));

let line_start_y = Line::new(
Coordinate {
x: 0.,
y: 0. + delta,
},
Coordinate { x: 1., y: 1. },
);
assert!(line.abs_diff_eq(&line_start_y, 1e-2));
assert!(line.abs_diff_ne(&line_start_y, 1e-12));

let line_end_x = Line::new(
Coordinate { x: 0., y: 0. },
Coordinate {
x: 1. + delta,
y: 1.,
},
);

assert!(line.abs_diff_eq(&line_end_x, 1e-2));
assert!(line.abs_diff_ne(&line_end_x, 1e-12));

let line_end_y = Line::new(
Coordinate { x: 0., y: 0. },
Coordinate {
x: 1.,
y: 1. + delta,
},
);

assert!(line.abs_diff_eq(&line_end_y, 1e-2));
assert!(line.abs_diff_ne(&line_end_y, 1e-12));
}

#[test]
fn test_relative_eq() {
let delta = 1e-6;

let line = Line::new(Coordinate { x: 0., y: 0. }, Coordinate { x: 1., y: 1. });
let line_start_x = Line::new(
Point(Coordinate {
x: 0. + delta,
y: 0.,
}),
Point(Coordinate { x: 1., y: 1. }),
);
let line_start_y = Line::new(
Coordinate {
x: 0.,
y: 0. + delta,
},
Coordinate { x: 1., y: 1. },
);

assert!(line.relative_eq(&line_start_x, 1e-2, 1e-2));
assert!(line.relative_ne(&line_start_x, 1e-12, 1e-12));

assert!(line.relative_eq(&line_start_y, 1e-2, 1e-2));
assert!(line.relative_ne(&line_start_y, 1e-12, 1e-12));
}
}
148 changes: 148 additions & 0 deletions geo-types/src/line_string.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
#[cfg(feature = "relative_eq")]
use approx::AbsDiffEq;
#[cfg(feature = "relative_eq")]
use approx::RelativeEq;

use crate::{Coordinate, CoordinateType, Line, Point, Triangle};
use std::iter::FromIterator;
use std::ops::{Index, IndexMut};
Expand Down Expand Up @@ -282,6 +287,86 @@ impl<T: CoordinateType> IndexMut<usize> for LineString<T> {
}
}

#[cfg(feature = "relative_eq")]
impl<T> RelativeEq for LineString<T>
where
T: AbsDiffEq<Epsilon = T> + CoordinateType + RelativeEq,
{
#[inline]
fn default_max_relative() -> Self::Epsilon {
T::default_max_relative()
}

/// Equality assertion within a relative limit.
///
/// # Examples
///
/// ```
/// use geo_types::LineString;
///
/// let mut coords_a = vec![(0., 0.), (5., 0.), (7., 9.)];
/// let a: LineString<f32> = coords_a.into_iter().collect();
///
/// let mut coords_b = vec![(0., 0.), (5., 0.), (7.001, 9.)];
/// let b: LineString<f32> = coords_b.into_iter().collect();
///
/// approx::assert_relative_eq!(a, b, max_relative=0.1)
/// ```
///
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_iter().zip(other.points_iter());
for (lhs, rhs) in points_zipper {
if lhs.relative_ne(&rhs, epsilon, max_relative) {
return false;
}
}

true
}
}

#[cfg(feature = "relative_eq")]
impl<T: AbsDiffEq<Epsilon = T> + CoordinateType> AbsDiffEq for LineString<T> {
type Epsilon = T;

#[inline]
fn default_epsilon() -> Self::Epsilon {
T::default_epsilon()
}

/// Equality assertion within a relative limit.
///
/// # Examples
///
/// ```
/// use geo_types::LineString;
///
/// let mut coords_a = vec![(0., 0.), (5., 0.), (7., 9.)];
/// let a: LineString<f32> = coords_a.into_iter().collect();
///
/// let mut coords_b = vec![(0., 0.), (5., 0.), (7.001, 9.)];
/// let b: LineString<f32> = coords_b.into_iter().collect();
///
/// approx::assert_relative_eq!(a, b, epsilon=0.1)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto about assert_abs_diff_eq and the comment

/// ```
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_iter().zip(other.points_iter());
points_zipper.all(|(lhs, rhs)| lhs.abs_diff_eq(&rhs, epsilon))
}
}

#[cfg(feature = "rstar")]
impl<T> ::rstar::RTreeObject for LineString<T>
where
Expand Down Expand Up @@ -319,3 +404,66 @@ where
}
}
}

#[cfg(test)]
mod test {
use super::*;

use approx::AbsDiffEq;

#[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));

// Undersized, but otherwise equal.
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.));

// Oversized, but otherwise equal.
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));

// Undersized, but otherwise equal.
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.));

// Oversized, but otherwise equal.
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.));
}
}
Loading