diff --git a/src/algorithm/geo/area.rs b/src/algorithm/geo/area.rs index 5fa83ddb3..e01cfc591 100644 --- a/src/algorithm/geo/area.rs +++ b/src/algorithm/geo/area.rs @@ -3,6 +3,7 @@ use crate::{GeometryArray, GeometryArrayTrait}; use arrow2::array::{MutablePrimitiveArray, PrimitiveArray}; use geo::prelude::Area; +/// Unsigned planar area of the input geometries pub fn area(array: GeometryArray) -> Result> { let mut output_array = MutablePrimitiveArray::::with_capacity(array.len()); @@ -40,6 +41,7 @@ pub fn area(array: GeometryArray) -> Result> { Ok(output_array.into()) } +/// Signed planar area of the input geometries pub fn signed_area(array: GeometryArray) -> Result> { let mut output_array = MutablePrimitiveArray::::with_capacity(array.len()); diff --git a/src/algorithm/geo/chamberlain_duquette_area.rs b/src/algorithm/geo/chamberlain_duquette_area.rs new file mode 100644 index 000000000..c0a3715e8 --- /dev/null +++ b/src/algorithm/geo/chamberlain_duquette_area.rs @@ -0,0 +1,96 @@ +use crate::error::Result; +use crate::{GeometryArray, GeometryArrayTrait}; +use arrow2::array::{MutablePrimitiveArray, PrimitiveArray}; +use geo::prelude::ChamberlainDuquetteArea; + +/// Calculate the unsigned approximate geodesic area of geometries on a sphere using the algorithm +/// presented in Some Algorithms for Polygons on a Sphere by Chamberlain and Duquette (2007) +pub fn chamberlain_duquette_unsigned_area(array: GeometryArray) -> Result> { + let mut output_array = MutablePrimitiveArray::::with_capacity(array.len()); + + match array { + GeometryArray::WKB(arr) => { + arr.iter_geo().for_each(|maybe_g| { + output_array.push(maybe_g.map(|g| g.chamberlain_duquette_unsigned_area())) + }); + } + GeometryArray::Point(arr) => { + arr.iter_geo().for_each(|maybe_g| { + output_array.push(maybe_g.map(|g| g.chamberlain_duquette_unsigned_area())) + }); + } + GeometryArray::LineString(arr) => { + arr.iter_geo().for_each(|maybe_g| { + output_array.push(maybe_g.map(|g| g.chamberlain_duquette_unsigned_area())) + }); + } + GeometryArray::Polygon(arr) => { + arr.iter_geo().for_each(|maybe_g| { + output_array.push(maybe_g.map(|g| g.chamberlain_duquette_unsigned_area())) + }); + } + GeometryArray::MultiPoint(arr) => { + arr.iter_geo().for_each(|maybe_g| { + output_array.push(maybe_g.map(|g| g.chamberlain_duquette_unsigned_area())) + }); + } + GeometryArray::MultiLineString(arr) => { + arr.iter_geo().for_each(|maybe_g| { + output_array.push(maybe_g.map(|g| g.chamberlain_duquette_unsigned_area())) + }); + } + GeometryArray::MultiPolygon(arr) => { + arr.iter_geo().for_each(|maybe_g| { + output_array.push(maybe_g.map(|g| g.chamberlain_duquette_unsigned_area())) + }); + } + } + + Ok(output_array.into()) +} + +/// Calculate the signed approximate geodesic area of geometries on a sphere using the algorithm +/// presented in Some Algorithms for Polygons on a Sphere by Chamberlain and Duquette (2007) +pub fn chamberlain_duquette_signed_area(array: GeometryArray) -> Result> { + let mut output_array = MutablePrimitiveArray::::with_capacity(array.len()); + + match array { + GeometryArray::WKB(arr) => { + arr.iter_geo().for_each(|maybe_g| { + output_array.push(maybe_g.map(|g| g.chamberlain_duquette_signed_area())) + }); + } + GeometryArray::Point(arr) => { + arr.iter_geo().for_each(|maybe_g| { + output_array.push(maybe_g.map(|g| g.chamberlain_duquette_signed_area())) + }); + } + GeometryArray::LineString(arr) => { + arr.iter_geo().for_each(|maybe_g| { + output_array.push(maybe_g.map(|g| g.chamberlain_duquette_signed_area())) + }); + } + GeometryArray::Polygon(arr) => { + arr.iter_geo().for_each(|maybe_g| { + output_array.push(maybe_g.map(|g| g.chamberlain_duquette_signed_area())) + }); + } + GeometryArray::MultiPoint(arr) => { + arr.iter_geo().for_each(|maybe_g| { + output_array.push(maybe_g.map(|g| g.chamberlain_duquette_signed_area())) + }); + } + GeometryArray::MultiLineString(arr) => { + arr.iter_geo().for_each(|maybe_g| { + output_array.push(maybe_g.map(|g| g.chamberlain_duquette_signed_area())) + }); + } + GeometryArray::MultiPolygon(arr) => { + arr.iter_geo().for_each(|maybe_g| { + output_array.push(maybe_g.map(|g| g.chamberlain_duquette_signed_area())) + }); + } + } + + Ok(output_array.into()) +} diff --git a/src/algorithm/geo/geodesic_area.rs b/src/algorithm/geo/geodesic_area.rs new file mode 100644 index 000000000..2d314430b --- /dev/null +++ b/src/algorithm/geo/geodesic_area.rs @@ -0,0 +1,122 @@ +use crate::error::Result; +use crate::{GeometryArray, GeometryArrayTrait}; +use arrow2::array::{MutablePrimitiveArray, PrimitiveArray}; +use geo::prelude::GeodesicArea; + +/// Calculate the unsigned geodesic area of a geometry on an ellipsoid using the algorithm +/// presented in Algorithms for geodesics by Charles Karney (2013) +pub fn geodesic_area_unsigned(array: GeometryArray) -> Result> { + let mut output_array = MutablePrimitiveArray::::with_capacity(array.len()); + + match array { + GeometryArray::WKB(arr) => { + arr.iter_geo() + .for_each(|maybe_g| output_array.push(maybe_g.map(|g| g.geodesic_area_unsigned()))); + } + GeometryArray::Point(arr) => { + arr.iter_geo() + .for_each(|maybe_g| output_array.push(maybe_g.map(|g| g.geodesic_area_unsigned()))); + } + GeometryArray::LineString(arr) => { + arr.iter_geo() + .for_each(|maybe_g| output_array.push(maybe_g.map(|g| g.geodesic_area_unsigned()))); + } + GeometryArray::Polygon(arr) => { + arr.iter_geo() + .for_each(|maybe_g| output_array.push(maybe_g.map(|g| g.geodesic_area_unsigned()))); + } + GeometryArray::MultiPoint(arr) => { + arr.iter_geo() + .for_each(|maybe_g| output_array.push(maybe_g.map(|g| g.geodesic_area_unsigned()))); + } + GeometryArray::MultiLineString(arr) => { + arr.iter_geo() + .for_each(|maybe_g| output_array.push(maybe_g.map(|g| g.geodesic_area_unsigned()))); + } + GeometryArray::MultiPolygon(arr) => { + arr.iter_geo() + .for_each(|maybe_g| output_array.push(maybe_g.map(|g| g.geodesic_area_unsigned()))); + } + } + + Ok(output_array.into()) +} + +/// Calculate the signed geodesic area of a geometry on an ellipsoid using the algorithm +/// presented in Algorithms for geodesics by Charles Karney (2013) +pub fn geodesic_area_signed(array: GeometryArray) -> Result> { + let mut output_array = MutablePrimitiveArray::::with_capacity(array.len()); + + match array { + GeometryArray::WKB(arr) => { + arr.iter_geo() + .for_each(|maybe_g| output_array.push(maybe_g.map(|g| g.geodesic_area_signed()))); + } + GeometryArray::Point(arr) => { + arr.iter_geo() + .for_each(|maybe_g| output_array.push(maybe_g.map(|g| g.geodesic_area_signed()))); + } + GeometryArray::LineString(arr) => { + arr.iter_geo() + .for_each(|maybe_g| output_array.push(maybe_g.map(|g| g.geodesic_area_signed()))); + } + GeometryArray::Polygon(arr) => { + arr.iter_geo() + .for_each(|maybe_g| output_array.push(maybe_g.map(|g| g.geodesic_area_signed()))); + } + GeometryArray::MultiPoint(arr) => { + arr.iter_geo() + .for_each(|maybe_g| output_array.push(maybe_g.map(|g| g.geodesic_area_signed()))); + } + GeometryArray::MultiLineString(arr) => { + arr.iter_geo() + .for_each(|maybe_g| output_array.push(maybe_g.map(|g| g.geodesic_area_signed()))); + } + GeometryArray::MultiPolygon(arr) => { + arr.iter_geo() + .for_each(|maybe_g| output_array.push(maybe_g.map(|g| g.geodesic_area_signed()))); + } + } + + Ok(output_array.into()) +} + +/// Determine the perimeter of a geometry on an ellipsoidal model of the earth. +/// +/// This uses the geodesic measurement methods given by Karney (2013). +pub fn geodesic_perimeter(array: GeometryArray) -> Result> { + let mut output_array = MutablePrimitiveArray::::with_capacity(array.len()); + + match array { + GeometryArray::WKB(arr) => { + arr.iter_geo() + .for_each(|maybe_g| output_array.push(maybe_g.map(|g| g.geodesic_perimeter()))); + } + GeometryArray::Point(arr) => { + arr.iter_geo() + .for_each(|maybe_g| output_array.push(maybe_g.map(|g| g.geodesic_perimeter()))); + } + GeometryArray::LineString(arr) => { + arr.iter_geo() + .for_each(|maybe_g| output_array.push(maybe_g.map(|g| g.geodesic_perimeter()))); + } + GeometryArray::Polygon(arr) => { + arr.iter_geo() + .for_each(|maybe_g| output_array.push(maybe_g.map(|g| g.geodesic_perimeter()))); + } + GeometryArray::MultiPoint(arr) => { + arr.iter_geo() + .for_each(|maybe_g| output_array.push(maybe_g.map(|g| g.geodesic_perimeter()))); + } + GeometryArray::MultiLineString(arr) => { + arr.iter_geo() + .for_each(|maybe_g| output_array.push(maybe_g.map(|g| g.geodesic_perimeter()))); + } + GeometryArray::MultiPolygon(arr) => { + arr.iter_geo() + .for_each(|maybe_g| output_array.push(maybe_g.map(|g| g.geodesic_perimeter()))); + } + } + + Ok(output_array.into()) +} diff --git a/src/algorithm/geo/mod.rs b/src/algorithm/geo/mod.rs index 67f04fb87..f1d7c285b 100644 --- a/src/algorithm/geo/mod.rs +++ b/src/algorithm/geo/mod.rs @@ -2,8 +2,10 @@ mod area; mod centroid; +mod chamberlain_duquette_area; mod convex_hull; mod envelope; +mod geodesic_area; mod is_empty; mod length; mod simplify; @@ -11,8 +13,12 @@ mod simplify; pub use area::area; pub use area::signed_area; pub use centroid::centroid; +pub use chamberlain_duquette_area::{ + chamberlain_duquette_signed_area, chamberlain_duquette_unsigned_area, +}; pub use convex_hull::convex_hull; pub use envelope::envelope; +pub use geodesic_area::{geodesic_area_signed, geodesic_area_unsigned, geodesic_perimeter}; pub use is_empty::is_empty; pub use length::{euclidean_length, geodesic_length, haversine_length, vincenty_length}; pub use simplify::simplify;