Skip to content

Commit

Permalink
Merge pull request #46 from nmandery/containment-mode-covers
Browse files Browse the repository at this point in the history
Add support for the h3o containment mode "covers"
  • Loading branch information
nmandery committed Feb 2, 2024
2 parents e919d5a + 533aa63 commit 3f182a6
Show file tree
Hide file tree
Showing 7 changed files with 22 additions and 53 deletions.
2 changes: 2 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ Unreleased
with a few changes:
- `directededges_to_wkb_lines` and `directededges_to_lines` have been removed. Use the linestring-versions instead.
- Geometry collections are currently unsupported when working with WKB. This is still work-in-progress within geoarrow-rs.
- Add support for the h3o containment mode "covers"
- Finally removed the "all_intersecting" parameter in geometry to cells conversion as announced in v0.18

0.19.2 - 2023-11-16
-------------------
Expand Down
3 changes: 3 additions & 0 deletions docs/source/usage/vector.rst
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,9 @@ The `containment_mode` argument allow the control how polygons are filled. See :

fill_namibia(containment_mode=ContainmentMode.IntersectsBoundary)

.. jupyter-execute::

fill_namibia(containment_mode=ContainmentMode.Covers)

Merging cells into larger polygons
----------------------------------
Expand Down
11 changes: 1 addition & 10 deletions python/h3ronpy/arrow/vector.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,6 @@ def wkb_to_cells(
resolution: int,
containment_mode: ContainmentMode = ContainmentMode.ContainsCentroid,
compact: bool = False,
all_intersecting: Optional[bool] = None,
flatten: bool = False,
) -> Union[pa.Array, pa.ListArray]:
"""
Expand All @@ -116,8 +115,6 @@ def wkb_to_cells(
See the ContainmentMode class.
:param compact: Compact the returned cells by replacing cells with their parent cells when all children
of that cell are part of the set.
:param all_intersecting: DEPRECATED. (Was: Also return cells which only overlap partially with the given geometry
(without intersecting with their centroid)).
:param flatten: Return a non-nested cell array instead of a list array.
"""
if _HAS_POLARS:
Expand All @@ -136,7 +133,6 @@ def wkb_to_cells(
resolution,
containment_mode=containment_mode,
compact=compact,
all_intersecting=all_intersecting,
flatten=flatten,
)

Expand All @@ -146,7 +142,6 @@ def geometry_to_cells(
resolution: int,
containment_mode: ContainmentMode = ContainmentMode.ContainsCentroid,
compact: bool = False,
all_intersecting: Optional[bool] = None,
) -> pa.Array:
"""
Convert a single object which supports the python `__geo_interface__` protocol to H3 cells
Expand All @@ -157,12 +152,8 @@ def geometry_to_cells(
See the ContainmentMode class.
:param compact: Compact the returned cells by replacing cells with their parent cells when all children
of that cell are part of the set.
:param all_intersecting: DEPRECATED. (Was: Also return cells which only overlap partially with the given geometry
(without intersecting with their centroid)).
"""
return vector.geometry_to_cells(
geom, resolution, containment_mode=containment_mode, compact=compact, all_intersecting=all_intersecting
)
return vector.geometry_to_cells(geom, resolution, containment_mode=containment_mode, compact=compact)


__all__ = [
Expand Down
4 changes: 0 additions & 4 deletions python/h3ronpy/pandas/vector.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,6 @@ def geodataframe_to_cells(
containment_mode: ContainmentMode = ContainmentMode.ContainsCentroid,
compact: bool = False,
cell_column_name: str = DEFAULT_CELL_COLUMN_NAME,
all_intersecting: Optional[bool] = None,
) -> pd.DataFrame:
"""
Convert a `GeoDataFrame` to H3 cells while exploding all other columns according to the number of cells derived
Expand All @@ -103,16 +102,13 @@ def geodataframe_to_cells(
:param compact: Compact the returned cells by replacing cells with their parent cells when all children
of that cell are part of the set.
:param cell_column_name:
:param all_intersecting: DEPRECATED. (Was: Also return cells which only overlap partially with the given geometry
(without intersecting with their centroid)).
:return:
"""
cells = _av.wkb_to_cells(
gdf.geometry.to_wkb(),
resolution,
containment_mode=containment_mode,
compact=compact,
all_intersecting=all_intersecting,
flatten=False,
)
table = pa.Table.from_pandas(pd.DataFrame(gdf.drop(columns=gdf.geometry.name))).append_column(
Expand Down
14 changes: 1 addition & 13 deletions src/error.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use h3arrow::error::Error as A3Error;
use pyo3::exceptions::{PyIOError, PyRuntimeError, PyValueError};
use pyo3::{PyErr, PyResult, Python};
use pyo3::{PyErr, PyResult};
use rasterh3::Error;

pub trait IntoPyResult<T> {
Expand Down Expand Up @@ -92,15 +92,3 @@ where
self.map_err(IntoPyErr::into_pyerr)
}
}

pub(crate) fn warn_deprecated(msg: &str) -> PyResult<()> {
Python::with_gil(|py| {
let mod_builtins = py.import("builtins")?;
let deprecation_warning = mod_builtins.getattr("DeprecationWarning")?;

let mod_warn = py.import("warnings")?;
let warn = mod_warn.getattr("warn")?;
warn.call((msg, deprecation_warning), None)?;
Ok(())
})
}
37 changes: 12 additions & 25 deletions src/vector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use pyo3::prelude::*;
use pyo3::types::PyTuple;

use crate::arrow_interop::*;
use crate::error::{warn_deprecated, IntoPyResult};
use crate::error::IntoPyResult;

/// Containment mode used to decide if a cell is contained in a polygon or not.
///
Expand Down Expand Up @@ -59,6 +59,12 @@ pub enum PyContainmentMode {
Covers,
}

impl Default for PyContainmentMode {
fn default() -> Self {
Self::ContainsCentroid
}
}

impl PyContainmentMode {
fn containment_mode(&self) -> ContainmentMode {
match self {
Expand Down Expand Up @@ -295,46 +301,28 @@ pub(crate) fn directededges_to_wkb_linestrings(array: &PyAny, radians: bool) ->
Python::with_gil(|py| out.into_inner().into_data().into_pyarrow(py))
}

fn get_containment_mode(
pycm: Option<PyContainmentMode>,
all_intersecting: Option<bool>,
) -> PyResult<ContainmentMode> {
if all_intersecting.is_some() {
warn_deprecated("The all_intersecting parameter is deprecated and will be removed. Use containment_mode instead.")?;
}
if let Some(pycm) = pycm {
return Ok(pycm.containment_mode());
}
if all_intersecting == Some(true) {
return Ok(ContainmentMode::IntersectsBoundary);
}
Ok(ContainmentMode::ContainsCentroid)
}

fn get_to_cells_options(
resolution: u8,
containment_mode: Option<PyContainmentMode>,
all_intersecting: Option<bool>,
compact: bool,
) -> PyResult<ToCellsOptions> {
Ok(ToCellsOptions::new(
PolyfillConfig::new(Resolution::try_from(resolution).into_pyresult()?)
.containment_mode(get_containment_mode(containment_mode, all_intersecting)?),
.containment_mode(containment_mode.unwrap_or_default().containment_mode()),
)
.compact(compact))
}

#[pyfunction]
#[pyo3(signature = (array, resolution, containment_mode = None, compact = false, all_intersecting = None, flatten = false))]
#[pyo3(signature = (array, resolution, containment_mode = None, compact = false, flatten = false))]
pub(crate) fn wkb_to_cells(
array: &PyAny,
resolution: u8,
containment_mode: Option<PyContainmentMode>,
compact: bool,
all_intersecting: Option<bool>, // todo: DEPRECATED. Remove in v0.19
flatten: bool,
) -> PyResult<PyObject> {
let options = get_to_cells_options(resolution, containment_mode, all_intersecting, compact)?;
let options = get_to_cells_options(resolution, containment_mode, compact)?;
let wkbarray = WKBArray::new(
pyarray_to_native::<LargeBinaryArray>(array)?,
Default::default(),
Expand All @@ -351,18 +339,17 @@ pub(crate) fn wkb_to_cells(
}

#[pyfunction]
#[pyo3(signature = (obj, resolution, containment_mode = None, compact = false, all_intersecting = None))]
#[pyo3(signature = (obj, resolution, containment_mode = None, compact = false))]
pub(crate) fn geometry_to_cells(
obj: py_geo_interface::Geometry,
resolution: u8,
containment_mode: Option<PyContainmentMode>,
compact: bool,
all_intersecting: Option<bool>,
) -> PyResult<PyObject> {
if obj.0.is_empty() {
return Python::with_gil(|py| h3array_to_pyarray(CellIndexArray::new_null(0), py));
}
let options = get_to_cells_options(resolution, containment_mode, all_intersecting, compact)?;
let options = get_to_cells_options(resolution, containment_mode, compact)?;
let cellindexarray = CellIndexArray::from(
h3arrow::array::from_geo::geometry_to_cells(&obj.0, &options).into_pyresult()?,
);
Expand Down
4 changes: 3 additions & 1 deletion tests/pandas/test_vector.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,9 @@ def test_geoseries_to_cells_flatten():
assert cells.dtype == "uint64"


@pytest.mark.skip(reason="GeometryCollections are unsupported until https://github.com/geoarrow/geoarrow-rs/blob/3a2aaa883126274037cabaf46b1f5f6459938297/src/io/wkb/reader/geometry_collection.rs#L23 is fixed")
@pytest.mark.skip(
reason="GeometryCollections are unsupported until https://github.com/geoarrow/geoarrow-rs/blob/3a2aaa883126274037cabaf46b1f5f6459938297/src/io/wkb/reader/geometry_collection.rs#L23 is fixed"
)
def test_empty_geometrycollection_omitted():
gdf = gpd.GeoDataFrame(
{
Expand Down

0 comments on commit 3f182a6

Please sign in to comment.