This notebook derives spatial and raster metadata from the basal-melt COG and uses it to build a STAC Item. The same pattern applies to cloud-hosted COGs: derive machine-checkable fields from the asset wherever possible, then add human and provenance metadata explicitly.
from pathlib import Path
def find_repo_root(start: Path = Path.cwd()) -> Path:
"""Find the repository root when the kernel starts in a subfolder."""
start = start.resolve()
for path in (start, *start.parents):
if (path / ".git").exists() or (path / "downloaded_data").exists():
return path
return start
REPO_ROOT = find_repo_root()
DATA_DIR = REPO_ROOT / "downloaded_data"
DATA_DIR.mkdir(exist_ok=True)
print(f"Repository root: {REPO_ROOT}")
print(f"Data directory: {DATA_DIR}")
import datetime as dt
import json
import pystac
import rasterio
from pystac.extensions.projection import ProjectionExtension
from pystac.extensions.raster import RasterBand, RasterExtension
from rasterio.warp import transform_bounds
from shapely.geometry import box, mapping
COG_PATH = DATA_DIR / "basal_melt_map_racmo_firn_air_corrected_cog.tif"
PUBLIC_HREF = "https://s3.waw4-1.cloudferro.com/EarthCODE/OSCAssets/polar_cube_datasets/basal_melt/basal_melt_map_racmo_firn_air_corrected.tif"
if not COG_PATH.exists():
raise FileNotFoundError("Create the COG with 1_cog_from_tiff.ipynb before running this notebook.")
Repository root: /home/krasen/hackathon-repo
Data directory: /home/krasen/hackathon-repo/downloaded_data
Derive Spatial And Raster Fields¶
with rasterio.open(COG_PATH) as src:
bounds_native = src.bounds
bbox_wgs84 = transform_bounds(src.crs, "EPSG:4326", *bounds_native, densify_pts=21)
geometry_wgs84 = mapping(box(*bbox_wgs84))
epsg = src.crs.to_epsg()
transform = [src.transform.a, src.transform.b, src.transform.c, src.transform.d, src.transform.e, src.transform.f]
shape = [src.height, src.width]
data_type = src.dtypes[0]
nodata = src.nodata
resolution = abs(src.res[0])
print(f"EPSG: {epsg}")
print(f"WGS84 bbox: {bbox_wgs84}")
print(f"Shape: {shape}")
print(f"Data type: {data_type}, nodata: {nodata}")
EPSG: 3031
WGS84 bbox: (-180.0, -90.0, 180.0, -50.68637942059157)
Shape: [6435, 6120]
Data type: float32, nodata: -3.4028234663852886e+38
Create And Validate The STAC Item¶
item = pystac.Item(
id="antarctic-ice-shelf-basal-melt-cog",
geometry=geometry_wgs84,
bbox=list(bbox_wgs84),
datetime=dt.datetime(2021, 1, 1, tzinfo=dt.timezone.utc),
properties={
"title": "Antarctic ice shelf basal melt rate COG",
"description": "Cloud Optimized GeoTIFF example for Antarctic ice shelf basal melt rates.",
"license": "cc-by-4.0",
"start_datetime": "1997-01-01T00:00:00Z",
"end_datetime": "2021-12-31T23:59:59Z",
},
)
ProjectionExtension.add_to(item)
proj = ProjectionExtension.ext(item)
proj.epsg = epsg
proj.shape = shape
proj.bbox = list(bounds_native)
proj.transform = transform
asset = pystac.Asset(
href=PUBLIC_HREF,
media_type=pystac.MediaType.COG,
roles=["data"],
title="Basal melt rate COG",
)
RasterExtension.add_to(item)
item.add_asset("basal_melt_rate", asset)
raster = RasterExtension.ext(item.assets["basal_melt_rate"])
raster.bands = [
RasterBand.create(
nodata=nodata,
data_type=data_type,
spatial_resolution=resolution,
unit="m yr-1",
)
]
item.validate()
item.to_dict()
{'type': 'Feature',
'stac_version': '1.1.0',
'stac_extensions': ['https://stac-extensions.github.io/projection/v2.0.0/schema.json',
'https://stac-extensions.github.io/raster/v1.1.0/schema.json'],
'id': 'antarctic-ice-shelf-basal-melt-cog',
'geometry': {'type': 'Polygon',
'coordinates': (((180.0, -90.0),
(180.0, -50.68637942059157),
(-180.0, -50.68637942059157),
(-180.0, -90.0),
(180.0, -90.0)),)},
'bbox': [-180.0, -90.0, 180.0, -50.68637942059157],
'properties': {'title': 'Antarctic ice shelf basal melt rate COG',
'description': 'Cloud Optimized GeoTIFF example for Antarctic ice shelf basal melt rates.',
'license': 'cc-by-4.0',
'start_datetime': '1997-01-01T00:00:00Z',
'end_datetime': '2021-12-31T23:59:59Z',
'proj:code': 'EPSG:3031',
'proj:shape': [6435, 6120],
'proj:bbox': [-3060277.25, -3217740.25, 3059722.75, 3217259.75],
'proj:transform': [1000.0, 0.0, -3060277.25, 0.0, -1000.0, 3217259.75],
'datetime': '2021-01-01T00:00:00Z'},
'links': [],
'assets': {'basal_melt_rate': {'href': 'https://s3.waw4-1.cloudferro.com/EarthCODE/OSCAssets/polar_cube_datasets/basal_melt/basal_melt_map_racmo_firn_air_corrected.tif',
'type': 'image/tiff; application=geotiff; profile=cloud-optimized',
'title': 'Basal melt rate COG',
'raster:bands': [{'nodata': -3.4028234663852886e+38,
'data_type': 'float32',
'spatial_resolution': 1000.0,
'unit': 'm yr-1'}],
'roles': ['data']}}}