diff --git a/openeo_driver/testing.py b/openeo_driver/testing.py index d978a922..d1524f58 100644 --- a/openeo_driver/testing.py +++ b/openeo_driver/testing.py @@ -533,7 +533,15 @@ def to_geojson_feature_collection(self) -> dict: class ApproxGeoJSONByBounds: - """pytest assert helper to build a matcher to check if a certain GeoJSON construct is within expected bounds""" + """ + pytest assert helper to build a matcher to check if a certain GeoJSON construct has expected bounds + + Usage example: + + >>> geometry = {"type": "Polygon", "coordinates": [...]} + # Check that this geometry has bounds (1, 2, 6, 5) with some absolute tolerance + >>> assert geometry == ApproxGeoJSONByBounds(1, 2, 6, 5, abs=0.1) + """ def __init__( self, @@ -543,8 +551,9 @@ def __init__( abs: Optional[float] = None, ): bounds = args[0] if len(args) == 1 else args - assert isinstance(bounds, (list, tuple)) and len(bounds) == 4 - self.expected_bounds = [float(b) for b in bounds] + bounds = [float(b) for b in bounds] + assert len(bounds) == 4 + self.expected_bounds = bounds self.rel = rel self.abs = abs self.expected_types = set(types) @@ -572,7 +581,6 @@ def __repr__(self): msg += "\n" + "\n".join(f" # {i}" for i in self.actual_info) return msg - def caplog_with_custom_formatter(caplog: pytest.LogCaptureFixture, format: Union[str, logging.Formatter]): """ Context manager to set a custom formatter on the caplog fixture. diff --git a/tests/data/geojson/FeatureCollection10.json b/tests/data/geojson/FeatureCollection10.json new file mode 100644 index 00000000..b4de44e0 --- /dev/null +++ b/tests/data/geojson/FeatureCollection10.json @@ -0,0 +1,37 @@ +{ + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "properties": {"id": "first", "pop": 123}, + "geometry": { + "type": "Polygon", + "coordinates": [[[1, 1], [3, 1], [2, 3], [1, 1]]] + } + }, + { + "type": "Feature", + "properties": {"id": "second", "pop": 456}, + "geometry": { + "type": "Polygon", + "coordinates": [[[4, 2], [5, 4], [3, 4], [4, 2]]] + } + }, + { + "type": "Feature", + "properties": {"id": "third", "pop": 789}, + "geometry": { + "type": "Polygon", + "coordinates": [[[6, 2], [10, 2], [12, 6], [8, 6], [6, 2]]] + } + }, + { + "type": "Feature", + "properties": {"id": "fourth", "pop": 101112}, + "geometry": { + "type": "Polygon", + "coordinates": [[[-2, 7], [5, 7], [5, 14], [-2, 14], [-2, 7]]] + } + } + ] +} diff --git a/tests/test_views_execute.py b/tests/test_views_execute.py index 4ece7ad3..8f23aa77 100644 --- a/tests/test_views_execute.py +++ b/tests/test_views_execute.py @@ -3592,3 +3592,71 @@ def process_geometries(udf_data: UdfData) -> UdfData: ], } ) + + def test_apply_dimension_run_udf_filter_geometries_dimension_properties(self, api100): + """ + Test to use `apply_dimension(dimension="properties", process=UDF)` to filter out certain + entries from geometries dimension. + + Note that, strictly speaking, this approach draws outside the lines of the openEO API spec + as apply_dimension only allows changing the cardinality of the provided dimension ("properties" in this case), + not any other dimension (like "geometries" in this case). + """ + udf_code = """ + from openeo.udf import UdfData, FeatureCollection + import shapely.geometry + def process_geometries(udf_data: UdfData) -> UdfData: + [feature_collection] = udf_data.get_feature_collection_list() + gdf = feature_collection.data + to_intersect = shapely.geometry.box(4, 3, 8, 4) + gdf = gdf[gdf["geometry"].intersects(to_intersect)] + udf_data.set_feature_collection_list([ + FeatureCollection(id="_", data=gdf), + ]) + """ + udf_code = textwrap.dedent(udf_code) + process_graph = { + "get_vector_data": { + "process_id": "load_uploaded_files", + "arguments": {"paths": [str(get_path("geojson/FeatureCollection10.json"))], "format": "GeoJSON"}, + }, + "apply_dimension": { + "process_id": "apply_dimension", + "arguments": { + "data": {"from_node": "get_vector_data"}, + "dimension": "properties", + "process": { + "process_graph": { + "runudf1": { + "process_id": "run_udf", + "arguments": { + "data": {"from_node": "get_vector_data"}, + "udf": udf_code, + "runtime": "Python", + }, + "result": True, + } + }, + }, + }, + "result": True, + }, + } + resp = api100.check_result(process_graph) + assert resp.json == DictSubSet( + { + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "geometry": ApproxGeoJSONByBounds(3, 2, 5, 4, types=["Polygon"], abs=0.1), + "properties": {"id": "second", "pop": 456}, + }, + { + "type": "Feature", + "geometry": ApproxGeoJSONByBounds(6, 2, 12, 6, types=["Polygon"], abs=0.1), + "properties": {"id": "third", "pop": 789}, + }, + ], + } + )