44"""
55
66import enum
7- from typing import Generic , Optional , Set , TypeVar , cast
7+ from datetime import datetime as Datetime
8+ from pystac .summaries import RangeSummary
9+ from typing import Dict , Any , List , Generic , Iterable , Optional , Set , TypeVar , cast
810
911import pystac
1012from pystac .extensions .base import (
1113 ExtensionManagementMixin ,
1214 PropertiesExtension ,
15+ SummariesExtension ,
1316)
1417from pystac .extensions .hooks import ExtensionHooks
15- from pystac .utils import map_opt
18+ from pystac .utils import str_to_datetime , datetime_to_str , map_opt
1619
1720T = TypeVar ("T" , pystac .Item , pystac .Asset )
1821
1922SCHEMA_URI = "https://stac-extensions.github.io/sat/v1.0.0/schema.json"
2023
21- ORBIT_STATE : str = "sat:orbit_state"
22- RELATIVE_ORBIT : str = "sat:relative_orbit"
24+ PREFIX : str = "sat:"
25+ PLATFORM_INTERNATIONAL_DESIGNATOR_PROP : str = (
26+ PREFIX + "platform_international_designator"
27+ )
28+ ABSOLUTE_ORBIT_PROP : str = PREFIX + "absolute_orbit"
29+ ORBIT_STATE_PROP : str = PREFIX + "orbit_state"
30+ RELATIVE_ORBIT_PROP : str = PREFIX + "relative_orbit"
31+ ANX_DATETIME_PROP : str = PREFIX + "anx_datetime"
2332
2433
25- class OrbitState (enum .Enum ):
34+ class OrbitState (str , enum .Enum ):
2635 ASCENDING = "ascending"
2736 DESCENDING = "descending"
2837 GEOSTATIONARY = "geostationary"
@@ -31,25 +40,31 @@ class OrbitState(enum.Enum):
3140class SatExtension (
3241 Generic [T ], PropertiesExtension , ExtensionManagementMixin [pystac .Item ]
3342):
34- """SatItemExt extends Item to add sat properties to a STAC Item.
43+ """An abstract class that can be used to extend the properties of an
44+ :class:`~pystac.Item` or :class:`~pystac.Asset` with properties from the
45+ :stac-ext:`Satellite Extension <sat>`. This class is generic over the type of
46+ STAC Object to be extended (e.g. :class:`~pystac.Item`,
47+ :class:`~pystac.Collection`).
3548
36- Args:
37- item : The item to be extended.
49+ To create a concrete instance of :class:`SatExtension`, use the
50+ :meth:`SatExtension.ext` method. For example:
3851
39- Attributes:
40- item : The item that is being extended.
52+ .. code-block:: python
4153
42- Note:
43- Using SatItemExt to directly wrap an item will add the 'sat'
44- extension ID to the item's stac_extensions.
54+ >>> item: pystac.Item = ...
55+ >>> sat_ext = SatExtension.ext(item)
4556 """
4657
4758 def apply (
4859 self ,
4960 orbit_state : Optional [OrbitState ] = None ,
5061 relative_orbit : Optional [int ] = None ,
62+ absolute_orbit : Optional [int ] = None ,
63+ platform_international_designator : Optional [str ] = None ,
64+ anx_datetime : Optional [Datetime ] = None ,
5165 ) -> None :
52- """Applies ext extension properties to the extended Item.
66+ """Applies ext extension properties to the extended :class:`~pystac.Item` or
67+ class:`~pystac.Asset`.
5368
5469 Must specify at least one of orbit_state or relative_orbit in order
5570 for the sat extension to properties to be valid.
@@ -62,41 +77,75 @@ def apply(
6277 the time of acquisition.
6378 """
6479
80+ self .platform_international_designator = platform_international_designator
6581 self .orbit_state = orbit_state
82+ self .absolute_orbit = absolute_orbit
6683 self .relative_orbit = relative_orbit
84+ self .anx_datetime = anx_datetime
6785
6886 @property
69- def orbit_state (self ) -> Optional [OrbitState ]:
70- """Get or sets an orbit state of the item.
87+ def platform_international_designator (self ) -> Optional [str ]:
88+ """Gets or sets the International Designator, also known as COSPAR ID, and
89+ NSSDCA ID."""
90+ return self ._get_property (PLATFORM_INTERNATIONAL_DESIGNATOR_PROP , str )
7191
72- Returns:
73- OrbitState or None
74- """
75- return map_opt (lambda x : OrbitState (x ), self ._get_property (ORBIT_STATE , str ))
92+ @platform_international_designator .setter
93+ def platform_international_designator (self , v : Optional [str ]) -> None :
94+ self ._set_property (PLATFORM_INTERNATIONAL_DESIGNATOR_PROP , v )
95+
96+ @property
97+ def orbit_state (self ) -> Optional [OrbitState ]:
98+ """Get or sets an orbit state of the object."""
99+ return map_opt (
100+ lambda x : OrbitState (x ), self ._get_property (ORBIT_STATE_PROP , str )
101+ )
76102
77103 @orbit_state .setter
78104 def orbit_state (self , v : Optional [OrbitState ]) -> None :
79- self ._set_property (ORBIT_STATE , map_opt (lambda x : x .value , v ))
105+ self ._set_property (ORBIT_STATE_PROP , map_opt (lambda x : x .value , v ))
80106
81107 @property
82- def relative_orbit (self ) -> Optional [int ]:
83- """Get or sets a relative orbit number of the item.
108+ def absolute_orbit (self ) -> Optional [int ]:
109+ """Get or sets a absolute orbit number of the item."""
110+ return self ._get_property (ABSOLUTE_ORBIT_PROP , int )
84111
85- Returns:
86- int or None
87- """
88- return self ._get_property (RELATIVE_ORBIT , int )
112+ @absolute_orbit .setter
113+ def absolute_orbit (self , v : Optional [int ]) -> None :
114+ self ._set_property (ABSOLUTE_ORBIT_PROP , v )
115+
116+ @property
117+ def relative_orbit (self ) -> Optional [int ]:
118+ """Get or sets a relative orbit number of the item."""
119+ return self ._get_property (RELATIVE_ORBIT_PROP , int )
89120
90121 @relative_orbit .setter
91122 def relative_orbit (self , v : Optional [int ]) -> None :
92- self ._set_property (RELATIVE_ORBIT , v )
123+ self ._set_property (RELATIVE_ORBIT_PROP , v )
124+
125+ @property
126+ def anx_datetime (self ) -> Optional [Datetime ]:
127+ return map_opt (str_to_datetime , self ._get_property (ANX_DATETIME_PROP , str ))
128+
129+ @anx_datetime .setter
130+ def anx_datetime (self , v : Optional [Datetime ]) -> None :
131+ self ._set_property (ANX_DATETIME_PROP , map_opt (datetime_to_str , v ))
93132
94133 @classmethod
95134 def get_schema_uri (cls ) -> str :
96135 return SCHEMA_URI
97136
98137 @classmethod
99138 def ext (cls , obj : T , add_if_missing : bool = False ) -> "SatExtension[T]" :
139+ """Extends the given STAC Object with properties from the :stac-ext:`Satellite
140+ Extension <sat>`.
141+
142+ This extension can be applied to instances of :class:`~pystac.Item` or
143+ :class:`~pystac.Asset`.
144+
145+ Raises:
146+
147+ pystac.ExtensionTypeError : If an invalid object type is passed.
148+ """
100149 if isinstance (obj , pystac .Item ):
101150 if add_if_missing :
102151 cls .add_to (obj )
@@ -112,8 +161,28 @@ def ext(cls, obj: T, add_if_missing: bool = False) -> "SatExtension[T]":
112161 f"Satellite extension does not apply to type '{ type (obj ).__name__ } '"
113162 )
114163
164+ @staticmethod
165+ def summaries (obj : pystac .Collection ) -> "SummariesSatExtension" :
166+ """Returns the extended summaries object for the given collection."""
167+ return SummariesSatExtension (obj )
168+
115169
116170class ItemSatExtension (SatExtension [pystac .Item ]):
171+ """A concrete implementation of :class:`SatExtension` on an :class:`~pystac.Item`
172+ that extends the properties of the Item to include properties defined in the
173+ :stac-ext:`Satellite Extension <sat>`.
174+
175+ This class should generally not be instantiated directly. Instead, call
176+ :meth:`SatExtension.ext` on an :class:`~pystac.Item` to
177+ extend it.
178+ """
179+
180+ item : pystac .Item
181+ """The :class:`~pystac.Item` being extended."""
182+
183+ properties : Dict [str , Any ]
184+ """The :class:`~pystac.Item` properties, including extension properties."""
185+
117186 def __init__ (self , item : pystac .Item ):
118187 self .item = item
119188 self .properties = item .properties
@@ -123,6 +192,25 @@ def __repr__(self) -> str:
123192
124193
125194class AssetSatExtension (SatExtension [pystac .Asset ]):
195+ """A concrete implementation of :class:`SatExtension` on an :class:`~pystac.Asset`
196+ that extends the properties of the Asset to include properties defined in the
197+ :stac-ext:`Satellite Extension <sat>`.
198+
199+ This class should generally not be instantiated directly. Instead, call
200+ :meth:`SatExtension.ext` on an :class:`~pystac.Asset` to
201+ extend it.
202+ """
203+
204+ asset_href : str
205+ """The ``href`` value of the :class:`~pystac.Asset` being extended."""
206+
207+ properties : Dict [str , Any ]
208+ """The :class:`~pystac.Asset` fields, including extension properties."""
209+
210+ additional_read_properties : Optional [Iterable [Dict [str , Any ]]] = None
211+ """If present, this will be a list containing 1 dictionary representing the
212+ properties of the owning :class:`~pystac.Item`."""
213+
126214 def __init__ (self , asset : pystac .Asset ):
127215 self .asset_href = asset .href
128216 self .properties = asset .extra_fields
@@ -133,6 +221,75 @@ def __repr__(self) -> str:
133221 return "<AssetSatExtension Asset href={}>" .format (self .asset_href )
134222
135223
224+ class SummariesSatExtension (SummariesExtension ):
225+ """A concrete implementation of :class:`~SummariesExtension` that extends
226+ the ``summaries`` field of a :class:`~pystac.Collection` to include properties
227+ defined in the :stac-ext:`Satellite Extension <sat>`.
228+ """
229+
230+ @property
231+ def platform_international_designator (self ) -> Optional [List [str ]]:
232+ """Get or sets the summary of
233+ :attr:`SatExtension.platform_international_designator` values for this
234+ Collection.
235+ """
236+
237+ return self .summaries .get_list (PLATFORM_INTERNATIONAL_DESIGNATOR_PROP )
238+
239+ @platform_international_designator .setter
240+ def platform_international_designator (self , v : Optional [List [str ]]) -> None :
241+ self ._set_summary (PLATFORM_INTERNATIONAL_DESIGNATOR_PROP , v )
242+
243+ @property
244+ def orbit_state (self ) -> Optional [List [OrbitState ]]:
245+ """Get or sets the summary of :attr:`SatExtension.orbit_state` values
246+ for this Collection.
247+ """
248+
249+ return self .summaries .get_list (ORBIT_STATE_PROP )
250+
251+ @orbit_state .setter
252+ def orbit_state (self , v : Optional [List [OrbitState ]]) -> None :
253+ self ._set_summary (ORBIT_STATE_PROP , v )
254+
255+ @property
256+ def absolute_orbit (self ) -> Optional [RangeSummary [int ]]:
257+ return self .summaries .get_range (ABSOLUTE_ORBIT_PROP )
258+
259+ @absolute_orbit .setter
260+ def absolute_orbit (self , v : Optional [RangeSummary [int ]]) -> None :
261+ self ._set_summary (ABSOLUTE_ORBIT_PROP , v )
262+
263+ @property
264+ def relative_orbit (self ) -> Optional [RangeSummary [int ]]:
265+ return self .summaries .get_range (RELATIVE_ORBIT_PROP )
266+
267+ @relative_orbit .setter
268+ def relative_orbit (self , v : Optional [RangeSummary [int ]]) -> None :
269+ self ._set_summary (RELATIVE_ORBIT_PROP , v )
270+
271+ @property
272+ def anx_datetime (self ) -> Optional [RangeSummary [Datetime ]]:
273+ return map_opt (
274+ lambda s : RangeSummary (
275+ str_to_datetime (s .minimum ), str_to_datetime (s .maximum )
276+ ),
277+ self .summaries .get_range (ANX_DATETIME_PROP ),
278+ )
279+
280+ @anx_datetime .setter
281+ def anx_datetime (self , v : Optional [RangeSummary [Datetime ]]) -> None :
282+ self ._set_summary (
283+ ANX_DATETIME_PROP ,
284+ map_opt (
285+ lambda s : RangeSummary (
286+ datetime_to_str (s .minimum ), datetime_to_str (s .maximum )
287+ ),
288+ v ,
289+ ),
290+ )
291+
292+
136293class SatExtensionHooks (ExtensionHooks ):
137294 schema_uri : str = SCHEMA_URI
138295 prev_extension_ids : Set [str ] = set (["sat" ])
0 commit comments