diff --git a/geo-types/src/geometry.rs b/geo-types/src/geometry.rs index 4f4ac4abf..b964cd45b 100644 --- a/geo-types/src/geometry.rs +++ b/geo-types/src/geometry.rs @@ -3,6 +3,7 @@ use crate::{ MultiPolygon, Point, Polygon, Rect, Triangle, }; use num_traits::Float; +use std::borrow::Cow; use std::convert::TryFrom; use std::error::Error; use std::fmt; @@ -75,19 +76,111 @@ impl From> for Geometry { Geometry::MultiPolygon(x) } } - impl From> for Geometry { fn from(x: Rect) -> Geometry { Geometry::Rect(x) } } - impl From> for Geometry { fn from(x: Triangle) -> Geometry { Geometry::Triangle(x) } } +#[derive(PartialEq, Debug, Hash)] +pub enum GeometryCow<'a, T> +where + T: CoordinateType, +{ + Point(Cow<'a, Point>), + Line(Cow<'a, Line>), + LineString(Cow<'a, LineString>), + Polygon(Cow<'a, Polygon>), + MultiPoint(Cow<'a, MultiPoint>), + MultiLineString(Cow<'a, MultiLineString>), + MultiPolygon(Cow<'a, MultiPolygon>), + GeometryCollection(Cow<'a, GeometryCollection>), + Rect(Cow<'a, Rect>), + Triangle(Cow<'a, Triangle>), +} + +impl<'a, T: CoordinateType> From<&'a Geometry> for GeometryCow<'a, T> { + fn from(geometry: &'a Geometry) -> Self { + match geometry { + Geometry::Point(g) => GeometryCow::Point(Cow::Borrowed(g)), + Geometry::Line(g) => GeometryCow::Line(Cow::Borrowed(g)), + Geometry::LineString(g) => GeometryCow::LineString(Cow::Borrowed(g)), + Geometry::Polygon(g) => GeometryCow::Polygon(Cow::Borrowed(g)), + Geometry::MultiPoint(g) => GeometryCow::MultiPoint(Cow::Borrowed(g)), + Geometry::MultiLineString(g) => GeometryCow::MultiLineString(Cow::Borrowed(g)), + Geometry::MultiPolygon(g) => GeometryCow::MultiPolygon(Cow::Borrowed(g)), + Geometry::GeometryCollection(g) => GeometryCow::GeometryCollection(Cow::Borrowed(g)), + Geometry::Rect(g) => GeometryCow::Rect(Cow::Borrowed(g)), + Geometry::Triangle(g) => GeometryCow::Triangle(Cow::Borrowed(g)), + } + } +} + +impl<'a, T: CoordinateType> From<&'a Point> for GeometryCow<'a, T> { + fn from(point: &'a Point) -> Self { + GeometryCow::Point(Cow::Borrowed(point)) + } +} + +impl<'a, T: CoordinateType> From<&'a LineString> for GeometryCow<'a, T> { + fn from(line_string: &'a LineString) -> Self { + GeometryCow::LineString(Cow::Borrowed(line_string)) + } +} + +impl<'a, T: CoordinateType> From<&'a Line> for GeometryCow<'a, T> { + fn from(line: &'a Line) -> Self { + GeometryCow::Line(Cow::Borrowed(line)) + } +} + +impl<'a, T: CoordinateType> From<&'a Polygon> for GeometryCow<'a, T> { + fn from(polygon: &'a Polygon) -> Self { + GeometryCow::Polygon(Cow::Borrowed(polygon)) + } +} + +impl<'a, T: CoordinateType> From<&'a MultiPoint> for GeometryCow<'a, T> { + fn from(multi_point: &'a MultiPoint) -> GeometryCow<'a, T> { + GeometryCow::MultiPoint(Cow::Borrowed(multi_point)) + } +} + +impl<'a, T: CoordinateType> From<&'a MultiLineString> for GeometryCow<'a, T> { + fn from(multi_line_string: &'a MultiLineString) -> Self { + GeometryCow::MultiLineString(Cow::Borrowed(multi_line_string)) + } +} + +impl<'a, T: CoordinateType> From<&'a MultiPolygon> for GeometryCow<'a, T> { + fn from(multi_polygon: &'a MultiPolygon) -> Self { + GeometryCow::MultiPolygon(Cow::Borrowed(multi_polygon)) + } +} + +impl<'a, T: CoordinateType> From<&'a GeometryCollection> for GeometryCow<'a, T> { + fn from(geometry_collection: &'a GeometryCollection) -> Self { + GeometryCow::GeometryCollection(Cow::Borrowed(geometry_collection)) + } +} + +impl<'a, T: CoordinateType> From<&'a Rect> for GeometryCow<'a, T> { + fn from(rect: &'a Rect) -> Self { + GeometryCow::Rect(Cow::Borrowed(rect)) + } +} + +impl<'a, T: CoordinateType> From<&'a Triangle> for GeometryCow<'a, T> { + fn from(triangle: &'a Triangle) -> Self { + GeometryCow::Triangle(Cow::Borrowed(triangle)) + } +} + impl Geometry { /// If this Geometry is a Point, then return that, else None. /// diff --git a/geo-types/src/lib.rs b/geo-types/src/lib.rs index 0a054b6ed..aedbbef55 100644 --- a/geo-types/src/lib.rs +++ b/geo-types/src/lib.rs @@ -50,7 +50,7 @@ mod multi_polygon; pub use crate::multi_polygon::MultiPolygon; mod geometry; -pub use crate::geometry::Geometry; +pub use crate::geometry::{Geometry, GeometryCow}; mod geometry_collection; pub use crate::geometry_collection::GeometryCollection; diff --git a/geo/src/algorithm/area.rs b/geo/src/algorithm/area.rs index 5183242ae..c4249456a 100644 --- a/geo/src/algorithm/area.rs +++ b/geo/src/algorithm/area.rs @@ -1,5 +1,7 @@ -use crate::{CoordinateType, Line, LineString, MultiPolygon, Polygon, Rect, Triangle}; -use num_traits::Float; +use crate::{ + CoordinateType, GeometryCollection, GeometryCow, LineString, MultiPolygon, Polygon, Rect, + Triangle, +}; use crate::algorithm::winding_order::twice_signed_ring_area; @@ -27,72 +29,74 @@ use crate::algorithm::winding_order::twice_signed_ring_area; /// /// assert_eq!(polygon.area(), -30.); /// ``` -pub trait Area +pub trait Area<'a, T> where T: CoordinateType, { - fn area(&self) -> T; + fn area(&'a self) -> T; } // Calculation of simple (no interior holes) Polygon area pub(crate) fn get_linestring_area(linestring: &LineString) -> T where - T: Float, + T: CoordinateType, { twice_signed_ring_area(linestring) / (T::one() + T::one()) } -impl Area for Line -where - T: CoordinateType, -{ - fn area(&self) -> T { - T::zero() - } +fn area_polygon(polygon: &Polygon) -> T { + polygon + .interiors() + .iter() + .fold(get_linestring_area(polygon.exterior()), |total, next| { + total - get_linestring_area(next) + }) } -impl Area for Polygon -where - T: Float, -{ - fn area(&self) -> T { - self.interiors() - .iter() - .fold(get_linestring_area(self.exterior()), |total, next| { - total - get_linestring_area(next) - }) - } +fn area_multi_polygon(multi_polygon: &MultiPolygon) -> T { + multi_polygon + .0 + .iter() + .fold(T::zero(), |total, next| total + next.area()) } -impl Area for MultiPolygon -where - T: Float, -{ - fn area(&self) -> T { - self.0 - .iter() - .fold(T::zero(), |total, next| total + next.area()) - } +fn area_geometry_collection(geometry_collection: &GeometryCollection) -> T { + geometry_collection + .iter() + .fold(T::zero(), |total, geometry| total + geometry.area()) } -impl Area for Rect -where - T: CoordinateType, -{ - fn area(&self) -> T { - self.width() * self.height() - } +fn area_rect(rect: &Rect) -> T { + rect.width() * rect.height() +} + +fn area_triangle(triangle: &Triangle) -> T { + triangle + .to_lines() + .iter() + .fold(T::zero(), |total, line| total + line.determinant()) + / (T::one() + T::one()) } -impl Area for Triangle +impl<'a, I: 'a, T: 'a> Area<'a, T> for I where - T: Float, + &'a I: Into>, + T: CoordinateType, { - fn area(&self) -> T { - self.to_lines() - .iter() - .fold(T::zero(), |total, line| total + line.determinant()) - / (T::one() + T::one()) + fn area(&'a self) -> T { + let geometry_cow: GeometryCow<'a, T> = self.into(); + match geometry_cow { + GeometryCow::Point(_) => T::zero(), + GeometryCow::Line(_) => T::zero(), + GeometryCow::LineString(_) => T::zero(), + GeometryCow::Polygon(g) => area_polygon(&*g), + GeometryCow::MultiPoint(_) => T::zero(), + GeometryCow::MultiLineString(_) => T::zero(), + GeometryCow::MultiPolygon(g) => area_multi_polygon(&*g), + GeometryCow::GeometryCollection(g) => area_geometry_collection(&*g), + GeometryCow::Rect(g) => area_rect(&*g), + GeometryCow::Triangle(g) => area_triangle(&*g), + } } } diff --git a/geo/src/algorithm/map_coords.rs b/geo/src/algorithm/map_coords.rs index 87b4e1929..8b0c2305e 100644 --- a/geo/src/algorithm/map_coords.rs +++ b/geo/src/algorithm/map_coords.rs @@ -36,7 +36,7 @@ use crate::{ Coordinate, CoordinateType, Geometry, GeometryCollection, Line, LineString, MultiLineString, - MultiPoint, MultiPolygon, Point, Polygon, Rect, Triangle + MultiPoint, MultiPolygon, Point, Polygon, Rect, Triangle, }; use std::error::Error; diff --git a/geo/src/lib.rs b/geo/src/lib.rs index 6a940f750..8dfe918ff 100644 --- a/geo/src/lib.rs +++ b/geo/src/lib.rs @@ -54,8 +54,9 @@ pub use crate::traits::ToGeo; pub use crate::types::*; pub use geo_types::{ - line_string, point, polygon, Coordinate, CoordinateType, Geometry, GeometryCollection, Line, - LineString, MultiLineString, MultiPoint, MultiPolygon, Point, Polygon, Rect, Triangle, + line_string, point, polygon, Coordinate, CoordinateType, Geometry, GeometryCollection, + GeometryCow, Line, LineString, MultiLineString, MultiPoint, MultiPolygon, Point, Polygon, Rect, + Triangle, }; /// This module includes all the functions of geometric calculations