Краткое руководство. Создание веб-приложения с помощью Microsoft Planetary Computer Pro

В этом кратком руководстве вы создадите веб-приложение, которое отображает спутниковые изображения и геопространственные данные из GeoCatalog на интерактивной карте. Аутентификация пользователей с помощью Microsoft Entra ID, выполнение запросов коллекций STAC и отрисовка плиток карты — все в JavaScript в браузере.

Что вы узнаете:

  • Проверка подлинности пользователей и получение маркеров доступа с помощью MSAL.js
  • Выполните запрос к API STAC для обнаружения коллекций и элементов
  • Отображение растровых плиток на карте MapLibre GL с заголовками авторизации
  • Создание бесшовных слоев мозаики во всех коллекциях
  • Скачивание необработанных ресурсов с помощью маркеров SAS

Шаблоны кода работают с любой современной платформой JavaScript (React, Vue, Angular) или vanilla JavaScript. API GeoCatalog полностью поддерживают CORS, поэтому их можно напрямую вызывать во время разработки через localhost — без необходимости в прокси-сервере.

Вы можете скачать и проверить этот код из общедоступного репозитория GitHub Microsoft Planetary Computer Pro.

Предпосылки

Обзор архитектуры

Обычное веб-приложение GeoCatalog следует этой архитектуре:

Схема, на которой показана архитектура веб-приложения GeoCatalog с клиентом браузера, подключающимся к идентификатору Microsoft Entra для проверки подлинности и API GeoCatalog для доступа к данным.

Регистрация приложения в идентификаторе Microsoft Entra

Прежде чем веб-приложение сможет пройти проверку подлинности пользователей, зарегистрируйте его в идентификаторе Microsoft Entra. В этом кратком руководстве используется регистрация одностраничного приложения (SPA), которая идеально подходит для JavaScript приложений на стороне клиента и локальной разработки. Шаблоны интеграции API, показанные на последующих шагах, работают с любым типом приложения.

Замечание

Для рабочих приложений с серверным сервером рекомендуется выбрать другой тип регистрации (web, Native и т. д.). См. инструкции по настройке проверки подлинности приложений по выбору подходящего подхода для вашего сценария.

Регистрация в качестве одностраничного приложения

  1. Перейдите к идентификатору Microsoft Entra на портале Azure.
  2. Выберите регистрации приложений на боковой панели.
  3. Выберите Новая регистрация.
  4. Введите имя приложения (например, GeoCatalog Web App).
  5. В разделе Поддерживаемые типы учетных записей выберите Учетные записи только в этом каталоге организации.
  6. В разделе URI перенаправления выберите одностраничные приложения (SPA) и введите URL-адрес разработки (например, http://localhost:5173).
  7. Выберите Зарегистрировать.

После регистрации обратите внимание на следующие значения на странице обзора :

  • Идентификатор приложения (клиента)
  • Идентификатор каталога (клиента)

Дополнительные сведения см. в кратком руководстве по регистрации приложения.

Предоставление разрешений API

Приложению требуется разрешение на вызов API GeoCatalog от имени вошедшего пользователя:

  1. В разделе регистрации приложения выберите разрешения API>Добавить разрешение.
  2. Выберите API, которые используются в моей организации , и найдите Azure Orbital Spatio.
  3. Выберите делегированные разрешения и проверьте user_impersonation.
  4. Выберите Добавить разрешения.
  5. Если вы являетесь администратором, выберите "Предоставить согласие администратора" от имени всех пользователей в арендаторе.

Настройка приложения

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

Конфигурация Ценность Description
URL-адрес каталога https://{name}.{region}.geocatalog.spatio.azure.com Ваша конечная точка GeoCatalog
Идентификатор арендатора Из регистрации приложения Клиент Microsoft Entra
Идентификатор клиента Из регистрации приложения Идентификатор клиента приложения
Область API https://geocatalog.spatio.azure.com/.default Всегда используйте это точное значение

Установка зависимостей

Установите библиотеку проверки подлинности Майкрософт (MSAL) для браузерных приложений и библиотеки карт:

npm install @azure/msal-browser maplibre-gl
  • @azure/msal-browser — обрабатывает проверку подлинности OAuth 2.0 с помощью идентификатора Microsoft Entra
  • maplibre-gl — библиотека карт с открытым исходным кодом для визуализации плиток

Подсказка

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

  • auth.js: функции и конфигурация токенов MSAL
  • api.js: API STAC, API плитки и функции маркера SAS
  • map.js: инициализация MapLibre и управление слоями плиток
  • App.js или main.js: объедините все через пользовательский интерфейс

Каждая функция получает свои зависимости (маркеры доступа, URL-адреса) в качестве параметров, что упрощает интеграцию с любой платформой или структурой проекта.

Внедрение аутентификации MSAL

Снимок экрана: поток проверки подлинности в примере веб-приложения.

Настройте MSAL для проверки подлинности браузера. В следующем примере показана ключевая конфигурация и шаблон получения токенов:

import { PublicClientApplication, InteractionRequiredAuthError } from '@azure/msal-browser';

// Configuration - replace with your values or load from environment/config
const msalConfig = {
  auth: {
    clientId: 'YOUR_CLIENT_ID',
    authority: 'https://login.microsoftonline.com/YOUR_TENANT_ID',
    redirectUri: window.location.origin,
  },
  cache: {
    cacheLocation: 'sessionStorage',
    storeAuthStateInCookie: false,
  },
};

// Create MSAL instance
const msalInstance = new PublicClientApplication(msalConfig);

// GeoCatalog API scope - always use this exact value
const scopes = ['https://geocatalog.spatio.azure.com/.default'];

/**
 * Acquire an access token for GeoCatalog API calls.
 * Tries silent acquisition first, falls back to popup if needed.
 */
async function getAccessToken() {
  const account = msalInstance.getActiveAccount() || msalInstance.getAllAccounts()[0];
  
  if (!account) {
    throw new Error('No authenticated account. Call login() first.');
  }

  try {
    // Try silent token acquisition (uses cached token)
    const result = await msalInstance.acquireTokenSilent({ account, scopes });
    return result.accessToken;
  } catch (error) {
    // If silent fails (token expired), fall back to popup
    if (error instanceof InteractionRequiredAuthError) {
      const result = await msalInstance.acquireTokenPopup({ scopes });
      return result.accessToken;
    }
    throw error;
  }
}

/**
 * Sign in the user via popup.
 */
async function login() {
  const result = await msalInstance.loginPopup({ scopes });
  msalInstance.setActiveAccount(result.account);
  return result.account;
}

/**
 * Sign out the user.
 */
function logout() {
  msalInstance.logoutPopup();
}

API STAC: коллекции запросов и элементы

API STAC GeoCatalog предоставляет конечные точки для обнаружения и запроса геопространственных данных. Для всех запросов требуется заголовок Authorization с токеном носителя, полученным из реализации аутентификации MSAL.

Перечисление коллекций

Снимок экрана: список коллекций STAC в примере веб-приложения.

const API_VERSION = '2026-04-15';

async function listCollections(accessToken, catalogUrl) {
  const url = `${catalogUrl}/stac/collections?api-version=${API_VERSION}`;
  
  const response = await fetch(url, {
    headers: {
      'Authorization': `Bearer ${accessToken}`,
    },
  });
  
  if (!response.ok) {
    throw new Error(`Failed to list collections: ${response.statusText}`);
  }
  
  const data = await response.json();
  return data.collections; // Array of STAC Collection objects
}

Перечисление элементов в коллекции

Снимок экрана: список элементов STAC в примере веб-приложения.

const API_VERSION = '2026-04-15';

async function listItems(accessToken, catalogUrl, collectionId, limit = 10) {
  const url = `${catalogUrl}/stac/collections/${collectionId}/items?limit=${limit}&api-version=${API_VERSION}`;
  
  const response = await fetch(url, {
    headers: {
      'Authorization': `Bearer ${accessToken}`,
    },
  });
  
  if (!response.ok) {
    throw new Error(`Failed to list items: ${response.statusText}`);
  }
  
  const data = await response.json();
  return data.features; // Array of STAC Item objects
}

Поиск по коллекциям

Снимок экрана: использование поиска STAC для возврата интересующих элементов.

const API_VERSION = '2026-04-15';

async function searchItems(accessToken, catalogUrl, searchParams) {
  const url = `${catalogUrl}/stac/search?api-version=${API_VERSION}`;
  
  const response = await fetch(url, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${accessToken}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify(searchParams),
  });
  
  if (!response.ok) {
    throw new Error(`Search failed: ${response.statusText}`);
  }
  
  return await response.json();
}

// Example usage:
const results = await searchItems(token, catalogUrl, {
  collections: ['my-collection'],
  bbox: [-122.5, 37.5, -122.0, 38.0],  // [west, south, east, north]
  datetime: '2024-01-01/2024-12-31',
  limit: 20,
});

URL-адреса плиток: сборка URL-адресов для визуализации карты

API GeoCatalog Tiler предоставляет растровые данные в виде тайлов карты. Создайте URL-адреса плиток со следующим шаблоном:

Плитки одного элемента

Снимок экрана: отображение плиток для одного элемента.

{catalogUrl}/data/mosaic/collections/{collectionId}/items/{itemId}/tiles/{z}/{x}/{y}@1x.png
  ?api-version=2026-04-15
  &tileMatrixSetId=WebMercatorQuad
  &assets=visual

Функция построения URL-адресов плитки

const API_VERSION = '2026-04-15';

/**
 * Build a tile URL template for a STAC item.
 * Returns a URL with {z}/{x}/{y} placeholders for use with map libraries.
 */
function buildTileUrl(catalogUrl, collectionId, itemId, options = {}) {
  const { assets = 'visual', colormap, rescale } = options;
  
  const base = `${catalogUrl}/data/mosaic/collections/${collectionId}/items/${itemId}/tiles/{z}/{x}/{y}@1x.png`;
  
  const params = new URLSearchParams();
  params.set('api-version', API_VERSION);
  params.set('tileMatrixSetId', 'WebMercatorQuad');
  params.set('assets', assets);
  
  if (colormap) params.set('colormap_name', colormap);
  if (rescale) params.set('rescale', rescale);
  
  return `${base}?${params.toString()}`;
}

// Example usage:
const tileUrl = buildTileUrl(
  'https://mygeocatalog.northcentralus.geocatalog.spatio.azure.com',
  'aerial-imagery',
  'image-001',
  { assets: 'visual' }
);

Параметры ключевой плитки

Параметр Обязательно Description
api-version Да Версия API (2026-04-15)
tileMatrixSetId Да Используйте WebMercatorQuad для веб-карт
assets Да Имена активов для отрисовки (например, visual, image)
colormap_name нет Именованная цветовая карта (например, viridis, terrain)
rescale нет Диапазон значений для масштабирования (например: 0,255)
asset_bidx нет Индексы полосы (например, image\|1,2,3 для RGB)

Замечание

Для коллекций с изображениями с четырьмя полосами (например, NAIP с RGB+ NIR) используйте asset_bidx=image|1,2,3 только диапазоны RGB. Тайлер не может кодировать четыре канала в формате PNG.


Интеграция карты: отображение плиток с помощью MapLibre GL

Библиотеки карт, такие как MapLibre GL, Leaflet и OpenLayers, могут отображать растровые плитки. Основная задача - добавление заголовков авторизации в запросы к тайлам, так как эти библиотеки извлекают тайлы напрямую.

Пример MAPLibre GL

MapLibre GL предоставляет возможность внедрения заголовков с помощью transformRequest.

import maplibregl from 'maplibre-gl';
import 'maplibre-gl/dist/maplibre-gl.css';

// Store the current access token
let currentAccessToken = null;

function initializeMap(containerId, accessToken) {
  currentAccessToken = accessToken;
  
  const map = new maplibregl.Map({
    container: containerId,
    style: {
      version: 8,
      sources: {
        osm: {
          type: 'raster',
          tiles: ['https://tile.openstreetmap.org/{z}/{x}/{y}.png'],
          tileSize: 256,
          attribution: '© OpenStreetMap contributors',
        },
      },
      layers: [{ id: 'osm', type: 'raster', source: 'osm' }],
    },
    center: [0, 0],
    zoom: 2,
    // Add authorization header to tile requests
    transformRequest: (url, resourceType) => {
      // Only add auth for GeoCatalog tile requests
      if (url.includes('geocatalog.spatio.azure.com') && currentAccessToken) {
        return {
          url,
          headers: { 'Authorization': `Bearer ${currentAccessToken}` },
        };
      }
      return { url };
    },
  });
  
  return map;
}

function addTileLayer(map, tileUrl, bounds) {
  // Remove existing layer and source if present
  if (map.getLayer('data-layer')) {
    map.removeLayer('data-layer');
  }
  if (map.getSource('data-tiles')) {
    map.removeSource('data-tiles');
  }
  
  // Add tile source
  map.addSource('data-tiles', {
    type: 'raster',
    tiles: [tileUrl],
    tileSize: 256,
    minzoom: 10,  // Many aerial collections require zoom 10+
    maxzoom: 18,
  });
  
  // Add tile layer
  map.addLayer({
    id: 'data-layer',
    type: 'raster',
    source: 'data-tiles',
  });
  
  // Zoom to bounds [west, south, east, north]
  if (bounds) {
    map.fitBounds([[bounds[0], bounds[1]], [bounds[2], bounds[3]]], { padding: 50 });
  }
}

Это важно

Функция transformRequest вызывается для каждого запроса плитки. Сохраните токен доступа в переменной, к которой transformRequest может получить доступ, и обновите её при обновлении токена.


Плитки мозаики: отображение изображений на уровне коллекции

Снимок экрана, показывающий, как отобразить плитки мозаики для коллекции.

Чтобы просмотреть все элементы в коллекции как непрерывный слой, зарегистрируйте мозаичный поиск и используйте возвращенный идентификатор поиска:

const API_VERSION = '2026-04-15';

/**
 * Register a mosaic search for a collection.
 * Returns a search ID that can be used to fetch mosaic tiles.
 */
async function registerMosaic(catalogUrl, collectionId, accessToken) {
  const url = `${catalogUrl}/data/mosaic/register?api-version=${API_VERSION}`;
  
  const response = await fetch(url, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${accessToken}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      collections: [collectionId],
    }),
  });
  
  if (!response.ok) {
    throw new Error(`Failed to register mosaic: ${response.statusText}`);
  }
  
  const data = await response.json();
  // Note: API returns 'searchid' (lowercase), not 'searchId'
  return data.searchid;
}

/**
 * Build a mosaic tile URL template.
 */
function buildMosaicTileUrl(catalogUrl, searchId, collectionId, options = {}) {
  const { assets = 'visual' } = options;
  
  const base = `${catalogUrl}/data/mosaic/searches/${searchId}/tiles/{z}/{x}/{y}@1x.png`;
  
  const params = new URLSearchParams();
  params.set('api-version', API_VERSION);
  params.set('tileMatrixSetId', 'WebMercatorQuad');
  params.set('collection', collectionId);
  params.set('assets', assets);
  
  return `${base}?${params.toString()}`;
}

Метки SAS: загрузка сырых активов

API SAS предоставляет ограниченные по времени токены для скачивания необработанных файлов данных (GeoTIFFs, COG и других файлов) прямо из Хранилище BLOB-объектов Azure. Используйте этот параметр, если вам нужны исходные исходные файлы, а не отрисованные плитки.

Это важно

Маркеры SAS позволяют только загрузку в браузерных приложениях. Из-за политик CORS хранилища BLOB-объектов Azure браузеры не могут считывать данные BLOB-объектов с помощью JavaScript fetch(). См. раздел об ограничениях браузера .

Снимок экрана, показывающий, как скачать ресурсы с помощью токена SAS.

Получение маркера SAS

const API_VERSION = '2026-04-15';

/**
 * Get a SAS token for accessing assets in a collection.
 * Returns a token string that can be appended to asset URLs.
 */
async function getCollectionSasToken(accessToken, catalogUrl, collectionId) {
  const url = `${catalogUrl}/sas/token/${collectionId}?api-version=${API_VERSION}`;
  
  const response = await fetch(url, {
    headers: {
      'Authorization': `Bearer ${accessToken}`,
    },
  });
  
  if (!response.ok) {
    throw new Error(`Failed to get SAS token: ${response.statusText}`);
  }
  
  const data = await response.json();
  return data.token; // SAS token string
}

Создание подписанного URL-адреса для скачивания

/**
 * Build a signed URL for downloading an asset.
 * Appends the SAS token to the asset's href.
 */
function buildSignedAssetUrl(assetHref, sasToken) {
  const separator = assetHref.includes('?') ? '&' : '?';
  return `${assetHref}${separator}${sasToken}`;
}

// Example usage:
const sasToken = await getCollectionSasToken(accessToken, catalogUrl, 'my-collection');
const assetHref = item.assets['visual'].href;
const signedUrl = buildSignedAssetUrl(assetHref, sasToken);

Активация скачивания файла

/**
 * Trigger a browser download for an asset file.
 * Works by creating a temporary anchor element.
 */
function downloadAsset(signedUrl, filename) {
  const link = document.createElement('a');
  link.href = signedUrl;
  link.download = filename || 'download';
  document.body.appendChild(link);
  link.click();
  document.body.removeChild(link);
}

// Example: Download an asset
downloadAsset(signedUrl, 'aerial-image.tif');

Ограничения браузера

Маркеры SAS работают по-разному в браузерах и серверном коде:

Вариант использования Browser Server-side
Скачивание файлов (пользователь выбирает ссылку) ✅ Работает ✅ Работает
Чтение данных BLOB через fetch() ❌ Блокировка CORS ✅ Работает
Обработка необработанных пикселей в JavaScript ❌ Невозможно ✅ Работает

Загрузка через браузер работает, потому что навигация (щелчки по ссылкам) обходит CORS. Однако запросы к хранилищу объектов Blob в Azure блокируются, fetch() поскольку учетная запись хранения не включает источник вашего приложения в CORS-политику.

Если приложению необходимо считывать и обрабатывать необработанные данные ресурсов в браузере, реализуйте серверный прокси-сервер:

Замечание

Следующий код является упрощенным примером для иллюстрации шаблона прокси-сервера. Для рабочих приложений конечная точка прокси-сервера должна пройти проверку подлинности запросов (например, переадресовав маркер носителя пользователя или используя проверку подлинности сеанса) и проверить, разрешен ли пользователь получить доступ к запрошенным ресурсам.

// ❌ Browser: This fails due to CORS
const response = await fetch(signedUrl);
const data = await response.arrayBuffer(); // Error!

// ✅ Browser: Call your backend instead
const response = await fetch('/api/proxy-asset', {
  method: 'POST',
  body: JSON.stringify({ collectionId, itemId, assetName })
});
const data = await response.json(); // Works!

Бэкенд может извлечь большой двоичный объект с помощью токена SAS и вернуть обработанные результаты.


Рекомендации по разработке

Поддержка CORS

API GeoCatalog включают полную поддержку CORS.Access-Control-Allow-Origin: * Приложения на основе браузера могут отправлять прямые запросы в GeoCatalog из любого источника, в том числе http://localhost во время разработки. Прокси-сервер или обходной путь не требуется.

API разрешает заголовок Authorization в запросах CORS, поэтому вызовы, прошедшие проверку подлинности fetch(), работают непосредственно из JavaScript в браузере.

Выбор подходящего метода доступа к данным

Метод Браузер fetch() Скачивание браузера Server-side Лучше всего для
API тайлера ✅ Полностью поддерживается. ✅ Да ✅ Да Визуализация карты
Токены SAS ❌ Блокировка CORS ✅ Да ✅ Да Скачивание необработанных файлов
  • Tiler API: используется для отображения изображений на картах. Возвращает готовые плитки в форматах PNG или WebP с полной поддержкой CORS. Ознакомьтесь с URL-адресами плитки и интеграцией карт.

  • Токены SAS: используйте для загрузки оригинальных исходных файлов (GeoTIFFs, COG). Загрузка через браузер работает, но fetch() блокируется политиками CORS хранилища Azure Blob. См. токены SAS для получения подробной информации и возможных обходных путей.

Обновление токена

Срок действия маркеров доступа обычно истекает через час. Приложение должно:

  1. Обрабатывайте ошибки 401, получая новый токен.
  2. Используйте бесшумное получение токенов MSAL, которое автоматически обновляет истекшие токены.
  3. Обновите ссылку на токен, которую использует ваша карта transformRequest.

Обработка ошибок

Обработка распространенных сценариев ошибок:

Состояние HTTP Причина Solution
401 Срок действия токена истек или он недопустим Обновите токен доступа
404 Элемент или коллекция не найдены Убедитесь в наличии идентификаторов
424 Плитка вне границ данных Ожидаемое — обработайте аккуратно

Устранение неполадок

Ошибка Причина Solution
"AADSTS50011: несоответствие URL-адреса ответа" URI перенаправления в коде не соответствует регистрации в Microsoft Entra ID Добавьте URL-адрес вашей разработки (например, http://localhost:3000) в качестве URI перенаправления SPA в вашей регистрации приложения.
Ошибка "Недопустимая область" Использование URL-адреса GeoCatalog вместо области API Используйте https://geocatalog.spatio.azure.com/.default в качестве области
401 Несанкционированный доступ к запросам на плитку Библиотека карт не включает аутентификационные заголовки Используйте transformRequest (MapLibre) для добавления токена Bearer; убедитесь, что токен актуален.
Плитки не соответствуют базовой карте Неправильный набор матрицы плиток Используйте tileMatrixSetId=WebMercatorQuad для проекции Web Mercator (EPSG:3857)
"Не удалось декодировать изображение" Неправильное имя объекта, многоспектральное изображение или за пределами области данных Проверяйте item_assets на наличие допустимых имен; используйте asset_bidx=image\|1,2,3 для RGB; ожидается, что 404/424 будут вне зоны покрытия.

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