Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
В этом кратком руководстве вы создадите веб-приложение, которое отображает спутниковые изображения и геопространственные данные из 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.
Предпосылки
- Учетная запись Azure с активной подпиской. Создайте учетную запись бесплатно .
- Развернутый ресурс GeoCatalog с хотя бы одной коллекцией, содержащей элементы.
- Удостоверение пользователя должно иметь доступ к ресурсу GeoCatalog Reader (или более поздней версии). См. статью "Управление доступом к ресурсу GeoCatalog".
- Node.js версии 18 или более поздней.
Обзор архитектуры
Обычное веб-приложение GeoCatalog следует этой архитектуре:
Регистрация приложения в идентификаторе Microsoft Entra
Прежде чем веб-приложение сможет пройти проверку подлинности пользователей, зарегистрируйте его в идентификаторе Microsoft Entra. В этом кратком руководстве используется регистрация одностраничного приложения (SPA), которая идеально подходит для JavaScript приложений на стороне клиента и локальной разработки. Шаблоны интеграции API, показанные на последующих шагах, работают с любым типом приложения.
Замечание
Для рабочих приложений с серверным сервером рекомендуется выбрать другой тип регистрации (web, Native и т. д.). См. инструкции по настройке проверки подлинности приложений по выбору подходящего подхода для вашего сценария.
Регистрация в качестве одностраничного приложения
- Перейдите к идентификатору Microsoft Entra на портале Azure.
- Выберите регистрации приложений на боковой панели.
- Выберите Новая регистрация.
- Введите имя приложения (например, GeoCatalog Web App).
- В разделе Поддерживаемые типы учетных записей выберите Учетные записи только в этом каталоге организации.
- В разделе URI перенаправления выберите одностраничные приложения (SPA) и введите URL-адрес разработки (например,
http://localhost:5173). - Выберите Зарегистрировать.
После регистрации обратите внимание на следующие значения на странице обзора :
- Идентификатор приложения (клиента)
- Идентификатор каталога (клиента)
Дополнительные сведения см. в кратком руководстве по регистрации приложения.
Предоставление разрешений API
Приложению требуется разрешение на вызов API GeoCatalog от имени вошедшего пользователя:
- В разделе регистрации приложения выберите разрешения API>Добавить разрешение.
- Выберите API, которые используются в моей организации , и найдите Azure Orbital Spatio.
- Выберите делегированные разрешения и проверьте user_impersonation.
- Выберите Добавить разрешения.
- Если вы являетесь администратором, выберите "Предоставить согласие администратора" от имени всех пользователей в арендаторе.
Настройка приложения
Приложению требуются следующие значения конфигурации. Способ предоставления этих значений зависит от средства сборки (переменные среды, файлы конфигурации и т. д.):
| Конфигурация | Ценность | 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.
Перечисление коллекций
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
}
Перечисление элементов в коллекции
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
}
Поиск по коллекциям
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
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 для получения подробной информации и возможных обходных путей.
Обновление токена
Срок действия маркеров доступа обычно истекает через час. Приложение должно:
- Обрабатывайте ошибки 401, получая новый токен.
- Используйте бесшумное получение токенов MSAL, которое автоматически обновляет истекшие токены.
- Обновите ссылку на токен, которую использует ваша карта
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 будут вне зоны покрытия. |