diff --git a/geo-types/src/geometry/geometry_collection.rs b/geo-types/src/geometry/geometry_collection.rs index ce5bb2338..75c7e672c 100644 --- a/geo-types/src/geometry/geometry_collection.rs +++ b/geo-types/src/geometry/geometry_collection.rs @@ -1,4 +1,8 @@ +#[cfg(any(feature = "rstar_0_8", feature = "rstar_0_9"))] +use crate::{coord, Point, Rect}; use crate::{CoordNum, Geometry}; +#[cfg(any(feature = "rstar_0_8", feature = "rstar_0_9"))] +use num_traits::Bounded; use alloc::vec; use alloc::vec::Vec; @@ -247,6 +251,58 @@ impl<'a, T: CoordNum> GeometryCollection<T> { } } +// Return a new rectangle that encompasses the provided rectangles +fn bounding_rect_merge<T: CoordNum>(a: Rect<T>, b: Rect<T>) -> Rect<T> { + Rect::new( + coord! { + x: crate::private_utils::partial_min(a.min().x, b.min().x), + y: crate::private_utils::partial_min(a.min().y, b.min().y), + }, + coord! { + x: crate::private_utils::partial_max(a.max().x, b.max().x), + y: crate::private_utils::partial_max(a.max().y, b.max().y), + }, + ) +} + +#[cfg(any(feature = "rstar_0_8", feature = "rstar_0_9"))] +macro_rules! impl_rstar_geometry_collection { + ($rstar:ident) => { + impl<T> $rstar::RTreeObject for GeometryCollection<T> + where + T: ::num_traits::Float + ::$rstar::RTreeNum, + { + type Envelope = ::$rstar::AABB<Point<T>>; + + fn envelope(&self) -> Self::Envelope { + let bounding_rect = self.iter().fold(None, |acc, next| { + let next_bounding_rect = next.envelope(); + let lower = next_bounding_rect.lower(); + let upper = next_bounding_rect.upper(); + let rect = Rect::new(lower, upper); + Some(bounding_rect_merge(acc.unwrap(), rect)) + }); + 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_geometry_collection!(rstar_0_8); + +#[cfg(feature = "rstar_0_9")] +impl_rstar_geometry_collection!(rstar_0_9); + #[cfg(any(feature = "approx", test))] impl<T> RelativeEq for GeometryCollection<T> where diff --git a/geo-types/src/geometry/mod.rs b/geo-types/src/geometry/mod.rs index 4faf01753..74262105a 100644 --- a/geo-types/src/geometry/mod.rs +++ b/geo-types/src/geometry/mod.rs @@ -269,6 +269,39 @@ where } } +#[cfg(any(feature = "rstar_0_8", feature = "rstar_0_9"))] +macro_rules! impl_rstar_geometry { + ($rstar:ident) => { + impl<T> $rstar::RTreeObject for Geometry<T> + where + T: ::num_traits::Float + ::$rstar::RTreeNum, + { + type Envelope = ::$rstar::AABB<Point<T>>; + + 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(any(feature = "approx", test))] impl<T> RelativeEq for Geometry<T> where diff --git a/geo-types/src/geometry/multi_line_string.rs b/geo-types/src/geometry/multi_line_string.rs index 5cc84b515..ae28a9e8d 100644 --- a/geo-types/src/geometry/multi_line_string.rs +++ b/geo-types/src/geometry/multi_line_string.rs @@ -1,3 +1,8 @@ +#[cfg(any(feature = "rstar_0_8", feature = "rstar_0_9"))] +use crate::Point; +#[cfg(any(feature = "rstar_0_8", feature = "rstar_0_9"))] +use num_traits::Bounded; + use crate::{CoordNum, LineString}; use alloc::vec; @@ -118,6 +123,40 @@ impl<T: CoordNum> MultiLineString<T> { } } +#[cfg(any(feature = "rstar_0_8", feature = "rstar_0_9"))] +macro_rules! impl_rstar_multi_linestring { + ($rstar:ident) => { + impl<T> $rstar::RTreeObject for MultiLineString<T> + where + T: ::num_traits::Float + ::$rstar::RTreeNum, + { + type Envelope = ::$rstar::AABB<Point<T>>; + + 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(any(feature = "approx", test))] impl<T> RelativeEq for MultiLineString<T> where diff --git a/geo-types/src/geometry/multi_point.rs b/geo-types/src/geometry/multi_point.rs index c59622013..2c22b9cbe 100644 --- a/geo-types/src/geometry/multi_point.rs +++ b/geo-types/src/geometry/multi_point.rs @@ -1,7 +1,8 @@ use crate::{CoordNum, Point}; - #[cfg(any(feature = "approx", test))] use approx::{AbsDiffEq, RelativeEq}; +#[cfg(any(feature = "rstar_0_8", feature = "rstar_0_9"))] +use num_traits::Bounded; use alloc::vec; use alloc::vec::Vec; @@ -99,6 +100,39 @@ impl<T: CoordNum> MultiPoint<T> { } } +#[cfg(any(feature = "rstar_0_8", feature = "rstar_0_9"))] +macro_rules! impl_rstar_multi_point { + ($rstar:ident) => { + impl<T> $rstar::RTreeObject for MultiPoint<T> + where + T: ::num_traits::Float + ::$rstar::RTreeNum, + { + type Envelope = ::$rstar::AABB<Point<T>>; + + 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(any(feature = "approx", test))] impl<T> RelativeEq for MultiPoint<T> where diff --git a/geo-types/src/geometry/multi_polygon.rs b/geo-types/src/geometry/multi_polygon.rs index 15116353e..dce637804 100644 --- a/geo-types/src/geometry/multi_polygon.rs +++ b/geo-types/src/geometry/multi_polygon.rs @@ -1,4 +1,8 @@ +#[cfg(any(feature = "rstar_0_8", feature = "rstar_0_9"))] +use crate::Point; use crate::{CoordNum, Polygon}; +#[cfg(any(feature = "rstar_0_8", feature = "rstar_0_9"))] +use num_traits::Bounded; use alloc::vec; use alloc::vec::Vec; @@ -91,6 +95,41 @@ impl<T: CoordNum> MultiPolygon<T> { } } +#[cfg(any(feature = "rstar_0_8", feature = "rstar_0_9"))] +macro_rules! impl_rstar_multi_polygon { + ($rstar:ident) => { + impl<T> $rstar::RTreeObject for MultiPolygon<T> + where + T: ::num_traits::Float + ::$rstar::RTreeNum, + { + type Envelope = ::$rstar::AABB<Point<T>>; + + 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(any(feature = "approx", test))] impl<T> RelativeEq for MultiPolygon<T> where diff --git a/geo-types/src/geometry/rect.rs b/geo-types/src/geometry/rect.rs index 9d8a7b77d..76e77a55c 100644 --- a/geo-types/src/geometry/rect.rs +++ b/geo-types/src/geometry/rect.rs @@ -1,5 +1,7 @@ use crate::{coord, polygon, Coord, CoordFloat, CoordNum, Line, Polygon}; +#[cfg(any(feature = "rstar_0_8", feature = "rstar_0_9"))] +use crate::Point; #[cfg(any(feature = "approx", test))] use approx::{AbsDiffEq, RelativeEq}; @@ -378,6 +380,31 @@ impl<T: CoordFloat> Rect<T> { 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"))] +macro_rules! impl_rstar_rect { + ($rstar:ident) => { + impl<T> $rstar::RTreeObject for Rect<T> + where + T: ::num_traits::Float + ::$rstar::RTreeNum, + { + type Envelope = ::$rstar::AABB<Point<T>>; + + 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(any(feature = "approx", test))] impl<T> RelativeEq for Rect<T> where diff --git a/geo-types/src/geometry/triangle.rs b/geo-types/src/geometry/triangle.rs index 831e57cb1..eb5f86076 100644 --- a/geo-types/src/geometry/triangle.rs +++ b/geo-types/src/geometry/triangle.rs @@ -1,4 +1,11 @@ use crate::{polygon, Coord, CoordNum, Line, Polygon}; +#[cfg(any(feature = "rstar_0_8", feature = "rstar_0_9"))] +use num_traits::Bounded; + +#[cfg(any(feature = "rstar_0_8", feature = "rstar_0_9"))] +use crate::private_utils::get_bounding_rect; +#[cfg(any(feature = "rstar_0_8", feature = "rstar_0_9"))] +use crate::Point; #[cfg(any(feature = "approx", test))] use approx::{AbsDiffEq, RelativeEq}; @@ -63,6 +70,38 @@ impl<IC: Into<Coord<T>> + Copy, T: CoordNum> From<[IC; 3]> for Triangle<T> { } } +#[cfg(any(feature = "rstar_0_8", feature = "rstar_0_9"))] +macro_rules! impl_rstar_triangle { + ($rstar:ident) => { + impl<T> $rstar::RTreeObject for Triangle<T> + where + T: ::num_traits::Float + ::$rstar::RTreeNum, + { + type Envelope = ::$rstar::AABB<Point<T>>; + + 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(any(feature = "approx", test))] impl<T> RelativeEq for Triangle<T> where diff --git a/geo-types/src/private_utils.rs b/geo-types/src/private_utils.rs index 1311a7b4f..e251e3e1d 100644 --- a/geo-types/src/private_utils.rs +++ b/geo-types/src/private_utils.rs @@ -5,6 +5,24 @@ use crate::{Coord, CoordFloat, CoordNum, Line, LineString, Point, Rect}; +// The Rust standard library has `max` for `Ord`, but not for `PartialOrd` +pub fn partial_max<T: PartialOrd>(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<T: PartialOrd>(a: T, b: T) -> T { + if a < b { + a + } else { + b + } +} + pub fn line_string_bounding_rect<T>(line_string: &LineString<T>) -> Option<Rect<T>> where T: CoordNum,