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

Python constructors from raw buffers #578

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 69 additions & 0 deletions python/core/python/geoarrow/rust/core/_rust.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ from pathlib import Path
from typing import (
BinaryIO,
Dict,
Iterable,
List,
Optional,
Self,
Expand Down Expand Up @@ -42,6 +43,18 @@ from .types import (
)
from .enums import AreaMethod, LengthMethod, SimplifyMethod

class CoordBuffer:
@classmethod
def from_interleaved(
cls, coords: NDArray[np.float64] | ArrowArrayExportable
) -> Self: ...
@classmethod
def from_separated(
cls,
x: NDArray[np.float64] | ArrowArrayExportable,
y: NDArray[np.float64] | ArrowArrayExportable,
) -> Self: ...

class Point:
def __arrow_c_array__(
self, requested_schema: object | None = None
Expand Down Expand Up @@ -154,6 +167,8 @@ class PointArray:
def centroid(self) -> PointArray: ...
def convex_hull(self) -> PolygonArray: ...
@classmethod
def from_parts(cls, coords: CoordBuffer) -> Self: ...
@classmethod
def from_xy(
cls,
x: ArrowArrayExportable | NumpyArrayProtocolf64,
Expand Down Expand Up @@ -203,6 +218,10 @@ class LineStringArray:
def convex_hull(self) -> PolygonArray: ...
def densify(self, max_distance: float) -> Self: ...
def frechet_distance(self, other: BroadcastGeometry) -> Float64Array: ...
@classmethod
def from_parts(
cls, coords: CoordBuffer, geom_offsets: ArrowArrayExportable | NDArray[np.int32]
) -> Self: ...
def geodesic_perimeter(self) -> Float64Array: ...
def is_empty(self) -> BooleanArray: ...
def length(
Expand Down Expand Up @@ -252,6 +271,13 @@ class PolygonArray:
def chaikin_smoothing(self, n_iterations: int) -> Self: ...
def convex_hull(self) -> PolygonArray: ...
def densify(self, max_distance: float) -> Self: ...
@classmethod
def from_parts(
cls,
coords: CoordBuffer,
geom_offsets: ArrowArrayExportable | NDArray[np.int32],
ring_offsets: ArrowArrayExportable | NDArray[np.int32],
) -> Self: ...
def geodesic_perimeter(self) -> Float64Array: ...
def polylabel(self, tolerance: float) -> PointArray: ...
def is_empty(self) -> BooleanArray: ...
Expand Down Expand Up @@ -291,6 +317,12 @@ class MultiPointArray:
def center(self) -> PointArray: ...
def centroid(self) -> PointArray: ...
def convex_hull(self) -> PolygonArray: ...
@classmethod
def from_parts(
cls,
coords: CoordBuffer,
geom_offsets: ArrowArrayExportable | NDArray[np.int32],
) -> Self: ...
def geodesic_perimeter(self) -> Float64Array: ...
def is_empty(self) -> BooleanArray: ...
def length(
Expand Down Expand Up @@ -334,6 +366,13 @@ class MultiLineStringArray:
def chaikin_smoothing(self, n_iterations: int) -> Self: ...
def convex_hull(self) -> PolygonArray: ...
def densify(self, max_distance: float) -> Self: ...
@classmethod
def from_parts(
cls,
coords: CoordBuffer,
geom_offsets: ArrowArrayExportable | NDArray[np.int32],
ring_offsets: ArrowArrayExportable | NDArray[np.int32],
) -> Self: ...
def geodesic_perimeter(self) -> Float64Array: ...
def is_empty(self) -> BooleanArray: ...
def length(
Expand Down Expand Up @@ -377,6 +416,14 @@ class MultiPolygonArray:
def chaikin_smoothing(self, n_iterations: int) -> Self: ...
def convex_hull(self) -> PolygonArray: ...
def densify(self, max_distance: float) -> Self: ...
@classmethod
def from_parts(
cls,
coords: CoordBuffer,
geom_offsets: ArrowArrayExportable | NDArray[np.int32],
polygon_offsets: ArrowArrayExportable | NDArray[np.int32],
ring_offsets: ArrowArrayExportable | NDArray[np.int32],
) -> Self: ...
def geodesic_perimeter(self) -> Float64Array: ...
def is_empty(self) -> BooleanArray: ...
def simplify(
Expand Down Expand Up @@ -642,6 +689,7 @@ class ChunkedPointArray:
def __eq__(self, other: Self) -> bool: ...
def __getitem__(self, key: int) -> Point: ...
def __len__(self) -> int: ...
def __new__(cls, chunks: Iterable[PointArray | ArrowArrayExportable]) -> Self: ...
def __repr__(self) -> str: ...
def area(
self, *, method: AreaMethod | AreaMethodT = AreaMethod.Euclidean
Expand Down Expand Up @@ -680,6 +728,9 @@ class ChunkedLineStringArray:
def __eq__(self, other: Self) -> bool: ...
def __getitem__(self, key: int) -> LineString: ...
def __len__(self) -> int: ...
def __new__(
cls, chunks: Iterable[LineStringArray | ArrowArrayExportable]
) -> Self: ...
def __repr__(self) -> str: ...
def area(
self, *, method: AreaMethod | AreaMethodT = AreaMethod.Euclidean
Expand Down Expand Up @@ -727,6 +778,7 @@ class ChunkedPolygonArray:
def __eq__(self, other: Self) -> bool: ...
def __getitem__(self, key: int) -> Polygon: ...
def __len__(self) -> int: ...
def __new__(cls, chunks: Iterable[PolygonArray | ArrowArrayExportable]) -> Self: ...
def __repr__(self) -> str: ...
def area(
self, *, method: AreaMethod | AreaMethodT = AreaMethod.Euclidean
Expand Down Expand Up @@ -765,6 +817,9 @@ class ChunkedMultiPointArray:
def __eq__(self, other: Self) -> bool: ...
def __getitem__(self, key: int) -> MultiPoint: ...
def __len__(self) -> int: ...
def __new__(
cls, chunks: Iterable[MultiPointArray | ArrowArrayExportable]
) -> Self: ...
def __repr__(self) -> str: ...
def area(
self, *, method: AreaMethod | AreaMethodT = AreaMethod.Euclidean
Expand Down Expand Up @@ -803,6 +858,9 @@ class ChunkedMultiLineStringArray:
def __eq__(self, other: Self) -> bool: ...
def __getitem__(self, key: int) -> MultiLineString: ...
def __len__(self) -> int: ...
def __new__(
cls, chunks: Iterable[MultiLineStringArray | ArrowArrayExportable]
) -> Self: ...
def __repr__(self) -> str: ...
def area(
self, *, method: AreaMethod | AreaMethodT = AreaMethod.Euclidean
Expand Down Expand Up @@ -843,6 +901,9 @@ class ChunkedMultiPolygonArray:
def __eq__(self, other: Self) -> bool: ...
def __getitem__(self, key: int) -> MultiPolygon: ...
def __len__(self) -> int: ...
def __new__(
cls, chunks: Iterable[MultiPolygonArray | ArrowArrayExportable]
) -> Self: ...
def __repr__(self) -> str: ...
def area(
self, *, method: AreaMethod | AreaMethodT = AreaMethod.Euclidean
Expand Down Expand Up @@ -880,6 +941,9 @@ class ChunkedMixedGeometryArray:
def __eq__(self, other: Self) -> bool: ...
def __getitem__(self, key: int) -> Geometry: ...
def __len__(self) -> int: ...
def __new__(
cls, chunks: Iterable[MixedGeometryArray | ArrowArrayExportable]
) -> Self: ...
def __repr__(self) -> str: ...
def area(
self, *, method: AreaMethod | AreaMethodT = AreaMethod.Euclidean
Expand Down Expand Up @@ -909,6 +973,9 @@ class ChunkedGeometryCollectionArray:
def __eq__(self, other: Self) -> bool: ...
def __getitem__(self, key: int) -> GeometryCollection: ...
def __len__(self) -> int: ...
def __new__(
cls, chunks: Iterable[GeometryCollectionArray | ArrowArrayExportable]
) -> Self: ...
def __repr__(self) -> str: ...
def area(
self, *, method: AreaMethod | AreaMethodT = AreaMethod.Euclidean
Expand Down Expand Up @@ -939,6 +1006,7 @@ class ChunkedWKBArray:
def __eq__(self, other: Self) -> bool: ...
def __getitem__(self, key: int) -> WKB: ...
def __len__(self) -> int: ...
def __new__(cls, chunks: Iterable[WKBArray | ArrowArrayExportable]) -> Self: ...
def __repr__(self) -> str: ...
def chunk(self, i: int) -> WKBArray: ...
def chunks(self) -> List[WKBArray]: ...
Expand All @@ -955,6 +1023,7 @@ class ChunkedRectArray:
def __eq__(self, other: Self) -> bool: ...
def __getitem__(self, key: int) -> Rect: ...
def __len__(self) -> int: ...
# def __new__(cls, chunks: Iterable[RectArray | ArrowArrayExportable]) -> Self: ...
def __repr__(self) -> str: ...
def chunk(self, i: int) -> RectArray: ...
def chunks(self) -> List[RectArray]: ...
Expand Down
103 changes: 103 additions & 0 deletions python/core/src/array/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ pub mod getitem;
pub mod primitive;
pub mod repr;

use crate::buffer::{CoordBuffer, OffsetBuffer};
use crate::error::PyGeoArrowResult;
use crate::ffi::from_python::input::PyScalarBuffer;
use arrow::datatypes::Float64Type;
Expand Down Expand Up @@ -120,6 +121,15 @@ impl RectArray {

#[pymethods]
impl PointArray {
#[classmethod]
fn from_parts(_cls: &PyType, coords: &CoordBuffer) -> PyGeoArrowResult<Self> {
Ok(Self(geoarrow::array::PointArray::try_new(
coords.0.clone(),
None,
Default::default(),
)?))
}

/// Construct a PointArray from arrays of x and y values
#[classmethod]
fn from_xy(
Expand All @@ -131,3 +141,96 @@ impl PointArray {
Ok(geoarrow::array::PointArray::new(coords.into(), None, Default::default()).into())
}
}

#[pymethods]
impl LineStringArray {
#[classmethod]
fn from_parts(
_cls: &PyType,
coords: &CoordBuffer,
geom_offsets: &OffsetBuffer,
) -> PyGeoArrowResult<Self> {
Ok(Self(geoarrow::array::LineStringArray::try_new(
coords.0.clone(),
geom_offsets.0.clone(),
None,
Default::default(),
)?))
}
}

#[pymethods]
impl PolygonArray {
#[classmethod]
fn from_parts(
_cls: &PyType,
coords: &CoordBuffer,
geom_offsets: &OffsetBuffer,
ring_offsets: &OffsetBuffer,
) -> PyGeoArrowResult<Self> {
Ok(Self(geoarrow::array::PolygonArray::try_new(
coords.0.clone(),
geom_offsets.0.clone(),
ring_offsets.0.clone(),
None,
Default::default(),
)?))
}
}

#[pymethods]
impl MultiPointArray {
#[classmethod]
fn from_parts(
_cls: &PyType,
coords: &CoordBuffer,
geom_offsets: &OffsetBuffer,
) -> PyGeoArrowResult<Self> {
Ok(Self(geoarrow::array::MultiPointArray::try_new(
coords.0.clone(),
geom_offsets.0.clone(),
None,
Default::default(),
)?))
}
}

#[pymethods]
impl MultiLineStringArray {
#[classmethod]
fn from_parts(
_cls: &PyType,
coords: &CoordBuffer,
geom_offsets: &OffsetBuffer,
ring_offsets: &OffsetBuffer,
) -> PyGeoArrowResult<Self> {
Ok(Self(geoarrow::array::MultiLineStringArray::try_new(
coords.0.clone(),
geom_offsets.0.clone(),
ring_offsets.0.clone(),
None,
Default::default(),
)?))
}
}

#[pymethods]
impl MultiPolygonArray {
#[classmethod]
fn from_parts(
_cls: &PyType,
coords: &CoordBuffer,
geom_offsets: &OffsetBuffer,
polygon_offsets: &OffsetBuffer,
ring_offsets: &OffsetBuffer,
) -> PyGeoArrowResult<Self> {
Ok(Self(geoarrow::array::MultiPolygonArray::try_new(
coords.0.clone(),
geom_offsets.0.clone(),
polygon_offsets.0.clone(),
ring_offsets.0.clone(),
None,
Default::default(),
)?))
}
}
54 changes: 54 additions & 0 deletions python/core/src/buffer/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
use arrow::datatypes::{Float64Type, Int32Type};
use geoarrow::array::{InterleavedCoordBuffer, SeparatedCoordBuffer};
use pyo3::prelude::*;
use pyo3::types::PyType;

use crate::ffi::from_python::input::PyScalarBuffer;

#[pyclass(module = "geoarrow.rust.core._rust")]
pub struct CoordBuffer(pub(crate) geoarrow::array::CoordBuffer);

#[pymethods]
impl CoordBuffer {
/// Construct a CoordBuffer from a single buffer of interleaved XY coordinates.
#[classmethod]
pub fn from_interleaved(_cls: &PyType, coords: PyScalarBuffer<Float64Type>) -> Self {
let coord_buffer = InterleavedCoordBuffer::new(coords.0);
Self(geoarrow::array::CoordBuffer::Interleaved(coord_buffer))
}

/// Construct a CoordBuffer from two buffers of separated X and Y coordinates.
#[classmethod]
pub fn from_separated(
_cls: &PyType,
x: PyScalarBuffer<Float64Type>,
y: PyScalarBuffer<Float64Type>,
) -> Self {
let coord_buffer = SeparatedCoordBuffer::new(x.0, y.0);
Self(geoarrow::array::CoordBuffer::Separated(coord_buffer))
}
}

impl From<geoarrow::array::CoordBuffer> for CoordBuffer {
fn from(value: geoarrow::array::CoordBuffer) -> Self {
Self(value)
}
}

impl From<CoordBuffer> for geoarrow::array::CoordBuffer {
fn from(value: CoordBuffer) -> Self {
value.0
}
}

#[pyclass(module = "geoarrow.rust.core._rust")]
pub struct OffsetBuffer(pub(crate) arrow_buffer::buffer::OffsetBuffer<i32>);

impl<'a> FromPyObject<'a> for OffsetBuffer {
fn extract(ob: &'a PyAny) -> PyResult<Self> {
let scalar_buffer = ob.extract::<PyScalarBuffer<Int32Type>>()?;
Ok(Self(arrow_buffer::buffer::OffsetBuffer::new(
scalar_buffer.0,
)))
}
}
Loading
Loading