pystac-ext-render 2.0.0__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_render-2.0.0/.gitignore +163 -0
- pystac_ext_render-2.0.0/PKG-INFO +37 -0
- pystac_ext_render-2.0.0/README.md +13 -0
- pystac_ext_render-2.0.0/pyproject.toml +36 -0
- pystac_ext_render-2.0.0/pystac/extensions/py.typed +0 -0
- pystac_ext_render-2.0.0/pystac/extensions/render.py +411 -0
- pystac_ext_render-2.0.0/tests/cassettes/test_render/test_collection_validate.yaml +96 -0
- pystac_ext_render-2.0.0/tests/cassettes/test_render/test_item_validate.yaml +96 -0
- pystac_ext_render-2.0.0/tests/data-files/collection.json +42 -0
- pystac_ext_render-2.0.0/tests/data-files/item.json +175 -0
- pystac_ext_render-2.0.0/tests/test_render.py +236 -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-render
|
|
3
|
+
Version: 2.0.0
|
|
4
|
+
Summary: Render 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,pystac,raster,render
|
|
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-render
|
|
26
|
+
|
|
27
|
+
[PySTAC](https://pypi.org/project/pystac/) extension package for the [Render Extension](https://github.com/stac-extensions/render).
|
|
28
|
+
This extension provides fields for describing visualization parameters for assets, including rescaling, color maps, expressions, and tile matrix set configurations.
|
|
29
|
+
|
|
30
|
+
## Supported versions
|
|
31
|
+
|
|
32
|
+
- [v2.0.0](https://stac-extensions.github.io/render/v2.0.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.0.0.post1`.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# pystac-ext-render
|
|
2
|
+
|
|
3
|
+
[PySTAC](https://pypi.org/project/pystac/) extension package for the [Render Extension](https://github.com/stac-extensions/render).
|
|
4
|
+
This extension provides fields for describing visualization parameters for assets, including rescaling, color maps, expressions, and tile matrix set configurations.
|
|
5
|
+
|
|
6
|
+
## Supported versions
|
|
7
|
+
|
|
8
|
+
- [v2.0.0](https://stac-extensions.github.io/render/v2.0.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.0.0.post1`.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "pystac-ext-render"
|
|
3
|
+
description = "Render extension for PySTAC"
|
|
4
|
+
readme = "README.md"
|
|
5
|
+
version = "2.0.0"
|
|
6
|
+
authors = []
|
|
7
|
+
maintainers = []
|
|
8
|
+
keywords = ["pystac", "imagery", "raster", "catalog", "STAC", "render"]
|
|
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"]
|
|
File without changes
|
|
@@ -0,0 +1,411 @@
|
|
|
1
|
+
"""Implements the :stac-ext:`Render Extension <render>`."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any, Generic, Literal, TypeVar
|
|
6
|
+
|
|
7
|
+
import pystac
|
|
8
|
+
from pystac.extensions.base import ExtensionManagementMixin, PropertiesExtension
|
|
9
|
+
from pystac.extensions.hooks import ExtensionHooks
|
|
10
|
+
from pystac.utils import get_required, map_opt
|
|
11
|
+
|
|
12
|
+
#: Generalized version of :class:`~pystac.Collection` or :class:`~pystac.Item`
|
|
13
|
+
T = TypeVar("T", pystac.Collection, pystac.Item)
|
|
14
|
+
|
|
15
|
+
SCHEMA_URI_PATTERN: str = (
|
|
16
|
+
"https://stac-extensions.github.io/render/v{version}/schema.json"
|
|
17
|
+
)
|
|
18
|
+
DEFAULT_VERSION: str = "2.0.0"
|
|
19
|
+
|
|
20
|
+
SUPPORTED_VERSIONS = [DEFAULT_VERSION]
|
|
21
|
+
|
|
22
|
+
RENDERS_PROP = "renders"
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class Render:
|
|
26
|
+
"""Parameters for creating a rendered view of assets."""
|
|
27
|
+
|
|
28
|
+
properties: dict[str, Any]
|
|
29
|
+
|
|
30
|
+
def __init__(self, properties: dict[str, Any]) -> None:
|
|
31
|
+
self.properties = properties
|
|
32
|
+
|
|
33
|
+
@property
|
|
34
|
+
def assets(self) -> list[str]:
|
|
35
|
+
"""
|
|
36
|
+
List of asset keys referencing the assets that are
|
|
37
|
+
used to make the rendering.
|
|
38
|
+
"""
|
|
39
|
+
return get_required(self.properties.get("assets"), self, "assets")
|
|
40
|
+
|
|
41
|
+
@assets.setter
|
|
42
|
+
def assets(self, v: list[str]) -> None:
|
|
43
|
+
self.properties["assets"] = v
|
|
44
|
+
|
|
45
|
+
@property
|
|
46
|
+
def title(self) -> str | None:
|
|
47
|
+
"""Title of the rendering"""
|
|
48
|
+
return self.properties.get("title")
|
|
49
|
+
|
|
50
|
+
@title.setter
|
|
51
|
+
def title(self, v: str | None) -> None:
|
|
52
|
+
if v is not None:
|
|
53
|
+
self.properties["title"] = v
|
|
54
|
+
else:
|
|
55
|
+
self.properties.pop("title", None)
|
|
56
|
+
|
|
57
|
+
@property
|
|
58
|
+
def rescale(self) -> list[list[float]] | None:
|
|
59
|
+
"""A list of min/max value pairs to rescale each asset by, e.g.
|
|
60
|
+
``[[0, 5000], [0, 7000], [0, 9000]]``. If not provided, the
|
|
61
|
+
assets will not be rescaled.
|
|
62
|
+
"""
|
|
63
|
+
return self.properties.get("rescale")
|
|
64
|
+
|
|
65
|
+
@rescale.setter
|
|
66
|
+
def rescale(self, v: list[list[float]] | None) -> None:
|
|
67
|
+
if v is not None:
|
|
68
|
+
self.properties["rescale"] = v
|
|
69
|
+
else:
|
|
70
|
+
self.properties.pop("rescale", None)
|
|
71
|
+
|
|
72
|
+
@property
|
|
73
|
+
def nodata(self) -> float | str | None:
|
|
74
|
+
"""Nodata value."""
|
|
75
|
+
return self.properties.get("nodata")
|
|
76
|
+
|
|
77
|
+
@nodata.setter
|
|
78
|
+
def nodata(self, v: float | str | None) -> None:
|
|
79
|
+
if v is not None:
|
|
80
|
+
self.properties["nodata"] = v
|
|
81
|
+
else:
|
|
82
|
+
self.properties.pop("nodata", None)
|
|
83
|
+
|
|
84
|
+
@property
|
|
85
|
+
def colormap_name(self) -> str | None:
|
|
86
|
+
"""Name of color map to apply to the render.
|
|
87
|
+
See: https://matplotlib.org/stable/gallery/color/colormap_reference.html
|
|
88
|
+
"""
|
|
89
|
+
return self.properties.get("colormap_name")
|
|
90
|
+
|
|
91
|
+
@colormap_name.setter
|
|
92
|
+
def colormap_name(self, v: str | None) -> None:
|
|
93
|
+
if v is not None:
|
|
94
|
+
self.properties["colormap_name"] = v
|
|
95
|
+
else:
|
|
96
|
+
self.properties.pop("colormap_name", None)
|
|
97
|
+
|
|
98
|
+
@property
|
|
99
|
+
def colormap(self) -> dict[str, Any] | None:
|
|
100
|
+
"""A dictionary containing a custom colormap definition.
|
|
101
|
+
See: https://developmentseed.org/titiler/advanced/rendering/#custom-colormaps
|
|
102
|
+
"""
|
|
103
|
+
return self.properties.get("colormap")
|
|
104
|
+
|
|
105
|
+
@colormap.setter
|
|
106
|
+
def colormap(self, v: dict[str, Any] | None) -> None:
|
|
107
|
+
if v is not None:
|
|
108
|
+
self.properties["colormap"] = v
|
|
109
|
+
else:
|
|
110
|
+
self.properties.pop("colormap", None)
|
|
111
|
+
|
|
112
|
+
@property
|
|
113
|
+
def color_formula(self) -> str | None:
|
|
114
|
+
"""A string containing a color formula to apply
|
|
115
|
+
color corrections to images. Useful for reducing
|
|
116
|
+
artefacts like atmospheric haze, dark shadows, or
|
|
117
|
+
muted colors.
|
|
118
|
+
See: https://developmentseed.org/titiler/advanced/rendering/#color-formula
|
|
119
|
+
"""
|
|
120
|
+
return self.properties.get("color_formula")
|
|
121
|
+
|
|
122
|
+
@color_formula.setter
|
|
123
|
+
def color_formula(self, v: str | None) -> None:
|
|
124
|
+
if v is not None:
|
|
125
|
+
self.properties["color_formula"] = v
|
|
126
|
+
else:
|
|
127
|
+
self.properties.pop("color_formula", None)
|
|
128
|
+
|
|
129
|
+
@property
|
|
130
|
+
def resampling(self) -> str | None:
|
|
131
|
+
"""Resampling algorithm to apply to the referenced assets. See GDAL
|
|
132
|
+
resampling algorithm for some examples.
|
|
133
|
+
See: https://gdal.org/en/latest/programs/gdalwarp.html#cmdoption-gdalwarp-r
|
|
134
|
+
"""
|
|
135
|
+
return self.properties.get("resampling")
|
|
136
|
+
|
|
137
|
+
@resampling.setter
|
|
138
|
+
def resampling(self, v: str | None) -> None:
|
|
139
|
+
if v is not None:
|
|
140
|
+
self.properties["resampling"] = v
|
|
141
|
+
else:
|
|
142
|
+
self.properties.pop("resampling", None)
|
|
143
|
+
|
|
144
|
+
@property
|
|
145
|
+
def expression(self) -> str | None:
|
|
146
|
+
"""Band arithmetic formula to apply to the referenced assets."""
|
|
147
|
+
return self.properties.get("expression")
|
|
148
|
+
|
|
149
|
+
@expression.setter
|
|
150
|
+
def expression(self, v: str | None) -> None:
|
|
151
|
+
if v is not None:
|
|
152
|
+
self.properties["expression"] = v
|
|
153
|
+
else:
|
|
154
|
+
self.properties.pop("expression", None)
|
|
155
|
+
|
|
156
|
+
@property
|
|
157
|
+
def minmax_zoom(self) -> list[int] | None:
|
|
158
|
+
"""Zoom level range applicable for the visualization, e.g. ``[2, 18]``."""
|
|
159
|
+
return self.properties.get("minmax_zoom")
|
|
160
|
+
|
|
161
|
+
@minmax_zoom.setter
|
|
162
|
+
def minmax_zoom(self, v: list[int] | None) -> None:
|
|
163
|
+
if v is not None:
|
|
164
|
+
self.properties["minmax_zoom"] = v
|
|
165
|
+
else:
|
|
166
|
+
self.properties.pop("minmax_zoom", None)
|
|
167
|
+
|
|
168
|
+
def apply(
|
|
169
|
+
self,
|
|
170
|
+
assets: list[str],
|
|
171
|
+
title: str | None = None,
|
|
172
|
+
rescale: list[list[float]] | None = None,
|
|
173
|
+
nodata: float | str | None = None,
|
|
174
|
+
colormap_name: str | None = None,
|
|
175
|
+
colormap: dict[str, Any] | None = None,
|
|
176
|
+
color_formula: str | None = None,
|
|
177
|
+
resampling: str | None = None,
|
|
178
|
+
expression: str | None = None,
|
|
179
|
+
minmax_zoom: list[int] | None = None,
|
|
180
|
+
) -> None:
|
|
181
|
+
"""Set the properties for a new Render.
|
|
182
|
+
|
|
183
|
+
Args:
|
|
184
|
+
assets : List of asset keys referencing the assets that are
|
|
185
|
+
used to make the rendering.
|
|
186
|
+
title : Title of the rendering.
|
|
187
|
+
rescale : A list of min/max value pairs to rescale each asset by, e.g.
|
|
188
|
+
``[[0, 5000], [0, 7000], [0, 9000]]``. If not provided, the
|
|
189
|
+
assets will not be rescaled.
|
|
190
|
+
nodata : Nodata value.
|
|
191
|
+
colormap_name : Name of color map to apply to the render.
|
|
192
|
+
https://matplotlib.org/stable/gallery/color/colormap_reference.html
|
|
193
|
+
colormap : A dictionary containing a custom colormap definition.
|
|
194
|
+
https://developmentseed.org/titiler/advanced/rendering/#custom-colormaps
|
|
195
|
+
color_formula : A string containing a color formula to apply
|
|
196
|
+
color corrections to images. Useful for reducing
|
|
197
|
+
artifacts like atmospheric haze, dark shadows, or
|
|
198
|
+
muted colors.
|
|
199
|
+
https://developmentseed.org/titiler/advanced/rendering/#color-formula
|
|
200
|
+
resampling : Resampling algorithm to apply to the referenced assets. See
|
|
201
|
+
GDAL resampling algorithm for some examples.
|
|
202
|
+
https://gdal.org/en/latest/programs/gdalwarp.html#cmdoption-gdalwarp-r
|
|
203
|
+
expression : Band arithmetic formula to apply to the referenced assets.
|
|
204
|
+
minmax_zoom : Zoom level range applicable for the visualization,
|
|
205
|
+
e.g. ``[2, 18]``.
|
|
206
|
+
"""
|
|
207
|
+
self.assets = assets
|
|
208
|
+
self.title = title
|
|
209
|
+
self.rescale = rescale
|
|
210
|
+
self.nodata = nodata
|
|
211
|
+
self.colormap_name = colormap_name
|
|
212
|
+
self.colormap = colormap
|
|
213
|
+
self.color_formula = color_formula
|
|
214
|
+
self.resampling = resampling
|
|
215
|
+
self.expression = expression
|
|
216
|
+
self.minmax_zoom = minmax_zoom
|
|
217
|
+
|
|
218
|
+
@classmethod
|
|
219
|
+
def create(
|
|
220
|
+
cls,
|
|
221
|
+
assets: list[str],
|
|
222
|
+
title: str | None = None,
|
|
223
|
+
rescale: list[list[float]] | None = None,
|
|
224
|
+
nodata: float | str | None = None,
|
|
225
|
+
colormap_name: str | None = None,
|
|
226
|
+
colormap: dict[str, Any] | None = None,
|
|
227
|
+
color_formula: str | None = None,
|
|
228
|
+
resampling: str | None = None,
|
|
229
|
+
expression: str | None = None,
|
|
230
|
+
minmax_zoom: list[int] | None = None,
|
|
231
|
+
) -> Render:
|
|
232
|
+
"""Create a new Render.
|
|
233
|
+
|
|
234
|
+
Args:
|
|
235
|
+
assets: List of asset keys referencing the assets that are
|
|
236
|
+
used to make the rendering.
|
|
237
|
+
title: Title of the rendering.
|
|
238
|
+
rescale: A list of min/max value pairs to rescale each asset by, e.g.
|
|
239
|
+
``[[0, 5000], [0, 7000], [0, 9000]]``. If not provided, the
|
|
240
|
+
assets will not be rescaled.
|
|
241
|
+
nodata: Nodata value.
|
|
242
|
+
colormap_name: Name of color map to apply to the render.
|
|
243
|
+
https://matplotlib.org/stable/gallery/color/colormap_reference.html
|
|
244
|
+
colormap: A dictionary containing a custom colormap definition.
|
|
245
|
+
https://developmentseed.org/titiler/advanced/rendering/#custom-colormaps
|
|
246
|
+
color_formula: A string containing a color formula to apply
|
|
247
|
+
color corrections to images. Useful for reducing
|
|
248
|
+
artifacts like atmospheric haze, dark shadows, or
|
|
249
|
+
muted colors.
|
|
250
|
+
https://developmentseed.org/titiler/advanced/rendering/#color-formula
|
|
251
|
+
resampling: Resampling algorithm to apply to the referenced assets. See
|
|
252
|
+
GDAL resampling algorithm for some examples.
|
|
253
|
+
https://gdal.org/en/latest/programs/gdalwarp.html#cmdoption-gdalwarp-r
|
|
254
|
+
expression: Band arithmetic formula to apply to the referenced assets.
|
|
255
|
+
minmax_zoom: Zoom level range applicable for the visualization,
|
|
256
|
+
e.g. ``[2, 18]``.
|
|
257
|
+
"""
|
|
258
|
+
c = cls({})
|
|
259
|
+
c.apply(
|
|
260
|
+
assets=assets,
|
|
261
|
+
title=title,
|
|
262
|
+
rescale=rescale,
|
|
263
|
+
nodata=nodata,
|
|
264
|
+
colormap_name=colormap_name,
|
|
265
|
+
colormap=colormap,
|
|
266
|
+
color_formula=color_formula,
|
|
267
|
+
resampling=resampling,
|
|
268
|
+
expression=expression,
|
|
269
|
+
minmax_zoom=minmax_zoom,
|
|
270
|
+
)
|
|
271
|
+
return c
|
|
272
|
+
|
|
273
|
+
def to_dict(self) -> dict[str, Any]:
|
|
274
|
+
return self.properties
|
|
275
|
+
|
|
276
|
+
def __eq__(self, other: object) -> bool:
|
|
277
|
+
if not isinstance(other, Render):
|
|
278
|
+
raise NotImplementedError
|
|
279
|
+
return self.properties == other.properties
|
|
280
|
+
|
|
281
|
+
def __repr__(self) -> str:
|
|
282
|
+
props = " ".join(
|
|
283
|
+
[
|
|
284
|
+
f"{key}={value}"
|
|
285
|
+
for key, value in self.properties.items()
|
|
286
|
+
if value is not None
|
|
287
|
+
]
|
|
288
|
+
)
|
|
289
|
+
return f"<Render {props}>"
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
class RenderExtension(
|
|
293
|
+
Generic[T],
|
|
294
|
+
PropertiesExtension,
|
|
295
|
+
ExtensionManagementMixin[pystac.Item | pystac.Collection],
|
|
296
|
+
):
|
|
297
|
+
"""An abstract class that can be used to extend the properties of a
|
|
298
|
+
:class:`~pystac.Collection` or :class:`~pystac.Item` with
|
|
299
|
+
properties from the :stac-ext:`Render Extension <render>`. This class is
|
|
300
|
+
generic over the type of STAC Object to be extended (e.g. :class:`~pystac.Item`,
|
|
301
|
+
:class:`~pystac.Collection`).
|
|
302
|
+
|
|
303
|
+
To create a concrete instance of :class:`RenderExtension`, use the
|
|
304
|
+
:meth:`RenderExtension.ext` method. For example:
|
|
305
|
+
|
|
306
|
+
.. code-block:: python
|
|
307
|
+
|
|
308
|
+
>>> item: pystac.Item = ...
|
|
309
|
+
>>> xr_ext = RenderExtension.ext(item)
|
|
310
|
+
|
|
311
|
+
"""
|
|
312
|
+
|
|
313
|
+
name: Literal["render"] = "render"
|
|
314
|
+
|
|
315
|
+
@classmethod
|
|
316
|
+
def get_schema_uri(cls) -> str:
|
|
317
|
+
return SCHEMA_URI_PATTERN.format(version=DEFAULT_VERSION)
|
|
318
|
+
|
|
319
|
+
@classmethod
|
|
320
|
+
def ext(cls, obj: T, add_if_missing: bool = False) -> RenderExtension[T]:
|
|
321
|
+
"""Extend the given STAC Object with properties from the
|
|
322
|
+
:stac-ext:`Render Extension <render>`.
|
|
323
|
+
|
|
324
|
+
This extension can be applied to instances of :class:`~pystac.Collection`
|
|
325
|
+
or :class:`~pystac.Item`.
|
|
326
|
+
|
|
327
|
+
Raises:
|
|
328
|
+
pystac.ExtensionTypeError : If an invalid object type is passed.
|
|
329
|
+
"""
|
|
330
|
+
if isinstance(obj, pystac.Collection):
|
|
331
|
+
cls.ensure_has_extension(obj, add_if_missing)
|
|
332
|
+
return CollectionRenderExtension(obj)
|
|
333
|
+
elif isinstance(obj, pystac.Item):
|
|
334
|
+
cls.ensure_has_extension(obj, add_if_missing)
|
|
335
|
+
return ItemRenderExtension(obj)
|
|
336
|
+
else:
|
|
337
|
+
raise pystac.ExtensionTypeError(
|
|
338
|
+
f"RenderExtension does not apply to type '{type(obj).__name__}'"
|
|
339
|
+
)
|
|
340
|
+
|
|
341
|
+
def apply(
|
|
342
|
+
self,
|
|
343
|
+
renders: dict[str, Render],
|
|
344
|
+
) -> None:
|
|
345
|
+
"""Applies the render extension fields to the extended
|
|
346
|
+
object.
|
|
347
|
+
|
|
348
|
+
Args:
|
|
349
|
+
renders: a dictionary mapping render names to
|
|
350
|
+
:class: `~pystac.extensions.render.Render` objects.
|
|
351
|
+
"""
|
|
352
|
+
self.renders = renders
|
|
353
|
+
|
|
354
|
+
@property
|
|
355
|
+
def renders(self) -> dict[str, Render]:
|
|
356
|
+
"""A dictionary where each key is the name of a render and each
|
|
357
|
+
value is a :class:`~Render` object.
|
|
358
|
+
"""
|
|
359
|
+
renders: dict[str, dict[str, Any]] = get_required(
|
|
360
|
+
self._get_property(RENDERS_PROP, dict[str, dict[str, Any]]),
|
|
361
|
+
self,
|
|
362
|
+
RENDERS_PROP,
|
|
363
|
+
)
|
|
364
|
+
return {k: Render(v) for k, v in renders.items()}
|
|
365
|
+
|
|
366
|
+
@renders.setter
|
|
367
|
+
def renders(self, v: dict[str, Render]) -> None:
|
|
368
|
+
self._set_property(
|
|
369
|
+
RENDERS_PROP,
|
|
370
|
+
map_opt(lambda renders: {k: r.to_dict() for k, r in renders.items()}, v),
|
|
371
|
+
pop_if_none=False,
|
|
372
|
+
)
|
|
373
|
+
|
|
374
|
+
|
|
375
|
+
class CollectionRenderExtension(RenderExtension[pystac.Collection]):
|
|
376
|
+
"""A concrete implementation of :class:`RenderExtension` on a
|
|
377
|
+
:class:`~pystac.Collection` that extends the properties of the Collection to include
|
|
378
|
+
properties defined in the :stac-ext:`Render Extension <render>`.
|
|
379
|
+
|
|
380
|
+
This class should generally not be instantiated directly. Instead, call
|
|
381
|
+
:meth:`RenderExtension.ext` on an :class:`~pystac.Collection` to extend it.
|
|
382
|
+
"""
|
|
383
|
+
|
|
384
|
+
def __init__(self, collection: pystac.Collection):
|
|
385
|
+
self.collection = collection
|
|
386
|
+
self.properties = collection.extra_fields
|
|
387
|
+
|
|
388
|
+
def __repr__(self) -> str:
|
|
389
|
+
return f"<CollectionRenderExtension Collection id={self.collection.id}>"
|
|
390
|
+
|
|
391
|
+
|
|
392
|
+
class ItemRenderExtension(RenderExtension[pystac.Item]):
|
|
393
|
+
"""A concrete implementation of :class:`RenderExtension` on a
|
|
394
|
+
:class:`~pystac.Item` that extends the properties of the Item to include
|
|
395
|
+
properties defined in the :stac-ext:`Render Extension <render>`.
|
|
396
|
+
|
|
397
|
+
This class should generally not be instantiated directly. Instead, call
|
|
398
|
+
:meth:`RenderExtension.ext` on an :class:`~pystac.Item` to extend it.
|
|
399
|
+
"""
|
|
400
|
+
|
|
401
|
+
def __init__(self, item: pystac.Item):
|
|
402
|
+
self.item = item
|
|
403
|
+
self.properties = item.properties
|
|
404
|
+
|
|
405
|
+
def __repr__(self) -> str:
|
|
406
|
+
return f"<ItemRenderExtension Item id={self.item.id}>"
|
|
407
|
+
|
|
408
|
+
|
|
409
|
+
class RenderExtensionHooks(ExtensionHooks):
|
|
410
|
+
schema_uri: str = SCHEMA_URI_PATTERN.format(version=DEFAULT_VERSION)
|
|
411
|
+
stac_object_types = {pystac.STACObjectType.COLLECTION, pystac.STACObjectType.ITEM}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
interactions:
|
|
2
|
+
- request:
|
|
3
|
+
body: null
|
|
4
|
+
headers: {}
|
|
5
|
+
method: GET
|
|
6
|
+
uri: https://stac-extensions.github.io/render/v2.0.0/schema.json
|
|
7
|
+
response:
|
|
8
|
+
body:
|
|
9
|
+
string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\":
|
|
10
|
+
\"https://stac-extensions.github.io/render/v2.0.0/schema.json#\",\n \"title\":
|
|
11
|
+
\"Rendering Extension\",\n \"description\": \"STAC Rendering Extension for
|
|
12
|
+
STAC Items and STAC Collections.\",\n \"oneOf\": [\n {\n \"$comment\":
|
|
13
|
+
\"This is the schema for STAC Items.\",\n \"allOf\": [\n {\n \"$ref\":
|
|
14
|
+
\"#/definitions/stac_extensions\"\n },\n {\n \"type\":
|
|
15
|
+
\"object\",\n \"required\": [\n \"type\",\n \"properties\",\n
|
|
16
|
+
\ \"assets\"\n ],\n \"properties\": {\n \"type\":
|
|
17
|
+
{\n \"const\": \"Feature\"\n },\n \"properties\":
|
|
18
|
+
{\n \"type\": \"object\",\n \"required\": [\"renders\"],\n
|
|
19
|
+
\ \"properties\": {\n \"renders\": {\n \"type\":
|
|
20
|
+
\"object\",\n \"additionalProperties\": {\n \"$ref\":
|
|
21
|
+
\"#/definitions/fields\"\n }\n }\n }\n
|
|
22
|
+
\ }\n }\n },\n {\n \"if\": {\n \"properties\":
|
|
23
|
+
{\n \"stac_extensions\": {\n \"contains\": {\n
|
|
24
|
+
\ \"type\": \"string\",\n \"pattern\": \"https:\\/\\/stac-extensions\\\\.github\\\\.io\\/web-map-links\\/.*\"\n
|
|
25
|
+
\ }\n }\n }\n },\n \"then\":
|
|
26
|
+
{\n \"properties\": {\n \"links\": {\n \"type\":
|
|
27
|
+
\"array\",\n \"contains\": {\n \"type\": \"object\",\n
|
|
28
|
+
\ \"required\": [\n \"rel\",\n \"render\"\n
|
|
29
|
+
\ ],\n \"properties\": {\n \"render\":
|
|
30
|
+
{\n \"type\": \"string\"\n }\n }\n
|
|
31
|
+
\ }\n }\n }\n }\n }\n
|
|
32
|
+
\ ]\n },\n {\n \"$comment\": \"This is the schema for STAC
|
|
33
|
+
Collections.\",\n \"type\": \"object\",\n \"allOf\": [\n {\n
|
|
34
|
+
\ \"required\": [\n \"type\",\n \"renders\"\n
|
|
35
|
+
\ ],\n \"properties\": {\n \"type\": {\n \"const\":
|
|
36
|
+
\"Collection\"\n }\n }\n },\n {\n \"$ref\":
|
|
37
|
+
\"#/definitions/stac_extensions\"\n }\n ],\n \"anyOf\": [\n
|
|
38
|
+
\ {\n \"$comment\": \"This validates the fields in Collection
|
|
39
|
+
Assets, but does not require them.\",\n \"anyOf\": [\n {\n
|
|
40
|
+
\ \"type\": \"object\",\n \"required\": [\n \"assets\"\n
|
|
41
|
+
\ ]\n },\n {\n \"type\": \"object\",\n
|
|
42
|
+
\ \"required\": [\n \"item_assets\"\n ]\n
|
|
43
|
+
\ }\n ],\n \"properties\": {\n \"renders\":
|
|
44
|
+
{\n \"type\": \"object\",\n \"not\": {\n \"additionalProperties\":
|
|
45
|
+
{\n \"not\": {\n \"allOf\": [\n {\n
|
|
46
|
+
\ \"$ref\": \"#/definitions/require_any_field\"\n },\n
|
|
47
|
+
\ {\n \"$ref\": \"#/definitions/fields\"\n
|
|
48
|
+
\ }\n ]\n }\n }\n
|
|
49
|
+
\ }\n }\n }\n }\n ]\n }\n ],\n
|
|
50
|
+
\ \"definitions\": {\n \"stac_extensions\": {\n \"type\": \"object\",\n
|
|
51
|
+
\ \"required\": [\n \"stac_extensions\"\n ],\n \"properties\":
|
|
52
|
+
{\n \"stac_extensions\": {\n \"type\": \"array\",\n \"contains\":
|
|
53
|
+
{\n \"const\": \"https://stac-extensions.github.io/render/v2.0.0/schema.json\"\n
|
|
54
|
+
\ }\n }\n }\n },\n \"require_any_field\": {\n \"$comment\":
|
|
55
|
+
\"Please list all fields here so that we can force the existence of one of
|
|
56
|
+
them in other parts of the schemas.\",\n \"anyOf\": [\n {\n \"type\":
|
|
57
|
+
\"object\",\n \"required\": [\n \"assets\"\n ]\n
|
|
58
|
+
\ },\n {\n \"type\": \"object\",\n \"required\":
|
|
59
|
+
[\n \"title\"\n ]\n },\n {\n \"type\":
|
|
60
|
+
\"object\",\n \"required\": [\n \"rescale\"\n ]\n
|
|
61
|
+
\ },\n {\n \"type\": \"object\",\n \"required\":
|
|
62
|
+
[\n \"nodata\"\n ]\n },\n {\n \"type\":
|
|
63
|
+
\"object\",\n \"required\": [\n \"colormap_name\"\n ]\n
|
|
64
|
+
\ },\n {\n \"type\": \"object\",\n \"required\":
|
|
65
|
+
[\n \"colormap\"\n ]\n },\n {\n \"type\":
|
|
66
|
+
\"object\",\n \"required\": [\n \"color_formula\"\n ]\n
|
|
67
|
+
\ },\n {\n \"type\": \"object\",\n \"required\":
|
|
68
|
+
[\n \"resampling\"\n ]\n },\n {\n \"type\":
|
|
69
|
+
\"object\",\n \"required\": [\n \"expression\"\n ]\n
|
|
70
|
+
\ },\n {\n \"type\": \"object\",\n \"required\":
|
|
71
|
+
[\n \"minmax_zoom\"\n ]\n },\n {\n \"type\":
|
|
72
|
+
\"object\",\n \"required\": [\n \"bidx\"\n ]\n
|
|
73
|
+
\ }\n ]\n },\n \"fields\": {\n \"$comment\": \"Add your
|
|
74
|
+
new fields here. Don't require them here, do that above in the corresponding
|
|
75
|
+
schema.\",\n \"type\": \"object\",\n \"required\": [\n \"assets\"\n
|
|
76
|
+
\ ],\n \"properties\": {\n \"assets\": {\n \"type\":
|
|
77
|
+
\"array\",\n \"items\": {\n \"type\": \"string\"\n }\n
|
|
78
|
+
\ },\n \"title\": {\n \"type\": \"string\"\n },\n
|
|
79
|
+
\ \"rescale\": {\n \"type\": \"array\",\n \"items\":
|
|
80
|
+
{\n \"type\": \"array\",\n \"items\": {\n \"type\":
|
|
81
|
+
\"number\"\n }\n }\n },\n \"nodata\": {\n
|
|
82
|
+
\ \"type\": [\n \"number\",\n \"string\"\n ]\n
|
|
83
|
+
\ },\n \"colormap_name\": {\n \"type\": \"string\"\n
|
|
84
|
+
\ },\n \"colormap\": {\n \"type\": \"object\"\n },\n
|
|
85
|
+
\ \"color_formula\": {\n \"type\": \"string\"\n },\n
|
|
86
|
+
\ \"resampling\": {\n \"type\": \"string\"\n },\n \"expression\":
|
|
87
|
+
{\n \"type\": [\"string\", \"object\", \"array\"]\n },\n \"minmax_zoom\":
|
|
88
|
+
{\n \"type\": \"array\",\n \"items\": {\n \"type\":
|
|
89
|
+
\"number\"\n }\n },\n \"bidx\": {\n \"type\":
|
|
90
|
+
\"array\",\n \"items\": {\n \"type\": \"number\"\n }\n
|
|
91
|
+
\ }\n },\n \"additionalProperties\": true\n }\n }\n}"
|
|
92
|
+
headers: {}
|
|
93
|
+
status:
|
|
94
|
+
code: 200
|
|
95
|
+
message: OK
|
|
96
|
+
version: 1
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
interactions:
|
|
2
|
+
- request:
|
|
3
|
+
body: null
|
|
4
|
+
headers: {}
|
|
5
|
+
method: GET
|
|
6
|
+
uri: https://stac-extensions.github.io/render/v2.0.0/schema.json
|
|
7
|
+
response:
|
|
8
|
+
body:
|
|
9
|
+
string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\":
|
|
10
|
+
\"https://stac-extensions.github.io/render/v2.0.0/schema.json#\",\n \"title\":
|
|
11
|
+
\"Rendering Extension\",\n \"description\": \"STAC Rendering Extension for
|
|
12
|
+
STAC Items and STAC Collections.\",\n \"oneOf\": [\n {\n \"$comment\":
|
|
13
|
+
\"This is the schema for STAC Items.\",\n \"allOf\": [\n {\n \"$ref\":
|
|
14
|
+
\"#/definitions/stac_extensions\"\n },\n {\n \"type\":
|
|
15
|
+
\"object\",\n \"required\": [\n \"type\",\n \"properties\",\n
|
|
16
|
+
\ \"assets\"\n ],\n \"properties\": {\n \"type\":
|
|
17
|
+
{\n \"const\": \"Feature\"\n },\n \"properties\":
|
|
18
|
+
{\n \"type\": \"object\",\n \"required\": [\"renders\"],\n
|
|
19
|
+
\ \"properties\": {\n \"renders\": {\n \"type\":
|
|
20
|
+
\"object\",\n \"additionalProperties\": {\n \"$ref\":
|
|
21
|
+
\"#/definitions/fields\"\n }\n }\n }\n
|
|
22
|
+
\ }\n }\n },\n {\n \"if\": {\n \"properties\":
|
|
23
|
+
{\n \"stac_extensions\": {\n \"contains\": {\n
|
|
24
|
+
\ \"type\": \"string\",\n \"pattern\": \"https:\\/\\/stac-extensions\\\\.github\\\\.io\\/web-map-links\\/.*\"\n
|
|
25
|
+
\ }\n }\n }\n },\n \"then\":
|
|
26
|
+
{\n \"properties\": {\n \"links\": {\n \"type\":
|
|
27
|
+
\"array\",\n \"contains\": {\n \"type\": \"object\",\n
|
|
28
|
+
\ \"required\": [\n \"rel\",\n \"render\"\n
|
|
29
|
+
\ ],\n \"properties\": {\n \"render\":
|
|
30
|
+
{\n \"type\": \"string\"\n }\n }\n
|
|
31
|
+
\ }\n }\n }\n }\n }\n
|
|
32
|
+
\ ]\n },\n {\n \"$comment\": \"This is the schema for STAC
|
|
33
|
+
Collections.\",\n \"type\": \"object\",\n \"allOf\": [\n {\n
|
|
34
|
+
\ \"required\": [\n \"type\",\n \"renders\"\n
|
|
35
|
+
\ ],\n \"properties\": {\n \"type\": {\n \"const\":
|
|
36
|
+
\"Collection\"\n }\n }\n },\n {\n \"$ref\":
|
|
37
|
+
\"#/definitions/stac_extensions\"\n }\n ],\n \"anyOf\": [\n
|
|
38
|
+
\ {\n \"$comment\": \"This validates the fields in Collection
|
|
39
|
+
Assets, but does not require them.\",\n \"anyOf\": [\n {\n
|
|
40
|
+
\ \"type\": \"object\",\n \"required\": [\n \"assets\"\n
|
|
41
|
+
\ ]\n },\n {\n \"type\": \"object\",\n
|
|
42
|
+
\ \"required\": [\n \"item_assets\"\n ]\n
|
|
43
|
+
\ }\n ],\n \"properties\": {\n \"renders\":
|
|
44
|
+
{\n \"type\": \"object\",\n \"not\": {\n \"additionalProperties\":
|
|
45
|
+
{\n \"not\": {\n \"allOf\": [\n {\n
|
|
46
|
+
\ \"$ref\": \"#/definitions/require_any_field\"\n },\n
|
|
47
|
+
\ {\n \"$ref\": \"#/definitions/fields\"\n
|
|
48
|
+
\ }\n ]\n }\n }\n
|
|
49
|
+
\ }\n }\n }\n }\n ]\n }\n ],\n
|
|
50
|
+
\ \"definitions\": {\n \"stac_extensions\": {\n \"type\": \"object\",\n
|
|
51
|
+
\ \"required\": [\n \"stac_extensions\"\n ],\n \"properties\":
|
|
52
|
+
{\n \"stac_extensions\": {\n \"type\": \"array\",\n \"contains\":
|
|
53
|
+
{\n \"const\": \"https://stac-extensions.github.io/render/v2.0.0/schema.json\"\n
|
|
54
|
+
\ }\n }\n }\n },\n \"require_any_field\": {\n \"$comment\":
|
|
55
|
+
\"Please list all fields here so that we can force the existence of one of
|
|
56
|
+
them in other parts of the schemas.\",\n \"anyOf\": [\n {\n \"type\":
|
|
57
|
+
\"object\",\n \"required\": [\n \"assets\"\n ]\n
|
|
58
|
+
\ },\n {\n \"type\": \"object\",\n \"required\":
|
|
59
|
+
[\n \"title\"\n ]\n },\n {\n \"type\":
|
|
60
|
+
\"object\",\n \"required\": [\n \"rescale\"\n ]\n
|
|
61
|
+
\ },\n {\n \"type\": \"object\",\n \"required\":
|
|
62
|
+
[\n \"nodata\"\n ]\n },\n {\n \"type\":
|
|
63
|
+
\"object\",\n \"required\": [\n \"colormap_name\"\n ]\n
|
|
64
|
+
\ },\n {\n \"type\": \"object\",\n \"required\":
|
|
65
|
+
[\n \"colormap\"\n ]\n },\n {\n \"type\":
|
|
66
|
+
\"object\",\n \"required\": [\n \"color_formula\"\n ]\n
|
|
67
|
+
\ },\n {\n \"type\": \"object\",\n \"required\":
|
|
68
|
+
[\n \"resampling\"\n ]\n },\n {\n \"type\":
|
|
69
|
+
\"object\",\n \"required\": [\n \"expression\"\n ]\n
|
|
70
|
+
\ },\n {\n \"type\": \"object\",\n \"required\":
|
|
71
|
+
[\n \"minmax_zoom\"\n ]\n },\n {\n \"type\":
|
|
72
|
+
\"object\",\n \"required\": [\n \"bidx\"\n ]\n
|
|
73
|
+
\ }\n ]\n },\n \"fields\": {\n \"$comment\": \"Add your
|
|
74
|
+
new fields here. Don't require them here, do that above in the corresponding
|
|
75
|
+
schema.\",\n \"type\": \"object\",\n \"required\": [\n \"assets\"\n
|
|
76
|
+
\ ],\n \"properties\": {\n \"assets\": {\n \"type\":
|
|
77
|
+
\"array\",\n \"items\": {\n \"type\": \"string\"\n }\n
|
|
78
|
+
\ },\n \"title\": {\n \"type\": \"string\"\n },\n
|
|
79
|
+
\ \"rescale\": {\n \"type\": \"array\",\n \"items\":
|
|
80
|
+
{\n \"type\": \"array\",\n \"items\": {\n \"type\":
|
|
81
|
+
\"number\"\n }\n }\n },\n \"nodata\": {\n
|
|
82
|
+
\ \"type\": [\n \"number\",\n \"string\"\n ]\n
|
|
83
|
+
\ },\n \"colormap_name\": {\n \"type\": \"string\"\n
|
|
84
|
+
\ },\n \"colormap\": {\n \"type\": \"object\"\n },\n
|
|
85
|
+
\ \"color_formula\": {\n \"type\": \"string\"\n },\n
|
|
86
|
+
\ \"resampling\": {\n \"type\": \"string\"\n },\n \"expression\":
|
|
87
|
+
{\n \"type\": [\"string\", \"object\", \"array\"]\n },\n \"minmax_zoom\":
|
|
88
|
+
{\n \"type\": \"array\",\n \"items\": {\n \"type\":
|
|
89
|
+
\"number\"\n }\n },\n \"bidx\": {\n \"type\":
|
|
90
|
+
\"array\",\n \"items\": {\n \"type\": \"number\"\n }\n
|
|
91
|
+
\ }\n },\n \"additionalProperties\": true\n }\n }\n}"
|
|
92
|
+
headers: {}
|
|
93
|
+
status:
|
|
94
|
+
code: 200
|
|
95
|
+
message: OK
|
|
96
|
+
version: 1
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"stac_version": "1.1.0",
|
|
3
|
+
"stac_extensions": [
|
|
4
|
+
"https://stac-extensions.github.io/render/v2.0.0/schema.json"
|
|
5
|
+
],
|
|
6
|
+
"type": "Collection",
|
|
7
|
+
"id": "senitnel2-l2a",
|
|
8
|
+
"title": "Sentinel-2 L2A",
|
|
9
|
+
"description": "Sentinel-2 L2A data",
|
|
10
|
+
"license": "Apache-2.0",
|
|
11
|
+
"extent": {
|
|
12
|
+
"spatial": {
|
|
13
|
+
"bbox": [[172.9, 1.3, 173, 1.4]]
|
|
14
|
+
},
|
|
15
|
+
"temporal": {
|
|
16
|
+
"interval": [["2015-06-23T00:00:00Z", null]]
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"item_assets": {},
|
|
20
|
+
"renders": {
|
|
21
|
+
"ndvi": {
|
|
22
|
+
"title": "Normalized Difference Vegetation Index",
|
|
23
|
+
"assets": ["ndvi"],
|
|
24
|
+
"resampling": "average",
|
|
25
|
+
"colormap_name": "ylgn"
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
"summaries": {
|
|
29
|
+
"datetime": {
|
|
30
|
+
"minimum": "2015-06-23T00:00:00Z",
|
|
31
|
+
"maximum": "2019-07-10T13:44:56Z"
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
"links": [
|
|
35
|
+
{
|
|
36
|
+
"href": "./collection.json",
|
|
37
|
+
"rel": "root",
|
|
38
|
+
"title": "Sentinel-2 L2A",
|
|
39
|
+
"type": "application/json"
|
|
40
|
+
}
|
|
41
|
+
]
|
|
42
|
+
}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
{
|
|
2
|
+
"type": "Feature",
|
|
3
|
+
"stac_version": "1.1.0",
|
|
4
|
+
"stac_extensions": [
|
|
5
|
+
"https://stac-extensions.github.io/render/v2.0.0/schema.json"
|
|
6
|
+
],
|
|
7
|
+
"id": "S2B_33SVB_20210221_0_L2A",
|
|
8
|
+
"bbox": [
|
|
9
|
+
13.86148243891681, 36.95257399124932, 15.111074610520053,
|
|
10
|
+
37.94752813015372
|
|
11
|
+
],
|
|
12
|
+
"geometry": {
|
|
13
|
+
"type": "Polygon",
|
|
14
|
+
"coordinates": [
|
|
15
|
+
[
|
|
16
|
+
[13.876381589019879, 36.95257399124932],
|
|
17
|
+
[13.86148243891681, 37.942072015005024],
|
|
18
|
+
[15.111074610520053, 37.94752813015372],
|
|
19
|
+
[15.109620666835209, 36.95783951241028],
|
|
20
|
+
[13.876381589019879, 36.95257399124932]
|
|
21
|
+
]
|
|
22
|
+
]
|
|
23
|
+
},
|
|
24
|
+
"properties": {
|
|
25
|
+
"datetime": "2021-02-21T10:00:17Z",
|
|
26
|
+
"platform": "sentinel-2b",
|
|
27
|
+
"constellation": "sentinel-2",
|
|
28
|
+
"instruments": ["msi"],
|
|
29
|
+
"gsd": 10,
|
|
30
|
+
"renders": {
|
|
31
|
+
"thumbnail": {
|
|
32
|
+
"title": "Thumbnail",
|
|
33
|
+
"assets": ["B04", "B03", "B02"],
|
|
34
|
+
"rescale": [[0, 150]],
|
|
35
|
+
"colormap_name": "rainbow",
|
|
36
|
+
"resampling": "bilinear",
|
|
37
|
+
"bidx": [1],
|
|
38
|
+
"width": 1024,
|
|
39
|
+
"height": 1024,
|
|
40
|
+
"bands": ["B4", "B3", "B2"]
|
|
41
|
+
},
|
|
42
|
+
"sir": {
|
|
43
|
+
"title": "Shortwave Infra-red",
|
|
44
|
+
"assets": ["B12", "B08", "B04"],
|
|
45
|
+
"rescale": [
|
|
46
|
+
[0, 5000],
|
|
47
|
+
[0, 7000],
|
|
48
|
+
[0, 9000]
|
|
49
|
+
],
|
|
50
|
+
"resampling": "nearest"
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
"collection": "sentinel-s2-l2a",
|
|
55
|
+
"assets": {
|
|
56
|
+
"metadata": {
|
|
57
|
+
"title": "Original XML metadata",
|
|
58
|
+
"type": "application/xml",
|
|
59
|
+
"roles": ["metadata"],
|
|
60
|
+
"href": "metadata.xml"
|
|
61
|
+
},
|
|
62
|
+
"B01": {
|
|
63
|
+
"title": "Band 1 (coastal)",
|
|
64
|
+
"type": "image/tiff; application=geotiff; profile=cloud-optimized",
|
|
65
|
+
"roles": ["data"],
|
|
66
|
+
"gsd": 60,
|
|
67
|
+
"href": "B01.tif"
|
|
68
|
+
},
|
|
69
|
+
"B02": {
|
|
70
|
+
"title": "Band 2 (blue)",
|
|
71
|
+
"type": "image/tiff; application=geotiff; profile=cloud-optimized",
|
|
72
|
+
"roles": ["data"],
|
|
73
|
+
"gsd": 10,
|
|
74
|
+
"href": "B02.tif"
|
|
75
|
+
},
|
|
76
|
+
"B03": {
|
|
77
|
+
"title": "Band 3 (green)",
|
|
78
|
+
"type": "image/tiff; application=geotiff; profile=cloud-optimized",
|
|
79
|
+
"roles": ["data"],
|
|
80
|
+
"gsd": 10,
|
|
81
|
+
"href": "B03.tif"
|
|
82
|
+
},
|
|
83
|
+
"B04": {
|
|
84
|
+
"title": "Band 4 (red)",
|
|
85
|
+
"type": "image/tiff; application=geotiff; profile=cloud-optimized",
|
|
86
|
+
"roles": ["data"],
|
|
87
|
+
"gsd": 10,
|
|
88
|
+
"href": "B04.tif"
|
|
89
|
+
},
|
|
90
|
+
"B05": {
|
|
91
|
+
"title": "Band 5",
|
|
92
|
+
"type": "image/tiff; application=geotiff; profile=cloud-optimized",
|
|
93
|
+
"roles": ["data"],
|
|
94
|
+
"gsd": 20,
|
|
95
|
+
"href": "B05.tif"
|
|
96
|
+
},
|
|
97
|
+
"B06": {
|
|
98
|
+
"title": "Band 6",
|
|
99
|
+
"type": "image/tiff; application=geotiff; profile=cloud-optimized",
|
|
100
|
+
"roles": ["data"],
|
|
101
|
+
"gsd": 20,
|
|
102
|
+
"href": "B06.tif"
|
|
103
|
+
},
|
|
104
|
+
"B07": {
|
|
105
|
+
"title": "Band 7",
|
|
106
|
+
"type": "image/tiff; application=geotiff; profile=cloud-optimized",
|
|
107
|
+
"roles": ["data"],
|
|
108
|
+
"gsd": 20,
|
|
109
|
+
"href": "B07.tif"
|
|
110
|
+
},
|
|
111
|
+
"B08": {
|
|
112
|
+
"title": "Band 8 (nir)",
|
|
113
|
+
"type": "image/tiff; application=geotiff; profile=cloud-optimized",
|
|
114
|
+
"roles": ["data"],
|
|
115
|
+
"gsd": 10,
|
|
116
|
+
"href": "B08.tif"
|
|
117
|
+
},
|
|
118
|
+
"B8A": {
|
|
119
|
+
"title": "Band 8A",
|
|
120
|
+
"type": "image/tiff; application=geotiff; profile=cloud-optimized",
|
|
121
|
+
"roles": ["data"],
|
|
122
|
+
"gsd": 20,
|
|
123
|
+
"href": "B8A.tif"
|
|
124
|
+
},
|
|
125
|
+
"B09": {
|
|
126
|
+
"title": "Band 9",
|
|
127
|
+
"type": "image/tiff; application=geotiff; profile=cloud-optimized",
|
|
128
|
+
"roles": ["data"],
|
|
129
|
+
"gsd": 60,
|
|
130
|
+
"href": "B09.tif"
|
|
131
|
+
},
|
|
132
|
+
"B11": {
|
|
133
|
+
"title": "Band 11 (swir16)",
|
|
134
|
+
"type": "image/tiff; application=geotiff; profile=cloud-optimized",
|
|
135
|
+
"roles": ["data"],
|
|
136
|
+
"gsd": 20,
|
|
137
|
+
"href": "B11.tif"
|
|
138
|
+
},
|
|
139
|
+
"B12": {
|
|
140
|
+
"title": "Band 12 (swir22)",
|
|
141
|
+
"type": "image/tiff; application=geotiff; profile=cloud-optimized",
|
|
142
|
+
"roles": ["data"],
|
|
143
|
+
"gsd": 20,
|
|
144
|
+
"href": "B12.tif"
|
|
145
|
+
},
|
|
146
|
+
"AOT": {
|
|
147
|
+
"title": "Aerosol Optical Thickness (AOT)",
|
|
148
|
+
"type": "image/tiff; application=geotiff; profile=cloud-optimized",
|
|
149
|
+
"roles": ["data"],
|
|
150
|
+
"href": "AOT.tif"
|
|
151
|
+
},
|
|
152
|
+
"WVP": {
|
|
153
|
+
"title": "Water Vapour (WVP)",
|
|
154
|
+
"type": "image/tiff; application=geotiff; profile=cloud-optimized",
|
|
155
|
+
"roles": ["data"],
|
|
156
|
+
"href": "WVP.tif",
|
|
157
|
+
"proj:shape": [10980, 10980],
|
|
158
|
+
"proj:transform": [10, 0, 399960, 0, -10, 4200000, 0, 0, 1]
|
|
159
|
+
},
|
|
160
|
+
"SCL": {
|
|
161
|
+
"title": "Scene Classification Map (SCL)",
|
|
162
|
+
"type": "image/tiff; application=geotiff; profile=cloud-optimized",
|
|
163
|
+
"roles": ["data"],
|
|
164
|
+
"href": "SCL.tif"
|
|
165
|
+
}
|
|
166
|
+
},
|
|
167
|
+
"links": [
|
|
168
|
+
{
|
|
169
|
+
"rel": "collection",
|
|
170
|
+
"href": "./collection.json",
|
|
171
|
+
"type": "application/json",
|
|
172
|
+
"title": "Sentinel-2 L2A Cogs Collection"
|
|
173
|
+
}
|
|
174
|
+
]
|
|
175
|
+
}
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
"""Tests for pystac.tests.extensions.render"""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
import pytest
|
|
7
|
+
|
|
8
|
+
import pystac
|
|
9
|
+
import pystac.errors
|
|
10
|
+
from pystac.extensions.render import Render, RenderExtension
|
|
11
|
+
|
|
12
|
+
DATA_FILES = Path(__file__).resolve().parent / "data-files"
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@pytest.fixture
|
|
16
|
+
def ext_collection_uri() -> str:
|
|
17
|
+
return str(DATA_FILES / "collection.json")
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@pytest.fixture
|
|
21
|
+
def ext_collection(ext_collection_uri: str) -> pystac.Collection:
|
|
22
|
+
return pystac.Collection.from_file(ext_collection_uri)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@pytest.fixture
|
|
26
|
+
def ext_item_uri() -> str:
|
|
27
|
+
return str(DATA_FILES / "item.json")
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@pytest.fixture
|
|
31
|
+
def ext_item(ext_item_uri: str) -> pystac.Item:
|
|
32
|
+
return pystac.Item.from_file(ext_item_uri)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@pytest.fixture
|
|
36
|
+
def render() -> Render:
|
|
37
|
+
return Render.create(
|
|
38
|
+
assets=["B4", "B3", "B2"],
|
|
39
|
+
title="RGB",
|
|
40
|
+
rescale=[[0, 1000], [0, 1000], [0, 1000]],
|
|
41
|
+
nodata=-9999,
|
|
42
|
+
colormap_name="viridis",
|
|
43
|
+
colormap={
|
|
44
|
+
"0": "#e5f5f9",
|
|
45
|
+
"10": "#99d8c9",
|
|
46
|
+
"255": "#2ca25f",
|
|
47
|
+
},
|
|
48
|
+
color_formula="gamma rg 1.3, sigmoidal rgb 22 0.1, saturation 1.5",
|
|
49
|
+
resampling="bilinear",
|
|
50
|
+
expression="(B08-B04)/(B08+B04)",
|
|
51
|
+
minmax_zoom=[2, 18],
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
@pytest.fixture
|
|
56
|
+
def thumbnail_render(ext_item: pystac.Item) -> Render:
|
|
57
|
+
return RenderExtension.ext(ext_item).renders["thumbnail"]
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def test_collection_stac_extensions(ext_collection: pystac.Collection) -> None:
|
|
61
|
+
assert RenderExtension.has_extension(ext_collection)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def test_item_stac_extensions(ext_item: pystac.Item) -> None:
|
|
65
|
+
assert RenderExtension.has_extension(ext_item)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def test_ext_raises_if_item_does_not_conform(item: pystac.Item) -> None:
|
|
69
|
+
with pytest.raises(pystac.errors.ExtensionNotImplemented):
|
|
70
|
+
RenderExtension.ext(item)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def test_ext_raises_if_collection_does_not_conform(
|
|
74
|
+
collection: pystac.Collection,
|
|
75
|
+
) -> None:
|
|
76
|
+
with pytest.raises(pystac.errors.ExtensionNotImplemented):
|
|
77
|
+
RenderExtension.ext(collection)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def test_ext_raises_on_catalog(catalog: pystac.Catalog) -> None:
|
|
81
|
+
with pytest.raises(
|
|
82
|
+
pystac.errors.ExtensionTypeError,
|
|
83
|
+
match="RenderExtension does not apply to type 'Catalog'",
|
|
84
|
+
):
|
|
85
|
+
RenderExtension.ext(catalog) # type: ignore
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def test_item_to_from_dict(ext_item_uri: str, ext_item: pystac.Item) -> None:
|
|
89
|
+
with open(ext_item_uri) as f:
|
|
90
|
+
d = json.load(f)
|
|
91
|
+
actual = ext_item.to_dict(include_self_link=False)
|
|
92
|
+
assert actual == d
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def test_collection_to_from_dict(
|
|
96
|
+
ext_collection_uri: str, ext_collection: pystac.Collection
|
|
97
|
+
) -> None:
|
|
98
|
+
with open(ext_collection_uri) as f:
|
|
99
|
+
d = json.load(f)
|
|
100
|
+
actual = ext_collection.to_dict(include_self_link=False)
|
|
101
|
+
assert actual == d
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def test_add_to_item(item: pystac.Item) -> None:
|
|
105
|
+
assert not RenderExtension.has_extension(item)
|
|
106
|
+
RenderExtension.add_to(item)
|
|
107
|
+
|
|
108
|
+
assert RenderExtension.has_extension(item)
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def test_add_to_collection(collection: pystac.Collection) -> None:
|
|
112
|
+
assert not RenderExtension.has_extension(collection)
|
|
113
|
+
RenderExtension.add_to(collection)
|
|
114
|
+
|
|
115
|
+
assert RenderExtension.has_extension(collection)
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def test_get_render_values(thumbnail_render: Render) -> None:
|
|
119
|
+
assert thumbnail_render.title == "Thumbnail"
|
|
120
|
+
assert thumbnail_render.assets == ["B04", "B03", "B02"]
|
|
121
|
+
assert thumbnail_render.rescale == [[0, 150]]
|
|
122
|
+
assert thumbnail_render.colormap_name == "rainbow"
|
|
123
|
+
assert thumbnail_render.resampling == "bilinear"
|
|
124
|
+
assert thumbnail_render.properties.get("bidx") == [1]
|
|
125
|
+
assert thumbnail_render.properties.get("width") == 1024
|
|
126
|
+
assert thumbnail_render.properties.get("height") == 1024
|
|
127
|
+
assert thumbnail_render.properties.get("bands") == ["B4", "B3", "B2"]
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def test_apply_renders_to_item(item: pystac.Item, render: Render) -> None:
|
|
131
|
+
RenderExtension.add_to(item)
|
|
132
|
+
|
|
133
|
+
RenderExtension.ext(item).apply({"render": render})
|
|
134
|
+
assert item.ext.render.renders["render"].assets == ["B4", "B3", "B2"]
|
|
135
|
+
assert item.ext.render.renders["render"].title == "RGB"
|
|
136
|
+
assert item.ext.render.renders["render"].rescale == [
|
|
137
|
+
[0, 1000],
|
|
138
|
+
[0, 1000],
|
|
139
|
+
[0, 1000],
|
|
140
|
+
]
|
|
141
|
+
assert item.ext.render.renders["render"].nodata == -9999
|
|
142
|
+
assert item.ext.render.renders["render"].colormap_name == "viridis"
|
|
143
|
+
assert item.ext.render.renders["render"].colormap == {
|
|
144
|
+
"0": "#e5f5f9",
|
|
145
|
+
"10": "#99d8c9",
|
|
146
|
+
"255": "#2ca25f",
|
|
147
|
+
}
|
|
148
|
+
assert (
|
|
149
|
+
item.ext.render.renders["render"].color_formula
|
|
150
|
+
== "gamma rg 1.3, sigmoidal rgb 22 0.1, saturation 1.5"
|
|
151
|
+
)
|
|
152
|
+
assert item.ext.render.renders["render"].resampling == "bilinear"
|
|
153
|
+
assert item.ext.render.renders["render"].expression == "(B08-B04)/(B08+B04)"
|
|
154
|
+
assert item.ext.render.renders["render"].minmax_zoom == [2, 18]
|
|
155
|
+
|
|
156
|
+
assert item.ext.render.renders["render"] == render
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def test_get_unset_properties(item: pystac.Item) -> None:
|
|
160
|
+
RenderExtension.add_to(item)
|
|
161
|
+
RenderExtension.ext(item).apply(
|
|
162
|
+
{
|
|
163
|
+
"render": Render.create(
|
|
164
|
+
assets=["B4", "B3", "B2"],
|
|
165
|
+
)
|
|
166
|
+
}
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
assert item.ext.render.renders["render"].title is None
|
|
170
|
+
assert item.ext.render.renders["render"].rescale is None
|
|
171
|
+
assert item.ext.render.renders["render"].nodata is None
|
|
172
|
+
assert item.ext.render.renders["render"].colormap_name is None
|
|
173
|
+
assert item.ext.render.renders["render"].colormap is None
|
|
174
|
+
assert item.ext.render.renders["render"].color_formula is None
|
|
175
|
+
assert item.ext.render.renders["render"].resampling is None
|
|
176
|
+
assert item.ext.render.renders["render"].expression is None
|
|
177
|
+
assert item.ext.render.renders["render"].minmax_zoom is None
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def test_equality_check_with_unexpected_type_raises_notimplemented_error() -> None:
|
|
181
|
+
render = Render.create(
|
|
182
|
+
assets=["B4", "B3", "B2"],
|
|
183
|
+
)
|
|
184
|
+
with pytest.raises(NotImplementedError):
|
|
185
|
+
_ = render == 1
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
def test_item_repr(ext_item: pystac.Item) -> None:
|
|
189
|
+
ext = RenderExtension.ext(ext_item)
|
|
190
|
+
assert ext.__repr__() == f"<ItemRenderExtension Item id={ext_item.id}>"
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def test_collection_repr(ext_collection: pystac.Collection) -> None:
|
|
194
|
+
ext = RenderExtension.ext(ext_collection)
|
|
195
|
+
assert (
|
|
196
|
+
ext.__repr__()
|
|
197
|
+
== f"<CollectionRenderExtension Collection id={ext_collection.id}>"
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
def test_render_repr() -> None:
|
|
202
|
+
render = Render.create(
|
|
203
|
+
assets=["B4", "B3", "B2"],
|
|
204
|
+
title="RGB",
|
|
205
|
+
rescale=[[0, 1000], [0, 1000], [0, 1000]],
|
|
206
|
+
nodata=-9999,
|
|
207
|
+
colormap_name="viridis",
|
|
208
|
+
colormap={
|
|
209
|
+
"0": "#e5f5f9",
|
|
210
|
+
"10": "#99d8c9",
|
|
211
|
+
"255": "#2ca25f",
|
|
212
|
+
},
|
|
213
|
+
color_formula="gamma rg 1.3, sigmoidal rgb 22 0.1, saturation 1.5",
|
|
214
|
+
resampling="bilinear",
|
|
215
|
+
expression="(B08-B04)/(B08+B04)",
|
|
216
|
+
minmax_zoom=[2, 18],
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
assert render.__repr__() == (
|
|
220
|
+
"<Render assets=['B4', 'B3', 'B2'] "
|
|
221
|
+
"title=RGB rescale=[[0, 1000], [0, 1000], [0, 1000]] "
|
|
222
|
+
"nodata=-9999 colormap_name=viridis "
|
|
223
|
+
"colormap={'0': '#e5f5f9', '10': '#99d8c9', '255': '#2ca25f'} "
|
|
224
|
+
"color_formula=gamma rg 1.3, sigmoidal rgb 22 0.1, saturation 1.5 "
|
|
225
|
+
"resampling=bilinear expression=(B08-B04)/(B08+B04) minmax_zoom=[2, 18]>"
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
@pytest.mark.vcr
|
|
230
|
+
def test_item_validate(ext_item: pystac.Item) -> None:
|
|
231
|
+
assert ext_item.validate()
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
@pytest.mark.vcr
|
|
235
|
+
def test_collection_validate(ext_collection: pystac.Collection) -> None:
|
|
236
|
+
assert ext_collection.validate()
|