23
23
)
24
24
from pystac .extensions .hooks import ExtensionHooks
25
25
import pystac .extensions .item_assets as item_assets
26
+ from pystac .extensions .raster import RasterExtension
26
27
from pystac .serialization .identify import STACJSONDescription , STACVersionID
27
28
from pystac .utils import get_required , map_opt
28
29
36
37
CLASSES_PROP : str = PREFIX + "classes"
37
38
RASTER_BANDS_PROP : str = "raster:bands"
38
39
40
+
39
41
class Classification :
40
42
properties : Dict [str , Any ]
41
43
@@ -48,9 +50,7 @@ def apply(
48
50
name : str ,
49
51
description : Optional [str ] = None ,
50
52
title : Optional [str ] = None ,
51
- color_hint : Optional [
52
- str
53
- ] = None ,
53
+ color_hint : Optional [str ] = None ,
54
54
nodata : Optional [bool ] = None ,
55
55
) -> None :
56
56
self .value = value
@@ -62,7 +62,9 @@ def apply(
62
62
63
63
color_hint_pattern = re .compile ("^([0-9A-F]{6})$" )
64
64
match = color_hint_pattern .match (color_hint )
65
- assert color_hint is None or match is not None and match .group () == color_hint , "Must format color hints as '^([0-9A-F]{6})$'"
65
+ assert (
66
+ color_hint is None or match is not None and match .group () == color_hint
67
+ ), "Must format color hints as '^([0-9A-F]{6})$'"
66
68
67
69
@classmethod
68
70
def create (
@@ -165,7 +167,7 @@ def apply(
165
167
classes : List [Classification ],
166
168
roles : Optional [List [str ]] = None ,
167
169
description : Optional [str ] = None ,
168
- name : Optional [str ] = None
170
+ name : Optional [str ] = None ,
169
171
) -> None :
170
172
self .offset = offset
171
173
self .length = length
@@ -177,11 +179,9 @@ def apply(
177
179
assert offset >= 0 , "Non-negative offsets only"
178
180
assert length >= 1 , "Positive field lengths only"
179
181
assert len (classes ) > 0 , "Must specify at least one class"
180
- assert roles is None or len (roles ) > 0 , "When set, roles must contain at least one item"
181
-
182
- ## Ensure complete coverage of bit fields in classes
183
- # class_coverage = set([c.value for c in classes])
184
- # assert set(range(0, 2**length)) - class_coverage == set(), "Classes must cover the complete range of values"
182
+ assert (
183
+ roles is None or len (roles ) > 0
184
+ ), "When set, roles must contain at least one item"
185
185
186
186
@classmethod
187
187
def create (
@@ -222,7 +222,10 @@ def length(self, v: int) -> None:
222
222
223
223
@property
224
224
def classes (self ) -> List [Classification ]:
225
- return [Classification (d ) for d in get_required (self .properties .get ("classes" ), self , "classes" )]
225
+ return [
226
+ Classification (d )
227
+ for d in get_required (self .properties .get ("classes" ), self , "classes" )
228
+ ]
226
229
227
230
@classes .setter
228
231
def classes (self , v : List [Classification ]) -> None :
@@ -262,29 +265,24 @@ def name(self, v: Optional[str]) -> None:
262
265
self .properties .pop ("name" , None )
263
266
264
267
def __repr__ (self ) -> str :
265
- return f"<Bitfield offset={ self .offset } length={ self .length } classes={ self .classes } >"
268
+ return (
269
+ f"<Bitfield offset={ self .offset } length={ self .length } "
270
+ "classes={self.classes}>"
271
+ )
266
272
267
273
def to_dict (self ) -> Dict [str , Any ]:
268
274
return self .properties
269
275
270
276
271
277
def __fetch_from_dict (d : Dict [str , Any ]) -> Optional [Union [Classification , Bitfield ]]:
272
- assert not (CLASSES_PROP in d and BITFIELDS_PROP in d ), f"Cannot define both { CLASSES_PROP } and { BITFIELDS :PROP} in the same entity"
278
+ assert not (
279
+ CLASSES_PROP in d and BITFIELDS_PROP in d
280
+ ), f"Cannot define both { CLASSES_PROP } and { BITFIELDS_PROP } in the same entity"
273
281
274
282
if CLASSES_PROP in d :
275
- return list (
276
- map (
277
- lambda item : Classification (item ),
278
- d [CLASSES_PROP ]
279
- )
280
- )
283
+ return list (map (lambda item : Classification (item ), d [CLASSES_PROP ]))
281
284
elif BITFIELDS_PROP in d :
282
- return list (
283
- map (
284
- lambda item : Bitfield (item ),
285
- d [BITFIELDS_PROP ]
286
- )
287
- )
285
+ return list (map (lambda item : Bitfield (item ), d [BITFIELDS_PROP ]))
288
286
else :
289
287
return None
290
288
@@ -299,7 +297,12 @@ def apply(
299
297
classes : Optional [List [Classification ]] = None ,
300
298
bitfields : Optional [List [Bitfield ]] = None ,
301
299
) -> None :
302
- # assert classes is None and bitfields is not None or bitfields is None and classes is not None, "Must set exactly one of `classes` or `bitfields`"
300
+ assert (
301
+ classes is None
302
+ and bitfields is not None
303
+ or bitfields is None
304
+ and classes is not None
305
+ ), "Must set exactly one of `classes` or `bitfields`"
303
306
self .classes = classes
304
307
self .bitfields = bitfields
305
308
@@ -348,14 +351,17 @@ def ext(cls, obj: T, add_if_missing: bool = False) -> ClassificationExtension[T]
348
351
elif isinstance (obj , pystac .Asset ):
349
352
cls .validate_owner_has_extension (obj , add_if_missing )
350
353
return cast (ClassificationExtension [T ], AssetClassificationExtension (obj ))
351
- elif isinstance (obj , pystac .Collection ):
352
- cls .validate_has_extension (obj , add_if_missing )
353
- return cast (ClassificationExtension [T ], CollectionClassificationExtension (obj ))
354
354
elif isinstance (obj , item_assets .AssetDefinition ):
355
- return cast (ClassificationExtension [T ], ItemAssetsClassificationExtension (obj ))
355
+ cls .validate_has_extension (
356
+ cast (Union [pystac .Item , pystac .Collection ], obj .owner ), add_if_missing
357
+ )
358
+ return cast (
359
+ ClassificationExtension [T ], ItemAssetsClassificationExtension (obj )
360
+ )
356
361
else :
357
362
raise pystac .ExtensionTypeError (
358
- f"Classification extension does not apply to type '{ type (obj ).__name__ } '"
363
+ "Classification extension does not apply to type "
364
+ f"'{ type (obj ).__name__ } '"
359
365
)
360
366
361
367
@classmethod
@@ -383,11 +389,11 @@ def _get_classes(self) -> Optional[List[Classification]]:
383
389
384
390
return None
385
391
386
- def _get_bitfields (self ) -> Optional [List [Bitfields ]]:
392
+ def _get_bitfields (self ) -> Optional [List [Bitfield ]]:
387
393
bitfields = self ._get_property (BITFIELDS_PROP , List [Dict [str , Any ]])
388
394
389
395
if bitfields is not None :
390
- return [Bitfields (b ) for b in bitfields ]
396
+ return [Bitfield (b ) for b in bitfields ]
391
397
392
398
return None
393
399
@@ -403,13 +409,10 @@ class AssetClassificationExtension(ClassificationExtension[pystac.Asset]):
403
409
404
410
def _get_classes (self ) -> Optional [List [Classification ]]:
405
411
if CLASSES_PROP not in self .properties :
406
- if ( self .asset .owner and RasterExtension .has_extension (self .asset .owner ) ):
412
+ if self .asset .owner and RasterExtension .has_extension (self .asset .owner ):
407
413
bnds = RasterExtension .ext (self .asset ).bands
408
414
return list (
409
- map (
410
- lambda b : Classification (b .properties .get (CLASSES_PROP )),
411
- bnds
412
- )
415
+ map (lambda b : Classification (b .properties .get (CLASSES_PROP )), bnds )
413
416
)
414
417
else :
415
418
return None
@@ -418,13 +421,10 @@ def _get_classes(self) -> Optional[List[Classification]]:
418
421
419
422
def _get_bitfields (self ) -> Optional [List [Bitfield ]]:
420
423
if BITFIELDS_PROP not in self .properties :
421
- if ( self .asset .owner and RasterExtension .has_extension (self .asset .owner ) ):
424
+ if self .asset .owner and RasterExtension .has_extension (self .asset .owner ):
422
425
bnds = RasterExtension .ext (self .asset ).bands
423
426
return list (
424
- map (
425
- lambda b : Bitfield (b .properties .get (BITFIELDS_PROP )),
426
- bnds
427
- )
427
+ map (lambda b : Bitfield (b .properties .get (BITFIELDS_PROP )), bnds )
428
428
)
429
429
else :
430
430
return None
@@ -442,7 +442,9 @@ def __repr__(self) -> str:
442
442
return f"<AssetClassificationExtension Asset href={ self .asset_href } >"
443
443
444
444
445
- class ItemAssetsClassificationExtension (ClassificationExtension [item_assets .AssetDefinition ]):
445
+ class ItemAssetsClassificationExtension (
446
+ ClassificationExtension [item_assets .AssetDefinition ]
447
+ ):
446
448
properties : Dict [str , Any ]
447
449
asset_defn : item_assets .AssetDefinition
448
450
@@ -454,52 +456,30 @@ def __repr__(self) -> str:
454
456
return f"<ItemAssetsClassificationExtension AssetDefinition={ self .asset_defn } "
455
457
456
458
457
- class CollectionClassificationExtension (ClassificationExtension [pystac .Collection ]):
458
- """A concrete implementation of :class:`ClassificationExtension` on a
459
- :class:`~pystac.Collection` that extends the properties of a Collection to
460
- include properties defined in the :stac_ext:`Classification Extension
461
- <classification>`.
462
- """
463
- collection : pystac .Collection
464
- properties : Dict [str , Any ]
459
+ class SummariesClassificationExtension (SummariesExtension ):
460
+ @property
461
+ def classes (self ) -> Optional [List [Classification ]]:
462
+ return map_opt (
463
+ lambda classes : [Classification (c ) for c in classes ],
464
+ self .summaries .get_list (CLASSES_PROP ),
465
+ )
465
466
466
- def __init__ ( self , collection : pystac . Collection ):
467
- self . collection = collection
468
- self .properties = collection . assets
467
+ @ classes . setter
468
+ def classes ( self , v : Optional [ List [ Classification ]]) -> None :
469
+ self ._set_summary ( CLASSES_PROP , map_opt ( lambda x : [ c . to_dict () for c in x ], v ))
469
470
470
- def _get_classes (self ) -> Optional [List [Optional [Classification ]]]:
471
- classes = __fetch_from_dict (self .properties )
472
- if classes is None and ItemAssetsExtension .has_extension (self .collection ):
473
- classes = __fetch_from_dict (ItemAssetsExtension .ext (self .collection ).item_assets )
474
- return list (filter (lambda x : isinstance (x , Classification ), classes ))
471
+ @property
472
+ def bitfields (self ) -> Optional [List [Bitfield ]]:
473
+ return map_opt (
474
+ lambda bitfields : [Bitfield (b ) for b in bitfields ],
475
+ self .summaries .get_list (BITFIELDS_PROP ),
476
+ )
475
477
476
- def _get_bitfields (self ) -> Optional [List [Bitfield ]]:
477
- pass
478
-
479
- # class SummariesClassificationExtension(SummariesExtension):
480
- # @property
481
- # def classes(self) -> Optional[List[Classification]]:
482
- # return map_opt(
483
- # lambda classes: [Classification(c) for c in classes],
484
- # self.summaries.get_list(CLASSES_PROP),
485
- # )
486
-
487
- # @classes.setter
488
- # def classes(self, v: Optional[List[Classification]]) -> None:
489
- # self._set_summary(CLASSES_PROP, map_opt(lambda x: [c.to_dict() for c in x], v))
490
-
491
- # @property
492
- # def bitfields(self) -> Optional[List[Bitfield]]:
493
- # return map_opt(
494
- # lambda bitfields: [Bitfield(b) for b in bitfields],
495
- # self.summaries.get_list(BITFIELDS_PROP),
496
- # )
497
-
498
- # @bitfields.setter
499
- # def bitfields(self, v: Optional[List[Bitfield]]) -> None:
500
- # self._set_summary(
501
- # BITFIELDS_PROP, map_opt(lambda x: [b.to_dict() for b in x], v)
502
- # )
478
+ @bitfields .setter
479
+ def bitfields (self , v : Optional [List [Bitfield ]]) -> None :
480
+ self ._set_summary (
481
+ BITFIELDS_PROP , map_opt (lambda x : [b .to_dict () for b in x ], v )
482
+ )
503
483
504
484
505
485
class ClassificationExtensionHooks (ExtensionHooks ):
0 commit comments