次の方法で共有


クイック スタート: Microsoft Planetary Computer Pro を使用して Web アプリケーションを構築する

このクイック スタートでは、GeoCatalog の衛星画像と地理空間データを対話型マップに表示する Web アプリケーションを構築します。 Microsoft Entra ID を使用してユーザーを認証し、STAC コレクションに対してクエリを実行し、マップ タイルをレンダリングします(すべてブラウザーの JavaScript から)。

学習内容:

  • MSAL.js を使用してユーザーを認証し、アクセス トークンを取得する
  • STAC API にクエリを実行してコレクションと項目を検出する
  • 承認ヘッダーを使用して MapLibre GL マップにラスター タイルを表示する
  • コレクション全体でシームレスなモザイク レイヤーを作成する
  • SAS トークンを使用して生アセットをダウンロードする

コード パターンは、最新の JavaScript フレームワーク (React、Vue、Angular) またはバニラ JavaScript で動作します。 GeoCatalog API には完全な CORS サポートがあるため、開発中に localhost から直接呼び出すことができます。プロキシは必要ありません。

このコードは、 Microsoft Planetary Computer Pro パブリック GitHub リポジトリからダウンロードしてテストできます。

[前提条件]

アーキテクチャの概要

一般的な GeoCatalog Web アプリケーションは、次のアーキテクチャに従います。

認証のために Microsoft Entra ID に接続し、データ アクセス用に GeoCatalog API に接続するブラウザー クライアントを含む GeoCatalog Web アプリケーションのアーキテクチャを示す図。

Microsoft Entra ID でアプリケーションを登録する

Web アプリケーションでユーザーを認証する前に、Microsoft Entra ID に登録します。 このクイック スタートでは、 シングル ページ アプリケーション (SPA) の登録を使用します。これは、クライアント側の JavaScript アプリケーションやローカル開発に最適です。 後の手順で示す API 統合パターンは、任意のアプリケーションの種類で動作します。

バックエンド サーバーを使用する運用アプリケーションの場合は、別の登録の種類 (Web、ネイティブなど) を選択することを検討してください。 シナリオに適した方法の選択に関するガイダンスについては、「 アプリケーション認証の構成」 を参照してください。

シングルページ アプリケーションとして登録する

  1. Azure portal で Microsoft Entra ID に移動します。
  2. サイド パネルから [アプリの登録 ] を選択します。
  3. [新規登録] を選択します。
  4. アプリケーションの名前 ("GeoCatalog Web App" など) を入力します。
  5. [サポートされているアカウントの種類] で、 [この組織のディレクトリ内のアカウントのみ] を選択します。
  6. [ リダイレクト URI] で、[ シングルページ アプリケーション (SPA)] を選択し、開発 URL ( http://localhost:5173 など) を入力します。
  7. 登録 を選択します。

登録後、[ 概要 ] ページで次の値に注意してください。

  • アプリケーション (クライアント) ID
  • ディレクトリ (テナント) ID

詳細については、 クイック スタート アプリの登録を確認してください

API アクセス許可を付与する

アプリケーションには、サインインしているユーザーの代わりに GeoCatalog API を呼び出すアクセス許可が必要です。

  1. アプリの登録で、[API のアクセス許可] を選択します>アクセス許可を追加します
  2. 組織が使用する API を選択し、Azure Orbital Spatio を検索します。
  3. 委任されたアクセス許可 を選択し、user_impersonation をチェックします。
  4. アクセス許可の追加 を選択します。
  5. 管理者の場合は、[テナント内のすべてのユーザーに代わって 同意する管理者の同意を付与 する] を選択します。

アプリケーションの作成

アプリケーションには、次の構成値が必要です。 これらの値を指定する方法は、ビルド ツール (環境変数、構成ファイルなど) によって異なります。

コンフィギュレーション 価値 Description
カタログ URL https://{name}.{region}.geocatalog.spatio.azure.com GeoCatalog エンドポイント
テナント ID アプリの登録から あなたの Microsoft Entra テナント
クライアントID アプリの登録から アプリケーションのクライアント ID
API スコープ https://geocatalog.spatio.azure.com/.default 常にこの正確な値を使用する

依存関係のインストール

ブラウザー アプリケーションとマップ ライブラリ用の Microsoft Authentication Library (MSAL) をインストールします。

npm install @azure/msal-browser maplibre-gl
  • @azure/msal-browser - Microsoft Entra ID を使用して OAuth 2.0 認証を処理します
  • maplibre-gl - タイル視覚化用のオープン ソース マップ ライブラリ

ヒント

プロジェクト構造: このクイック スタートのコード サンプルは、必要に応じて整理できるスタンドアロン関数です。 一般的なパターン:

  • auth.js: MSAL 構成およびトークン関数
  • api.js: STAC API、Tiler API、SAS トークン関数
  • map.js: MapLibre の初期化とタイル レイヤーの管理
  • App.js または main.js: UI と一緒にすべてを結び付け

各関数は、依存関係 (アクセス トークン、URL) をパラメーターとして受け取り、任意のフレームワークまたはプロジェクト構造に簡単に統合できます。

MSAL 認証を実装する

サンプル Web アプリケーションの認証フローを示すスクリーンショット。

ブラウザー認証用に 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();
}

STAC API: コレクションと項目のクエリ

GeoCatalog STAC API は、地理空間データを検出およびクエリするためのエンドポイントを提供します。 すべての要求には、Authorizationから取得したベアラー トークンを含む ヘッダーが必要です。

コレクションの一覧表示

サンプル Web アプリケーションの STAC コレクションの一覧を示すスクリーンショット。

const API_VERSION = '2025-04-30-preview';

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
}

コレクション内のアイテムを一覧表示する

サンプル Web アプリケーションの STAC 項目の一覧を示すスクリーンショット。

const API_VERSION = '2025-04-30-preview';

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 = '2025-04-30-preview';

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 を作成する

GeoCatalog Tiler API は、ラスター データをマップ タイルとして提供します。 次のパターンでタイル URL を構築します。

1 つのアイテムのタイル

1 つの項目のタイルを表示する方法を示すスクリーンショット。

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

タイル URL ビルダー関数

const API_VERSION = '2025-04-30-preview';

/**
 * 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/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 バージョン (2025-04-30-preview)
tileMatrixSetId イエス Web マップに WebMercatorQuad を使用する
assets イエス レンダリングするアセット名 (例: visualimage)
colormap_name いいえ 名前付きカラーマップ (例: viridisterrain)
rescale いいえ スケーリングの値範囲 (例: 0,255)
asset_bidx いいえ バンド インデックス (例: RGB の image\|1,2,3 )

4 バンドの画像を含むコレクション (RGB + NIR を使用した NAIP など) の場合は、 asset_bidx=image|1,2,3 を使用して RGB バンドのみを選択します。 タイルは、4 つのバンドを 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 });
  }
}

Important

transformRequest関数は、すべてのタイル要求に対して呼び出されます。 transformRequestアクセスできる変数にアクセス トークンを格納し、トークンの更新時に更新します。


モザイク タイル: コレクション全体の画像を表示する

コレクションのモザイク タイルを表示する方法を示すスクリーンショット。

コレクション内のすべてのアイテムをシームレス レイヤーとして表示するには、モザイク検索を登録し、返された検索 ID を使用します。

const API_VERSION = '2025-04-30-preview';

/**
 * 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/${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 トークン: 生の資産をダウンロードする

SAS API は、Azure Blob Storage から直接生の資産ファイル (GeoTIF、COG、およびその他のファイル) をダウンロードするための時間制限付きトークンを提供します。 レンダリングされたタイルではなく元のソース ファイルが必要な場合は、このオプションを使用します。

Important

SAS トークンは、ブラウザー アプリケーション でのみダウンロード を有効にします。 Azure Blob Storage CORS ポリシーにより、ブラウザーは JavaScript fetch()経由で BLOB データを読み取ることはできません。 「 ブラウザーの制限事項」セクションを 参照してください。

SAS トークンを使用して資産をダウンロードする方法を示すスクリーンショット。

SAS トークンを取得する

const API_VERSION = '2025-04-30-preview';

/**
 * 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
ファイルをダウンロードする (ユーザーがリンクを選択) ✅ が機能している ✅ が機能している
fetch() を使用して BLOB データを読み取る ❌ CORS がブロックされました ✅ が機能している
JavaScript で生ピクセルを処理する ❌ 無理です ✅ が機能している

ナビゲーション (リンクをクリック) すると CORS がバイパスされるため、ブラウザーのダウンロードは機能します。 ただし、ストレージ アカウントにアプリケーションの配信元が CORS ポリシーに含まれていないため、Azure Blob Storage への fetch() 要求はブロックされます。

アプリケーションがブラウザーで生資産データを読み取って処理する必要がある場合は、サーバー側プロキシを実装します。

次のコードは、プロキシ パターンを示す簡略化された例です。 運用アプリケーションの場合、プロキシ エンドポイントは要求を認証し (たとえば、ユーザーの Bearer トークンを転送するか、セッション認証を使用して)、要求されたリソースへのアクセスがユーザーに承認されていることを検証する必要があります。

// ❌ 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) トークンを使用して BLOB を取得し、処理された結果を返すことが可能です。


開発に関する考慮事項

CORS サポート

GeoCatalog API には、 Access-Control-Allow-Origin: *を使用した完全な CORS サポートが含まれています。 ブラウザー ベースのアプリケーションは、開発中に http://localhost を含め、任意の配信元から GeoCatalog に直接要求できます。 プロキシや回避策は必要ありません。

API では CORS 要求で Authorization ヘッダーが許可されるため、認証された fetch() 呼び出しはブラウザーの JavaScript から直接機能します。

適切なデータ アクセス方法の選択

メソッド ブラウザー fetch() ブラウザーのダウンロード Server-side 最適な対象者
Tiler API ✅ が完全にサポートされている ✅ はい ✅ はい マップの視覚化
SAS トークン ❌ CORS がブロックされました ✅ はい ✅ はい 未加工ファイルのダウンロード
  • Tiler API: マップに画像を表示するために使用します。 完全な CORS をサポートするレンダリングされた PNG タイルまたは WebP タイルを返します。 「タイル URL」「マップの統合」を参照してください。

  • SAS トークン: 元のソース ファイル (GeoTIF、COG) のダウンロードに使用します。 ブラウザーのダウンロードは機能しますが、 fetch() は Azure Blob Storage CORS ポリシーによってブロックされます。 詳細と回避策については 、SAS トークン を参照してください。

トークンの更新

通常、アクセス トークンの有効期限は 1 時間後です。 アプリケーションでは、次のようにする必要があります。

  1. 新しいトークンを取得して、401 エラーを処理します。
  2. 期限切れのトークンを自動的に更新する MSAL のサイレント トークン取得を使用します。
  3. マップの transformRequestで使用されるトークン参照を更新します。

エラー処理

一般的なエラー シナリオを処理します。

HTTP の状態 原因 解決策
401 トークンの有効期限が切れているか無効です アクセス トークンを更新する
404 アイテムまたはコレクションが見つかりません ID が存在するかどうかを確認する
424 データ範囲外のタイル 予期される - 適切に処理する

トラブルシューティング

エラー 原因 解決策
"AADSTS50011: 応答 URL の不一致" コード内のリダイレクト URI が Microsoft Entra ID 登録と一致しない 開発 URL ( http://localhost:3000 など) を SPA リダイレクト URI としてアプリ登録に追加します
"無効なスコープ" エラー API スコープの代わりに GeoCatalog URL を使用する スコープとして https://geocatalog.spatio.azure.com/.default を使用する
401 タイル要求に対する未承認エラー 認証ヘッダーを含まないマップ ライブラリ transformRequest (MapLibre) を使用してベアラー トークンを追加します。トークンが最新であることを確認します
タイルがベースマップと一致しない 間違ったタイル マトリックス セット Web メルカトルプロジェクションに tileMatrixSetId=WebMercatorQuad を使用する (EPSG:3857)
"イメージをデコードできませんでした" 不適切な資産名、マルチバンド画像、またはデータ範囲外 有効な名前については item_assets を確認してください。RGB には asset_bidx=image\|1,2,3 を使用します。404/424 の外部カバレッジは予想されています。

次のステップ