diff --git a/geo/CHANGES.md b/geo/CHANGES.md index 0d66d51658..dfb8e785a9 100644 --- a/geo/CHANGES.md +++ b/geo/CHANGES.md @@ -83,6 +83,8 @@ changing some trait constraints, but they are unlikely to affect you in practice unless you have your own Relate implementation. - +- Add: PreparedGeometry::geometry and into_geometry to get at the inner geometry type. + - ## 0.29.3 - 2024.12.03 diff --git a/geo/src/algorithm/relate/geomgraph/index/mod.rs b/geo/src/algorithm/relate/geomgraph/index/mod.rs index 5695878e24..c43ba52ef3 100644 --- a/geo/src/algorithm/relate/geomgraph/index/mod.rs +++ b/geo/src/algorithm/relate/geomgraph/index/mod.rs @@ -6,6 +6,7 @@ mod segment_intersector; mod simple_edge_set_intersector; pub(crate) use edge_set_intersector::EdgeSetIntersector; +pub(crate) use prepared_geometry::prepare_geometry; pub use prepared_geometry::PreparedGeometry; pub(crate) use rstar_edge_set_intersector::RStarEdgeSetIntersector; pub(crate) use segment::Segment; diff --git a/geo/src/algorithm/relate/geomgraph/index/prepared_geometry.rs b/geo/src/algorithm/relate/geomgraph/index/prepared_geometry.rs index 701c70118f..dfc01cf0e0 100644 --- a/geo/src/algorithm/relate/geomgraph/index/prepared_geometry.rs +++ b/geo/src/algorithm/relate/geomgraph/index/prepared_geometry.rs @@ -8,7 +8,7 @@ use std::cell::RefCell; use std::rc::Rc; use crate::dimensions::Dimensions; -use rstar::{RTree, RTreeNum}; +use rstar::{Envelope, RTree, RTreeNum}; /// A `PreparedGeometry` can be more efficient than a plain Geometry when performing /// multiple topological comparisons against the `PreparedGeometry`. @@ -27,175 +27,62 @@ use rstar::{RTree, RTreeNum}; /// assert!(prepared_polygon.relate(&contained_line).is_contains()); /// /// ``` -pub struct PreparedGeometry<'a, F: GeoFloat + RTreeNum = f64> { +pub struct PreparedGeometry<'a, G: Into>, F: GeoFloat + RTreeNum = f64> { + pub(crate) geometry: G, pub(crate) geometry_graph: GeometryGraph<'a, F>, pub(crate) bounding_rect: Option>, } -mod conversions { - use crate::geometry_cow::GeometryCow; - use crate::relate::geomgraph::{GeometryGraph, RobustLineIntersector}; - use crate::{BoundingRect, GeoFloat, PreparedGeometry}; - use geo_types::{ - Geometry, GeometryCollection, Line, LineString, MultiLineString, MultiPoint, MultiPolygon, - Point, Polygon, Rect, Triangle, +use crate::geometry::*; +pub(crate) fn prepare_geometry<'a, F, T>(geometry: T) -> PreparedGeometry<'a, T, F> +where + F: GeoFloat, + T: Clone + Into>, +{ + let mut geometry_graph = GeometryGraph::new(0, geometry.clone().into()); + let r_tree = geometry_graph.build_tree(); + let envelope = r_tree.root().envelope(); + + // geo and rstar have different conventions for how to represet an empty bounding boxes + let bounding_rect: Option> = if envelope == rstar::AABB::new_empty() { + None + } else { + Some(Rect::new(envelope.lower(), envelope.upper())) }; - use rstar::Envelope; - use std::rc::Rc; - - impl From> for PreparedGeometry<'_, F> { - fn from(point: Point) -> Self { - PreparedGeometry::from(GeometryCow::from(point)) - } - } - impl From> for PreparedGeometry<'_, F> { - fn from(line: Line) -> Self { - PreparedGeometry::from(GeometryCow::from(line)) - } - } - impl From> for PreparedGeometry<'_, F> { - fn from(line_string: LineString) -> Self { - PreparedGeometry::from(GeometryCow::from(line_string)) - } - } - impl From> for PreparedGeometry<'_, F> { - fn from(polygon: Polygon) -> Self { - PreparedGeometry::from(GeometryCow::from(polygon)) - } - } - impl From> for PreparedGeometry<'_, F> { - fn from(multi_point: MultiPoint) -> Self { - PreparedGeometry::from(GeometryCow::from(multi_point)) - } - } - impl From> for PreparedGeometry<'_, F> { - fn from(multi_line_string: MultiLineString) -> Self { - PreparedGeometry::from(GeometryCow::from(multi_line_string)) - } - } - impl From> for PreparedGeometry<'_, F> { - fn from(multi_polygon: MultiPolygon) -> Self { - PreparedGeometry::from(GeometryCow::from(multi_polygon)) - } - } - impl From> for PreparedGeometry<'_, F> { - fn from(rect: Rect) -> Self { - PreparedGeometry::from(GeometryCow::from(rect)) - } - } - impl From> for PreparedGeometry<'_, F> { - fn from(triangle: Triangle) -> Self { - PreparedGeometry::from(GeometryCow::from(triangle)) - } - } - impl From> for PreparedGeometry<'_, F> { - fn from(geometry_collection: GeometryCollection) -> Self { - PreparedGeometry::from(GeometryCow::from(geometry_collection)) - } - } - impl From> for PreparedGeometry<'_, F> { - fn from(geometry: Geometry) -> Self { - PreparedGeometry::from(GeometryCow::from(geometry)) - } - } - - impl<'a, F: GeoFloat> From<&'a Point> for PreparedGeometry<'a, F> { - fn from(point: &'a Point) -> Self { - PreparedGeometry::from(GeometryCow::from(point)) - } - } - impl<'a, F: GeoFloat> From<&'a Line> for PreparedGeometry<'a, F> { - fn from(line: &'a Line) -> Self { - PreparedGeometry::from(GeometryCow::from(line)) - } - } - impl<'a, F: GeoFloat> From<&'a LineString> for PreparedGeometry<'a, F> { - fn from(line_string: &'a LineString) -> Self { - PreparedGeometry::from(GeometryCow::from(line_string)) - } - } - impl<'a, F: GeoFloat> From<&'a Polygon> for PreparedGeometry<'a, F> { - fn from(polygon: &'a Polygon) -> Self { - PreparedGeometry::from(GeometryCow::from(polygon)) - } - } - impl<'a, F: GeoFloat> From<&'a MultiPoint> for PreparedGeometry<'a, F> { - fn from(multi_point: &'a MultiPoint) -> Self { - PreparedGeometry::from(GeometryCow::from(multi_point)) - } - } - impl<'a, F: GeoFloat> From<&'a MultiLineString> for PreparedGeometry<'a, F> { - fn from(multi_line_string: &'a MultiLineString) -> Self { - PreparedGeometry::from(GeometryCow::from(multi_line_string)) - } - } - impl<'a, F: GeoFloat> From<&'a MultiPolygon> for PreparedGeometry<'a, F> { - fn from(multi_polygon: &'a MultiPolygon) -> Self { - PreparedGeometry::from(GeometryCow::from(multi_polygon)) - } - } - impl<'a, F: GeoFloat> From<&'a GeometryCollection> for PreparedGeometry<'a, F> { - fn from(geometry_collection: &'a GeometryCollection) -> Self { - PreparedGeometry::from(GeometryCow::from(geometry_collection)) - } - } - impl<'a, F: GeoFloat> From<&'a Rect> for PreparedGeometry<'a, F> { - fn from(rect: &'a Rect) -> Self { - PreparedGeometry::from(GeometryCow::from(rect)) - } - } - impl<'a, F: GeoFloat> From<&'a Triangle> for PreparedGeometry<'a, F> { - fn from(triangle: &'a Triangle) -> Self { - PreparedGeometry::from(GeometryCow::from(triangle)) - } - } - - impl<'a, F: GeoFloat> From<&'a Geometry> for PreparedGeometry<'a, F> { - fn from(geometry: &'a Geometry) -> Self { - PreparedGeometry::from(GeometryCow::from(geometry)) - } - } + // They should be equal - but we can save the computation in the `--release` case + // by using the bounding_rect from the RTree + debug_assert_eq!(bounding_rect, geometry_graph.geometry().bounding_rect()); - impl<'a, F: GeoFloat> From> for PreparedGeometry<'a, F> { - fn from(geometry: GeometryCow<'a, F>) -> Self { - let mut geometry_graph = GeometryGraph::new(0, geometry); - let r_tree = geometry_graph.build_tree(); + geometry_graph.set_tree(Rc::new(r_tree)); - let envelope = r_tree.root().envelope(); - - // geo and rstar have different conventions for how to represet an empty bounding boxes - let bounding_rect: Option> = if envelope == rstar::AABB::new_empty() { - None - } else { - Some(Rect::new(envelope.lower(), envelope.upper())) - }; - // They should be equal - but we can save the computation in the `--release` case - // by using the bounding_rect from the RTree - debug_assert_eq!(bounding_rect, geometry_graph.geometry().bounding_rect()); - - geometry_graph.set_tree(Rc::new(r_tree)); - - // TODO: don't pass in line intersector here - in theory we'll want pluggable line intersectors - // and the type (Robust) shouldn't be hard coded here. - geometry_graph.compute_self_nodes(Box::new(RobustLineIntersector::new())); - Self { - geometry_graph, - bounding_rect, - } - } + // TODO: don't pass in line intersector here - in theory we'll want pluggable line intersectors + // and the type (Robust) shouldn't be hard coded here. + geometry_graph.compute_self_nodes(Box::new(RobustLineIntersector::new())); + PreparedGeometry { + geometry, + geometry_graph, + bounding_rect, } } -impl PreparedGeometry<'_, F> +impl<'a, G, F> PreparedGeometry<'a, G, F> where F: GeoFloat + RTreeNum, + G: Into>, { - pub(crate) fn geometry(&self) -> &GeometryCow { - self.geometry_graph.geometry() + pub fn geometry(&self) -> &G { + &self.geometry + } + pub fn into_geometry(self) -> G { + self.geometry } } -impl BoundingRect for PreparedGeometry<'_, F> { +impl<'a, G, F> BoundingRect for PreparedGeometry<'a, G, F> +where + F: GeoFloat, + G: Into>, +{ type Output = Option>; fn bounding_rect(&self) -> Option> { @@ -203,7 +90,11 @@ impl BoundingRect for PreparedGeometry<'_, F> { } } -impl HasDimensions for PreparedGeometry<'_, F> { +impl<'a, G, F: GeoFloat> HasDimensions for PreparedGeometry<'a, G, F> +where + F: GeoFloat, + G: Into>, +{ fn is_empty(&self) -> bool { self.geometry_graph.geometry().is_empty() } @@ -217,7 +108,11 @@ impl HasDimensions for PreparedGeometry<'_, F> { } } -impl Relate for PreparedGeometry<'_, F> { +impl<'a, G, F: GeoFloat> Relate for PreparedGeometry<'a, G, F> +where + F: GeoFloat, + G: Into>, +{ /// Efficiently builds a [`GeometryGraph`] which can then be used for topological /// computations. fn geometry_graph(&self, arg_index: usize) -> GeometryGraph { @@ -265,4 +160,16 @@ mod tests { let fresh_graph = GeometryGraph::new(1, poly_cow); cached_graph.assert_eq_graph(&fresh_graph); } + + #[test] + fn get_geometry() { + let poly = polygon![(x: 0.0, y: 0.0), (x: 2.0, y: 0.0), (x: 1.0, y: 1.0)]; + let prepared_geom = PreparedGeometry::from(&poly); + assert_eq!(&poly, *prepared_geom.geometry()); + assert_eq!(&poly, prepared_geom.into_geometry()); + + let prepared_geom = PreparedGeometry::from(poly.clone()); + assert_eq!(&poly, prepared_geom.geometry()); + assert_eq!(poly, prepared_geom.into_geometry()); + } } diff --git a/geo/src/algorithm/relate/mod.rs b/geo/src/algorithm/relate/mod.rs index bb97bddff3..b35f9245a5 100644 --- a/geo/src/algorithm/relate/mod.rs +++ b/geo/src/algorithm/relate/mod.rs @@ -76,7 +76,17 @@ macro_rules! relate_impl { $( impl Relate for $t { fn geometry_graph(&self, arg_index: usize) -> GeometryGraph { - GeometryGraph::new(arg_index, GeometryCow::from(self)) + $crate::relate::GeometryGraph::new(arg_index, GeometryCow::from(self)) + } + } + impl From<$t> for PreparedGeometry<'static, $t, F> { + fn from(geometry: $t) -> Self { + $crate::relate::geomgraph::index::prepare_geometry(geometry) + } + } + impl<'a, F: GeoFloat> From<&'a $t> for PreparedGeometry<'a, &'a $t, F> { + fn from(geometry: &'a $t) -> Self { + $crate::relate::geomgraph::index::prepare_geometry(geometry) } } )* diff --git a/geo/src/geometry_cow.rs b/geo/src/geometry_cow.rs index c837c0f441..3649c8c271 100644 --- a/geo/src/geometry_cow.rs +++ b/geo/src/geometry_cow.rs @@ -11,7 +11,7 @@ use std::borrow::Cow; /// /// As an example, see the [`Relate`] trait which uses `GeometryCow`. #[derive(PartialEq, Debug, Hash, Clone)] -pub(crate) enum GeometryCow<'a, T> +pub enum GeometryCow<'a, T> where T: CoordNum, {