pystac-core 1.15.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pystac/__init__.py +260 -0
- pystac/asset.py +395 -0
- pystac/cache.py +349 -0
- pystac/catalog.py +1307 -0
- pystac/client.py +23 -0
- pystac/collection.py +899 -0
- pystac/common_metadata.py +251 -0
- pystac/errors.py +119 -0
- pystac/extensions/__init__.py +3 -0
- pystac/extensions/base.py +270 -0
- pystac/extensions/ext.py +458 -0
- pystac/extensions/hooks.py +111 -0
- pystac/html/JSON.jinja2 +57 -0
- pystac/html/Macros.jinja2 +34 -0
- pystac/html/__init__.py +3 -0
- pystac/html/jinja_env.py +15 -0
- pystac/item.py +539 -0
- pystac/item_assets.py +247 -0
- pystac/item_collection.py +257 -0
- pystac/layout.py +699 -0
- pystac/link.py +530 -0
- pystac/media_type.py +54 -0
- pystac/provider.py +122 -0
- pystac/py.typed +0 -0
- pystac/rel_type.py +29 -0
- pystac/serialization/__init__.py +14 -0
- pystac/serialization/common_properties.py +110 -0
- pystac/serialization/identify.py +300 -0
- pystac/serialization/migrate.py +193 -0
- pystac/stac_io.py +486 -0
- pystac/stac_object.py +701 -0
- pystac/static/__init__.py +0 -0
- pystac/static/fields-normalized.json +1 -0
- pystac/summaries.py +334 -0
- pystac/utils.py +627 -0
- pystac/validation/__init__.py +276 -0
- pystac/validation/jsonschemas/__init__.py +0 -0
- pystac/validation/jsonschemas/geojson/Feature.json +505 -0
- pystac/validation/jsonschemas/geojson/Geometry.json +218 -0
- pystac/validation/jsonschemas/stac-spec/v1.1.0/bands.json +24 -0
- pystac/validation/jsonschemas/stac-spec/v1.1.0/basics.json +34 -0
- pystac/validation/jsonschemas/stac-spec/v1.1.0/catalog.json +57 -0
- pystac/validation/jsonschemas/stac-spec/v1.1.0/collection.json +230 -0
- pystac/validation/jsonschemas/stac-spec/v1.1.0/common.json +30 -0
- pystac/validation/jsonschemas/stac-spec/v1.1.0/data-values.json +84 -0
- pystac/validation/jsonschemas/stac-spec/v1.1.0/datetime.json +53 -0
- pystac/validation/jsonschemas/stac-spec/v1.1.0/instrument.json +32 -0
- pystac/validation/jsonschemas/stac-spec/v1.1.0/item.json +347 -0
- pystac/validation/jsonschemas/stac-spec/v1.1.0/licensing.json +12 -0
- pystac/validation/jsonschemas/stac-spec/v1.1.0/provider.json +47 -0
- pystac/validation/local_validator.py +126 -0
- pystac/validation/schema_uri_map.py +351 -0
- pystac/validation/stac_validator.py +304 -0
- pystac/version.py +72 -0
- pystac_core-1.15.0.dist-info/METADATA +24 -0
- pystac_core-1.15.0.dist-info/RECORD +57 -0
- pystac_core-1.15.0.dist-info/WHEEL +4 -0
pystac/__init__.py
ADDED
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
# isort: skip_file
|
|
2
|
+
"""
|
|
3
|
+
PySTAC is a library for working with SpatioTemporal Asset Catalogs (STACs)
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from pkgutil import extend_path
|
|
7
|
+
|
|
8
|
+
__path__ = extend_path(__path__, __name__)
|
|
9
|
+
|
|
10
|
+
__all__ = [
|
|
11
|
+
"__version__",
|
|
12
|
+
"TemplateError",
|
|
13
|
+
"STACError",
|
|
14
|
+
"STACTypeError",
|
|
15
|
+
"DuplicateObjectKeyError",
|
|
16
|
+
"ExtensionAlreadyExistsError",
|
|
17
|
+
"ExtensionNotImplemented",
|
|
18
|
+
"ExtensionTypeError",
|
|
19
|
+
"RequiredPropertyMissing",
|
|
20
|
+
"STACValidationError",
|
|
21
|
+
"DeprecatedWarning",
|
|
22
|
+
"MediaType",
|
|
23
|
+
"RelType",
|
|
24
|
+
"StacIO",
|
|
25
|
+
"STACObject",
|
|
26
|
+
"STACObjectType",
|
|
27
|
+
"Link",
|
|
28
|
+
"HIERARCHICAL_LINKS",
|
|
29
|
+
"Catalog",
|
|
30
|
+
"CatalogType",
|
|
31
|
+
"Collection",
|
|
32
|
+
"Extent",
|
|
33
|
+
"SpatialExtent",
|
|
34
|
+
"TemporalExtent",
|
|
35
|
+
"Summaries",
|
|
36
|
+
"CommonMetadata",
|
|
37
|
+
"RangeSummary",
|
|
38
|
+
"Item",
|
|
39
|
+
"Asset",
|
|
40
|
+
"ItemAssetDefinition",
|
|
41
|
+
"ItemCollection",
|
|
42
|
+
"Provider",
|
|
43
|
+
"ProviderRole",
|
|
44
|
+
"read_file",
|
|
45
|
+
"read_dict",
|
|
46
|
+
"write_file",
|
|
47
|
+
"get_stac_version",
|
|
48
|
+
"set_stac_version",
|
|
49
|
+
]
|
|
50
|
+
|
|
51
|
+
import warnings
|
|
52
|
+
from typing import Any
|
|
53
|
+
|
|
54
|
+
from pystac.errors import (
|
|
55
|
+
TemplateError,
|
|
56
|
+
STACError,
|
|
57
|
+
STACTypeError,
|
|
58
|
+
DuplicateObjectKeyError,
|
|
59
|
+
ExtensionAlreadyExistsError,
|
|
60
|
+
ExtensionNotImplemented,
|
|
61
|
+
ExtensionTypeError,
|
|
62
|
+
RequiredPropertyMissing,
|
|
63
|
+
STACValidationError,
|
|
64
|
+
DeprecatedWarning,
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
from pystac.version import (
|
|
68
|
+
__version__,
|
|
69
|
+
get_stac_version,
|
|
70
|
+
set_stac_version,
|
|
71
|
+
)
|
|
72
|
+
from pystac.media_type import MediaType
|
|
73
|
+
from pystac.rel_type import RelType
|
|
74
|
+
from pystac.stac_io import StacIO
|
|
75
|
+
from pystac.stac_object import STACObject, STACObjectType
|
|
76
|
+
from pystac.link import Link, HIERARCHICAL_LINKS
|
|
77
|
+
from pystac.catalog import Catalog, CatalogType
|
|
78
|
+
from pystac.collection import (
|
|
79
|
+
Collection,
|
|
80
|
+
Extent,
|
|
81
|
+
SpatialExtent,
|
|
82
|
+
TemporalExtent,
|
|
83
|
+
)
|
|
84
|
+
from pystac.common_metadata import CommonMetadata
|
|
85
|
+
from pystac.summaries import RangeSummary, Summaries
|
|
86
|
+
from pystac.asset import Asset
|
|
87
|
+
from pystac.item import Item
|
|
88
|
+
from pystac.item_assets import ItemAssetDefinition
|
|
89
|
+
from pystac.item_collection import ItemCollection
|
|
90
|
+
from pystac.provider import ProviderRole, Provider
|
|
91
|
+
from pystac.utils import HREF
|
|
92
|
+
|
|
93
|
+
import pystac.extensions.hooks
|
|
94
|
+
import pystac.extensions.classification
|
|
95
|
+
import pystac.extensions.datacube
|
|
96
|
+
import pystac.extensions.eo
|
|
97
|
+
import pystac.extensions.file
|
|
98
|
+
import pystac.extensions.grid
|
|
99
|
+
import pystac.extensions.item_assets
|
|
100
|
+
|
|
101
|
+
with warnings.catch_warnings():
|
|
102
|
+
warnings.filterwarnings("ignore", category=DeprecationWarning)
|
|
103
|
+
import pystac.extensions.label
|
|
104
|
+
import pystac.extensions.mgrs
|
|
105
|
+
import pystac.extensions.mlm
|
|
106
|
+
import pystac.extensions.pointcloud
|
|
107
|
+
import pystac.extensions.projection
|
|
108
|
+
import pystac.extensions.raster
|
|
109
|
+
import pystac.extensions.sar
|
|
110
|
+
import pystac.extensions.sat
|
|
111
|
+
import pystac.extensions.scientific
|
|
112
|
+
import pystac.extensions.storage
|
|
113
|
+
import pystac.extensions.table
|
|
114
|
+
import pystac.extensions.timestamps
|
|
115
|
+
import pystac.extensions.version
|
|
116
|
+
import pystac.extensions.view
|
|
117
|
+
import pystac.extensions.xarray_assets
|
|
118
|
+
|
|
119
|
+
EXTENSION_HOOKS = pystac.extensions.hooks.RegisteredExtensionHooks(
|
|
120
|
+
[
|
|
121
|
+
pystac.extensions.classification.CLASSIFICATION_EXTENSION_HOOKS,
|
|
122
|
+
pystac.extensions.datacube.DATACUBE_EXTENSION_HOOKS,
|
|
123
|
+
pystac.extensions.eo.EO_EXTENSION_HOOKS,
|
|
124
|
+
pystac.extensions.file.FILE_EXTENSION_HOOKS,
|
|
125
|
+
pystac.extensions.grid.GRID_EXTENSION_HOOKS,
|
|
126
|
+
pystac.extensions.item_assets.ITEM_ASSETS_EXTENSION_HOOKS,
|
|
127
|
+
pystac.extensions.label.LABEL_EXTENSION_HOOKS,
|
|
128
|
+
pystac.extensions.mgrs.MGRS_EXTENSION_HOOKS,
|
|
129
|
+
pystac.extensions.mlm.MLM_EXTENSION_HOOKS,
|
|
130
|
+
pystac.extensions.pointcloud.POINTCLOUD_EXTENSION_HOOKS,
|
|
131
|
+
pystac.extensions.projection.PROJECTION_EXTENSION_HOOKS,
|
|
132
|
+
pystac.extensions.raster.RASTER_EXTENSION_HOOKS,
|
|
133
|
+
pystac.extensions.sar.SAR_EXTENSION_HOOKS,
|
|
134
|
+
pystac.extensions.sat.SAT_EXTENSION_HOOKS,
|
|
135
|
+
pystac.extensions.scientific.SCIENTIFIC_EXTENSION_HOOKS,
|
|
136
|
+
pystac.extensions.storage.STORAGE_EXTENSION_HOOKS,
|
|
137
|
+
pystac.extensions.table.TABLE_EXTENSION_HOOKS,
|
|
138
|
+
pystac.extensions.timestamps.TIMESTAMPS_EXTENSION_HOOKS,
|
|
139
|
+
pystac.extensions.version.VERSION_EXTENSION_HOOKS,
|
|
140
|
+
pystac.extensions.view.VIEW_EXTENSION_HOOKS,
|
|
141
|
+
pystac.extensions.xarray_assets.XARRAY_ASSETS_EXTENSION_HOOKS,
|
|
142
|
+
]
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def read_file(href: HREF, stac_io: StacIO | None = None) -> STACObject:
|
|
147
|
+
"""Reads a STAC object from a file.
|
|
148
|
+
|
|
149
|
+
This method will return either a Catalog, a Collection, or an Item based on what
|
|
150
|
+
the file contains.
|
|
151
|
+
|
|
152
|
+
This is a convenience method for :meth:`StacIO.read_stac_object
|
|
153
|
+
<pystac.StacIO.read_stac_object>`
|
|
154
|
+
|
|
155
|
+
Args:
|
|
156
|
+
href : The HREF to read the object from.
|
|
157
|
+
stac_io: Optional :class:`~StacIO` instance to use for I/O operations. If not
|
|
158
|
+
provided, will use :meth:`StacIO.default` to create an instance.
|
|
159
|
+
|
|
160
|
+
Returns:
|
|
161
|
+
The specific STACObject implementation class that is represented
|
|
162
|
+
by the JSON read from the file located at HREF.
|
|
163
|
+
|
|
164
|
+
Raises:
|
|
165
|
+
STACTypeError : If the file at ``href`` does not represent a valid
|
|
166
|
+
:class:`~pystac.STACObject`. Note that an :class:`~pystac.ItemCollection`
|
|
167
|
+
is not a :class:`~pystac.STACObject` and must be read using
|
|
168
|
+
:meth:`ItemCollection.from_file <pystac.ItemCollection.from_file>`
|
|
169
|
+
"""
|
|
170
|
+
if stac_io is None:
|
|
171
|
+
stac_io = StacIO.default()
|
|
172
|
+
return stac_io.read_stac_object(href)
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def write_file(
|
|
176
|
+
obj: STACObject,
|
|
177
|
+
include_self_link: bool = True,
|
|
178
|
+
dest_href: HREF | None = None,
|
|
179
|
+
stac_io: StacIO | None = None,
|
|
180
|
+
) -> None:
|
|
181
|
+
"""Writes a STACObject to a file.
|
|
182
|
+
|
|
183
|
+
This will write only the Catalog, Collection or Item ``obj``. It will not attempt
|
|
184
|
+
to write any other objects that are linked to ``obj``; if you'd like functionality
|
|
185
|
+
to save off catalogs recursively see :meth:`Catalog.save <pystac.Catalog.save>`.
|
|
186
|
+
|
|
187
|
+
This method will write the JSON of the object to the object's assigned "self" link
|
|
188
|
+
or to the dest_href if provided. To set the self link, see
|
|
189
|
+
:meth:`STACObject.set_self_href <pystac.STACObject.set_self_href>`.
|
|
190
|
+
|
|
191
|
+
Convenience method for :meth:`STACObject.from_file <pystac.STACObject.from_file>`
|
|
192
|
+
|
|
193
|
+
Args:
|
|
194
|
+
obj : The STACObject to save.
|
|
195
|
+
include_self_link : If ``True``, include the ``"self"`` link with this object.
|
|
196
|
+
Otherwise, leave out the self link.
|
|
197
|
+
dest_href : Optional HREF to save the file to. If ``None``, the object will be
|
|
198
|
+
saved to the object's ``"self"`` href.
|
|
199
|
+
stac_io: Optional :class:`~StacIO` instance to use for I/O operations. If not
|
|
200
|
+
provided, will use :meth:`StacIO.default` to create an instance.
|
|
201
|
+
"""
|
|
202
|
+
if stac_io is None:
|
|
203
|
+
stac_io = StacIO.default()
|
|
204
|
+
import os
|
|
205
|
+
|
|
206
|
+
dest_href = None if dest_href is None else str(os.fspath(dest_href))
|
|
207
|
+
obj.save_object(
|
|
208
|
+
include_self_link=include_self_link, dest_href=dest_href, stac_io=stac_io
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
def read_dict(
|
|
213
|
+
d: dict[str, Any],
|
|
214
|
+
href: str | None = None,
|
|
215
|
+
root: Catalog | None = None,
|
|
216
|
+
stac_io: StacIO | None = None,
|
|
217
|
+
) -> STACObject:
|
|
218
|
+
"""Reads a :class:`~STACObject` or :class:`~ItemCollection` from a JSON-like dict
|
|
219
|
+
representing a serialized STAC object.
|
|
220
|
+
|
|
221
|
+
This method will return either a :class:`~Catalog`, :class:`~Collection`,
|
|
222
|
+
or :class`~Item` based on the contents of the dict.
|
|
223
|
+
|
|
224
|
+
This is a convenience method for either
|
|
225
|
+
:meth:`StacIO.stac_object_from_dict <pystac.StacIO.stac_object_from_dict>`.
|
|
226
|
+
|
|
227
|
+
Args:
|
|
228
|
+
d : The dict to parse.
|
|
229
|
+
href : Optional href that is the file location of the object being
|
|
230
|
+
parsed.
|
|
231
|
+
root : Optional root of the catalog for this object.
|
|
232
|
+
If provided, the root's resolved object cache can be used to search for
|
|
233
|
+
previously resolved instances of the STAC object.
|
|
234
|
+
stac_io: Optional :class:`~StacIO` instance to use for reading. If ``None``,
|
|
235
|
+
the default instance will be used.
|
|
236
|
+
|
|
237
|
+
Raises:
|
|
238
|
+
STACTypeError : If the ``d`` dictionary does not represent a valid
|
|
239
|
+
:class:`~pystac.STACObject`. Note that an :class:`~pystac.ItemCollection`
|
|
240
|
+
is not a :class:`~pystac.STACObject` and must be read using
|
|
241
|
+
:meth:`ItemCollection.from_dict <pystac.ItemCollection.from_dict>`
|
|
242
|
+
"""
|
|
243
|
+
if stac_io is None:
|
|
244
|
+
stac_io = StacIO.default()
|
|
245
|
+
return stac_io.stac_object_from_dict(d, href, root)
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
def __getattr__(name: str) -> Any:
|
|
249
|
+
if name == "validation":
|
|
250
|
+
import warnings
|
|
251
|
+
import pystac.validation
|
|
252
|
+
|
|
253
|
+
warnings.warn(
|
|
254
|
+
"pystac.validation will not be automatically imported to the package in "
|
|
255
|
+
"pystac v2.0. Instead, import it directly: `import pystac.validation`",
|
|
256
|
+
DeprecationWarning,
|
|
257
|
+
stacklevel=2,
|
|
258
|
+
)
|
|
259
|
+
return pystac.validation
|
|
260
|
+
raise AttributeError(f"module '{__name__}' has no attribute '{name}'")
|
pystac/asset.py
ADDED
|
@@ -0,0 +1,395 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import shutil
|
|
4
|
+
from copy import copy, deepcopy
|
|
5
|
+
from typing import TYPE_CHECKING, Any, Protocol, TypeVar
|
|
6
|
+
|
|
7
|
+
from pystac import MediaType, STACError, common_metadata, utils
|
|
8
|
+
from pystac.utils import is_absolute_href, make_absolute_href, make_relative_href
|
|
9
|
+
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
from pystac.common_metadata import CommonMetadata
|
|
12
|
+
from pystac.extensions.ext import AssetExt
|
|
13
|
+
|
|
14
|
+
#: Generalized version of :class:`Asset`
|
|
15
|
+
A = TypeVar("A", bound="Asset")
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class Asset:
|
|
19
|
+
"""An object that contains a link to data associated with an Item or Collection that
|
|
20
|
+
can be downloaded or streamed.
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
href : Link to the asset object. Relative and absolute links are both
|
|
24
|
+
allowed.
|
|
25
|
+
title : Optional displayed title for clients and users.
|
|
26
|
+
description : A description of the Asset providing additional details,
|
|
27
|
+
such as how it was processed or created. CommonMark 0.29 syntax MAY be used
|
|
28
|
+
for rich text representation.
|
|
29
|
+
media_type : Optional description of the media type. Registered Media Types
|
|
30
|
+
are preferred. See :class:`~pystac.MediaType` for common media types.
|
|
31
|
+
roles : Optional, Semantic roles (i.e. thumbnail, overview,
|
|
32
|
+
data, metadata) of the asset.
|
|
33
|
+
extra_fields : Optional, additional fields for this asset. This is used
|
|
34
|
+
by extensions as a way to serialize and deserialize properties on asset
|
|
35
|
+
object JSON.
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
href: str
|
|
39
|
+
"""Link to the asset object. Relative and absolute links are both allowed."""
|
|
40
|
+
|
|
41
|
+
title: str | None
|
|
42
|
+
"""Optional displayed title for clients and users."""
|
|
43
|
+
|
|
44
|
+
description: str | None
|
|
45
|
+
"""A description of the Asset providing additional details, such as how it was
|
|
46
|
+
processed or created. CommonMark 0.29 syntax MAY be used for rich text
|
|
47
|
+
representation."""
|
|
48
|
+
|
|
49
|
+
media_type: str | None
|
|
50
|
+
"""Optional description of the media type. Registered Media Types are preferred.
|
|
51
|
+
See :class:`~pystac.MediaType` for common media types."""
|
|
52
|
+
|
|
53
|
+
roles: list[str] | None
|
|
54
|
+
"""Optional, Semantic roles (i.e. thumbnail, overview, data, metadata) of the
|
|
55
|
+
asset."""
|
|
56
|
+
|
|
57
|
+
owner: Assets | None
|
|
58
|
+
"""The :class:`~pystac.Item` or :class:`~pystac.Collection` that this asset belongs
|
|
59
|
+
to, or ``None`` if it has no owner."""
|
|
60
|
+
|
|
61
|
+
extra_fields: dict[str, Any]
|
|
62
|
+
"""Optional, additional fields for this asset. This is used by extensions as a
|
|
63
|
+
way to serialize and deserialize properties on asset object JSON."""
|
|
64
|
+
|
|
65
|
+
def __init__(
|
|
66
|
+
self,
|
|
67
|
+
href: str,
|
|
68
|
+
title: str | None = None,
|
|
69
|
+
description: str | None = None,
|
|
70
|
+
media_type: str | None = None,
|
|
71
|
+
roles: list[str] | None = None,
|
|
72
|
+
extra_fields: dict[str, Any] | None = None,
|
|
73
|
+
) -> None:
|
|
74
|
+
self.href = utils.make_posix_style(href)
|
|
75
|
+
self.title = title
|
|
76
|
+
self.description = description
|
|
77
|
+
self.media_type = media_type
|
|
78
|
+
self.roles = roles
|
|
79
|
+
self.extra_fields = extra_fields or {}
|
|
80
|
+
|
|
81
|
+
# The Item which owns this Asset.
|
|
82
|
+
self.owner = None
|
|
83
|
+
|
|
84
|
+
def set_owner(self, obj: Assets) -> None:
|
|
85
|
+
"""Sets the owning item of this Asset.
|
|
86
|
+
|
|
87
|
+
The owning item will be used to resolve relative HREFs of this asset.
|
|
88
|
+
|
|
89
|
+
Args:
|
|
90
|
+
obj: The Collection or Item that owns this asset.
|
|
91
|
+
"""
|
|
92
|
+
self.owner = obj
|
|
93
|
+
|
|
94
|
+
def get_absolute_href(self) -> str | None:
|
|
95
|
+
"""Gets the absolute href for this asset, if possible.
|
|
96
|
+
|
|
97
|
+
If this Asset has no associated Item, and the asset HREF is a relative path,
|
|
98
|
+
this method will return ``None``. If the Item that owns the Asset has no
|
|
99
|
+
self HREF, this will also return ``None``.
|
|
100
|
+
|
|
101
|
+
Returns:
|
|
102
|
+
str: The absolute HREF of this asset, or None if an absolute HREF could not
|
|
103
|
+
be determined.
|
|
104
|
+
"""
|
|
105
|
+
item_self = self.owner.get_self_href() if self.owner is not None else None
|
|
106
|
+
if utils.is_absolute_href(self.href, item_self):
|
|
107
|
+
return self.href
|
|
108
|
+
else:
|
|
109
|
+
if item_self is not None:
|
|
110
|
+
return utils.make_absolute_href(self.href, item_self)
|
|
111
|
+
return None
|
|
112
|
+
|
|
113
|
+
def to_dict(self) -> dict[str, Any]:
|
|
114
|
+
"""Returns this Asset as a dictionary.
|
|
115
|
+
|
|
116
|
+
Returns:
|
|
117
|
+
dict: A serialization of the Asset.
|
|
118
|
+
"""
|
|
119
|
+
|
|
120
|
+
d: dict[str, Any] = {"href": self.href}
|
|
121
|
+
|
|
122
|
+
if self.media_type is not None:
|
|
123
|
+
d["type"] = self.media_type
|
|
124
|
+
|
|
125
|
+
if self.title is not None:
|
|
126
|
+
d["title"] = self.title
|
|
127
|
+
|
|
128
|
+
if self.description is not None:
|
|
129
|
+
d["description"] = self.description
|
|
130
|
+
|
|
131
|
+
if self.extra_fields is not None and len(self.extra_fields) > 0:
|
|
132
|
+
for k, v in self.extra_fields.items():
|
|
133
|
+
d[k] = v
|
|
134
|
+
|
|
135
|
+
if self.roles is not None:
|
|
136
|
+
d["roles"] = self.roles
|
|
137
|
+
|
|
138
|
+
return d
|
|
139
|
+
|
|
140
|
+
def clone(self) -> Asset:
|
|
141
|
+
"""Clones this asset. Makes a ``deepcopy`` of the
|
|
142
|
+
:attr:`~pystac.Asset.extra_fields`.
|
|
143
|
+
|
|
144
|
+
Returns:
|
|
145
|
+
Asset: The clone of this asset.
|
|
146
|
+
"""
|
|
147
|
+
cls = self.__class__
|
|
148
|
+
return cls(
|
|
149
|
+
href=self.href,
|
|
150
|
+
title=self.title,
|
|
151
|
+
description=self.description,
|
|
152
|
+
media_type=self.media_type,
|
|
153
|
+
roles=self.roles,
|
|
154
|
+
extra_fields=deepcopy(self.extra_fields),
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
def has_role(self, role: str) -> bool:
|
|
158
|
+
"""Check if a role exists in the Asset role list.
|
|
159
|
+
|
|
160
|
+
Args:
|
|
161
|
+
role: Role to check for existence.
|
|
162
|
+
|
|
163
|
+
Returns:
|
|
164
|
+
bool: True if role exists, else False.
|
|
165
|
+
"""
|
|
166
|
+
if self.roles is None:
|
|
167
|
+
return False
|
|
168
|
+
else:
|
|
169
|
+
return role in self.roles
|
|
170
|
+
|
|
171
|
+
@property
|
|
172
|
+
def common_metadata(self) -> CommonMetadata:
|
|
173
|
+
"""Access the asset's common metadata fields as a
|
|
174
|
+
:class:`~pystac.CommonMetadata` object."""
|
|
175
|
+
return common_metadata.CommonMetadata(self)
|
|
176
|
+
|
|
177
|
+
def __repr__(self) -> str:
|
|
178
|
+
return f"<Asset href={self.href}>"
|
|
179
|
+
|
|
180
|
+
def _repr_html_(self) -> str:
|
|
181
|
+
from html import escape
|
|
182
|
+
|
|
183
|
+
from pystac.html.jinja_env import get_jinja_env
|
|
184
|
+
|
|
185
|
+
jinja_env = get_jinja_env()
|
|
186
|
+
if jinja_env:
|
|
187
|
+
template = jinja_env.get_template("JSON.jinja2")
|
|
188
|
+
return str(template.render(dict=self.to_dict(), plain=escape(repr(self))))
|
|
189
|
+
else:
|
|
190
|
+
return escape(repr(self))
|
|
191
|
+
|
|
192
|
+
@classmethod
|
|
193
|
+
def from_dict(cls: type[A], d: dict[str, Any]) -> A:
|
|
194
|
+
"""Constructs an Asset from a dict.
|
|
195
|
+
|
|
196
|
+
Returns:
|
|
197
|
+
Asset: The Asset deserialized from the JSON dict.
|
|
198
|
+
"""
|
|
199
|
+
d = copy(d)
|
|
200
|
+
href = d.pop("href")
|
|
201
|
+
media_type = d.pop("type", None)
|
|
202
|
+
title = d.pop("title", None)
|
|
203
|
+
description = d.pop("description", None)
|
|
204
|
+
roles = d.pop("roles", None)
|
|
205
|
+
properties = None
|
|
206
|
+
if any(d):
|
|
207
|
+
properties = d
|
|
208
|
+
|
|
209
|
+
return cls(
|
|
210
|
+
href=href,
|
|
211
|
+
media_type=media_type,
|
|
212
|
+
title=title,
|
|
213
|
+
description=description,
|
|
214
|
+
roles=roles,
|
|
215
|
+
extra_fields=properties,
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
def move(self, href: str) -> Asset:
|
|
219
|
+
"""Moves this asset's file to a new location on the local filesystem,
|
|
220
|
+
setting the asset href accordingly.
|
|
221
|
+
|
|
222
|
+
Modifies the asset in place, and returns the same asset.
|
|
223
|
+
|
|
224
|
+
Args:
|
|
225
|
+
href: The new asset location. Must be a local path. If relative
|
|
226
|
+
it must be relative to the owner object.
|
|
227
|
+
|
|
228
|
+
Returns:
|
|
229
|
+
Asset: The asset with the updated href.
|
|
230
|
+
"""
|
|
231
|
+
src = _absolute_href(self.href, self.owner, "move")
|
|
232
|
+
dst = _absolute_href(href, self.owner, "move")
|
|
233
|
+
shutil.move(src, dst)
|
|
234
|
+
self.href = href
|
|
235
|
+
return self
|
|
236
|
+
|
|
237
|
+
def copy(self, href: str) -> Asset:
|
|
238
|
+
"""Copies this asset's file to a new location on the local filesystem,
|
|
239
|
+
setting the asset href accordingly.
|
|
240
|
+
|
|
241
|
+
Modifies the asset in place, and returns the same asset.
|
|
242
|
+
|
|
243
|
+
Args:
|
|
244
|
+
href: The new asset location. Must be a local path. If relative
|
|
245
|
+
it must be relative to the owner object.
|
|
246
|
+
|
|
247
|
+
Returns:
|
|
248
|
+
Asset: The asset with the updated href.
|
|
249
|
+
"""
|
|
250
|
+
src = _absolute_href(self.href, self.owner, "copy")
|
|
251
|
+
dst = _absolute_href(href, self.owner, "copy")
|
|
252
|
+
shutil.copy2(src, dst)
|
|
253
|
+
self.href = href
|
|
254
|
+
return self
|
|
255
|
+
|
|
256
|
+
def delete(self) -> None:
|
|
257
|
+
"""Delete this asset's file. Does not delete the asset from the item
|
|
258
|
+
that owns it. See :meth:`~pystac.Item.delete_asset` for that.
|
|
259
|
+
|
|
260
|
+
Does not modify the asset.
|
|
261
|
+
"""
|
|
262
|
+
import os
|
|
263
|
+
|
|
264
|
+
href = _absolute_href(self.href, self.owner, "delete")
|
|
265
|
+
os.remove(href)
|
|
266
|
+
|
|
267
|
+
@property
|
|
268
|
+
def ext(self) -> AssetExt:
|
|
269
|
+
"""Accessor for extension classes on this asset
|
|
270
|
+
|
|
271
|
+
Example::
|
|
272
|
+
|
|
273
|
+
asset.ext.proj.code = "EPSG:4326"
|
|
274
|
+
"""
|
|
275
|
+
from pystac.extensions.ext import AssetExt
|
|
276
|
+
|
|
277
|
+
return AssetExt(stac_object=self)
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
class Assets(Protocol):
|
|
281
|
+
"""Protocol, with functionality, for STAC objects that have assets."""
|
|
282
|
+
|
|
283
|
+
assets: dict[str, Asset]
|
|
284
|
+
"""The asset dictionary."""
|
|
285
|
+
|
|
286
|
+
def get_assets(
|
|
287
|
+
self,
|
|
288
|
+
media_type: str | MediaType | None = None,
|
|
289
|
+
role: str | None = None,
|
|
290
|
+
) -> dict[str, Asset]:
|
|
291
|
+
"""Get this object's assets.
|
|
292
|
+
|
|
293
|
+
Args:
|
|
294
|
+
media_type: If set, filter the assets such that only those with a
|
|
295
|
+
matching ``media_type`` are returned.
|
|
296
|
+
role: If set, filter the assets such that only those with a matching
|
|
297
|
+
``role`` are returned.
|
|
298
|
+
|
|
299
|
+
Returns:
|
|
300
|
+
Dict[str, Asset]: A dictionary of assets that match ``media_type``
|
|
301
|
+
and/or ``role`` if set or else all of this object's assets.
|
|
302
|
+
"""
|
|
303
|
+
return {
|
|
304
|
+
k: deepcopy(v)
|
|
305
|
+
for k, v in self.assets.items()
|
|
306
|
+
if (media_type is None or v.media_type == media_type)
|
|
307
|
+
and (role is None or v.has_role(role))
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
def add_asset(self, key: str, asset: Asset) -> None:
|
|
311
|
+
"""Adds an Asset to this object.
|
|
312
|
+
|
|
313
|
+
Args:
|
|
314
|
+
key : The unique key of this asset.
|
|
315
|
+
asset : The Asset to add.
|
|
316
|
+
"""
|
|
317
|
+
asset.set_owner(self)
|
|
318
|
+
self.assets[key] = asset
|
|
319
|
+
|
|
320
|
+
def delete_asset(self, key: str) -> None:
|
|
321
|
+
"""Deletes the asset at the given key, and removes the asset's data
|
|
322
|
+
file from the local filesystem.
|
|
323
|
+
|
|
324
|
+
It is an error to attempt to delete an asset's file if it is on a
|
|
325
|
+
remote filesystem.
|
|
326
|
+
|
|
327
|
+
To delete the asset without removing the file, use `del item.assets["key"]`.
|
|
328
|
+
|
|
329
|
+
Args:
|
|
330
|
+
key: The unique key of this asset.
|
|
331
|
+
"""
|
|
332
|
+
asset = self.assets[key]
|
|
333
|
+
asset.set_owner(self)
|
|
334
|
+
asset.delete()
|
|
335
|
+
|
|
336
|
+
del self.assets[key]
|
|
337
|
+
|
|
338
|
+
def make_asset_hrefs_relative(self) -> Assets:
|
|
339
|
+
"""Modify each asset's HREF to be relative to this object's self HREF.
|
|
340
|
+
|
|
341
|
+
Returns:
|
|
342
|
+
Item: self
|
|
343
|
+
"""
|
|
344
|
+
self_href = self.get_self_href()
|
|
345
|
+
for asset in self.assets.values():
|
|
346
|
+
if is_absolute_href(asset.href, self_href):
|
|
347
|
+
if self_href is None:
|
|
348
|
+
raise STACError(
|
|
349
|
+
"Cannot make asset HREFs relative if no self_href is set."
|
|
350
|
+
)
|
|
351
|
+
asset.href = make_relative_href(asset.href, self_href)
|
|
352
|
+
return self
|
|
353
|
+
|
|
354
|
+
def make_asset_hrefs_absolute(self) -> Assets:
|
|
355
|
+
"""Modify each asset's HREF to be absolute.
|
|
356
|
+
|
|
357
|
+
Any asset HREFs that are relative will be modified to absolute based on this
|
|
358
|
+
item's self HREF.
|
|
359
|
+
|
|
360
|
+
Returns:
|
|
361
|
+
Assets: self
|
|
362
|
+
"""
|
|
363
|
+
self_href = self.get_self_href()
|
|
364
|
+
for asset in self.assets.values():
|
|
365
|
+
if not is_absolute_href(asset.href, self_href):
|
|
366
|
+
if self_href is None:
|
|
367
|
+
raise STACError(
|
|
368
|
+
"Cannot make relative asset HREFs absolute "
|
|
369
|
+
"if no self_href is set."
|
|
370
|
+
)
|
|
371
|
+
asset.href = make_absolute_href(asset.href, self_href)
|
|
372
|
+
return self
|
|
373
|
+
|
|
374
|
+
def get_self_href(self) -> str | None:
|
|
375
|
+
"""Abstract definition of STACObject.get_self_href.
|
|
376
|
+
|
|
377
|
+
Needed to make the `make_asset_hrefs_{absolute|relative}` methods pass
|
|
378
|
+
type checking. Refactoring out all the link behavior in STACObject to
|
|
379
|
+
its own protocol would be too heavy, so we just use this stub instead.
|
|
380
|
+
"""
|
|
381
|
+
...
|
|
382
|
+
|
|
383
|
+
|
|
384
|
+
def _absolute_href(href: str, owner: Assets | None, action: str = "access") -> str:
|
|
385
|
+
item_self = owner.get_self_href() if owner else None
|
|
386
|
+
if utils.is_absolute_href(href, item_self):
|
|
387
|
+
return href
|
|
388
|
+
else:
|
|
389
|
+
if item_self is None:
|
|
390
|
+
raise ValueError(
|
|
391
|
+
f"Cannot {action} file if asset href ('{href}') is relative "
|
|
392
|
+
"and owner item is not set. Hint: try using "
|
|
393
|
+
":func:`~pystac.Item.make_asset_hrefs_absolute`"
|
|
394
|
+
)
|
|
395
|
+
return utils.make_absolute_href(href, item_self)
|