55
66from __future__ import annotations
77
8+ from datetime import datetime
89from typing import (
910 Any ,
1011 Generic ,
4243
4344class ProcessingLevel (StringEnum ):
4445 RAW = "RAW"
46+ """Data in their original packets, as received from the instrument."""
4547 L0 = "L0"
48+ """Reconstructed unprocessed instrument data at full space time resolution with all
49+ available supplemental information to be used in subsequent processing
50+ (e.g., ephemeris, health and safety) appended."""
4651 L1 = "L1"
52+ """Unpacked, reformatted level 0 data, with all supplemental information to be used
53+ in subsequent processing appended. Optional radiometric and geometric correction
54+ applied to produce parameters in physical units. Data generally presented as full
55+ time/space resolution."""
4756 L1A = "L1A"
57+ """Unpacked, reformatted level 0 data, with all supplemental information to be used
58+ in subsequent processing appended. Optional radiometric and geometric correction
59+ applied to produce parameters in physical units. Data generally presented as full
60+ time/space resolution."""
4861 L1B = "L1B"
62+ """Unpacked, reformatted level 0 data, with all supplemental information to be used
63+ in subsequent processing appended. Optional radiometric and geometric correction
64+ applied to produce parameters in physical units. Data generally presented as full
65+ time/space resolution."""
4966 L1C = "L1C"
67+ """Unpacked, reformatted level 0 data, with all supplemental information to be used
68+ in subsequent processing appended. Optional radiometric and geometric correction
69+ applied to produce parameters in physical units. Data generally presented as full
70+ time/space resolution."""
5071 L2 = "L2"
72+ """Retrieved environmental variables (e.g., ocean wave height, soil-moisture,
73+ ice concentration) at the same resolution and location as the level 1 source
74+ data."""
5175 L2A = "L2A"
76+ """Retrieved environmental variables (e.g., ocean wave height, soil-moisture,
77+ ice concentration) at the same resolution and location as the level 1 source
78+ data."""
5279 L3 = "L3"
80+ """Data or retrieved environmental variables which have been spatially and/or
81+ temporally re-sampled (i.e., derived from level 1 or 2 products). Such
82+ re-sampling may include averaging and compositing."""
5383 L4 = "L4"
84+ """Model output or results from analyses of lower level data (i.e., variables that
85+ are not directly measured by the instruments, but are derived from these
86+ measurements)"""
87+
88+
89+ class ProcessingRelType (StringEnum ):
90+ """A list of rel types defined in the Processing Extension.
91+
92+ See the :stac-ext:`Processing Extension Relation types
93+ <processing#relation-types>` documentation
94+ for details."""
95+
96+ EXPRESSION = "processing-expression"
97+ """A processing chain (or script) that describes how the data has been processed."""
98+
99+ EXECUTION = "processing-execution"
100+ """URL to any resource representing the processing execution
101+ (e.g. OGC Process API)."""
102+
103+ SOFTWARE = "processing-software"
104+ """URL to any resource that identifies the software and versions used for processing
105+ the data, e.g. a Pipfile.lock (Python) or package-lock.json (NodeJS)."""
106+
107+ VALIDATION = "processing-validation"
108+ """URL to any kind of validation that has been applied after processing, e.g. a
109+ validation report or a script used for validation."""
54110
55111
56112class ProcessingExtension (
@@ -74,74 +130,162 @@ class ProcessingExtension(
74130
75131 """
76132
133+ name : Literal ["processing" ] = "processing"
134+
77135 def __init__ (self : Self , item : pystac .Item ) -> None :
78136 self .item = item
79137 self .properties = item .properties
80138
81139 def __repr__ (self : Self ) -> str :
82140 return f"<ProcessingExtension Item id={ self .item .id } >"
83141
84- def apply (self : Self , level : str | None = None ) -> None :
142+ def apply (
143+ self : Self ,
144+ level : ProcessingLevel | None = None ,
145+ datetime : datetime | None = None ,
146+ expression : str | None = None ,
147+ lineage : str | None = None ,
148+ facility : str | None = None ,
149+ version : str | None = None ,
150+ software : dict [str , str ] | None = None ,
151+ ) -> None :
152+ """Applies the processing extension properties to the extended Item.
153+
154+ Args:
155+ level: The processing level of the product. This should be the short name,
156+ as one of the available options under the `ProcessingLevel` enum.
157+ datetime: The datetime when the product was processed. Might be already
158+ specified in the common STAC metadata.
159+ expression: The expression used to obtain the processed product, like
160+ `gdal-calc` or `rio-calc`.
161+ lineage: Free text information about the how observations were processed or
162+ models that were used to create the resource being described.
163+ facility: The name of the facility that produced the data, like ESA.
164+ version: The version of the primary processing software or processing chain
165+ that produced the data, like the processing baseline for the Sentinel
166+ missions.
167+ software: A dictionary describing one or more applications or libraries that
168+ were involved during the production of the data for provenance purposes.
169+ """
85170 self .level = level
171+ self .datetime = datetime
172+ self .expression = expression
173+ self .lineage = lineage
174+ self .facility = facility
175+ self .version = version
176+ self .software = software
86177
87178 @property
88179 def level (self : Self ) -> ProcessingLevel | None :
89- """Get or sets the processing level of the object."""
180+ """Get or sets the processing level as the name commonly used to refer to the
181+ processing level to make it easier to search for product level across
182+ collections or items. This property is expected to be a `ProcessingLevel`"""
90183 return map_opt (
91184 lambda x : ProcessingLevel (x ), self ._get_property (LEVEL_PROP , str )
92185 )
93186
94187 @level .setter
95188 def level (self : Self , v : ProcessingLevel | None ) -> None :
96- self ._set_property (LEVEL_PROP , map_opt (lambda x : x .value , v ), pop_if_none = True )
189+ self ._set_property (LEVEL_PROP , map_opt (lambda x : x .value , v ))
97190
98191 @property
99- def datetime (self : Self ) -> str | None :
100- return self ._get_property (DATETIME_PROP , str )
192+ def datetime (self : Self ) -> datetime | None :
193+ """Gets or set the processing date and time of the corresponding data formatted
194+ according to RFC 3339, section 5.6, in UTC. The time of the processing can be
195+ specified as a global field in processing:datetime, but it can also be specified
196+ directly and individually via the created properties of the target asset as
197+ specified in the STAC Common metadata. See more at
198+ https://github.com/stac-extensions/processing?tab=readme-ov-file#processing-date-time"""
199+ return map_opt (str_to_datetime , self ._get_property (DATETIME_PROP , str ))
101200
102201 @datetime .setter
103- def datetime (self : Self , v : str | None ) -> None :
104- self ._set_property (DATETIME_PROP , v , pop_if_none = True )
202+ def datetime (self : Self , v : datetime | None ) -> None :
203+ self ._set_property (DATETIME_PROP , map_opt ( datetime_to_str , v ) )
105204
106205 @property
107- def expression (self : Self ) -> str | None :
108- return self ._get_property (EXPRESSION_PROP , str )
206+ def expression (self : Self ) -> dict [str , str | Any ] | None :
207+ """Gets or sets an expression or processing chain that describes how the data
208+ has been processed. Alternatively, you can also link to a processing chain with
209+ the relation type processing-expression.
210+ .. code-block:: python
211+ >>> proc_ext.expression = "(b4-b1)/(b4+b1)"
212+ """
213+ return self ._get_property (EXPRESSION_PROP , dict [str , str | Any ])
109214
110215 @expression .setter
111- def expression (self : Self , v : str | None ) -> None :
112- self ._set_property (EXPRESSION_PROP , v , pop_if_none = True )
216+ def expression (self : Self , v : str | Any | None ) -> None :
217+ if isinstance (v .expression , str ):
218+ exp_format = "string"
219+ elif isinstance (v .expression , object ):
220+ exp_format = "object"
221+ else :
222+ raise ValueError (
223+ "The provided expression is not a valid type (string or object)"
224+ )
225+
226+ expression = {
227+ "format" : exp_format ,
228+ "expression" : v ,
229+ }
230+
231+ self ._set_property (EXPRESSION_PROP , expression )
113232
114233 @property
115234 def lineage (self : Self ) -> str | None :
235+ """Gets or sets the lineage provided as free text information about how
236+ observations were processed or models that were used to create the resource
237+ being described NASA ISO. For example, GRD Post Processing for "GRD" product of
238+ Sentinel-1 satellites. CommonMark 0.29 syntax MAY be used for rich text
239+ representation."""
116240 return self ._get_property (LINEAGE_PROP , str )
117241
118242 @lineage .setter
119243 def lineage (self : Self , v : str | None ) -> None :
120- self ._set_property (LINEAGE_PROP , v , pop_if_none = True )
244+ self ._set_property (LINEAGE_PROP , v )
121245
122246 @property
123247 def facility (self : Self ) -> str | None :
248+ """Gets or sets the name of the facility that produced the data. For example,
249+ Copernicus S1 Core Ground Segment - DPA for product of Sentinel-1 satellites."""
124250 return self ._get_property (FACILITY_PROP , str )
125251
126252 @facility .setter
127253 def facility (self : Self , v : str | None ) -> None :
128- self ._set_property (FACILITY_PROP , v , pop_if_none = True )
254+ self ._set_property (FACILITY_PROP , v )
129255
130256 @property
131257 def version (self : Self ) -> str | None :
258+ """Gets or sets The version of the primary processing software or processing
259+ chain that produced the data. For example, this could be the processing baseline
260+ for the Sentinel missions."""
132261 return self ._get_property (VERSION_PROP , str )
133262
134263 @version .setter
135264 def version (self : Self , v : str | None ) -> None :
136- self ._set_property (VERSION_PROP , v , pop_if_none = True )
265+ self ._set_property (VERSION_PROP , v )
137266
138267 @property
139- def software (self : Self ) -> str | None :
140- return self ._get_property (SOFTWARE_PROP , str )
268+ def software (self : Self ) -> dict [str , str ] | None :
269+ """Gets or sets the processing software as a dictionary with name/version for
270+ key/value describing one or more applications or libraries that were involved
271+ during the production of the data for provenance purposes.
272+
273+ They are mostly informative and important to be complete for reproducibility
274+ purposes. Thus, the values in the object can not just be version numbers, but
275+ also be e.g. tag names, commit hashes or similar. For example, you could expose
276+ a simplified version of the Pipfile.lock (Python) or package-lock.json (NodeJS).
277+ If you need more information, you could also link to such files via the relation
278+ type processing-software.
279+ .. code-block:: python
280+ >>> proc_ext.software = {
281+ "Sentinel-1 IPF": "002.71"
282+ }
283+ """
284+ return self ._get_property (SOFTWARE_PROP , dict [str , str ])
141285
142286 @software .setter
143- def software (self : Self , v : str | None ) -> None :
144- self ._set_property (SOFTWARE_PROP , v , pop_if_none = True )
287+ def software (self : Self , v : dict [ str , str ] | None ) -> None :
288+ self ._set_property (SOFTWARE_PROP , v )
145289
146290 @classmethod
147291 def get_schema_uri (cls ) -> str :
0 commit comments