Руководство: Использование API Microsoft Planetary Computer Pro для загрузки и визуализации данных

Коллекции STAC (каталог ресурсов SpatioTemporal) используются в GeoCatalog для индексирования и хранения связанных пространственно-временных ресурсов. В этом комплексном руководстве вы создадите новую коллекцию STAC, загрузите изображения Sentinel-2 в коллекцию и выполните запросы этих изображений с помощью API GeoCatalog.

Изучив это руководство, вы:

  • Создадим собственную коллекцию STAC в планетарном компьютере Pro GeoCatalog
  • Добавление спутниковых снимков в эту коллекцию из Европейского космического агентства
  • Настройте коллекцию, чтобы изображения в коллекции могли визуализироваться в веб-интерфейсе Planetary Computer Pro
  • Запрос данных из коллекции STAC с помощью STAC API Planetary Computer Pro

В этом руководстве показаны и объясняются возможности с помощью фрагментов кода для интерактивного интерфейса стиля записной книжки, скачайте это руководство в виде записной книжки Jupyter.

Предпосылки

Перед выполнением этого руководства вам потребуется планетарный компьютер Pro GeoCatalog, развернутый в подписке Azure. Вам также нужна среда для выполнения этой записной книжки и установки необходимых пакетов. Мы предлагаем выполнять это руководство на виртуальной машине Azure Machine Learning или использовать интеграцию записной книжки Visual Studio Code в виртуальной среде Python. Однако этот блокнот должен выполняться везде, где поддерживаются блокноты Jupyter, если выполнены следующие требования.

  • Python 3.10 или более поздней версии
  • Azure CLI установлен, и вы запустите az login для входа в учетную запись Azure.
  • Необходимые требования, перечисленные в разделе «Опции руководства», установлены.

Откройте Jupyter notebook в Azure Machine Learning или VS Code

Вход в Azure с помощью Azure CLI

Следующая команда регистрирует вас в Azure с помощью Azure CLI. Выполните команду и следуйте инструкциям для входа.

!az login

Выбор параметров руководства

Перед выполнением этого руководства вам потребуются права доступа участника к существующему экземпляру GeoCatalog. Введите URL-адрес экземпляра GeoCatalog в переменной geocatalog_url. В этом руководстве вы создадите коллекцию для изображений Sentinel-2, предоставляемых Европейским космическим агентством (ESA), которые в настоящее время хранятся в каталоге данных планетарного компьютера Майкрософт.

# URL for your given GeoCatalog
geocatalog_url = (
    "<GEOCATALOG_URL>"
)
geocatalog_url = geocatalog_url.rstrip("/")  # Remove trailing slash if present

api_version = "2025-04-30-preview"

# User selections for demo

# Collection within the Planetary Computer
pc_collection = "sentinel-2-l2a"

# Bounding box for AOI
bbox_aoi = [-22.455626, 63.834083, -22.395201, 63.880750]

# Date range to search for imagery
param_date_range = "2024-02-04/2024-02-11"

# Maximum number of items to ingest
param_max_items = 6

Импорт необходимых пакетов

Прежде чем создать коллекцию STAC, необходимо импортировать несколько пакетов Python и определить вспомогательные функции для получения необходимого маркера доступа.

pip install pystac-client azure-identity requests pillow
# Import the required packages
import json
import random
import string
import time
from datetime import datetime, timedelta, timezone
from io import BytesIO
from typing import Any, Optional, Dict

import requests
from azure.identity import AzureCliCredential
from IPython.display import Markdown as md
from IPython.display import clear_output
from PIL import Image
from pystac_client import Client

# Function to get a bearer token for the  Planetary Computer Pro API
MPC_APP_ID = "https://geocatalog.spatio.azure.com"

_access_token = None
def getBearerToken():
    global _access_token
    if not _access_token or datetime.fromtimestamp(_access_token.expires_on) < datetime.now() + timedelta(minutes=5):
        credential = AzureCliCredential()
        _access_token = credential.get_token(f"{MPC_APP_ID}/.default")

    return {"Authorization": f"Bearer {_access_token.token}"}

# Method to print error messages when checking response status
def raise_for_status(r: requests.Response) -> None:
    try:
        r.raise_for_status()
    except requests.exceptions.HTTPError as e:
        try:
            print(json.dumps(r.json(), indent=2))
        except:
            print(r.content)
        finally:
            raise

Создание коллекции STAC

Определение JSON коллекции STAC

Затем вы определяете коллекцию STAC как элемент JSON. В этом руководстве используйте существующий JSON коллекции STAC для коллекции Sentinel-2-l2a в рамках Планетарного компьютера Microsoft. Коллекция назначается случайным идентификатором и заголовком, чтобы избежать конфликта с другими существующими коллекциями.

# Load example STAC collection JSON

response = requests.get(
    f"https://planetarycomputer.microsoft.com/api/stac/v1/collections/{pc_collection}"
)
raise_for_status(response)
stac_collection = response.json()

collection_id = pc_collection + "-tutorial-" + str(random.randint(0, 1000))

# Genereate a unique name for the test collection
stac_collection["id"] = collection_id
stac_collection["title"] = collection_id

# Determine the storage account and container for the assets
pc_storage_account = stac_collection.pop("msft:storage_account")
pc_storage_container = stac_collection.pop("msft:container")
pc_collection_asset_container = (
    f"https://{pc_storage_account}.blob.core.windows.net/{pc_storage_container}"
)

# View your STAC collection JSON
stac_collection

При создании коллекции в GeoCatalog в JSON коллекции не может быть ресурсов уровня коллекции (например, эскиза коллекции), связанных с коллекцией, поэтому сначала удалите существующие ресурсы (не беспокойтесь, что вы добавите эскиз позже).

# Save the thumbnail url
thumbnail_url = stac_collection['assets']['thumbnail']['href']

# Remove the assets field from the JSON (you'll see how to add this back later)
print("Removed the following items from the STAC Collection JSON:")
stac_collection.pop('assets')
# Create a STAC collection by posting to the STAC collections API

collections_endpoint = f"{geocatalog_url}/stac/collections"

response = requests.post(
    collections_endpoint,
    json=stac_collection,
    headers=getBearerToken(),
    params={"api-version": api_version}
)

if response.status_code==201:
    print("STAC Collection created named:",stac_collection['title'])
else:
    raise_for_status(response)

Откройте веб-интерфейс GeoCatalog, и вы увидите новую коллекцию, указанную на вкладке "Коллекции".

Эскиз коллекции Access

Затем вы хотите добавить эскиз в нашу коллекцию, чтобы отобразиться вместе с нашей коллекцией. В целях этой демонстрации используйте эскиз из существующей коллекции Sentinel-2 в планетарном компьютере Майкрософт.

# Read thumbnail for your collection

thumbnail_response = requests.get(thumbnail_url)
raise_for_status(thumbnail_response)
img = Image.open(BytesIO(thumbnail_response.content))
img

Добавьте эскиз в ваш планетарный компьютер Pro GeoCatalog

После чтения эскиза вы можете добавить его в нашу коллекцию, разместив его в конечной точке API ресурсов коллекции GeoCatalogs, а также требуемый json активов.

# Define the GeoCatalog collections API endpoint
collection_assets_endpoint = f"{geocatalog_url}/stac/collections/{collection_id}/assets"

# Read the example thumbnail from this collection from the Planetary Computer
thumbnail = {"file": ("lulc.png", thumbnail_response.content)}

# Define the STAC collection asset type - thumbnail in this case
asset = {
    "data": '{"key": "thumbnail", "href":"", "type": "image/png", '
    '"roles":  ["test_asset"], "title": "test_asset"}'
}

# Post the thumbnail to the GeoCatalog collections asset endpoint
response = requests.post(
    collection_assets_endpoint,
    data=asset,
    files=thumbnail,
    headers=getBearerToken(),
    params={"api-version": api_version}
)

if response.status_code==201:
    print("STAC Collection thumbnail updated for:",stac_collection['title'])
else:
    raise_for_status(response)

Прочитайте новую коллекцию в вашем Planetary Computer Pro GeoCatalog

Обновите браузер и увидите эскиз. Вы также можете получить JSON коллекции программным способом, выполнив следующий вызов к конечной точке коллекций:

# Request the collection JSON from your GeoCatalog
collection_endpoint = f"{geocatalog_url}/stac/collections/{stac_collection['id']}"

response = requests.get(
    collection_endpoint,
    json={'collection_id':stac_collection['id']},
    headers=getBearerToken(),
    params={"api-version": api_version}
)

if response.status_code==200:
    print("STAC Collection successfully read:",stac_collection['title'])
else:
    raise_for_status(response)

response.json()
print(f"""
You successfully created a new STAC Collection in GeoCatalog named {collection_id}.
You can view your collection by visiting the GeoCatalog Explorer: {geocatalog_url}/collections
""")

Прием элементов STAC и ресурсов

После создания этой коллекции вы готовы принять новые элементы STAC в коллекцию STAC с помощью API элементов GeoCatalog! Выполните этот процесс следующим образом:

  1. Получение маркера SAS от планетарного компьютера Майкрософт
  2. Зарегистрируйте этот токен в качестве источника загрузки в GeoCatalog
  3. Опубликуйте элементы STAC из этой коллекции в API элементов GeoCatalog.
  4. Проверка успешного приема элементов
ingestion_sources_endpoint = f"{geocatalog_url}/inma/ingestion-sources"
ingestion_source_endpoint = lambda id: f"{geocatalog_url}/inma/ingestion-sources/{id}"


def find_ingestion_source(container_url: str) -> Optional[Dict[str, Any]]:

    response = requests.get(
        ingestion_sources_endpoint,
        headers=getBearerToken(),
        params={"api-version": api_version},
    )

    for source in response.json()["value"]:
        ingestion_source_id = source["id"]

        response = requests.get(
            ingestion_source_endpoint(ingestion_source_id),
            headers=getBearerToken(),
            params={"api-version": api_version},
        )
        raise_for_status(response)

        response = response.json()

        if response["connectionInfo"]["containerUrl"] == container_url:
            return response


def create_ingestion_source(container_url: str, sas_token: str):
    response = requests.post(
        ingestion_sources_endpoint,
        json={
            "kind": "SasToken",
            "connectionInfo": {
                "containerUrl": container_url,
                "sasToken": sas_token,
            },
        },
        headers=getBearerToken(),
        params={"api-version": api_version},
    )
    raise_for_status(response)


def remove_ingestion_source(ingestion_source_id: str):
    response = requests.delete(
        ingestion_source_endpoint(ingestion_source_id),
        headers=getBearerToken(),
        params={"api-version": api_version},
    )
    raise_for_status(response)

Выполнить запрос к планетарному компьютеру

Сначала необходимо запросить планетарный компьютер для поиска изображений Sentinel-2, которые соответствуют нашим конкретным требованиям. В этом случае вы ищете изображения Sentinel-2 на планетарном компьютере, который соответствует следующим критериям:

  • Коллекция — изображения из коллекции Sentinel-2-l2a
  • Диапазон времени — собираемый от 4 февраля до 11 февраля 2024 г.
  • Область интереса — изображение, собранное над южной Исландией (определено как ограничивающий прямоугольник)

Выполнив этот поиск, вы увидите, что соответствующие элементы STAC находятся в планетарном компьютере.

# Search criteria
print("Using the below parameters to search the Planetary Computer:\n")
print("Collection:", pc_collection)
print("Bounding box for area of interest:",bbox_aoi)
print("Date range:",param_date_range)
print("Max number of items:",param_max_items)
# Query the Planetary Computer

# Connect to the Planetary Computer
catalog = Client.open("https://planetarycomputer.microsoft.com/api/stac/v1")

search = catalog.search(collections=[pc_collection], bbox=bbox_aoi, datetime=param_date_range)
total_items = search.item_collection()

items = total_items[:param_max_items]
print("Total number of matching items:",len(total_items))
print("Total number of items for ingest base on user selected parameter:",len(items))

if total_items==0:
    print("No items matched your user specified parameters used at the top of this demo. Update these parameters")
# Print an example STAC item returned by the Planetary Computer
items[0]

Регистрация источника приема

Перед приемом этих элементов STAC и связанных с ними ресурсов (изображений) в коллекцию GeoCatalog необходимо определить, нужно ли зарегистрировать новый источник приема. GeoCatalog использует источники приема для отслеживания мест хранения (контейнеров в Azure Blob Storage), к которым у него есть доступ.

Регистрация источника загрузки данных осуществляется путем предоставления GeoCatalog расположения контейнера хранилища и маркера SAS с правами на чтение для доступа к контейнеру. Если элементы STAC или связанные с ними ресурсы находятся в контейнере хранилища, то при отсутствии доступа вашего GeoCatalog к ingest операция будет завершена сбоем.

Чтобы запустить этот процесс, сначала запросите маркер SAS с планетарного компьютера, который предоставляет нам доступ на чтение к контейнеру, где находятся образы Sentinel-2.

# Request API token from the Planetary Computer

pc_token = requests.get("https://planetarycomputer.microsoft.com/api/sas/v1/token/{}".format(pc_collection)).json()
print(f"Planetary Computer API Token will expire {pc_token['msft:expiry']}")

Затем попытайтесь зарегистрировать этот контейнер хранилища BLOB-объектов Azure и связанный маркер SAS в качестве источника приема с помощью GeoCatalog. Существует потенциал того, что источник приема уже существует для этого контейнера хранилища. Если да, найдите идентификатор существующего источника приема.

Предупреждение

Если будет найден дублирующий источник приема с маркером, срок действия которого истекает в течение следующих 15 минут, он будет удален и заменен. Удаление источника загрузки, который используется в настоящее время при выполнении загрузки, может нарушить эти загрузки.

existing_ingestion_source: Optional[Dict[str, Any]] = find_ingestion_source(pc_collection_asset_container)

if existing_ingestion_source:
    connection_info = existing_ingestion_source["connectionInfo"]
    expiration = datetime.fromisoformat(connection_info["expiration"].split('.')[0]) # works in all Python 3.X versions
    expiration = expiration.replace(tzinfo=timezone.utc) # set timezone to UTC
    if expiration < datetime.now(tz=timezone.utc) + timedelta(minutes=15):
        print(f"Recreating existing ingestion source for {pc_collection_asset_container}")
        remove_ingestion_source(existing_ingestion_source["id"])
        create_ingestion_source(pc_collection_asset_container, pc_token["token"])
    else:
        print(f"Using existing ingestion source for {pc_collection_asset_container} with expiration {expiration}")
else:
    print(f"Creating ingestion source for {pc_collection_asset_container}")
    create_ingestion_source(pc_collection_asset_container, pc_token["token"])

Импортировать элементы STAC, используя API элементов GeoCatalog

Теперь, когда вы зарегистрировали источник данных или убедились, что он существует, вы будете импортировать найденные элементы STAC в Планетарный компьютер с помощью API элементов GeoCatalog. Это можно сделать, разместив каждый элемент в API элементов, который создает новую операцию приема в GeoCatalog.

# Ingest items

items_endpoint = f"{geocatalog_url}/stac/collections/{collection_id}/items"

operation_ids = []

for item in items:

    item_json = item.to_dict()
    item_json['collection'] = collection_id

    # Remove non-static assets
    del(item_json['assets']['rendered_preview'])
    del(item_json['assets']['preview'])
    del(item_json['assets']['tilejson'])

    response = requests.post(
        items_endpoint,
        json=item_json,
        headers=getBearerToken(),
        params={"api-version": api_version}
    )

    operation_ids.append(response.json()['id'])
    print(f"Ingesting item {item_json['id']} with operation id {response.json()['id']}")

Учитывая, что процесс приема данных Sentinel-2 может занять некоторое время, вы можете запустить этот код, чтобы проверить статус ваших операций загрузки с помощью API операций GeoCatalog.

# Check the status of the operations
operations_endpoint = f"{geocatalog_url}/inma/operations"
# Loop through all the operations ids until the status of each operation ids is "Finished"
pending=True

start = time.time()

while pending:
    # Count the number of operation ids that are finished vs unfinished
    num_running = 0
    num_finished = 0
    num_failed = 0
    clear_output(wait=True)
    for operation_id in operation_ids:
        response = requests.get(
            f"{operations_endpoint}/{operation_id}",
            headers=getBearerToken(),
            params={"api-version": api_version},
        )
        raise_for_status(response)
        status = response.json()["status"]
        print(f"Operation id {operation_id} status: {status}")
        if status == "Running":
            num_running+=1
        elif status == "Failed":
            num_failed+=1
        elif status == "Succeeded":
            num_finished+=1
    
    num_running
    stop=time.time()
    # Print the sumary of num finished, num running and num failed
    
    print("Ingesting Imagery:")
    print(f"\tFinished: {num_finished}\n\tRunning: {num_running}\n\tFailed: {num_failed}")
    print("Time Elapsed (seconds):",str(stop-start))
    
    if num_running == 0:
        pending=False
        print(f"Ingestion Complete!\n\t{num_finished} items ingested.\n\t{num_failed} items failed.")

    else:
        print(f"Waiting for {num_running} operations to finish")
        time.sleep(5)

Вы должны иметь возможность обновить веб-браузер и щелкнуть вкладку "Элементы", чтобы увидеть эти только что отправленные элементы.

Управление коллекциями

После импорта этих элементов STAC и связанных с ними ресурсов (изображений) в коллекцию STAC вам необходимо предоставить GeoCatalog другие файлы конфигурации, прежде чем визуализировать эти элементы в веб-интерфейсе GeoCatalog.

Параметры визуализации коллекции

Сначала загрузите файл конфигурации визуализации для этой коллекции с Планетарного Компьютера. Этот файл конфигурации можно считывать в GeoCatalog для отрисовки изображений различными способами в обозревателе. Это связано с тем, что элементы STAC могут содержать множество различных ресурсов (изображений), которые можно объединить для создания совершенно новых изображений определенной области, которая выделяет видимые или невидимые функции. Например, элементы STAC Sentinel-2 имеют более 12 различных изображений из разных частей электромагнитного спектра. Эта конфигурация отрисовки указывает GeoCatalog, как объединить эти изображения, чтобы он мог отображать изображения в естественных цветах или псевдоцветах (инфракрасных цветах).

# Read render JSON from Planetary Computer

render_json = requests.get("https://planetarycomputer.microsoft.com/api/data/v1/mosaic/info?collection={}".format(pc_collection)).json()
render_json['renderOptions']

После чтения этой конфигурации параметров отрисовки с планетарного компьютера можно включить эти параметры отрисовки для коллекции, разместив эту конфигурацию в конечной точке параметров отрисовки.

# Post render options config to GeoCatalog render-options API

render_config_endpoint = f"{geocatalog_url}/stac/collections/{collection_id}/configurations/render-options"

for render_option in render_json['renderOptions']:

    # Rename render configs such that they can be stored by GeoCatalog
    render_option['id'] = render_option['name'].translate(str.maketrans('', '', string.punctuation)).lower().replace(" ","-")[:30]

    # Post render definition
    response = requests.post(
        render_config_endpoint,
        json=render_option,
        headers=getBearerToken(),
        params={"api-version": api_version}
    )

Определения мозаики

Как и в описанной выше конфигурации отрисовки, обозреватель GeoCatalog позволяет указать одно или несколько определений мозаики для коллекции. Эти определения мозаики позволяют нам задавать инструкции Проводнику GeoCatalog о том, как фильтровать элементы, отображаемые в Проводнике. Например, одна базовая конфигурация отрисовки (показанная в следующей ячейке) указывает GeoCatalog отображать последнее изображение для любой заданной области. Более сложные конфигурации отрисовки позволяют отображать различные представления, такие как наименее облачное изображение для заданного расположения, снятое в октябре 2023 года.

# Post mosaic definition

mosiacs_config_endpoint = f"{geocatalog_url}/stac/collections/{collection_id}/configurations/mosaics"

response = requests.post(
    mosiacs_config_endpoint,
    json={"id": "mos1",
          "name": "Most recent available",
          "description": "Most recent available imagery in this collection",
          "cql": []
    },
    headers=getBearerToken(),
    params={"api-version": api_version}
)

Открытие веб-интерфейса GeoCatalog

Поздравляю! Вы создали коллекцию, добавили элементы и ресурсы STAC и обновили коллекцию, чтобы включить необходимые файлы конфигурации, чтобы его можно было просмотреть через обозреватель в веб-интерфейсе GeoCatalog.

Вернитесь в эксплорер GeoCatalog в веб-интерфейсе, чтобы просмотреть вашу коллекцию!

Сбор запросов с помощью API STAC

Теперь, когда вы просмотрели коллекцию в GeoCatalog Explorer, вы сможете узнать, как использовать API STAC GeoCatalog для поиска и извлечения элементов и ресурсов STAC для дальнейшего анализа.

Этот процесс начинается с отправки поиска в API STAC GeoCatalog. В частности, вы будете искать изображения в вашей коллекции, которые находятся внутри исходной ограничивающей рамки, которую вы использовали для извлечения изображений из Планетарного Компьютера.

Неудивительно, что этот запрос возвращает все элементы STAC, ранее помещенные в коллекцию.

stac_search_endpoint = f"{geocatalog_url}/stac/search"

response = requests.post(
    stac_search_endpoint,
    json={"collections":[collection_id],
          "bbox":bbox_aoi
    },
    headers=getBearerToken(),
    params={"api-version": api_version, "sign": "true"}
)

matching_items = response.json()['features']
print(len(matching_items))

В предыдущем запросе вы также указали другой параметр: sign:true. Это указывает GeoCatalog на возврат подписанного href (href элемента + SAS токен), который позволяет читать указанные ресурсы из блоб-объектов хранилища Azure.

# Download one of the assets bands, band 09
asset_href = matching_items[0]['assets']['B09']['href']
print(asset_href)

response = requests.get(asset_href)
img = Image.open(BytesIO(response.content))
img

Очистите ресурсы

Удаление элементов

На этом этапе вы создали коллекцию GeoCatalog, добавили элементы и ресурсы в коллекцию и извлекли эти элементы и ресурсы с помощью API STAC GeoCatalog. На последнем этапе этого руководства вы собираетесь удалить эти элементы и удалить коллекцию.

# Delete all items

for item in matching_items:
    response = requests.delete(
        f"{items_endpoint}/{item['id']}",
        headers=getBearerToken(),
        params={"api-version": api_version}
    )

Вы можете подтвердить удаление всех элементов, выполнив следующую команду. Обратите внимание, что для полного удаления элементов и связанных с ними ресурсов может потребоваться несколько минут.

# Confirm that all the items have been deleted
response = requests.post(
    stac_search_endpoint,
    json={"collections":[stac_collection['id']],
          "bbox": bbox_aoi
    },
    headers=getBearerToken(),
    params={"api-version": api_version, "sign": "true"}
)

matching_items = response.json()['features']
print(len(matching_items))

Удаление коллекции

Теперь в качестве последнего шага может потребоваться полностью удалить коллекцию из экземпляра GeoCatalog.

# Delete the collection
response = requests.delete(
    f"{collections_endpoint}/{collection_id}",
    headers=getBearerToken(),
    params={"api-version": api_version}
)

raise_for_status(response)
print(f"STAC Collection deleted: {collection_id}")

Дальнейшие шаги

В этом комплексном руководстве описан процесс создания новой коллекции STAC, приема образов Sentinel-2 в коллекцию и отправки запросов к этим изображениям с помощью API GeoCatalog. Если вы хотите узнать больше о каждой из этих тем, изучите следующие материалы: