diff --git a/geo-types/Cargo.toml b/geo-types/Cargo.toml index 72401914b..bffb2e8b1 100644 --- a/geo-types/Cargo.toml +++ b/geo-types/Cargo.toml @@ -16,6 +16,7 @@ std = ["approx?/std", "num-traits/std", "serde?/std"] # 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 +bounding_rect = [] rstar = ["rstar_0_8"] use-rstar = ["use-rstar_0_8"] use-rstar_0_8 = ["rstar_0_8", "approx"] diff --git a/geo-types/src/bounding_rect.rs b/geo-types/src/bounding_rect.rs new file mode 100644 index 000000000..8ac3e225f --- /dev/null +++ b/geo-types/src/bounding_rect.rs @@ -0,0 +1,326 @@ +use crate::private_utils::{ + get_bounding_rect, line_string_bounding_rect, partial_max, partial_min, +}; +use crate::{coord, geometry::*, CoordNum}; + +/// Calculation of the bounding rectangle of a geometry. +pub trait BoundingRect { + type Output: Into>>; + + /// Return the bounding rectangle of a geometry + /// + /// # Examples + /// + /// ``` + /// use geo_types::bounding_rect::BoundingRect; + /// use geo_types::line_string; + /// + /// let line_string = line_string![ + /// (x: 40.02f64, y: 116.34), + /// (x: 42.02f64, y: 116.34), + /// (x: 42.02f64, y: 118.34), + /// ]; + /// + /// let bounding_rect = line_string.bounding_rect().unwrap(); + /// + /// assert_eq!(40.02f64, bounding_rect.min().x); + /// assert_eq!(42.02f64, bounding_rect.max().x); + /// assert_eq!(116.34, bounding_rect.min().y); + /// assert_eq!(118.34, bounding_rect.max().y); + /// ``` + fn bounding_rect(&self) -> Self::Output; +} + +impl BoundingRect for Coord +where + T: CoordNum, +{ + type Output = Rect; + + /// Return the bounding rectangle for a `Coord`. It will have zero width + /// and zero height. + fn bounding_rect(&self) -> Self::Output { + Rect::new(*self, *self) + } +} + +impl BoundingRect for Point +where + T: CoordNum, +{ + type Output = Rect; + + /// Return the bounding rectangle for a `Point`. It will have zero width + /// and zero height. + fn bounding_rect(&self) -> Self::Output { + Rect::new(self.0, self.0) + } +} + +impl BoundingRect for MultiPoint +where + T: CoordNum, +{ + type Output = Option>; + + /// + /// Return the BoundingRect for a MultiPoint + fn bounding_rect(&self) -> Self::Output { + get_bounding_rect(self.0.iter().map(|p| p.0)) + } +} + +impl BoundingRect for Line +where + T: CoordNum, +{ + type Output = Rect; + + fn bounding_rect(&self) -> Self::Output { + Rect::new(self.start, self.end) + } +} + +impl BoundingRect for LineString +where + T: CoordNum, +{ + type Output = Option>; + + /// + /// Return the BoundingRect for a LineString + fn bounding_rect(&self) -> Self::Output { + line_string_bounding_rect(self) + } +} + +impl BoundingRect for MultiLineString +where + T: CoordNum, +{ + type Output = Option>; + + /// + /// Return the BoundingRect for a MultiLineString + fn bounding_rect(&self) -> Self::Output { + get_bounding_rect(self.iter().flat_map(|line| line.0.iter().cloned())) + } +} + +impl BoundingRect for Polygon +where + T: CoordNum, +{ + type Output = Option>; + + /// + /// Return the BoundingRect for a Polygon + fn bounding_rect(&self) -> Self::Output { + let line = self.exterior(); + get_bounding_rect(line.0.iter().cloned()) + } +} + +impl BoundingRect for MultiPolygon +where + T: CoordNum, +{ + type Output = Option>; + + /// + /// Return the BoundingRect for a MultiPolygon + fn bounding_rect(&self) -> Self::Output { + get_bounding_rect( + self.iter() + .flat_map(|poly| poly.exterior().0.iter().cloned()), + ) + } +} + +impl BoundingRect for Triangle +where + T: CoordNum, +{ + type Output = Rect; + + fn bounding_rect(&self) -> Self::Output { + get_bounding_rect(self.to_array().iter().cloned()).unwrap() + } +} + +impl BoundingRect for Rect +where + T: CoordNum, +{ + type Output = Rect; + + fn bounding_rect(&self) -> Self::Output { + *self + } +} + +impl BoundingRect for Geometry +where + T: CoordNum, +{ + type Output = Option>; + + crate::geometry_delegate_impl! { + fn bounding_rect(&self) -> Self::Output; + } +} + +impl BoundingRect for GeometryCollection +where + T: CoordNum, +{ + type Output = Option>; + + fn bounding_rect(&self) -> Self::Output { + self.iter().fold(None, |acc, next| { + let next_bounding_rect = next.bounding_rect(); + + match (acc, next_bounding_rect) { + (None, None) => None, + (Some(r), None) | (None, Some(r)) => Some(r), + (Some(r1), Some(r2)) => Some(bounding_rect_merge(r1, r2)), + } + }) + } +} + +// Return a new rectangle that encompasses the provided rectangles +fn bounding_rect_merge(a: Rect, b: Rect) -> Rect { + Rect::new( + coord! { + x: partial_min(a.min().x, b.min().x), + y: partial_min(a.min().y, b.min().y), + }, + coord! { + x: partial_max(a.max().x, b.max().x), + y: partial_max(a.max().y, b.max().y), + }, + ) +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn empty_linestring_test() { + let linestring: LineString = line_string![]; + let bounding_rect = linestring.bounding_rect(); + assert!(bounding_rect.is_none()); + } + #[test] + fn linestring_one_point_test() { + let linestring = line_string![(x: 40.02f64, y: 116.34)]; + let bounding_rect = Rect::new( + coord! { + x: 40.02f64, + y: 116.34, + }, + coord! { + x: 40.02, + y: 116.34, + }, + ); + assert_eq!(bounding_rect, linestring.bounding_rect().unwrap()); + } + #[test] + fn linestring_test() { + let linestring = line_string![ + (x: 1., y: 1.), + (x: 2., y: -2.), + (x: -3., y: -3.), + (x: -4., y: 4.) + ]; + let bounding_rect = Rect::new(coord! { x: -4., y: -3. }, coord! { x: 2., y: 4. }); + assert_eq!(bounding_rect, linestring.bounding_rect().unwrap()); + } + #[test] + fn multilinestring_test() { + let multiline = MultiLineString::new(vec![ + line_string![(x: 1., y: 1.), (x: -40., y: 1.)], + line_string![(x: 1., y: 1.), (x: 50., y: 1.)], + line_string![(x: 1., y: 1.), (x: 1., y: -60.)], + line_string![(x: 1., y: 1.), (x: 1., y: 70.)], + ]); + let bounding_rect = Rect::new(coord! { x: -40., y: -60. }, coord! { x: 50., y: 70. }); + assert_eq!(bounding_rect, multiline.bounding_rect().unwrap()); + } + #[test] + fn multipoint_test() { + let multipoint = MultiPoint::from(vec![(1., 1.), (2., -2.), (-3., -3.), (-4., 4.)]); + let bounding_rect = Rect::new(coord! { x: -4., y: -3. }, coord! { x: 2., y: 4. }); + assert_eq!(bounding_rect, multipoint.bounding_rect().unwrap()); + } + #[test] + fn polygon_test() { + let linestring = line_string![ + (x: 0., y: 0.), + (x: 5., y: 0.), + (x: 5., y: 6.), + (x: 0., y: 6.), + (x: 0., y: 0.), + ]; + let line_bounding_rect = linestring.bounding_rect().unwrap(); + let poly = Polygon::new(linestring, Vec::new()); + assert_eq!(line_bounding_rect, poly.bounding_rect().unwrap()); + } + #[test] + fn multipolygon_test() { + let mpoly = MultiPolygon::new(vec![ + polygon![(x: 0., y: 0.), (x: 50., y: 0.), (x: 0., y: -70.), (x: 0., y: 0.)], + polygon![(x: 0., y: 0.), (x: 5., y: 0.), (x: 0., y: 80.), (x: 0., y: 0.)], + polygon![(x: 0., y: 0.), (x: -60., y: 0.), (x: 0., y: 6.), (x: 0., y: 0.)], + ]); + let bounding_rect = Rect::new(coord! { x: -60., y: -70. }, coord! { x: 50., y: 80. }); + assert_eq!(bounding_rect, mpoly.bounding_rect().unwrap()); + } + #[test] + fn line_test() { + let line1 = Line::new(coord! { x: 0., y: 1. }, coord! { x: 2., y: 3. }); + let line2 = Line::new(coord! { x: 2., y: 3. }, coord! { x: 0., y: 1. }); + assert_eq!( + line1.bounding_rect(), + Rect::new(coord! { x: 0., y: 1. }, coord! { x: 2., y: 3. },) + ); + assert_eq!( + line2.bounding_rect(), + Rect::new(coord! { x: 0., y: 1. }, coord! { x: 2., y: 3. },) + ); + } + + #[test] + fn bounding_rect_merge_test() { + assert_eq!( + bounding_rect_merge( + Rect::new(coord! { x: 0., y: 0. }, coord! { x: 1., y: 1. }), + Rect::new(coord! { x: 1., y: 1. }, coord! { x: 2., y: 2. }), + ), + Rect::new(coord! { x: 0., y: 0. }, coord! { x: 2., y: 2. }), + ); + } + + #[test] + fn point_bounding_rect_test() { + assert_eq!( + Rect::new(coord! { x: 1., y: 2. }, coord! { x: 1., y: 2. }), + point! { x: 1., y: 2. }.bounding_rect(), + ); + } + + #[test] + fn geometry_collection_bounding_rect_test() { + assert_eq!( + Some(Rect::new(coord! { x: 0., y: 0. }, coord! { x: 1., y: 2. })), + GeometryCollection::new_from(vec![ + Geometry::Point(point! { x: 0., y: 0. }), + Geometry::Point(point! { x: 1., y: 2. }), + ]) + .bounding_rect(), + ); + } +} diff --git a/geo-types/src/geometry/geometry_collection.rs b/geo-types/src/geometry/geometry_collection.rs index a870f1ff7..b16ba4562 100644 --- a/geo-types/src/geometry/geometry_collection.rs +++ b/geo-types/src/geometry/geometry_collection.rs @@ -1,11 +1,24 @@ +#[cfg(any( + feature = "rstar_0_8", + feature = "rstar_0_9", + feature = "rstar_0_10", + feature = "rstar_0_11" +))] +use crate::Point; use crate::{CoordNum, Geometry}; - use alloc::vec; use alloc::vec::Vec; #[cfg(any(feature = "approx", test))] use approx::{AbsDiffEq, RelativeEq}; use core::iter::FromIterator; use core::ops::{Index, IndexMut}; +#[cfg(any( + feature = "rstar_0_8", + feature = "rstar_0_9", + feature = "rstar_0_10", + feature = "rstar_0_11" +))] +use num_traits::Bounded; /// A collection of [`Geometry`](enum.Geometry.html) types. /// @@ -247,6 +260,53 @@ impl<'a, T: CoordNum> GeometryCollection { } } +#[cfg(any( + feature = "rstar_0_8", + feature = "rstar_0_9", + feature = "rstar_0_10", + feature = "rstar_0_11" +))] +macro_rules! impl_rstar_geometry_collection { + ($rstar:ident) => { + impl $rstar::RTreeObject for GeometryCollection + where + T: ::num_traits::Float + ::$rstar::RTreeNum, + { + type Envelope = ::$rstar::AABB>; + + fn envelope(&self) -> Self::Envelope { + let bounding_rect = self.iter().fold( + <::$rstar::AABB> as ::$rstar::Envelope>::new_empty(), + |acc: ::$rstar::AABB>, next| { + let next_bounding_rect = next.envelope(); + <::$rstar::AABB> as ::$rstar::Envelope>::merged( + &acc, + &next_bounding_rect, + ) + }, + ); + if bounding_rect == <::$rstar::AABB> as ::$rstar::Envelope>::new_empty() { + ::$rstar::AABB::from_corners( + Point::new(Bounded::min_value(), Bounded::min_value()), + Point::new(Bounded::max_value(), Bounded::max_value()), + ) + } else { + bounding_rect + } + } + } + }; +} + +#[cfg(feature = "rstar_0_8")] +impl_rstar_geometry_collection!(rstar_0_8); + +#[cfg(feature = "rstar_0_9")] +impl_rstar_geometry_collection!(rstar_0_9); + +#[cfg(feature = "rstar_0_10")] +impl_rstar_geometry_collection!(rstar_0_10); + #[cfg(any(feature = "approx", test))] impl RelativeEq for GeometryCollection where diff --git a/geo-types/src/geometry/mod.rs b/geo-types/src/geometry/mod.rs index 4faf01753..115613dc5 100644 --- a/geo-types/src/geometry/mod.rs +++ b/geo-types/src/geometry/mod.rs @@ -269,6 +269,47 @@ where } } +#[cfg(any( + feature = "rstar_0_8", + feature = "rstar_0_9", + feature = "rstar_0_10", + feature = "rstar_0_11" +))] +macro_rules! impl_rstar_geometry { + ($rstar:ident) => { + impl $rstar::RTreeObject for Geometry + where + T: ::num_traits::Float + ::$rstar::RTreeNum, + { + type Envelope = ::$rstar::AABB>; + + fn envelope(&self) -> Self::Envelope { + match self { + Self::Point(x) => x.envelope(), + Self::Line(x) => x.envelope(), + Self::LineString(x) => x.envelope(), + Self::Polygon(x) => x.envelope(), + Self::Rect(x) => x.envelope(), + Self::Triangle(x) => x.envelope(), + Self::MultiPoint(x) => x.envelope(), + Self::MultiLineString(x) => x.envelope(), + Self::MultiPolygon(x) => x.envelope(), + Self::GeometryCollection(x) => x.envelope(), + } + } + } + }; +} + +#[cfg(feature = "rstar_0_8")] +impl_rstar_geometry!(rstar_0_8); + +#[cfg(feature = "rstar_0_9")] +impl_rstar_geometry!(rstar_0_9); + +#[cfg(feature = "rstar_0_10")] +impl_rstar_geometry!(rstar_0_10); + #[cfg(any(feature = "approx", test))] impl RelativeEq for Geometry where @@ -371,3 +412,151 @@ impl + CoordNum> AbsDiffEq for Geometry { } } } + +/// Implements the common pattern where a Geometry enum simply delegates its trait impl to it's inner type. +/// +/// ``` +/// # use geo_types::{CoordNum, Coord, Point, Line, LineString, Polygon, MultiPoint, MultiLineString, MultiPolygon, GeometryCollection, Rect, Triangle, Geometry}; +/// +/// trait Foo { +/// fn foo_1(&self, coord: Coord) -> bool; +/// fn foo_2(&self) -> i32; +/// } +/// +/// // Assuming we have an impl for all the inner types like this: +/// impl Foo for Point { +/// fn foo_1(&self, coord: Coord) -> bool { true } +/// fn foo_2(&self) -> i32 { 1 } +/// } +/// impl Foo for Line { +/// fn foo_1(&self, coord: Coord) -> bool { false } +/// fn foo_2(&self) -> i32 { 2 } +/// } +/// impl Foo for LineString { +/// fn foo_1(&self, coord: Coord) -> bool { true } +/// fn foo_2(&self) -> i32 { 3 } +/// } +/// impl Foo for Polygon { +/// fn foo_1(&self, coord: Coord) -> bool { false } +/// fn foo_2(&self) -> i32 { 4 } +/// } +/// impl Foo for MultiPoint { +/// fn foo_1(&self, coord: Coord) -> bool { true } +/// fn foo_2(&self) -> i32 { 5 } +/// } +/// impl Foo for MultiLineString { +/// fn foo_1(&self, coord: Coord) -> bool { false } +/// fn foo_2(&self) -> i32 { 6 } +/// } +/// impl Foo for MultiPolygon { +/// fn foo_1(&self, coord: Coord) -> bool { true } +/// fn foo_2(&self) -> i32 { 7 } +/// } +/// impl Foo for GeometryCollection { +/// fn foo_1(&self, coord: Coord) -> bool { false } +/// fn foo_2(&self) -> i32 { 8 } +/// } +/// impl Foo for Rect { +/// fn foo_1(&self, coord: Coord) -> bool { true } +/// fn foo_2(&self) -> i32 { 9 } +/// } +/// impl Foo for Triangle { +/// fn foo_1(&self, coord: Coord) -> bool { true } +/// fn foo_2(&self) -> i32 { 10 } +/// } +/// +/// // If we want the impl for Geometry to simply delegate to it's +/// // inner case... +/// impl Foo for Geometry { +/// // Instead of writing out this trivial enum delegation... +/// // fn foo_1(&self, coord: Coord) -> bool { +/// // match self { +/// // Geometry::Point(g) => g.foo_1(coord), +/// // Geometry::LineString(g) => g.foo_1(coord), +/// // _ => unimplemented!("...etc for other cases") +/// // } +/// // } +/// // +/// // fn foo_2(&self) -> i32 { +/// // match self { +/// // Geometry::Point(g) => g.foo_2(), +/// // Geometry::LineString(g) => g.foo_2(), +/// // _ => unimplemented!("...etc for other cases") +/// // } +/// // } +/// +/// // we can equivalently write: +/// geo_types::geometry_delegate_impl! { +/// fn foo_1(&self, coord: Coord) -> bool; +/// fn foo_2(&self) -> i32; +/// } +/// } +/// ``` +#[macro_export] +macro_rules! geometry_delegate_impl { + ($($a:tt)*) => { $crate::__geometry_delegate_impl_helper!{ Geometry, $($a)* } } +} + +#[doc(hidden)] +#[macro_export] +macro_rules! geometry_cow_delegate_impl { + ($($a:tt)*) => { $crate::__geometry_delegate_impl_helper!{ GeometryCow, $($a)* } } +} + +#[doc(hidden)] +#[macro_export] +macro_rules! __geometry_delegate_impl_helper { + ( + $enum:ident, + $( + $(#[$outer:meta])* + fn $func_name: ident(&$($self_life:lifetime)?self $(, $arg_name: ident: $arg_type: ty)*) -> $return: ty; + )+ + ) => { + $( + $(#[$outer])* + fn $func_name(&$($self_life)? self, $($arg_name: $arg_type),*) -> $return { + match self { + $enum::Point(g) => g.$func_name($($arg_name),*).into(), + $enum::Line(g) => g.$func_name($($arg_name),*).into(), + $enum::LineString(g) => g.$func_name($($arg_name),*).into(), + $enum::Polygon(g) => g.$func_name($($arg_name),*).into(), + $enum::MultiPoint(g) => g.$func_name($($arg_name),*).into(), + $enum::MultiLineString(g) => g.$func_name($($arg_name),*).into(), + $enum::MultiPolygon(g) => g.$func_name($($arg_name),*).into(), + $enum::GeometryCollection(g) => g.$func_name($($arg_name),*).into(), + $enum::Rect(g) => g.$func_name($($arg_name),*).into(), + $enum::Triangle(g) => g.$func_name($($arg_name),*).into(), + } + } + )+ + }; +} + +#[cfg(test)] +mod tests { + #[cfg(feature = "rstar_0_9")] + use super::*; + #[cfg(feature = "rstar_0_9")] + use crate::point; + #[cfg(feature = "rstar_0_9")] + use rstar_0_9::{RTree, AABB}; + + #[test] + #[cfg(feature = "rstar_0_9")] + fn test_geometry_enum_rstar() { + let mut tree = RTree::new(); + let p1 = point!(x: 1.0, y: 1.0); + let p2 = point!(x: 2.0, y: 2.0); + let p3 = point!(x: 3.0, y: 3.0); + let pg1: Geometry = p1.into(); + let pg2: Geometry = p2.into(); + let pg3: Geometry = p3.into(); + tree.insert(pg1); + tree.insert(pg2); + tree.insert(pg3); + let qp = point!(x: 2.0, y: 2.0); + let found = tree.locate_in_envelope(&AABB::from_point(qp)).next(); + assert_eq!(found.unwrap(), &point!(x: 2.0, y: 2.0).into()); + } +} diff --git a/geo-types/src/geometry/multi_line_string.rs b/geo-types/src/geometry/multi_line_string.rs index 0e05673af..f9136a96e 100644 --- a/geo-types/src/geometry/multi_line_string.rs +++ b/geo-types/src/geometry/multi_line_string.rs @@ -1,3 +1,18 @@ +#[cfg(any( + feature = "rstar_0_8", + feature = "rstar_0_9", + feature = "rstar_0_10", + feature = "rstar_0_11" +))] +use crate::Point; +#[cfg(any( + feature = "rstar_0_8", + feature = "rstar_0_9", + feature = "rstar_0_10", + feature = "rstar_0_11" +))] +use num_traits::Bounded; + use crate::{CoordNum, LineString}; use alloc::vec; @@ -118,6 +133,48 @@ impl MultiLineString { } } +#[cfg(any( + feature = "rstar_0_8", + feature = "rstar_0_9", + feature = "rstar_0_10", + feature = "rstar_0_11" +))] +macro_rules! impl_rstar_multi_linestring { + ($rstar:ident) => { + impl $rstar::RTreeObject for MultiLineString + where + T: ::num_traits::Float + ::$rstar::RTreeNum, + { + type Envelope = ::$rstar::AABB>; + + fn envelope(&self) -> Self::Envelope { + let bounding_rect = crate::private_utils::get_bounding_rect( + self.iter().flat_map(|line| line.0.iter().cloned()), + ); + 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), + ), + } + } + } + }; +} + +#[cfg(feature = "rstar_0_8")] +impl_rstar_multi_linestring!(rstar_0_8); + +#[cfg(feature = "rstar_0_9")] +impl_rstar_multi_linestring!(rstar_0_9); + +#[cfg(feature = "rstar_0_10")] +impl_rstar_multi_linestring!(rstar_0_10); + #[cfg(any(feature = "approx", test))] impl RelativeEq for MultiLineString where diff --git a/geo-types/src/geometry/multi_point.rs b/geo-types/src/geometry/multi_point.rs index d28e59d8e..b9a6c9960 100644 --- a/geo-types/src/geometry/multi_point.rs +++ b/geo-types/src/geometry/multi_point.rs @@ -1,7 +1,13 @@ use crate::{CoordNum, Point}; - #[cfg(any(feature = "approx", test))] use approx::{AbsDiffEq, RelativeEq}; +#[cfg(any( + feature = "rstar_0_8", + feature = "rstar_0_9", + feature = "rstar_0_10", + feature = "rstar_0_11" +))] +use num_traits::Bounded; use alloc::vec; use alloc::vec::Vec; @@ -99,6 +105,47 @@ impl MultiPoint { } } +#[cfg(any( + feature = "rstar_0_8", + feature = "rstar_0_9", + feature = "rstar_0_10", + feature = "rstar_0_11" +))] +macro_rules! impl_rstar_multi_point { + ($rstar:ident) => { + impl $rstar::RTreeObject for MultiPoint + where + T: ::num_traits::Float + ::$rstar::RTreeNum, + { + type Envelope = ::$rstar::AABB>; + + fn envelope(&self) -> Self::Envelope { + let bounding_rect = + crate::private_utils::get_bounding_rect(self.0.iter().map(|p| p.0)); + 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), + ), + } + } + } + }; +} + +#[cfg(feature = "rstar_0_8")] +impl_rstar_multi_point!(rstar_0_8); + +#[cfg(feature = "rstar_0_9")] +impl_rstar_multi_point!(rstar_0_9); + +#[cfg(feature = "rstar_0_10")] +impl_rstar_multi_point!(rstar_0_10); + #[cfg(any(feature = "approx", test))] impl RelativeEq for MultiPoint where diff --git a/geo-types/src/geometry/multi_polygon.rs b/geo-types/src/geometry/multi_polygon.rs index 86f68768d..8dad68f47 100644 --- a/geo-types/src/geometry/multi_polygon.rs +++ b/geo-types/src/geometry/multi_polygon.rs @@ -1,4 +1,18 @@ +#[cfg(any( + feature = "rstar_0_8", + feature = "rstar_0_9", + feature = "rstar_0_10", + feature = "rstar_0_11" +))] +use crate::Point; use crate::{CoordNum, Polygon}; +#[cfg(any( + feature = "rstar_0_8", + feature = "rstar_0_9", + feature = "rstar_0_10", + feature = "rstar_0_11" +))] +use num_traits::Bounded; use alloc::vec; use alloc::vec::Vec; @@ -91,6 +105,49 @@ impl MultiPolygon { } } +#[cfg(any( + feature = "rstar_0_8", + feature = "rstar_0_9", + feature = "rstar_0_10", + feature = "rstar_0_11" +))] +macro_rules! impl_rstar_multi_polygon { + ($rstar:ident) => { + impl $rstar::RTreeObject for MultiPolygon + where + T: ::num_traits::Float + ::$rstar::RTreeNum, + { + type Envelope = ::$rstar::AABB>; + + fn envelope(&self) -> Self::Envelope { + let bounding_rect = crate::private_utils::get_bounding_rect( + self.iter() + .flat_map(|poly| poly.exterior().0.iter().cloned()), + ); + 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), + ), + } + } + } + }; +} + +#[cfg(feature = "rstar_0_8")] +impl_rstar_multi_polygon!(rstar_0_8); + +#[cfg(feature = "rstar_0_9")] +impl_rstar_multi_polygon!(rstar_0_9); + +#[cfg(feature = "rstar_0_10")] +impl_rstar_multi_polygon!(rstar_0_10); + #[cfg(any(feature = "approx", test))] impl RelativeEq for MultiPolygon where diff --git a/geo-types/src/geometry/point.rs b/geo-types/src/geometry/point.rs index 16ca9a8d6..39e1d2283 100644 --- a/geo-types/src/geometry/point.rs +++ b/geo-types/src/geometry/point.rs @@ -586,93 +586,53 @@ where } } -#[cfg(feature = "rstar_0_8")] -// These are required for rstar RTree -impl ::rstar_0_8::Point for Point -where - T: ::num_traits::Float + ::rstar_0_8::RTreeNum, -{ - type Scalar = T; - - const DIMENSIONS: usize = 2; - - fn generate(generator: impl Fn(usize) -> Self::Scalar) -> Self { - Point::new(generator(0), generator(1)) - } - - fn nth(&self, index: usize) -> Self::Scalar { - match index { - 0 => self.0.x, - 1 => self.0.y, - _ => unreachable!(), - } - } - fn nth_mut(&mut self, index: usize) -> &mut Self::Scalar { - match index { - 0 => &mut self.0.x, - 1 => &mut self.0.y, - _ => unreachable!(), +#[cfg(any( + feature = "rstar_0_8", + feature = "rstar_0_9", + feature = "rstar_0_10", + feature = "rstar_0_11" +))] +macro_rules! impl_rstar_point { + ($rstar:ident) => { + // These are required for rstar RTree + impl $rstar::Point for Point + where + T: ::num_traits::Float + ::$rstar::RTreeNum, + { + type Scalar = T; + + const DIMENSIONS: usize = 2; + + fn generate(mut generator: impl FnMut(usize) -> Self::Scalar) -> Self { + Point::new(generator(0), generator(1)) + } + + fn nth(&self, index: usize) -> Self::Scalar { + match index { + 0 => self.0.x, + 1 => self.0.y, + _ => unreachable!(), + } + } + fn nth_mut(&mut self, index: usize) -> &mut Self::Scalar { + match index { + 0 => &mut self.0.x, + 1 => &mut self.0.y, + _ => unreachable!(), + } + } } - } + }; } -#[cfg(feature = "rstar_0_9")] -impl ::rstar_0_9::Point for Point -where - T: ::num_traits::Float + ::rstar_0_9::RTreeNum, -{ - type Scalar = T; - - const DIMENSIONS: usize = 2; - - fn generate(mut generator: impl FnMut(usize) -> Self::Scalar) -> Self { - Point::new(generator(0), generator(1)) - } +#[cfg(feature = "rstar_0_8")] +impl_rstar_point!(rstar_0_8); - fn nth(&self, index: usize) -> Self::Scalar { - match index { - 0 => self.0.x, - 1 => self.0.y, - _ => unreachable!(), - } - } - fn nth_mut(&mut self, index: usize) -> &mut Self::Scalar { - match index { - 0 => &mut self.0.x, - 1 => &mut self.0.y, - _ => unreachable!(), - } - } -} +#[cfg(feature = "rstar_0_9")] +impl_rstar_point!(rstar_0_9); #[cfg(feature = "rstar_0_10")] -impl ::rstar_0_10::Point for Point -where - T: ::num_traits::Float + ::rstar_0_10::RTreeNum, -{ - type Scalar = T; - - const DIMENSIONS: usize = 2; - - fn generate(mut generator: impl FnMut(usize) -> Self::Scalar) -> Self { - Point::new(generator(0), generator(1)) - } - - fn nth(&self, index: usize) -> Self::Scalar { - match index { - 0 => self.0.x, - 1 => self.0.y, - _ => unreachable!(), - } - } - fn nth_mut(&mut self, index: usize) -> &mut Self::Scalar { - match index { - 0 => &mut self.0.x, - 1 => &mut self.0.y, - _ => unreachable!(), - } - } -} +impl_rstar_point!(rstar_0_10); #[cfg(feature = "rstar_0_11")] impl ::rstar_0_11::Point for Point diff --git a/geo-types/src/geometry/rect.rs b/geo-types/src/geometry/rect.rs index 9d8a7b77d..e1ba43f68 100644 --- a/geo-types/src/geometry/rect.rs +++ b/geo-types/src/geometry/rect.rs @@ -1,5 +1,12 @@ use crate::{coord, polygon, Coord, CoordFloat, CoordNum, Line, Polygon}; +#[cfg(any( + feature = "rstar_0_8", + feature = "rstar_0_9", + feature = "rstar_0_10", + feature = "rstar_0_11" +))] +use crate::Point; #[cfg(any(feature = "approx", test))] use approx::{AbsDiffEq, RelativeEq}; @@ -378,6 +385,39 @@ impl Rect { static RECT_INVALID_BOUNDS_ERROR: &str = "Failed to create Rect: 'min' coordinate's x/y value must be smaller or equal to the 'max' x/y value"; +#[cfg(any( + feature = "rstar_0_8", + feature = "rstar_0_9", + feature = "rstar_0_10", + feature = "rstar_0_11" +))] +macro_rules! impl_rstar_rect { + ($rstar:ident) => { + impl $rstar::RTreeObject for Rect + where + T: ::num_traits::Float + ::$rstar::RTreeNum, + { + type Envelope = ::$rstar::AABB>; + + fn envelope(&self) -> Self::Envelope { + ::$rstar::AABB::from_corners( + Point::new(self.min().x, self.min().y), + Point::new(self.max().x, self.max().y), + ) + } + } + }; +} + +#[cfg(feature = "rstar_0_8")] +impl_rstar_rect!(rstar_0_8); + +#[cfg(feature = "rstar_0_9")] +impl_rstar_rect!(rstar_0_9); + +#[cfg(feature = "rstar_0_10")] +impl_rstar_rect!(rstar_0_10); + #[cfg(any(feature = "approx", test))] impl RelativeEq for Rect where diff --git a/geo-types/src/geometry/triangle.rs b/geo-types/src/geometry/triangle.rs index 831e57cb1..1225cd7e7 100644 --- a/geo-types/src/geometry/triangle.rs +++ b/geo-types/src/geometry/triangle.rs @@ -1,4 +1,26 @@ use crate::{polygon, Coord, CoordNum, Line, Polygon}; +#[cfg(any( + feature = "rstar_0_8", + feature = "rstar_0_9", + feature = "rstar_0_10", + feature = "rstar_0_11" +))] +use num_traits::Bounded; + +#[cfg(any( + feature = "rstar_0_8", + feature = "rstar_0_9", + feature = "rstar_0_10", + feature = "rstar_0_11" +))] +use crate::private_utils::get_bounding_rect; +#[cfg(any( + feature = "rstar_0_8", + feature = "rstar_0_9", + feature = "rstar_0_10", + feature = "rstar_0_11" +))] +use crate::Point; #[cfg(any(feature = "approx", test))] use approx::{AbsDiffEq, RelativeEq}; @@ -63,6 +85,46 @@ impl> + Copy, T: CoordNum> From<[IC; 3]> for Triangle { } } +#[cfg(any( + feature = "rstar_0_8", + feature = "rstar_0_9", + feature = "rstar_0_10", + feature = "rstar_0_11" +))] +macro_rules! impl_rstar_triangle { + ($rstar:ident) => { + impl $rstar::RTreeObject for Triangle + where + T: ::num_traits::Float + ::$rstar::RTreeNum, + { + type Envelope = ::$rstar::AABB>; + + fn envelope(&self) -> Self::Envelope { + let bounding_rect = get_bounding_rect(self.to_array()); + 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), + ), + } + } + } + }; +} + +#[cfg(feature = "rstar_0_8")] +impl_rstar_triangle!(rstar_0_8); + +#[cfg(feature = "rstar_0_9")] +impl_rstar_triangle!(rstar_0_9); + +#[cfg(feature = "rstar_0_10")] +impl_rstar_triangle!(rstar_0_10); + #[cfg(any(feature = "approx", test))] impl RelativeEq for Triangle where diff --git a/geo-types/src/lib.rs b/geo-types/src/lib.rs index 3bd2b2f27..7e0187ec3 100644 --- a/geo-types/src/lib.rs +++ b/geo-types/src/lib.rs @@ -134,6 +134,9 @@ pub use error::Error; #[macro_use] mod macros; +#[cfg(feature = "bounding_rect")] +pub mod bounding_rect; + #[cfg(feature = "arbitrary")] mod arbitrary; diff --git a/geo-types/src/private_utils.rs b/geo-types/src/private_utils.rs index 1311a7b4f..347938053 100644 --- a/geo-types/src/private_utils.rs +++ b/geo-types/src/private_utils.rs @@ -171,3 +171,38 @@ where } false } + +// The Rust standard library has `max` for `Ord`, but not for `PartialOrd` +pub fn partial_max(a: T, b: T) -> T { + if a > b { + a + } else { + b + } +} + +// The Rust standard library has `min` for `Ord`, but not for `PartialOrd` +pub fn partial_min(a: T, b: T) -> T { + if a < b { + a + } else { + b + } +} + +#[cfg(test)] +mod test { + use super::{partial_max, partial_min}; + + #[test] + fn test_partial_max() { + assert_eq!(5, partial_max(5, 4)); + assert_eq!(5, partial_max(5, 5)); + } + + #[test] + fn test_partial_min() { + assert_eq!(4, partial_min(5, 4)); + assert_eq!(4, partial_min(4, 4)); + } +} diff --git a/geo/Cargo.toml b/geo/Cargo.toml index 59472b99b..5fb293c24 100644 --- a/geo/Cargo.toml +++ b/geo/Cargo.toml @@ -21,7 +21,7 @@ use-serde = ["serde", "geo-types/serde"] [dependencies] earcutr = { version = "0.4.2", optional = true } float_next_after = "1.0.0" -geo-types = { version = "0.7.9", features = ["approx", "use-rstar_0_11"] } +geo-types = { version = "0.7.9", features = ["approx", "use-rstar_0_11", "bounding_rect"] } geographiclib-rs = { version = "0.2.3", default-features = false } log = "0.4.11" num-traits = "0.2" diff --git a/geo/src/algorithm/bounding_rect.rs b/geo/src/algorithm/bounding_rect.rs index 7ded02805..192d282ee 100644 --- a/geo/src/algorithm/bounding_rect.rs +++ b/geo/src/algorithm/bounding_rect.rs @@ -1,342 +1,12 @@ -use crate::utils::{partial_max, partial_min}; -use crate::{coord, geometry::*, CoordNum, GeometryCow}; -use geo_types::private_utils::{get_bounding_rect, line_string_bounding_rect}; - -/// Calculation of the bounding rectangle of a geometry. -pub trait BoundingRect { - type Output: Into>>; - - /// Return the bounding rectangle of a geometry - /// - /// # Examples - /// - /// ``` - /// use geo::BoundingRect; - /// use geo::line_string; - /// - /// let line_string = line_string![ - /// (x: 40.02f64, y: 116.34), - /// (x: 42.02f64, y: 116.34), - /// (x: 42.02f64, y: 118.34), - /// ]; - /// - /// let bounding_rect = line_string.bounding_rect().unwrap(); - /// - /// assert_eq!(40.02f64, bounding_rect.min().x); - /// assert_eq!(42.02f64, bounding_rect.max().x); - /// assert_eq!(116.34, bounding_rect.min().y); - /// assert_eq!(118.34, bounding_rect.max().y); - /// ``` - fn bounding_rect(&self) -> Self::Output; -} - -impl BoundingRect for Coord -where - T: CoordNum, -{ - type Output = Rect; - - /// Return the bounding rectangle for a `Coord`. It will have zero width - /// and zero height. - fn bounding_rect(&self) -> Self::Output { - Rect::new(*self, *self) - } -} - -impl BoundingRect for Point -where - T: CoordNum, -{ - type Output = Rect; - - /// Return the bounding rectangle for a `Point`. It will have zero width - /// and zero height. - fn bounding_rect(&self) -> Self::Output { - Rect::new(self.0, self.0) - } -} - -impl BoundingRect for MultiPoint -where - T: CoordNum, -{ - type Output = Option>; - - /// - /// Return the BoundingRect for a MultiPoint - fn bounding_rect(&self) -> Self::Output { - get_bounding_rect(self.0.iter().map(|p| p.0)) - } -} - -impl BoundingRect for Line -where - T: CoordNum, -{ - type Output = Rect; - - fn bounding_rect(&self) -> Self::Output { - Rect::new(self.start, self.end) - } -} - -impl BoundingRect for LineString -where - T: CoordNum, -{ - type Output = Option>; - - /// - /// Return the BoundingRect for a LineString - fn bounding_rect(&self) -> Self::Output { - line_string_bounding_rect(self) - } -} - -impl BoundingRect for MultiLineString -where - T: CoordNum, -{ - type Output = Option>; - - /// - /// Return the BoundingRect for a MultiLineString - fn bounding_rect(&self) -> Self::Output { - get_bounding_rect(self.iter().flat_map(|line| line.0.iter().cloned())) - } -} - -impl BoundingRect for Polygon -where - T: CoordNum, -{ - type Output = Option>; - - /// - /// Return the BoundingRect for a Polygon - fn bounding_rect(&self) -> Self::Output { - let line = self.exterior(); - get_bounding_rect(line.0.iter().cloned()) - } -} - -impl BoundingRect for MultiPolygon -where - T: CoordNum, -{ - type Output = Option>; - - /// - /// Return the BoundingRect for a MultiPolygon - fn bounding_rect(&self) -> Self::Output { - get_bounding_rect( - self.iter() - .flat_map(|poly| poly.exterior().0.iter().cloned()), - ) - } -} - -impl BoundingRect for Triangle -where - T: CoordNum, -{ - type Output = Rect; - - fn bounding_rect(&self) -> Self::Output { - get_bounding_rect(self.to_array().iter().cloned()).unwrap() - } -} - -impl BoundingRect for Rect -where - T: CoordNum, -{ - type Output = Rect; - - fn bounding_rect(&self) -> Self::Output { - *self - } -} - -impl BoundingRect for Geometry -where - T: CoordNum, -{ - type Output = Option>; - - crate::geometry_delegate_impl! { - fn bounding_rect(&self) -> Self::Output; - } -} +use crate::{CoordNum, GeometryCow, Rect}; +pub use geo_types::bounding_rect::*; impl BoundingRect for GeometryCow<'_, T> where T: CoordNum, { type Output = Option>; - - crate::geometry_cow_delegate_impl! { + geo_types::geometry_cow_delegate_impl! { fn bounding_rect(&self) -> Self::Output; } } - -impl BoundingRect for GeometryCollection -where - T: CoordNum, -{ - type Output = Option>; - - fn bounding_rect(&self) -> Self::Output { - self.iter().fold(None, |acc, next| { - let next_bounding_rect = next.bounding_rect(); - - match (acc, next_bounding_rect) { - (None, None) => None, - (Some(r), None) | (None, Some(r)) => Some(r), - (Some(r1), Some(r2)) => Some(bounding_rect_merge(r1, r2)), - } - }) - } -} - -// Return a new rectangle that encompasses the provided rectangles -fn bounding_rect_merge(a: Rect, b: Rect) -> Rect { - Rect::new( - coord! { - x: partial_min(a.min().x, b.min().x), - y: partial_min(a.min().y, b.min().y), - }, - coord! { - x: partial_max(a.max().x, b.max().x), - y: partial_max(a.max().y, b.max().y), - }, - ) -} - -#[cfg(test)] -mod test { - use super::bounding_rect_merge; - use crate::line_string; - use crate::BoundingRect; - use crate::{ - coord, point, polygon, Geometry, GeometryCollection, Line, LineString, MultiLineString, - MultiPoint, MultiPolygon, Polygon, Rect, - }; - - #[test] - fn empty_linestring_test() { - let linestring: LineString = line_string![]; - let bounding_rect = linestring.bounding_rect(); - assert!(bounding_rect.is_none()); - } - #[test] - fn linestring_one_point_test() { - let linestring = line_string![(x: 40.02f64, y: 116.34)]; - let bounding_rect = Rect::new( - coord! { - x: 40.02f64, - y: 116.34, - }, - coord! { - x: 40.02, - y: 116.34, - }, - ); - assert_eq!(bounding_rect, linestring.bounding_rect().unwrap()); - } - #[test] - fn linestring_test() { - let linestring = line_string![ - (x: 1., y: 1.), - (x: 2., y: -2.), - (x: -3., y: -3.), - (x: -4., y: 4.) - ]; - let bounding_rect = Rect::new(coord! { x: -4., y: -3. }, coord! { x: 2., y: 4. }); - assert_eq!(bounding_rect, linestring.bounding_rect().unwrap()); - } - #[test] - fn multilinestring_test() { - let multiline = MultiLineString::new(vec![ - line_string![(x: 1., y: 1.), (x: -40., y: 1.)], - line_string![(x: 1., y: 1.), (x: 50., y: 1.)], - line_string![(x: 1., y: 1.), (x: 1., y: -60.)], - line_string![(x: 1., y: 1.), (x: 1., y: 70.)], - ]); - let bounding_rect = Rect::new(coord! { x: -40., y: -60. }, coord! { x: 50., y: 70. }); - assert_eq!(bounding_rect, multiline.bounding_rect().unwrap()); - } - #[test] - fn multipoint_test() { - let multipoint = MultiPoint::from(vec![(1., 1.), (2., -2.), (-3., -3.), (-4., 4.)]); - let bounding_rect = Rect::new(coord! { x: -4., y: -3. }, coord! { x: 2., y: 4. }); - assert_eq!(bounding_rect, multipoint.bounding_rect().unwrap()); - } - #[test] - fn polygon_test() { - let linestring = line_string![ - (x: 0., y: 0.), - (x: 5., y: 0.), - (x: 5., y: 6.), - (x: 0., y: 6.), - (x: 0., y: 0.), - ]; - let line_bounding_rect = linestring.bounding_rect().unwrap(); - let poly = Polygon::new(linestring, Vec::new()); - assert_eq!(line_bounding_rect, poly.bounding_rect().unwrap()); - } - #[test] - fn multipolygon_test() { - let mpoly = MultiPolygon::new(vec![ - polygon![(x: 0., y: 0.), (x: 50., y: 0.), (x: 0., y: -70.), (x: 0., y: 0.)], - polygon![(x: 0., y: 0.), (x: 5., y: 0.), (x: 0., y: 80.), (x: 0., y: 0.)], - polygon![(x: 0., y: 0.), (x: -60., y: 0.), (x: 0., y: 6.), (x: 0., y: 0.)], - ]); - let bounding_rect = Rect::new(coord! { x: -60., y: -70. }, coord! { x: 50., y: 80. }); - assert_eq!(bounding_rect, mpoly.bounding_rect().unwrap()); - } - #[test] - fn line_test() { - let line1 = Line::new(coord! { x: 0., y: 1. }, coord! { x: 2., y: 3. }); - let line2 = Line::new(coord! { x: 2., y: 3. }, coord! { x: 0., y: 1. }); - assert_eq!( - line1.bounding_rect(), - Rect::new(coord! { x: 0., y: 1. }, coord! { x: 2., y: 3. },) - ); - assert_eq!( - line2.bounding_rect(), - Rect::new(coord! { x: 0., y: 1. }, coord! { x: 2., y: 3. },) - ); - } - - #[test] - fn bounding_rect_merge_test() { - assert_eq!( - bounding_rect_merge( - Rect::new(coord! { x: 0., y: 0. }, coord! { x: 1., y: 1. }), - Rect::new(coord! { x: 1., y: 1. }, coord! { x: 2., y: 2. }), - ), - Rect::new(coord! { x: 0., y: 0. }, coord! { x: 2., y: 2. }), - ); - } - - #[test] - fn point_bounding_rect_test() { - assert_eq!( - Rect::new(coord! { x: 1., y: 2. }, coord! { x: 1., y: 2. }), - point! { x: 1., y: 2. }.bounding_rect(), - ); - } - - #[test] - fn geometry_collection_bounding_rect_test() { - assert_eq!( - Some(Rect::new(coord! { x: 0., y: 0. }, coord! { x: 1., y: 2. })), - GeometryCollection::new_from(vec![ - Geometry::Point(point! { x: 0., y: 0. }), - Geometry::Point(point! { x: 1., y: 2. }), - ]) - .bounding_rect(), - ); - } -} diff --git a/geo/src/algorithm/concave_hull.rs b/geo/src/algorithm/concave_hull.rs index c955ac1d0..b966c3181 100644 --- a/geo/src/algorithm/concave_hull.rs +++ b/geo/src/algorithm/concave_hull.rs @@ -1,9 +1,9 @@ use crate::convex_hull::qhull; -use crate::utils::partial_min; use crate::{ coord, Centroid, Coord, CoordNum, EuclideanDistance, EuclideanLength, GeoFloat, Line, LineString, MultiLineString, MultiPoint, MultiPolygon, Point, Polygon, }; +use geo_types::private_utils::partial_min; use rstar::{RTree, RTreeNum}; use std::collections::VecDeque; diff --git a/geo/src/lib.rs b/geo/src/lib.rs index 4f61f9b9e..ec9900156 100644 --- a/geo/src/lib.rs +++ b/geo/src/lib.rs @@ -209,7 +209,10 @@ extern crate rstar; pub use crate::algorithm::*; pub use crate::types::Closest; -pub use geo_types::{coord, line_string, point, polygon, CoordFloat, CoordNum}; +pub use geo_types::{ + coord, geometry_cow_delegate_impl, geometry_delegate_impl, line_string, point, polygon, + CoordFloat, CoordNum, +}; pub mod geometry; pub use geometry::*; diff --git a/geo/src/types.rs b/geo/src/types.rs index 03f7cb4b2..fe82c07f5 100644 --- a/geo/src/types.rs +++ b/geo/src/types.rs @@ -36,123 +36,3 @@ impl Closest { } } } - -/// Implements the common pattern where a Geometry enum simply delegates its trait impl to it's inner type. -/// -/// ``` -/// # use geo::{GeoNum, Coord, Point, Line, LineString, Polygon, MultiPoint, MultiLineString, MultiPolygon, GeometryCollection, Rect, Triangle, Geometry}; -/// -/// trait Foo { -/// fn foo_1(&self, coord: Coord) -> bool; -/// fn foo_2(&self) -> i32; -/// } -/// -/// // Assuming we have an impl for all the inner types like this: -/// impl Foo for Point { -/// fn foo_1(&self, coord: Coord) -> bool { true } -/// fn foo_2(&self) -> i32 { 1 } -/// } -/// impl Foo for Line { -/// fn foo_1(&self, coord: Coord) -> bool { false } -/// fn foo_2(&self) -> i32 { 2 } -/// } -/// impl Foo for LineString { -/// fn foo_1(&self, coord: Coord) -> bool { true } -/// fn foo_2(&self) -> i32 { 3 } -/// } -/// impl Foo for Polygon { -/// fn foo_1(&self, coord: Coord) -> bool { false } -/// fn foo_2(&self) -> i32 { 4 } -/// } -/// impl Foo for MultiPoint { -/// fn foo_1(&self, coord: Coord) -> bool { true } -/// fn foo_2(&self) -> i32 { 5 } -/// } -/// impl Foo for MultiLineString { -/// fn foo_1(&self, coord: Coord) -> bool { false } -/// fn foo_2(&self) -> i32 { 6 } -/// } -/// impl Foo for MultiPolygon { -/// fn foo_1(&self, coord: Coord) -> bool { true } -/// fn foo_2(&self) -> i32 { 7 } -/// } -/// impl Foo for GeometryCollection { -/// fn foo_1(&self, coord: Coord) -> bool { false } -/// fn foo_2(&self) -> i32 { 8 } -/// } -/// impl Foo for Rect { -/// fn foo_1(&self, coord: Coord) -> bool { true } -/// fn foo_2(&self) -> i32 { 9 } -/// } -/// impl Foo for Triangle { -/// fn foo_1(&self, coord: Coord) -> bool { true } -/// fn foo_2(&self) -> i32 { 10 } -/// } -/// -/// // If we want the impl for Geometry to simply delegate to it's -/// // inner case... -/// impl Foo for Geometry { -/// // Instead of writing out this trivial enum delegation... -/// // fn foo_1(&self, coord: Coord) -> bool { -/// // match self { -/// // Geometry::Point(g) => g.foo_1(coord), -/// // Geometry::LineString(g) => g.foo_1(coord), -/// // _ => unimplemented!("...etc for other cases") -/// // } -/// // } -/// // -/// // fn foo_2(&self) -> i32 { -/// // match self { -/// // Geometry::Point(g) => g.foo_2(), -/// // Geometry::LineString(g) => g.foo_2(), -/// // _ => unimplemented!("...etc for other cases") -/// // } -/// // } -/// -/// // we can equivalently write: -/// geo::geometry_delegate_impl! { -/// fn foo_1(&self, coord: Coord) -> bool; -/// fn foo_2(&self) -> i32; -/// } -/// } -/// ``` -#[macro_export] -macro_rules! geometry_delegate_impl { - ($($a:tt)*) => { $crate::__geometry_delegate_impl_helper!{ Geometry, $($a)* } } -} - -#[doc(hidden)] -#[macro_export] -macro_rules! geometry_cow_delegate_impl { - ($($a:tt)*) => { $crate::__geometry_delegate_impl_helper!{ GeometryCow, $($a)* } } -} - -#[doc(hidden)] -#[macro_export] -macro_rules! __geometry_delegate_impl_helper { - ( - $enum:ident, - $( - $(#[$outer:meta])* - fn $func_name: ident(&$($self_life:lifetime)?self $(, $arg_name: ident: $arg_type: ty)*) -> $return: ty; - )+ - ) => { - $( - $(#[$outer])* - fn $func_name(&$($self_life)? self, $($arg_name: $arg_type),*) -> $return { - match self { - $enum::Point(g) => g.$func_name($($arg_name),*).into(), - $enum::Line(g) => g.$func_name($($arg_name),*).into(), - $enum::LineString(g) => g.$func_name($($arg_name),*).into(), - $enum::Polygon(g) => g.$func_name($($arg_name),*).into(), - $enum::MultiPoint(g) => g.$func_name($($arg_name),*).into(), - $enum::MultiLineString(g) => g.$func_name($($arg_name),*).into(), - $enum::MultiPolygon(g) => g.$func_name($($arg_name),*).into(), - $enum::GeometryCollection(g) => g.$func_name($($arg_name),*).into(), - $enum::Rect(g) => g.$func_name($($arg_name),*).into(), - $enum::Triangle(g) => g.$func_name($($arg_name),*).into(), - } - } - )+ - }; -} diff --git a/geo/src/utils.rs b/geo/src/utils.rs index 18e5f7742..befcb7d11 100644 --- a/geo/src/utils.rs +++ b/geo/src/utils.rs @@ -74,24 +74,6 @@ where } } -// The Rust standard library has `max` for `Ord`, but not for `PartialOrd` -pub fn partial_max(a: T, b: T) -> T { - if a > b { - a - } else { - b - } -} - -// The Rust standard library has `min` for `Ord`, but not for `PartialOrd` -pub fn partial_min(a: T, b: T) -> T { - if a < b { - a - } else { - b - } -} - // Moved to their own module, but we re-export to avoid breaking the API. pub use crate::coordinate_position::{coord_pos_relative_to_ring, CoordPos}; @@ -154,20 +136,3 @@ pub fn least_and_greatest_index(pts: &[Coord]) -> (usize, usize) }); (min.unwrap().0, max.unwrap().0) } - -#[cfg(test)] -mod test { - use super::{partial_max, partial_min}; - - #[test] - fn test_partial_max() { - assert_eq!(5, partial_max(5, 4)); - assert_eq!(5, partial_max(5, 5)); - } - - #[test] - fn test_partial_min() { - assert_eq!(4, partial_min(5, 4)); - assert_eq!(4, partial_min(4, 4)); - } -}