diff --git a/CHANGELOG.md b/CHANGELOG.md index 3b910726..79fe028b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,7 +21,11 @@ and start a new "In Progress" section above it. ## In progress + +## 0.107.2 + - Fix default level of `inspect` process (defaults to `info`) ([#424](https://github.com/Open-EO/openeo-geopyspark-driver/issues/424)) +- `apply_polygon`: add support for `geometries` argument (in addition to legacy, but still supported `polygons`) ([Open-EO/openeo-processes#511](https://github.com/Open-EO/openeo-processes/issues/511)) ## 0.107.1 diff --git a/openeo_driver/ProcessGraphDeserializer.py b/openeo_driver/ProcessGraphDeserializer.py index b69f0fb9..e5f11a84 100644 --- a/openeo_driver/ProcessGraphDeserializer.py +++ b/openeo_driver/ProcessGraphDeserializer.py @@ -850,7 +850,12 @@ def chunk_polygon(args: ProcessArgs, env: EvalEnv) -> DriverDataCube: def apply_polygon(args: ProcessArgs, env: EvalEnv) -> DriverDataCube: data_cube = args.get_required("data", expected_type=DriverDataCube) process = args.get_deep("process", "process_graph", expected_type=dict) - polygons = args.get_required("polygons") + if "polygons" in args and "geometries" not in args: + # TODO remove this deprecated "polygons" parameter handling when not used anymore + _log.warning("In process 'apply_polygon': parameter 'polygons' is deprecated, use 'geometries' instead.") + geometries = args.get_required("polygons") + else: + geometries = args.get_required("geometries") mask_value = args.get_optional("mask_value", expected_type=(int, float), default=None) context = args.get_optional("context", default=None) @@ -858,24 +863,24 @@ def apply_polygon(args: ProcessArgs, env: EvalEnv) -> DriverDataCube: # TODO #288: this logic (copied from original chunk_polygon implementation) coerces the input polygons # to a single MultiPolygon of pure (non-multi) polygons, which is conceptually wrong. # Instead it should normalize to a feature collection or vector cube. - if isinstance(polygons, DelayedVector): - polygons = list(polygons.geometries) - for p in polygons: + if isinstance(geometries, DelayedVector): + geometries = list(geometries.geometries) + for p in geometries: if not isinstance(p, shapely.geometry.Polygon): reason = "{m!s} is not a polygon.".format(m=p) raise ProcessParameterInvalidException(parameter="polygons", process="apply_polygon", reason=reason) - polygon = MultiPolygon(polygons) - elif isinstance(polygons, DriverVectorCube): + polygon = MultiPolygon(geometries) + elif isinstance(geometries, DriverVectorCube): # TODO #288: I know it's wrong to coerce to MultiPolygon here, but we stick to this ill-defined API for now. - polygon = polygons.to_multipolygon() - elif isinstance(polygons, shapely.geometry.base.BaseGeometry): - polygon = MultiPolygon(polygons) - elif isinstance(polygons, dict): - polygon = geojson_to_multipolygon(polygons) + polygon = geometries.to_multipolygon() + elif isinstance(geometries, shapely.geometry.base.BaseGeometry): + polygon = MultiPolygon(geometries) + elif isinstance(geometries, dict): + polygon = geojson_to_multipolygon(geometries) if isinstance(polygon, shapely.geometry.Polygon): polygon = MultiPolygon([polygon]) else: - reason = f"unsupported type: {type(polygons).__name__}" + reason = f"unsupported type: {type(geometries).__name__}" raise ProcessParameterInvalidException(parameter="polygons", process="apply_polygon", reason=reason) if polygon.area == 0: diff --git a/openeo_driver/_version.py b/openeo_driver/_version.py index f3df7397..de5a1272 100644 --- a/openeo_driver/_version.py +++ b/openeo_driver/_version.py @@ -1 +1 @@ -__version__ = "0.107.1a1" +__version__ = "0.107.2a1" diff --git a/openeo_driver/dry_run.py b/openeo_driver/dry_run.py index 1d2aa5e5..d6e8d48c 100644 --- a/openeo_driver/dry_run.py +++ b/openeo_driver/dry_run.py @@ -595,6 +595,7 @@ def _normalize_geometry(self, geometries) -> Tuple[Union[DriverVectorCube, Delay elif isinstance(geometries, shapely.geometry.base.BaseGeometry): _log.warning( "_normalize_geometry: TODO are we still reaching this code?", + # TODO yes we are, apparently due to #288 stack_info=True, ) # TODO: buffer distance of 10m assumes certain resolution (e.g. sentinel2 pixels) diff --git a/tests/data/pg/1.0/apply_polygon.json b/tests/data/pg/1.0/apply_polygon.json index 67d69ffe..3dced459 100644 --- a/tests/data/pg/1.0/apply_polygon.json +++ b/tests/data/pg/1.0/apply_polygon.json @@ -7,7 +7,7 @@ "process_id": "apply_polygon", "arguments": { "data": {"from_node": "collection"}, - "polygons": { + "geometries": { "type": "FeatureCollection", "features": [ { diff --git a/tests/test_views_execute.py b/tests/test_views_execute.py index 821377b9..d923b48b 100644 --- a/tests/test_views_execute.py +++ b/tests/test_views_execute.py @@ -1,5 +1,5 @@ +import logging import shutil - import dataclasses import json import math @@ -3737,13 +3737,31 @@ def test_chunk_polygon(api): assert params["spatial_extent"] == {"west": 1.0, "south": 5.0, "east": 12.0, "north": 16.0, "crs": "EPSG:4326"} -def test_apply_polygon(api): +def test_apply_polygon_legacy(api, caplog): + caplog.set_level(logging.WARNING) + api.check_result("apply_polygon.json", preprocess=lambda s: s.replace("geometries", "polygons")) + params = dummy_backend.last_load_collection_call("S2_FOOBAR") + assert params["spatial_extent"] == {"west": 1.0, "south": 5.0, "east": 12.0, "north": 16.0, "crs": "EPSG:4326"} + assert "In process 'apply_polygon': parameter 'polygons' is deprecated, use 'geometries' instead." in caplog.text + + +def test_apply_polygon(api, caplog): + caplog.set_level(logging.WARNING) api.check_result("apply_polygon.json") params = dummy_backend.last_load_collection_call("S2_FOOBAR") assert params["spatial_extent"] == {"west": 1.0, "south": 5.0, "east": 12.0, "north": 16.0, "crs": "EPSG:4326"} + # TODO due to #288 we can not simply assert absence of any warnings/errors + # assert caplog.text == "" + assert "deprecated" not in caplog.text + + +def test_apply_polygon_no_geometries(api): + res = api.result("apply_polygon.json", preprocess=lambda s: s.replace("geometries", "heometriez")) + res.assert_error(400, "ProcessParameterRequired", "Process 'apply_polygon' parameter 'geometries' is required") -def test_apply_polygon_with_vector_cube(api, tmp_path): +@pytest.mark.parametrize("geometries_argument", ["polygons", "geometries"]) +def test_apply_polygon_with_vector_cube(api, tmp_path, geometries_argument): shutil.copy(get_path("geojson/FeatureCollection01.json"), tmp_path / "geometry.json") with ephemeral_fileserver(tmp_path) as fileserver_root: url = f"{fileserver_root}/geometry.json" @@ -3761,7 +3779,7 @@ def test_apply_polygon_with_vector_cube(api, tmp_path): "process_id": "apply_polygon", "arguments": { "data": {"from_node": "load_raster"}, - "polygons": {"from_node": "load_vector"}, + geometries_argument: {"from_node": "load_vector"}, "process": { "process_graph": { "constant": {