pystac-ext-render 2.0.0rc0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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,37 @@
1
+ Metadata-Version: 2.4
2
+ Name: pystac-ext-render
3
+ Version: 2.0.0rc0
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,5 @@
1
+ pystac/extensions/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ pystac/extensions/render.py,sha256=XSSxS0vp0p1ZMih8qGcfCtujCx91CD1xMIlAnTdvpf4,14896
3
+ pystac_ext_render-2.0.0rc0.dist-info/METADATA,sha256=4CdnkZwM5qTQVJXLJ974j2boqpR_t3LLYdvxq_-ynq0,1830
4
+ pystac_ext_render-2.0.0rc0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
5
+ pystac_ext_render-2.0.0rc0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.29.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any