@@ -175,7 +175,7 @@ def load_collection(
175175 bands : Union [Iterable [str ], Parameter , str , None ] = None ,
176176 fetch_metadata : bool = True ,
177177 properties : Union [
178- None , Dict [str , Union [str , PGNode , typing .Callable ]], List [CollectionProperty ], CollectionProperty
178+ Dict [str , Union [PGNode , typing .Callable ]], List [CollectionProperty ], CollectionProperty , None
179179 ] = None ,
180180 max_cloud_cover : Optional [float ] = None ,
181181 ) -> DataCube :
@@ -250,27 +250,13 @@ def load_collection(
250250 metadata = metadata .filter_bands (bands )
251251 arguments ['bands' ] = bands
252252
253- if isinstance (properties , list ):
254- # TODO: warn about items that are not CollectionProperty objects instead of silently dropping them.
255- properties = {p .name : p .from_node () for p in properties if isinstance (p , CollectionProperty )}
256- if isinstance (properties , CollectionProperty ):
257- properties = {properties .name : properties .from_node ()}
258- elif properties is None :
259- properties = {}
260- if max_cloud_cover :
261- properties ["eo:cloud_cover" ] = lambda v : v <= max_cloud_cover
262- if properties :
263- summaries = metadata and metadata .get ("summaries" ) or {}
264- undefined_properties = set (properties .keys ()).difference (summaries .keys ())
265- if undefined_properties :
266- warnings .warn (
267- f"{ collection_id } property filtering with properties that are undefined "
268- f"in the collection metadata (summaries): { ', ' .join (undefined_properties )} ." ,
269- stacklevel = 2 ,
270- )
271- arguments ["properties" ] = {
272- prop : build_child_callback (pred , parent_parameters = ["value" ]) for prop , pred in properties .items ()
273- }
253+ properties = cls ._build_load_properties_argument (
254+ properties = properties ,
255+ supported_properties = (metadata .get ("summaries" , default = {}).keys () if metadata else None ),
256+ max_cloud_cover = max_cloud_cover ,
257+ )
258+ if properties is not None :
259+ arguments ["properties" ] = properties
274260
275261 pg = PGNode (
276262 process_id = 'load_collection' ,
@@ -282,6 +268,50 @@ def load_collection(
282268 load_collection , name = "create_collection" , since = "0.4.6"
283269 )
284270
271+ @classmethod
272+ def _build_load_properties_argument (
273+ cls ,
274+ properties : Union [
275+ Dict [str , Union [PGNode , typing .Callable ]],
276+ List [CollectionProperty ],
277+ CollectionProperty ,
278+ None ,
279+ ],
280+ * ,
281+ supported_properties : Optional [typing .Collection [str ]] = None ,
282+ max_cloud_cover : Optional [float ] = None ,
283+ ) -> Union [Dict [str , PGNode ], None ]:
284+ """
285+ Helper to convert/build the ``properties`` argument
286+ for ``load_collection`` and ``load_stac`` from user input
287+ """
288+ if isinstance (properties , CollectionProperty ):
289+ properties = [properties ]
290+ if isinstance (properties , list ):
291+ if not all (isinstance (p , CollectionProperty ) for p in properties ):
292+ raise ValueError (
293+ f"When providing load properties as a list, all items must be CollectionProperty objects, but got { properties } "
294+ )
295+ properties = {p .name : p .from_node () for p in properties }
296+
297+ if max_cloud_cover is not None :
298+ properties = properties or {}
299+ properties ["eo:cloud_cover" ] = lambda v : v <= max_cloud_cover
300+
301+ if isinstance (properties , dict ):
302+ if supported_properties :
303+ unsupported_properties = set (properties .keys ()).difference (supported_properties )
304+ if unsupported_properties :
305+ warnings .warn (
306+ f"Property filtering with unsupported properties according to collection/STAC metadata: { unsupported_properties } (supported: { supported_properties } )." ,
307+ stacklevel = 3 ,
308+ )
309+ properties = {
310+ prop : build_child_callback (pred , parent_parameters = ["value" ]) for prop , pred in properties .items ()
311+ }
312+
313+ return properties
314+
285315 @classmethod
286316 @deprecated (reason = "Depends on non-standard process, replace with :py:meth:`openeo.rest.connection.Connection.load_stac` where possible." ,version = "0.25.0" )
287317 def load_disk_collection (cls , connection : Connection , file_format : str , glob_pattern : str , ** options ) -> DataCube :
@@ -314,7 +344,9 @@ def load_stac(
314344 spatial_extent : Union [dict , Parameter , shapely .geometry .base .BaseGeometry , str , pathlib .Path , None ] = None ,
315345 temporal_extent : Union [Sequence [InputDate ], Parameter , str , None ] = None ,
316346 bands : Union [Iterable [str ], Parameter , str , None ] = None ,
317- properties : Optional [Dict [str , Union [str , PGNode , Callable ]]] = None ,
347+ properties : Union [
348+ Dict [str , Union [PGNode , typing .Callable ]], List [CollectionProperty ], CollectionProperty , None
349+ ] = None ,
318350 connection : Optional [Connection ] = None ,
319351 ) -> DataCube :
320352 """
@@ -409,14 +441,19 @@ def load_stac(
409441 The value must be a condition (user-defined process) to be evaluated against a STAC API.
410442 This parameter is not supported for static STAC.
411443
444+ See :py:func:`~openeo.rest.graph_building.collection_property` for easy construction of property filters.
445+
412446 :param connection: The connection to use to connect with the backend.
413447
414448 .. versionadded:: 0.33.0
415449
416450 .. versionchanged:: 0.37.0
417451 Argument ``spatial_extent``: add support for passing a Shapely geometry or a local path to a GeoJSON file.
452+
453+ .. versionchanged:: 0.41.0
454+ Add support for :py:func:`~openeo.rest.graph_building.collection_property` based property filters
418455 """
419- arguments = {"url" : url }
456+ arguments : dict = {"url" : url }
420457 if spatial_extent :
421458 arguments ["spatial_extent" ] = _get_geometry_argument (
422459 argument = spatial_extent ,
@@ -434,10 +471,11 @@ def load_stac(
434471 bands = cls ._get_bands (bands , process_id = "load_stac" )
435472 if bands is not None :
436473 arguments ["bands" ] = bands
437- if properties :
438- arguments ["properties" ] = {
439- prop : build_child_callback (pred , parent_parameters = ["value" ]) for prop , pred in properties .items ()
440- }
474+
475+ properties = cls ._build_load_properties_argument (properties = properties )
476+ if properties is not None :
477+ arguments ["properties" ] = properties
478+
441479 graph = PGNode ("load_stac" , arguments = arguments )
442480 try :
443481 metadata = metadata_from_stac (url )
0 commit comments