Skip to content

Commit 76e4dca

Browse files
pjhartzellgadomski
andauthored
Add filter to Item and Collection get_assets method (#936)
* feat: filter Item and Collection get_assets * chore: update CHANGELOG * refactor: reduce iterations over asset dictionary * refactor: simplify role checking logic * chore: update CHANGELOG Co-authored-by: Pete Gadomski <pete.gadomski@gmail.com> * fix: correct typing Co-authored-by: Pete Gadomski <pete.gadomski@gmail.com>
1 parent a66559a commit 76e4dca

File tree

8 files changed

+114
-10
lines changed

8 files changed

+114
-10
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
### Added
66

7+
- Add `media_type` and `role` filtering to Item and Collection `get_assets()` method ([#936](https://github.com/stac-utils/pystac/pull/936))
8+
- `Asset.has_role` ([#936](https://github.com/stac-utils/pystac/pull/936))
79
- Enum MediaType entry for flatgeobuf ([discussion](https://github.com/flatgeobuf/flatgeobuf/discussions/112#discussioncomment-4606721)) ([#938](https://github.com/stac-utils/pystac/pull/938))
810
- Update Grid Extension support to v1.1.0 and fix issue with grid:code prefix validation ([#925](https://github.com/stac-utils/pystac/pull/925))
911
- Adds custom `header` support to `DefaultStacIO` ([#889](https://github.com/stac-utils/pystac/pull/889))

pystac/asset.py

+14
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,20 @@ def clone(self) -> "Asset":
152152
extra_fields=deepcopy(self.extra_fields),
153153
)
154154

155+
def has_role(self, role: str) -> bool:
156+
"""Check if a role exists in the Asset role list.
157+
158+
Args:
159+
role: Role to check for existence.
160+
161+
Returns:
162+
bool: True if role exists, else False.
163+
"""
164+
if self.roles is None:
165+
return False
166+
else:
167+
return role in self.roles
168+
155169
@property
156170
def common_metadata(self) -> "CommonMetadata_Type":
157171
"""Access the asset's common metadata fields as a

pystac/collection.py

+23-4
Original file line numberDiff line numberDiff line change
@@ -675,13 +675,32 @@ def from_dict(
675675

676676
return collection
677677

678-
def get_assets(self) -> Dict[str, Asset]:
679-
"""Get this item's assets.
678+
def get_assets(
679+
self,
680+
media_type: Optional[Union[str, pystac.MediaType]] = None,
681+
role: Optional[str] = None,
682+
) -> Dict[str, Asset]:
683+
"""Get this collection's assets.
684+
685+
Args:
686+
media_type: If set, filter the assets such that only those with a
687+
matching ``media_type`` are returned.
688+
role: If set, filter the assets such that only those with a matching
689+
``role`` are returned.
680690
681691
Returns:
682-
Dict[str, Asset]: A copy of the dictionary of this item's assets.
692+
Dict[str, Asset]: A dictionary of assets that match ``media_type``
693+
and/or ``role`` if set or else all of this collection's assets.
683694
"""
684-
return dict(self.assets.items())
695+
if media_type is None and role is None:
696+
return dict(self.assets.items())
697+
assets = dict()
698+
for key, asset in self.assets.items():
699+
if (media_type is None or asset.media_type == media_type) and (
700+
role is None or asset.has_role(role)
701+
):
702+
assets[key] = asset
703+
return assets
685704

686705
def add_asset(self, key: str, asset: Asset) -> None:
687706
"""Adds an Asset to this item.

pystac/item.py

+22-3
Original file line numberDiff line numberDiff line change
@@ -221,13 +221,32 @@ def set_datetime(self, datetime: Datetime, asset: Optional[Asset] = None) -> Non
221221
else:
222222
asset.extra_fields["datetime"] = datetime_to_str(datetime)
223223

224-
def get_assets(self) -> Dict[str, Asset]:
224+
def get_assets(
225+
self,
226+
media_type: Optional[Union[str, pystac.MediaType]] = None,
227+
role: Optional[str] = None,
228+
) -> Dict[str, Asset]:
225229
"""Get this item's assets.
226230
231+
Args:
232+
media_type: If set, filter the assets such that only those with a
233+
matching ``media_type`` are returned.
234+
role: If set, filter the assets such that only those with a matching
235+
``role`` are returned.
236+
227237
Returns:
228-
Dict[str, Asset]: A copy of the dictionary of this item's assets.
238+
Dict[str, Asset]: A dictionary of assets that match ``media_type``
239+
and/or ``role`` if set or else all of this item's assets.
229240
"""
230-
return dict(self.assets.items())
241+
if media_type is None and role is None:
242+
return dict(self.assets.items())
243+
assets = dict()
244+
for key, asset in self.assets.items():
245+
if (media_type is None or asset.media_type == media_type) and (
246+
role is None or asset.has_role(role)
247+
):
248+
assets[key] = asset
249+
return assets
231250

232251
def add_asset(self, key: str, asset: Asset) -> None:
233252
"""Adds an Asset to this item.

tests/data-files/collections/with-assets.json

+4-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,10 @@
3030
"thumbnail": {
3131
"href": "http://example.com/thumbnail.png",
3232
"title": "thumbnail",
33-
"media_type": "image/png"
33+
"type": "image/png",
34+
"roles": [
35+
"thumbnail"
36+
]
3437
}
3538
},
3639
"extent": {

tests/data-files/item/sample-item.json

+11-2
Original file line numberDiff line numberDiff line change
@@ -54,11 +54,20 @@
5454
"analytic": {
5555
"href": "http://cool-sat.com/catalog/CS3-20160503_132130_04/analytic.tif",
5656
"title": "4-Band Analytic",
57-
"product": "http://cool-sat.com/catalog/products/analytic.json"
57+
"product": "http://cool-sat.com/catalog/products/analytic.json",
58+
"type": "image/tiff; application=geotiff; profile=cloud-optimized",
59+
"roles": [
60+
"data",
61+
"analytic"
62+
]
5863
},
5964
"thumbnail": {
6065
"href": "http://cool-sat.com/catalog/CS3-20160503_132130_04/thumbnail.png",
61-
"title": "Thumbnail"
66+
"title": "Thumbnail",
67+
"type": "image/png",
68+
"roles": [
69+
"thumbnail"
70+
]
6271
}
6372
},
6473
"bbox": [

tests/test_collection.py

+19
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,25 @@ def test_assets(self) -> None:
226226
collection = pystac.Collection.from_dict(data)
227227
collection.validate()
228228

229+
def test_get_assets(self) -> None:
230+
collection = pystac.Collection.from_file(
231+
TestCases.get_path("data-files/collections/with-assets.json")
232+
)
233+
234+
media_type_filter = collection.get_assets(media_type=pystac.MediaType.PNG)
235+
self.assertCountEqual(media_type_filter.keys(), ["thumbnail"])
236+
role_filter = collection.get_assets(role="thumbnail")
237+
self.assertCountEqual(role_filter.keys(), ["thumbnail"])
238+
multi_filter = collection.get_assets(
239+
media_type=pystac.MediaType.PNG, role="thumbnail"
240+
)
241+
self.assertCountEqual(multi_filter.keys(), ["thumbnail"])
242+
243+
no_filter = collection.get_assets()
244+
self.assertCountEqual(no_filter.keys(), ["thumbnail"])
245+
no_assets = collection.get_assets(media_type=pystac.MediaType.HDF)
246+
self.assertEqual(no_assets, {})
247+
229248
def test_removing_optional_attributes(self) -> None:
230249
path = TestCases.get_path("data-files/collections/with-assets.json")
231250
with open(path, "r") as file:

tests/test_item.py

+19
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,25 @@ def test_null_datetime(self) -> None:
161161

162162
null_dt_item.validate()
163163

164+
def test_get_assets(self) -> None:
165+
item = pystac.Item.from_file(
166+
TestCases.get_path("data-files/item/sample-item.json")
167+
)
168+
169+
media_type_filter = item.get_assets(media_type=pystac.MediaType.COG)
170+
self.assertCountEqual(media_type_filter.keys(), ["analytic"])
171+
role_filter = item.get_assets(role="data")
172+
self.assertCountEqual(role_filter.keys(), ["analytic"])
173+
multi_filter = item.get_assets(
174+
media_type=pystac.MediaType.PNG, role="thumbnail"
175+
)
176+
self.assertCountEqual(multi_filter.keys(), ["thumbnail"])
177+
178+
no_filter = item.get_assets()
179+
self.assertCountEqual(no_filter.keys(), ["analytic", "thumbnail"])
180+
no_assets = item.get_assets(media_type=pystac.MediaType.HDF)
181+
self.assertEqual(no_assets, {})
182+
164183
def test_get_set_asset_datetime(self) -> None:
165184
item = pystac.Item.from_file(
166185
TestCases.get_path("data-files/item/sample-item-asset-properties.json")

0 commit comments

Comments
 (0)