-
Notifications
You must be signed in to change notification settings - Fork 113
/
view.py
297 lines (236 loc) · 10.9 KB
/
view.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
"""Implement the :stac-ext:`View Geometry Extension <view>`."""
from typing import Any, Dict, Generic, Iterable, Optional, TypeVar, Union, cast
import pystac
from pystac.summaries import RangeSummary
from pystac.extensions.base import (
ExtensionManagementMixin,
PropertiesExtension,
SummariesExtension,
)
from pystac.extensions.hooks import ExtensionHooks
T = TypeVar("T", pystac.Item, pystac.Asset)
SCHEMA_URI: str = "https://stac-extensions.github.io/view/v1.0.0/schema.json"
PREFIX: str = "view:"
OFF_NADIR_PROP: str = PREFIX + "off_nadir"
INCIDENCE_ANGLE_PROP: str = PREFIX + "incidence_angle"
AZIMUTH_PROP: str = PREFIX + "azimuth"
SUN_AZIMUTH_PROP: str = PREFIX + "sun_azimuth"
SUN_ELEVATION_PROP: str = PREFIX + "sun_elevation"
class ViewExtension(
Generic[T],
PropertiesExtension,
ExtensionManagementMixin[Union[pystac.Item, pystac.Collection]],
):
"""An abstract class that can be used to extend the properties of an
:class:`~pystac.Item` with properties from the :stac-ext:`View Geometry
Extension <view>`. This class is generic over the type of STAC Object to be
extended (e.g. :class:`~pystac.Item`, :class:`~pystac.Asset`).
To create a concrete instance of :class:`ViewExtension`, use the
:meth:`ViewExtension.ext` method. For example:
.. code-block:: python
>>> item: pystac.Item = ...
>>> view_ext = ViewExtension.ext(item)
"""
def apply(
self,
off_nadir: Optional[float] = None,
incidence_angle: Optional[float] = None,
azimuth: Optional[float] = None,
sun_azimuth: Optional[float] = None,
sun_elevation: Optional[float] = None,
) -> None:
"""Applies View Geometry extension properties to the extended
:class:`~pystac.Item`.
Args:
off_nadir : The angle from the sensor between nadir (straight down)
and the scene center. Measured in degrees (0-90).
incidence_angle : The incidence angle is the angle between the
vertical (normal) to the intercepting surface and the line of sight
back to the satellite at the scene center. Measured in degrees (0-90).
azimuth : Viewing azimuth angle. The angle measured from the
sub-satellite point (point on the ground below the platform) between
the scene center and true north. Measured clockwise from north in
degrees (0-360).
sun_azimuth : Sun azimuth angle. From the scene center point on the
ground, this is the angle between truth north and the sun. Measured
clockwise in degrees (0-360).
sun_elevation : Sun elevation angle. The angle from the tangent of
the scene center point to the sun. Measured from the horizon in
degrees (0-90).
"""
self.off_nadir = off_nadir
self.incidence_angle = incidence_angle
self.azimuth = azimuth
self.sun_azimuth = sun_azimuth
self.sun_elevation = sun_elevation
@property
def off_nadir(self) -> Optional[float]:
"""Get or sets the angle from the sensor between nadir (straight down)
and the scene center. Measured in degrees (0-90).
"""
return self._get_property(OFF_NADIR_PROP, float)
@off_nadir.setter
def off_nadir(self, v: Optional[float]) -> None:
self._set_property(OFF_NADIR_PROP, v)
@property
def incidence_angle(self) -> Optional[float]:
"""Get or sets the incidence angle is the angle between the vertical (normal)
to the intercepting surface and the line of sight back to the satellite at
the scene center. Measured in degrees (0-90).
"""
return self._get_property(INCIDENCE_ANGLE_PROP, float)
@incidence_angle.setter
def incidence_angle(self, v: Optional[float]) -> None:
self._set_property(INCIDENCE_ANGLE_PROP, v)
@property
def azimuth(self) -> Optional[float]:
"""Get or sets the viewing azimuth angle.
The angle measured from the sub-satellite
point (point on the ground below the platform) between the scene center and true
north. Measured clockwise from north in degrees (0-360).
"""
return self._get_property(AZIMUTH_PROP, float)
@azimuth.setter
def azimuth(self, v: Optional[float]) -> None:
self._set_property(AZIMUTH_PROP, v)
@property
def sun_azimuth(self) -> Optional[float]:
"""Get or sets the sun azimuth angle.
From the scene center point on the ground, this
is the angle between truth north and the sun. Measured clockwise in
degrees (0-360).
"""
return self._get_property(SUN_AZIMUTH_PROP, float)
@sun_azimuth.setter
def sun_azimuth(self, v: Optional[float]) -> None:
self._set_property(SUN_AZIMUTH_PROP, v)
@property
def sun_elevation(self) -> Optional[float]:
"""Get or sets the sun elevation angle. The angle from the tangent of the scene
center point to the sun. Measured from the horizon in degrees (0-90).
"""
return self._get_property(SUN_ELEVATION_PROP, float)
@sun_elevation.setter
def sun_elevation(self, v: Optional[float]) -> None:
self._set_property(SUN_ELEVATION_PROP, v)
@classmethod
def get_schema_uri(cls) -> str:
return SCHEMA_URI
@classmethod
def ext(cls, obj: T, add_if_missing: bool = False) -> "ViewExtension[T]":
"""Extends the given STAC Object with properties from the :stac-ext:`View
Geometry Extension <scientific>`.
This extension can be applied to instances of :class:`~pystac.Item` or
:class:`~pystac.Asset`.
Raises:
pystac.ExtensionTypeError : If an invalid object type is passed.
"""
if isinstance(obj, pystac.Item):
cls.validate_has_extension(obj, add_if_missing)
return cast(ViewExtension[T], ItemViewExtension(obj))
elif isinstance(obj, pystac.Asset):
cls.validate_owner_has_extension(obj, add_if_missing)
return cast(ViewExtension[T], AssetViewExtension(obj))
else:
raise pystac.ExtensionTypeError(
f"View extension does not apply to type '{type(obj).__name__}'"
)
@classmethod
def summaries(
cls, obj: pystac.Collection, add_if_missing: bool = False
) -> "SummariesViewExtension":
"""Returns the extended summaries object for the given collection."""
cls.validate_has_extension(obj, add_if_missing)
return SummariesViewExtension(obj)
class ItemViewExtension(ViewExtension[pystac.Item]):
"""A concrete implementation of :class:`ViewExtension` on an :class:`~pystac.Item`
that extends the properties of the Item to include properties defined in the
:stac-ext:`View Geometry Extension <view>`.
This class should generally not be instantiated directly. Instead, call
:meth:`ViewExtension.ext` on an :class:`~pystac.Item` to extend it.
"""
item: pystac.Item
"""The :class:`~pystac.Item` being extended."""
properties: Dict[str, Any]
"""The :class:`~pystac.Item` properties, including extension properties."""
def __init__(self, item: pystac.Item):
self.item = item
self.properties = item.properties
def __repr__(self) -> str:
return "<ItemViewExtension Item id={}>".format(self.item.id)
class AssetViewExtension(ViewExtension[pystac.Asset]):
"""A concrete implementation of :class:`ViewExtension` on an :class:`~pystac.Asset`
that extends the Asset fields to include properties defined in the
:stac-ext:`View Geometry Extension <view>`.
This class should generally not be instantiated directly. Instead, call
:meth:`ViewExtension.ext` on an :class:`~pystac.Asset` to extend it.
"""
asset_href: str
"""The ``href`` value of the :class:`~pystac.Asset` being extended."""
properties: Dict[str, Any]
"""The :class:`~pystac.Asset` fields, including extension properties."""
additional_read_properties: Optional[Iterable[Dict[str, Any]]] = None
"""If present, this will be a list containing 1 dictionary representing the
properties of the owning :class:`~pystac.Item`."""
def __init__(self, asset: pystac.Asset):
self.asset_href = asset.href
self.properties = asset.extra_fields
if asset.owner and isinstance(asset.owner, pystac.Item):
self.additional_read_properties = [asset.owner.properties]
def __repr__(self) -> str:
return "<AssetViewExtension Asset href={}>".format(self.asset_href)
class SummariesViewExtension(SummariesExtension):
"""A concrete implementation of :class:`~SummariesExtension` that extends
the ``summaries`` field of a :class:`~pystac.Collection` to include properties
defined in the :stac-ext:`View Object Extension <view>`.
"""
@property
def off_nadir(self) -> Optional[RangeSummary[float]]:
"""Get or sets the summary of :attr:`ViewExtension.off_nadir` values for
this Collection.
"""
return self.summaries.get_range(OFF_NADIR_PROP)
@off_nadir.setter
def off_nadir(self, v: Optional[RangeSummary[float]]) -> None:
self._set_summary(OFF_NADIR_PROP, v)
@property
def incidence_angle(self) -> Optional[RangeSummary[float]]:
"""Get or sets the summary of :attr:`ViewExtension.incidence_angle` values
for this Collection.
"""
return self.summaries.get_range(INCIDENCE_ANGLE_PROP)
@incidence_angle.setter
def incidence_angle(self, v: Optional[RangeSummary[float]]) -> None:
self._set_summary(INCIDENCE_ANGLE_PROP, v)
@property
def azimuth(self) -> Optional[RangeSummary[float]]:
"""Get or sets the summary of :attr:`ViewExtension.azimuth` values
for this Collection.
"""
return self.summaries.get_range(AZIMUTH_PROP)
@azimuth.setter
def azimuth(self, v: Optional[RangeSummary[float]]) -> None:
self._set_summary(AZIMUTH_PROP, v)
@property
def sun_azimuth(self) -> Optional[RangeSummary[float]]:
"""Get or sets the summary of :attr:`ViewExtension.sun_azimuth` values
for this Collection.
"""
return self.summaries.get_range(SUN_AZIMUTH_PROP)
@sun_azimuth.setter
def sun_azimuth(self, v: Optional[RangeSummary[float]]) -> None:
self._set_summary(SUN_AZIMUTH_PROP, v)
@property
def sun_elevation(self) -> Optional[RangeSummary[float]]:
"""Get or sets the summary of :attr:`ViewExtension.sun_elevation` values
for this Collection.
"""
return self.summaries.get_range(SUN_ELEVATION_PROP)
@sun_elevation.setter
def sun_elevation(self, v: Optional[RangeSummary[float]]) -> None:
self._set_summary(SUN_ELEVATION_PROP, v)
class ViewExtensionHooks(ExtensionHooks):
schema_uri = SCHEMA_URI
prev_extension_ids = {"view"}
stac_object_types = {pystac.STACObjectType.ITEM}
VIEW_EXTENSION_HOOKS: ExtensionHooks = ViewExtensionHooks()