From 4db1718ef4d018e5981f8f127df5be65d9c7f329 Mon Sep 17 00:00:00 2001 From: Elien Vandermaesen Date: Tue, 10 Dec 2024 14:20:14 +0100 Subject: [PATCH 1/7] issue #678 support shapely in load collection spatial extent --- openeo/rest/datacube.py | 158 ++++++++++++++------------- tests/rest/datacube/test_datacube.py | 13 +++ 2 files changed, 94 insertions(+), 77 deletions(-) diff --git a/openeo/rest/datacube.py b/openeo/rest/datacube.py index 3e80edbe5..0a7e43186 100644 --- a/openeo/rest/datacube.py +++ b/openeo/rest/datacube.py @@ -143,7 +143,7 @@ def load_collection( cls, collection_id: Union[str, Parameter], connection: Optional[Connection] = None, - spatial_extent: Union[Dict[str, float], Parameter, None] = None, + spatial_extent: Union[Dict[str, float], Parameter, shapely.geometry.base.BaseGeometry, None] = None, temporal_extent: Union[Sequence[InputDate], Parameter, str, None] = None, bands: Union[None, List[str], Parameter] = None, fetch_metadata: bool = True, @@ -187,6 +187,12 @@ def load_collection( "Unexpected parameterized `spatial_extent` in `load_collection`:" f" expected schema with type 'object' but got {spatial_extent.schema!r}." ) + valid_geojson_types = [ + "Polygon", "MultiPolygon", "GeometryCollection", "FeatureCollection" + ] + if spatial_extent and not isinstance(spatial_extent, dict): + spatial_extent = _get_geometry_argument(argument=spatial_extent,valid_geojson_types=valid_geojson_types,connection=connection) + arguments = { 'id': collection_id, # TODO: spatial_extent could also be a "geojson" subtype object, so we might want to allow (and convert) shapely shapes as well here. @@ -628,10 +634,9 @@ def filter_spatial( (which will be loaded client-side to get the geometries as GeoJSON construct). """ valid_geojson_types = [ - "Point", "MultiPoint", "LineString", "MultiLineString", "Polygon", "MultiPolygon", "GeometryCollection", "FeatureCollection" ] - geometries = self._get_geometry_argument(geometries, valid_geojson_types=valid_geojson_types, crs=None) + geometries = _get_geometry_argument(geometries, valid_geojson_types=valid_geojson_types, connection=self.connection, crs=None) return self.process( process_id='filter_spatial', arguments={ @@ -1058,75 +1063,6 @@ def _merge_operator_binary_cubes( } )) - def _get_geometry_argument( - self, - argument: Union[ - shapely.geometry.base.BaseGeometry, - dict, - str, - pathlib.Path, - Parameter, - _FromNodeMixin, - ], - valid_geojson_types: List[str], - crs: Optional[str] = None, - ) -> Union[dict, Parameter, PGNode]: - """ - Convert input to a geometry as "geojson" subtype object or vectorcube. - - :param crs: value that encodes a coordinate reference system. - See :py:func:`openeo.util.normalize_crs` for more details about additional normalization that is applied to this argument. - """ - if isinstance(argument, Parameter): - return argument - elif isinstance(argument, _FromNodeMixin): - return argument.from_node() - - if isinstance(argument, str) and re.match(r"^https?://", argument, flags=re.I): - # Geometry provided as URL: load with `load_url` (with best-effort format guess) - url = urllib.parse.urlparse(argument) - suffix = pathlib.Path(url.path.lower()).suffix - format = { - ".json": "GeoJSON", - ".geojson": "GeoJSON", - ".pq": "Parquet", - ".parquet": "Parquet", - ".geoparquet": "Parquet", - }.get(suffix, suffix.split(".")[-1]) - return self.connection.load_url(url=argument, format=format) - - if ( - isinstance(argument, (str, pathlib.Path)) - and pathlib.Path(argument).is_file() - and pathlib.Path(argument).suffix.lower() in [".json", ".geojson"] - ): - geometry = load_json(argument) - elif isinstance(argument, shapely.geometry.base.BaseGeometry): - geometry = mapping(argument) - elif isinstance(argument, dict): - geometry = argument - else: - raise OpenEoClientException(f"Invalid geometry argument: {argument!r}") - - if geometry.get("type") not in valid_geojson_types: - raise OpenEoClientException("Invalid geometry type {t!r}, must be one of {s}".format( - t=geometry.get("type"), s=valid_geojson_types - )) - if crs: - # TODO: don't warn when the crs is Lon-Lat like EPSG:4326? - warnings.warn(f"Geometry with non-Lon-Lat CRS {crs!r} is only supported by specific back-ends.") - # TODO #204 alternative for non-standard CRS in GeoJSON object? - epsg_code = normalize_crs(crs) - if epsg_code is not None: - # proj did recognize the CRS - crs_name = f"EPSG:{epsg_code}" - else: - # proj did not recognise this CRS - warnings.warn(f"non-Lon-Lat CRS {crs!r} is not known to the proj library and might not be supported.") - crs_name = crs - geometry["crs"] = {"type": "name", "properties": {"name": crs_name}} - return geometry - @openeo_process def aggregate_spatial( self, @@ -1198,7 +1134,7 @@ def aggregate_spatial( "Point", "MultiPoint", "LineString", "MultiLineString", "Polygon", "MultiPolygon", "GeometryCollection", "Feature", "FeatureCollection" ] - geometries = self._get_geometry_argument(geometries, valid_geojson_types=valid_geojson_types, crs=crs) + geometries = _get_geometry_argument(geometries, valid_geojson_types=valid_geojson_types, connection= self.connection, crs=crs) reducer = build_child_callback(reducer, parent_parameters=["data"]) return VectorCube( graph=self._build_pgnode( @@ -1478,8 +1414,8 @@ def chunk_polygon( "Feature", "FeatureCollection", ] - chunks = self._get_geometry_argument( - chunks, valid_geojson_types=valid_geojson_types + chunks = _get_geometry_argument( + chunks, valid_geojson_types=valid_geojson_types, connection=self.connection ) mask_value = float(mask_value) if mask_value is not None else None return self.process( @@ -1568,7 +1504,7 @@ def apply_polygon( process = build_child_callback(process, parent_parameters=["data"], connection=self.connection) valid_geojson_types = ["Polygon", "MultiPolygon", "Feature", "FeatureCollection"] - geometries = self._get_geometry_argument(geometries, valid_geojson_types=valid_geojson_types) + geometries = _get_geometry_argument(geometries, valid_geojson_types=valid_geojson_types, connection=self.connection) mask_value = float(mask_value) if mask_value is not None else None return self.process( process_id="apply_polygon", @@ -2056,7 +1992,7 @@ def mask_polygon( (which will be loaded client-side to get the geometries as GeoJSON construct). """ valid_geojson_types = ["Polygon", "MultiPolygon", "GeometryCollection", "Feature", "FeatureCollection"] - mask = self._get_geometry_argument(mask, valid_geojson_types=valid_geojson_types, crs=srs) + mask = _get_geometry_argument(mask, valid_geojson_types=valid_geojson_types, connection=self.connection, crs=srs) return self.process( process_id="mask_polygon", arguments=dict_no_none( @@ -2860,3 +2796,71 @@ def unflatten_dimension(self, dimension: str, target_dimensions: List[str], labe label_separator=label_separator, ), ) +def _get_geometry_argument( + argument: Union[ + shapely.geometry.base.BaseGeometry, + dict, + str, + pathlib.Path, + Parameter, + _FromNodeMixin, + ], + valid_geojson_types: List[str], + connection: Connection = None, + crs: Optional[str] = None, +) -> Union[dict, Parameter, PGNode]: + """ + Convert input to a geometry as "geojson" subtype object or vectorcube. + + :param crs: value that encodes a coordinate reference system. + See :py:func:`openeo.util.normalize_crs` for more details about additional normalization that is applied to this argument. + """ + if isinstance(argument, Parameter): + return argument + elif isinstance(argument, _FromNodeMixin): + return argument.from_node() + + if isinstance(argument, str) and re.match(r"^https?://", argument, flags=re.I): + # Geometry provided as URL: load with `load_url` (with best-effort format guess) + url = urllib.parse.urlparse(argument) + suffix = pathlib.Path(url.path.lower()).suffix + format = { + ".json": "GeoJSON", + ".geojson": "GeoJSON", + ".pq": "Parquet", + ".parquet": "Parquet", + ".geoparquet": "Parquet", + }.get(suffix, suffix.split(".")[-1]) + return connection.load_url(url=argument, format=format) + # + if ( + isinstance(argument, (str, pathlib.Path)) + and pathlib.Path(argument).is_file() + and pathlib.Path(argument).suffix.lower() in [".json", ".geojson"] + ): + geometry = load_json(argument) + elif isinstance(argument, shapely.geometry.base.BaseGeometry): + geometry = mapping(argument) + elif isinstance(argument, dict): + geometry = argument + else: + raise OpenEoClientException(f"Invalid geometry argument: {argument!r}") + + if geometry.get("type") not in valid_geojson_types: + raise OpenEoClientException("Invalid geometry type {t!r}, must be one of {s}".format( + t=geometry.get("type"), s=valid_geojson_types + )) + if crs: + # TODO: don't warn when the crs is Lon-Lat like EPSG:4326? + warnings.warn(f"Geometry with non-Lon-Lat CRS {crs!r} is only supported by specific back-ends.") + # TODO #204 alternative for non-standard CRS in GeoJSON object? + epsg_code = normalize_crs(crs) + if epsg_code is not None: + # proj did recognize the CRS + crs_name = f"EPSG:{epsg_code}" + else: + # proj did not recognise this CRS + warnings.warn(f"non-Lon-Lat CRS {crs!r} is not known to the proj library and might not be supported.") + crs_name = crs + geometry["crs"] = {"type": "name", "properties": {"name": crs_name}} + return geometry \ No newline at end of file diff --git a/tests/rest/datacube/test_datacube.py b/tests/rest/datacube/test_datacube.py index cd3afbcba..7e8745109 100644 --- a/tests/rest/datacube/test_datacube.py +++ b/tests/rest/datacube/test_datacube.py @@ -136,6 +136,19 @@ def test_load_collection_connectionless_temporal_extent_shortcut(self): } } + def test_load_collection_connectionless_shapely_spatial_extent(self): + polygon = shapely.Polygon(((0.0,1.0),(2.0,1.0),(3.0,2.0),(1.5,0.0),(0.0,1.0))) + cube = DataCube.load_collection("T3", spatial_extent=polygon) + assert cube.flat_graph() == { + "loadcollection1": { + "arguments": {"id": "T3", "spatial_extent": + {'coordinates': (((0.0,1.0),(2.0,1.0),(3.0,2.0),(1.5,0.0),(0.0,1.0)),),'type': 'Polygon'}, + "temporal_extent": None}, + "process_id": "load_collection", + "result": True, + } + } + def test_load_collection_connectionless_save_result(self): cube = DataCube.load_collection("T3").save_result(format="GTiff") assert cube.flat_graph() == { From 146f35e9973129b01482648d7ed4243426ae9867 Mon Sep 17 00:00:00 2001 From: Elien Vandermaesen Date: Tue, 10 Dec 2024 15:00:21 +0100 Subject: [PATCH 2/7] issue #678 support shapely in load collection spatial extent --- openeo/rest/datacube.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openeo/rest/datacube.py b/openeo/rest/datacube.py index 0a7e43186..6f114b369 100644 --- a/openeo/rest/datacube.py +++ b/openeo/rest/datacube.py @@ -190,7 +190,7 @@ def load_collection( valid_geojson_types = [ "Polygon", "MultiPolygon", "GeometryCollection", "FeatureCollection" ] - if spatial_extent and not isinstance(spatial_extent, dict): + if spatial_extent and not (isinstance(spatial_extent, dict) and spatial_extent.keys() & {"west", "east", "north", "south"}): spatial_extent = _get_geometry_argument(argument=spatial_extent,valid_geojson_types=valid_geojson_types,connection=connection) arguments = { From ef50a25dfa817ae6a656650391ebe7780ce35895 Mon Sep 17 00:00:00 2001 From: Elien Vandermaesen Date: Tue, 10 Dec 2024 15:01:18 +0100 Subject: [PATCH 3/7] issue #678 support shapely in load collection spatial extent --- openeo/rest/datacube.py | 1 - 1 file changed, 1 deletion(-) diff --git a/openeo/rest/datacube.py b/openeo/rest/datacube.py index 6f114b369..4a0b26a70 100644 --- a/openeo/rest/datacube.py +++ b/openeo/rest/datacube.py @@ -195,7 +195,6 @@ def load_collection( arguments = { 'id': collection_id, - # TODO: spatial_extent could also be a "geojson" subtype object, so we might want to allow (and convert) shapely shapes as well here. 'spatial_extent': spatial_extent, 'temporal_extent': temporal_extent, } From 8cadb84bda50ec9c2c38ef8511773d835972ae65 Mon Sep 17 00:00:00 2001 From: Elien Vandermaesen Date: Fri, 13 Dec 2024 08:37:26 +0100 Subject: [PATCH 4/7] issue #678 support shapely and local path in load collection spatial extent --- CHANGELOG.md | 1 + openeo/rest/connection.py | 9 +++++++-- openeo/rest/datacube.py | 9 +++++++-- tests/rest/datacube/test_datacube.py | 14 ++++++++++++++ 4 files changed, 29 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fe65c0475..241f60fd3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Automatically use `load_url` when providing a URL as geometries to `DataCube.aggregate_spatial()`, `DataCube.mask_polygon()`, etc. ([#104](https://github.com/Open-EO/openeo-python-client/issues/104), [#457](https://github.com/Open-EO/openeo-python-client/issues/457)) +- Argument `spatial_extent` in `load_collection` supports type `shapely` and loading geometry from a local path. ### Changed diff --git a/openeo/rest/connection.py b/openeo/rest/connection.py index 79ee478f8..a1befaa79 100644 --- a/openeo/rest/connection.py +++ b/openeo/rest/connection.py @@ -1230,7 +1230,7 @@ def datacube_from_json(self, src: Union[str, Path], parameters: Optional[dict] = def load_collection( self, collection_id: Union[str, Parameter], - spatial_extent: Union[Dict[str, float], Parameter, None] = None, + spatial_extent: Union[Dict[str, float], Parameter, shapely.geometry.base.BaseGeometry, None] = None, temporal_extent: Union[Sequence[InputDate], Parameter, str, None] = None, bands: Union[None, List[str], Parameter] = None, properties: Union[ @@ -1243,7 +1243,12 @@ def load_collection( Load a DataCube by collection id. :param collection_id: image collection identifier - :param spatial_extent: limit data to specified bounding box or polygons + :param spatial_extent: limit data to specified bounding box or polygons. Can be provided in different ways: + - a shapely geometry + - a GeoJSON-style dictionary, + - a path (:py:class:`str` or :py:class:`~pathlib.Path`) to a local, client-side GeoJSON file, + which will be loaded automatically to get the geometries as GeoJSON construct. + - a :py:class:`~openeo.api.process.Parameter` instance. :param temporal_extent: limit data to specified temporal interval. Typically, just a two-item list or tuple containing start and end date. See :ref:`filtering-on-temporal-extent-section` for more details on temporal extent handling and shorthand notation. diff --git a/openeo/rest/datacube.py b/openeo/rest/datacube.py index 4a0b26a70..4629c37d5 100644 --- a/openeo/rest/datacube.py +++ b/openeo/rest/datacube.py @@ -158,7 +158,12 @@ def load_collection( :param collection_id: image collection identifier :param connection: The backend connection to use. Can be ``None`` to work without connection and collection metadata. - :param spatial_extent: limit data to specified bounding box or polygons + :param spatial_extent: limit data to specified bounding box or polygons. Can be provided in different ways: + - a shapely geometry + - a GeoJSON-style dictionary, + - a path (:py:class:`str` or :py:class:`~pathlib.Path`) to a local, client-side GeoJSON file, + which will be loaded automatically to get the geometries as GeoJSON construct. + - a :py:class:`~openeo.api.process.Parameter` instance. :param temporal_extent: limit data to specified temporal interval. Typically, just a two-item list or tuple containing start and end date. See :ref:`filtering-on-temporal-extent-section` for more details on temporal extent handling and shorthand notation. @@ -188,7 +193,7 @@ def load_collection( f" expected schema with type 'object' but got {spatial_extent.schema!r}." ) valid_geojson_types = [ - "Polygon", "MultiPolygon", "GeometryCollection", "FeatureCollection" + "Polygon", "MultiPolygon", "Feature", "FeatureCollection" ] if spatial_extent and not (isinstance(spatial_extent, dict) and spatial_extent.keys() & {"west", "east", "north", "south"}): spatial_extent = _get_geometry_argument(argument=spatial_extent,valid_geojson_types=valid_geojson_types,connection=connection) diff --git a/tests/rest/datacube/test_datacube.py b/tests/rest/datacube/test_datacube.py index 7e8745109..820f720a4 100644 --- a/tests/rest/datacube/test_datacube.py +++ b/tests/rest/datacube/test_datacube.py @@ -149,6 +149,20 @@ def test_load_collection_connectionless_shapely_spatial_extent(self): } } + @pytest.mark.parametrize("path_factory", [str, pathlib.Path]) + def test_load_collection_connectionless_local_path_spatial_extent(self, path_factory, test_data): + path = path_factory(test_data.get_path("geojson/polygon02.json")) + cube = DataCube.load_collection("T3", spatial_extent=path) + assert cube.flat_graph() == { + "loadcollection1": { + "arguments": {"id": "T3", "spatial_extent": + {"type": "Polygon", "coordinates": [[[3, 50], [4, 50], [4, 51], [3, 50]]]}, + "temporal_extent": None}, + "process_id": "load_collection", + "result": True, + } + } + def test_load_collection_connectionless_save_result(self): cube = DataCube.load_collection("T3").save_result(format="GTiff") assert cube.flat_graph() == { From d2770119a541fdcd8dd7ec200216ffb953e41cb8 Mon Sep 17 00:00:00 2001 From: Elien Vandermaesen Date: Mon, 16 Dec 2024 08:55:39 +0100 Subject: [PATCH 5/7] issue #678 support shapely and local path in load collection spatial extent --- CHANGELOG.md | 2 +- openeo/rest/connection.py | 1 + openeo/rest/datacube.py | 14 +++++++++----- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 241f60fd3..1f4dd5dd3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Automatically use `load_url` when providing a URL as geometries to `DataCube.aggregate_spatial()`, `DataCube.mask_polygon()`, etc. ([#104](https://github.com/Open-EO/openeo-python-client/issues/104), [#457](https://github.com/Open-EO/openeo-python-client/issues/457)) -- Argument `spatial_extent` in `load_collection` supports type `shapely` and loading geometry from a local path. +- Argument `spatial_extent` in `load_collection` supports Shapely objects and loading GeoJSON from a local path. ### Changed diff --git a/openeo/rest/connection.py b/openeo/rest/connection.py index a1befaa79..77d4fc759 100644 --- a/openeo/rest/connection.py +++ b/openeo/rest/connection.py @@ -1249,6 +1249,7 @@ def load_collection( - a path (:py:class:`str` or :py:class:`~pathlib.Path`) to a local, client-side GeoJSON file, which will be loaded automatically to get the geometries as GeoJSON construct. - a :py:class:`~openeo.api.process.Parameter` instance. + - a bounding box dictionary :param temporal_extent: limit data to specified temporal interval. Typically, just a two-item list or tuple containing start and end date. See :ref:`filtering-on-temporal-extent-section` for more details on temporal extent handling and shorthand notation. diff --git a/openeo/rest/datacube.py b/openeo/rest/datacube.py index 4629c37d5..7f12dcfb7 100644 --- a/openeo/rest/datacube.py +++ b/openeo/rest/datacube.py @@ -164,6 +164,7 @@ def load_collection( - a path (:py:class:`str` or :py:class:`~pathlib.Path`) to a local, client-side GeoJSON file, which will be loaded automatically to get the geometries as GeoJSON construct. - a :py:class:`~openeo.api.process.Parameter` instance. + - a bounding box dictionary :param temporal_extent: limit data to specified temporal interval. Typically, just a two-item list or tuple containing start and end date. See :ref:`filtering-on-temporal-extent-section` for more details on temporal extent handling and shorthand notation. @@ -192,11 +193,14 @@ def load_collection( "Unexpected parameterized `spatial_extent` in `load_collection`:" f" expected schema with type 'object' but got {spatial_extent.schema!r}." ) - valid_geojson_types = [ - "Polygon", "MultiPolygon", "Feature", "FeatureCollection" - ] - if spatial_extent and not (isinstance(spatial_extent, dict) and spatial_extent.keys() & {"west", "east", "north", "south"}): - spatial_extent = _get_geometry_argument(argument=spatial_extent,valid_geojson_types=valid_geojson_types,connection=connection) + elif not spatial_extent or (isinstance(spatial_extent, dict) and spatial_extent.keys() & {"west", "east", "north", "south"}): + pass + else: + valid_geojson_types = [ + "Polygon", "MultiPolygon", "Feature", "FeatureCollection" + ] + spatial_extent = _get_geometry_argument(argument=spatial_extent, valid_geojson_types=valid_geojson_types, + connection=connection) arguments = { 'id': collection_id, From 81431c23261b001f89802036116219b9f3401e45 Mon Sep 17 00:00:00 2001 From: Elien Vandermaesen Date: Thu, 19 Dec 2024 08:28:18 +0100 Subject: [PATCH 6/7] issue #678 support shapely and local path in load collection, make changes clear --- openeo/rest/connection.py | 5 ++++- openeo/rest/datacube.py | 11 ++++++++--- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/openeo/rest/connection.py b/openeo/rest/connection.py index 77d4fc759..44a100746 100644 --- a/openeo/rest/connection.py +++ b/openeo/rest/connection.py @@ -1244,12 +1244,12 @@ def load_collection( :param collection_id: image collection identifier :param spatial_extent: limit data to specified bounding box or polygons. Can be provided in different ways: + - a bounding box dictionary - a shapely geometry - a GeoJSON-style dictionary, - a path (:py:class:`str` or :py:class:`~pathlib.Path`) to a local, client-side GeoJSON file, which will be loaded automatically to get the geometries as GeoJSON construct. - a :py:class:`~openeo.api.process.Parameter` instance. - - a bounding box dictionary :param temporal_extent: limit data to specified temporal interval. Typically, just a two-item list or tuple containing start and end date. See :ref:`filtering-on-temporal-extent-section` for more details on temporal extent handling and shorthand notation. @@ -1268,6 +1268,9 @@ def load_collection( .. versionchanged:: 0.26.0 Add :py:func:`~openeo.rest.graph_building.collection_property` support to ``properties`` argument. + + .. versionchanged:: 0.37.0 + Add support for shapely geometry and local path to GeoJSON file to spatial_extent argument. """ return DataCube.load_collection( collection_id=collection_id, diff --git a/openeo/rest/datacube.py b/openeo/rest/datacube.py index 7f12dcfb7..457b7e61c 100644 --- a/openeo/rest/datacube.py +++ b/openeo/rest/datacube.py @@ -159,12 +159,12 @@ def load_collection( :param connection: The backend connection to use. Can be ``None`` to work without connection and collection metadata. :param spatial_extent: limit data to specified bounding box or polygons. Can be provided in different ways: + - a bounding box dictionary - a shapely geometry - a GeoJSON-style dictionary, - a path (:py:class:`str` or :py:class:`~pathlib.Path`) to a local, client-side GeoJSON file, which will be loaded automatically to get the geometries as GeoJSON construct. - a :py:class:`~openeo.api.process.Parameter` instance. - - a bounding box dictionary :param temporal_extent: limit data to specified temporal interval. Typically, just a two-item list or tuple containing start and end date. See :ref:`filtering-on-temporal-extent-section` for more details on temporal extent handling and shorthand notation. @@ -183,6 +183,9 @@ def load_collection( .. versionchanged:: 0.26.0 Add :py:func:`~openeo.rest.graph_building.collection_property` support to ``properties`` argument. + + .. versionchanged:: 0.37.0 + Add support for shapely geometry and local path to GeoJSON file to spatial_extent argument. """ if temporal_extent: temporal_extent = cls._get_temporal_extent(extent=temporal_extent) @@ -193,7 +196,9 @@ def load_collection( "Unexpected parameterized `spatial_extent` in `load_collection`:" f" expected schema with type 'object' but got {spatial_extent.schema!r}." ) - elif not spatial_extent or (isinstance(spatial_extent, dict) and spatial_extent.keys() & {"west", "east", "north", "south"}): + elif spatial_extent is None or ( + isinstance(spatial_extent, dict) and spatial_extent.keys() & {"west", "east", "north", "south"} + ): pass else: valid_geojson_types = [ @@ -2871,4 +2876,4 @@ def _get_geometry_argument( warnings.warn(f"non-Lon-Lat CRS {crs!r} is not known to the proj library and might not be supported.") crs_name = crs geometry["crs"] = {"type": "name", "properties": {"name": crs_name}} - return geometry \ No newline at end of file + return geometry From a64ce16d6a4489adddde073af0a7ec977f60fcc6 Mon Sep 17 00:00:00 2001 From: Elien Vandermaesen Date: Thu, 2 Jan 2025 09:02:39 +0100 Subject: [PATCH 7/7] issue #678 correct valid types of geojson types in filter_spatial --- openeo/rest/datacube.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/openeo/rest/datacube.py b/openeo/rest/datacube.py index 457b7e61c..2afcf46e7 100644 --- a/openeo/rest/datacube.py +++ b/openeo/rest/datacube.py @@ -647,7 +647,14 @@ def filter_spatial( (which will be loaded client-side to get the geometries as GeoJSON construct). """ valid_geojson_types = [ - "Polygon", "MultiPolygon", "GeometryCollection", "FeatureCollection" + "Point", + "MultiPoint", + "LineString", + "MultiLineString", + "Polygon", + "MultiPolygon", + "GeometryCollection", + "FeatureCollection", ] geometries = _get_geometry_argument(geometries, valid_geojson_types=valid_geojson_types, connection=self.connection, crs=None) return self.process( @@ -2809,6 +2816,8 @@ def unflatten_dimension(self, dimension: str, target_dimensions: List[str], labe label_separator=label_separator, ), ) + + def _get_geometry_argument( argument: Union[ shapely.geometry.base.BaseGeometry,