From d6ebe17ff18f7174dd04a0226b5ad4fbfcb27b87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stephan=20H=C3=BCgel?= Date: Tue, 14 Feb 2023 16:40:27 +0000 Subject: [PATCH 01/10] Implement RTreeObject for Geometry enum Fixes https://github.com/georust/geo/issues/982 --- geo-types/src/geometry/geometry_collection.rs | 56 +++++++++++++++++++ geo-types/src/geometry/mod.rs | 33 +++++++++++ geo-types/src/geometry/multi_line_string.rs | 39 +++++++++++++ geo-types/src/geometry/multi_point.rs | 36 +++++++++++- geo-types/src/geometry/multi_polygon.rs | 39 +++++++++++++ geo-types/src/geometry/rect.rs | 27 +++++++++ geo-types/src/geometry/triangle.rs | 39 +++++++++++++ geo-types/src/private_utils.rs | 18 ++++++ 8 files changed, 286 insertions(+), 1 deletion(-) diff --git a/geo-types/src/geometry/geometry_collection.rs b/geo-types/src/geometry/geometry_collection.rs index a870f1ff7..b7fc834be 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 { } } +// Return a new rectangle that encompasses the provided rectangles +fn bounding_rect_merge(a: Rect, b: Rect) -> Rect { + 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 $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(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 RelativeEq for GeometryCollection 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 $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(any(feature = "approx", test))] impl RelativeEq for Geometry where diff --git a/geo-types/src/geometry/multi_line_string.rs b/geo-types/src/geometry/multi_line_string.rs index 0e05673af..e7b528107 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 MultiLineString { } } +#[cfg(any(feature = "rstar_0_8", feature = "rstar_0_9"))] +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(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..11af5b1ca 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 MultiPoint { } } +#[cfg(any(feature = "rstar_0_8", feature = "rstar_0_9"))] +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(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..342b15eef 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 MultiPolygon { } } +#[cfg(any(feature = "rstar_0_8", feature = "rstar_0_9"))] +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(any(feature = "approx", test))] impl RelativeEq for MultiPolygon 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 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"))] +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(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..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> + Copy, T: CoordNum> From<[IC; 3]> for Triangle { } } +#[cfg(any(feature = "rstar_0_8", feature = "rstar_0_9"))] +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(any(feature = "approx", test))] impl RelativeEq for Triangle 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(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 + } +} + pub fn line_string_bounding_rect(line_string: &LineString) -> Option> where T: CoordNum, From 37ae87db27572fd73a474fa8e5bd047b35d1761f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stephan=20H=C3=BCgel?= Date: Tue, 14 Feb 2023 16:50:47 +0000 Subject: [PATCH 02/10] Move helpers to private utils --- geo-types/src/geometry/geometry_collection.rs | 23 +++++-------------- geo-types/src/private_utils.rs | 19 +++++++++++++-- 2 files changed, 23 insertions(+), 19 deletions(-) diff --git a/geo-types/src/geometry/geometry_collection.rs b/geo-types/src/geometry/geometry_collection.rs index b7fc834be..f7ebfb8da 100644 --- a/geo-types/src/geometry/geometry_collection.rs +++ b/geo-types/src/geometry/geometry_collection.rs @@ -1,7 +1,7 @@ -#[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 crate::{Point, Rect}; +#[cfg(any(feature = "rstar_0_8", feature = "rstar_0_9"))] use num_traits::Bounded; use alloc::vec; @@ -251,20 +251,6 @@ impl<'a, T: CoordNum> GeometryCollection { } } -// Return a new rectangle that encompasses the provided rectangles -fn bounding_rect_merge(a: Rect, b: Rect) -> Rect { - 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) => { @@ -280,7 +266,10 @@ macro_rules! impl_rstar_geometry_collection { 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)) + Some(crate::private_utils::bounding_rect_merge( + acc.unwrap(), + rect, + )) }); match bounding_rect { None => ::$rstar::AABB::from_corners( diff --git a/geo-types/src/private_utils.rs b/geo-types/src/private_utils.rs index e251e3e1d..a5cf3ff1b 100644 --- a/geo-types/src/private_utils.rs +++ b/geo-types/src/private_utils.rs @@ -6,7 +6,7 @@ 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(a: T, b: T) -> T { +fn partial_max(a: T, b: T) -> T { if a > b { a } else { @@ -15,7 +15,7 @@ pub fn partial_max(a: T, b: T) -> T { } // The Rust standard library has `min` for `Ord`, but not for `PartialOrd` -pub fn partial_min(a: T, b: T) -> T { +fn partial_min(a: T, b: T) -> T { if a < b { a } else { @@ -23,6 +23,21 @@ pub fn partial_min(a: T, b: T) -> T { } } +/// Return a new rectangle that encompasses the provided rectangles +// copy-pasted from geo's bounding_rect implementation +pub 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), + }, + ) +} + pub fn line_string_bounding_rect(line_string: &LineString) -> Option> where T: CoordNum, From 080bdd1129a1f1a46dbf6ebb1c7d3214d83f2715 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stephan=20H=C3=BCgel?= Date: Wed, 15 Feb 2023 15:18:03 +0000 Subject: [PATCH 03/10] Simplify rstar impl for GeometryCollection --- geo-types/src/geometry/geometry_collection.rs | 36 +++++++++---------- geo-types/src/private_utils.rs | 33 ----------------- 2 files changed, 17 insertions(+), 52 deletions(-) diff --git a/geo-types/src/geometry/geometry_collection.rs b/geo-types/src/geometry/geometry_collection.rs index f7ebfb8da..d481a29e4 100644 --- a/geo-types/src/geometry/geometry_collection.rs +++ b/geo-types/src/geometry/geometry_collection.rs @@ -1,8 +1,10 @@ -use crate::{CoordNum, Geometry}; #[cfg(any(feature = "rstar_0_8", feature = "rstar_0_9"))] -use crate::{Point, Rect}; +use crate::Point; +use crate::{CoordNum, Geometry}; #[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 rstar_0_9::Envelope; use alloc::vec; use alloc::vec::Vec; @@ -254,6 +256,8 @@ impl<'a, T: CoordNum> GeometryCollection { #[cfg(any(feature = "rstar_0_8", feature = "rstar_0_9"))] macro_rules! impl_rstar_geometry_collection { ($rstar:ident) => { + // we have to put the use statement here bc we don't know which rstar version is in use outside the macro + use $rstar::Envelope as Env; impl $rstar::RTreeObject for GeometryCollection where T: ::num_traits::Float + ::$rstar::RTreeNum, @@ -261,25 +265,19 @@ macro_rules! impl_rstar_geometry_collection { type Envelope = ::$rstar::AABB>; 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(crate::private_utils::bounding_rect_merge( - acc.unwrap(), - rect, - )) - }); - match bounding_rect { - None => ::$rstar::AABB::from_corners( + let bounding_rect = + self.iter() + .fold(Env::new_empty(), |acc: ::$rstar::AABB>, next| { + let next_bounding_rect = next.envelope(); + acc.merged(&next_bounding_rect) + }); + if bounding_rect == Env::new_empty() { + ::$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), - ), + ) + } else { + bounding_rect } } } diff --git a/geo-types/src/private_utils.rs b/geo-types/src/private_utils.rs index a5cf3ff1b..1311a7b4f 100644 --- a/geo-types/src/private_utils.rs +++ b/geo-types/src/private_utils.rs @@ -5,39 +5,6 @@ use crate::{Coord, CoordFloat, CoordNum, Line, LineString, Point, Rect}; -// The Rust standard library has `max` for `Ord`, but not for `PartialOrd` -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` -fn partial_min(a: T, b: T) -> T { - if a < b { - a - } else { - b - } -} - -/// Return a new rectangle that encompasses the provided rectangles -// copy-pasted from geo's bounding_rect implementation -pub 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), - }, - ) -} - pub fn line_string_bounding_rect(line_string: &LineString) -> Option> where T: CoordNum, From 297f583ac9d26861d3c3c97dffa333077e60df02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stephan=20H=C3=BCgel?= Date: Wed, 15 Feb 2023 15:45:31 +0000 Subject: [PATCH 04/10] Move bounding rect impl to geo-types This avoids duplicating its functionality, as it's required in order to make all geometry types and the Geometry enum RStar compatible --- geo-types/Cargo.toml | 1 + geo-types/src/bounding_rect.rs | 326 ++++++++++++++++++++++++++++ geo-types/src/geometry/mod.rs | 120 +++++++++++ geo-types/src/lib.rs | 3 + geo-types/src/private_utils.rs | 35 +++ geo/src/algorithm/bounding_rect.rs | 336 +---------------------------- geo/src/algorithm/concave_hull.rs | 2 +- geo/src/lib.rs | 5 +- geo/src/types.rs | 120 ----------- geo/src/utils.rs | 35 --- 10 files changed, 493 insertions(+), 490 deletions(-) create mode 100644 geo-types/src/bounding_rect.rs 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..1ce63bef8 --- /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::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/mod.rs b/geo-types/src/geometry/mod.rs index 74262105a..94d4bbe08 100644 --- a/geo-types/src/geometry/mod.rs +++ b/geo-types/src/geometry/mod.rs @@ -404,3 +404,123 @@ 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(), + } + } + )+ + }; +} 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/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)); - } -} From c0c34e8ba29e31aa780f1bcb1b433a2467e5a0cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stephan=20H=C3=BCgel?= Date: Wed, 15 Feb 2023 16:37:15 +0000 Subject: [PATCH 05/10] Fix doctest --- geo-types/src/bounding_rect.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/geo-types/src/bounding_rect.rs b/geo-types/src/bounding_rect.rs index 1ce63bef8..8ac3e225f 100644 --- a/geo-types/src/bounding_rect.rs +++ b/geo-types/src/bounding_rect.rs @@ -12,7 +12,7 @@ pub trait BoundingRect { /// # Examples /// /// ``` - /// use geo_types::BoundingRect; + /// use geo_types::bounding_rect::BoundingRect; /// use geo_types::line_string; /// /// let line_string = line_string![ From 91fa1339b9a126583765c4babe350f16c6eb32c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stephan=20H=C3=BCgel?= Date: Wed, 15 Feb 2023 16:37:32 +0000 Subject: [PATCH 06/10] Fix method invocation in macro --- geo-types/src/geometry/geometry_collection.rs | 27 +++++++++---------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/geo-types/src/geometry/geometry_collection.rs b/geo-types/src/geometry/geometry_collection.rs index d481a29e4..5375df40c 100644 --- a/geo-types/src/geometry/geometry_collection.rs +++ b/geo-types/src/geometry/geometry_collection.rs @@ -1,17 +1,14 @@ #[cfg(any(feature = "rstar_0_8", feature = "rstar_0_9"))] use crate::Point; use crate::{CoordNum, Geometry}; -#[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 rstar_0_9::Envelope; - 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"))] +use num_traits::Bounded; /// A collection of [`Geometry`](enum.Geometry.html) types. /// @@ -256,8 +253,6 @@ impl<'a, T: CoordNum> GeometryCollection { #[cfg(any(feature = "rstar_0_8", feature = "rstar_0_9"))] macro_rules! impl_rstar_geometry_collection { ($rstar:ident) => { - // we have to put the use statement here bc we don't know which rstar version is in use outside the macro - use $rstar::Envelope as Env; impl $rstar::RTreeObject for GeometryCollection where T: ::num_traits::Float + ::$rstar::RTreeNum, @@ -265,13 +260,17 @@ macro_rules! impl_rstar_geometry_collection { type Envelope = ::$rstar::AABB>; fn envelope(&self) -> Self::Envelope { - let bounding_rect = - self.iter() - .fold(Env::new_empty(), |acc: ::$rstar::AABB>, next| { - let next_bounding_rect = next.envelope(); - acc.merged(&next_bounding_rect) - }); - if bounding_rect == Env::new_empty() { + 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()), From fe40a6c5ff79c567a43dbc14e1adb59fb37d997e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stephan=20H=C3=BCgel?= Date: Thu, 16 Feb 2023 10:33:04 +0000 Subject: [PATCH 07/10] Add Geometry enum RStar test --- geo-types/src/geometry/mod.rs | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/geo-types/src/geometry/mod.rs b/geo-types/src/geometry/mod.rs index 94d4bbe08..dcefefd28 100644 --- a/geo-types/src/geometry/mod.rs +++ b/geo-types/src/geometry/mod.rs @@ -524,3 +524,31 @@ macro_rules! __geometry_delegate_impl_helper { )+ }; } + +#[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()); + } +} From 1494baa10567f9d4c188438d74094e2544b67136 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stephan=20H=C3=BCgel?= Date: Fri, 14 Apr 2023 10:24:20 +0100 Subject: [PATCH 08/10] Add RTreeObject geom impls for rstar 0.10 Also tidy up rstar::Point impl for geo_types::Point --- geo-types/src/geometry/geometry_collection.rs | 9 +- geo-types/src/geometry/mod.rs | 5 +- geo-types/src/geometry/multi_line_string.rs | 9 +- geo-types/src/geometry/multi_point.rs | 7 +- geo-types/src/geometry/multi_polygon.rs | 9 +- geo-types/src/geometry/point.rs | 117 ++++++------------ geo-types/src/geometry/rect.rs | 7 +- geo-types/src/geometry/triangle.rs | 11 +- 8 files changed, 75 insertions(+), 99 deletions(-) diff --git a/geo-types/src/geometry/geometry_collection.rs b/geo-types/src/geometry/geometry_collection.rs index 5375df40c..9ce5ce2d8 100644 --- a/geo-types/src/geometry/geometry_collection.rs +++ b/geo-types/src/geometry/geometry_collection.rs @@ -1,4 +1,4 @@ -#[cfg(any(feature = "rstar_0_8", feature = "rstar_0_9"))] +#[cfg(any(feature = "rstar_0_8", feature = "rstar_0_9", feature = "rstar_0_10"))] use crate::Point; use crate::{CoordNum, Geometry}; use alloc::vec; @@ -7,7 +7,7 @@ use alloc::vec::Vec; use approx::{AbsDiffEq, RelativeEq}; use core::iter::FromIterator; use core::ops::{Index, IndexMut}; -#[cfg(any(feature = "rstar_0_8", feature = "rstar_0_9"))] +#[cfg(any(feature = "rstar_0_8", feature = "rstar_0_9", feature = "rstar_0_10"))] use num_traits::Bounded; /// A collection of [`Geometry`](enum.Geometry.html) types. @@ -250,7 +250,7 @@ impl<'a, T: CoordNum> GeometryCollection { } } -#[cfg(any(feature = "rstar_0_8", feature = "rstar_0_9"))] +#[cfg(any(feature = "rstar_0_8", feature = "rstar_0_9", feature = "rstar_0_10"))] macro_rules! impl_rstar_geometry_collection { ($rstar:ident) => { impl $rstar::RTreeObject for GeometryCollection @@ -289,6 +289,9 @@ 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 dcefefd28..175859e57 100644 --- a/geo-types/src/geometry/mod.rs +++ b/geo-types/src/geometry/mod.rs @@ -269,7 +269,7 @@ where } } -#[cfg(any(feature = "rstar_0_8", feature = "rstar_0_9"))] +#[cfg(any(feature = "rstar_0_8", feature = "rstar_0_9", feature = "rstar_0_10"))] macro_rules! impl_rstar_geometry { ($rstar:ident) => { impl $rstar::RTreeObject for Geometry @@ -302,6 +302,9 @@ 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 diff --git a/geo-types/src/geometry/multi_line_string.rs b/geo-types/src/geometry/multi_line_string.rs index e7b528107..45495ff12 100644 --- a/geo-types/src/geometry/multi_line_string.rs +++ b/geo-types/src/geometry/multi_line_string.rs @@ -1,6 +1,6 @@ -#[cfg(any(feature = "rstar_0_8", feature = "rstar_0_9"))] +#[cfg(any(feature = "rstar_0_8", feature = "rstar_0_9", feature = "rstar_0_10"))] use crate::Point; -#[cfg(any(feature = "rstar_0_8", feature = "rstar_0_9"))] +#[cfg(any(feature = "rstar_0_8", feature = "rstar_0_9", feature = "rstar_0_10"))] use num_traits::Bounded; use crate::{CoordNum, LineString}; @@ -123,7 +123,7 @@ impl MultiLineString { } } -#[cfg(any(feature = "rstar_0_8", feature = "rstar_0_9"))] +#[cfg(any(feature = "rstar_0_8", feature = "rstar_0_9", feature = "rstar_0_10"))] macro_rules! impl_rstar_multi_linestring { ($rstar:ident) => { impl $rstar::RTreeObject for MultiLineString @@ -157,6 +157,9 @@ 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 11af5b1ca..4284c659f 100644 --- a/geo-types/src/geometry/multi_point.rs +++ b/geo-types/src/geometry/multi_point.rs @@ -1,7 +1,7 @@ use crate::{CoordNum, Point}; #[cfg(any(feature = "approx", test))] use approx::{AbsDiffEq, RelativeEq}; -#[cfg(any(feature = "rstar_0_8", feature = "rstar_0_9"))] +#[cfg(any(feature = "rstar_0_8", feature = "rstar_0_9", feature = "rstar_0_10"))] use num_traits::Bounded; use alloc::vec; @@ -100,7 +100,7 @@ impl MultiPoint { } } -#[cfg(any(feature = "rstar_0_8", feature = "rstar_0_9"))] +#[cfg(any(feature = "rstar_0_8", feature = "rstar_0_9", feature = "rstar_0_10"))] macro_rules! impl_rstar_multi_point { ($rstar:ident) => { impl $rstar::RTreeObject for MultiPoint @@ -133,6 +133,9 @@ 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 342b15eef..704e1b681 100644 --- a/geo-types/src/geometry/multi_polygon.rs +++ b/geo-types/src/geometry/multi_polygon.rs @@ -1,7 +1,7 @@ -#[cfg(any(feature = "rstar_0_8", feature = "rstar_0_9"))] +#[cfg(any(feature = "rstar_0_8", feature = "rstar_0_9", feature = "rstar_0_10"))] use crate::Point; use crate::{CoordNum, Polygon}; -#[cfg(any(feature = "rstar_0_8", feature = "rstar_0_9"))] +#[cfg(any(feature = "rstar_0_8", feature = "rstar_0_9", feature = "rstar_0_10"))] use num_traits::Bounded; use alloc::vec; @@ -95,7 +95,7 @@ impl MultiPolygon { } } -#[cfg(any(feature = "rstar_0_8", feature = "rstar_0_9"))] +#[cfg(any(feature = "rstar_0_8", feature = "rstar_0_9", feature = "rstar_0_10"))] macro_rules! impl_rstar_multi_polygon { ($rstar:ident) => { impl $rstar::RTreeObject for MultiPolygon @@ -130,6 +130,9 @@ 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..512d32b1b 100644 --- a/geo-types/src/geometry/point.rs +++ b/geo-types/src/geometry/point.rs @@ -586,93 +586,48 @@ 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"))] +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 76e77a55c..b85bed4ad 100644 --- a/geo-types/src/geometry/rect.rs +++ b/geo-types/src/geometry/rect.rs @@ -1,6 +1,6 @@ use crate::{coord, polygon, Coord, CoordFloat, CoordNum, Line, Polygon}; -#[cfg(any(feature = "rstar_0_8", feature = "rstar_0_9"))] +#[cfg(any(feature = "rstar_0_8", feature = "rstar_0_9", feature = "rstar_0_10"))] use crate::Point; #[cfg(any(feature = "approx", test))] use approx::{AbsDiffEq, RelativeEq}; @@ -380,7 +380,7 @@ 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"))] +#[cfg(any(feature = "rstar_0_8", feature = "rstar_0_9", feature = "rstar_0_10"))] macro_rules! impl_rstar_rect { ($rstar:ident) => { impl $rstar::RTreeObject for Rect @@ -405,6 +405,9 @@ 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 eb5f86076..67e393c57 100644 --- a/geo-types/src/geometry/triangle.rs +++ b/geo-types/src/geometry/triangle.rs @@ -1,10 +1,10 @@ use crate::{polygon, Coord, CoordNum, Line, Polygon}; -#[cfg(any(feature = "rstar_0_8", feature = "rstar_0_9"))] +#[cfg(any(feature = "rstar_0_8", feature = "rstar_0_9", feature = "rstar_0_10"))] use num_traits::Bounded; -#[cfg(any(feature = "rstar_0_8", feature = "rstar_0_9"))] +#[cfg(any(feature = "rstar_0_8", feature = "rstar_0_9", feature = "rstar_0_10"))] use crate::private_utils::get_bounding_rect; -#[cfg(any(feature = "rstar_0_8", feature = "rstar_0_9"))] +#[cfg(any(feature = "rstar_0_8", feature = "rstar_0_9", feature = "rstar_0_10"))] use crate::Point; #[cfg(any(feature = "approx", test))] @@ -70,7 +70,7 @@ impl> + Copy, T: CoordNum> From<[IC; 3]> for Triangle { } } -#[cfg(any(feature = "rstar_0_8", feature = "rstar_0_9"))] +#[cfg(any(feature = "rstar_0_8", feature = "rstar_0_9", feature = "rstar_0_10"))] macro_rules! impl_rstar_triangle { ($rstar:ident) => { impl $rstar::RTreeObject for Triangle @@ -102,6 +102,9 @@ 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 From 607a6ddff03009de65672eba0eb38e5710aceb96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stephan=20H=C3=BCgel?= Date: Mon, 4 Sep 2023 17:07:27 +0100 Subject: [PATCH 09/10] Add rstar 0.11 compat --- geo-types/src/geometry/geometry_collection.rs | 21 ++++++++++++-- geo-types/src/geometry/mod.rs | 7 ++++- geo-types/src/geometry/multi_line_string.rs | 21 ++++++++++++-- geo-types/src/geometry/multi_point.rs | 14 ++++++++-- geo-types/src/geometry/multi_polygon.rs | 21 ++++++++++++-- geo-types/src/geometry/point.rs | 7 ++++- geo-types/src/geometry/rect.rs | 14 ++++++++-- geo-types/src/geometry/triangle.rs | 28 ++++++++++++++++--- 8 files changed, 114 insertions(+), 19 deletions(-) diff --git a/geo-types/src/geometry/geometry_collection.rs b/geo-types/src/geometry/geometry_collection.rs index 9ce5ce2d8..b16ba4562 100644 --- a/geo-types/src/geometry/geometry_collection.rs +++ b/geo-types/src/geometry/geometry_collection.rs @@ -1,4 +1,9 @@ -#[cfg(any(feature = "rstar_0_8", feature = "rstar_0_9", feature = "rstar_0_10"))] +#[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; @@ -7,7 +12,12 @@ use alloc::vec::Vec; 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"))] +#[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. @@ -250,7 +260,12 @@ impl<'a, T: CoordNum> GeometryCollection { } } -#[cfg(any(feature = "rstar_0_8", feature = "rstar_0_9", feature = "rstar_0_10"))] +#[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 diff --git a/geo-types/src/geometry/mod.rs b/geo-types/src/geometry/mod.rs index 175859e57..115613dc5 100644 --- a/geo-types/src/geometry/mod.rs +++ b/geo-types/src/geometry/mod.rs @@ -269,7 +269,12 @@ where } } -#[cfg(any(feature = "rstar_0_8", feature = "rstar_0_9", feature = "rstar_0_10"))] +#[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 diff --git a/geo-types/src/geometry/multi_line_string.rs b/geo-types/src/geometry/multi_line_string.rs index 45495ff12..f9136a96e 100644 --- a/geo-types/src/geometry/multi_line_string.rs +++ b/geo-types/src/geometry/multi_line_string.rs @@ -1,6 +1,16 @@ -#[cfg(any(feature = "rstar_0_8", feature = "rstar_0_9", feature = "rstar_0_10"))] +#[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"))] +#[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}; @@ -123,7 +133,12 @@ impl MultiLineString { } } -#[cfg(any(feature = "rstar_0_8", feature = "rstar_0_9", feature = "rstar_0_10"))] +#[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 diff --git a/geo-types/src/geometry/multi_point.rs b/geo-types/src/geometry/multi_point.rs index 4284c659f..b9a6c9960 100644 --- a/geo-types/src/geometry/multi_point.rs +++ b/geo-types/src/geometry/multi_point.rs @@ -1,7 +1,12 @@ 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"))] +#[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; @@ -100,7 +105,12 @@ impl MultiPoint { } } -#[cfg(any(feature = "rstar_0_8", feature = "rstar_0_9", feature = "rstar_0_10"))] +#[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 diff --git a/geo-types/src/geometry/multi_polygon.rs b/geo-types/src/geometry/multi_polygon.rs index 704e1b681..8dad68f47 100644 --- a/geo-types/src/geometry/multi_polygon.rs +++ b/geo-types/src/geometry/multi_polygon.rs @@ -1,7 +1,17 @@ -#[cfg(any(feature = "rstar_0_8", feature = "rstar_0_9", feature = "rstar_0_10"))] +#[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"))] +#[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; @@ -95,7 +105,12 @@ impl MultiPolygon { } } -#[cfg(any(feature = "rstar_0_8", feature = "rstar_0_9", feature = "rstar_0_10"))] +#[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 diff --git a/geo-types/src/geometry/point.rs b/geo-types/src/geometry/point.rs index 512d32b1b..39e1d2283 100644 --- a/geo-types/src/geometry/point.rs +++ b/geo-types/src/geometry/point.rs @@ -586,7 +586,12 @@ where } } -#[cfg(any(feature = "rstar_0_8", feature = "rstar_0_9", feature = "rstar_0_10"))] +#[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 diff --git a/geo-types/src/geometry/rect.rs b/geo-types/src/geometry/rect.rs index b85bed4ad..e1ba43f68 100644 --- a/geo-types/src/geometry/rect.rs +++ b/geo-types/src/geometry/rect.rs @@ -1,6 +1,11 @@ use crate::{coord, polygon, Coord, CoordFloat, CoordNum, Line, Polygon}; -#[cfg(any(feature = "rstar_0_8", feature = "rstar_0_9", feature = "rstar_0_10"))] +#[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}; @@ -380,7 +385,12 @@ 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"))] +#[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 diff --git a/geo-types/src/geometry/triangle.rs b/geo-types/src/geometry/triangle.rs index 67e393c57..1225cd7e7 100644 --- a/geo-types/src/geometry/triangle.rs +++ b/geo-types/src/geometry/triangle.rs @@ -1,10 +1,25 @@ use crate::{polygon, Coord, CoordNum, Line, Polygon}; -#[cfg(any(feature = "rstar_0_8", feature = "rstar_0_9", feature = "rstar_0_10"))] +#[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"))] +#[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"))] +#[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))] @@ -70,7 +85,12 @@ impl> + Copy, T: CoordNum> From<[IC; 3]> for Triangle { } } -#[cfg(any(feature = "rstar_0_8", feature = "rstar_0_9", feature = "rstar_0_10"))] +#[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 From e9ff06314a68d3d1ce73138310970246b182a1dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stephan=20H=C3=BCgel?= Date: Mon, 4 Sep 2023 17:33:58 +0100 Subject: [PATCH 10/10] Use the bounding_rect feature in geo by default --- geo/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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"