Skip to content

Commit f9e5bd6

Browse files
jsignellgadomski
andauthored
Xarray Assets Extension class (#1161)
* Add XarrayAssetsExtension class * Add tests and docs * Add some `open` tests * Docstring for open * Add xpystac to test deps * Update pystac/extensions/xarray_assets.py Co-authored-by: Pete Gadomski <pete.gadomski@gmail.com> * Remove open method * Update changelog * Fix copypasta --------- Co-authored-by: Pete Gadomski <pete.gadomski@gmail.com>
1 parent dd999cd commit f9e5bd6

File tree

9 files changed

+445
-0
lines changed

9 files changed

+445
-0
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
- `KML` as a built in media type ([#1127](https://github.com/stac-utils/pystac/issues/1127))
2121
- `move/copy/delete` operations for local Assets ([#1158](https://github.com/stac-utils/pystac/issues/1158))
2222
- Latest core STAC spec jsonshemas are included in pytstac and used for validation ([#1165](https://github.com/stac-utils/pystac/pull/1165))
23+
- Xarray Assets Extension class ([#1161](https://github.com/stac-utils/pystac/pull/1161))
2324

2425
### Changed
2526

docs/api.rst

+1
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ PySTAC provides support for the following STAC Extensions:
107107
* :mod:`Timestamps <pystac.extensions.timestamps>`
108108
* :mod:`Versioning Indicators <pystac.extensions.version>`
109109
* :mod:`View Geometry <pystac.extensions.view>`
110+
* :mod:`Xarray Assets <pystac.extensions.xarray_assets>`
110111

111112
The following classes are used internally to implement these extensions and may be used
112113
to create custom implementations of STAC Extensions not supported by the library (see

docs/api/extensions.rst

+1
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,4 @@ pystac.extensions
3232
timestamps.TimestampsExtension
3333
version.VersionExtension
3434
view.ViewExtension
35+
xarray_assets.XarrayAssetsExtension

docs/api/extensions/xarray_assets.rst

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
pystac.extensions.xarray_assets
2+
===============================
3+
4+
.. automodule:: pystac.extensions.xarray_assets
5+
:members:
6+
:undoc-members:

pystac/__init__.py

+2
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@
104104
import pystac.extensions.timestamps
105105
import pystac.extensions.version
106106
import pystac.extensions.view
107+
import pystac.extensions.xarray_assets
107108

108109
EXTENSION_HOOKS = pystac.extensions.hooks.RegisteredExtensionHooks(
109110
[
@@ -126,6 +127,7 @@
126127
pystac.extensions.timestamps.TIMESTAMPS_EXTENSION_HOOKS,
127128
pystac.extensions.version.VERSION_EXTENSION_HOOKS,
128129
pystac.extensions.view.VIEW_EXTENSION_HOOKS,
130+
pystac.extensions.xarray_assets.XARRAY_ASSETS_EXTENSION_HOOKS,
129131
]
130132
)
131133

pystac/extensions/xarray_assets.py

+162
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
"""Implements the :stac-ext:`Xarray Assets Extension <xarray>`."""
2+
3+
from __future__ import annotations
4+
5+
from typing import Any, Dict, Generic, List, Optional, TypeVar, Union
6+
7+
import pystac
8+
from pystac.extensions.base import ExtensionManagementMixin, PropertiesExtension
9+
from pystac.extensions.hooks import ExtensionHooks
10+
11+
T = TypeVar("T", pystac.Collection, pystac.Item, pystac.Asset)
12+
13+
SCHEMA_URI = "https://stac-extensions.github.io/xarray-assets/v1.0.0/schema.json"
14+
15+
PREFIX: str = "xarray:"
16+
OPEN_KWARGS_PROP = PREFIX + "open_kwargs"
17+
STORAGE_OPTIONS_PROP = PREFIX + "storage_options"
18+
19+
20+
class XarrayAssetsExtension(
21+
Generic[T],
22+
PropertiesExtension,
23+
ExtensionManagementMixin[Union[pystac.Collection, pystac.Item]],
24+
):
25+
"""An abstract class that can be used to extend the properties of a
26+
:class:`~pystac.Collection`, :class:`~pystac.Item`, or :class:`~pystac.Asset` with
27+
properties from the :stac-ext:`Xarray Assets Extension <xarray>`. This class is
28+
generic over the type of STAC Object to be extended (e.g. :class:`~pystac.Item`,
29+
:class:`~pystac.Asset`).
30+
31+
To create a concrete instance of :class:`XarrayAssetsExtension`, use the
32+
:meth:`XarrayAssetsExtension.ext` method. For example:
33+
34+
.. code-block:: python
35+
36+
>>> item: pystac.Item = ...
37+
>>> xr_ext = XarrayAssetsExtension.ext(item)
38+
39+
"""
40+
41+
@classmethod
42+
def get_schema_uri(cls) -> str:
43+
return SCHEMA_URI
44+
45+
@classmethod
46+
def ext(cls, obj: T, add_if_missing: bool = False) -> XarrayAssetsExtension[T]:
47+
"""Extend the given STAC Object with properties from the
48+
:stac-ext:`XarrayAssets Extension <xarray>`.
49+
50+
This extension can be applied to instances of :class:`~pystac.Collection`,
51+
:class:`~pystac.Item` or :class:`~pystac.Asset`.
52+
53+
Raises:
54+
pystac.ExtensionTypeError : If an invalid object type is passed.
55+
"""
56+
if isinstance(obj, pystac.Collection):
57+
cls.validate_has_extension(obj, add_if_missing)
58+
return CollectionXarrayAssetsExtension(obj)
59+
if isinstance(obj, pystac.Item):
60+
cls.validate_has_extension(obj, add_if_missing)
61+
return ItemXarrayAssetsExtension(obj)
62+
if isinstance(obj, pystac.Asset):
63+
cls.validate_owner_has_extension(obj, add_if_missing)
64+
return AssetXarrayAssetsExtension(obj)
65+
else:
66+
raise pystac.ExtensionTypeError(cls._ext_error_message(obj))
67+
68+
69+
class CollectionXarrayAssetsExtension(XarrayAssetsExtension[pystac.Collection]):
70+
"""A concrete implementation of :class:`XarrayAssetsExtension` on a
71+
:class:`~pystac.Collection` that extends the properties of the Item to include
72+
properties defined in the :stac-ext:`XarrayAssets Extension <xarray>`.
73+
74+
This class should generally not be instantiated directly. Instead, call
75+
:meth:`XarrayAssetsExtension.ext` on an :class:`~pystac.Collection` to extend it.
76+
"""
77+
78+
collection: pystac.Collection
79+
properties: Dict[str, Any]
80+
81+
def __init__(self, collection: pystac.Collection):
82+
self.collection = collection
83+
self.properties = collection.extra_fields
84+
85+
def __repr__(self) -> str:
86+
return "<CollectionXarrayAssetsExtension Item id={}>".format(self.collection.id)
87+
88+
89+
class ItemXarrayAssetsExtension(XarrayAssetsExtension[pystac.Item]):
90+
"""A concrete implementation of :class:`XarrayAssetsExtension` on an
91+
:class:`~pystac.Item` that extends the properties of the Item to include properties
92+
defined in the :stac-ext:`XarrayAssets Extension <xarray>`.
93+
94+
This class should generally not be instantiated directly. Instead, call
95+
:meth:`XarrayAssetsExtension.ext` on an :class:`~pystac.Item` to extend it.
96+
"""
97+
98+
item: pystac.Item
99+
properties: Dict[str, Any]
100+
101+
def __init__(self, item: pystac.Item):
102+
self.item = item
103+
self.properties = item.properties
104+
105+
def __repr__(self) -> str:
106+
return "<ItemXarrayAssetsExtension Item id={}>".format(self.item.id)
107+
108+
109+
class AssetXarrayAssetsExtension(XarrayAssetsExtension[pystac.Asset]):
110+
"""A concrete implementation of :class:`XarrayAssetsExtension` on an
111+
:class:`~pystac.Asset` that extends the Asset fields to include properties defined
112+
in the :stac-ext:`XarrayAssets Extension <xarray>`.
113+
114+
This class should generally not be instantiated directly. Instead, call
115+
:meth:`XarrayAssetsExtension.ext` on an :class:`~pystac.Asset` to extend it.
116+
"""
117+
118+
asset: pystac.Asset
119+
properties: Dict[str, Any]
120+
additional_read_properties: Optional[List[Dict[str, Any]]] = None
121+
122+
def __init__(self, asset: pystac.Asset):
123+
self.asset = asset
124+
self.properties = asset.extra_fields
125+
if asset.owner and isinstance(asset.owner, pystac.Item):
126+
self.additional_read_properties = [asset.owner.properties]
127+
128+
@property
129+
def storage_options(self) -> Optional[Dict[str, Any]]:
130+
"""Additional keywords for accessing the dataset from remote storage"""
131+
return self.properties.get(STORAGE_OPTIONS_PROP)
132+
133+
@storage_options.setter
134+
def storage_options(self, v: Optional[Dict[str, Any]]) -> Any:
135+
if v is None:
136+
self.properties.pop(STORAGE_OPTIONS_PROP, None)
137+
else:
138+
self.properties[STORAGE_OPTIONS_PROP] = v
139+
140+
@property
141+
def open_kwargs(self) -> Optional[Dict[str, Any]]:
142+
"""Additional keywords for opening the dataset"""
143+
return self.properties.get(OPEN_KWARGS_PROP)
144+
145+
@open_kwargs.setter
146+
def open_kwargs(self, v: Optional[Dict[str, Any]]) -> Any:
147+
if v is None:
148+
self.properties.pop(OPEN_KWARGS_PROP, None)
149+
else:
150+
self.properties[OPEN_KWARGS_PROP] = v
151+
152+
def __repr__(self) -> str:
153+
return "<AssetXarrayAssetsExtension Asset href={}>".format(self.asset.href)
154+
155+
156+
class XarrayAssetsExtensionHooks(ExtensionHooks):
157+
schema_uri: str = SCHEMA_URI
158+
prev_extension_ids = {"xarray"}
159+
stac_object_types = {pystac.STACObjectType.COLLECTION, pystac.STACObjectType.ITEM}
160+
161+
162+
XARRAY_ASSETS_EXTENSION_HOOKS: ExtensionHooks = XarrayAssetsExtensionHooks()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
{
2+
"stac_version": "1.0.0",
3+
"stac_extensions": [
4+
"https://stac-extensions.github.io/xarray-assets/v1.0.0/schema.json"
5+
],
6+
"type": "Collection",
7+
"id": "collection",
8+
"title": "A title",
9+
"description": "A description",
10+
"license": "Apache-2.0",
11+
"extent": {
12+
"spatial": {
13+
"bbox": [
14+
[
15+
172.9,
16+
1.3,
17+
173,
18+
1.4
19+
]
20+
]
21+
},
22+
"temporal": {
23+
"interval": [
24+
[
25+
"2015-06-23T00:00:00Z",
26+
null
27+
]
28+
]
29+
}
30+
},
31+
"assets": {
32+
"example": {
33+
"href": "abfs://cpdata/raw/terraclimate/4000m/raster.zarr",
34+
"type": "application/vnd+zarr",
35+
"xarray:storage_options": {
36+
"account_name": "cpdataeuwest"
37+
},
38+
"xarray:open_kwargs": {
39+
"consolidated": true
40+
}
41+
}
42+
},
43+
"summaries": {
44+
"datetime": {
45+
"minimum": "2015-06-23T00:00:00Z",
46+
"maximum": "2019-07-10T13:44:56Z"
47+
}
48+
},
49+
"links": [
50+
{
51+
"href": "./collection.json",
52+
"rel": "root",
53+
"title": "A title",
54+
"type": "application/json"
55+
},
56+
{
57+
"href": "./item.json",
58+
"rel": "item"
59+
}
60+
]
61+
}
+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
{
2+
"stac_version": "1.0.0",
3+
"stac_extensions": [
4+
"https://stac-extensions.github.io/xarray-assets/v1.0.0/schema.json"
5+
],
6+
"type": "Feature",
7+
"id": "item",
8+
"bbox": [
9+
172.9,
10+
1.3,
11+
173,
12+
1.4
13+
],
14+
"geometry": {
15+
"type": "Polygon",
16+
"coordinates": [
17+
[
18+
[
19+
172.9,
20+
1.3
21+
],
22+
[
23+
173,
24+
1.3
25+
],
26+
[
27+
173,
28+
1.4
29+
],
30+
[
31+
172.9,
32+
1.4
33+
],
34+
[
35+
172.9,
36+
1.3
37+
]
38+
]
39+
]
40+
},
41+
"properties": {
42+
"datetime": "2020-12-11T22:38:32Z"
43+
},
44+
"links": [],
45+
"assets": {
46+
"data": {
47+
"href": "abfs://example.com/examples/file.zarr",
48+
"xarray:storage_options": {
49+
"account_name": "test-account"
50+
},
51+
"xarray:open_kwargs": {
52+
"consolidated": true
53+
}
54+
}
55+
}
56+
}

0 commit comments

Comments
 (0)