pystac-ext-label 1.0.1__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.
|
@@ -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,38 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pystac-ext-label
|
|
3
|
+
Version: 1.0.1
|
|
4
|
+
Summary: Label 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,imagery,label,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-label
|
|
26
|
+
|
|
27
|
+
[PySTAC](https://pypi.org/project/pystac/) extension package for the [Label Extension](https://github.com/stac-extensions/label).
|
|
28
|
+
This extension provides fields for describing labeled training data for machine learning, including label types, classes, tasks, methods, and overviews of label distributions.
|
|
29
|
+
|
|
30
|
+
## Supported versions
|
|
31
|
+
|
|
32
|
+
- [v1.0.1](https://stac-extensions.github.io/label/v1.0.1/schema.json)
|
|
33
|
+
- [v1.0.0](https://stac-extensions.github.io/label/v1.0.0/schema.json)
|
|
34
|
+
|
|
35
|
+
## Versioning
|
|
36
|
+
|
|
37
|
+
This package's version corresponds to the version of the extension specification it targets.
|
|
38
|
+
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. `1.0.1.post1`.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# pystac-ext-label
|
|
2
|
+
|
|
3
|
+
[PySTAC](https://pypi.org/project/pystac/) extension package for the [Label Extension](https://github.com/stac-extensions/label).
|
|
4
|
+
This extension provides fields for describing labeled training data for machine learning, including label types, classes, tasks, methods, and overviews of label distributions.
|
|
5
|
+
|
|
6
|
+
## Supported versions
|
|
7
|
+
|
|
8
|
+
- [v1.0.1](https://stac-extensions.github.io/label/v1.0.1/schema.json)
|
|
9
|
+
- [v1.0.0](https://stac-extensions.github.io/label/v1.0.0/schema.json)
|
|
10
|
+
|
|
11
|
+
## Versioning
|
|
12
|
+
|
|
13
|
+
This package's version corresponds to the version of the extension specification it targets.
|
|
14
|
+
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. `1.0.1.post1`.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "pystac-ext-label"
|
|
3
|
+
description = "Label extension for PySTAC"
|
|
4
|
+
readme = "README.md"
|
|
5
|
+
version = "1.0.1"
|
|
6
|
+
authors = []
|
|
7
|
+
maintainers = []
|
|
8
|
+
keywords = ["pystac", "imagery", "raster", "catalog", "STAC", "label"]
|
|
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,851 @@
|
|
|
1
|
+
"""Implements the :stac-ext:`Label Extension <label>`."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import warnings
|
|
6
|
+
from collections.abc import Iterable, Sequence
|
|
7
|
+
from typing import Any, Literal, cast
|
|
8
|
+
|
|
9
|
+
import pystac
|
|
10
|
+
from pystac.extensions.base import ExtensionManagementMixin, SummariesExtension
|
|
11
|
+
from pystac.extensions.hooks import ExtensionHooks
|
|
12
|
+
from pystac.serialization.identify import STACJSONDescription, STACVersionID
|
|
13
|
+
from pystac.utils import StringEnum, get_required, map_opt
|
|
14
|
+
|
|
15
|
+
warnings.warn(
|
|
16
|
+
"The PySTAC Label Extension is deprecated. The extension itself "
|
|
17
|
+
'(https://github.com/stac-extensions/label) is currently unmaintained, in "pilot" '
|
|
18
|
+
"maturity, and has significant issues.",
|
|
19
|
+
DeprecationWarning,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
SCHEMA_URI = "https://stac-extensions.github.io/label/v1.0.1/schema.json"
|
|
23
|
+
SCHEMA_URIS = [
|
|
24
|
+
"https://stac-extensions.github.io/label/v1.0.0/schema.json",
|
|
25
|
+
SCHEMA_URI,
|
|
26
|
+
]
|
|
27
|
+
PREFIX = "label:"
|
|
28
|
+
|
|
29
|
+
PROPERTIES_PROP = PREFIX + "properties"
|
|
30
|
+
CLASSES_PROP = PREFIX + "classes"
|
|
31
|
+
DESCRIPTION_PROP = PREFIX + "description"
|
|
32
|
+
TYPE_PROP = PREFIX + "type"
|
|
33
|
+
TASKS_PROP = PREFIX + "tasks"
|
|
34
|
+
METHODS_PROP = PREFIX + "methods"
|
|
35
|
+
OVERVIEWS_PROP = PREFIX + "overviews"
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class LabelRelType(StringEnum):
|
|
39
|
+
"""A list of rel types defined in the Label Extension.
|
|
40
|
+
|
|
41
|
+
See the :stac-ext:`Label Extension Links <label#links-source-imagery>`
|
|
42
|
+
documentation for details.
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
SOURCE = "source"
|
|
46
|
+
"""Used to indicate a link to the source item to which a label item applies."""
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class LabelType(StringEnum):
|
|
50
|
+
"""Enumerates valid label types ("raster" or "vector")."""
|
|
51
|
+
|
|
52
|
+
VECTOR = "vector"
|
|
53
|
+
RASTER = "raster"
|
|
54
|
+
|
|
55
|
+
ALL = [VECTOR, RASTER]
|
|
56
|
+
"""Convenience attribute for checking if values are valid label types"""
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class LabelTask(StringEnum):
|
|
60
|
+
"""Enumerates recommended values for "label:tasks" field."""
|
|
61
|
+
|
|
62
|
+
REGRESSION = "regression"
|
|
63
|
+
CLASSIFICATION = "classification"
|
|
64
|
+
DETECTION = "detection"
|
|
65
|
+
SEGMENTATION = "segmentation"
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class LabelMethod(StringEnum):
|
|
69
|
+
"""Enumerates recommended values for "label:methods" field."""
|
|
70
|
+
|
|
71
|
+
AUTOMATED = "automated"
|
|
72
|
+
MANUAL = "manual"
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
class LabelClasses:
|
|
76
|
+
"""Defines the list of possible class names (e.g., tree, building, car, hippo).
|
|
77
|
+
|
|
78
|
+
Use :meth:`LabelClasses.create` to create a new instance from property values.
|
|
79
|
+
"""
|
|
80
|
+
|
|
81
|
+
properties: dict[str, Any]
|
|
82
|
+
|
|
83
|
+
def __init__(self, properties: dict[str, Any]):
|
|
84
|
+
self.properties = properties
|
|
85
|
+
|
|
86
|
+
def apply(
|
|
87
|
+
self,
|
|
88
|
+
classes: Sequence[str | int | float],
|
|
89
|
+
name: str | None = None,
|
|
90
|
+
) -> None:
|
|
91
|
+
"""Sets the properties for this instance.
|
|
92
|
+
|
|
93
|
+
Args:
|
|
94
|
+
classes : The different possible class values.
|
|
95
|
+
name : The property key within the asset's each Feature corresponding
|
|
96
|
+
to class labels. If labels are raster-formatted, do not supply;
|
|
97
|
+
required otherwise.
|
|
98
|
+
"""
|
|
99
|
+
self.classes = classes
|
|
100
|
+
self.name = name
|
|
101
|
+
|
|
102
|
+
@classmethod
|
|
103
|
+
def create(
|
|
104
|
+
cls,
|
|
105
|
+
classes: Sequence[str | int | float],
|
|
106
|
+
name: str | None = None,
|
|
107
|
+
) -> LabelClasses:
|
|
108
|
+
"""Creates a new :class:`~LabelClasses` instance.
|
|
109
|
+
|
|
110
|
+
Args:
|
|
111
|
+
classes : The different possible class values.
|
|
112
|
+
name : The property key within the asset's each Feature corresponding
|
|
113
|
+
to class labels. If labels are raster-formatted, do not supply;
|
|
114
|
+
required otherwise.
|
|
115
|
+
"""
|
|
116
|
+
c = cls({})
|
|
117
|
+
c.apply(classes, name)
|
|
118
|
+
return c
|
|
119
|
+
|
|
120
|
+
@property
|
|
121
|
+
def classes(self) -> Sequence[str | int | float]:
|
|
122
|
+
"""Gets or sets the class values."""
|
|
123
|
+
return get_required(self.properties.get("classes"), self, "classes")
|
|
124
|
+
|
|
125
|
+
@classes.setter
|
|
126
|
+
def classes(self, v: Sequence[str | int | float]) -> None:
|
|
127
|
+
self.properties["classes"] = v
|
|
128
|
+
|
|
129
|
+
@property
|
|
130
|
+
def name(self) -> str | None:
|
|
131
|
+
"""Gets or sets the property key within each Feature in the asset corresponding
|
|
132
|
+
to class labels. If labels are raster-formatted, use ``None``.
|
|
133
|
+
"""
|
|
134
|
+
return self.properties.get("name")
|
|
135
|
+
|
|
136
|
+
@name.setter
|
|
137
|
+
def name(self, v: str | None) -> None:
|
|
138
|
+
# The "name" property is required but may be null
|
|
139
|
+
self.properties["name"] = v
|
|
140
|
+
|
|
141
|
+
def __repr__(self) -> str:
|
|
142
|
+
return "<ClassObject classes={}>".format(
|
|
143
|
+
",".join([str(x) for x in self.classes])
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
def __eq__(self, o: object) -> bool:
|
|
147
|
+
if isinstance(o, LabelClasses):
|
|
148
|
+
o = o.to_dict()
|
|
149
|
+
|
|
150
|
+
if not isinstance(o, dict):
|
|
151
|
+
return NotImplemented
|
|
152
|
+
|
|
153
|
+
return self.to_dict() == o
|
|
154
|
+
|
|
155
|
+
def to_dict(self) -> dict[str, Any]:
|
|
156
|
+
"""Returns this label classes object as a dictionary."""
|
|
157
|
+
return self.properties
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
class LabelCount:
|
|
161
|
+
"""Contains counts for categorical data.
|
|
162
|
+
|
|
163
|
+
Use :meth:`LabelCount.create` to create a new instance.
|
|
164
|
+
"""
|
|
165
|
+
|
|
166
|
+
properties: dict[str, Any]
|
|
167
|
+
|
|
168
|
+
def __init__(self, properties: dict[str, Any]):
|
|
169
|
+
self.properties = properties
|
|
170
|
+
|
|
171
|
+
def apply(self, name: str, count: int) -> None:
|
|
172
|
+
"""Sets the properties for this instance.
|
|
173
|
+
|
|
174
|
+
Args:
|
|
175
|
+
name : One of the different possible classes within the property.
|
|
176
|
+
count : The number of occurrences of the class.
|
|
177
|
+
"""
|
|
178
|
+
self.name = name
|
|
179
|
+
self.count = count
|
|
180
|
+
|
|
181
|
+
@classmethod
|
|
182
|
+
def create(cls, name: str, count: int) -> LabelCount:
|
|
183
|
+
"""Creates a :class:`LabelCount` instance.
|
|
184
|
+
|
|
185
|
+
Args:
|
|
186
|
+
name : One of the different possible classes within the property.
|
|
187
|
+
count : The number of occurrences of the class.
|
|
188
|
+
"""
|
|
189
|
+
x = cls({})
|
|
190
|
+
x.apply(name, count)
|
|
191
|
+
return x
|
|
192
|
+
|
|
193
|
+
@property
|
|
194
|
+
def name(self) -> str:
|
|
195
|
+
"""Gets or sets the class that this count represents."""
|
|
196
|
+
return cast(str, get_required(self.properties.get("name"), self, "name"))
|
|
197
|
+
|
|
198
|
+
@name.setter
|
|
199
|
+
def name(self, v: str) -> None:
|
|
200
|
+
self.properties["name"] = v
|
|
201
|
+
|
|
202
|
+
@property
|
|
203
|
+
def count(self) -> int:
|
|
204
|
+
"""Get or sets the number of occurrences of the class."""
|
|
205
|
+
return cast(int, get_required(self.properties.get("count"), self, "count"))
|
|
206
|
+
|
|
207
|
+
@count.setter
|
|
208
|
+
def count(self, v: int) -> None:
|
|
209
|
+
self.properties["count"] = v
|
|
210
|
+
|
|
211
|
+
def to_dict(self) -> dict[str, Any]:
|
|
212
|
+
"""Returns this label count object as a dictionary."""
|
|
213
|
+
return self.properties
|
|
214
|
+
|
|
215
|
+
def __eq__(self, o: object) -> bool:
|
|
216
|
+
if isinstance(o, LabelCount):
|
|
217
|
+
o = o.to_dict()
|
|
218
|
+
|
|
219
|
+
if not isinstance(o, dict):
|
|
220
|
+
return NotImplemented
|
|
221
|
+
|
|
222
|
+
return self.to_dict() == o
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
class LabelStatistics:
|
|
226
|
+
"""Contains statistics for regression/continuous numeric value data.
|
|
227
|
+
|
|
228
|
+
Use :meth:`LabelStatistics.create` to create a new instance.
|
|
229
|
+
"""
|
|
230
|
+
|
|
231
|
+
properties: dict[str, Any]
|
|
232
|
+
|
|
233
|
+
def __init__(self, properties: dict[str, Any]) -> None:
|
|
234
|
+
self.properties = properties
|
|
235
|
+
|
|
236
|
+
def apply(self, name: str, value: float) -> None:
|
|
237
|
+
"""Sets the property values for this instance.
|
|
238
|
+
|
|
239
|
+
Args:
|
|
240
|
+
name : The name of the statistic being reported.
|
|
241
|
+
value : The value of the statistic
|
|
242
|
+
"""
|
|
243
|
+
self.name = name
|
|
244
|
+
self.value = value
|
|
245
|
+
|
|
246
|
+
@classmethod
|
|
247
|
+
def create(cls, name: str, value: float) -> LabelStatistics:
|
|
248
|
+
"""Creates a new :class:`LabelStatistics` instance.
|
|
249
|
+
|
|
250
|
+
Args:
|
|
251
|
+
name : The name of the statistic being reported.
|
|
252
|
+
value : The value of the statistic
|
|
253
|
+
"""
|
|
254
|
+
x = cls({})
|
|
255
|
+
x.apply(name, value)
|
|
256
|
+
return x
|
|
257
|
+
|
|
258
|
+
@property
|
|
259
|
+
def name(self) -> str:
|
|
260
|
+
"""Gets or sets the name of the statistic being reported."""
|
|
261
|
+
return cast(str, get_required(self.properties.get("name"), self, "name"))
|
|
262
|
+
|
|
263
|
+
@name.setter
|
|
264
|
+
def name(self, v: str) -> None:
|
|
265
|
+
self.properties["name"] = v
|
|
266
|
+
|
|
267
|
+
@property
|
|
268
|
+
def value(self) -> float:
|
|
269
|
+
"""Gets or sets the value of the statistic."""
|
|
270
|
+
return cast(float, get_required(self.properties.get("value"), self, "value"))
|
|
271
|
+
|
|
272
|
+
@value.setter
|
|
273
|
+
def value(self, v: float) -> None:
|
|
274
|
+
self.properties["value"] = v
|
|
275
|
+
|
|
276
|
+
def to_dict(self) -> dict[str, Any]:
|
|
277
|
+
"""Returns this label statistics object as a dictionary."""
|
|
278
|
+
return self.properties
|
|
279
|
+
|
|
280
|
+
def __eq__(self, o: object) -> bool:
|
|
281
|
+
if isinstance(o, LabelStatistics):
|
|
282
|
+
o = o.to_dict()
|
|
283
|
+
|
|
284
|
+
if not isinstance(o, dict):
|
|
285
|
+
return NotImplemented
|
|
286
|
+
|
|
287
|
+
return self.to_dict() == o
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
class LabelOverview:
|
|
291
|
+
"""Stores counts (for classification-type data) or summary statistics (for
|
|
292
|
+
continuous numerical/regression data).
|
|
293
|
+
|
|
294
|
+
Use :meth:`LabelOverview.create` to create a new instance.
|
|
295
|
+
"""
|
|
296
|
+
|
|
297
|
+
properties: dict[str, Any]
|
|
298
|
+
|
|
299
|
+
def __init__(self, properties: dict[str, Any]):
|
|
300
|
+
self.properties = properties
|
|
301
|
+
|
|
302
|
+
def apply(
|
|
303
|
+
self,
|
|
304
|
+
property_key: str | None,
|
|
305
|
+
counts: list[LabelCount] | None = None,
|
|
306
|
+
statistics: list[LabelStatistics] | None = None,
|
|
307
|
+
) -> None:
|
|
308
|
+
"""Sets the properties for this instance.
|
|
309
|
+
|
|
310
|
+
Either ``counts`` or ``statistics``, or both, can be placed in an overview;
|
|
311
|
+
at least one is required.
|
|
312
|
+
|
|
313
|
+
Args:
|
|
314
|
+
property_key : The property key within the asset corresponding to
|
|
315
|
+
class labels that these counts or statistics are referencing. If the
|
|
316
|
+
label data is raster data, this should be None.
|
|
317
|
+
counts: Optional list of :class:`LabelCounts` containing counts
|
|
318
|
+
for categorical data.
|
|
319
|
+
statistics: Optional list of :class:`LabelStatistics` containing statistics
|
|
320
|
+
for regression/continuous numeric value data.
|
|
321
|
+
"""
|
|
322
|
+
self.property_key = property_key
|
|
323
|
+
self.counts = counts
|
|
324
|
+
self.statistics = statistics
|
|
325
|
+
|
|
326
|
+
@classmethod
|
|
327
|
+
def create(
|
|
328
|
+
cls,
|
|
329
|
+
property_key: str | None,
|
|
330
|
+
counts: list[LabelCount] | None = None,
|
|
331
|
+
statistics: list[LabelStatistics] | None = None,
|
|
332
|
+
) -> LabelOverview:
|
|
333
|
+
"""Creates a new instance.
|
|
334
|
+
|
|
335
|
+
Either ``counts`` or ``statistics``, or both, can be placed in an overview;
|
|
336
|
+
at least one is required.
|
|
337
|
+
|
|
338
|
+
Args:
|
|
339
|
+
property_key : The property key within the asset corresponding to
|
|
340
|
+
class labels.
|
|
341
|
+
counts: Optional list of :class:`LabelCounts` containing counts for
|
|
342
|
+
categorical data.
|
|
343
|
+
statistics: Optional list of :class:`LabelStatistics` containing statistics
|
|
344
|
+
for regression/continuous numeric value data.
|
|
345
|
+
"""
|
|
346
|
+
x = LabelOverview({})
|
|
347
|
+
x.apply(property_key, counts, statistics)
|
|
348
|
+
return x
|
|
349
|
+
|
|
350
|
+
@property
|
|
351
|
+
def property_key(self) -> str | None:
|
|
352
|
+
"""Gets or sets the property key within the asset corresponding to class
|
|
353
|
+
labels."""
|
|
354
|
+
return self.properties.get("property_key")
|
|
355
|
+
|
|
356
|
+
@property_key.setter
|
|
357
|
+
def property_key(self, v: str | None) -> None:
|
|
358
|
+
self.properties["property_key"] = v
|
|
359
|
+
|
|
360
|
+
@property
|
|
361
|
+
def counts(self) -> list[LabelCount] | None:
|
|
362
|
+
"""Gets or sets the list of :class:`LabelCounts` containing counts for
|
|
363
|
+
categorical data."""
|
|
364
|
+
counts = self.properties.get("counts")
|
|
365
|
+
if counts is None:
|
|
366
|
+
return None
|
|
367
|
+
return [LabelCount(c) for c in counts]
|
|
368
|
+
|
|
369
|
+
@counts.setter
|
|
370
|
+
def counts(self, v: list[LabelCount] | None) -> None:
|
|
371
|
+
if v is None:
|
|
372
|
+
self.properties.pop("counts", None)
|
|
373
|
+
else:
|
|
374
|
+
if not isinstance(v, list):
|
|
375
|
+
raise pystac.STACError(f"counts must be a list! Invalid input: {v}")
|
|
376
|
+
|
|
377
|
+
self.properties["counts"] = [c.to_dict() for c in v]
|
|
378
|
+
|
|
379
|
+
@property
|
|
380
|
+
def statistics(self) -> list[LabelStatistics] | None:
|
|
381
|
+
"""Gets or sets the list of :class:`LabelStatistics` containing statistics for
|
|
382
|
+
regression/continuous numeric value data."""
|
|
383
|
+
statistics = self.properties.get("statistics")
|
|
384
|
+
if statistics is None:
|
|
385
|
+
return None
|
|
386
|
+
|
|
387
|
+
return [LabelStatistics(s) for s in statistics]
|
|
388
|
+
|
|
389
|
+
@statistics.setter
|
|
390
|
+
def statistics(self, v: list[LabelStatistics] | None) -> None:
|
|
391
|
+
if v is None:
|
|
392
|
+
self.properties.pop("statistics", None)
|
|
393
|
+
else:
|
|
394
|
+
self.properties["statistics"] = [s.to_dict() for s in v]
|
|
395
|
+
|
|
396
|
+
def merge_counts(self, other: LabelOverview) -> LabelOverview:
|
|
397
|
+
"""Merges the counts associated with this overview with another overview.
|
|
398
|
+
Creates a new instance.
|
|
399
|
+
|
|
400
|
+
Args:
|
|
401
|
+
other : The other LabelOverview to merge.
|
|
402
|
+
|
|
403
|
+
Returns:
|
|
404
|
+
A new LabelOverview with the counts merged. This will
|
|
405
|
+
drop any statistics associated with either of the LabelOverviews.
|
|
406
|
+
"""
|
|
407
|
+
assert self.property_key == other.property_key
|
|
408
|
+
|
|
409
|
+
if self.counts is None:
|
|
410
|
+
new_counts = other.counts
|
|
411
|
+
else:
|
|
412
|
+
if other.counts is None:
|
|
413
|
+
new_counts = self.counts
|
|
414
|
+
else:
|
|
415
|
+
count_by_prop: dict[str, int] = {}
|
|
416
|
+
|
|
417
|
+
def add_counts(counts: list[LabelCount]) -> None:
|
|
418
|
+
for c in counts:
|
|
419
|
+
if c.name not in count_by_prop:
|
|
420
|
+
count_by_prop[c.name] = c.count
|
|
421
|
+
else:
|
|
422
|
+
count_by_prop[c.name] += c.count
|
|
423
|
+
|
|
424
|
+
add_counts(self.counts)
|
|
425
|
+
add_counts(other.counts)
|
|
426
|
+
new_counts = [LabelCount.create(k, v) for k, v in count_by_prop.items()]
|
|
427
|
+
return LabelOverview.create(self.property_key, counts=new_counts)
|
|
428
|
+
|
|
429
|
+
def to_dict(self) -> dict[str, Any]:
|
|
430
|
+
"""Returns this label overview as a dictionary."""
|
|
431
|
+
return self.properties
|
|
432
|
+
|
|
433
|
+
def __eq__(self, o: object) -> bool:
|
|
434
|
+
if isinstance(o, LabelOverview):
|
|
435
|
+
o = o.to_dict()
|
|
436
|
+
|
|
437
|
+
if not isinstance(o, dict):
|
|
438
|
+
return NotImplemented
|
|
439
|
+
|
|
440
|
+
return self.to_dict() == o
|
|
441
|
+
|
|
442
|
+
|
|
443
|
+
class LabelExtension(ExtensionManagementMixin[pystac.Item | pystac.Collection]):
|
|
444
|
+
"""A class that can be used to extend the properties of an
|
|
445
|
+
:class:`~pystac.Item` with properties from the :stac-ext:`Label Extension <label>`.
|
|
446
|
+
|
|
447
|
+
To create an instance of :class:`LabeExtension`, use the
|
|
448
|
+
:meth:`LabelExtension.ext` method. For example:
|
|
449
|
+
|
|
450
|
+
.. code-block:: python
|
|
451
|
+
|
|
452
|
+
>>> item: pystac.Item = ...
|
|
453
|
+
>>> label_ext = LabelExtension.ext(item)
|
|
454
|
+
"""
|
|
455
|
+
|
|
456
|
+
name: Literal["label"] = "label"
|
|
457
|
+
obj: pystac.Item
|
|
458
|
+
schema_uri: str
|
|
459
|
+
|
|
460
|
+
def __init__(self, item: pystac.Item) -> None:
|
|
461
|
+
self.obj = item
|
|
462
|
+
self.schema_uri = SCHEMA_URI
|
|
463
|
+
|
|
464
|
+
def apply(
|
|
465
|
+
self,
|
|
466
|
+
label_description: str,
|
|
467
|
+
label_type: LabelType,
|
|
468
|
+
label_properties: list[str] | None = None,
|
|
469
|
+
label_classes: list[LabelClasses] | None = None,
|
|
470
|
+
label_tasks: list[LabelTask | str] | None = None,
|
|
471
|
+
label_methods: list[LabelMethod | str] | None = None,
|
|
472
|
+
label_overviews: list[LabelOverview] | None = None,
|
|
473
|
+
) -> None:
|
|
474
|
+
"""Applies label extension properties to the extended Item.
|
|
475
|
+
|
|
476
|
+
Args:
|
|
477
|
+
label_description : A description of the label, how it was created,
|
|
478
|
+
and what it is recommended for
|
|
479
|
+
label_type : An Enum of either vector label type or raster label type. Use
|
|
480
|
+
one of :class:`~pystac.LabelType`.
|
|
481
|
+
label_properties : These are the names of the property field(s) in each
|
|
482
|
+
Feature of the label asset's FeatureCollection that contains the classes
|
|
483
|
+
(keywords from label:classes if the property defines classes).
|
|
484
|
+
If labels are rasters, this should be None.
|
|
485
|
+
label_classes : Optional, but required if using categorical data.
|
|
486
|
+
A list of :class:`LabelClasses` instances defining the list of possible
|
|
487
|
+
class names for each label:properties. (e.g., tree, building, car,
|
|
488
|
+
hippo)
|
|
489
|
+
label_tasks : Recommended to be a subset of 'regression', 'classification',
|
|
490
|
+
'detection', or 'segmentation', but may be an arbitrary value.
|
|
491
|
+
label_methods: Recommended to be a subset of 'automated' or 'manual',
|
|
492
|
+
but may be an arbitrary value.
|
|
493
|
+
label_overviews : Optional list of :class:`LabelOverview` instances
|
|
494
|
+
that store counts (for classification-type data) or summary statistics
|
|
495
|
+
(for continuous numerical/regression data).
|
|
496
|
+
"""
|
|
497
|
+
self.label_description = label_description
|
|
498
|
+
self.label_type = label_type
|
|
499
|
+
self.label_properties = label_properties
|
|
500
|
+
self.label_classes = label_classes
|
|
501
|
+
self.label_tasks = label_tasks
|
|
502
|
+
self.label_methods = label_methods
|
|
503
|
+
self.label_overviews = label_overviews
|
|
504
|
+
|
|
505
|
+
@property
|
|
506
|
+
def label_description(self) -> str:
|
|
507
|
+
"""Gets or sets a description of the label, how it was created,
|
|
508
|
+
and what it is recommended for."""
|
|
509
|
+
return cast(
|
|
510
|
+
str,
|
|
511
|
+
get_required(
|
|
512
|
+
self.obj.properties.get(DESCRIPTION_PROP), self.obj, DESCRIPTION_PROP
|
|
513
|
+
),
|
|
514
|
+
)
|
|
515
|
+
|
|
516
|
+
@label_description.setter
|
|
517
|
+
def label_description(self, v: str) -> None:
|
|
518
|
+
self.obj.properties[DESCRIPTION_PROP] = v
|
|
519
|
+
|
|
520
|
+
@property
|
|
521
|
+
def label_type(self) -> LabelType:
|
|
522
|
+
"""Gets or sets an Enum of either vector label type or raster label type."""
|
|
523
|
+
return LabelType(
|
|
524
|
+
get_required(self.obj.properties.get(TYPE_PROP), self.obj, TYPE_PROP)
|
|
525
|
+
)
|
|
526
|
+
|
|
527
|
+
@label_type.setter
|
|
528
|
+
def label_type(self, v: LabelType) -> None:
|
|
529
|
+
self.obj.properties[TYPE_PROP] = v
|
|
530
|
+
|
|
531
|
+
@property
|
|
532
|
+
def label_properties(self) -> list[str] | None:
|
|
533
|
+
"""Gets or sets the names of the property field(s) in each
|
|
534
|
+
Feature of the label asset's FeatureCollection that contains the classes
|
|
535
|
+
(keywords from label:classes if the property defines classes).
|
|
536
|
+
If labels are rasters, this should be None."""
|
|
537
|
+
return self.obj.properties.get(PROPERTIES_PROP)
|
|
538
|
+
|
|
539
|
+
@label_properties.setter
|
|
540
|
+
def label_properties(self, v: list[str] | None) -> None:
|
|
541
|
+
self.obj.properties[PROPERTIES_PROP] = v
|
|
542
|
+
|
|
543
|
+
@property
|
|
544
|
+
def label_classes(self) -> list[LabelClasses] | None:
|
|
545
|
+
"""Gets or set a list of :class:`LabelClasses` defining the list of possible
|
|
546
|
+
class names for each label:properties. (e.g., tree, building, car, hippo).
|
|
547
|
+
|
|
548
|
+
Optional, but required if using categorical data."""
|
|
549
|
+
label_classes = self.obj.properties.get(CLASSES_PROP)
|
|
550
|
+
if label_classes is not None:
|
|
551
|
+
return [LabelClasses(classes) for classes in label_classes]
|
|
552
|
+
else:
|
|
553
|
+
return None
|
|
554
|
+
|
|
555
|
+
@label_classes.setter
|
|
556
|
+
def label_classes(self, v: list[LabelClasses] | None) -> None:
|
|
557
|
+
if v is None:
|
|
558
|
+
self.obj.properties.pop(CLASSES_PROP, None)
|
|
559
|
+
else:
|
|
560
|
+
if not isinstance(v, list):
|
|
561
|
+
raise pystac.STACError(
|
|
562
|
+
f"label_classes must be a list! Invalid input: {v}"
|
|
563
|
+
)
|
|
564
|
+
|
|
565
|
+
classes = [x.to_dict() for x in v]
|
|
566
|
+
self.obj.properties[CLASSES_PROP] = classes
|
|
567
|
+
|
|
568
|
+
@property
|
|
569
|
+
def label_tasks(self) -> list[LabelTask | str] | None:
|
|
570
|
+
"""Gets or set a list of tasks these labels apply to. Usually a subset of
|
|
571
|
+
'regression', 'classification', 'detection', or 'segmentation', but may be
|
|
572
|
+
arbitrary values."""
|
|
573
|
+
return self.obj.properties.get(TASKS_PROP)
|
|
574
|
+
|
|
575
|
+
@label_tasks.setter
|
|
576
|
+
def label_tasks(self, v: list[LabelTask | str] | None) -> None:
|
|
577
|
+
if v is None:
|
|
578
|
+
self.obj.properties.pop(TASKS_PROP, None)
|
|
579
|
+
else:
|
|
580
|
+
self.obj.properties[TASKS_PROP] = v
|
|
581
|
+
|
|
582
|
+
@property
|
|
583
|
+
def label_methods(self) -> list[LabelMethod | str] | None:
|
|
584
|
+
"""Gets or set a list of methods used for labeling.
|
|
585
|
+
|
|
586
|
+
Usually a subset of 'automated' or 'manual', but may be arbitrary values."""
|
|
587
|
+
return self.obj.properties.get("label:methods")
|
|
588
|
+
|
|
589
|
+
@label_methods.setter
|
|
590
|
+
def label_methods(self, v: list[LabelMethod | str] | None) -> None:
|
|
591
|
+
if v is None:
|
|
592
|
+
self.obj.properties.pop("label:methods", None)
|
|
593
|
+
else:
|
|
594
|
+
self.obj.properties["label:methods"] = v
|
|
595
|
+
|
|
596
|
+
@property
|
|
597
|
+
def label_overviews(self) -> list[LabelOverview] | None:
|
|
598
|
+
"""Gets or set a list of :class:`LabelOverview` instances
|
|
599
|
+
that store counts (for classification-type data) or summary statistics (for
|
|
600
|
+
continuous numerical/regression data)."""
|
|
601
|
+
overviews = self.obj.properties.get(OVERVIEWS_PROP)
|
|
602
|
+
if overviews is not None:
|
|
603
|
+
return [LabelOverview(overview) for overview in overviews]
|
|
604
|
+
else:
|
|
605
|
+
return None
|
|
606
|
+
|
|
607
|
+
@label_overviews.setter
|
|
608
|
+
def label_overviews(self, v: list[LabelOverview] | None) -> None:
|
|
609
|
+
if v is None:
|
|
610
|
+
self.obj.properties.pop(OVERVIEWS_PROP, None)
|
|
611
|
+
else:
|
|
612
|
+
self.obj.properties[OVERVIEWS_PROP] = [x.to_dict() for x in v]
|
|
613
|
+
|
|
614
|
+
def __repr__(self) -> str:
|
|
615
|
+
return f"<LabelItemExt Item id={self.obj.id}>"
|
|
616
|
+
|
|
617
|
+
def add_source(
|
|
618
|
+
self,
|
|
619
|
+
source_item: pystac.Item,
|
|
620
|
+
title: str | None = None,
|
|
621
|
+
assets: list[str] | None = None,
|
|
622
|
+
) -> None:
|
|
623
|
+
"""Adds a link to a source item.
|
|
624
|
+
|
|
625
|
+
Args:
|
|
626
|
+
source_item : Source imagery that the LabelItem applies to.
|
|
627
|
+
title : Optional title for the link.
|
|
628
|
+
assets : Optional list of assets that determine what
|
|
629
|
+
assets in the source item this label item data applies to.
|
|
630
|
+
"""
|
|
631
|
+
extra_fields = None
|
|
632
|
+
if assets is not None:
|
|
633
|
+
extra_fields = {"label:assets": assets}
|
|
634
|
+
link = pystac.Link(
|
|
635
|
+
"source",
|
|
636
|
+
source_item,
|
|
637
|
+
title=title,
|
|
638
|
+
media_type=pystac.MediaType.JSON,
|
|
639
|
+
extra_fields=extra_fields,
|
|
640
|
+
)
|
|
641
|
+
self.obj.add_link(link)
|
|
642
|
+
|
|
643
|
+
def get_sources(self) -> Iterable[pystac.Item]:
|
|
644
|
+
"""Gets any source items that describe the source imagery used to generate
|
|
645
|
+
this LabelItem.
|
|
646
|
+
|
|
647
|
+
Returns:
|
|
648
|
+
A possibly empty list of source imagery items. Determined by links of this
|
|
649
|
+
LabelItem that have ``rel=='source'``.
|
|
650
|
+
"""
|
|
651
|
+
return map(lambda x: cast(pystac.Item, x), self.obj.get_stac_objects("source"))
|
|
652
|
+
|
|
653
|
+
def add_labels(
|
|
654
|
+
self,
|
|
655
|
+
href: str,
|
|
656
|
+
title: str | None = None,
|
|
657
|
+
media_type: str | None = None,
|
|
658
|
+
properties: dict[str, Any] | None = None,
|
|
659
|
+
) -> None:
|
|
660
|
+
"""Adds a label asset to this LabelItem.
|
|
661
|
+
|
|
662
|
+
Args:
|
|
663
|
+
href : Link to the asset object. Relative and absolute links are both
|
|
664
|
+
allowed.
|
|
665
|
+
title : Optional displayed title for clients and users.
|
|
666
|
+
media_type : Optional description of the media type. Registered Media
|
|
667
|
+
Types are preferred. See :class:`~pystac.MediaType` for common
|
|
668
|
+
media types.
|
|
669
|
+
properties : Optional, additional properties for this asset. This is
|
|
670
|
+
used by extensions as a way to serialize and deserialize properties on
|
|
671
|
+
asset object JSON.
|
|
672
|
+
"""
|
|
673
|
+
|
|
674
|
+
self.obj.add_asset(
|
|
675
|
+
"labels",
|
|
676
|
+
pystac.Asset(
|
|
677
|
+
href=href, title=title, media_type=media_type, extra_fields=properties
|
|
678
|
+
),
|
|
679
|
+
)
|
|
680
|
+
|
|
681
|
+
def add_geojson_labels(
|
|
682
|
+
self,
|
|
683
|
+
href: str,
|
|
684
|
+
title: str | None = None,
|
|
685
|
+
properties: dict[str, Any] | None = None,
|
|
686
|
+
) -> None:
|
|
687
|
+
"""Adds a GeoJSON label asset to this LabelItem.
|
|
688
|
+
|
|
689
|
+
Args:
|
|
690
|
+
href : Link to the asset object. Relative and absolute links are both
|
|
691
|
+
allowed.
|
|
692
|
+
title : Optional displayed title for clients and users.
|
|
693
|
+
properties : Optional, additional properties for this asset. This is
|
|
694
|
+
used by extensions as a way to serialize and deserialize properties on
|
|
695
|
+
asset object JSON.
|
|
696
|
+
"""
|
|
697
|
+
self.add_labels(
|
|
698
|
+
href,
|
|
699
|
+
title=title,
|
|
700
|
+
properties=properties,
|
|
701
|
+
media_type=pystac.MediaType.GEOJSON,
|
|
702
|
+
)
|
|
703
|
+
|
|
704
|
+
@classmethod
|
|
705
|
+
def get_schema_uri(cls) -> str:
|
|
706
|
+
return SCHEMA_URI
|
|
707
|
+
|
|
708
|
+
@classmethod
|
|
709
|
+
def get_schema_uris(cls) -> list[str]:
|
|
710
|
+
warnings.warn(
|
|
711
|
+
"get_schema_uris is deprecated and will be removed in v2",
|
|
712
|
+
DeprecationWarning,
|
|
713
|
+
)
|
|
714
|
+
return SCHEMA_URIS
|
|
715
|
+
|
|
716
|
+
@classmethod
|
|
717
|
+
def ext(cls, obj: pystac.Item, add_if_missing: bool = False) -> LabelExtension:
|
|
718
|
+
"""Extends the given STAC Object with properties from the :stac-ext:`Label
|
|
719
|
+
Extension <label>`.
|
|
720
|
+
|
|
721
|
+
This extension can be applied to instances of :class:`~pystac.Item`.
|
|
722
|
+
"""
|
|
723
|
+
if isinstance(obj, pystac.Item):
|
|
724
|
+
cls.ensure_has_extension(obj, add_if_missing)
|
|
725
|
+
return cls(obj)
|
|
726
|
+
else:
|
|
727
|
+
raise pystac.ExtensionTypeError(cls._ext_error_message(obj))
|
|
728
|
+
|
|
729
|
+
@classmethod
|
|
730
|
+
def summaries(
|
|
731
|
+
cls, obj: pystac.Collection, add_if_missing: bool = False
|
|
732
|
+
) -> SummariesLabelExtension:
|
|
733
|
+
"""Returns the extended summaries object for the given collection."""
|
|
734
|
+
cls.ensure_has_extension(obj, add_if_missing)
|
|
735
|
+
return SummariesLabelExtension(obj)
|
|
736
|
+
|
|
737
|
+
|
|
738
|
+
class SummariesLabelExtension(SummariesExtension):
|
|
739
|
+
"""A concrete implementation of :class:`~pystac.extensions.base.SummariesExtension`
|
|
740
|
+
that extends the ``summaries`` field of a :class:`~pystac.Collection` to include
|
|
741
|
+
properties defined in the :stac-ext:`Label Extension <label>`.
|
|
742
|
+
"""
|
|
743
|
+
|
|
744
|
+
@property
|
|
745
|
+
def label_properties(self) -> list[str] | None:
|
|
746
|
+
"""Get or sets the summary of :attr:`LabelExtension.label_properties` values
|
|
747
|
+
for this Collection.
|
|
748
|
+
"""
|
|
749
|
+
|
|
750
|
+
return self.summaries.get_list(PROPERTIES_PROP)
|
|
751
|
+
|
|
752
|
+
@label_properties.setter
|
|
753
|
+
def label_properties(self, v: list[str] | None) -> None:
|
|
754
|
+
self._set_summary(PROPERTIES_PROP, v)
|
|
755
|
+
|
|
756
|
+
@property
|
|
757
|
+
def label_classes(self) -> list[LabelClasses] | None:
|
|
758
|
+
"""Get or sets the summary of :attr:`LabelExtension.label_classes` values
|
|
759
|
+
for this Collection.
|
|
760
|
+
"""
|
|
761
|
+
|
|
762
|
+
return map_opt(
|
|
763
|
+
lambda classes: [LabelClasses(c) for c in classes],
|
|
764
|
+
self.summaries.get_list(CLASSES_PROP),
|
|
765
|
+
)
|
|
766
|
+
|
|
767
|
+
@label_classes.setter
|
|
768
|
+
def label_classes(self, v: list[LabelClasses] | None) -> None:
|
|
769
|
+
self._set_summary(
|
|
770
|
+
CLASSES_PROP, map_opt(lambda classes: [c.to_dict() for c in classes], v)
|
|
771
|
+
)
|
|
772
|
+
|
|
773
|
+
@property
|
|
774
|
+
def label_type(self) -> list[LabelType] | None:
|
|
775
|
+
"""Get or sets the summary of :attr:`LabelExtension.label_type` values
|
|
776
|
+
for this Collection.
|
|
777
|
+
"""
|
|
778
|
+
|
|
779
|
+
return self.summaries.get_list(TYPE_PROP)
|
|
780
|
+
|
|
781
|
+
@label_type.setter
|
|
782
|
+
def label_type(self, v: list[LabelType] | None) -> None:
|
|
783
|
+
self._set_summary(TYPE_PROP, v)
|
|
784
|
+
|
|
785
|
+
@property
|
|
786
|
+
def label_tasks(self) -> list[LabelTask | str] | None:
|
|
787
|
+
"""Get or sets the summary of :attr:`LabelExtension.label_tasks` values
|
|
788
|
+
for this Collection.
|
|
789
|
+
"""
|
|
790
|
+
|
|
791
|
+
return self.summaries.get_list(TASKS_PROP)
|
|
792
|
+
|
|
793
|
+
@label_tasks.setter
|
|
794
|
+
def label_tasks(self, v: list[LabelTask | str] | None) -> None:
|
|
795
|
+
self._set_summary(TASKS_PROP, v)
|
|
796
|
+
|
|
797
|
+
@property
|
|
798
|
+
def label_methods(self) -> list[LabelMethod | str] | None:
|
|
799
|
+
"""Get or sets the summary of :attr:`LabelExtension.label_methods` values
|
|
800
|
+
for this Collection.
|
|
801
|
+
"""
|
|
802
|
+
|
|
803
|
+
return self.summaries.get_list(METHODS_PROP)
|
|
804
|
+
|
|
805
|
+
@label_methods.setter
|
|
806
|
+
def label_methods(self, v: list[LabelMethod | str] | None) -> None:
|
|
807
|
+
self._set_summary(METHODS_PROP, v)
|
|
808
|
+
|
|
809
|
+
|
|
810
|
+
class LabelExtensionHooks(ExtensionHooks):
|
|
811
|
+
schema_uri: str = SCHEMA_URI
|
|
812
|
+
prev_extension_ids = {
|
|
813
|
+
"label",
|
|
814
|
+
*[uri for uri in SCHEMA_URIS if uri != SCHEMA_URI],
|
|
815
|
+
}
|
|
816
|
+
stac_object_types = {pystac.STACObjectType.ITEM}
|
|
817
|
+
|
|
818
|
+
def get_object_links(
|
|
819
|
+
self, obj: pystac.STACObject
|
|
820
|
+
) -> list[str | pystac.RelType] | None:
|
|
821
|
+
if isinstance(obj, pystac.Item):
|
|
822
|
+
return [LabelRelType.SOURCE]
|
|
823
|
+
return None
|
|
824
|
+
|
|
825
|
+
def migrate(
|
|
826
|
+
self, obj: dict[str, Any], version: STACVersionID, info: STACJSONDescription
|
|
827
|
+
) -> None:
|
|
828
|
+
if info.object_type == pystac.STACObjectType.ITEM and version < "1.0.0":
|
|
829
|
+
props = obj["properties"]
|
|
830
|
+
# Migrate 0.8.0-rc1 non-pluralized forms
|
|
831
|
+
# As it's a common mistake, convert for any pre-1.0.0 version.
|
|
832
|
+
if "label:property" in props and PROPERTIES_PROP not in props:
|
|
833
|
+
props[PROPERTIES_PROP] = props["label:property"]
|
|
834
|
+
del props["label:property"]
|
|
835
|
+
|
|
836
|
+
if "label:task" in props and TASKS_PROP not in props:
|
|
837
|
+
props[TASKS_PROP] = props["label:task"]
|
|
838
|
+
del props["label:task"]
|
|
839
|
+
|
|
840
|
+
if "label:overview" in props and OVERVIEWS_PROP not in props:
|
|
841
|
+
props[OVERVIEWS_PROP] = props["label:overview"]
|
|
842
|
+
del props["label:overview"]
|
|
843
|
+
|
|
844
|
+
if "label:method" in props and "label:methods" not in props:
|
|
845
|
+
props["label:methods"] = props["label:method"]
|
|
846
|
+
del props["label:method"]
|
|
847
|
+
|
|
848
|
+
super().migrate(obj, version, info)
|
|
849
|
+
|
|
850
|
+
|
|
851
|
+
LABEL_EXTENSION_HOOKS: ExtensionHooks = LabelExtensionHooks()
|
|
File without changes
|