pystac-ext-file 2.1.0rc0__tar.gz
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_ext_file-2.1.0rc0/.gitignore +163 -0
- pystac_ext_file-2.1.0rc0/PKG-INFO +37 -0
- pystac_ext_file-2.1.0rc0/README.md +13 -0
- pystac_ext_file-2.1.0rc0/pyproject.toml +36 -0
- pystac_ext_file-2.1.0rc0/pystac/extensions/file.py +385 -0
- pystac_ext_file-2.1.0rc0/pystac/extensions/py.typed +0 -0
- pystac_ext_file-2.1.0rc0/tests/cassettes/test_file/test_migrate_from_v1_0_0.yaml +48 -0
- pystac_ext_file-2.1.0rc0/tests/cassettes/test_file/test_migrate_from_v2_0_0.yaml +47 -0
- pystac_ext_file-2.1.0rc0/tests/cassettes/test_file/test_set_field_on_asset[calibrations-local_path-different-file.xml].yaml +74 -0
- pystac_ext_file-2.1.0rc0/tests/cassettes/test_file/test_set_field_on_asset[measurement-header_size-8192].yaml +74 -0
- pystac_ext_file-2.1.0rc0/tests/cassettes/test_file/test_set_field_on_asset[thumbnail-byte_order-little-endian].yaml +74 -0
- pystac_ext_file-2.1.0rc0/tests/cassettes/test_file/test_set_field_on_asset[thumbnail-checksum-90e40210163700a8a6501eccd00b6d3b44ddaed0].yaml +74 -0
- pystac_ext_file-2.1.0rc0/tests/cassettes/test_file/test_set_field_on_asset[thumbnail-size-1].yaml +74 -0
- pystac_ext_file-2.1.0rc0/tests/cassettes/test_file/test_set_field_on_link[about-byte_order-big-endian].yaml +74 -0
- pystac_ext_file-2.1.0rc0/tests/cassettes/test_file/test_set_field_on_link[about-checksum-90e40210163700a8a6501eccd00b6d3b44ddaedb].yaml +74 -0
- pystac_ext_file-2.1.0rc0/tests/cassettes/test_file/test_set_field_on_link[about-header_size-4092].yaml +74 -0
- pystac_ext_file-2.1.0rc0/tests/cassettes/test_file/test_set_field_on_link[about-local_path-a-path].yaml +74 -0
- pystac_ext_file-2.1.0rc0/tests/cassettes/test_file/test_set_field_on_link[about-size-129302].yaml +74 -0
- pystac_ext_file-2.1.0rc0/tests/cassettes/test_file/test_validate_catalog.yaml +74 -0
- pystac_ext_file-2.1.0rc0/tests/cassettes/test_file/test_validate_collection.yaml +74 -0
- pystac_ext_file-2.1.0rc0/tests/cassettes/test_file/test_validate_item.yaml +74 -0
- pystac_ext_file-2.1.0rc0/tests/data-files/catalog.json +29 -0
- pystac_ext_file-2.1.0rc0/tests/data-files/collection.json +50 -0
- pystac_ext_file-2.1.0rc0/tests/data-files/examples/1.0.0-beta.2/extensions/checksum/examples/sentinel1.json +95 -0
- pystac_ext_file-2.1.0rc0/tests/data-files/item.json +93 -0
- pystac_ext_file-2.1.0rc0/tests/test_file.py +324 -0
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
*.pyc
|
|
2
|
+
*.egg-info
|
|
3
|
+
*.eggs
|
|
4
|
+
.DS_Store
|
|
5
|
+
data
|
|
6
|
+
config.json
|
|
7
|
+
stdout*
|
|
8
|
+
/integration*
|
|
9
|
+
.idea
|
|
10
|
+
.vscode
|
|
11
|
+
.actrc
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
# Sphinx documentation
|
|
15
|
+
.ipynb_checkpoints/
|
|
16
|
+
|
|
17
|
+
docs/tutorials/pystac-example*
|
|
18
|
+
docs/tutorials/spacenet-stac/
|
|
19
|
+
docs/tutorials/spacenet-cog-stac/
|
|
20
|
+
docs/tutorials/data/
|
|
21
|
+
docs/quickstart_stac/
|
|
22
|
+
|
|
23
|
+
# Byte-compiled / optimized / DLL files
|
|
24
|
+
__pycache__/
|
|
25
|
+
*.py[cod]
|
|
26
|
+
*$py.class
|
|
27
|
+
|
|
28
|
+
# C extensions
|
|
29
|
+
*.so
|
|
30
|
+
|
|
31
|
+
# Distribution / packaging
|
|
32
|
+
.Python
|
|
33
|
+
build/
|
|
34
|
+
develop-eggs/
|
|
35
|
+
dist/
|
|
36
|
+
downloads/
|
|
37
|
+
eggs/
|
|
38
|
+
.eggs/
|
|
39
|
+
lib/
|
|
40
|
+
lib64/
|
|
41
|
+
parts/
|
|
42
|
+
sdist/
|
|
43
|
+
var/
|
|
44
|
+
wheels/
|
|
45
|
+
share/python-wheels/
|
|
46
|
+
*.egg-info/
|
|
47
|
+
.installed.cfg
|
|
48
|
+
*.egg
|
|
49
|
+
MANIFEST
|
|
50
|
+
|
|
51
|
+
# PyInstaller
|
|
52
|
+
# Usually these files are written by a python script from a template
|
|
53
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
54
|
+
*.manifest
|
|
55
|
+
*.spec
|
|
56
|
+
|
|
57
|
+
# Installer logs
|
|
58
|
+
pip-log.txt
|
|
59
|
+
pip-delete-this-directory.txt
|
|
60
|
+
|
|
61
|
+
# Unit test / coverage reports
|
|
62
|
+
htmlcov/
|
|
63
|
+
.tox/
|
|
64
|
+
.nox/
|
|
65
|
+
.coverage
|
|
66
|
+
.coverage.*
|
|
67
|
+
.cache
|
|
68
|
+
nosetests.xml
|
|
69
|
+
coverage.xml
|
|
70
|
+
*.cover
|
|
71
|
+
*.py,cover
|
|
72
|
+
.hypothesis/
|
|
73
|
+
.pytest_cache/
|
|
74
|
+
cover/
|
|
75
|
+
|
|
76
|
+
# Translations
|
|
77
|
+
*.mo
|
|
78
|
+
*.pot
|
|
79
|
+
|
|
80
|
+
# Django stuff:
|
|
81
|
+
*.log
|
|
82
|
+
local_settings.py
|
|
83
|
+
db.sqlite3
|
|
84
|
+
db.sqlite3-journal
|
|
85
|
+
|
|
86
|
+
# Flask stuff:
|
|
87
|
+
instance/
|
|
88
|
+
.webassets-cache
|
|
89
|
+
|
|
90
|
+
# Scrapy stuff:
|
|
91
|
+
.scrapy
|
|
92
|
+
|
|
93
|
+
# Sphinx documentation
|
|
94
|
+
docs/_build/
|
|
95
|
+
|
|
96
|
+
# PyBuilder
|
|
97
|
+
.pybuilder/
|
|
98
|
+
target/
|
|
99
|
+
|
|
100
|
+
# Jupyter Notebook
|
|
101
|
+
.ipynb_checkpoints
|
|
102
|
+
|
|
103
|
+
# IPython
|
|
104
|
+
profile_default/
|
|
105
|
+
ipython_config.py
|
|
106
|
+
|
|
107
|
+
# pyenv
|
|
108
|
+
# For a library or package, you might want to ignore these files since the code is
|
|
109
|
+
# intended to run in multiple environments; otherwise, check them in:
|
|
110
|
+
# .python-version
|
|
111
|
+
|
|
112
|
+
# pipenv
|
|
113
|
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
114
|
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
115
|
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
116
|
+
# install all needed dependencies.
|
|
117
|
+
# Pipfile.lock
|
|
118
|
+
|
|
119
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
|
|
120
|
+
__pypackages__/
|
|
121
|
+
|
|
122
|
+
# Celery stuff
|
|
123
|
+
celerybeat-schedule
|
|
124
|
+
celerybeat.pid
|
|
125
|
+
|
|
126
|
+
# SageMath parsed files
|
|
127
|
+
*.sage.py
|
|
128
|
+
|
|
129
|
+
# Environments
|
|
130
|
+
.env
|
|
131
|
+
.venv
|
|
132
|
+
env/
|
|
133
|
+
venv/
|
|
134
|
+
ENV/
|
|
135
|
+
env.bak/
|
|
136
|
+
venv.bak/
|
|
137
|
+
|
|
138
|
+
# Spyder project settings
|
|
139
|
+
.spyderproject
|
|
140
|
+
.spyproject
|
|
141
|
+
|
|
142
|
+
# Rope project settings
|
|
143
|
+
.ropeproject
|
|
144
|
+
|
|
145
|
+
# mkdocs documentation
|
|
146
|
+
/site
|
|
147
|
+
|
|
148
|
+
# mypy
|
|
149
|
+
.mypy_cache/
|
|
150
|
+
.dmypy.json
|
|
151
|
+
dmypy.json
|
|
152
|
+
|
|
153
|
+
# Pyre type checker
|
|
154
|
+
.pyre/
|
|
155
|
+
|
|
156
|
+
# pytype static type analyzer
|
|
157
|
+
.pytype/
|
|
158
|
+
|
|
159
|
+
# Cython debug symbols
|
|
160
|
+
cython_debug/
|
|
161
|
+
|
|
162
|
+
# asv environments
|
|
163
|
+
.asv
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pystac-ext-file
|
|
3
|
+
Version: 2.1.0rc0
|
|
4
|
+
Summary: File extension for PySTAC
|
|
5
|
+
Project-URL: Documentation, https://pystac.readthedocs.io
|
|
6
|
+
Project-URL: Repository, https://github.com/stac-utils/pystac
|
|
7
|
+
Project-URL: Issues, https://github.com/stac-utils/pystac/issues
|
|
8
|
+
Project-URL: Changelog, https://github.com/stac-utils/pystac/blob/main/CHANGELOG.md
|
|
9
|
+
Project-URL: Discussions, https://github.com/radiantearth/stac-spec/discussions/categories/stac-software
|
|
10
|
+
License: Apache-2.0
|
|
11
|
+
Keywords: STAC,catalog,file,imagery,pystac,raster
|
|
12
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
15
|
+
Classifier: Natural Language :: English
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Requires-Python: >=3.10
|
|
22
|
+
Requires-Dist: pystac-core
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
|
|
25
|
+
# pystac-ext-file
|
|
26
|
+
|
|
27
|
+
[PySTAC](https://pypi.org/project/pystac/) extension package for the [File Info Extension](https://github.com/stac-extensions/file).
|
|
28
|
+
This extension provides fields for describing file details such as byte order, checksum, header size, and data type for assets.
|
|
29
|
+
|
|
30
|
+
## Supported versions
|
|
31
|
+
|
|
32
|
+
- [v2.1.0](https://stac-extensions.github.io/file/v2.1.0/schema.json)
|
|
33
|
+
|
|
34
|
+
## Versioning
|
|
35
|
+
|
|
36
|
+
This package's version corresponds to the version of the extension specification it targets.
|
|
37
|
+
When we release updates to the package code without changing the target extension version, we use [post releases](https://packaging.python.org/en/latest/discussions/versioning/#post-releases), e.g. `2.1.0.post1`.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# pystac-ext-file
|
|
2
|
+
|
|
3
|
+
[PySTAC](https://pypi.org/project/pystac/) extension package for the [File Info Extension](https://github.com/stac-extensions/file).
|
|
4
|
+
This extension provides fields for describing file details such as byte order, checksum, header size, and data type for assets.
|
|
5
|
+
|
|
6
|
+
## Supported versions
|
|
7
|
+
|
|
8
|
+
- [v2.1.0](https://stac-extensions.github.io/file/v2.1.0/schema.json)
|
|
9
|
+
|
|
10
|
+
## Versioning
|
|
11
|
+
|
|
12
|
+
This package's version corresponds to the version of the extension specification it targets.
|
|
13
|
+
When we release updates to the package code without changing the target extension version, we use [post releases](https://packaging.python.org/en/latest/discussions/versioning/#post-releases), e.g. `2.1.0.post1`.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "pystac-ext-file"
|
|
3
|
+
description = "File extension for PySTAC"
|
|
4
|
+
readme = "README.md"
|
|
5
|
+
version = "2.1.0-rc.0"
|
|
6
|
+
authors = []
|
|
7
|
+
maintainers = []
|
|
8
|
+
keywords = ["pystac", "imagery", "raster", "catalog", "STAC", "file"]
|
|
9
|
+
license = { text = "Apache-2.0" }
|
|
10
|
+
classifiers = [
|
|
11
|
+
"Development Status :: 5 - Production/Stable",
|
|
12
|
+
"Intended Audience :: Developers",
|
|
13
|
+
"License :: OSI Approved :: Apache Software License",
|
|
14
|
+
"Natural Language :: English",
|
|
15
|
+
"Programming Language :: Python :: 3",
|
|
16
|
+
"Programming Language :: Python :: 3.10",
|
|
17
|
+
"Programming Language :: Python :: 3.11",
|
|
18
|
+
"Programming Language :: Python :: 3.12",
|
|
19
|
+
"Programming Language :: Python :: 3.13",
|
|
20
|
+
]
|
|
21
|
+
requires-python = ">=3.10"
|
|
22
|
+
dependencies = ["pystac-core"]
|
|
23
|
+
|
|
24
|
+
[project.urls]
|
|
25
|
+
Documentation = "https://pystac.readthedocs.io"
|
|
26
|
+
Repository = "https://github.com/stac-utils/pystac"
|
|
27
|
+
Issues = "https://github.com/stac-utils/pystac/issues"
|
|
28
|
+
Changelog = "https://github.com/stac-utils/pystac/blob/main/CHANGELOG.md"
|
|
29
|
+
Discussions = "https://github.com/radiantearth/stac-spec/discussions/categories/stac-software"
|
|
30
|
+
|
|
31
|
+
[build-system]
|
|
32
|
+
requires = ["hatchling"]
|
|
33
|
+
build-backend = "hatchling.build"
|
|
34
|
+
|
|
35
|
+
[tool.hatch.build.targets.wheel]
|
|
36
|
+
packages = ["pystac"]
|
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
"""Implements the :stac-ext:`File Info Extension <file>`."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from collections.abc import Iterable
|
|
6
|
+
from typing import Any, Generic, Literal, TypeVar, cast
|
|
7
|
+
|
|
8
|
+
from pystac import (
|
|
9
|
+
Asset,
|
|
10
|
+
Catalog,
|
|
11
|
+
Collection,
|
|
12
|
+
ExtensionTypeError,
|
|
13
|
+
Item,
|
|
14
|
+
Link,
|
|
15
|
+
STACObjectType,
|
|
16
|
+
)
|
|
17
|
+
from pystac.extensions.base import ExtensionManagementMixin, PropertiesExtension
|
|
18
|
+
from pystac.extensions.hooks import ExtensionHooks
|
|
19
|
+
from pystac.serialization.identify import (
|
|
20
|
+
OldExtensionShortIDs,
|
|
21
|
+
STACJSONDescription,
|
|
22
|
+
STACVersionID,
|
|
23
|
+
)
|
|
24
|
+
from pystac.utils import StringEnum, get_required, map_opt
|
|
25
|
+
|
|
26
|
+
#: Generalized version of :class:`~pystac.Asset`, :class:`~pystac.Link`,
|
|
27
|
+
T = TypeVar("T", Asset, Link)
|
|
28
|
+
|
|
29
|
+
SCHEMA_URI = "https://stac-extensions.github.io/file/v2.1.0/schema.json"
|
|
30
|
+
|
|
31
|
+
PREFIX = "file:"
|
|
32
|
+
BYTE_ORDER_PROP = PREFIX + "byte_order"
|
|
33
|
+
CHECKSUM_PROP = PREFIX + "checksum"
|
|
34
|
+
HEADER_SIZE_PROP = PREFIX + "header_size"
|
|
35
|
+
SIZE_PROP = PREFIX + "size"
|
|
36
|
+
VALUES_PROP = PREFIX + "values"
|
|
37
|
+
LOCAL_PATH_PROP = PREFIX + "local_path"
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class ByteOrder(StringEnum):
|
|
41
|
+
"""List of allows values for the ``"file:byte_order"`` field defined by the
|
|
42
|
+
:stac-ext:`File Info Extension <file>`."""
|
|
43
|
+
|
|
44
|
+
LITTLE_ENDIAN = "little-endian"
|
|
45
|
+
BIG_ENDIAN = "big-endian"
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class MappingObject:
|
|
49
|
+
"""Represents a value map used by assets that are used as classification layers, and
|
|
50
|
+
give details about the values in the asset and their meanings."""
|
|
51
|
+
|
|
52
|
+
properties: dict[str, Any]
|
|
53
|
+
|
|
54
|
+
def __init__(self, properties: dict[str, Any]) -> None:
|
|
55
|
+
self.properties = properties
|
|
56
|
+
|
|
57
|
+
def apply(self, values: list[Any], summary: str) -> None:
|
|
58
|
+
"""Sets the properties for this :class:`~MappingObject` instance.
|
|
59
|
+
|
|
60
|
+
Args:
|
|
61
|
+
values : The value(s) in the file. At least one array element is required.
|
|
62
|
+
summary : A short description of the value(s).
|
|
63
|
+
"""
|
|
64
|
+
self.values = values
|
|
65
|
+
self.summary = summary
|
|
66
|
+
|
|
67
|
+
@classmethod
|
|
68
|
+
def create(cls, values: list[Any], summary: str) -> MappingObject:
|
|
69
|
+
"""Creates a new :class:`~MappingObject` instance.
|
|
70
|
+
|
|
71
|
+
Args:
|
|
72
|
+
values : The value(s) in the file. At least one array element is required.
|
|
73
|
+
summary : A short description of the value(s).
|
|
74
|
+
"""
|
|
75
|
+
m = cls({})
|
|
76
|
+
m.apply(values=values, summary=summary)
|
|
77
|
+
return m
|
|
78
|
+
|
|
79
|
+
@property
|
|
80
|
+
def values(self) -> list[Any]:
|
|
81
|
+
"""Gets or sets the list of value(s) in the file. At least one array element is
|
|
82
|
+
required."""
|
|
83
|
+
return get_required(self.properties.get("values"), self, "values")
|
|
84
|
+
|
|
85
|
+
@values.setter
|
|
86
|
+
def values(self, v: list[Any]) -> None:
|
|
87
|
+
self.properties["values"] = v
|
|
88
|
+
|
|
89
|
+
@property
|
|
90
|
+
def summary(self) -> str:
|
|
91
|
+
"""Gets or sets the short description of the value(s)."""
|
|
92
|
+
return cast(str, get_required(self.properties.get("summary"), self, "summary"))
|
|
93
|
+
|
|
94
|
+
@summary.setter
|
|
95
|
+
def summary(self, v: str) -> None:
|
|
96
|
+
self.properties["summary"] = v
|
|
97
|
+
|
|
98
|
+
@classmethod
|
|
99
|
+
def from_dict(cls, d: dict[str, Any]) -> MappingObject:
|
|
100
|
+
return cls.create(**d)
|
|
101
|
+
|
|
102
|
+
def to_dict(self) -> dict[str, Any]:
|
|
103
|
+
return self.properties
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
class FileExtension(
|
|
107
|
+
Generic[T],
|
|
108
|
+
PropertiesExtension,
|
|
109
|
+
ExtensionManagementMixin[Catalog | Collection | Item],
|
|
110
|
+
):
|
|
111
|
+
"""A class that can be used to extend the properties of an :class:`~pystac.Asset`
|
|
112
|
+
or :class:`~pystac.Link` with properties from the
|
|
113
|
+
:stac-ext:`File Info Extension <file>`.
|
|
114
|
+
|
|
115
|
+
To create an instance of :class:`FileExtension`, use the
|
|
116
|
+
:meth:`FileExtension.ext` method. For example:
|
|
117
|
+
|
|
118
|
+
.. code-block:: python
|
|
119
|
+
|
|
120
|
+
>>> asset: pystac.Asset = ...
|
|
121
|
+
>>> file_ext = FileExtension.ext(asset)
|
|
122
|
+
"""
|
|
123
|
+
|
|
124
|
+
name: Literal["file"] = "file"
|
|
125
|
+
|
|
126
|
+
def apply(
|
|
127
|
+
self,
|
|
128
|
+
byte_order: ByteOrder | None = None,
|
|
129
|
+
checksum: str | None = None,
|
|
130
|
+
header_size: int | None = None,
|
|
131
|
+
size: int | None = None,
|
|
132
|
+
values: list[MappingObject] | None = None,
|
|
133
|
+
local_path: str | None = None,
|
|
134
|
+
) -> None:
|
|
135
|
+
"""Applies file extension properties to the extended Item.
|
|
136
|
+
|
|
137
|
+
Args:
|
|
138
|
+
byte_order : Optional byte order of integer values in the file. One of
|
|
139
|
+
``"big-endian"`` or ``"little-endian"``.
|
|
140
|
+
checksum : Optional multihash for the corresponding file,
|
|
141
|
+
encoded as hexadecimal (base 16) string with lowercase letters.
|
|
142
|
+
header_size : Optional header size of the file, in bytes.
|
|
143
|
+
size : Optional size of the file, in bytes.
|
|
144
|
+
values : Optional list of :class:`~MappingObject` instances that lists the
|
|
145
|
+
values that are in the file and describe their meaning. See the
|
|
146
|
+
:stac-ext:`Mapping Object <file#mapping-object>` docs for an example.
|
|
147
|
+
If given, at least one array element is required.
|
|
148
|
+
"""
|
|
149
|
+
self.byte_order = byte_order
|
|
150
|
+
self.checksum = checksum
|
|
151
|
+
self.header_size = header_size
|
|
152
|
+
self.size = size
|
|
153
|
+
self.values = values
|
|
154
|
+
self.local_path = local_path
|
|
155
|
+
|
|
156
|
+
@property
|
|
157
|
+
def byte_order(self) -> ByteOrder | None:
|
|
158
|
+
"""Gets or sets the byte order of integer values in the file. One of big-endian
|
|
159
|
+
or little-endian."""
|
|
160
|
+
return self._get_property(BYTE_ORDER_PROP, ByteOrder)
|
|
161
|
+
|
|
162
|
+
@byte_order.setter
|
|
163
|
+
def byte_order(self, v: ByteOrder | None) -> None:
|
|
164
|
+
self._set_property(BYTE_ORDER_PROP, v)
|
|
165
|
+
|
|
166
|
+
@property
|
|
167
|
+
def checksum(self) -> str | None:
|
|
168
|
+
"""Get or sets the multihash for the corresponding file, encoded as hexadecimal
|
|
169
|
+
(base 16) string with lowercase letters."""
|
|
170
|
+
return self._get_property(CHECKSUM_PROP, str)
|
|
171
|
+
|
|
172
|
+
@checksum.setter
|
|
173
|
+
def checksum(self, v: str | None) -> None:
|
|
174
|
+
self._set_property(CHECKSUM_PROP, v)
|
|
175
|
+
|
|
176
|
+
@property
|
|
177
|
+
def header_size(self) -> int | None:
|
|
178
|
+
"""Get or sets the header size of the file, in bytes."""
|
|
179
|
+
return self._get_property(HEADER_SIZE_PROP, int)
|
|
180
|
+
|
|
181
|
+
@header_size.setter
|
|
182
|
+
def header_size(self, v: int | None) -> None:
|
|
183
|
+
self._set_property(HEADER_SIZE_PROP, v)
|
|
184
|
+
|
|
185
|
+
@property
|
|
186
|
+
def local_path(self) -> str | None:
|
|
187
|
+
"""Get or sets a relative local path for the asset/link.
|
|
188
|
+
|
|
189
|
+
The ``file:local_path`` field indicates a **relative** path that
|
|
190
|
+
can be used by clients for different purposes to organize the
|
|
191
|
+
files locally. For compatibility reasons the name-separator
|
|
192
|
+
character in paths **must** be ``/`` and the Windows separator ``\\``
|
|
193
|
+
is **not** allowed.
|
|
194
|
+
"""
|
|
195
|
+
return self._get_property(LOCAL_PATH_PROP, str)
|
|
196
|
+
|
|
197
|
+
@local_path.setter
|
|
198
|
+
def local_path(self, v: str | None) -> None:
|
|
199
|
+
self._set_property(LOCAL_PATH_PROP, v, pop_if_none=True)
|
|
200
|
+
|
|
201
|
+
@property
|
|
202
|
+
def size(self) -> int | None:
|
|
203
|
+
"""Get or sets the size of the file, in bytes."""
|
|
204
|
+
return self._get_property(SIZE_PROP, int)
|
|
205
|
+
|
|
206
|
+
@size.setter
|
|
207
|
+
def size(self, v: int | None) -> None:
|
|
208
|
+
self._set_property(SIZE_PROP, v)
|
|
209
|
+
|
|
210
|
+
@property
|
|
211
|
+
def values(self) -> list[MappingObject] | None:
|
|
212
|
+
"""Get or sets the list of :class:`~MappingObject` instances that lists the
|
|
213
|
+
values that are in the file and describe their meaning. See the
|
|
214
|
+
:stac-ext:`Mapping Object <file#mapping-object>` docs for an example. If given,
|
|
215
|
+
at least one array element is required."""
|
|
216
|
+
return map_opt(
|
|
217
|
+
lambda values: [
|
|
218
|
+
MappingObject.from_dict(mapping_obj) for mapping_obj in values
|
|
219
|
+
],
|
|
220
|
+
self._get_property(VALUES_PROP, list[dict[str, Any]]),
|
|
221
|
+
)
|
|
222
|
+
|
|
223
|
+
@values.setter
|
|
224
|
+
def values(self, v: list[MappingObject] | None) -> None:
|
|
225
|
+
self._set_property(
|
|
226
|
+
VALUES_PROP,
|
|
227
|
+
map_opt(
|
|
228
|
+
lambda values: [mapping_obj.to_dict() for mapping_obj in values], v
|
|
229
|
+
),
|
|
230
|
+
)
|
|
231
|
+
|
|
232
|
+
@classmethod
|
|
233
|
+
def get_schema_uri(cls) -> str:
|
|
234
|
+
return SCHEMA_URI
|
|
235
|
+
|
|
236
|
+
@classmethod
|
|
237
|
+
def ext(cls, obj: Asset | Link, add_if_missing: bool = False) -> FileExtension[T]:
|
|
238
|
+
"""Extends the given STAC Object with properties from the :stac-ext:`File Info
|
|
239
|
+
Extension <file>`.
|
|
240
|
+
|
|
241
|
+
This extension can be applied to instances of :class:`~pystac.Asset` or
|
|
242
|
+
:class:`~pystac.Link`
|
|
243
|
+
"""
|
|
244
|
+
if isinstance(obj, Asset):
|
|
245
|
+
cls.ensure_owner_has_extension(obj, add_if_missing)
|
|
246
|
+
return cast(FileExtension[T], AssetFileExtension(obj))
|
|
247
|
+
elif isinstance(obj, Link):
|
|
248
|
+
cls.ensure_owner_has_extension(obj, add_if_missing)
|
|
249
|
+
return cast(FileExtension[T], LinkFileExtension(obj))
|
|
250
|
+
else:
|
|
251
|
+
raise ExtensionTypeError(cls._ext_error_message(obj))
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
class AssetFileExtension(FileExtension[Asset]):
|
|
255
|
+
"""A concrete implementation of :class:`FileExtension` on an
|
|
256
|
+
:class:`~pystac.Asset` that extends the Asset fields to include properties defined
|
|
257
|
+
in the :stac-ext:`File Info Extension <file>`.
|
|
258
|
+
|
|
259
|
+
This class should generally not be instantiated directly. Instead, call
|
|
260
|
+
:meth:`FileExtension.ext` on an :class:`~pystac.Asset` to extend it.
|
|
261
|
+
"""
|
|
262
|
+
|
|
263
|
+
asset_href: str
|
|
264
|
+
"""The ``href`` value of the :class:`~pystac.Asset` being extended."""
|
|
265
|
+
|
|
266
|
+
properties: dict[str, Any]
|
|
267
|
+
"""The :class:`~pystac.Asset` fields, including extension properties."""
|
|
268
|
+
|
|
269
|
+
additional_read_properties: Iterable[dict[str, Any]] | None = None
|
|
270
|
+
"""If present, this will be a list containing 1 dictionary representing the
|
|
271
|
+
properties of the owner."""
|
|
272
|
+
|
|
273
|
+
def __init__(self, asset: Asset):
|
|
274
|
+
self.asset_href = asset.href
|
|
275
|
+
self.properties = asset.extra_fields
|
|
276
|
+
if asset.owner and hasattr(asset.owner, "properties"):
|
|
277
|
+
self.additional_read_properties = [asset.owner.properties]
|
|
278
|
+
|
|
279
|
+
def __repr__(self) -> str:
|
|
280
|
+
return f"<AssetFileExtension Asset href={self.asset_href}>"
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
class LinkFileExtension(FileExtension[Link]):
|
|
284
|
+
"""A concrete implementation of :class:`FileExtension` on an
|
|
285
|
+
:class:`~pystac.Link` that extends the Link fields to include properties defined
|
|
286
|
+
in the :stac-ext:`File Info Extension <file>`.
|
|
287
|
+
|
|
288
|
+
This class should generally not be instantiated directly. Instead, call
|
|
289
|
+
:meth:`FileExtension.ext` on an :class:`~pystac.Link` to extend it.
|
|
290
|
+
"""
|
|
291
|
+
|
|
292
|
+
link_href: str
|
|
293
|
+
"""The ``href`` value of the :class:`~pystac.Link` being extended."""
|
|
294
|
+
|
|
295
|
+
properties: dict[str, Any]
|
|
296
|
+
"""The :class:`~pystac.Link` fields, including extension properties."""
|
|
297
|
+
|
|
298
|
+
additional_read_properties: Iterable[dict[str, Any]] | None = None
|
|
299
|
+
"""If present, this will be a list containing 1 dictionary representing the
|
|
300
|
+
properties of the owner."""
|
|
301
|
+
|
|
302
|
+
def __init__(self, link: Link):
|
|
303
|
+
self.link_href = link.href
|
|
304
|
+
self.properties = link.extra_fields
|
|
305
|
+
if link.owner and hasattr(link.owner, "properties"):
|
|
306
|
+
self.additional_read_properties = [link.owner.properties]
|
|
307
|
+
|
|
308
|
+
def __repr__(self) -> str:
|
|
309
|
+
return f"<LinkFileExtension Link href={self.link_href}>"
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
class FileExtensionHooks(ExtensionHooks):
|
|
313
|
+
schema_uri: str = SCHEMA_URI
|
|
314
|
+
prev_extension_ids = {
|
|
315
|
+
"file",
|
|
316
|
+
"https://stac-extensions.github.io/file/v1.0.0/schema.json",
|
|
317
|
+
"https://stac-extensions.github.io/file/v2.0.0/schema.json",
|
|
318
|
+
}
|
|
319
|
+
stac_object_types = {
|
|
320
|
+
STACObjectType.ITEM,
|
|
321
|
+
STACObjectType.COLLECTION,
|
|
322
|
+
STACObjectType.CATALOG,
|
|
323
|
+
}
|
|
324
|
+
removed_fields = {
|
|
325
|
+
"file:bits_per_sample",
|
|
326
|
+
"file:data_type",
|
|
327
|
+
"file:nodata",
|
|
328
|
+
"file:unit",
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
def migrate(
|
|
332
|
+
self, obj: dict[str, Any], version: STACVersionID, info: STACJSONDescription
|
|
333
|
+
) -> None:
|
|
334
|
+
# The checksum field was previously its own extension.
|
|
335
|
+
old_checksum: dict[str, str] | None = None
|
|
336
|
+
if info.version_range.latest_valid_version() < "v1.0.0-rc.2":
|
|
337
|
+
if OldExtensionShortIDs.CHECKSUM.value in info.extensions:
|
|
338
|
+
old_item_checksum = obj["properties"].get("checksum:multihash")
|
|
339
|
+
if old_item_checksum is not None:
|
|
340
|
+
if old_checksum is None:
|
|
341
|
+
old_checksum = {}
|
|
342
|
+
old_checksum["__item__"] = old_item_checksum
|
|
343
|
+
for asset_key, asset in obj["assets"].items():
|
|
344
|
+
old_asset_checksum = asset.get("checksum:multihash")
|
|
345
|
+
if old_asset_checksum is not None:
|
|
346
|
+
if old_checksum is None:
|
|
347
|
+
old_checksum = {}
|
|
348
|
+
old_checksum[asset_key] = old_asset_checksum
|
|
349
|
+
|
|
350
|
+
try:
|
|
351
|
+
obj["stac_extensions"].remove(OldExtensionShortIDs.CHECKSUM.value)
|
|
352
|
+
except ValueError:
|
|
353
|
+
pass
|
|
354
|
+
|
|
355
|
+
super().migrate(obj, version, info)
|
|
356
|
+
|
|
357
|
+
if old_checksum is not None:
|
|
358
|
+
if SCHEMA_URI not in obj["stac_extensions"]:
|
|
359
|
+
obj["stac_extensions"].append(SCHEMA_URI)
|
|
360
|
+
for key in old_checksum:
|
|
361
|
+
if key == "__item__":
|
|
362
|
+
obj["properties"][CHECKSUM_PROP] = old_checksum[key]
|
|
363
|
+
else:
|
|
364
|
+
obj["assets"][key][CHECKSUM_PROP] = old_checksum[key]
|
|
365
|
+
|
|
366
|
+
found_fields = {}
|
|
367
|
+
for asset_key, asset in obj.get("assets", {}).items():
|
|
368
|
+
if values := set(asset.keys()).intersection(self.removed_fields):
|
|
369
|
+
found_fields[asset_key] = values
|
|
370
|
+
|
|
371
|
+
if found_fields:
|
|
372
|
+
import warnings
|
|
373
|
+
|
|
374
|
+
warnings.warn(
|
|
375
|
+
f"Assets {list(found_fields.keys())} contain fields: "
|
|
376
|
+
f"{list(set.union(*found_fields.values()))} which "
|
|
377
|
+
"were removed from the file extension spec in v2.0.0. Please "
|
|
378
|
+
"consult the release notes "
|
|
379
|
+
"(https://github.com/stac-extensions/file/releases/tag/v2.0.0) "
|
|
380
|
+
"for instructions on how to migrate these fields.",
|
|
381
|
+
UserWarning,
|
|
382
|
+
)
|
|
383
|
+
|
|
384
|
+
|
|
385
|
+
FILE_EXTENSION_HOOKS: ExtensionHooks = FileExtensionHooks()
|
|
File without changes
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
interactions:
|
|
2
|
+
- request:
|
|
3
|
+
body: null
|
|
4
|
+
headers: {}
|
|
5
|
+
method: GET
|
|
6
|
+
uri: https://raw.githubusercontent.com/stac-extensions/file/v1.0.0/examples/item.json
|
|
7
|
+
response:
|
|
8
|
+
body:
|
|
9
|
+
string: "{\n \"id\": \"S1A_EW_GRDM_1SSH_20181103T235855_20181103T235955_024430_02AD5D_5616\",\n
|
|
10
|
+
\ \"type\": \"Feature\",\n \"stac_version\": \"1.0.0-beta.2\",\n \"stac_extensions\":
|
|
11
|
+
[\n \"https://stac-extensions.github.io/file/v1.0.0/schema.json\"\n ],\n
|
|
12
|
+
\ \"bbox\": [\n -70.275032,\n -64.72924,\n -65.087479,\n -51.105831\n
|
|
13
|
+
\ ],\n \"geometry\": {\n \"type\": \"Polygon\",\n \"coordinates\":
|
|
14
|
+
[\n [\n [\n -67.071648,\n -64.72924\n ],\n
|
|
15
|
+
\ [\n -65.087479,\n -56.674374\n ],\n [\n
|
|
16
|
+
\ -68.033211,\n -51.105831\n ],\n [\n -70.275032,\n
|
|
17
|
+
\ -59.805672\n ],\n [\n -67.071648,\n -64.72924\n
|
|
18
|
+
\ ]\n ]\n ]\n },\n \"properties\": {\n \"datetime\": \"2018-11-03T23:58:55Z\"\n
|
|
19
|
+
\ },\n \"assets\": {\n \"noises\": {\n \"href\": \"./annotation/calibration/noise-s1a-ew-grd-hh-20181103t235855-20181103t235955-024430-02ad5d-001.xml\",\n
|
|
20
|
+
\ \"title\": \"Calibration Schema\",\n \"type\": \"text/xml\",\n
|
|
21
|
+
\ \"file:checksum\": \"90e40210a30d1711e81a4b11ef67b28744321659\"\n },\n
|
|
22
|
+
\ \"calibrations\": {\n \"href\": \"./annotation/calibration/calibration-s1a-ew-grd-hh-20181103t235855-20181103t235955-024430-02ad5d-001.xml\",\n
|
|
23
|
+
\ \"title\": \"Noise Schema\",\n \"type\": \"text/xml\",\n \"file:checksum\":
|
|
24
|
+
\"90e402104fc5351af67db0b8f1746efe421a05e4\"\n },\n \"products\": {\n
|
|
25
|
+
\ \"href\": \"./annotation/s1a-ew-grd-hh-20181103t235855-20181103t235955-024430-02ad5d-001.xml\",\n
|
|
26
|
+
\ \"title\": \"Product Schema\",\n \"type\": \"text/xml\",\n \"file:checksum\":
|
|
27
|
+
\"90e402107a7f2588a85362b9beea2a12d4514d45\"\n },\n \"measurement\":
|
|
28
|
+
{\n \"href\": \"./measurement/s1a-ew-grd-hh-20181103t235855-20181103t235955-024430-02ad5d-001.tiff\",\n
|
|
29
|
+
\ \"title\": \"Measurements\",\n \"type\": \"image/tiff\",\n \"sar:polarizations\":
|
|
30
|
+
[\n \"HH\"\n ],\n \"file:byte_order\": \"little-endian\",\n
|
|
31
|
+
\ \"file:data_type\": \"uint16\",\n \"file:size\": 209715200,\n \"file:header_size\":
|
|
32
|
+
4096,\n \"file:checksum\": \"90e40210163700a8a6501eccd00b6d3b44ddaed0\"\n
|
|
33
|
+
\ },\n \"thumbnail\": {\n \"href\": \"./preview/quick-look.png\",\n
|
|
34
|
+
\ \"title\": \"Thumbnail\",\n \"type\": \"image/png\",\n \"file:byte_order\":
|
|
35
|
+
\"big-endian\",\n \"file:data_type\": \"uint8\",\n \"file:size\":
|
|
36
|
+
146484,\n \"file:checksum\": \"90e40210f52acd32b09769d3b1871b420789456c\"\n
|
|
37
|
+
\ }\n },\n \"links\": [\n {\n \"rel\": \"self\",\n \"href\":
|
|
38
|
+
\"https://example.com/collections/sentinel-1/items/S1A_EW_GRDM_1SSH_20181103T235855_20181103T235955_024430_02AD5D_5616\"\n
|
|
39
|
+
\ },\n {\n \"rel\": \"parent\",\n \"href\": \"https://example.com/collections/sentinel-1\",\n
|
|
40
|
+
\ \"file:checksum\": \"11146d97123fd2c02dec9a1b6d3b13136dbe600cf966\"\n
|
|
41
|
+
\ },\n {\n \"rel\": \"root\",\n \"href\": \"https://example.com/collections\",\n
|
|
42
|
+
\ \"file:checksum\": \"1114fa4b9d69fdddc7c1be7bed9440621400b383b43f\"\n
|
|
43
|
+
\ }\n ]\n}"
|
|
44
|
+
headers: {}
|
|
45
|
+
status:
|
|
46
|
+
code: 200
|
|
47
|
+
message: OK
|
|
48
|
+
version: 1
|