Skip to content

Commit

Permalink
Merge #567
Browse files Browse the repository at this point in the history
567: relative_eq() r=michaelkirk a=martinfrances107

Added num_coords() method to MultiPoint. To Point, Line, LineString and MultiPoint test helpers and associated tests were added to assert that two structs were relatively equal. That is identical within the limits of numerical rounding accuracy.

- [x] I agree to follow the project's [code of conduct](https://github.com/georust/geo/blob/master/CODE_OF_CONDUCT.md).
- [ ] I added an entry to `CHANGES.md` if knowledge of this change could be valuable to users.
---



Co-authored-by: Martin <[email protected]>
Co-authored-by: Michael Kirk <[email protected]>
Co-authored-by: martin frances <[email protected]>
  • Loading branch information
3 people authored Jan 4, 2021
2 parents 0b38c9b + e90ee64 commit 7dbc853
Show file tree
Hide file tree
Showing 11 changed files with 572 additions and 15 deletions.
2 changes: 2 additions & 0 deletions geo-types/CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

* `geo_types::LineString::num_coords` has been deprecated in favor of `geo::algorithm::coords_iter::CoordsIter::coords_count`
* <https://github.com/georust/geo/pull/563>
* Introduce `use-rstar` feature rather than `rstar` so that `approx` dependency can be optional
* <https://github.com/georust/geo/pull/567>

## 0.6.2

Expand Down
15 changes: 14 additions & 1 deletion geo-types/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,24 @@ keywords = ["gis", "geo", "geography", "geospatial"]
description = "Geospatial primitive data types"
edition = "2018"

[features]
use-rstar = ["rstar", "approx"]

[dependencies]
approx = "0.4.0"
approx = { version = "0.4.0", optional = true }
num-traits = "0.2"
serde = { version = "1", optional = true, features = ["derive"] }
# Prefer `use-rstar` feature rather than enabling rstar directly.
# rstar integration relies on the optional approx crate, but implicit features cannot yet enable other features.
# See: https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#namespaced-features
rstar = { version = "0.8", optional = true }

[dev-dependencies]
approx = "0.4.0"

[package.metadata.cargo-all-features]

skip_feature_sets = [
# must be enabled via use-rstar
["rstar"],
]
13 changes: 9 additions & 4 deletions geo-types/src/coordinate.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::{CoordinateType, Point};
#[cfg(test)]

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

/// A lightweight struct used to store coordinates on the 2-dimensional
Expand Down Expand Up @@ -240,38 +241,42 @@ impl<T: CoordinateType> Zero for Coordinate<T> {
}
}

#[cfg(test)]
#[cfg(any(feature = "approx", test))]
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(any(feature = "approx", test))]
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)
}
}

#[cfg(test)]
#[cfg(any(feature = "approx", test))]
impl<T: CoordinateType + UlpsEq> UlpsEq for Coordinate<T>
where
T::Epsilon: Copy,
Expand Down
2 changes: 2 additions & 0 deletions geo-types/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ extern crate serde;
#[cfg(feature = "rstar")]
extern crate rstar;

#[cfg(test)]
#[macro_use]
extern crate approx;

Expand Down Expand Up @@ -95,6 +96,7 @@ pub use crate::rect::{InvalidRectCoordinatesError, Rect};
#[macro_use]
mod macros;

#[cfg(feature = "rstar")]
#[doc(hidden)]
pub mod private_utils;

Expand Down
141 changes: 141 additions & 0 deletions geo-types/src/line.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
use crate::{Coordinate, CoordinateType, Point};
#[cfg(any(feature = "approx", test))]
use approx::{AbsDiffEq, RelativeEq};

/// A line segment made up of exactly two
/// [`Coordinate`s](struct.Coordinate.html).
Expand Down Expand Up @@ -164,6 +166,66 @@ impl<T: CoordinateType> From<[(T, T); 2]> for Line<T> {
Line::new(coord[0], coord[1])
}
}
#[cfg(any(feature = "approx", test))]
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(any(feature = "approx", test))]
impl<T: AbsDiffEq<Epsilon = T> + CoordinateType> AbsDiffEq for Line<T> {
type Epsilon = T;

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

/// Equality assertion with a absolute 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_abs_diff_eq!(a, b, epsilon=0.1);
/// ```
#[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 +250,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));
}
}
Loading

0 comments on commit 7dbc853

Please sign in to comment.