Skip to content

Commit dbdbfa9

Browse files
committed
[wip]
1 parent fbe362f commit dbdbfa9

File tree

1 file changed

+67
-87
lines changed

1 file changed

+67
-87
lines changed

pystac/extensions/classification.py

Lines changed: 67 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
)
2424
from pystac.extensions.hooks import ExtensionHooks
2525
import pystac.extensions.item_assets as item_assets
26+
from pystac.extensions.raster import RasterExtension
2627
from pystac.serialization.identify import STACJSONDescription, STACVersionID
2728
from pystac.utils import get_required, map_opt
2829

@@ -36,6 +37,7 @@
3637
CLASSES_PROP: str = PREFIX + "classes"
3738
RASTER_BANDS_PROP: str = "raster:bands"
3839

40+
3941
class Classification:
4042
properties: Dict[str, Any]
4143

@@ -48,9 +50,7 @@ def apply(
4850
name: str,
4951
description: Optional[str] = None,
5052
title: Optional[str] = None,
51-
color_hint: Optional[
52-
str
53-
] = None,
53+
color_hint: Optional[str] = None,
5454
nodata: Optional[bool] = None,
5555
) -> None:
5656
self.value = value
@@ -62,7 +62,9 @@ def apply(
6262

6363
color_hint_pattern = re.compile("^([0-9A-F]{6})$")
6464
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})$'"
6668

6769
@classmethod
6870
def create(
@@ -165,7 +167,7 @@ def apply(
165167
classes: List[Classification],
166168
roles: Optional[List[str]] = None,
167169
description: Optional[str] = None,
168-
name: Optional[str] = None
170+
name: Optional[str] = None,
169171
) -> None:
170172
self.offset = offset
171173
self.length = length
@@ -177,11 +179,9 @@ def apply(
177179
assert offset >= 0, "Non-negative offsets only"
178180
assert length >= 1, "Positive field lengths only"
179181
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"
185185

186186
@classmethod
187187
def create(
@@ -222,7 +222,10 @@ def length(self, v: int) -> None:
222222

223223
@property
224224
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+
]
226229

227230
@classes.setter
228231
def classes(self, v: List[Classification]) -> None:
@@ -262,29 +265,24 @@ def name(self, v: Optional[str]) -> None:
262265
self.properties.pop("name", None)
263266

264267
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+
)
266272

267273
def to_dict(self) -> Dict[str, Any]:
268274
return self.properties
269275

270276

271277
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"
273281

274282
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]))
281284
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]))
288286
else:
289287
return None
290288

@@ -299,7 +297,12 @@ def apply(
299297
classes: Optional[List[Classification]] = None,
300298
bitfields: Optional[List[Bitfield]] = None,
301299
) -> 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`"
303306
self.classes = classes
304307
self.bitfields = bitfields
305308

@@ -348,14 +351,17 @@ def ext(cls, obj: T, add_if_missing: bool = False) -> ClassificationExtension[T]
348351
elif isinstance(obj, pystac.Asset):
349352
cls.validate_owner_has_extension(obj, add_if_missing)
350353
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))
354354
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+
)
356361
else:
357362
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__}'"
359365
)
360366

361367
@classmethod
@@ -383,11 +389,11 @@ def _get_classes(self) -> Optional[List[Classification]]:
383389

384390
return None
385391

386-
def _get_bitfields(self) -> Optional[List[Bitfields]]:
392+
def _get_bitfields(self) -> Optional[List[Bitfield]]:
387393
bitfields = self._get_property(BITFIELDS_PROP, List[Dict[str, Any]])
388394

389395
if bitfields is not None:
390-
return [Bitfields(b) for b in bitfields]
396+
return [Bitfield(b) for b in bitfields]
391397

392398
return None
393399

@@ -403,13 +409,10 @@ class AssetClassificationExtension(ClassificationExtension[pystac.Asset]):
403409

404410
def _get_classes(self) -> Optional[List[Classification]]:
405411
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):
407413
bnds = RasterExtension.ext(self.asset).bands
408414
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)
413416
)
414417
else:
415418
return None
@@ -418,13 +421,10 @@ def _get_classes(self) -> Optional[List[Classification]]:
418421

419422
def _get_bitfields(self) -> Optional[List[Bitfield]]:
420423
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):
422425
bnds = RasterExtension.ext(self.asset).bands
423426
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)
428428
)
429429
else:
430430
return None
@@ -442,7 +442,9 @@ def __repr__(self) -> str:
442442
return f"<AssetClassificationExtension Asset href={self.asset_href}>"
443443

444444

445-
class ItemAssetsClassificationExtension(ClassificationExtension[item_assets.AssetDefinition]):
445+
class ItemAssetsClassificationExtension(
446+
ClassificationExtension[item_assets.AssetDefinition]
447+
):
446448
properties: Dict[str, Any]
447449
asset_defn: item_assets.AssetDefinition
448450

@@ -454,52 +456,30 @@ def __repr__(self) -> str:
454456
return f"<ItemAssetsClassificationExtension AssetDefinition={self.asset_defn}"
455457

456458

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+
)
465466

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))
469470

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+
)
475477

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+
)
503483

504484

505485
class ClassificationExtensionHooks(ExtensionHooks):

0 commit comments

Comments
 (0)