Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement conversions from GEOS objects to GeoArrow arrays #202

Merged
merged 11 commits into from
Sep 30, 2023
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,10 @@ name = "gdal"
test = false
required-features = ["gdal"]

[[bench]]
name = "geos_buffer"
harness = false

[[bench]]
name = "nybb"
harness = false
Expand Down
22 changes: 22 additions & 0 deletions benches/geos_buffer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
use criterion::{criterion_group, criterion_main, Criterion};
use geoarrow2::algorithm::geos::buffer::Buffer;
use geoarrow2::array::{CoordBuffer, InterleavedCoordBuffer, PointArray};

fn generate_data() -> PointArray {
let coords = vec![0.0; 100_000];
let coord_buffer = CoordBuffer::Interleaved(InterleavedCoordBuffer::new(coords.into()));
PointArray::new(coord_buffer, None)
}

pub fn criterion_benchmark(c: &mut Criterion) {
let point_array = generate_data();

c.bench_function("buffer", |b| {
b.iter(|| {
let _buffered = point_array.buffer(1.0, 8).unwrap();
})
});
}

criterion_group!(benches, criterion_benchmark);
criterion_main!(benches);
4 changes: 2 additions & 2 deletions src/algorithm/geo/area.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,11 +107,11 @@ mod test {
use arrow2::array::Float64Array;

use super::*;
use crate::test::polygon::polygon_arr;
use crate::test::polygon::p_array;

#[test]
fn tmp() {
let arr = polygon_arr();
let arr = p_array();
let area = arr.unsigned_area();
assert_eq!(area, Float64Array::from_vec(vec![28., 18.]));
}
Expand Down
4 changes: 2 additions & 2 deletions src/algorithm/geos/area.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,11 +72,11 @@ mod test {
use arrow2::array::Float64Array;

use super::*;
use crate::test::polygon::polygon_arr;
use crate::test::polygon::p_array;

#[test]
fn tmp() {
let arr = polygon_arr();
let arr = p_array();
let area = arr.area().unwrap();
assert_eq!(area, Float64Array::from_vec(vec![28., 18.]));
}
Expand Down
88 changes: 88 additions & 0 deletions src/algorithm/geos/buffer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
use crate::array::{PointArray, PolygonArray};
use crate::error::Result;
use crate::GeometryArrayTrait;
use geos::Geom;

pub trait Buffer {
type Output;

fn buffer(&self, width: f64, quadsegs: i32) -> Result<Self::Output>;
}

impl Buffer for PointArray {
type Output = PolygonArray<i32>;

fn buffer(&self, width: f64, quadsegs: i32) -> Result<Self::Output> {
// NOTE: the bumpalo allocator didn't appear to make any perf difference with geos :shrug:
// Presumably GEOS is allocating on its own before we can put the geometry in the Bump?
let bump = bumpalo::Bump::new();

let mut geos_geoms = bumpalo::collections::Vec::with_capacity_in(self.len(), &bump);

for maybe_g in self.iter_geos() {
if let Some(g) = maybe_g {
let area = g.buffer(width, quadsegs)?;
geos_geoms.push(Some(area));
} else {
geos_geoms.push(None);
}
}

let polygon_array: PolygonArray<i32> = geos_geoms.try_into()?;
Ok(polygon_array)
}
}

// // Note: this can't (easily) be parameterized in the macro because PointArray is not generic over O
// impl Area for PointArray {
// fn area(&self) -> Result<PrimitiveArray<f64>> {
// Ok(zeroes(self.len(), self.validity()))
// }
// }

// /// Implementation where the result is zero.
// macro_rules! zero_impl {
// ($type:ty) => {
// impl<O: Offset> Area for $type {
// fn area(&self) -> Result<PrimitiveArray<f64>> {
// Ok(zeroes(self.len(), self.validity()))
// }
// }
// };
// }

// zero_impl!(LineStringArray<O>);
// zero_impl!(MultiPointArray<O>);
// zero_impl!(MultiLineStringArray<O>);

// macro_rules! iter_geos_impl {
// ($type:ty) => {
// impl<O: Offset> Area for $type {
// fn area(&self) -> Result<PrimitiveArray<f64>> {
// }
// }
// };
// }

// iter_geos_impl!(PolygonArray<O>);
// iter_geos_impl!(MultiPolygonArray<O>);
// iter_geos_impl!(WKBArray<O>);

// impl<O: Offset> Area for GeometryArray<O> {
// crate::geometry_array_delegate_impl! {
// fn area(&self) -> Result<PrimitiveArray<f64>>;
// }
// }

#[cfg(test)]
mod test {
use super::*;
use crate::test::point::point_array;

#[test]
fn point_buffer() {
let arr = point_array();
let buffered = arr.buffer(1., 8).unwrap();
dbg!(buffered);
}
}
1 change: 1 addition & 0 deletions src/algorithm/geos/mod.rs
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
pub mod area;
pub mod buffer;
2 changes: 1 addition & 1 deletion src/array/linestring/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@ pub use mutable::MutableLineStringArray;

mod array;
pub mod iterator;
mod mutable;
pub(crate) mod mutable;
31 changes: 4 additions & 27 deletions src/array/linestring/mutable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -226,8 +226,8 @@ impl<O: Offset> From<MutableLineStringArray<O>> for ListArray<O> {
}
}

fn first_pass<'a>(
geoms: impl Iterator<Item = Option<impl LineStringTrait<'a> + 'a>>,
pub(crate) fn first_pass<'a>(
geoms: impl Iterator<Item = Option<impl LineStringTrait<'a>>>,
geoms_length: usize,
) -> (usize, usize) {
let mut coord_capacity = 0;
Expand All @@ -240,8 +240,8 @@ fn first_pass<'a>(
(coord_capacity, geom_capacity)
}

fn second_pass<'a, O: Offset>(
geoms: impl Iterator<Item = Option<impl LineStringTrait<'a, T = f64> + 'a>>,
pub(crate) fn second_pass<'a, O: Offset>(
geoms: impl Iterator<Item = Option<impl LineStringTrait<'a, T = f64>>>,
coord_capacity: usize,
geom_capacity: usize,
) -> MutableLineStringArray<O> {
Expand Down Expand Up @@ -310,29 +310,6 @@ impl<O: Offset> TryFrom<WKBArray<O>> for MutableLineStringArray<O> {
}
}

// #[cfg(feature = "geos")]
// impl<O: Offset> TryFrom<Vec<Option<geos::Geometry<'_>>>> for MutableLineStringArray<O> {
// type Error = GeoArrowError;
// fn try_from(value: Vec<Option<geos::Geometry>>) -> std::result::Result<Self, Self::Error> {
// let length = value.len();
// let geos_linestring_objects: Vec<Option<GEOSLineString>> = value
// .iter()
// .map(|geom| {
// geom.map(|geom| GEOSLineString::new_unchecked(std::borrow::Cow::Owned(geom)))
// })
// .collect();
// let (coord_capacity, geom_capacity) = first_pass(
// geos_linestring_objects.iter().map(|item| item.as_ref()),
// length,
// );
// Ok(second_pass(
// geos_linestring_objects.iter().map(|item| item.as_ref()),
// coord_capacity,
// geom_capacity,
// ))
// }
// }

/// LineString and MultiPoint have the same layout, so enable conversions between the two to change
/// the semantic type
impl<O: Offset> From<MutableLineStringArray<O>> for MutableMultiPointArray<O> {
Expand Down
10 changes: 5 additions & 5 deletions src/array/multilinestring/mutable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,16 @@ use arrow2::types::Offset;

#[derive(Debug, Clone)]
pub struct MutableMultiLineStringArray<O: Offset> {
coords: MutableCoordBuffer,
pub(crate) coords: MutableCoordBuffer,

/// Offsets into the ring array where each geometry starts
geom_offsets: Offsets<O>,
pub(crate) geom_offsets: Offsets<O>,

/// Offsets into the coordinate array where each ring starts
ring_offsets: Offsets<O>,
pub(crate) ring_offsets: Offsets<O>,

/// Validity is only defined at the geometry level
validity: Option<MutableBitmap>,
pub(crate) validity: Option<MutableBitmap>,
}

pub type MultiLineStringInner<O> = (
Expand Down Expand Up @@ -268,7 +268,7 @@ impl<'a, O: Offset> MutableMultiLineStringArray<O> {
}

#[inline]
fn push_null(&mut self) {
pub(crate) fn push_null(&mut self) {
// NOTE! Only the geom_offsets array needs to get extended, because the next geometry will
// point to the same ring array location
self.geom_offsets.extend_constant(1);
Expand Down
2 changes: 1 addition & 1 deletion src/array/multipoint/mutable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ impl<'a, O: Offset> MutableMultiPointArray<O> {
}

#[inline]
fn push_null(&mut self) {
pub(crate) fn push_null(&mut self) {
self.geom_offsets.extend_constant(1);
match &mut self.validity {
Some(validity) => validity.push(false),
Expand Down
14 changes: 7 additions & 7 deletions src/array/multipolygon/mutable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,19 +24,19 @@ pub type MutableMultiPolygonParts<O> = (
/// Converting a [`MutableMultiPolygonArray`] into a [`MultiPolygonArray`] is `O(1)`.
#[derive(Debug, Clone)]
pub struct MutableMultiPolygonArray<O: Offset> {
coords: MutableCoordBuffer,
pub(crate) coords: MutableCoordBuffer,

/// Offsets into the polygon array where each geometry starts
geom_offsets: Offsets<O>,
pub(crate) geom_offsets: Offsets<O>,

/// Offsets into the ring array where each polygon starts
polygon_offsets: Offsets<O>,
pub(crate) polygon_offsets: Offsets<O>,

/// Offsets into the coordinate array where each ring starts
ring_offsets: Offsets<O>,
pub(crate) ring_offsets: Offsets<O>,

/// Validity is only defined at the geometry level
validity: Option<MutableBitmap>,
pub(crate) validity: Option<MutableBitmap>,
}

impl<'a, O: Offset> MutableMultiPolygonArray<O> {
Expand Down Expand Up @@ -319,15 +319,15 @@ impl<'a, O: Offset> MutableMultiPolygonArray<O> {
}

#[inline]
fn push_empty(&mut self) {
pub(crate) fn push_empty(&mut self) {
self.geom_offsets.try_push_usize(0).unwrap();
if let Some(validity) = &mut self.validity {
validity.push(true)
}
}

#[inline]
fn push_null(&mut self) {
pub(crate) fn push_null(&mut self) {
// NOTE! Only the geom_offsets array needs to get extended, because the next geometry will
// point to the same polygon array location
// Note that we don't use self.try_push_geom_offset because that sets validity to true
Expand Down
2 changes: 1 addition & 1 deletion src/array/point/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@ pub use mutable::MutablePointArray;

mod array;
pub mod iterator;
mod mutable;
pub(crate) mod mutable;
2 changes: 1 addition & 1 deletion src/array/point/mutable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ fn from_coords(
mutable_array
}

fn from_nullable_coords(
pub(crate) fn from_nullable_coords(
geoms: impl Iterator<Item = Option<impl PointTrait<T = f64>>>,
geoms_length: usize,
) -> MutablePointArray {
Expand Down
14 changes: 7 additions & 7 deletions src/array/polygon/mutable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,16 @@ pub type MutablePolygonParts<O> = (
/// Converting a [`MutablePolygonArray`] into a [`PolygonArray`] is `O(1)`.
#[derive(Debug, Clone)]
pub struct MutablePolygonArray<O: Offset> {
coords: MutableCoordBuffer,
pub(crate) coords: MutableCoordBuffer,

/// Offsets into the ring array where each geometry starts
geom_offsets: Offsets<O>,
pub(crate) geom_offsets: Offsets<O>,

/// Offsets into the coordinate array where each ring starts
ring_offsets: Offsets<O>,
pub(crate) ring_offsets: Offsets<O>,

/// Validity is only defined at the geometry level
validity: Option<MutableBitmap>,
pub(crate) validity: Option<MutableBitmap>,
}

impl<'a, O: Offset> MutablePolygonArray<O> {
Expand Down Expand Up @@ -184,7 +184,7 @@ impl<'a, O: Offset> MutablePolygonArray<O> {
// - Get ring
// - Add ring's # of coords to self.ring_offsets
// - Push ring's coords to self.coords
for int_ring_idx in 0..polygon.num_interiors() {
for int_ring_idx in 0..num_interiors {
let int_ring = polygon.interior(int_ring_idx).unwrap();
let int_ring_num_coords = int_ring.num_coords();
self.ring_offsets.try_push_usize(int_ring_num_coords)?;
Expand Down Expand Up @@ -241,15 +241,15 @@ impl<'a, O: Offset> MutablePolygonArray<O> {
}

#[inline]
fn push_empty(&mut self) {
pub(crate) fn push_empty(&mut self) {
self.geom_offsets.try_push_usize(0).unwrap();
if let Some(validity) = &mut self.validity {
validity.push(true)
}
}

#[inline]
fn push_null(&mut self) {
pub(crate) fn push_null(&mut self) {
// NOTE! Only the geom_offsets array needs to get extended, because the next geometry will
// point to the same ring array location
self.geom_offsets.extend_constant(1);
Expand Down
Loading