Condividi tramite


Creare un elemento SpatioTemporal Asset Catalog (STAC)

Informazioni su come creare un elemento SpatioTemporal Asset Catalog (STAC) per un asset di dati geospaziali raster. Ogni asset di dati geospaziali caricato in un GeoCatalog di Microsoft Planetary Computer pro deve avere un elemento conforme a STAC associato.

In questa guida:

  • Installare le librerie Python necessarie usando PIP.
  • Visualizzare ed esaminare i dati GOES-18 usando il codice Python fornito.
  • Estrarre metadati dal nome del file usando espressioni regolari.
  • Creare Item STAC dai file GeoTIFF, Cloud-Optimized, usando rio-stac.
  • Migliorare l'elemento STAC con i metadati estratti dal nome del file.
  • Aggiungere l'elemento STAC a un insieme STAC padre.
  • Convalidare e salvare il catalogo, la raccolta e gli elementi STAC.
  • Aggiungere gli elementi STAC a Microsoft Planetary Computer Pro.

Questa esercitazione mostra e spiega le funzionalità tramite frammenti di codice, per un'esperienza in stile notebook interattivo, scarica questa esercitazione come notebook di Jupyter.

Prerequisiti

Per completare questo avvio rapido, avrai bisogno di:

Funzionalità principali di STAC

SpatioTemporal Asset Catalog (STAC) è uno standard aperto per la strutturazione e la condivisione di dati geospaziali. Fornisce un linguaggio comune e un formato per descrivere gli asset geospaziali, semplificando l'individuazione, l'accesso e l'uso di queste risorse in piattaforme e applicazioni diverse. Di seguito sono riportate le funzionalità principali dello standard STAC:

  • Interoperabilità: lo schema basato su JSON standardizzato garantisce una facile integrazione e comprensione tra strumenti e sistemi.
  • Estendibilità: campi principali flessibili con estensioni personalizzate per esigenze specifiche.
  • Esplorabilità: Una struttura coerente migliora la ricerca e la scoperta di asset geospaziali.
  • Accessibilità: i collegamenti diretti agli asset di dati facilitano il recupero e l'integrazione senza problemi.
  • Automazione: consente l'elaborazione e l'analisi automatizzate con metadati standardizzati.

Vantaggi di STAC per la standardizzazione dei metadati

  • Coerenza tra tipi di dati: framework unificato per diversi tipi di dati geospaziali.
  • Collaborazione avanzata: semplifica la condivisione e la collaborazione con un formato di metadati comune.
  • Integrazione dei dati migliorata: facilita l'integrazione dei set di dati da più origini.
  • Gestione dei dati semplificata: gli strumenti automatizzati riducono il lavoro manuale nella gestione dei metadati.
  • Future Proofing: la natura estendibile si adatta a nuovi tipi di dati e tecnologie.

Microsoft Planetary Computer Pro (MC Pro) usa STAC come standard di indicizzazione principale per garantire l'interoperabilità tra set di dati. Questa esercitazione illustra agli utenti come creare elementi STAC da zero usando librerie open source comuni.

La specifica dell'elemento STAC descrive in dettaglio come vengono costruiti gli elementi STAC e i metadati minimi necessari che devono essere popolati. STAC è uno standard flessibile, che consente agli utenti di decidere quali dati includere come parte dei metadati. Metadati che possono essere usati per popolare un elemento STAC potrebbero essere inclusi nell'asset di dati, in un file sidecar (.XML, .JSON, .TXT e così via) o anche in posizioni come il nome del file. Gli utenti dovrebbero considerare i tipi di metadati che potrebbero voler cercare e ordinare in futuro quando decidono quali metadati includere.

Questa esercitazione usa i dati di immagini geoTIFF (COG) ottimizzati per cloud dal set di dati National Oceanic and Atmospheric Agency (NOAA) Geostationary Operational Environmental Satellite R (GOES-R) satellite Land Surface Temperature (LST). Questi dati sono inclusi nel computer planetaria aperto come file COG, ma non dispone di metadati STAC. I satelliti GOES producono molti prodotti dati. Ulteriori dettagli possono essere trovati nella Definizione del Prodotto e Guida per gli Utenti della SerieGOES-R.

Il processo usato in questa esercitazione può essere generalizzato per qualsiasi tipo di dati in cui i campi dei metadati chiave vengono enumerati usando diverse librerie open source.

Installare librerie Python

Per iniziare, si installano le librerie Python necessarie usando PIP:

  • rasterio: questo pacchetto viene usato per leggere e scrivere dati raster geospaziali. Fornisce strumenti per l'uso di formati di dati raster, ad esempio GeoTIFF.

  • pystac: questo pacchetto viene usato per l'uso dello standard STAC. Fornisce strumenti per la creazione, la lettura e la modifica dei metadati STAC.

  • rio-stac: Questo pacchetto integra rasterio con pystac per creare elementi STAC dai dati raster. Semplifica il processo di generazione di metadati STAC per set di dati raster.

  • matplotlib: questa libreria viene usata per la creazione di visualizzazioni e tracciati. Nel contesto dei dati geospaziali consente di eseguire il rendering e la visualizzazione di immagini raster, consentendo agli utenti di esaminare visivamente i dati prima di creare metadati STAC.

  • azure-storage-blob: La libreria client di archiviazione di Azure fornisce l'accesso ai servizi di archiviazione Blob di Azure. Consente l'interazione diretta con i dati geospaziali archiviati nel cloud, consentendo agli utenti di leggere e usare i file archiviati nei contenitori BLOB di Azure senza prima scaricarli.

!pip install rasterio pystac rio_stac matplotlib azure-storage-blob 

Nella sezione successiva del codice vengono visualizzati alcuni dati GOES-18 dall'open Planetary Computer. Selezioniamo il set di dati GOES-R Advanced Baseline Imager Level 2 Land Surface Temperature - CONUS e un file arbitrario dal giorno 208 del 2023.

# Import Necessary Libraries 
import requests
from azure.storage.blob import ContainerClient
import matplotlib.pyplot as plt
from rasterio.io import MemoryFile
import os
from urllib.parse import urlparse

# Function to get the SAS token
def get_sas_token(endpoint):
    response = requests.get(endpoint)
    if response.status_code == 200:
        data = response.json()
        return data.get("token")
    else:
        raise Exception(f"Failed to get SAS token: {response.status_code} - {response.text}")

# Define Azure Blob Storage parameters
storage_account_name = "goeseuwest"
container_name = "noaa-goes-cogs"
blob_domain = f"https://{storage_account_name}.blob.core.windows.net"
sas_endpoint = f"https://planetarycomputer.microsoft.com/api/sas/v1/token/{storage_account_name}/{container_name}/"


# Get the SAS token
sas_token = get_sas_token(sas_endpoint)

# Construct the container URL with the SAS token
container_url = f"{blob_domain}/{container_name}?{sas_token}"

# Create a ContainerClient
container_client = ContainerClient.from_container_url(container_url)

# Define data you want, this can be changed for other datasets that are in storage in the open Planetary Computer
satellite = "goes-18"      # The specific GOES satellite (GOES-18, also known as GOES-West)
product = "ABI-L2-LSTC"    # The data product type (Advanced Baseline Imager Level 2 Land Surface Temperature - CONUS)
year = "2023"              # The year the data was collected
day_of_year = "208"        # The day of year (DOY) - day 208 corresponds to July 27, 2023

# Construct the directory path
directory_path = f"{satellite}/{product}/{year}/{day_of_year}/"

# Get just the first blob by using next() and limiting the iterator

first_blob = next(container_client.list_blobs(name_starts_with=directory_path))

# Function to read and display a .tif file from a URL
def display_tif_from_url(url, title):
    response = requests.get(url, stream=True)
    if response.status_code == 200:
        with MemoryFile(response.content) as memfile:
            with memfile.open() as dataset:
                plt.figure(figsize=(10, 10))
                plt.imshow(dataset.read(1), cmap='gray')
                plt.title(title)
                plt.colorbar()
                plt.show()
    else:
        raise Exception(f"Failed to read .tif file: {response.status_code} - {response.text}")

# Create the URL for the blob using the container_url components
file_url = f"{blob_domain}/{container_name}/{first_blob.name}?{sas_token}"

# Extract the filename safely from the URL without the SAS token
parsed_url = urlparse(file_url)
path = parsed_url.path  # Gets just the path portion of the URL
filename = os.path.basename(path)  # Get just the filename part

# Remove .tif extension if present
file_name = filename.replace('.tif', '')

print(f"Extracted File Name: {file_name}")

display_tif_from_url(file_url, file_name)

Visualizzazione in scala di grigi dei dati raster geospaziali da un satellite GOES-18, che mostra i modelli di temperatura della superficie terrestre in un'area.

Esaminando i dati e il nome del file, è già possibile visualizzare le parti chiave di metadati necessarie per compilare l'elemento STAC. Il nome del file contiene informazioni su quale satellite ha acquisito i dati e quando è stato acquisito.

Per l'esempio, il nome del file è OR_ABI-L2-LSTC-M6_G18_s20232080101177_e20232080103550_c20232080104570_DQF, in base alla guida del prodotto, questo significa:

Componenti dettagliati

Campo Descrizione Scopo
O Ambiente del sistema operativo Specifica l'ambiente di sistema in cui sono stati raccolti i dati
ABI Strumento Advanced Baseline Imager Identifica lo strumento usato per l'acquisizione dei dati
L2 Prodotto di livello 2 (prodotto derivato) Indica che i dati sono un prodotto di dati derivato, elaborato dalle osservazioni grezze
LSTC Prodotto Temperatura della superficie terrestre (Cielo sereno) Rappresenta il tipo di prodotto specifico, concentrandosi sulla temperatura della superficie terrestre in condizioni di cielo chiaro
M6 Analisi in modalità 6 (analisi completa del disco) Descrive la modalità di scansione, coprendo il disco completo della Terra
G18 SATELLITE GOES-18 (noto anche come GOES-West) Identifica il satellite da cui sono stati raccolti i dati

Dettagli tempo di osservazione

Inizio dell'osservazione (s20232080201177)-

Campo Descrizione Scopo
Anno 2023 Specifica l'anno dell'osservazione
Giorno dell'anno 208 Indica il giorno dell'anno in cui è iniziata l'osservazione
Tempo 02:01:17 UTC Fornisce l'ora esatta dell'avvio dell'osservazione in formato UTC
Decimi del secondo 7 Aggiunge precisione all'ora di inizio dell'osservazione

Fine dell'osservazione (e20232080203550)-

Campo Descrizione Scopo
Anno 2023 Specifica l'anno dell'osservazione
Giorno dell'anno 208 Indica il giorno dell'anno in cui l'osservazione è terminata
Tempo 02:03:55 UTC Fornisce l'ora esatta in cui l'osservazione è terminata in formato UTC
Decimi del secondo 0 Aggiunge precisione all'ora di fine dell'osservazione

Ora di creazione file (c20232080204563)-

Campo Descrizione Scopo
Anno 2023 Specifica l'anno in cui è stato creato il file
Giorno dell'anno 208 Indica il giorno dell'anno in cui è stato creato il file
Tempo 02:04:56 UTC Fornisce l'ora esatta in cui il file è stato creato in formato UTC
Decimi del secondo 3 Aggiunge precisione al tempo di creazione del file

Altre informazioni:

Campo Descrizione Scopo
DQF Flag di Qualità dei Dati, che indica le informazioni di qualità per i dati corrispondenti Fornisce informazioni sulla qualità dei dati
.tif Estensione del file Indica il formato di file dei dati

Il codice seguente estrae questi metadati dal nome file usando espressioni regolari (regex).

import re
from datetime import datetime, timedelta

def extract_goes_metadata(filename):
    """
    Extracts key metadata from a NOAA GOES satellite filename using regular expressions.

    Args:
        filename (str): The filename to parse.

    Returns:
        dict: A dictionary containing the extracted metadata.
    """

    # Regular expression pattern to match the filename format
    pattern = re.compile(
        r"^(OR)_"  # System (OR)
        r"(ABI)-(L\d)-(LSTC)-(M\d)_"  # Instrument, Level, Product, Mode
        r"(G\d{2})_"  # Satellite (G18)
        r"s(\d{4})(\d{3})(\d{2})(\d{2})(\d{2})(\d)_"  # Start time
        r"e(\d{4})(\d{3})(\d{2})(\d{2})(\d{2})(\d)_"  # End time
        r"c(\d{4})(\d{3})(\d{2})(\d{2})(\d{2})(\d)_"  # Creation time
        r"([A-Z0-9]+)$"  # Data quality flag
    )

    match = pattern.match(filename)

    if not match:
        return None  # Or raise an exception if you prefer

    # Extract all fields from the regular expression match groups
    (
        system,          # Operational system environment
        instrument,      # Advanced Baseline Imager
        level,           # Product level (L2)
        product_type,    # Product type (LSTC - Land Surface Temperature)
        mode,            # Scanning mode (M6)
        satellite,       # Satellite identifier (G18)
        s_year, s_doy, s_hour, s_minute, s_second, s_tenth,  # Start time components
        e_year, e_doy, e_hour, e_minute, e_second, e_tenth,  # End time components
        c_year, c_doy, c_hour, c_minute, c_second, c_tenth,  # Creation time components
        data_quality_flag  # Quality flag indicator
    ) = match.groups()

    def parse_goes_time(year, doy, hour, minute, second, tenth):
        """Parses GOES time components into an ISO format string."""
        try:
            dt = datetime(int(year), 1, 1) + timedelta(
                days=int(doy) - 1,
                hours=int(hour),
                minutes=int(minute),
                seconds=int(second),
                microseconds=int(tenth) * 100000,
            )
            return dt
        except ValueError:
            return None

    # Parse the time components into datetime objects
    start_time = parse_goes_time(s_year, s_doy, s_hour, s_minute, s_second, s_tenth)
    end_time = parse_goes_time(e_year, e_doy, e_hour, e_minute, e_second, e_tenth)
    creation_time = parse_goes_time(c_year, c_doy, c_hour, c_minute, c_second, c_tenth)

    # Create a dictionary to organize all extracted metadata
    metadata = {
        "system": system,               # Operational system environment (e.g., "OR" for operational)
        "instrument": instrument,       # Instrument used to capture data (e.g., "ABI" for Advanced Baseline Imager)
        "level": level,                 # Processing level of the data (e.g., "L2" for Level 2)
        "product_type": product_type,   # Type of product (e.g., "LSTC" for Land Surface Temperature Clear Sky)
        "mode": mode,                   # Scanning mode (e.g., "M6" for Mode 6, full disk scan)
        "satellite": satellite,         # Satellite identifier (e.g., "G18" for GOES-18)
        "start_time": start_time,       # Observation start time as datetime object
        "end_time": end_time,           # Observation end time as datetime object
        "creation_time": creation_time, # File creation time as datetime object
        "data_quality_flag": data_quality_flag,  # Quality flag for the data (e.g., "DQF")
    }

    return metadata


# Example usage:
print(file_name)
metadata_from_filename = extract_goes_metadata(file_name)
print(metadata_from_filename)
OR_ABI-L2-LSTC-M6_G18_s20232080001177_e20232080003550_c20232080004568_DQF
{'system': 'OR', 'instrument': 'ABI', 'level': 'L2', 'product_type': 'LSTC', 'mode': 'M6', 'satellite': 'G18', 'start_time': datetime.datetime(2023, 7, 27, 0, 1, 17, 700000), 'end_time': datetime.datetime(2023, 7, 27, 0, 3, 55), 'creation_time': datetime.datetime(2023, 7, 27, 0, 4, 56, 800000), 'data_quality_flag': 'DQF'}

Creare Elementi STAC dai file GeoTIFF Cloud-Optimized

Il blocco di codice seguente usa la rio-stac libreria per automatizzare la creazione di elementi STAC da Cloud-Optimized GeoTIFFs (COG).

Quando punta a un file COG, rio-stac estrae e organizza automaticamente metadati essenziali come limiti spaziali, informazioni di proiezione e proprietà raster in un formato STAC standardizzato. La libreria gestisce l'attività complessa di lettura dei metadati tecnici incorporati da GeoTIFF e la converte in campi conformi a STAC, tra cui:

  • geometria
  • Rettangolo di delimitazione (bbox)
  • Dettagli proiezione
  • Caratteristiche raster
  • Estensioni STAC

Questa automazione riduce significativamente il lavoro manuale necessario per creare elementi STAC validi e garantisce la coerenza nei metadati

from rio_stac import create_stac_item
from rasterio.io import MemoryFile
import json
from urllib.parse import urlparse, unquote

def create_stac_item_from_cog(url):
    """
    Create a basic STAC Item for GOES data using rio-stac with proper spatial handling
    
    Args:
        url (str): URL to the COG file
        
    Returns:
        pystac.Item: STAC Item with basic metadata and correct spatial information
    """
    
    # Extract the filename safely from the URL
    parsed_url = urlparse(url)
    path = parsed_url.path  # Gets just the path portion of the URL
    filename = os.path.basename(path)  # Get just the filename part
    
    # Remove .tif extension if present
    item_id = filename.replace('.tif', '')
    
    response = requests.get(url, stream=True)
    if response.status_code == 200:
        with MemoryFile(response.content) as memfile:
            with memfile.open() as dataset:
                # Create base STAC item from rasterio dataset calling create_stac_item from rio_stac
                stac_item = create_stac_item(
                    source=dataset,  # The rasterio dataset object representing the COG file
                    id=item_id,  # Generate a unique ID by extracting the filename without the .tif extension
                    asset_name='data',  # Name of the asset, indicating it contains the primary data
                    asset_href=url,  # URL to the COG file, used as the asset's location
                    with_proj=True,  # Include projection metadata (e.g., CRS, bounding box, etc.)
                    with_raster=True,  # Include raster-specific metadata (e.g., bands, resolution, etc.)
                    properties={
                        'datetime': None,  # Set datetime to None since explicit start/end times may be added later
                        # Add rasterio-specific metadata for the raster bands
                        'raster:bands': [
                            {
                                'nodata': dataset.nodata,  # Value representing no data in the raster
                                'data_type': dataset.dtypes[0],  # Data type of the raster (e.g., uint16)
                                'spatial_resolution': dataset.res[0]  # Spatial resolution of the raster in meters
                            }
                        ],
                        'file:size': len(response.content)  # Size of the file in bytes
                    },
                    extensions=[
                        'https://stac-extensions.github.io/file/v2.1.0/schema.json'  # Add the file extension schema for additional metadata
                    ]
                )
                
                return stac_item
    else:
        raise Exception(f"Failed to read .tif file: {response.status_code} - {response.text}")

# Example usage
sas_token = get_sas_token(sas_endpoint) # refresh the SAS token prior to creating STAC item
# Create file URL using the first_blob variable that's already defined
file_url = f"{blob_domain}/{container_name}/{first_blob.name}?{sas_token}"
# Create STAC item for the first blob
stac_item = create_stac_item_from_cog(file_url)

# Print the STAC item as JSON
print(json.dumps(stac_item.to_dict(), indent=2))
    {
      "type": "Feature",
      "stac_version": "1.0.0",
      "stac_extensions": [
        "https://stac-extensions.github.io/file/v2.1.0/schema.json",
        "https://stac-extensions.github.io/projection/v1.1.0/schema.json",
        "https://stac-extensions.github.io/raster/v1.1.0/schema.json"
      ],
      "id": "OR_ABI-L2-LSTC-M6_G18_s20232080001177_e20232080003550_c20232080004568_DQF",
      "geometry": {
        "type": "Polygon",
        "coordinates": [
          [
            [
              -161.57885623466754,
              14.795666555826678
            ],
            [
              -112.42114380921453,
              14.79566655500485
            ],
            [
              -89.5501123912648,
              53.52729778421186
            ],
            [
              175.5501122574517,
              53.52729779865781
            ],
            [
              -161.57885623466754,
              14.795666555826678
            ]
          ]
        ]
      },
      "bbox": [
        -161.57885623466754,
        14.79566655500485,
        175.5501122574517,
        53.52729779865781
      ],
      "properties": {
        "datetime": "2025-03-26T14:46:05.484602Z",
        "raster:bands": [
          {
            "nodata": 65535.0,
            "data_type": "uint16",
            "spatial_resolution": 2004.017315487541
          }
        ],
        "file:size": 118674,
        "proj:epsg": null,
        "proj:geometry": {
          "type": "Polygon",
          "coordinates": [
            [
              [
                -2505021.6463773525,
                1583173.791653181
              ],
              [
                2505021.6423414997,
                1583173.791653181
              ],
              [
                2505021.6423414997,
                4589199.764884492
              ],
              [
                -2505021.6463773525,
                4589199.764884492
              ],
              [
                -2505021.6463773525,
                1583173.791653181
              ]
            ]
          ]
        },
        "proj:bbox": [
          -2505021.6463773525,
          1583173.791653181,
          2505021.6423414997,
          4589199.764884492
        ],
        "proj:shape": [
          1500,
          2500
        ],
        "proj:transform": [
          2004.017315487541,
          0.0,
          -2505021.6463773525,
          0.0,
          -2004.017315487541,
          4589199.764884492,
          0.0,
          0.0,
          1.0
        ],
        "proj:wkt2": "PROJCS[\"unnamed\",GEOGCS[\"unknown\",DATUM[\"unnamed\",SPHEROID[\"Spheroid\",6378137,298.2572221]],PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]]],PROJECTION[\"Geostationary_Satellite\"],PARAMETER[\"central_meridian\",-137],PARAMETER[\"satellite_height\",35786023],PARAMETER[\"false_easting\",0],PARAMETER[\"false_northing\",0],UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]],AXIS[\"Easting\",EAST],AXIS[\"Northing\",NORTH],EXTENSION[\"PROJ4\",\"+proj=geos +lon_0=-137 +h=35786023 +x_0=0 +y_0=0 +ellps=GRS80 +units=m +no_defs +sweep=x\"]]"
      },
      "links": [],
      "assets": {
        "data": {
          "href": "https://goeseuwest.blob.core.windows.net/noaa-goes-cogs/goes-18/ABI-L2-LSTC/2023/208/00/OR_ABI-L2-LSTC-M6_G18_s20232080001177_e20232080003550_c20232080004568_DQF.tif?st=2025-03-25T14%3A46%3A03Z&se=2025-03-26T15%3A31%3A03Z&sp=rl&sv=2024-05-04&sr=c&skoid=9c8ff44a-6a2c-4dfb-b298-1c9212f64d9a&sktid=72f988bf-86f1-41af-91ab-2d7cd011db47&skt=2025-03-26T12%3A41%3A49Z&ske=2025-04-02T12%3A41%3A49Z&sks=b&skv=2024-05-04&sig=BuMxN2NUTdrhzY7Dvpd/X4yfX8gnFpHOzANHQLkKE1k%3D",
          "type": "image/tiff; application=geotiff",
          "raster:bands": [
            {
              "data_type": "uint16",
              "scale": 1.0,
              "offset": 0.0,
              "sampling": "area",
              "nodata": 65535.0,
              "unit": "1",
              "statistics": {
                "mean": 2.780959413109756,
                "minimum": 0,
                "maximum": 3,
                "stddev": 0.710762086175375,
                "valid_percent": 100.0
              },
              "histogram": {
                "count": 11,
                "min": 0.0,
                "max": 3.0,
                "buckets": [
                  26655,
                  0,
                  0,
                  25243,
                  0,
                  0,
                  7492,
                  0,
                  0,
                  570370
                ]
              }
            }
          ],
          "roles": []
        }
      }
    }

JSON dell'elemento STAC di rio-stac

La libreria rio-stac legge il file GOES COG ed estrae automaticamente i metadati della chiave.

Inoltre, in base al tipo di metadati inclusi, rio-stac ha aggiunto le estensioni STAC pertinenti. Le estensioni STAC migliorano la specifica di base aggiungendo metadati standardizzati e specifici del dominio.

  • L'estensione file fornisce metadati di file essenziali, ad esempio dimensioni e checksum.
  • L'estensione di proiezione acquisisce informazioni di riferimento spaziali, inclusi sistemi di coordinate e rettangoli di delimitazione.
  • L'estensione raster standardizza le proprietà specifiche dei dati raster, ad esempio le informazioni sulla banda e la risoluzione spaziale.

Le tabelle seguenti forniscono una spiegazione di tutti i metadati trovati in rio-stac.

Campi principali

Campo Descrizione Scopo
tipo Sempre «Funzionalità» Identifica il tipo come funzionalità GeoJSON
stac_version "1.0.0" Specifica la versione standard STAC
Id Identificatore univoco Contiene informazioni su satellite, prodotto e ora
stac_extensions Elenco di URL dello schema Definisce campi di metadati aggiuntivi

Informazioni spaziali

Campo Descrizione Scopo
geometria Poligono GeoJSON Definisce l'impronta dei dati nelle coordinate WGS84
bbox Coordinate del rettangolo delimitatore Fornisce un'ampiezza spaziale semplice per un rapido filtraggio
proj:geometry Poligono specifico della proiezione Impronta nelle coordinate della proiezione native
proj:bbox Limiti di proiezione nativa Estensione spaziale nel sistema di coordinate del satellite
proj:shape [1500, 2500] Dimensioni dell'immagine in pixel
proj:trasformazione Trasformazione affine Mappa il pixel nello spazio delle coordinate
proj:wkt2 Well-Known Testo Definizione di proiezione completa
proj:epsg nullo Non esiste alcun codice EPSG standard per questa proiezione della mappa

Informazioni temporali

Campo Descrizione Scopo
data e ora Timestamp di creazione Quando è stato creato questo elemento STAC

Informazioni raster

Campo Descrizione Scopo
raster:bande Matrice di oggetti banda Descrive le proprietà dei dati raster
→ tipo_di_dato uint16 Tipo di dati Pixel
→ risoluzione_spaziale 2004.02m Distanza del campione di terreno
→ scala/scostamento Fattori di conversione Trasforma i valori pixel in unità fisiche (Kelvin)
→ nodata 65535.0 Valore che non rappresenta dati
→ statistiche Riepilogo statistico Fornisce informazioni sulla distribuzione dei dati
istogramma → Distribuzione dei valori Visualizza la distribuzione dei dati

Informazioni sugli asset

Campo Descrizione Scopo
dati.beni Asset di dati principale Punta verso il file di dati reale
→ href URL Posizione del Cloud-Optimized GeoTIFF
→ tipo Tipo di supporto Identifica il formato di file

Metadati dei file

Campo Descrizione Scopo
file:dimensione 943.250 byte Dimensioni del file di dati

Aggiunta dei metadati dal Nome del File

Aggiungere quindi i dati trovati nel nome del file per completare la compilazione dei metadati per questo elemento STAC.

Una cosa da notare è che tutti i valori datetime degli elementi STAC devono conformarsi a ISO 8601. La libreria PySTAC ha una funzione datetime_to_str che garantisce che i dati siano formattati correttamente.

import pystac

def enhance_stac_item_with_metadata(stac_item, metadata_from_filename):
    """
    Enhances a STAC Item with additional metadata from GOES filename.
    
    Args:
        stac_item (pystac.Item): Existing STAC Item created by rio-stac
        metadata_from_filename (dict): Metadata extracted from filename
        
    Returns:
        pystac.Item: Enhanced STAC Item
    """
    # Add satellite/sensor properties to the STAC item
    stac_item.properties.update({
        'platform': f"GOES-{metadata_from_filename['satellite'][1:]}",
        'instruments': [metadata_from_filename['instrument']],
        'constellation': 'GOES'
    })
    
    # Add temporal properties to the STAC item, use pystac to ensure time conforms to ISO 8601
    stac_item.datetime = None  # Clear the default datetime
    stac_item.properties.update({
        'start_datetime': pystac.utils.datetime_to_str(metadata_from_filename['start_time']),
        'end_datetime': pystac.utils.datetime_to_str(metadata_from_filename['end_time']),
        'created': pystac.utils.datetime_to_str(metadata_from_filename['creation_time'])
    })
    
    # Add GOES-specific properties to the STAC item
    stac_item.properties.update({
        'goes:system': metadata_from_filename['system'],
        'goes:level': metadata_from_filename['level'],
        'goes:product_type': metadata_from_filename['product_type'],
        'goes:mode': metadata_from_filename['mode'],
        'goes:processing_level': metadata_from_filename['level'],
        'goes:data_quality_flag': metadata_from_filename['data_quality_flag']
    })
    
    return stac_item


# Example usage in new cell
stac_item = enhance_stac_item_with_metadata(stac_item, metadata_from_filename)
print(json.dumps(stac_item.to_dict(), indent=2))

Elemento STAC:

    {
      "type": "Feature",
      "stac_version": "1.0.0",
      "stac_extensions": [
        "https://stac-extensions.github.io/file/v2.1.0/schema.json",
        "https://stac-extensions.github.io/projection/v1.1.0/schema.json",
        "https://stac-extensions.github.io/raster/v1.1.0/schema.json"
      ],
      "id": "OR_ABI-L2-LSTC-M6_G18_s20232080001177_e20232080003550_c20232080004568_DQF",
      "geometry": {
        "type": "Polygon",
        "coordinates": [
          [
            [
              -161.57885623466754,
              14.795666555826678
            ],
            [
              -112.42114380921453,
              14.79566655500485
            ],
            [
              -89.5501123912648,
              53.52729778421186
            ],
            [
              175.5501122574517,
              53.52729779865781
            ],
            [
              -161.57885623466754,
              14.795666555826678
            ]
          ]
        ]
      },
      "bbox": [
        -161.57885623466754,
        14.79566655500485,
        175.5501122574517,
        53.52729779865781
      ],
      "properties": {
        "datetime": null,
        "raster:bands": [
          {
            "nodata": 65535.0,
            "data_type": "uint16",
            "spatial_resolution": 2004.017315487541
          }
        ],
        "file:size": 118674,
        "proj:epsg": null,
        "proj:geometry": {
          "type": "Polygon",
          "coordinates": [
            [
              [
                -2505021.6463773525,
                1583173.791653181
              ],
              [
                2505021.6423414997,
                1583173.791653181
              ],
              [
                2505021.6423414997,
                4589199.764884492
              ],
              [
                -2505021.6463773525,
                4589199.764884492
              ],
              [
                -2505021.6463773525,
                1583173.791653181
              ]
            ]
          ]
        },
        "proj:bbox": [
          -2505021.6463773525,
          1583173.791653181,
          2505021.6423414997,
          4589199.764884492
        ],
        "proj:shape": [
          1500,
          2500
        ],
        "proj:transform": [
          2004.017315487541,
          0.0,
          -2505021.6463773525,
          0.0,
          -2004.017315487541,
          4589199.764884492,
          0.0,
          0.0,
          1.0
        ],
        "proj:wkt2": "PROJCS[\"unnamed\",GEOGCS[\"unknown\",DATUM[\"unnamed\",SPHEROID[\"Spheroid\",6378137,298.2572221]],PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]]],PROJECTION[\"Geostationary_Satellite\"],PARAMETER[\"central_meridian\",-137],PARAMETER[\"satellite_height\",35786023],PARAMETER[\"false_easting\",0],PARAMETER[\"false_northing\",0],UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]],AXIS[\"Easting\",EAST],AXIS[\"Northing\",NORTH],EXTENSION[\"PROJ4\",\"+proj=geos +lon_0=-137 +h=35786023 +x_0=0 +y_0=0 +ellps=GRS80 +units=m +no_defs +sweep=x\"]]",
        "platform": "GOES-18",
        "instruments": [
          "ABI"
        ],
        "constellation": "GOES",
        "start_datetime": "2023-07-27T00:01:17.700000Z",
        "end_datetime": "2023-07-27T00:03:55Z",
        "created": "2023-07-27T00:04:56.800000Z",
        "goes:system": "OR",
        "goes:level": "L2",
        "goes:product_type": "LSTC",
        "goes:mode": "M6",
        "goes:processing_level": "L2",
        "goes:data_quality_flag": "DQF"
      },
      "links": [],
      "assets": {
        "data": {
          "href": "https://goeseuwest.blob.core.windows.net/noaa-goes-cogs/goes-18/ABI-L2-LSTC/2023/208/00/OR_ABI-L2-LSTC-M6_G18_s20232080001177_e20232080003550_c20232080004568_DQF.tif?st=2025-03-25T14%3A46%3A03Z&se=2025-03-26T15%3A31%3A03Z&sp=rl&sv=2024-05-04&sr=c&skoid=9c8ff44a-6a2c-4dfb-b298-1c9212f64d9a&sktid=72f988bf-86f1-41af-91ab-2d7cd011db47&skt=2025-03-26T12%3A41%3A49Z&ske=2025-04-02T12%3A41%3A49Z&sks=b&skv=2024-05-04&sig=BuMxN2NUTdrhzY7Dvpd/X4yfX8gnFpHOzANHQLkKE1k%3D",
          "type": "image/tiff; application=geotiff",
          "raster:bands": [
            {
              "data_type": "uint16",
              "scale": 1.0,
              "offset": 0.0,
              "sampling": "area",
              "nodata": 65535.0,
              "unit": "1",
              "statistics": {
                "mean": 2.780959413109756,
                "minimum": 0,
                "maximum": 3,
                "stddev": 0.710762086175375,
                "valid_percent": 100.0
              },
              "histogram": {
                "count": 11,
                "min": 0.0,
                "max": 3.0,
                "buckets": [
                  26655,
                  0,
                  0,
                  25243,
                  0,
                  0,
                  7492,
                  0,
                  0,
                  570370
                ]
              }
            }
          ],
          "roles": []
        }
      }
    }

Aggiungere l'elemento STAC a una raccolta

MC Pro richiede che tutti gli elementi STAC abbiano un riferimento all'ID collezione STAC padre in cui vengono inseriti. Per questa esercitazione, l'ID della collezione STAC è il nome del satellite e del prodotto dati.

Con PySTAC, è facile usare alcuni dei metadati raccolti dai file di origine per popolare i campi chiave per la raccolta STAC e usare le funzioni di convalida predefinite.

Il codice seguente crea una raccolta STAC padre per ospitare i dati GOES. Il codice salva quindi queste informazioni nei file usati per creare la raccolta Microsoft Planetary Computer Pro STAC e inserire gli elementi STAC in Planetary Computer Pro.

# Define collection properties
collection_id = f"{satellite}-{product}"
collection_title = f"{satellite.upper()} {product} Collection" 
collection_desc = f"Collection of {satellite} {product} Earth observation data"

# Create spatial extent
bbox = [-180, -60, 10, 60]  # placeholder, replace with actual data at a later date
spatial_extent = pystac.SpatialExtent([bbox])

# Create temporal extent, use current date time or replace with existing datetimes in stac_item
start_datetime = datetime.now()
if hasattr(metadata_from_filename, 'get'): 
    if metadata_from_filename.get('start_time'):
        start_datetime = metadata_from_filename.get('start_time')

temporal_extent = pystac.TemporalExtent([[start_datetime, None]])
extent = pystac.Extent(spatial=spatial_extent, temporal=temporal_extent)

# Create the STAC Collection
collection = pystac.Collection(
    id=collection_id,
    description=collection_desc,
    extent=extent,
    title=collection_title,
    license="public-domain",
)

# Add keywords and provider
collection.keywords = ["GOES", "satellite", "weather", "NOAA", satellite, product]
collection.providers = [
    pystac.Provider(
        name="NOAA",
        roles=["producer", "licensor"],
        url="https://www.noaa.gov/"
    )
]

# Create output directories
output_dir = "stac_catalog"
collection_dir = os.path.join(output_dir, collection_id)
items_dir = os.path.join(collection_dir, "items")
os.makedirs(items_dir, exist_ok=True)

# Important: Save the collection first to generate proper file paths
collection_path = os.path.join(collection_dir, "collection.json")
collection.set_self_href(collection_path)

# Extract filename for the item
original_filename = first_blob.name.split('/')[-1]
base_filename = original_filename.replace('.tif', '')
item_path = os.path.join(items_dir, f"{base_filename}.json")

# Set the item's proper href
stac_item.set_self_href(item_path)

# Now associate the item with the collection (after setting hrefs)
collection.add_item(stac_item)

# Create a catalog to contain the collection
catalog = pystac.Catalog(
    id="goes-data-catalog",
    description="GOES Satellite Data Catalog",
    title="GOES Data"
)
catalog_path = os.path.join(output_dir, "catalog.json")
catalog.set_self_href(catalog_path)
catalog.add_child(collection)

# Validate the collection and contained items
print("Validating collection and items...")
try:
    collection.validate_all()
    print("✅ Collection and items validated successfully")
    
    # Save everything to disk
    catalog.normalize_and_save(catalog_path, pystac.CatalogType.SELF_CONTAINED)
    print(f"✅ STAC catalog saved at: {catalog_path}")
    print(f"✅ STAC collection saved at: {collection_path}")
    print(f"✅ STAC item saved at: {item_path}")
    
except Exception as e:
    print(f"❌ Validation error: {str(e)}")
Validating collection and items...
✅ Collection and items validated successfully
✅ STAC catalog saved at: stac_catalog/catalog.json
✅ STAC collection saved at: stac_catalog/goes-18-ABI-L2-LSTC/collection.json
✅ STAC item saved at: stac_catalog/goes-18-ABI-L2-LSTC/items/OR_ABI-L2-LSTC-M6_G18_s20232080001177_e20232080003550_c20232080004568_DQF.json

Passaggi successivi

Dopo aver creato alcuni elementi STAC, è possibile inserire i dati in Microsoft Planetary Computer Pro.

Per l'inserimento di un singolo elemento:

Per l'ingestione in massa:

Offriamo anche lo strumento STAC Forge che offre un'automazione maggiore usando modelli relativi ai dati.