Skip to content

Commit 5f6d095

Browse files
Draft: apply_polygon and test_apply_polygon (#298)
* adding apply_polygon and tests * update apply_polygon * delete apply_polygon_large test * updated submodule * update pyproject.toml
1 parent 7635e62 commit 5f6d095

File tree

4 files changed

+93
-3
lines changed

4 files changed

+93
-3
lines changed

openeo_processes_dask/process_implementations/cubes/apply.py

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,16 @@
33
import numpy as np
44
import scipy.ndimage
55
import xarray as xr
6+
from shapely.geometry import MultiPolygon, Polygon, shape
7+
from shapely.ops import unary_union
68

7-
from openeo_processes_dask.process_implementations.data_model import RasterCube
9+
from openeo_processes_dask.process_implementations.cubes.mask_polygon import (
10+
mask_polygon,
11+
)
12+
from openeo_processes_dask.process_implementations.data_model import (
13+
RasterCube,
14+
VectorCube,
15+
)
816
from openeo_processes_dask.process_implementations.exceptions import (
917
DimensionNotAvailable,
1018
KernelDimensionsUneven,
@@ -165,3 +173,40 @@ def convolve(data, kernel, mode="constant", cval=0, fill_value=0):
165173
cval = 0
166174

167175
return convolve(data, kernel, mode, cval, replace_invalid) * factor
176+
177+
178+
def apply_polygon(
179+
data: RasterCube,
180+
polygons: Union[VectorCube, dict],
181+
process: Callable,
182+
mask_value: Optional[Union[int, float, str, None]] = None,
183+
context: Optional[dict] = None,
184+
) -> RasterCube:
185+
if isinstance(polygons, dict) and polygons.get("type") == "FeatureCollection":
186+
polygon_geometries = [
187+
shape(feature["geometry"]) for feature in polygons["features"]
188+
]
189+
elif isinstance(polygons, dict) and polygons.get("type") in [
190+
"Polygon",
191+
"MultiPolygon",
192+
]:
193+
polygon_geometries = [shape(polygons)]
194+
else:
195+
raise ValueError(
196+
"Unsupported polygons format. Expected GeoJSON-like FeatureCollection or Polygon."
197+
)
198+
199+
unified_polygon = unary_union(polygon_geometries)
200+
201+
if isinstance(unified_polygon, MultiPolygon) and len(unified_polygon.geoms) < len(
202+
polygon_geometries
203+
):
204+
raise Exception("GeometriesOverlap")
205+
206+
masked_data = mask_polygon(data, polygons, replacement=np.nan)
207+
208+
processed_data = apply(masked_data, process, context=context)
209+
210+
result = mask_polygon(processed_data, polygons, replacement=mask_value)
211+
212+
return result

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "openeo-processes-dask"
3-
version = "2024.11.3"
3+
version = "2024.11.4"
44
description = "Python implementations of many OpenEO processes, dask-friendly by default."
55
authors = ["Lukas Weidenholzer <lukas.weidenholzer@eodc.eu>", "Sean Hoyal <sean.hoyal@eodc.eu>", "Valentina Hutter <valentina.hutter@eodc.eu>"]
66
maintainers = ["EODC Staff <support@eodc.eu>"]

tests/test_apply.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@
1010
apply,
1111
apply_dimension,
1212
apply_kernel,
13+
apply_polygon,
14+
)
15+
from openeo_processes_dask.process_implementations.cubes.mask_polygon import (
16+
mask_polygon,
1317
)
1418
from tests.general_checks import assert_numpy_equals_dask_numpy, general_output_checks
1519
from tests.mockdata import create_fake_rastercube
@@ -482,3 +486,44 @@ def test_apply_dimension_cummin_process(
482486
).compute()
483487

484488
assert np.isnan(output_cube_cummin_with_nan[0, 0, 16, 0].values)
489+
490+
491+
@pytest.mark.parametrize("size", [(6, 5, 4, 4)])
492+
@pytest.mark.parametrize("dtype", [np.float32])
493+
def test_apply_polygon(
494+
temporal_interval,
495+
bounding_box,
496+
random_raster_data,
497+
polygon_geometry_small,
498+
process_registry,
499+
):
500+
input_cube = create_fake_rastercube(
501+
data=random_raster_data,
502+
spatial_extent=bounding_box,
503+
temporal_extent=temporal_interval,
504+
bands=["B02", "B03", "B04", "B08"],
505+
backend="dask",
506+
)
507+
508+
_process = partial(
509+
process_registry["add"].implementation,
510+
y=1,
511+
x=ParameterReference(from_parameter="x"),
512+
)
513+
514+
output_cube = apply_polygon(
515+
data=input_cube,
516+
polygons=polygon_geometry_small,
517+
process=_process,
518+
mask_value=np.nan,
519+
)
520+
521+
assert len(output_cube.dims) == len(input_cube.dims)
522+
523+
expected_result_mask = mask_polygon(data=input_cube, mask=polygon_geometry_small)
524+
525+
expected_results = expected_result_mask + 1
526+
527+
assert len(output_cube.dims) == len(expected_results.dims)
528+
529+
assert output_cube.all() == expected_results.all()

0 commit comments

Comments
 (0)