Skip to content

Commit

Permalink
Add in geo algs from geopolars/geopolars (#6)
Browse files Browse the repository at this point in the history
* area, centroid

* envelope, is_empty, length, simplify

* convex hull

* fmt
  • Loading branch information
kylebarron authored Jul 1, 2023
1 parent 5876175 commit 1d80ac4
Show file tree
Hide file tree
Showing 10 changed files with 969 additions and 0 deletions.
78 changes: 78 additions & 0 deletions src/algorithm/geo/area.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
use crate::error::Result;
use crate::{GeometryArray, GeometryArrayTrait};
use arrow2::array::{MutablePrimitiveArray, PrimitiveArray};
use geo::prelude::Area;

pub fn area(array: GeometryArray) -> Result<PrimitiveArray<f64>> {
let mut output_array = MutablePrimitiveArray::<f64>::with_capacity(array.len());

match array {
GeometryArray::WKB(arr) => {
arr.iter_geo()
.for_each(|maybe_g| output_array.push(maybe_g.map(|g| g.unsigned_area())));
}
GeometryArray::Point(arr) => {
arr.iter_geo()
.for_each(|maybe_g| output_array.push(maybe_g.map(|g| g.unsigned_area())));
}
GeometryArray::LineString(arr) => {
arr.iter_geo()
.for_each(|maybe_g| output_array.push(maybe_g.map(|g| g.unsigned_area())));
}
GeometryArray::Polygon(arr) => {
arr.iter_geo()
.for_each(|maybe_g| output_array.push(maybe_g.map(|g| g.unsigned_area())));
}
GeometryArray::MultiPoint(arr) => {
arr.iter_geo()
.for_each(|maybe_g| output_array.push(maybe_g.map(|g| g.unsigned_area())));
}
GeometryArray::MultiLineString(arr) => {
arr.iter_geo()
.for_each(|maybe_g| output_array.push(maybe_g.map(|g| g.unsigned_area())));
}
GeometryArray::MultiPolygon(arr) => {
arr.iter_geo()
.for_each(|maybe_g| output_array.push(maybe_g.map(|g| g.unsigned_area())));
}
}

Ok(output_array.into())
}

pub fn signed_area(array: GeometryArray) -> Result<PrimitiveArray<f64>> {
let mut output_array = MutablePrimitiveArray::<f64>::with_capacity(array.len());

match array {
GeometryArray::WKB(arr) => {
arr.iter_geo()
.for_each(|maybe_g| output_array.push(maybe_g.map(|g| g.signed_area())));
}
GeometryArray::Point(arr) => {
arr.iter_geo()
.for_each(|maybe_g| output_array.push(maybe_g.map(|g| g.signed_area())));
}
GeometryArray::LineString(arr) => {
arr.iter_geo()
.for_each(|maybe_g| output_array.push(maybe_g.map(|g| g.signed_area())));
}
GeometryArray::Polygon(arr) => {
arr.iter_geo()
.for_each(|maybe_g| output_array.push(maybe_g.map(|g| g.signed_area())));
}
GeometryArray::MultiPoint(arr) => {
arr.iter_geo()
.for_each(|maybe_g| output_array.push(maybe_g.map(|g| g.signed_area())));
}
GeometryArray::MultiLineString(arr) => {
arr.iter_geo()
.for_each(|maybe_g| output_array.push(maybe_g.map(|g| g.signed_area())));
}
GeometryArray::MultiPolygon(arr) => {
arr.iter_geo()
.for_each(|maybe_g| output_array.push(maybe_g.map(|g| g.signed_area())));
}
}

Ok(output_array.into())
}
41 changes: 41 additions & 0 deletions src/algorithm/geo/centroid.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
use crate::error::Result;
use crate::MutablePointArray;
use crate::{GeometryArray, GeometryArrayTrait, PointArray};
use geo::algorithm::centroid::Centroid;

pub fn centroid(array: GeometryArray) -> Result<PointArray> {
let mut output_array = MutablePointArray::with_capacity(array.len());

match array {
GeometryArray::WKB(arr) => {
arr.iter_geo()
.for_each(|maybe_g| output_array.push_geo(maybe_g.and_then(|g| g.centroid())));
}
GeometryArray::Point(arr) => {
arr.iter_geo()
.for_each(|maybe_g| output_array.push_geo(maybe_g.map(|g| g.centroid())));
}
GeometryArray::LineString(arr) => {
arr.iter_geo()
.for_each(|maybe_g| output_array.push_geo(maybe_g.and_then(|g| g.centroid())));
}
GeometryArray::Polygon(arr) => {
arr.iter_geo()
.for_each(|maybe_g| output_array.push_geo(maybe_g.and_then(|g| g.centroid())));
}
GeometryArray::MultiPoint(arr) => {
arr.iter_geo()
.for_each(|maybe_g| output_array.push_geo(maybe_g.and_then(|g| g.centroid())));
}
GeometryArray::MultiLineString(arr) => {
arr.iter_geo()
.for_each(|maybe_g| output_array.push_geo(maybe_g.and_then(|g| g.centroid())));
}
GeometryArray::MultiPolygon(arr) => {
arr.iter_geo()
.for_each(|maybe_g| output_array.push_geo(maybe_g.and_then(|g| g.centroid())));
}
}

Ok(output_array.into())
}
136 changes: 136 additions & 0 deletions src/algorithm/geo/convex_hull.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
use crate::error::Result;
use crate::GeometryArray;
use geo::algorithm::convex_hull::ConvexHull;
use geo::Polygon;

pub fn convex_hull(array: GeometryArray) -> Result<GeometryArray> {
match array {
GeometryArray::WKB(arr) => {
let output_geoms: Vec<Option<Polygon>> = arr
.iter_geo()
.map(|maybe_g| maybe_g.map(|geom| geom.convex_hull()))
.collect();

Ok(GeometryArray::Polygon(output_geoms.into()))
}
GeometryArray::Point(arr) => {
let output_geoms: Vec<Option<Polygon>> = arr
.iter_geo()
.map(|maybe_g| maybe_g.map(|geom| geom.convex_hull()))
.collect();

Ok(GeometryArray::Polygon(output_geoms.into()))
}

GeometryArray::MultiPoint(arr) => {
let output_geoms: Vec<Option<Polygon>> = arr
.iter_geo()
.map(|maybe_g| maybe_g.map(|geom| geom.convex_hull()))
.collect();

Ok(GeometryArray::Polygon(output_geoms.into()))
}
GeometryArray::LineString(arr) => {
let output_geoms: Vec<Option<Polygon>> = arr
.iter_geo()
.map(|maybe_g| maybe_g.map(|geom| geom.convex_hull()))
.collect();

Ok(GeometryArray::Polygon(output_geoms.into()))
}
GeometryArray::MultiLineString(arr) => {
let output_geoms: Vec<Option<Polygon>> = arr
.iter_geo()
.map(|maybe_g| maybe_g.map(|geom| geom.convex_hull()))
.collect();

Ok(GeometryArray::Polygon(output_geoms.into()))
}
GeometryArray::Polygon(arr) => {
let output_geoms: Vec<Option<Polygon>> = arr
.iter_geo()
.map(|maybe_g| maybe_g.map(|geom| geom.convex_hull()))
.collect();

Ok(GeometryArray::Polygon(output_geoms.into()))
}
GeometryArray::MultiPolygon(arr) => {
let output_geoms: Vec<Option<Polygon>> = arr
.iter_geo()
.map(|maybe_g| maybe_g.map(|geom| geom.convex_hull()))
.collect();

Ok(GeometryArray::Polygon(output_geoms.into()))
}
}
}

#[cfg(test)]
mod tests {
use super::convex_hull;
use crate::{GeometryArray, GeometryArrayTrait, LineStringArray, MultiPointArray};
use geo::{line_string, polygon, Geometry, MultiPoint, Point};

#[test]
fn convex_hull_for_multipoint() {
// Values borrowed from this test in geo crate: https://docs.rs/geo/0.14.2/src/geo/algorithm/convexhull.rs.html#323
let input_geom: MultiPoint = vec![
Point::new(0.0, 10.0),
Point::new(1.0, 1.0),
Point::new(10.0, 0.0),
Point::new(1.0, -1.0),
Point::new(0.0, -10.0),
Point::new(-1.0, -1.0),
Point::new(-10.0, 0.0),
Point::new(-1.0, 1.0),
Point::new(0.0, 10.0),
]
.into();
let input_array: MultiPointArray = vec![input_geom].into();
let result_array = convex_hull(GeometryArray::MultiPoint(input_array)).unwrap();

let expected = polygon![
(x:0.0, y: -10.0),
(x:10.0, y: 0.0),
(x:0.0, y:10.0),
(x:-10.0, y:0.0),
(x:0.0, y:-10.0),
];

assert_eq!(
Geometry::Polygon(expected),
result_array.get_as_geo(0).unwrap()
);
}

#[test]
fn convex_hull_linestring_test() {
let input_geom = line_string![
(x: 0.0, y: 10.0),
(x: 1.0, y: 1.0),
(x: 10.0, y: 0.0),
(x: 1.0, y: -1.0),
(x: 0.0, y: -10.0),
(x: -1.0, y: -1.0),
(x: -10.0, y: 0.0),
(x: -1.0, y: 1.0),
(x: 0.0, y: 10.0),
];

let input_array: LineStringArray = vec![input_geom].into();
let result_array = convex_hull(GeometryArray::LineString(input_array)).unwrap();

let expected = polygon![
(x: 0.0, y: -10.0),
(x: 10.0, y: 0.0),
(x: 0.0, y: 10.0),
(x: -10.0, y: 0.0),
(x: 0.0, y: -10.0),
];

assert_eq!(
Geometry::Polygon(expected),
result_array.get_as_geo(0).unwrap()
);
}
}
70 changes: 70 additions & 0 deletions src/algorithm/geo/envelope.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
use crate::error::Result;
use crate::GeometryArray;
use geo::algorithm::bounding_rect::BoundingRect;
use geo::Polygon;

pub fn envelope(array: GeometryArray) -> Result<GeometryArray> {
match array {
GeometryArray::WKB(arr) => {
let output_geoms: Vec<Option<Polygon>> = arr
.iter_geo()
.map(|maybe_g| {
maybe_g.and_then(|geom| geom.bounding_rect().map(|rect| rect.to_polygon()))
})
.collect();

Ok(GeometryArray::Polygon(output_geoms.into()))
}
GeometryArray::Point(arr) => Ok(GeometryArray::Point(arr)),
GeometryArray::MultiPoint(arr) => {
let output_geoms: Vec<Option<Polygon>> = arr
.iter_geo()
.map(|maybe_g| {
maybe_g.and_then(|geom| geom.bounding_rect().map(|rect| rect.to_polygon()))
})
.collect();

Ok(GeometryArray::Polygon(output_geoms.into()))
}
GeometryArray::LineString(arr) => {
let output_geoms: Vec<Option<Polygon>> = arr
.iter_geo()
.map(|maybe_g| {
maybe_g.and_then(|geom| geom.bounding_rect().map(|rect| rect.to_polygon()))
})
.collect();

Ok(GeometryArray::Polygon(output_geoms.into()))
}
GeometryArray::MultiLineString(arr) => {
let output_geoms: Vec<Option<Polygon>> = arr
.iter_geo()
.map(|maybe_g| {
maybe_g.and_then(|geom| geom.bounding_rect().map(|rect| rect.to_polygon()))
})
.collect();

Ok(GeometryArray::Polygon(output_geoms.into()))
}
GeometryArray::Polygon(arr) => {
let output_geoms: Vec<Option<Polygon>> = arr
.iter_geo()
.map(|maybe_g| {
maybe_g.and_then(|geom| geom.bounding_rect().map(|rect| rect.to_polygon()))
})
.collect();

Ok(GeometryArray::Polygon(output_geoms.into()))
}
GeometryArray::MultiPolygon(arr) => {
let output_geoms: Vec<Option<Polygon>> = arr
.iter_geo()
.map(|maybe_g| {
maybe_g.and_then(|geom| geom.bounding_rect().map(|rect| rect.to_polygon()))
})
.collect();

Ok(GeometryArray::Polygon(output_geoms.into()))
}
}
}
41 changes: 41 additions & 0 deletions src/algorithm/geo/is_empty.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
use crate::error::Result;
use crate::{GeometryArray, GeometryArrayTrait};
use arrow2::array::{BooleanArray, MutableBooleanArray};
use geo::dimensions::HasDimensions;

pub fn is_empty(array: GeometryArray) -> Result<BooleanArray> {
let mut output_array = MutableBooleanArray::with_capacity(array.len());

match array {
GeometryArray::WKB(arr) => {
arr.iter_geo()
.for_each(|maybe_g| output_array.push(maybe_g.map(|g| g.is_empty())));
}
GeometryArray::Point(arr) => {
arr.iter_geo()
.for_each(|maybe_g| output_array.push(maybe_g.map(|g| g.is_empty())));
}
GeometryArray::LineString(arr) => {
arr.iter_geo()
.for_each(|maybe_g| output_array.push(maybe_g.map(|g| g.is_empty())));
}
GeometryArray::Polygon(arr) => {
arr.iter_geo()
.for_each(|maybe_g| output_array.push(maybe_g.map(|g| g.is_empty())));
}
GeometryArray::MultiPoint(arr) => {
arr.iter_geo()
.for_each(|maybe_g| output_array.push(maybe_g.map(|g| g.is_empty())));
}
GeometryArray::MultiLineString(arr) => {
arr.iter_geo()
.for_each(|maybe_g| output_array.push(maybe_g.map(|g| g.is_empty())));
}
GeometryArray::MultiPolygon(arr) => {
arr.iter_geo()
.for_each(|maybe_g| output_array.push(maybe_g.map(|g| g.is_empty())));
}
}

Ok(output_array.into())
}
Loading

0 comments on commit 1d80ac4

Please sign in to comment.