@@ -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 ,
@@ -390,11 +383,22 @@ def load_stac(
390
383
391
384
.. versionadded:: 0.33.0
392
385
386
+ .. versionchanged:: 0.37.0
387
+ Argument ``spatial_extent``: add support for passing a Shapely geometry or a local path to a GeoJSON file.
393
388
"""
394
389
arguments = {"url" : url }
395
- # TODO #425 more normalization/validation of extent/band parameters
396
390
if spatial_extent :
397
- arguments ["spatial_extent" ] = spatial_extent
391
+ arguments ["spatial_extent" ] = _get_geometry_argument (
392
+ argument = spatial_extent ,
393
+ valid_geojson_types = ["Polygon" , "MultiPolygon" , "Feature" , "FeatureCollection" ],
394
+ connection = connection ,
395
+ allow_none = True ,
396
+ allow_parameter = True ,
397
+ allow_bounding_box = True ,
398
+ argument_name = "spatial_extent" ,
399
+ process_id = "load_stac" ,
400
+ )
401
+
398
402
if temporal_extent :
399
403
arguments ["temporal_extent" ] = DataCube ._get_temporal_extent (extent = temporal_extent )
400
404
bands = cls ._get_bands (bands , process_id = "load_stac" )
@@ -2892,23 +2896,47 @@ def _get_geometry_argument(
2892
2896
Parameter ,
2893
2897
_FromNodeMixin ,
2894
2898
],
2899
+ * ,
2895
2900
valid_geojson_types : List [str ],
2896
2901
connection : Connection = None ,
2897
2902
crs : Optional [str ] = None ,
2898
- ) -> Union [dict , Parameter , PGNode ]:
2903
+ allow_parameter : bool = True ,
2904
+ allow_bounding_box : bool = False ,
2905
+ allow_none : bool = False ,
2906
+ argument_name : str = "n/a" ,
2907
+ process_id : str = "n/a" ,
2908
+ ) -> Union [dict , Parameter , PGNode , _FromNodeMixin , None ]:
2899
2909
"""
2900
- Convert input to a geometry as "geojson" subtype object or vectorcube .
2910
+ Convert input to a geometry as "geojson" subtype object or vector cube .
2901
2911
2902
2912
:param crs: value that encodes a coordinate reference system.
2903
2913
See :py:func:`openeo.util.normalize_crs` for more details about additional normalization that is applied to this argument.
2914
+ :param allow_parameter: allow argument to be a :py:class:`Parameter` instance, and pass-through as such
2915
+ :param allow_none: allow argument to be ``None`` and pass-through as such
2916
+ :param allow_bounding_box: allow argument to be a bounding box dictionary and pass-through as such
2904
2917
"""
2905
- if isinstance (argument , Parameter ):
2918
+ # Some quick exit shortcuts
2919
+ if allow_parameter and isinstance (argument , Parameter ):
2920
+ if not schema_supports (argument .schema , type = "object" ):
2921
+ warnings .warn (
2922
+ f"Unexpected parameterized `{ argument_name } ` in `{ process_id } `:"
2923
+ f" expected schema compatible with type 'object' but got { argument .schema !r} ."
2924
+ )
2906
2925
return argument
2907
2926
elif isinstance (argument , _FromNodeMixin ):
2927
+ # Typical use case here: VectorCube instance
2908
2928
return argument .from_node ()
2929
+ elif allow_none and argument is None :
2930
+ return argument
2931
+ elif (
2932
+ allow_bounding_box
2933
+ and isinstance (argument , dict )
2934
+ and all (k in argument for k in ["west" , "south" , "east" , "north" ])
2935
+ ):
2936
+ return argument
2909
2937
2938
+ # Support URL based geometry references (with `load_url` and best-effort format guess)
2910
2939
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
2940
url = urllib .parse .urlparse (argument )
2913
2941
suffix = pathlib .Path (url .path .lower ()).suffix
2914
2942
format = {
@@ -2919,7 +2947,8 @@ def _get_geometry_argument(
2919
2947
".geoparquet" : "Parquet" ,
2920
2948
}.get (suffix , suffix .split ("." )[- 1 ])
2921
2949
return connection .load_url (url = argument , format = format )
2922
- #
2950
+
2951
+ # Support loading GeoJSON from local files
2923
2952
if (
2924
2953
isinstance (argument , (str , pathlib .Path ))
2925
2954
and pathlib .Path (argument ).is_file ()
@@ -2933,6 +2962,8 @@ def _get_geometry_argument(
2933
2962
else :
2934
2963
raise OpenEoClientException (f"Invalid geometry argument: { argument !r} " )
2935
2964
2965
+ # The assumption at this point is that we are working with a GeoJSON style dictionary
2966
+ assert isinstance (geometry , dict )
2936
2967
if geometry .get ("type" ) not in valid_geojson_types :
2937
2968
raise OpenEoClientException ("Invalid geometry type {t!r}, must be one of {s}" .format (
2938
2969
t = geometry .get ("type" ), s = valid_geojson_types
0 commit comments