Tangram
Tangram は、モバイルフレンドリーなインタラクティブマップ用のオープンソースの JavaScript ライブラリである Leaflet
Tilezen スキーマ
-
Bubble Wrap
— 興味のある地点をわかりやすく表示する便利なアイコンが付いた、フル機能の道案内スタイル -
Cinnabar
— クラシックな外観で、一般的なマップアプリ向け -
Refill
— Stamen Design の独創的な Toner スタイルにインスパイアされた、データ視覚化オーバーレイ用にデザインされたミニマルなマップスタイル -
Tron
— TRON のビジュアル言語によるスケール返還を探求したスタイル -
Walkabout
— ハイキングや外出に最適なアウトドア重視のスタイル
このガイドでは、Bubble Wrap
他の Tangram スタイルには地形情報をエンコードするラスタータイルが最適ですが、この機能はまだ Amazon Location ではサポートされていません。
重要
以下のチュートリアルの Tangram スタイルは、VectorHereContrast
スタイルで設定された Amazon Location マップリソースとのみ互換性があります。
アプリケーションの構築: Scaffolding
このアプリケーションは、Web アプリケーション上にマップを構築するための JavaScript を備えた HTML ページです。マップのコンテナを含む HTML ページ (index.html
) を作成します。
-
マップの
id
が付いたdiv
要素を入力し、マップの寸法をマップビューに適用します。 -
寸法はビューポートから継承されます。
<html>
<head>
<style>
body {
margin: 0;
}
#map {
height: 100vh; /* 100% of viewport height */
}
</style>
</head>
<body>
<!-- map container -->
<div id="map" />
</body>
</html>
アプリケーションの構築: 依存関係の追加
以下の依存関係を追加します。
-
Leaflet とそれに関連する CSS。
-
Tangram。
-
AWS SDK for JavaScript。
<!-- CSS dependencies -->
<link
rel="stylesheet"
href="https://unpkg.com/leaflet@1.7.1/dist/leaflet.css"
crossorigin=""
/>
<!-- JavaScript dependencies -->
<script src="https://unpkg.com/leaflet@1.7.1/dist/leaflet.js"></script>
<script src="https://unpkg.com/tangram"></script>
<script src="https://sdk.amazonaws.com/js/aws-sdk-2.784.0.min.js"></script>
<script>
// application-specific code
</script>
これにより、必要な前提条件を含む空のページが作成されます。次のステップでは、アプリケーションの JavaScript コードを作成する手順を説明します。
アプリケーションの構築: 設定
リソースと認証情報を使用してアプリケーションを設定します。
-
リソースの名前と ID を入力します。
// Cognito Identity Pool ID const identityPoolId = "
us-east-1:54f2ba88-9390-498d-aaa5-0d97fb7ca3bd
"; // Amazon Location Service map name; must be HERE-backed const mapName = "TangramExampleMap
"; -
「マップを使用する - ステップ 2. 認証を設定する」で作成した認証されていない アイデンティティプールを使用して、認証情報プロバイダーをインスタンス化します。通常の AWS SDK ワークフローではない認証情報を使用するため、セッションの有効期限は 1 時間となっています。
// extract the region from the Identity Pool ID; this will be used for both Amazon Cognito and Amazon Location AWS.config.region = identityPoolId.split(":", 1)[0]; // instantiate a Cognito-backed credential provider const credentials = new AWS.CognitoIdentityCredentials({ IdentityPoolId:
identityPoolId
, }); -
Tangram ではタイルの取得に使用する URL を上書きできますが、リクエストをインターセプトして署名する機能は含まれていません。
その解決策として、合成ホスト名
amazon.location
を使用して Amazon Location を指すようにsources.mapzen.url
をオーバーライドします。このホスト名はサービスワーカーが処理します。以下は、Bubble Wrap を使用したシーン設定の例です。 const scene = { import: [ // Bubble Wrap style "https://www.nextzen.org/carto/bubble-wrap-style/10/bubble-wrap-style.zip", "https://www.nextzen.org/carto/bubble-wrap-style/10/themes/label-7.zip", "https://www.nextzen.org/carto/bubble-wrap-style/10/themes/bubble-wrap-road-shields-usa.zip", "https://www.nextzen.org/carto/bubble-wrap-style/10/themes/bubble-wrap-road-shields-international.zip", ], // override values beneath the `sources` key in the style above sources: { mapzen: { // point at Amazon Location using a synthetic URL, which will be handled by the service // worker url: `https://amazon.location/${mapName}/{z}/{x}/{y}`, }, // effectively disable raster tiles containing encoded normals normals: { max_zoom: 0, }, "normals-elevation": { max_zoom: 0, }, }, };
アプリケーションの構築: リクエスト変換
サービスワーカーを登録して初期化するには、マップが初期化される前に呼び出される registerServiceWorker
関数を作成します。これにより、サービスワーカーが index.html
をコントロールし、sw.js
と呼ばれる別のファイルに提供される JavaScript コードが登録されます。
認証情報は Amazon Cognito から読み込まれ、リージョンと一緒にサービスワーカーに渡されます。この情報は、署名バージョン 4 でタイルリクエストに署名するために使われます。
/**
* Register a service worker that will rewrite and sign requests using Signature Version 4.
*/
async function registerServiceWorker() {
if ("serviceWorker" in navigator) {
try {
const reg = await navigator.serviceWorker.register("./sw.js");
// refresh credentials from Amazon Cognito
await credentials.refreshPromise();
await reg.active.ready;
if (navigator.serviceWorker.controller == null) {
// trigger a navigate event to active the controller for this page
window.location.reload();
}
// pass credentials to the service worker
reg.active.postMessage({
credentials: {
accessKeyId: credentials.accessKeyId
,
secretAccessKey: credentials.secretAccessKey
,
sessionToken: credentials.sessionToken
,
},
region: AWS.config.region,
});
} catch (error) {
console.error("Service worker registration failed:", error);
}
} else {
console.warn("Service worker support is required for this example");
}
}
sw.js
の Service Worker は、認証情報やリージョンの設定変更を拾うために message
のイベントを待ち受けるよう実装されています。また、fetch
のイベントを監視することでプロキシサーバーとしても機能します。amazon.location
合成ホスト名をターゲットとする fetch
イベントは、適切な Amazon Location API を対象とするように書き替えられ、Amplify Core の Signer
を使用して署名されます。
// sw.js
self.importScripts(
"https://unpkg.com/@aws-amplify/core@3.7.0/dist/aws-amplify-core.min.js"
);
const { Signer } = aws_amplify_core;
let credentials;
let region;
self.addEventListener("install", (event) => {
// install immediately
event.waitUntil(self.skipWaiting());
});
self.addEventListener("activate", (event) => {
// control clients ASAP
event.waitUntil(self.clients.claim());
});
self.addEventListener("message", (event) => {
const {
data: { credentials: newCredentials, region: newRegion },
} = event;
if (newCredentials != null) {
credentials = newCredentials;
}
if (newRegion != null) {
region = newRegion;
}
});
async function signedFetch(request) {
const url = new URL(request.url);
const path = url.pathname.slice(1).split("/");
// update URL to point to Amazon Location
url.pathname = `/maps/v0/maps/${path[0]}/tiles/${path.slice(1).join("/")}`;
url.host = `maps.geo.${region}.amazonaws.com`;
// strip params (Tangram generates an empty api_key param)
url.search = "";
const signed = Signer.signUrl(url.toString(), {
access_key: credentials.accessKeyId,
secret_key: credentials.secretAccessKey,
session_token: credentials.sessionToken,
});
return fetch(signed);
}
self.addEventListener("fetch", (event) => {
const { request } = event;
// match the synthetic hostname we're telling Tangram to use
if (request.url.includes("amazon.location")) {
return event.respondWith(signedFetch(request));
}
// fetch normally
return event.respondWith(fetch(request));
});
認証情報を自動的に更新し、有効期限が切れる前にサービスワーカーに送信するには、index.html
で以下の関数を使用します。
async function refreshCredentials() {
await credentials.refreshPromise();
if ("serviceWorker" in navigator) {
const controller = navigator.serviceWorker.controller;
controller.postMessage({
credentials: {
accessKeyId: credentials.accessKeyId,
secretAccessKey: credentials.secretAccessKey,
sessionToken: credentials.sessionToken,
},
});
} else {
console.warn("Service worker support is required for this example.");
}
// schedule the next credential refresh when they're about to expire
setTimeout(refreshCredentials, credentials.expireTime - new Date());
}
アプリケーションの構築: マップの初期化
ページが読み込まれた後にマップを表示するには、マップを初期化する必要があります。マップの初期位置を調整したり、コントロールを追加したり、データをオーバーレイしたりするオプションがあります。
注記
使用する各データプロバイダーのワードマークまたはテキストアトリビューションを、アプリケーションまたはドキュメントに記載する必要があります。アトリビューション文字列は、スタイル記述子のレスポンスの sources.esri.attribution
、sources.here.attribution
、source.grabmaptiles.attribution
キーに含まれています。
Tangram はこれらのリソースをリクエストせず、HERE のマップとのみ互換性があるため、「© 2020 HERE」を使用してください。Amazon Location リソースをデータプロバイダーと併用する場合は、サービスの利用規約
/**
* Initialize a map.
*/
async function initializeMap() {
// register the service worker to handle requests to https://amazon.location
await registerServiceWorker();
// Initialize the map
const map = L.map("map").setView([49.2819, -123.1187], 10);
Tangram.leafletLayer({
scene,
}).addTo(map);
map.attributionControl.setPrefix("");
map.attributionControl.addAttribution("© 2020 HERE");
}
initializeMap();
アプリケーションを実行する
このサンプルを実行するには、次の方法があります。
-
HTTPS をサポートするホストを使用する。
-
ローカル Web サーバーを使用して、サービスワーカーのセキュリティ制限に準拠する。
ローカル Web サーバーで使用するには、npx を使用できます。Node.js の一部としてインストールされるためです。npx serve
は、index.html
および sw.js
と同じディレクトリ内から使用できます。これによってアプリケーションに localhost:5000
以下は、index.html
ファイルです。
<!-- index.html -->
<html>
<head>
<link
rel="stylesheet"
href="https://unpkg.com/leaflet@1.7.1/dist/leaflet.css"
crossorigin=""
/>
<style>
body {
margin: 0;
}
#map {
height: 100vh;
}
</style>
</head>
<body>
<div id="map" />
<script src="https://unpkg.com/leaflet@1.7.1/dist/leaflet.js"></script>
<script src="https://unpkg.com/tangram"></script>
<script src="https://sdk.amazonaws.com/js/aws-sdk-2.784.0.min.js"></script>
<script>
// configuration
// Cognito Identity Pool ID
const identityPoolId = "<Identity Pool ID>";
// Amazon Location Service Map name; must be HERE-backed
const mapName = "<Map name>";
AWS.config.region = identityPoolId.split(":")[0];
// instantiate a credential provider
credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId: identityPoolId,
});
const scene = {
import: [
// Bubble Wrap style
"https://www.nextzen.org/carto/bubble-wrap-style/10/bubble-wrap-style.zip",
"https://www.nextzen.org/carto/bubble-wrap-style/10/themes/label-7.zip",
"https://www.nextzen.org/carto/bubble-wrap-style/10/themes/bubble-wrap-road-shields-usa.zip",
"https://www.nextzen.org/carto/bubble-wrap-style/10/themes/bubble-wrap-road-shields-international.zip",
],
// override values beneath the `sources` key in the style above
sources: {
mapzen: {
// point at Amazon Location using a synthetic URL, which will be handled by the service
// worker
url: `https://amazon.location/${mapName}/{z}/{x}/{y}`,
},
// effectively disable raster tiles containing encoded normals
normals: {
max_zoom: 0,
},
"normals-elevation": {
max_zoom: 0,
},
},
};
/**
* Register a service worker that will rewrite and sign requests using Signature Version 4.
*/
async function registerServiceWorker() {
if ("serviceWorker" in navigator) {
try {
const reg = await navigator.serviceWorker.register("./sw.js");
// refresh credentials from Amazon Cognito
await credentials.refreshPromise();
await reg.active.ready;
if (navigator.serviceWorker.controller == null) {
// trigger a navigate event to active the controller for this page
window.location.reload();
}
// pass credentials to the service worker
reg.active.postMessage({
credentials: {
accessKeyId: credentials.accessKeyId,
secretAccessKey: credentials.secretAccessKey,
sessionToken: credentials.sessionToken,
},
region: AWS.config.region,
});
} catch (error) {
console.error("Service worker registration failed:", error);
}
} else {
console.warn("Service Worker support is required for this example");
}
}
/**
* Initialize a map.
*/
async function initializeMap() {
// register the service worker to handle requests to https://amazon.location
await registerServiceWorker();
// Initialize the map
const map = L.map("map").setView([49.2819, -123.1187], 10);
Tangram.leafletLayer({
scene,
}).addTo(map);
map.attributionControl.setPrefix("");
map.attributionControl.addAttribution("© 2020 HERE");
}
initializeMap();
</script>
</body>
</html>
以下は、sw.js
ファイルです。
// sw.js
self.importScripts(
"https://unpkg.com/@aws-amplify/core@3.7.0/dist/aws-amplify-core.min.js"
);
const { Signer } = aws_amplify_core;
let credentials;
let region;
self.addEventListener("install", (event) => {
// install immediately
event.waitUntil(self.skipWaiting());
});
self.addEventListener("activate", (event) => {
// control clients ASAP
event.waitUntil(self.clients.claim());
});
self.addEventListener("message", (event) => {
const {
data: { credentials: newCredentials, region: newRegion },
} = event;
if (newCredentials != null) {
credentials = newCredentials;
}
if (newRegion != null) {
region = newRegion;
}
});
async function signedFetch(request) {
const url = new URL(request.url);
const path = url.pathname.slice(1).split("/");
// update URL to point to Amazon Location
url.pathname = `/maps/v0/maps/${path[0]}/tiles/${path.slice(1).join("/")}`;
url.host = `maps.geo.${region}.amazonaws.com`;
// strip params (Tangram generates an empty api_key param)
url.search = "";
const signed = Signer.signUrl(url.toString(), {
access_key: credentials.accessKeyId,
secret_key: credentials.secretAccessKey,
session_token: credentials.sessionToken,
});
return fetch(signed);
}
self.addEventListener("fetch", (event) => {
const { request } = event;
// match the synthetic hostname we're telling Tangram to use
if (request.url.includes("amazon.location")) {
return event.respondWith(signedFetch(request));
}
// fetch normally
return event.respondWith(fetch(request));
});
このサンプルは、GitHub