使用七巧板和 Amazon Location Service - Amazon Location Service

本文為英文版的機器翻譯版本,如內容有任何歧義或不一致之處,概以英文版為準。

使用七巧板和 Amazon Location Service

七巧板是一個靈活的地圖引擎,設計用於從矢量瓷磚實時渲染 2D 和 3D 地圖。它可以與地圖禪設計的樣式和亞馬遜定 Location Service 地圖 API 提供的 HERE 瓷磚一起使用。本指南說明如何在基本的 HTML/JavaScript 應用程式中整合七巧板與 Amazon 位置,雖然在使用 React 和 Angular 等架構時,同樣的程式庫和技術也適用。

七巧板是建立在小冊子上,這是一個開放源代碼的圖 JavaScript 書館,用於移動友好的互動地圖。這意味著許多與葉子兼容的插件和控件也可以與七巧板一起使用。

使用來自 HERE 的地圖時,為與 Tlezen 架構搭配使用而建立的七巧板樣式與 Amazon 位置大致相容。其中包含:

  • 泡沫包裝-一個全功能的尋路風格與興趣點有用的圖標

  • 朱砂 — 一般地圖應用的經典外觀和首選

  • 筆芯 — 一種簡約的地圖風格,專為數據可視化覆蓋而設計,靈感來自 Stamen Design 的開創性碳粉樣式

  • Tron — TRON 視覺語言中規模轉換的探索

  • Walkabout — 一種以戶外為中心的風格,非常適合徒步旅行或外出

本指南說明如何使用稱為氣泡紙的七巧板樣式在基本 HTML/JavaScript 應用程式中整合七巧板與 Amazon 位置。此範例可作為上 Amazon 定 Location Service 範例儲存庫的一部分提供GitHub

雖然其他七巧板樣式最好配有對地形資訊進行編碼的點陣圖磚,但 Amazon Location 尚不支援此功能。

重要

以下教學中的七巧板樣式僅與使用該樣VectorHereContrast式設定的 Amazon 位置地圖資源相容。

構建應用程序:腳手架

該應用程序是一個 HTML 頁面,用 JavaScript 於在 Web 應用程序上構建地圖。創建一個 HTML 頁面(index.html)並創建地圖的容器:

  • 輸入具有 mapiddiv元素,以將地圖的維度應用於地圖檢視。

  • 尺寸會繼承自視埠。

<html> <head> <style> body { margin: 0; } #map { height: 100vh; /* 100% of viewport height */ } </style> </head> <body> <!-- map container --> <div id="map" /> </body> </html>

構建應用程序:新增相依性

新增下列相依性:

  • 傳單及其相關的 CSS。

  • 七巧板。

  • AWS 用於 JavaScript.

<!-- CSS dependencies --> <link rel="stylesheet" href="https://unpkg.com/leaflet@1.7.1/dist/leaflet.css" integrity="sha512-xodZBNTC5n17Xt2atTPuE1HxjVMSvLVW9ocqUKLsCC5CXdbqCmblAshOMAS6/keqq/sMZMZ19scR4PsZChSR7A==" 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 碼。

構建應用程序:組態

若要使用您的資源和認證來設定應用程式:

  1. 輸入資源的名稱和識別碼。

    // 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. 使用您在使用 map-步驟 2,設定驗證中建立的未驗證身分集區實例化認證提供者。由於這會使用一般 AWS SDK 工作流程以外的登入資料,因此工作階段會在小時後過期。

    // 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, });
  3. 雖然七巧板允許您覆蓋用於獲取圖塊的 URL,但它不包括攔截請求以便簽署請求的功能。

    若要解決此問題,sources.mapzen.url請使用綜合主機名稱覆寫指向 Amazon 位置amazon.location,該名稱將由服務工作線程處理。以下是使用氣泡包裝的場景配置的示例:

    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函數。這將提供的 JavaScript 代碼註冊在一個單獨的文件中,稱sw.js為服務工作線程控制index.html

登入資料會從 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偵聽message事件以獲取憑據和區域配置更改。它還通過監聽fetch事件充當代理服務器。 fetch將重寫以合amazon.location成主機名稱為目標的事件,以指定適當的 Amazon 位置 API,並使用 Amplify 核心簽署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()); }

構建應用程序:Map 初始化

要使地圖在頁面加載後顯示,您必須初始化映射。您可以選擇調整初始地圖位置,添加其他控件和覆蓋數據。

注意

您必須在應用程式或文件上為您使用的每個資料提供者提供文字標記或文字歸因。歸因字串包含在sources.esri.attribution和下的樣式描述元回應中sources.here.attribution keys

由於七巧板不會要求這些資源,而且只能與 HERE 的地圖相容,因此請使用「© 2020 這裡」。搭配資料供應商使用 Amazon 位置資源時,請務必閱讀服務條款與條件

/** * 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.htmlsw.js。這服務於本地主機上的應用程序:5000

以下是該index.html文件:

<!-- index.html --> <html> <head> <link rel="stylesheet" href="https://unpkg.com/leaflet@1.7.1/dist/leaflet.css" integrity="sha512-xodZBNTC5n17Xt2atTPuE1HxjVMSvLVW9ocqUKLsCC5CXdbqCmblAshOMAS6/keqq/sMZMZ19scR4PsZChSR7A==" 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)); });

此範例可作為上 Amazon 定 Location Service 範例儲存庫的一部分提供GitHub