Créer un élément SpatioTemporal Asset Catalog (STAC)

Découvrez comment créer un élément SpatioTemporal Asset Catalog (STAC) pour une ressource de données géospatiales raster. Chaque ressource de données géospatiales chargée sur un ordinateur Microsoft Planetary Pro GeoCatalog doit avoir un élément compatible STAC associé.

Dans ce guide, vous allez :

  • Installez les bibliothèques Python requises à l’aide de PIP.
  • Affichez et inspectez les données GOES-18 à l’aide du code Python fourni.
  • Extrayez les métadonnées du nom de fichier à l’aide d’expressions régulières.
  • Créez des éléments STAC à partir de fichiers GeoTIFF Cloud-Optimized à l’aide de rio-stac.
  • Améliorez l’élément STAC avec les métadonnées extraites du nom de fichier.
  • Ajoutez l’élément STAC à une collection STAC parente.
  • Validez et enregistrez le catalogue, la collection et les éléments STAC.
  • Ajoutez les éléments STAC à Microsoft Planetary Computer Pro.

Ce tutoriel présente et explique les fonctionnalités par le biais d’extraits de code, pour une expérience de style de bloc-notes interactive, téléchargez ce didacticiel en tant que notebook Jupyter.

Conditions préalables

Pour effectuer ce démarrage rapide, les éléments suivants sont requis :

Fonctionnalités clés de STAC

Le catalogue de ressources SpatioTemporal (STAC) est une norme ouverte pour structurer et partager des données géospatiales. Il fournit un langage et un format communs pour décrire les ressources géospatiales, ce qui facilite la découverte, l’accès et l’utilisation de ces ressources sur différentes plateformes et applications. Voici les principales fonctionnalités de la norme STAC :

  • Interopérabilité : le schéma json standardisé garantit une intégration et une compréhension faciles entre les outils et systèmes.
  • Extensibilité : champs principaux flexibles avec des extensions personnalisées pour des besoins spécifiques.
  • Détectabilité : une structure cohérente améliore la recherche et la découverte des ressources géospatiales.
  • Accessibilité : les liens directs vers les ressources de données facilitent la récupération et l’intégration transparentes.
  • Automatisation : permet le traitement et l’analyse automatisés avec des métadonnées standardisées.

Avantages de STAC pour la normalisation des métadonnées

  • Cohérence entre les types de données : infrastructure unifiée pour divers types de données géospatiales.
  • Collaboration améliorée : simplifie le partage et la collaboration avec un format de métadonnées commun.
  • Intégration améliorée des données : facilite l’intégration des jeux de données à partir de plusieurs sources.
  • Gestion simplifiée des données : les outils automatisés réduisent les efforts manuels en matière de maintenance des métadonnées.
  • Future-Proofing : la nature extensible s’adapte aux nouveaux types et technologies de données.

Microsoft Planetary Computer Pro (MC Pro) utilise STAC comme norme d’indexation principale pour assurer l’interopérabilité entre les jeux de données. Ce tutoriel montre aux utilisateurs comment créer des éléments STAC à partir de zéro à l’aide de bibliothèques open source courantes.

La spécification de l’élément STAC détaille la façon dont les éléments STAC sont construits et les métadonnées minimales requises qui doivent être remplies. STAC est une norme flexible, ce qui permet aux utilisateurs de décider des données qu’ils souhaitent inclure dans le cadre des métadonnées. Les métadonnées qui peuvent être utilisées pour remplir un élément STAC peuvent être inclues dans la ressource de données, dans un fichier sidecar (.XML, .JSON, .TXT, etc.), ou même dans des emplacements tels que le nom de fichier. Les utilisateurs doivent prendre en compte les types d’utilisateurs de métadonnées peuvent souhaiter effectuer des recherches et effectuer un tri à l’avenir lors du choix des métadonnées à inclure.

Ce tutoriel utilise des échantillons de données d’imagerie COG (GeoTIFF optimisé pour le cloud) provenant du jeu de données LST (Land Surface Temperature) des satellites GOES-R (Geostationary Operational Environmental Satellite R) de la NOAA (National Oceanic and Atmospheric Agency). Ces données sont incluses dans l’ordinateur planetaire ouvert en tant que fichiers COG, mais ne disposent pas de métadonnées STAC. Les satellites GOES produisent de nombreux produits de données. Vous trouverez plus d’informations dans le guide de définition de produit et d’utilisateurs de la sérieGOES-R.

Le processus utilisé dans ce didacticiel peut être généralisé pour tout type de données où les champs de métadonnées clés sont énumérés à l’aide de plusieurs bibliothèques open source.

Installer des bibliothèques Python

Pour commencer, nous installons les bibliothèques Python requises à l’aide de PIP :

  • rasterio: ce package est utilisé pour lire et écrire des données raster géospatiales. Il fournit des outils permettant d’utiliser des formats de données raster tels que GeoTIFF.

  • pystac: ce package est utilisé pour utiliser la norme STAC. Il fournit des outils pour créer, lire et manipuler des métadonnées STAC.

  • rio-stac : Ce package intègre rasterio et pystac afin de créer des éléments STAC à partir de données raster. Il simplifie le processus de génération de métadonnées STAC pour les jeux de données raster.

  • matplotlib: cette bibliothèque est utilisée pour créer des visualisations et des tracés. Dans le contexte des données géospatiales, il permet de restituer et d’afficher des images raster, ce qui permet aux utilisateurs d’inspecter visuellement les données avant de créer des métadonnées STAC.

  • azure-storage-blob: Cette bibliothèque cliente de Stockage Azure fournit l'accès aux services Azure Blob Storage. Il permet une interaction directe avec les données géospatiales stockées dans le cloud, ce qui permet aux utilisateurs de lire et d’utiliser des fichiers stockés dans des conteneurs d’objets blob Azure sans les télécharger en premier.

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

La section suivante du code affiche des données GOES-18 de l’ordinateur planetaire ouvert. Nous sélectionnons le jeu de données GOES-R Advanced Baseline Imager Level 2 Land Surface Temperature - CONUS et un fichier arbitraire du jour 208 de 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)

Visualisation de nuances de gris des données de raster géospatiales d’un satellite GOES-18, affichant des modèles de température de surface terrestre dans une région.

En examinant les données et le nom du fichier, nous pouvons déjà voir les éléments clés des métadonnées nécessaires pour générer l’élément STAC. Le nom du fichier contient des informations sur les satellites qui ont capturé les données et quand elles ont été capturées.

Pour l’exemple, le nom de fichier est OR_ABI-L2-LSTC-M6_G18_s20232080101177_e20232080103550_c20232080104570_DQF, en fonction du guide du produit, cela signifie :

Composants détaillés

Champ Descriptif Objectif
OU Environnement du système opérationnel Spécifie l’environnement système dans lequel les données ont été collectées
ABI Instrument Advanced Baseline Imager Identifie l’instrument utilisé pour capturer les données
L2 Produit de niveau 2 (produit dérivé) Indique que les données sont un produit dérivé, traité à partir d’observations brutes
LSTC Produit de température de surface terrestre (Clear Sky) Représente le type de produit spécifique, en se concentrant sur la température de la surface terrestre dans des conditions de ciel claires
M6 Analyse en mode 6 (analyse de disque complet) Décrit le mode d’analyse, couvrant le disque complet de la Terre
G18 SATELLITE GOES-18 (également appelé GOES-West) Identifie le satellite à partir duquel les données ont été collectées

Détails du temps d’observation

Début de l’observation (s20232080201177)-

Champ Descriptif Objectif
Année 2023 Spécifie l’année de l’observation
Jour de l’année 208 Indique le jour de l’année où l’observation a commencé
Heure 02:01:17 UTC Fournit l’heure exacte à laquelle l’observation a démarré au format UTC
Dixièmes de seconde 7 Ajoute une précision à l’heure de début de l’observation

Fin de l’observation (e20232080203550)-

Champ Descriptif Objectif
Année 2023 Spécifie l’année de l’observation
Jour de l’année 208 Indique le jour de l’année où l’observation s’est terminée
Heure 02:03:55 UTC Fournit l’heure exacte à laquelle l’observation s’est terminée au format UTC
Dixièmes de seconde 0 Ajoute une précision à l’heure de fin de l’observation

Heure de création de fichier (c20232080204563)-

Champ Descriptif Objectif
Année 2023 Spécifie l’année de création du fichier
Jour de l’année 208 Indique le jour de l’année où le fichier a été créé
Heure 02:04:56 UTC Fournit l’heure exacte de création du fichier au format UTC
Dixièmes de seconde 3 Ajoute une précision à l’heure de création de fichier

Informations supplémentaires :

Champ Descriptif Objectif
DQF Indicateur de qualité des données, indiquant des informations de qualité pour les données correspondantes Fournit des informations sur la qualité des données
.tif Extension de fichier Indique le format de fichier des données

Le code suivant extrait ces métadonnées du nom de fichier à l’aide d’expressions régulières (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'}

Créer des éléments STAC à partir des fichiers GeoTIFF Cloud-Optimized

Le bloc de code suivant utilise la bibliothèque rio-stac pour automatiser la création d’éléments STAC à partir de GeoTIFF optimisés pour le cloud (COG).

En cas de pointage vers un fichier COG, rio-stac extrait et organise automatiquement les métadonnées essentielles telles que les limites spatiales, les informations de projection et les propriétés raster dans un format STAC standardisé. La bibliothèque gère la tâche complexe de lecture des métadonnées techniques incorporées à partir du GeoTIFF et la convertit en champs conformes STAC, notamment :

  • Géométrie
  • Cadre englobant (bbox)
  • Détails de la projection
  • Caractéristiques de la trame
  • STAC Extensions

Cette automatisation réduit considérablement le travail manuel nécessaire pour créer des éléments STAC valides et garantit la cohérence dans les métadonnées

Note

GeoCatalog présente des limitations sur les caractères qui peuvent être utilisés dans les ID d’élément STAC et les clés de ressource. Vérifiez que vos ID ne contiennent pas les caractères suivants : -, , _, +, (, )et .. Vous devrez peut-être modifier la item_id logique de génération pour remplacer ou supprimer ces caractères de vos noms de fichiers.

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 de l’élément STAC à partir de rio-stac

La bibliothèque rio-stac lit automatiquement le fichier GOES COG et extrait les métadonnées de clé.

En outre, en fonction du type de métadonnées inclus, rio-stac a ajouté les extensions STAC pertinentes. Les extensions STAC améliorent la spécification principale en ajoutant des métadonnées standardisées et spécifiques au domaine.

  • L’extension de fichier fournit des métadonnées de fichier essentielles telles que la taille et les sommes de contrôle.
  • L’extension de projection capture des informations de référence spatiales, notamment des systèmes de coordonnées et des cadres englobants.
  • L’extension Raster standardise les propriétés spécifiques aux données raster, telles que les informations de bande et la résolution spatiale.

Les tableaux suivants fournissent une explication de toutes les métadonnées rio-stac trouvées.

Champs principaux

Champ Descriptif Objectif
type Toujours « Fonctionnalité » Identifie le type en tant que fonctionnalité GeoJSON
stac_version « 1.0.0 » Spécifie la version standard STAC
id Identificateur unique Contient des informations sur le satellite, le produit et l’heure
stac_extensions Liste des URL de schéma Définit des champs de métadonnées supplémentaires

Informations spatiales

Champ Descriptif Objectif
géométrie Polygone GeoJSON Définit l’empreinte des données dans les coordonnées WGS84
bbox Coordonnées du cadre englobant Fournit une étendue spatiale simple pour le filtrage rapide
proj :geometry Polygone spécifique à la projection Empreinte dans les coordonnées de projection natives
proj:bbox Limites de projection natives Étendue spatiale dans le système de coordonnées du satellite
proj : forme [1500, 2500] Dimensions d’image en pixels
proj:transform Transformation affine Mappe le pixel à l’espace de coordonnées
proj:wkt2 Well-Known Text Définition de la projection complète
proj:epsg zéro Aucun code EPSG standard n’existe pour cette projection de carte

Informations temporelles

Champ Descriptif Objectif
date/heure Horodateur de création Lorsque cet élément STAC a été créé

Informations relatives à la trame

Champ Descriptif Objectif
raster:bands Tableau des objets de bande Décrit les propriétés de données raster
→ type_de_données « uint16 » Type de données pixel
→ résolution_spatiale 2004.02 mètres Distance de l’échantillon de sol
→ mise à l’échelle/décalage Facteurs de conversion Transforme les valeurs de pixels en unités physiques (Kelvin)
→ nodata 65535.0 Valeur représentant aucune donnée
statistiques → Résumé statistique Fournit des informations de distribution de données
→ histogram Distribution des valeurs Visualise la distribution des données

Informations sur les ressources

Champ Descriptif Objectif
assets.data Ressource de données principale Pointe vers le fichier de données réel
→ href URL Emplacement du GeoTIFF optimisé pour le cloud
→ type Type de support Identifie le format de fichier

Métadonnées de fichier

Champ Descriptif Objectif
fichier : taille 943 250 octets Taille du fichier de données

Ajout des métadonnées à partir du nom de fichier

Ensuite, nous ajoutons les données que nous avons trouvées dans le nom de fichier pour remplir les métadonnées de cet élément STAC.

Il convient de noter que les dates et heures des éléments STAC doivent être conformes à la norme ISO 8601. La bibliothèque PySTAC a une fonction datetime_to_str qui garantit que les données sont correctement mises en forme.

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

Élément 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": []
        }
      }
    }

Ajouter l’élément STAC à une collection

MC Pro exige que tous les éléments STAC contiennent une référence à l'ID de collection STAC parent dans lequel ils sont intégrés. Pour ce tutoriel, l’ID de collection STAC est le nom du satellite et du produit de données.

Avec PySTAC, il est facile d’utiliser certaines des métadonnées collectées à partir des fichiers sources pour remplir les champs clés de la collection STAC et utiliser les fonctions de validation intégrées.

Le code suivant crée une collection STAC parente pour héberger les données GOES. Le code enregistre ensuite ces informations dans les fichiers utilisés pour créer la collection Microsoft Planetary Computer Pro STAC et ingérer des éléments STAC dans 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

Étapes suivantes

Maintenant que vous avez créé certains éléments STAC, il est temps d’ingérer les données dans Microsoft Planetary Computer Pro.

Pour l’ingestion d’un seul élément :

Pour l’ingestion en bloc :

Nous offrons également l’outil STAC Forge qui fournit une automatisation accrue à l’aide de modèles autour des données.