# SPDX-FileCopyrightText: 2023 Karlsruher Institut für Technologie
#
# SPDX-License-Identifier: CC0-1.0
import sys
from datetime import datetime
import pystac
from shapely import geometry
from .analysers.existence_validator import ExistenceValidator
from .analysers.processing import Processing
from .assets import Assets
from .extensions.datacube import Datacube
from .extensions.extra_metadata import ExtraMetadata
from .logger import Logger
[docs]
class Creator:
"""
A class to create the STAC-Catalog, STAC-Collection, and STAC-Item.
Args:
logger_properties (dict): A dictionary of logger properties.
"""
def __init__(self, logger_properties: dict):
self.logger_properties = logger_properties
[docs]
def STACCatalog(
self,
sta_link: str,
stac_id: str,
stac_title: str,
stac_description: str,
stac_dir: str,
default_catalog_name: str = "catalog.json",
stac_existance_catalog: bool = False,
):
"""
Create the STAC-Catalog.
"""
catalog: dict = dict()
if stac_description is None:
stac_description = "This is a STAC catalog created by STA2STAC."
# In the following if condition we are going to create a new STAC catalog or use the existed one.
if stac_existance_catalog is True:
if stac_dir is None:
self.logger_properties["logger_level"] = "WARNING"
self.logger_properties[
"logger_msg"
] = "You have turned on the `stac_existance`, so please provide the directory of the existed STAC catalog"
Logger(self.logger_properties)
return
else:
if (
ExistenceValidator(
stac_dir, default_catalog_name, self.logger_properties
).existance
is True
):
self.logger_properties["logger_level"] = "INFO"
self.logger_properties[
"logger_msg"
] = "The STAC catalog already exists in the directory. So, it will be used"
Logger(self.logger_properties)
id_catalog = pystac.Catalog.from_file(
stac_dir + "/" + default_catalog_name
).id
catalog[id_catalog] = pystac.Catalog.from_file(
stac_dir + "/" + default_catalog_name
)
else:
self.logger_properties["logger_level"] = "INFO"
self.logger_properties[
"logger_msg"
] = "The STAC catalog does not exist in the directory. So, a new one will be created"
Logger(self.logger_properties)
id_catalog = stac_id + " Catalog"
catalog[id_catalog] = pystac.Catalog(
id=stac_id,
title=stac_title,
description="["
+ stac_description
+ "]("
+ str(sta_link)
+ ")",
)
else:
self.logger_properties["logger_level"] = "INFO"
self.logger_properties[
"logger_msg"
] = "It creates a new catalog in the directory"
Logger(self.logger_properties)
id_catalog = stac_id + "-catalog"
catalog[id_catalog] = pystac.Catalog(
id=stac_id,
title=stac_title,
description="["
+ stac_description
+ "]("
+ str(sta_link)
+ ")",
)
return catalog[id_catalog]
[docs]
def STACCollection(
self,
stac_id: str,
stac_title: str,
stac_description: str,
harvesting_vars: dict,
stac_existance_collection: bool,
extra_metadata: dict = dict(),
asset_properties: dict = dict(),
):
"""
Create the STAC-Collection.
"""
collection: pystac.Collection = pystac.Collection(
id="stac_id",
extent=pystac.Extent(
spatial=pystac.SpatialExtent(bboxes=[0.0, 0.0]),
temporal=pystac.TemporalExtent(
intervals=[[datetime.utcnow(), datetime.utcnow()]]
),
),
description="stac_description",
)
existed_items_id_list = []
if stac_existance_collection is True:
existed_collections_id_list = []
existed_collections_id_list = [
existence_collection.id
for existence_collection in list(
harvesting_vars["catalog"].get_collections()
)
]
if (
collection is not None
and stac_id in existed_collections_id_list
):
collection = harvesting_vars["catalog"].get_child(stac_id)
existed_items_id_list = [
existed_item.id
for existed_item in list(collection.get_items())
]
else:
# Defining a None Spatial and Temporal extent for the collection
if stac_description is None:
stac_description = (
"This is a STAC collection created by STA2STAC."
)
collection = pystac.Collection(
id=stac_id,
title=stac_title,
extent=pystac.Extent(
spatial=pystac.SpatialExtent(bboxes=[0.0, 0.0]),
temporal=pystac.TemporalExtent(
intervals=[[datetime.utcnow(), datetime.utcnow()]]
),
),
description=stac_description,
)
else:
# When the STAC collection does not exist in the catalog
# Instead of None value for Spatial and Temporal extent, we define a default value
# as a list of [0.0, 0.0] for Spatial extent and [[datetime.utcnow(), datetime.utcnow()]] for Temporal extent
collection = pystac.Collection(
id=stac_id,
title=stac_title,
extent=pystac.Extent(
spatial=pystac.SpatialExtent(bboxes=[0.0, 0.0]),
temporal=pystac.TemporalExtent(
intervals=[[datetime.utcnow(), datetime.utcnow()]]
),
),
description=stac_description,
)
if asset_properties is not None and asset_properties != {}:
if asset_properties.get("collection") is not None:
Assets(logger_properties=self.logger_properties).collection(
collection=collection, asset_properties=asset_properties
)
else:
self.logger_properties["logger_level"] = "INFO"
self.logger_properties[
"logger_msg"
] = "The `collection` is not activated. So, it does not add any asset to the STAC collection."
Logger(self.logger_properties)
if extra_metadata is not None:
if extra_metadata.get("extra_metadata"):
ExtraMetadata(
logger_properties=self.logger_properties
).collection(
collection=collection, extra_metadata=extra_metadata
)
else:
self.logger_properties["logger_level"] = "INFO"
self.logger_properties[
"logger_msg"
] = "The `extra_metadata` is not activated. So, it does not add any extra metadata to the STAC collection."
Logger(self.logger_properties)
harvesting_vars["catalog"].add_child(collection)
return existed_items_id_list, collection
[docs]
def STACItem(
self,
harvesting_vars: dict,
extra_metadata: dict = dict(),
datacube_extension: bool = False,
asset_properties: dict = dict(),
) -> None:
"""
Create the STAC-Item.
"""
harvesting_vars["item_geometry"] = Processing(
logger_properties=self.logger_properties
).geometry(
harvesting_vars["item_bbox"], harvesting_vars["item_geometry"]
)
if (
harvesting_vars["item_datetime"] is not None
and harvesting_vars["item_bbox"] is not None
and harvesting_vars["item_geometry"] is not None
):
item = pystac.Item(
id=harvesting_vars["item_id"],
# geometry=geometry.mapping(harvesting_vars["item_footprint"]),
geometry=geometry.mapping(harvesting_vars["item_footprint"]),
bbox=harvesting_vars["item_bbox"],
datetime=harvesting_vars["item_datetime"][1],
properties={},
)
# TODO: Add the assets
if asset_properties is not None and asset_properties != {}:
if asset_properties.get("item") is not None:
Assets(logger_properties=self.logger_properties).item(
item=item,
asset_properties=asset_properties,
harvesting_vars=harvesting_vars,
)
else:
self.logger_properties["logger_level"] = "INFO"
self.logger_properties[
"logger_msg"
] = "The `item` is not activated. So, it does not add any asset to the STAC item."
Logger(self.logger_properties)
# TODO: Add extensions
if extra_metadata is not None:
if extra_metadata.get("extra_metadata"):
ExtraMetadata(
logger_properties=self.logger_properties
).item(
item=item,
extra_metadata=extra_metadata,
harvesting_vars=harvesting_vars,
)
else:
self.logger_properties["logger_level"] = "INFO"
self.logger_properties[
"logger_msg"
] = "The `extra_metadata` is not activated. So, it does not add any extra metadata to the STAC item."
Logger(self.logger_properties)
if datacube_extension:
Datacube(logger_properties=self.logger_properties).item(
item=item, harvesting_vars=harvesting_vars
)
harvesting_vars["collection"].add_item(item)
[docs]
def SaveCatalog(self, catalog: pystac.Catalog, stac_dir: str):
"""
Save the STAC-Catalog.
"""
try:
catalog.normalize_hrefs(stac_dir)
catalog.save(catalog_type=pystac.CatalogType.SELF_CONTAINED)
except Exception:
import traceback
print(traceback.format_exc())
ex_type, ex_value, ex_traceback = sys.exc_info()
if ex_type is not None and ex_value is not None:
self.logger_properties["logger_level"] = "ERROR"
self.logger_properties["logger_msg"] = (
"The primary cause of this error is the absence of temporal and bounding box information in the Collection. The STAC-Catalog can therefore not be generated. You can examine the arguments entered or use the `spatial_information` or `temporal_information` arguments for this purpose. %s : %s"
% (
ex_type.__name__,
ex_value,
)
)
Logger(self.logger_properties)