Skip to content

Commit

Permalink
Merge #628
Browse files Browse the repository at this point in the history
628: approx for all Geometry types r=frewsxcv a=michaelkirk

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

Follow up to #567 which implemented for Coord, Point, LineString, and a few others, this PR implements it for the remaining types, and `Geometry` itself.

FYI this is a precursor to an integration I'm working on with some of the JTS test suite.



Co-authored-by: Michael Kirk <[email protected]>
  • Loading branch information
bors[bot] and michaelkirk authored Feb 22, 2021
2 parents a8c2f16 + 6008397 commit b317c7b
Show file tree
Hide file tree
Showing 12 changed files with 613 additions and 4 deletions.
5 changes: 5 additions & 0 deletions geo-types/CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Changes

## Unreleased

* Implement `RelativeEq` and `AbsDiffEq` for fuzzy comparison of remaining Geometry Types
* <https://github.com/georust/geo/pull/628>

## 0.7.1

* Implement `Default` on `Coordinate` and `Point` structs (defaults to `(x: 0, y: 0)`)
Expand Down
106 changes: 106 additions & 0 deletions geo-types/src/geometry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ use crate::{
CoordNum, Error, GeometryCollection, Line, LineString, MultiLineString, MultiPoint,
MultiPolygon, Point, Polygon, Rect, Triangle,
};

#[cfg(any(feature = "approx", test))]
use approx::{AbsDiffEq, RelativeEq};
use core::any::type_name;
use std::convert::TryFrom;

Expand Down Expand Up @@ -234,3 +237,106 @@ where
Geometry::Triangle(_) => type_name::<Triangle<T>>(),
}
}

#[cfg(any(feature = "approx", test))]
impl<T> RelativeEq for Geometry<T>
where
T: AbsDiffEq<Epsilon = T> + CoordNum + RelativeEq,
{
#[inline]
fn default_max_relative() -> Self::Epsilon {
T::default_max_relative()
}

/// Equality assertion within a relative limit.
///
/// # Examples
///
/// ```
/// use geo_types::{Geometry, polygon};
///
/// let a: Geometry<f32> = polygon![(x: 0., y: 0.), (x: 5., y: 0.), (x: 7., y: 9.), (x: 0., y: 0.)].into();
/// let b: Geometry<f32> = polygon![(x: 0., y: 0.), (x: 5., y: 0.), (x: 7.01, y: 9.), (x: 0., y: 0.)].into();
///
/// approx::assert_relative_eq!(a, b, max_relative=0.1);
/// approx::assert_relative_ne!(a, b, max_relative=0.001);
/// ```
///
fn relative_eq(
&self,
other: &Self,
epsilon: Self::Epsilon,
max_relative: Self::Epsilon,
) -> bool {
match (self, other) {
(Geometry::Point(g1), Geometry::Point(g2)) => g1.relative_eq(g2, epsilon, max_relative),
(Geometry::Line(g1), Geometry::Line(g2)) => g1.relative_eq(g2, epsilon, max_relative),
(Geometry::LineString(g1), Geometry::LineString(g2)) => {
g1.relative_eq(g2, epsilon, max_relative)
}
(Geometry::Polygon(g1), Geometry::Polygon(g2)) => {
g1.relative_eq(g2, epsilon, max_relative)
}
(Geometry::MultiPoint(g1), Geometry::MultiPoint(g2)) => {
g1.relative_eq(g2, epsilon, max_relative)
}
(Geometry::MultiLineString(g1), Geometry::MultiLineString(g2)) => {
g1.relative_eq(g2, epsilon, max_relative)
}
(Geometry::MultiPolygon(g1), Geometry::MultiPolygon(g2)) => {
g1.relative_eq(g2, epsilon, max_relative)
}
(Geometry::GeometryCollection(g1), Geometry::GeometryCollection(g2)) => {
g1.relative_eq(g2, epsilon, max_relative)
}
(Geometry::Rect(g1), Geometry::Rect(g2)) => g1.relative_eq(g2, epsilon, max_relative),
(Geometry::Triangle(g1), Geometry::Triangle(g2)) => {
g1.relative_eq(g2, epsilon, max_relative)
}
(_, _) => false,
}
}
}

#[cfg(any(feature = "approx", test))]
impl<T: AbsDiffEq<Epsilon = T> + CoordNum> AbsDiffEq for Geometry<T> {
type Epsilon = T;

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

/// Equality assertion with an absolute limit.
///
/// # Examples
///
/// ```
/// use geo_types::{Geometry, polygon};
///
/// let a: Geometry<f32> = polygon![(x: 0., y: 0.), (x: 5., y: 0.), (x: 7., y: 9.), (x: 0., y: 0.)].into();
/// let b: Geometry<f32> = polygon![(x: 0., y: 0.), (x: 5., y: 0.), (x: 7.01, y: 9.), (x: 0., y: 0.)].into();
///
/// approx::assert_abs_diff_eq!(a, b, epsilon=0.1);
/// approx::assert_abs_diff_ne!(a, b, epsilon=0.001);
/// ```
fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool {
match (self, other) {
(Geometry::Point(g1), Geometry::Point(g2)) => g1.abs_diff_eq(g2, epsilon),
(Geometry::Line(g1), Geometry::Line(g2)) => g1.abs_diff_eq(g2, epsilon),
(Geometry::LineString(g1), Geometry::LineString(g2)) => g1.abs_diff_eq(g2, epsilon),
(Geometry::Polygon(g1), Geometry::Polygon(g2)) => g1.abs_diff_eq(g2, epsilon),
(Geometry::MultiPoint(g1), Geometry::MultiPoint(g2)) => g1.abs_diff_eq(g2, epsilon),
(Geometry::MultiLineString(g1), Geometry::MultiLineString(g2)) => {
g1.abs_diff_eq(g2, epsilon)
}
(Geometry::MultiPolygon(g1), Geometry::MultiPolygon(g2)) => g1.abs_diff_eq(g2, epsilon),
(Geometry::GeometryCollection(g1), Geometry::GeometryCollection(g2)) => {
g1.abs_diff_eq(g2, epsilon)
}
(Geometry::Rect(g1), Geometry::Rect(g2)) => g1.abs_diff_eq(g2, epsilon),
(Geometry::Triangle(g1), Geometry::Triangle(g2)) => g1.abs_diff_eq(g2, epsilon),
(_, _) => false,
}
}
}
79 changes: 79 additions & 0 deletions geo-types/src/geometry_collection.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
use crate::{CoordNum, Geometry};

#[cfg(any(feature = "approx", test))]
use approx::{AbsDiffEq, RelativeEq};
use std::iter::FromIterator;
use std::ops::{Index, IndexMut};

Expand Down Expand Up @@ -222,3 +225,79 @@ impl<'a, T: CoordNum> GeometryCollection<T> {
self.into_iter()
}
}

#[cfg(any(feature = "approx", test))]
impl<T> RelativeEq for GeometryCollection<T>
where
T: AbsDiffEq<Epsilon = T> + CoordNum + RelativeEq,
{
#[inline]
fn default_max_relative() -> Self::Epsilon {
T::default_max_relative()
}

/// Equality assertion within a relative limit.
///
/// # Examples
///
/// ```
/// use geo_types::{GeometryCollection, point};
///
/// let a = GeometryCollection(vec![point![x: 1.0, y: 2.0].into()]);
/// let b = GeometryCollection(vec![point![x: 1.0, y: 2.01].into()]);
///
/// approx::assert_relative_eq!(a, b, max_relative=0.1);
/// approx::assert_relative_ne!(a, b, max_relative=0.0001);
/// ```
#[inline]
fn relative_eq(
&self,
other: &Self,
epsilon: Self::Epsilon,
max_relative: Self::Epsilon,
) -> bool {
if self.0.len() != other.0.len() {
return false;
}

let mut mp_zipper = self.iter().zip(other.iter());
mp_zipper.all(|(lhs, rhs)| lhs.relative_eq(&rhs, epsilon, max_relative))
}
}

#[cfg(any(feature = "approx", test))]
impl<T> AbsDiffEq for GeometryCollection<T>
where
T: AbsDiffEq<Epsilon = T> + CoordNum,
T::Epsilon: Copy,
{
type Epsilon = T;

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

/// Equality assertion with an absolute limit.
///
/// # Examples
///
/// ```
/// use geo_types::{GeometryCollection, point};
///
/// let a = GeometryCollection(vec![point![x: 0.0, y: 0.0].into()]);
/// let b = GeometryCollection(vec![point![x: 0.0, y: 0.1].into()]);
///
/// approx::abs_diff_eq!(a, b, epsilon=0.1);
/// approx::abs_diff_ne!(a, b, epsilon=0.001);
/// ```
#[inline]
fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool {
if self.0.len() != other.0.len() {
return false;
}

let mut mp_zipper = self.into_iter().zip(other.into_iter());
mp_zipper.all(|(lhs, rhs)| lhs.abs_diff_eq(&rhs, epsilon))
}
}
2 changes: 1 addition & 1 deletion geo-types/src/line.rs
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ impl<T: AbsDiffEq<Epsilon = T> + CoordNum> AbsDiffEq for Line<T> {
T::default_epsilon()
}

/// Equality assertion with a absolute limit.
/// Equality assertion with an absolute limit.
///
/// # Examples
///
Expand Down
2 changes: 1 addition & 1 deletion geo-types/src/line_string.rs
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,7 @@ impl<T: AbsDiffEq<Epsilon = T> + CoordNum> AbsDiffEq for LineString<T> {
T::default_epsilon()
}

/// Equality assertion with a absolute limit.
/// Equality assertion with an absolute limit.
///
/// # Examples
///
Expand Down
79 changes: 79 additions & 0 deletions geo-types/src/multi_line_string.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
use crate::{CoordNum, LineString};

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

/// A collection of
Expand Down Expand Up @@ -110,6 +113,82 @@ impl<T: CoordNum> MultiLineString<T> {
}
}

#[cfg(any(feature = "approx", test))]
impl<T> RelativeEq for MultiLineString<T>
where
T: AbsDiffEq<Epsilon = T> + CoordNum + RelativeEq,
{
#[inline]
fn default_max_relative() -> Self::Epsilon {
T::default_max_relative()
}

/// Equality assertion within a relative limit.
///
/// # Examples
///
/// ```
/// use geo_types::{MultiLineString, line_string};
///
/// let a = MultiLineString(vec![line_string![(x: 0., y: 0.), (x: 10., y: 10.)]]);
/// let b = MultiLineString(vec![line_string![(x: 0., y: 0.), (x: 10.01, y: 10.)]]);
///
/// approx::assert_relative_eq!(a, b, max_relative=0.1);
/// approx::assert_relative_ne!(a, b, max_relative=0.0001);
/// ```
#[inline]
fn relative_eq(
&self,
other: &Self,
epsilon: Self::Epsilon,
max_relative: Self::Epsilon,
) -> bool {
if self.0.len() != other.0.len() {
return false;
}

let mut mp_zipper = self.iter().zip(other.iter());
mp_zipper.all(|(lhs, rhs)| lhs.relative_eq(&rhs, epsilon, max_relative))
}
}

#[cfg(any(feature = "approx", test))]
impl<T> AbsDiffEq for MultiLineString<T>
where
T: AbsDiffEq<Epsilon = T> + CoordNum,
T::Epsilon: Copy,
{
type Epsilon = T;

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

/// Equality assertion with an absolute limit.
///
/// # Examples
///
/// ```
/// use geo_types::{MultiLineString, line_string};
///
/// let a = MultiLineString(vec![line_string![(x: 0., y: 0.), (x: 10., y: 10.)]]);
/// let b = MultiLineString(vec![line_string![(x: 0., y: 0.), (x: 10.01, y: 10.)]]);
///
/// approx::abs_diff_eq!(a, b, epsilon=0.1);
/// approx::abs_diff_ne!(a, b, epsilon=0.001);
/// ```
#[inline]
fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool {
if self.0.len() != other.0.len() {
return false;
}

let mut mp_zipper = self.into_iter().zip(other.into_iter());
mp_zipper.all(|(lhs, rhs)| lhs.abs_diff_eq(&rhs, epsilon))
}
}

#[cfg(test)]
mod test {
use super::*;
Expand Down
2 changes: 1 addition & 1 deletion geo-types/src/multi_point.rs
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ where
T::default_epsilon()
}

/// Equality assertion with a absolute limit.
/// Equality assertion with an absolute limit.
///
/// # Examples
///
Expand Down
Loading

0 comments on commit b317c7b

Please sign in to comment.