From 35cb09e9ff686ec3cfeab732a676cf385c097ec3 Mon Sep 17 00:00:00 2001 From: Michael Kirk Date: Mon, 28 Dec 2020 15:01:06 -0800 Subject: [PATCH 1/4] make kernels public. I'm not sure if we want to do this, but something like this was needed to address: https://github.com/urschrei/polylabel-rs/issues/6 --- geo/src/algorithm/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/geo/src/algorithm/mod.rs b/geo/src/algorithm/mod.rs index 1c473fc68..5e3d0da30 100644 --- a/geo/src/algorithm/mod.rs +++ b/geo/src/algorithm/mod.rs @@ -1,5 +1,5 @@ /// Kernels to compute various predicates -pub(crate) mod kernels; +pub mod kernels; /// Calculate the area of the surface of a `Geometry`. pub mod area; From d21fc2199b794837984f1d7be388de02acf06346 Mon Sep 17 00:00:00 2001 From: Michael Kirk Date: Mon, 28 Dec 2020 15:24:23 -0800 Subject: [PATCH 2/4] introduce geo float --- geo/CHANGES.md | 6 ++++++ geo/src/lib.rs | 3 +++ 2 files changed, 9 insertions(+) diff --git a/geo/CHANGES.md b/geo/CHANGES.md index c8146e944..1d3516053 100644 --- a/geo/CHANGES.md +++ b/geo/CHANGES.md @@ -8,11 +8,17 @@ * * Add new `EuclideanDistance` implementations: `impl EuclideanDistance> for Line`, `impl EuclideanDistance for Coordinate`, `impl EuclideanDistance for Coordinate` * +* Introduce `geo::Float` trait so external crates can implement methods which + operate on geometries generically. + * +* Make `HasKernel` public to allow geo on exotic numeric types. + * * Fix panic when `simplify` is given a negative epsilon * * Performance improvements to `simplify` * + ## 0.16.0 * Fix panic when `simplify` is given a negative epsilon diff --git a/geo/src/lib.rs b/geo/src/lib.rs index f9bb6ed2a..75a539865 100644 --- a/geo/src/lib.rs +++ b/geo/src/lib.rs @@ -122,3 +122,6 @@ pub mod prelude { pub use crate::algorithm::vincenty_distance::VincentyDistance; pub use crate::algorithm::vincenty_length::VincentyLength; } + +pub trait Float: num_traits::Float + CoordinateType + algorithm::kernels::HasKernel {} +impl Float for T where T: num_traits::Float + CoordinateType + algorithm::kernels::HasKernel {} From 5e3c63aa13837e33bf0f3b442692f2e1eb0aafd8 Mon Sep 17 00:00:00 2001 From: Michael Kirk Date: Tue, 29 Dec 2020 17:07:01 -0800 Subject: [PATCH 3/4] apply geo::Float --- geo/src/algorithm/closest_point.rs | 16 +++---- geo/src/algorithm/concave_hull.rs | 16 +++---- geo/src/algorithm/euclidean_distance.rs | 44 +++++++++---------- geo/src/algorithm/frechet_distance.rs | 4 +- .../algorithm/polygon_distance_fast_path.rs | 7 ++- geo/src/algorithm/simplify.rs | 3 +- geo/src/types.rs | 3 +- 7 files changed, 44 insertions(+), 49 deletions(-) diff --git a/geo/src/algorithm/closest_point.rs b/geo/src/algorithm/closest_point.rs index 8e00d54b6..52714b087 100644 --- a/geo/src/algorithm/closest_point.rs +++ b/geo/src/algorithm/closest_point.rs @@ -1,7 +1,7 @@ -use crate::kernels::*; use crate::prelude::*; -use crate::{Closest, Line, LineString, MultiLineString, MultiPoint, MultiPolygon, Point, Polygon}; -use num_traits::Float; +use crate::{ + Closest, Float, Line, LineString, MultiLineString, MultiPoint, MultiPolygon, Point, Polygon, +}; use std::iter; /// Find the closest `Point` between a given geometry and an input `Point`. @@ -49,7 +49,7 @@ impl ClosestPoint for Point { } #[allow(clippy::many_single_char_names)] -impl ClosestPoint for Line { +impl ClosestPoint for Line { fn closest_point(&self, p: &Point) -> Closest { let line_length = self.euclidean_length(); if line_length == F::zero() { @@ -108,20 +108,20 @@ where best } -impl ClosestPoint for LineString { +impl ClosestPoint for LineString { fn closest_point(&self, p: &Point) -> Closest { closest_of(self.lines(), *p) } } -impl ClosestPoint for Polygon { +impl ClosestPoint for Polygon { fn closest_point(&self, p: &Point) -> Closest { let prospectives = self.interiors().iter().chain(iter::once(self.exterior())); closest_of(prospectives, *p) } } -impl ClosestPoint for MultiPolygon { +impl ClosestPoint for MultiPolygon { fn closest_point(&self, p: &Point) -> Closest { closest_of(self.iter(), *p) } @@ -133,7 +133,7 @@ impl ClosestPoint for MultiPoint { } } -impl ClosestPoint for MultiLineString { +impl ClosestPoint for MultiLineString { fn closest_point(&self, p: &Point) -> Closest { closest_of(self.iter(), *p) } diff --git a/geo/src/algorithm/concave_hull.rs b/geo/src/algorithm/concave_hull.rs index 8496159dd..803c35d1a 100644 --- a/geo/src/algorithm/concave_hull.rs +++ b/geo/src/algorithm/concave_hull.rs @@ -1,12 +1,10 @@ use crate::algorithm::convex_hull::qhull; use crate::algorithm::euclidean_distance::EuclideanDistance; use crate::algorithm::euclidean_length::EuclideanLength; -use crate::algorithm::kernels::HasKernel; use crate::prelude::Centroid; use crate::utils::partial_min; -use crate::{Line, LineString, MultiLineString, MultiPoint, MultiPolygon, Point, Polygon}; +use crate::{Float, Line, LineString, MultiLineString, MultiPoint, MultiPolygon, Point, Polygon}; use geo_types::{Coordinate, CoordinateType}; -use num_traits::Float; use rstar::{RTree, RTreeNum}; use std::collections::VecDeque; @@ -51,7 +49,7 @@ pub trait ConcaveHull { impl ConcaveHull for Polygon where - T: Float + RTreeNum + HasKernel, + T: Float + RTreeNum, { type Scalar = T; fn concave_hull(&self, concavity: Self::Scalar) -> Polygon { @@ -62,7 +60,7 @@ where impl ConcaveHull for MultiPolygon where - T: Float + RTreeNum + HasKernel, + T: Float + RTreeNum, { type Scalar = T; fn concave_hull(&self, concavity: Self::Scalar) -> Polygon { @@ -77,7 +75,7 @@ where impl ConcaveHull for LineString where - T: Float + RTreeNum + HasKernel, + T: Float + RTreeNum, { type Scalar = T; fn concave_hull(&self, concavity: Self::Scalar) -> Polygon { @@ -87,7 +85,7 @@ where impl ConcaveHull for MultiLineString where - T: Float + RTreeNum + HasKernel, + T: Float + RTreeNum, { type Scalar = T; fn concave_hull(&self, concavity: T) -> Polygon { @@ -99,7 +97,7 @@ where impl ConcaveHull for MultiPoint where - T: Float + RTreeNum + HasKernel, + T: Float + RTreeNum, { type Scalar = T; fn concave_hull(&self, concavity: T) -> Polygon { @@ -188,7 +186,7 @@ where // https://github.com/mapbox/concaveman/blob/54838e1/index.js#L11 fn concave_hull(coords: &mut [Coordinate], concavity: T) -> LineString where - T: Float + RTreeNum + HasKernel, + T: Float + RTreeNum, { let hull = qhull::quick_hull(coords); diff --git a/geo/src/algorithm/euclidean_distance.rs b/geo/src/algorithm/euclidean_distance.rs index 72790ab4c..a217c4eed 100644 --- a/geo/src/algorithm/euclidean_distance.rs +++ b/geo/src/algorithm/euclidean_distance.rs @@ -2,14 +2,14 @@ use crate::algorithm::contains::Contains; use crate::algorithm::euclidean_length::EuclideanLength; use crate::algorithm::intersects::Intersects; use crate::algorithm::polygon_distance_fast_path::*; -use crate::kernels::*; +use crate::kernels::HasKernel; use crate::utils::{coord_pos_relative_to_ring, CoordPos}; use crate::{ - Coordinate, Line, LineString, MultiLineString, MultiPoint, MultiPolygon, Point, Polygon, + Coordinate, Float, Line, LineString, MultiLineString, MultiPoint, MultiPolygon, Point, Polygon, Triangle, }; use num_traits::float::FloatConst; -use num_traits::{Bounded, Float, Signed}; +use num_traits::{Bounded, Signed}; use rstar::RTree; use rstar::RTreeNum; @@ -143,7 +143,7 @@ where impl EuclideanDistance> for Point where - T: Float + HasKernel, + T: Float, { /// Minimum distance from a Point to a Polygon fn euclidean_distance(&self, polygon: &Polygon) -> T { @@ -174,7 +174,7 @@ where impl EuclideanDistance> for Polygon where - T: Float + HasKernel, + T: Float, { /// Minimum distance from a Polygon to a Point fn euclidean_distance(&self, point: &Point) -> T { @@ -184,7 +184,7 @@ where impl EuclideanDistance> for Point where - T: Float + HasKernel, + T: Float, { /// Minimum distance from a Point to a MultiPolygon fn euclidean_distance(&self, mpolygon: &MultiPolygon) -> T { @@ -198,7 +198,7 @@ where impl EuclideanDistance> for MultiPolygon where - T: Float + HasKernel, + T: Float, { /// Minimum distance from a MultiPolygon to a Point fn euclidean_distance(&self, point: &Point) -> T { @@ -292,7 +292,7 @@ where /// LineString-LineString distance impl EuclideanDistance> for LineString where - T: Float + HasKernel + Signed + RTreeNum, + T: Float + Signed + RTreeNum, { fn euclidean_distance(&self, other: &LineString) -> T { if self.intersects(other) { @@ -320,7 +320,7 @@ where /// LineString to Line impl EuclideanDistance> for LineString where - T: Float + FloatConst + Signed + RTreeNum + HasKernel, + T: Float + FloatConst + Signed + RTreeNum, { fn euclidean_distance(&self, other: &Line) -> T { self.lines().fold(Bounded::max_value(), |acc, line| { @@ -332,7 +332,7 @@ where /// Line to LineString impl EuclideanDistance> for Line where - T: Float + FloatConst + Signed + RTreeNum + HasKernel, + T: Float + FloatConst + Signed + RTreeNum, { fn euclidean_distance(&self, other: &LineString) -> T { other.euclidean_distance(self) @@ -342,14 +342,14 @@ where /// LineString to Polygon impl EuclideanDistance> for LineString where - T: Float + FloatConst + Signed + RTreeNum + HasKernel, + T: Float + FloatConst + Signed + RTreeNum, { fn euclidean_distance(&self, other: &Polygon) -> T { if self.intersects(other) || other.contains(self) { T::zero() } else if !other.interiors().is_empty() && ring_contains_point(other, Point(self.0[0])) { // check each ring distance, returning the minimum - let mut mindist: T = Float::max_value(); + let mut mindist: T = num_traits::Float::max_value(); for ring in other.interiors() { mindist = mindist.min(nearest_neighbour_distance(self, ring)) } @@ -363,7 +363,7 @@ where /// Polygon to LineString distance impl EuclideanDistance> for Polygon where - T: Float + FloatConst + Signed + RTreeNum + HasKernel, + T: Float + FloatConst + Signed + RTreeNum, { fn euclidean_distance(&self, other: &LineString) -> T { other.euclidean_distance(self) @@ -373,7 +373,7 @@ where /// Line to MultiPolygon distance impl EuclideanDistance> for Line where - T: Float + FloatConst + Signed + RTreeNum + HasKernel, + T: Float + FloatConst + Signed + RTreeNum, { fn euclidean_distance(&self, mpolygon: &MultiPolygon) -> T { mpolygon @@ -387,7 +387,7 @@ where /// MultiPolygon to Line distance impl EuclideanDistance> for MultiPolygon where - T: Float + FloatConst + Signed + RTreeNum + HasKernel, + T: Float + FloatConst + Signed + RTreeNum, { fn euclidean_distance(&self, other: &Line) -> T { other.euclidean_distance(self) @@ -397,7 +397,7 @@ where /// Line to Line distance impl EuclideanDistance> for Line where - T: Float + FloatConst + Signed + RTreeNum + HasKernel, + T: Float + FloatConst + Signed + RTreeNum, { fn euclidean_distance(&self, other: &Line) -> T { if self.intersects(other) || self.contains(other) { @@ -415,7 +415,7 @@ where // Line to Polygon distance impl EuclideanDistance> for Line where - T: Float + Signed + RTreeNum + FloatConst + HasKernel, + T: Float + Signed + RTreeNum + FloatConst, { fn euclidean_distance(&self, other: &Polygon) -> T { if other.contains(self) || self.intersects(other) { @@ -449,7 +449,7 @@ where // Polygon to Line distance impl EuclideanDistance> for Polygon where - T: Float + FloatConst + Signed + RTreeNum + HasKernel, + T: Float + FloatConst + Signed + RTreeNum, { fn euclidean_distance(&self, other: &Line) -> T { other.euclidean_distance(self) @@ -459,7 +459,7 @@ where // Polygon to Polygon distance impl EuclideanDistance> for Polygon where - T: Float + FloatConst + RTreeNum + HasKernel, + T: Float + FloatConst + RTreeNum, { /// This implementation has a "fast path" in cases where both input polygons are convex: /// it switches to an implementation of the "rotating calipers" method described in [Pirzadeh (1999), pp24—30](http://digitool.library.mcgill.ca/R/?func=dbin-jump-full&object_id=21623&local_base=GEN01-MCG02), @@ -471,7 +471,7 @@ where // Containment check if !self.interiors().is_empty() && ring_contains_point(self, Point(poly2.exterior().0[0])) { // check each ring distance, returning the minimum - let mut mindist: T = Float::max_value(); + let mut mindist: T = num_traits::Float::max_value(); for ring in self.interiors() { mindist = mindist.min(nearest_neighbour_distance(&poly2.exterior(), ring)) } @@ -479,7 +479,7 @@ where } else if !poly2.interiors().is_empty() && ring_contains_point(poly2, Point(self.exterior().0[0])) { - let mut mindist: T = Float::max_value(); + let mut mindist: T = num_traits::Float::max_value(); for ring in poly2.interiors() { mindist = mindist.min(nearest_neighbour_distance(&self.exterior(), ring)) } @@ -497,7 +497,7 @@ where impl EuclideanDistance> for Triangle where - T: Float + HasKernel, + T: Float, { fn euclidean_distance(&self, point: &Point) -> T { if self.contains(point) { diff --git a/geo/src/algorithm/frechet_distance.rs b/geo/src/algorithm/frechet_distance.rs index c6ba378d0..a3bdba03b 100644 --- a/geo/src/algorithm/frechet_distance.rs +++ b/geo/src/algorithm/frechet_distance.rs @@ -1,7 +1,7 @@ use crate::coords_iter::CoordsIter; use crate::euclidean_distance::EuclideanDistance; -use crate::{LineString, Point}; -use num_traits::{Float, FromPrimitive}; +use crate::{Float, LineString, Point}; +use num_traits::FromPrimitive; /// Determine the similarity between two `LineStrings` using the [Frechet distance]. /// diff --git a/geo/src/algorithm/polygon_distance_fast_path.rs b/geo/src/algorithm/polygon_distance_fast_path.rs index 13b007f92..8fc4d656c 100644 --- a/geo/src/algorithm/polygon_distance_fast_path.rs +++ b/geo/src/algorithm/polygon_distance_fast_path.rs @@ -1,9 +1,8 @@ use crate::algorithm::extremes::ExtremeIndices; -use crate::kernels::*; use crate::prelude::*; -use crate::{Line, Point, Polygon, Triangle}; +use crate::{Float, Line, Point, Polygon, Triangle}; use num_traits::float::FloatConst; -use num_traits::{Float, Signed}; +use num_traits::Signed; // These are helper functions for the "fast path" of Polygon-Polygon distance // They use the rotating calipers method to speed up calculations. @@ -13,7 +12,7 @@ use num_traits::{Float, Signed}; /// using the rotating calipers method pub(crate) fn min_poly_dist(poly1: &Polygon, poly2: &Polygon) -> T where - T: Float + FloatConst + Signed + HasKernel, + T: Float + FloatConst + Signed, { let poly1_extremes = poly1.extreme_indices().unwrap(); let poly2_extremes = poly2.extreme_indices().unwrap(); diff --git a/geo/src/algorithm/simplify.rs b/geo/src/algorithm/simplify.rs index 1d66915c7..d077a800c 100644 --- a/geo/src/algorithm/simplify.rs +++ b/geo/src/algorithm/simplify.rs @@ -1,7 +1,6 @@ use crate::algorithm::coords_iter::CoordsIter; use crate::algorithm::euclidean_distance::EuclideanDistance; -use crate::{Coordinate, Line, LineString, MultiLineString, MultiPolygon, Polygon}; -use num_traits::Float; +use crate::{Float, Coordinate, Line, LineString, MultiLineString, MultiPolygon, Polygon}; // Because the RDP algorithm is recursive, we can't assign an index to a point inside the loop // instead, we wrap a simple struct around index and point in a wrapper function, diff --git a/geo/src/types.rs b/geo/src/types.rs index 240595181..54354ddb2 100644 --- a/geo/src/types.rs +++ b/geo/src/types.rs @@ -1,5 +1,4 @@ -use crate::{CoordinateType, Point}; -use num_traits::Float; +use crate::{CoordinateType, Float, Point}; /// A container for _indices_ of the minimum and maximum `Point`s of a [`Geometry`](enum.Geometry.html). #[cfg_attr(feature = "use-serde", derive(Serialize, Deserialize))] From 620dd58fe182b8adfc4af5c808962d32405716e7 Mon Sep 17 00:00:00 2001 From: Michael Kirk Date: Fri, 1 Jan 2021 14:53:51 -0800 Subject: [PATCH 4/4] rename geo::Float -> geo::GeoFloat, add docs --- geo/src/algorithm/closest_point.rs | 22 +++---- geo/src/algorithm/concave_hull.rs | 18 +++--- geo/src/algorithm/euclidean_distance.rs | 62 +++++++++---------- geo/src/algorithm/frechet_distance.rs | 8 +-- .../algorithm/polygon_distance_fast_path.rs | 24 +++---- geo/src/algorithm/simplify.rs | 24 +++---- geo/src/lib.rs | 33 +++++++++- geo/src/types.rs | 6 +- 8 files changed, 114 insertions(+), 83 deletions(-) diff --git a/geo/src/algorithm/closest_point.rs b/geo/src/algorithm/closest_point.rs index 52714b087..737d86b0a 100644 --- a/geo/src/algorithm/closest_point.rs +++ b/geo/src/algorithm/closest_point.rs @@ -1,6 +1,6 @@ use crate::prelude::*; use crate::{ - Closest, Float, Line, LineString, MultiLineString, MultiPoint, MultiPolygon, Point, Polygon, + Closest, GeoFloat, Line, LineString, MultiLineString, MultiPoint, MultiPolygon, Point, Polygon, }; use std::iter; @@ -23,7 +23,7 @@ use std::iter; /// let closest = horizontal_line.closest_point(&p); /// assert_eq!(closest, Closest::SinglePoint(Point::new(0.0, 0.0))); /// ``` -pub trait ClosestPoint> { +pub trait ClosestPoint> { /// Find the closest point between `self` and `p`. fn closest_point(&self, p: &Rhs) -> Closest; } @@ -31,14 +31,14 @@ pub trait ClosestPoint> { impl<'a, F, C> ClosestPoint for &'a C where C: ClosestPoint, - F: Float, + F: GeoFloat, { fn closest_point(&self, p: &Point) -> Closest { (*self).closest_point(p) } } -impl ClosestPoint for Point { +impl ClosestPoint for Point { fn closest_point(&self, p: &Self) -> Closest { if self == p { Closest::Intersection(*self) @@ -49,7 +49,7 @@ impl ClosestPoint for Point { } #[allow(clippy::many_single_char_names)] -impl ClosestPoint for Line { +impl ClosestPoint for Line { fn closest_point(&self, p: &Point) -> Closest { let line_length = self.euclidean_length(); if line_length == F::zero() { @@ -94,7 +94,7 @@ impl ClosestPoint for Line { /// If the iterator is empty, we get `Closest::Indeterminate`. fn closest_of(iter: I, p: Point) -> Closest where - F: Float, + F: GeoFloat, I: IntoIterator, C: ClosestPoint, { @@ -108,32 +108,32 @@ where best } -impl ClosestPoint for LineString { +impl ClosestPoint for LineString { fn closest_point(&self, p: &Point) -> Closest { closest_of(self.lines(), *p) } } -impl ClosestPoint for Polygon { +impl ClosestPoint for Polygon { fn closest_point(&self, p: &Point) -> Closest { let prospectives = self.interiors().iter().chain(iter::once(self.exterior())); closest_of(prospectives, *p) } } -impl ClosestPoint for MultiPolygon { +impl ClosestPoint for MultiPolygon { fn closest_point(&self, p: &Point) -> Closest { closest_of(self.iter(), *p) } } -impl ClosestPoint for MultiPoint { +impl ClosestPoint for MultiPoint { fn closest_point(&self, p: &Point) -> Closest { closest_of(self.iter(), *p) } } -impl ClosestPoint for MultiLineString { +impl ClosestPoint for MultiLineString { fn closest_point(&self, p: &Point) -> Closest { closest_of(self.iter(), *p) } diff --git a/geo/src/algorithm/concave_hull.rs b/geo/src/algorithm/concave_hull.rs index 803c35d1a..0d585a32d 100644 --- a/geo/src/algorithm/concave_hull.rs +++ b/geo/src/algorithm/concave_hull.rs @@ -3,7 +3,9 @@ use crate::algorithm::euclidean_distance::EuclideanDistance; use crate::algorithm::euclidean_length::EuclideanLength; use crate::prelude::Centroid; use crate::utils::partial_min; -use crate::{Float, Line, LineString, MultiLineString, MultiPoint, MultiPolygon, Point, Polygon}; +use crate::{ + GeoFloat, Line, LineString, MultiLineString, MultiPoint, MultiPolygon, Point, Polygon, +}; use geo_types::{Coordinate, CoordinateType}; use rstar::{RTree, RTreeNum}; use std::collections::VecDeque; @@ -49,7 +51,7 @@ pub trait ConcaveHull { impl ConcaveHull for Polygon where - T: Float + RTreeNum, + T: GeoFloat + RTreeNum, { type Scalar = T; fn concave_hull(&self, concavity: Self::Scalar) -> Polygon { @@ -60,7 +62,7 @@ where impl ConcaveHull for MultiPolygon where - T: Float + RTreeNum, + T: GeoFloat + RTreeNum, { type Scalar = T; fn concave_hull(&self, concavity: Self::Scalar) -> Polygon { @@ -75,7 +77,7 @@ where impl ConcaveHull for LineString where - T: Float + RTreeNum, + T: GeoFloat + RTreeNum, { type Scalar = T; fn concave_hull(&self, concavity: Self::Scalar) -> Polygon { @@ -85,7 +87,7 @@ where impl ConcaveHull for MultiLineString where - T: Float + RTreeNum, + T: GeoFloat + RTreeNum, { type Scalar = T; fn concave_hull(&self, concavity: T) -> Polygon { @@ -97,7 +99,7 @@ where impl ConcaveHull for MultiPoint where - T: Float + RTreeNum, + T: GeoFloat + RTreeNum, { type Scalar = T; fn concave_hull(&self, concavity: T) -> Polygon { @@ -115,7 +117,7 @@ fn find_point_closest_to_line( line_tree: &RTree>, ) -> Option> where - T: Float + RTreeNum, + T: GeoFloat + RTreeNum, { let h = max_dist + max_dist; let w = line.euclidean_length() + h; @@ -186,7 +188,7 @@ where // https://github.com/mapbox/concaveman/blob/54838e1/index.js#L11 fn concave_hull(coords: &mut [Coordinate], concavity: T) -> LineString where - T: Float + RTreeNum, + T: GeoFloat + RTreeNum, { let hull = qhull::quick_hull(coords); diff --git a/geo/src/algorithm/euclidean_distance.rs b/geo/src/algorithm/euclidean_distance.rs index a217c4eed..7675a2217 100644 --- a/geo/src/algorithm/euclidean_distance.rs +++ b/geo/src/algorithm/euclidean_distance.rs @@ -5,8 +5,8 @@ use crate::algorithm::polygon_distance_fast_path::*; use crate::kernels::HasKernel; use crate::utils::{coord_pos_relative_to_ring, CoordPos}; use crate::{ - Coordinate, Float, Line, LineString, MultiLineString, MultiPoint, MultiPolygon, Point, Polygon, - Triangle, + Coordinate, GeoFloat, Line, LineString, MultiLineString, MultiPoint, MultiPolygon, Point, + Polygon, Triangle, }; use num_traits::float::FloatConst; use num_traits::{Bounded, Signed}; @@ -99,7 +99,7 @@ pub trait EuclideanDistance { impl EuclideanDistance> for Coordinate where - T: Float, + T: GeoFloat, { /// Minimum distance between two `Coordinate`s fn euclidean_distance(&self, c: &Coordinate) -> T { @@ -109,7 +109,7 @@ where impl EuclideanDistance> for Point where - T: Float, + T: GeoFloat, { /// Minimum distance between two Points fn euclidean_distance(&self, p: &Point) -> T { @@ -119,7 +119,7 @@ where impl EuclideanDistance> for Point where - T: Float, + T: GeoFloat, { /// Minimum distance from a Point to a MultiPoint fn euclidean_distance(&self, points: &MultiPoint) -> T { @@ -133,7 +133,7 @@ where impl EuclideanDistance> for MultiPoint where - T: Float, + T: GeoFloat, { /// Minimum distance from a MultiPoint to a Point fn euclidean_distance(&self, point: &Point) -> T { @@ -143,7 +143,7 @@ where impl EuclideanDistance> for Point where - T: Float, + T: GeoFloat, { /// Minimum distance from a Point to a Polygon fn euclidean_distance(&self, polygon: &Polygon) -> T { @@ -174,7 +174,7 @@ where impl EuclideanDistance> for Polygon where - T: Float, + T: GeoFloat, { /// Minimum distance from a Polygon to a Point fn euclidean_distance(&self, point: &Point) -> T { @@ -184,7 +184,7 @@ where impl EuclideanDistance> for Point where - T: Float, + T: GeoFloat, { /// Minimum distance from a Point to a MultiPolygon fn euclidean_distance(&self, mpolygon: &MultiPolygon) -> T { @@ -198,7 +198,7 @@ where impl EuclideanDistance> for MultiPolygon where - T: Float, + T: GeoFloat, { /// Minimum distance from a MultiPolygon to a Point fn euclidean_distance(&self, point: &Point) -> T { @@ -208,7 +208,7 @@ where impl EuclideanDistance> for Point where - T: Float, + T: GeoFloat, { /// Minimum distance from a Point to a MultiLineString fn euclidean_distance(&self, mls: &MultiLineString) -> T { @@ -221,7 +221,7 @@ where impl EuclideanDistance> for MultiLineString where - T: Float, + T: GeoFloat, { /// Minimum distance from a MultiLineString to a Point fn euclidean_distance(&self, point: &Point) -> T { @@ -231,7 +231,7 @@ where impl EuclideanDistance> for Point where - T: Float, + T: GeoFloat, { /// Minimum distance from a Point to a LineString fn euclidean_distance(&self, linestring: &LineString) -> T { @@ -241,7 +241,7 @@ where impl EuclideanDistance> for LineString where - T: Float, + T: GeoFloat, { /// Minimum distance from a LineString to a Point fn euclidean_distance(&self, point: &Point) -> T { @@ -251,7 +251,7 @@ where impl EuclideanDistance> for Line where - T: Float, + T: GeoFloat, { /// Minimum distance from a `Line` to a `Coordinate` fn euclidean_distance(&self, coord: &Coordinate) -> T { @@ -261,7 +261,7 @@ where impl EuclideanDistance> for Coordinate where - T: Float, + T: GeoFloat, { /// Minimum distance from a `Coordinate` to a `Line` fn euclidean_distance(&self, line: &Line) -> T { @@ -271,7 +271,7 @@ where impl EuclideanDistance> for Line where - T: Float, + T: GeoFloat, { /// Minimum distance from a Line to a Point fn euclidean_distance(&self, point: &Point) -> T { @@ -281,7 +281,7 @@ where impl EuclideanDistance> for Point where - T: Float, + T: GeoFloat, { /// Minimum distance from a Line to a Point fn euclidean_distance(&self, line: &Line) -> T { @@ -292,7 +292,7 @@ where /// LineString-LineString distance impl EuclideanDistance> for LineString where - T: Float + Signed + RTreeNum, + T: GeoFloat + Signed + RTreeNum, { fn euclidean_distance(&self, other: &LineString) -> T { if self.intersects(other) { @@ -320,7 +320,7 @@ where /// LineString to Line impl EuclideanDistance> for LineString where - T: Float + FloatConst + Signed + RTreeNum, + T: GeoFloat + FloatConst + Signed + RTreeNum, { fn euclidean_distance(&self, other: &Line) -> T { self.lines().fold(Bounded::max_value(), |acc, line| { @@ -332,7 +332,7 @@ where /// Line to LineString impl EuclideanDistance> for Line where - T: Float + FloatConst + Signed + RTreeNum, + T: GeoFloat + FloatConst + Signed + RTreeNum, { fn euclidean_distance(&self, other: &LineString) -> T { other.euclidean_distance(self) @@ -342,7 +342,7 @@ where /// LineString to Polygon impl EuclideanDistance> for LineString where - T: Float + FloatConst + Signed + RTreeNum, + T: GeoFloat + FloatConst + Signed + RTreeNum, { fn euclidean_distance(&self, other: &Polygon) -> T { if self.intersects(other) || other.contains(self) { @@ -363,7 +363,7 @@ where /// Polygon to LineString distance impl EuclideanDistance> for Polygon where - T: Float + FloatConst + Signed + RTreeNum, + T: GeoFloat + FloatConst + Signed + RTreeNum, { fn euclidean_distance(&self, other: &LineString) -> T { other.euclidean_distance(self) @@ -373,7 +373,7 @@ where /// Line to MultiPolygon distance impl EuclideanDistance> for Line where - T: Float + FloatConst + Signed + RTreeNum, + T: GeoFloat + FloatConst + Signed + RTreeNum, { fn euclidean_distance(&self, mpolygon: &MultiPolygon) -> T { mpolygon @@ -387,7 +387,7 @@ where /// MultiPolygon to Line distance impl EuclideanDistance> for MultiPolygon where - T: Float + FloatConst + Signed + RTreeNum, + T: GeoFloat + FloatConst + Signed + RTreeNum, { fn euclidean_distance(&self, other: &Line) -> T { other.euclidean_distance(self) @@ -397,7 +397,7 @@ where /// Line to Line distance impl EuclideanDistance> for Line where - T: Float + FloatConst + Signed + RTreeNum, + T: GeoFloat + FloatConst + Signed + RTreeNum, { fn euclidean_distance(&self, other: &Line) -> T { if self.intersects(other) || self.contains(other) { @@ -415,7 +415,7 @@ where // Line to Polygon distance impl EuclideanDistance> for Line where - T: Float + Signed + RTreeNum + FloatConst, + T: GeoFloat + Signed + RTreeNum + FloatConst, { fn euclidean_distance(&self, other: &Polygon) -> T { if other.contains(self) || self.intersects(other) { @@ -449,7 +449,7 @@ where // Polygon to Line distance impl EuclideanDistance> for Polygon where - T: Float + FloatConst + Signed + RTreeNum, + T: GeoFloat + FloatConst + Signed + RTreeNum, { fn euclidean_distance(&self, other: &Line) -> T { other.euclidean_distance(self) @@ -459,7 +459,7 @@ where // Polygon to Polygon distance impl EuclideanDistance> for Polygon where - T: Float + FloatConst + RTreeNum, + T: GeoFloat + FloatConst + RTreeNum, { /// This implementation has a "fast path" in cases where both input polygons are convex: /// it switches to an implementation of the "rotating calipers" method described in [Pirzadeh (1999), pp24—30](http://digitool.library.mcgill.ca/R/?func=dbin-jump-full&object_id=21623&local_base=GEN01-MCG02), @@ -497,7 +497,7 @@ where impl EuclideanDistance> for Triangle where - T: Float, + T: GeoFloat, { fn euclidean_distance(&self, point: &Point) -> T { if self.contains(point) { @@ -514,7 +514,7 @@ where // This is somewhat slow and memory-inefficient, but certainly better than quadratic time pub fn nearest_neighbour_distance(geom1: &LineString, geom2: &LineString) -> T where - T: Float + RTreeNum, + T: GeoFloat + RTreeNum, { let tree_a: RTree> = RTree::bulk_load(geom1.lines().collect::>()); let tree_b: RTree> = RTree::bulk_load(geom2.lines().collect::>()); diff --git a/geo/src/algorithm/frechet_distance.rs b/geo/src/algorithm/frechet_distance.rs index a3bdba03b..e2d3fc85f 100644 --- a/geo/src/algorithm/frechet_distance.rs +++ b/geo/src/algorithm/frechet_distance.rs @@ -1,6 +1,6 @@ use crate::coords_iter::CoordsIter; use crate::euclidean_distance::EuclideanDistance; -use crate::{Float, LineString, Point}; +use crate::{GeoFloat, LineString, Point}; use num_traits::FromPrimitive; /// Determine the similarity between two `LineStrings` using the [Frechet distance]. @@ -43,7 +43,7 @@ pub trait FrechetDistance { impl FrechetDistance> for LineString where - T: Float + FromPrimitive, + T: GeoFloat + FromPrimitive, { fn frechet_distance(&self, ls: &LineString) -> T { if self.coords_count() != 0 && ls.coords_count() != 0 { @@ -61,7 +61,7 @@ where struct Data<'a, T> where - T: Float + FromPrimitive, + T: GeoFloat + FromPrimitive, { cache: Vec>, ls_a: &'a LineString, @@ -70,7 +70,7 @@ where impl<'a, T> Data<'a, T> where - T: Float + FromPrimitive, + T: GeoFloat + FromPrimitive, { fn compute(&mut self, i: usize, j: usize) -> T { if self.cache[i][j].is_nan() { diff --git a/geo/src/algorithm/polygon_distance_fast_path.rs b/geo/src/algorithm/polygon_distance_fast_path.rs index 8fc4d656c..d48c5305c 100644 --- a/geo/src/algorithm/polygon_distance_fast_path.rs +++ b/geo/src/algorithm/polygon_distance_fast_path.rs @@ -1,6 +1,6 @@ use crate::algorithm::extremes::ExtremeIndices; use crate::prelude::*; -use crate::{Float, Line, Point, Polygon, Triangle}; +use crate::{GeoFloat, Line, Point, Polygon, Triangle}; use num_traits::float::FloatConst; use num_traits::Signed; @@ -12,7 +12,7 @@ use num_traits::Signed; /// using the rotating calipers method pub(crate) fn min_poly_dist(poly1: &Polygon, poly2: &Polygon) -> T where - T: Float + FloatConst + Signed, + T: GeoFloat + FloatConst + Signed, { let poly1_extremes = poly1.extreme_indices().unwrap(); let poly2_extremes = poly2.extreme_indices().unwrap(); @@ -56,7 +56,7 @@ where /// Minimum distance between a vertex and an imaginary line drawn from p to q fn vertex_line_distance(v: Point, p: Point, q: Point) -> T where - T: Float, + T: GeoFloat, { v.euclidean_distance(&Line::new(p.0, q.0)) } @@ -64,7 +64,7 @@ where /// Wrap-around previous Polygon index fn prev_vertex(poly: &Polygon, current_vertex: usize) -> usize where - T: Float, + T: GeoFloat, { (current_vertex + (poly.exterior().0.len() - 1) - 1) % (poly.exterior().0.len() - 1) } @@ -72,7 +72,7 @@ where /// Wrap-around next Polygon index fn next_vertex(poly: &Polygon, current_vertex: usize) -> usize where - T: Float, + T: GeoFloat, { (current_vertex + 1) % (poly.exterior().0.len() - 1) } @@ -88,7 +88,7 @@ enum AlignedEdge { #[derive(Debug)] pub(crate) struct Polydist<'a, T> where - T: Float, + T: GeoFloat, T: 'a, { poly1: &'a Polygon, @@ -119,7 +119,7 @@ where // http://web.archive.org/web/20150330010154/http://cgm.cs.mcgill.ca/%7Eorm/rotcal.html fn unitvector(slope: &T, poly: &Polygon, p: Point, idx: usize) -> Point where - T: Float + Signed, + T: GeoFloat + Signed, { let tansq = slope.powi(2); let cossq = T::one() / (T::one() + tansq); @@ -267,7 +267,7 @@ where /// Perpendicular unit vector of a vertex and a unit vector fn unitpvector(p: Point, u: Point) -> Point where - T: Float, + T: GeoFloat, { let hundred = T::from(100).unwrap(); let vertical = p.x() == u.x(); @@ -317,7 +317,7 @@ where /// Angle between a vertex and an edge fn vertex_line_angle(poly: &Polygon, p: Point, m: &T, vertical: bool, idx: usize) -> T where - T: Float + FloatConst + Signed, + T: GeoFloat + FloatConst + Signed, { let hundred = T::from(100).unwrap(); let pnext = poly.exterior().0[next_vertex(poly, idx)]; @@ -398,7 +398,7 @@ where /// Does abc turn left? fn leftturn(a: Point, b: Point, c: Point) -> i8 where - T: Float, + T: GeoFloat, { let narea = Triangle::from([a, b, c]).signed_area(); if narea > T::zero() { @@ -413,7 +413,7 @@ where /// Calculate next set of caliper points fn nextpoints(state: &mut Polydist) where - T: Float + FloatConst + Signed, + T: GeoFloat + FloatConst + Signed, { state.alignment = None; state.ip1 = false; @@ -504,7 +504,7 @@ where /// compute the minimum distance between entities (edges or vertices) fn computemin(state: &mut Polydist) where - T: Float + Signed, + T: GeoFloat + Signed, { let u; let u1; diff --git a/geo/src/algorithm/simplify.rs b/geo/src/algorithm/simplify.rs index d077a800c..2607f1002 100644 --- a/geo/src/algorithm/simplify.rs +++ b/geo/src/algorithm/simplify.rs @@ -1,6 +1,6 @@ use crate::algorithm::coords_iter::CoordsIter; use crate::algorithm::euclidean_distance::EuclideanDistance; -use crate::{Float, Coordinate, Line, LineString, MultiLineString, MultiPolygon, Polygon}; +use crate::{Coordinate, GeoFloat, Line, LineString, MultiLineString, MultiPolygon, Polygon}; // Because the RDP algorithm is recursive, we can't assign an index to a point inside the loop // instead, we wrap a simple struct around index and point in a wrapper function, @@ -8,7 +8,7 @@ use crate::{Float, Coordinate, Line, LineString, MultiLineString, MultiPolygon, #[derive(Copy, Clone)] struct RdpIndex where - T: Float, + T: GeoFloat, { index: usize, coord: Coordinate, @@ -17,7 +17,7 @@ where // Wrapper for the RDP algorithm, returning simplified points fn rdp(coords: impl Iterator>, epsilon: &T) -> Vec> where - T: Float, + T: GeoFloat, { // Epsilon must be greater than zero for any meaningful simplification to happen if *epsilon <= T::zero() { @@ -38,7 +38,7 @@ where // Wrapper for the RDP algorithm, returning simplified point indices fn calculate_rdp_indices(rdp_indices: &[RdpIndex], epsilon: &T) -> Vec where - T: Float, + T: GeoFloat, { if *epsilon <= T::zero() { return rdp_indices.iter().map(|rdp_index| rdp_index.index).collect(); @@ -54,7 +54,7 @@ where // for more flexible use by FFI implementers fn compute_rdp(rdp_indices: &[RdpIndex], epsilon: &T) -> Vec> where - T: Float, + T: GeoFloat, { if rdp_indices.is_empty() { return vec![]; @@ -136,7 +136,7 @@ pub trait Simplify { /// ``` fn simplify(&self, epsilon: &T) -> Self where - T: Float; + T: GeoFloat; } /// Simplifies a geometry, returning the retained _indices_ of the input. @@ -175,12 +175,12 @@ pub trait SimplifyIdx { /// ``` fn simplify_idx(&self, epsilon: &T) -> Vec where - T: Float; + T: GeoFloat; } impl Simplify for LineString where - T: Float, + T: GeoFloat, { fn simplify(&self, epsilon: &T) -> Self { LineString::from(rdp(self.coords_iter(), epsilon)) @@ -189,7 +189,7 @@ where impl SimplifyIdx for LineString where - T: Float, + T: GeoFloat, { fn simplify_idx(&self, epsilon: &T) -> Vec { calculate_rdp_indices( @@ -209,7 +209,7 @@ where impl Simplify for MultiLineString where - T: Float, + T: GeoFloat, { fn simplify(&self, epsilon: &T) -> Self { MultiLineString(self.iter().map(|l| l.simplify(epsilon)).collect()) @@ -218,7 +218,7 @@ where impl Simplify for Polygon where - T: Float, + T: GeoFloat, { fn simplify(&self, epsilon: &T) -> Self { Polygon::new( @@ -233,7 +233,7 @@ where impl Simplify for MultiPolygon where - T: Float, + T: GeoFloat, { fn simplify(&self, epsilon: &T) -> Self { MultiPolygon(self.iter().map(|p| p.simplify(epsilon)).collect()) diff --git a/geo/src/lib.rs b/geo/src/lib.rs index 75a539865..7e8ae79c6 100644 --- a/geo/src/lib.rs +++ b/geo/src/lib.rs @@ -123,5 +123,34 @@ pub mod prelude { pub use crate::algorithm::vincenty_length::VincentyLength; } -pub trait Float: num_traits::Float + CoordinateType + algorithm::kernels::HasKernel {} -impl Float for T where T: num_traits::Float + CoordinateType + algorithm::kernels::HasKernel {} +/// A common floating point trait used for geo algorithms. +/// +/// Different numeric types have different tradeoffs. `geo` strives to utilize generics to allow +/// users to choose their numeric types. If you are writing a function which you'd like to be +/// generic over the floating point types supported by geo, you probably want to constrain +/// your function input to GeoFloat. +/// +/// # Examples +/// +/// ``` +/// use geo::{GeoFloat, MultiPolygon, Polygon, Point}; +/// +/// // An admittedly silly method implementation, but the signature shows how to use the GeoFloat trait +/// fn farthest_from<'a, T: GeoFloat>(point: &Point, polygons: &'a MultiPolygon) -> Option<&'a Polygon> { +/// polygons.iter().fold(None, |accum, next| { +/// match accum { +/// None => Some(next), +/// Some(farthest) => { +/// use geo::algorithm::{euclidean_distance::EuclideanDistance}; +/// if next.euclidean_distance(point) > farthest.euclidean_distance(point) { +/// Some(next) +/// } else { +/// Some(farthest) +/// } +/// } +/// } +/// }) +/// } +/// ``` +pub trait GeoFloat: num_traits::Float + CoordinateType + algorithm::kernels::HasKernel {} +impl GeoFloat for T where T: num_traits::Float + CoordinateType + algorithm::kernels::HasKernel {} diff --git a/geo/src/types.rs b/geo/src/types.rs index 54354ddb2..a922ad9eb 100644 --- a/geo/src/types.rs +++ b/geo/src/types.rs @@ -1,4 +1,4 @@ -use crate::{CoordinateType, Float, Point}; +use crate::{CoordinateType, GeoFloat, Point}; /// A container for _indices_ of the minimum and maximum `Point`s of a [`Geometry`](enum.Geometry.html). #[cfg_attr(feature = "use-serde", derive(Serialize, Deserialize))] @@ -37,7 +37,7 @@ where /// The result of trying to find the closest spot on an object to a point. #[cfg_attr(feature = "use-serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone, Copy, PartialEq)] -pub enum Closest { +pub enum Closest { /// The point actually intersects with the object. Intersection(Point), /// There is exactly one place on this object which is closest to the point. @@ -46,7 +46,7 @@ pub enum Closest { Indeterminate, } -impl Closest { +impl Closest { /// Compare two `Closest`s relative to `p` and return a copy of the best /// one. pub fn best_of_two(&self, other: &Self, p: Point) -> Self {