Skip to content

Commit

Permalink
get the cordex extension to work with the stac-populator cli.
Browse files Browse the repository at this point in the history
  • Loading branch information
huard committed Oct 11, 2024
1 parent 0212008 commit 04d94df
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 24 deletions.
60 changes: 39 additions & 21 deletions STACpopulator/extensions/base.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from datetime import datetime
import json
import jsonschema
import logging
from typing import Any, Dict, Generic, TypeVar, Union, cast, Optional
from pydantic import (BaseModel, create_model, Field, FilePath, field_validator, model_validator, HttpUrl, ConfigDict,
PrivateAttr)
Expand Down Expand Up @@ -28,7 +29,7 @@

T = TypeVar("T", pystac.Collection, pystac.Item, pystac.Asset, item_assets.AssetDefinition)


LOGGER = logging.getLogger(__name__)
"""
# Context
Expand Down Expand Up @@ -150,7 +151,7 @@ def stac_item(self) -> "pystac.Item":
datetime=None,
)

# self.metadata_extension(item)
self.metadata_extension(item)
self.datacube_extension(item)
self.thredds_extension(item)

Expand All @@ -164,46 +165,43 @@ def stac_item(self) -> "pystac.Item":


def metadata_extension(self, item):
"""Add extension for the properties of the dataset to the STAC item.
The extension class is created dynamically from the properties.
"""
ExtSubCls = metacls_extension(self.properties._prefix, schema_uri=str(self.properties._schema_uri))
item_ext = ExtSubCls.ext(item, add_if_missing=False)
item_ext.apply(self.properties.model_dump(mode="json", by_alias=True))
return item

def datacube_extension(self, item):
"""Add datacube extension to the STAC item."""
dc_ext = DatacubeExtension.ext(item, add_if_missing=True)
dc_ext.apply(dimensions=self.datacube.dimensions, variables=self.datacube.variables)

def thredds_extension(self, item):
"""Add THREDDS extension to the STAC item."""
thredds_ext = THREDDSExtension.ext(item, add_if_missing=False)
thredds_ext.apply(self.thredds.services, self.thredds.links)


def metacls_extension(name, schema_uri):
cls_name = f"{name.upper()}Extension"

bases = (MetaExtension,
Generic[T],
PropertiesExtension,
ExtensionManagementMixin[Union[pystac.Asset, pystac.Item, pystac.Collection]]
)

attrs = {"schema_name": name, "schema_uri": schema_uri}
return types.new_class(name=cls_name, bases=bases, kwds=None, exec_body=lambda ns: ns.update(attrs))
"""Create an extension class dynamically from the properties."""
cls_name = f"{name.upper()}Extension"

bases = (MetaExtension,
Generic[T],
PropertiesExtension,
ExtensionManagementMixin[Union[pystac.Asset, pystac.Item, pystac.Collection]]
)

def extend_type(stac, cls, ext):
cls_name = f"{stac.__name__ }{ext.__name__}"
return types.new_class(cls_name, (cls, ext), {}, lambda ns: ns)
attrs = {"name": name, "schema_uri": schema_uri}
return types.new_class(name=cls_name, bases=bases, kwds=None, exec_body=lambda ns: ns.update(attrs))


class MetaExtension:
schema_name: str
name: str
schema_uri: str

@property
def name(self) -> str:
return self.schema_name

def apply(self, properties: dict[str, Any]) -> None:
"""Applies CMIP6 Extension properties to the extended
:class:`~pystac.Item` or :class:`~pystac.Asset`.
Expand Down Expand Up @@ -240,13 +238,31 @@ def ext(cls, obj: T, add_if_missing: bool = False) -> "Extension[T]":

for key, meta in cls_map.items():
if isinstance(obj, key):
cls.ensure_has_extension(obj, add_if_missing)
# cls.ensure_has_extension(obj, add_if_missing)
kls = extend_type(key, meta, cls[key])
return cast(cls[T], kls(obj))
else:
raise pystac.ExtensionTypeError(cls._ext_error_message(obj))


def extend_type(stac, cls, ext):
"""Create an extension subclass for different STAC objects.
Note: This is super confusing... we should come up with some better nomenclature.
Parameters
----------
stac: pystac.Item, pystac.Asset, pystac.Collection
The STAC object.
cls: MetaItemExtension
The generic extension class for the STAC object.
ext: MetaExtension[T]
The meta extension class.
"""
cls_name = f"{stac.__name__ }{ext.__name__}"
return types.new_class(cls_name, (cls, ext), {}, lambda ns: ns)


class MetaItemExtension:
"""A concrete implementation of :class:`CMIP6Extension` on an :class:`~pystac.Item`
that extends the properties of the Item to include properties defined in the
Expand Down Expand Up @@ -284,6 +300,8 @@ def __repr__(self) -> str:
return f"<{self.__class__.__name__} Item id={self.item.id}>"


# TODO: Add the other STAC item meta extensions

def schema_properties(schema: dict) -> list[str]:
"""Return the list of properties described by schema."""
out = []
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@
from typing import Any
from STACpopulator.populator_base import THREDDSRunner
from STACpopulator.populator_base import STACpopulatorBase
from STACpopulator.extensions.cordex6 import THREDDSCatalogDataModel
from STACpopulator.extensions.cordex6 import Cordex6DataModel


class CORDEX_STAC_Populator(STACpopulatorBase):
data_model = THREDDSCatalogDataModel
data_model = Cordex6DataModel
item_geometry_model = None # Unnecessary, but kept for consistency

def create_stac_item(self, item_name: str, item_data: dict[str, Any]) -> dict[str, Any]:
Expand Down
2 changes: 1 addition & 1 deletion STACpopulator/populator_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ def ingest(self) -> None:

counter += 1
LOGGER.info(f"Processed {counter} data items. {failures} failures")

break


class THREDDSRunner:
Expand Down

0 comments on commit 04d94df

Please sign in to comment.