@@ -46,28 +46,41 @@ class SpatialExtent:
46
46
array must be 2*n where n is the number of dimensions. For example, a
47
47
2D Collection with only one bbox would be [[xmin, ymin, xmax, ymax]]
48
48
49
- Attributes:
50
- bboxes : A list of bboxes that represent the spatial
51
- extent of the collection. Each bbox can be 2D or 3D. The length of the bbox
52
- array must be 2*n where n is the number of dimensions. For example, a
53
- 2D Collection with only one bbox would be [[xmin, ymin, xmax, ymax]]
49
+ extra_fields : Dictionary containing additional top-level fields defined on the
50
+ Spatial Extent object.
54
51
"""
55
52
56
- def __init__ (self , bboxes : Union [List [List [float ]], List [float ]]) -> None :
53
+ bboxes : List [List [float ]]
54
+ """A list of bboxes that represent the spatial
55
+ extent of the collection. Each bbox can be 2D or 3D. The length of the bbox
56
+ array must be 2*n where n is the number of dimensions. For example, a
57
+ 2D Collection with only one bbox would be [[xmin, ymin, xmax, ymax]]"""
58
+
59
+ extra_fields : Dict [str , Any ]
60
+ """Dictionary containing additional top-level fields defined on the Spatial
61
+ Extent object."""
62
+
63
+ def __init__ (
64
+ self ,
65
+ bboxes : Union [List [List [float ]], List [float ]],
66
+ extra_fields : Optional [Dict [str , Any ]] = None ,
67
+ ) -> None :
57
68
# A common mistake is to pass in a single bbox instead of a list of bboxes.
58
69
# Account for this by transforming the input in that case.
59
70
if isinstance (bboxes , list ) and isinstance (bboxes [0 ], float ):
60
71
self .bboxes : List [List [float ]] = [cast (List [float ], bboxes )]
61
72
else :
62
73
self .bboxes = cast (List [List [float ]], bboxes )
63
74
75
+ self .extra_fields = extra_fields or {}
76
+
64
77
def to_dict (self ) -> Dict [str , Any ]:
65
78
"""Generate a dictionary representing the JSON of this SpatialExtent.
66
79
67
80
Returns:
68
81
dict: A serialization of the SpatialExtent that can be written out as JSON.
69
82
"""
70
- d = {"bbox" : self .bboxes }
83
+ d = {"bbox" : self .bboxes , ** self . extra_fields }
71
84
return d
72
85
73
86
def clone (self ) -> "SpatialExtent" :
@@ -76,26 +89,34 @@ def clone(self) -> "SpatialExtent":
76
89
Returns:
77
90
SpatialExtent: The clone of this object.
78
91
"""
79
- return SpatialExtent (deepcopy (self .bboxes ))
92
+ return SpatialExtent (
93
+ bboxes = deepcopy (self .bboxes ), extra_fields = deepcopy (self .extra_fields )
94
+ )
80
95
81
96
@staticmethod
82
97
def from_dict (d : Dict [str , Any ]) -> "SpatialExtent" :
83
- """Constructs an SpatialExtent from a dict.
98
+ """Constructs a SpatialExtent from a dict.
84
99
85
100
Returns:
86
101
SpatialExtent: The SpatialExtent deserialized from the JSON dict.
87
102
"""
88
- return SpatialExtent (bboxes = d ["bbox" ])
103
+ return SpatialExtent (
104
+ bboxes = d ["bbox" ], extra_fields = {k : v for k , v in d .items () if k != "bbox" }
105
+ )
89
106
90
107
@staticmethod
91
- def from_coordinates (coordinates : List [Any ]) -> "SpatialExtent" :
108
+ def from_coordinates (
109
+ coordinates : List [Any ], extra_fields : Optional [Dict [str , Any ]] = None
110
+ ) -> "SpatialExtent" :
92
111
"""Constructs a SpatialExtent from a set of coordinates.
93
112
94
113
This method will only produce a single bbox that covers all points
95
114
in the coordinate set.
96
115
97
116
Args:
98
117
coordinates : Coordinates to derive the bbox from.
118
+ extra_fields : Dictionary containing additional top-level fields defined on
119
+ the Spatial Extent object.
99
120
100
121
Returns:
101
122
SpatialExtent: A SpatialExtent with a single bbox that covers the
@@ -133,31 +154,40 @@ def process_coords(
133
154
f"Could not determine bounds from coordinate sequence { coordinates } "
134
155
)
135
156
136
- return SpatialExtent ([[xmin , ymin , xmax , ymax ]])
157
+ return SpatialExtent (
158
+ bboxes = [[xmin , ymin , xmax , ymax ]], extra_fields = extra_fields
159
+ )
137
160
138
161
139
162
class TemporalExtent :
140
163
"""Describes the temporal extent of a Collection.
141
164
142
165
Args:
143
166
intervals : A list of two datetimes wrapped in a list,
144
- representing the temporal extent of a Collection. Open date ranges are supported
145
- by setting either the start (the first element of the interval) or the end (the
146
- second element of the interval) to None.
147
-
148
-
149
- Attributes:
150
- intervals : A list of two datetimes wrapped in a list,
151
- representing the temporal extent of a Collection. Open date ranges are
152
- represented by either the start (the first element of the interval) or the
153
- end (the second element of the interval) being None.
167
+ representing the temporal extent of a Collection. Open date ranges are
168
+ supported by setting either the start (the first element of the interval)
169
+ or the end (the second element of the interval) to None.
154
170
171
+ extra_fields : Dictionary containing additional top-level fields defined on the
172
+ Temporal Extent object.
155
173
Note:
156
174
Datetimes are required to be in UTC.
157
175
"""
158
176
177
+ intervals : List [List [Optional [Datetime ]]]
178
+ """A list of two datetimes wrapped in a list,
179
+ representing the temporal extent of a Collection. Open date ranges are
180
+ represented by either the start (the first element of the interval) or the
181
+ end (the second element of the interval) being None."""
182
+
183
+ extra_fields : Dict [str , Any ]
184
+ """Dictionary containing additional top-level fields defined on the Temporal
185
+ Extent object."""
186
+
159
187
def __init__ (
160
- self , intervals : Union [List [List [Optional [Datetime ]]], List [Optional [Datetime ]]]
188
+ self ,
189
+ intervals : Union [List [List [Optional [Datetime ]]], List [Optional [Datetime ]]],
190
+ extra_fields : Optional [Dict [str , Any ]] = None ,
161
191
):
162
192
# A common mistake is to pass in a single interval instead of a
163
193
# list of intervals. Account for this by transforming the input
@@ -167,6 +197,8 @@ def __init__(
167
197
else :
168
198
self .intervals = cast (List [List [Optional [Datetime ]]], intervals )
169
199
200
+ self .extra_fields = extra_fields or {}
201
+
170
202
def to_dict (self ) -> Dict [str , Any ]:
171
203
"""Generate a dictionary representing the JSON of this TemporalExtent.
172
204
@@ -186,7 +218,7 @@ def to_dict(self) -> Dict[str, Any]:
186
218
187
219
encoded_intervals .append ([start , end ])
188
220
189
- d = {"interval" : encoded_intervals }
221
+ d = {"interval" : encoded_intervals , ** self . extra_fields }
190
222
return d
191
223
192
224
def clone (self ) -> "TemporalExtent" :
@@ -195,7 +227,9 @@ def clone(self) -> "TemporalExtent":
195
227
Returns:
196
228
TemporalExtent: The clone of this object.
197
229
"""
198
- return TemporalExtent (intervals = deepcopy (self .intervals ))
230
+ return TemporalExtent (
231
+ intervals = deepcopy (self .intervals ), extra_fields = deepcopy (self .extra_fields )
232
+ )
199
233
200
234
@staticmethod
201
235
def from_dict (d : Dict [str , Any ]) -> "TemporalExtent" :
@@ -215,7 +249,10 @@ def from_dict(d: Dict[str, Any]) -> "TemporalExtent":
215
249
end = dateutil .parser .parse (i [1 ])
216
250
parsed_intervals .append ([start , end ])
217
251
218
- return TemporalExtent (intervals = parsed_intervals )
252
+ return TemporalExtent (
253
+ intervals = parsed_intervals ,
254
+ extra_fields = {k : v for k , v in d .items () if k != "interval" },
255
+ )
219
256
220
257
@staticmethod
221
258
def from_now () -> "TemporalExtent" :
@@ -236,23 +273,41 @@ class Extent:
236
273
Args:
237
274
spatial : Potential spatial extent covered by the collection.
238
275
temporal : Potential temporal extent covered by the collection.
239
-
240
- Attributes:
241
- spatial : Potential spatial extent covered by the collection.
242
- temporal : Potential temporal extent covered by the collection.
276
+ extra_fields : Dictionary containing additional top-level fields defined on the
277
+ Extent object.
243
278
"""
244
279
245
- def __init__ (self , spatial : SpatialExtent , temporal : TemporalExtent ):
280
+ spatial : SpatialExtent
281
+ """Potential spatial extent covered by the collection."""
282
+
283
+ temporal : TemporalExtent
284
+ """Potential temporal extent covered by the collection."""
285
+
286
+ extra_fields : Dict [str , Any ]
287
+ """Dictionary containing additional top-level fields defined on the Extent
288
+ object."""
289
+
290
+ def __init__ (
291
+ self ,
292
+ spatial : SpatialExtent ,
293
+ temporal : TemporalExtent ,
294
+ extra_fields : Optional [Dict [str , Any ]] = None ,
295
+ ):
246
296
self .spatial = spatial
247
297
self .temporal = temporal
298
+ self .extra_fields = extra_fields or {}
248
299
249
300
def to_dict (self ) -> Dict [str , Any ]:
250
301
"""Generate a dictionary representing the JSON of this Extent.
251
302
252
303
Returns:
253
304
dict: A serialization of the Extent that can be written out as JSON.
254
305
"""
255
- d = {"spatial" : self .spatial .to_dict (), "temporal" : self .temporal .to_dict ()}
306
+ d = {
307
+ "spatial" : self .spatial .to_dict (),
308
+ "temporal" : self .temporal .to_dict (),
309
+ ** self .extra_fields ,
310
+ }
256
311
257
312
return d
258
313
@@ -262,7 +317,11 @@ def clone(self) -> "Extent":
262
317
Returns:
263
318
Extent: The clone of this extent.
264
319
"""
265
- return Extent (spatial = copy (self .spatial ), temporal = copy (self .temporal ))
320
+ return Extent (
321
+ spatial = copy (self .spatial ),
322
+ temporal = copy (self .temporal ),
323
+ extra_fields = deepcopy (self .extra_fields ),
324
+ )
266
325
267
326
@staticmethod
268
327
def from_dict (d : Dict [str , Any ]) -> "Extent" :
@@ -287,16 +346,23 @@ def from_dict(d: Dict[str, Any]) -> "Extent":
287
346
temporal_extent_dict = temporal_extent
288
347
289
348
return Extent (
290
- SpatialExtent .from_dict (spatial_extent_dict ),
291
- TemporalExtent .from_dict (temporal_extent_dict ),
349
+ spatial = SpatialExtent .from_dict (spatial_extent_dict ),
350
+ temporal = TemporalExtent .from_dict (temporal_extent_dict ),
351
+ extra_fields = {
352
+ k : v for k , v in d .items () if k not in {"spatial" , "temporal" }
353
+ },
292
354
)
293
355
294
356
@staticmethod
295
- def from_items (items : Iterable ["Item_Type" ]) -> "Extent" :
357
+ def from_items (
358
+ items : Iterable ["Item_Type" ], extra_fields : Optional [Dict [str , Any ]] = None
359
+ ) -> "Extent" :
296
360
"""Create an Extent based on the datetimes and bboxes of a list of items.
297
361
298
362
Args:
299
363
items : A list of items to derive the extent from.
364
+ extra_fields : Optional dictionary containing additional top-level fields
365
+ defined on the Extent object.
300
366
301
367
Returns:
302
368
Extent: An Extent that spatially and temporally covers all of the
@@ -354,7 +420,7 @@ def from_items(items: Iterable["Item_Type"]) -> "Extent":
354
420
)
355
421
temporal = TemporalExtent ([[start_timestamp , end_timestamp ]])
356
422
357
- return Extent (spatial , temporal )
423
+ return Extent (spatial = spatial , temporal = temporal , extra_fields = extra_fields )
358
424
359
425
360
426
class Provider :
0 commit comments