@@ -143,7 +143,7 @@ def load_collection(
143
143
cls ,
144
144
collection_id : Union [str , Parameter ],
145
145
connection : Optional [Connection ] = None ,
146
- spatial_extent : Union [Dict [ str , float ], Parameter , shapely .geometry .base .BaseGeometry , None ] = None ,
146
+ spatial_extent : Union [dict , Parameter , shapely .geometry .base .BaseGeometry , str , pathlib . Path , None ] = None ,
147
147
temporal_extent : Union [Sequence [InputDate ], Parameter , str , None ] = None ,
148
148
bands : Union [Iterable [str ], Parameter , str , None ] = None ,
149
149
fetch_metadata : bool = True ,
@@ -161,8 +161,8 @@ def load_collection(
161
161
:param spatial_extent: limit data to specified bounding box or polygons. Can be provided in different ways:
162
162
- a bounding box dictionary
163
163
- a Shapely geometry object
164
- - a GeoJSON-style dictionary,
165
- - a path (:py:class:`str` or :py:class:`~pathlib.Path`) to a local, client-side GeoJSON file,
164
+ - a GeoJSON-style dictionary
165
+ - a path (as :py:class:`str` or :py:class:`~pathlib.Path`) to a local, client-side GeoJSON file,
166
166
which will be loaded automatically to get the geometries as GeoJSON construct.
167
167
- a :py:class:`~openeo.api.process.Parameter` instance.
168
168
:param temporal_extent: limit data to specified temporal interval.
@@ -185,27 +185,20 @@ def load_collection(
185
185
Add :py:func:`~openeo.rest.graph_building.collection_property` support to ``properties`` argument.
186
186
187
187
.. versionchanged:: 0.37.0
188
- Add support for passing a Shapely geometry or a local path to a GeoJSON file to the ``spatial_extent`` argument .
188
+ Argument ``spatial_extent``: add support for passing a Shapely geometry or a local path to a GeoJSON file.
189
189
"""
190
190
if temporal_extent :
191
191
temporal_extent = cls ._get_temporal_extent (extent = temporal_extent )
192
-
193
- if isinstance (spatial_extent , Parameter ):
194
- if not schema_supports (spatial_extent .schema , type = "object" ):
195
- warnings .warn (
196
- "Unexpected parameterized `spatial_extent` in `load_collection`:"
197
- f" expected schema compatible with type 'object' but got { spatial_extent .schema !r} ."
198
- )
199
- elif spatial_extent is None or (
200
- isinstance (spatial_extent , dict ) and spatial_extent .keys () & {"west" , "east" , "north" , "south" }
201
- ):
202
- pass
203
- else :
204
- valid_geojson_types = [
205
- "Polygon" , "MultiPolygon" , "Feature" , "FeatureCollection"
206
- ]
207
- spatial_extent = _get_geometry_argument (argument = spatial_extent , valid_geojson_types = valid_geojson_types ,
208
- connection = connection )
192
+ spatial_extent = _get_geometry_argument (
193
+ argument = spatial_extent ,
194
+ valid_geojson_types = ["Polygon" , "MultiPolygon" , "Feature" , "FeatureCollection" ],
195
+ connection = connection ,
196
+ allow_none = True ,
197
+ allow_parameter = True ,
198
+ allow_bounding_box = True ,
199
+ argument_name = "spatial_extent" ,
200
+ process_id = "load_collection" ,
201
+ )
209
202
210
203
arguments = {
211
204
'id' : collection_id ,
@@ -392,9 +385,18 @@ def load_stac(
392
385
393
386
"""
394
387
arguments = {"url" : url }
395
- # TODO #425 more normalization/validation of extent/band parameters
396
388
if spatial_extent :
397
- arguments ["spatial_extent" ] = spatial_extent
389
+ arguments ["spatial_extent" ] = _get_geometry_argument (
390
+ argument = spatial_extent ,
391
+ valid_geojson_types = ["Polygon" , "MultiPolygon" , "Feature" , "FeatureCollection" ],
392
+ connection = connection ,
393
+ allow_none = True ,
394
+ allow_parameter = True ,
395
+ allow_bounding_box = True ,
396
+ argument_name = "spatial_extent" ,
397
+ process_id = "load_stac" ,
398
+ )
399
+
398
400
if temporal_extent :
399
401
arguments ["temporal_extent" ] = DataCube ._get_temporal_extent (extent = temporal_extent )
400
402
bands = cls ._get_bands (bands , process_id = "load_stac" )
@@ -2892,23 +2894,47 @@ def _get_geometry_argument(
2892
2894
Parameter ,
2893
2895
_FromNodeMixin ,
2894
2896
],
2897
+ * ,
2895
2898
valid_geojson_types : List [str ],
2896
2899
connection : Connection = None ,
2897
2900
crs : Optional [str ] = None ,
2898
- ) -> Union [dict , Parameter , PGNode ]:
2901
+ allow_parameter : bool = True ,
2902
+ allow_bounding_box : bool = False ,
2903
+ allow_none : bool = False ,
2904
+ argument_name : str = "n/a" ,
2905
+ process_id : str = "n/a" ,
2906
+ ) -> Union [dict , Parameter , PGNode , _FromNodeMixin , None ]:
2899
2907
"""
2900
- Convert input to a geometry as "geojson" subtype object or vectorcube .
2908
+ Convert input to a geometry as "geojson" subtype object or vector cube .
2901
2909
2902
2910
:param crs: value that encodes a coordinate reference system.
2903
2911
See :py:func:`openeo.util.normalize_crs` for more details about additional normalization that is applied to this argument.
2912
+ :param allow_parameter: allow argument to be a :py:class:`Parameter` instance, and pass-through as such
2913
+ :param allow_none: allow argument to be ``None`` and pass-through as such
2914
+ :param allow_bounding_box: allow argument to be a bounding box dictionary and pass-through as such
2904
2915
"""
2905
- if isinstance (argument , Parameter ):
2916
+ # Some quick exit shortcuts
2917
+ if allow_parameter and isinstance (argument , Parameter ):
2918
+ if not schema_supports (argument .schema , type = "object" ):
2919
+ warnings .warn (
2920
+ f"Unexpected parameterized `{ argument_name } ` in `{ process_id } `:"
2921
+ f" expected schema compatible with type 'object' but got { argument .schema !r} ."
2922
+ )
2906
2923
return argument
2907
2924
elif isinstance (argument , _FromNodeMixin ):
2925
+ # Typical use case here: VectorCube instance
2908
2926
return argument .from_node ()
2927
+ elif allow_none and argument is None :
2928
+ return argument
2929
+ elif (
2930
+ allow_bounding_box
2931
+ and isinstance (argument , dict )
2932
+ and all (k in argument for k in ["west" , "south" , "east" , "north" ])
2933
+ ):
2934
+ return argument
2909
2935
2936
+ # Support URL based geometry references (with `load_url` and best-effort format guess)
2910
2937
if isinstance (argument , str ) and re .match (r"^https?://" , argument , flags = re .I ):
2911
- # Geometry provided as URL: load with `load_url` (with best-effort format guess)
2912
2938
url = urllib .parse .urlparse (argument )
2913
2939
suffix = pathlib .Path (url .path .lower ()).suffix
2914
2940
format = {
@@ -2919,7 +2945,8 @@ def _get_geometry_argument(
2919
2945
".geoparquet" : "Parquet" ,
2920
2946
}.get (suffix , suffix .split ("." )[- 1 ])
2921
2947
return connection .load_url (url = argument , format = format )
2922
- #
2948
+
2949
+ # Support loading GeoJSON from local files
2923
2950
if (
2924
2951
isinstance (argument , (str , pathlib .Path ))
2925
2952
and pathlib .Path (argument ).is_file ()
@@ -2933,6 +2960,8 @@ def _get_geometry_argument(
2933
2960
else :
2934
2961
raise OpenEoClientException (f"Invalid geometry argument: { argument !r} " )
2935
2962
2963
+ # The assumption at this point is that we are working with a GeoJSON style dictionary
2964
+ assert isinstance (geometry , dict )
2936
2965
if geometry .get ("type" ) not in valid_geojson_types :
2937
2966
raise OpenEoClientException ("Invalid geometry type {t!r}, must be one of {s}" .format (
2938
2967
t = geometry .get ("type" ), s = valid_geojson_types
0 commit comments