Amazon Location Service로 Tangram 사용 - Amazon Location Service

기계 번역으로 제공되는 번역입니다. 제공된 번역과 원본 영어의 내용이 상충하는 경우에는 영어 버전이 우선합니다.

Amazon Location Service로 Tangram 사용

Tangram은 벡터 타일에서 2D 및 3D 맵을 실시간으로 렌더링하도록 설계된 유연한 매핑 엔진입니다. Mapzen 설계 스타일 및 Amazon Location Service Maps API에서 제공하는 HERE 타일과 함께 사용할 수 있습니다. 이 가이드에서는 기본 HTML/ JavaScript 애플리케이션 내에서 Tangram을 Amazon Location과 통합하는 방법을 설명합니다. 하지만 React 및 Angular와 같은 프레임워크를 사용할 때도 동일한 라이브러리와 기술이 적용됩니다.

Tangram은 모바일 친화적인 대화형 지도를 위한 오픈 소스 라이브러리인 Leaflet을 기반으로 구축되었습니다. JavaScript 즉, 많은 Leaflet 호환 플러그인 및 컨트롤이 Tangram에서도 작동합니다.

Tilezen 스키마와 함께 작동하도록 구축된 Tangram 스타일은 HERE의 맵을 사용할 때 Amazon Location과 대부분 호환됩니다. 다음이 포함됩니다.

  • Bubble Wrap – 모든 기능을 갖춘 길 찾기 스타일로, 관심 지점을 표시하는 유용한 아이콘이 포함되어 있습니다.

  • Cinnabar – 클래식한 디자인으로 일반 매핑 애플리케이션에 적합합니다.

  • Refill – Stamen Design의 획기적인 Toner 스타일에서 영감을 받아 데이터 시각화 오버레이를 위해 디자인된 미니멀한 맵 스타일입니다.

  • Tron – TRON의 시각적 언어를 활용한 스케일 변환에 대한 탐구

  • Walkabout – 야외 활동에 초점을 맞춘 스타일로 하이킹이나 야외 활동에 안성맞춤입니다.

이 가이드에서는 버블 랩이라는 탱그램 스타일을 사용하여 기본 HTML/ JavaScript 애플리케이션 내에서 Tangram을 Amazon Location과 통합하는 방법을 설명합니다. 이 샘플은 의 Amazon Location Service 샘플 리포지토리의 일부로 제공됩니다 GitHub.

다른 Tangram 스타일은 지형 정보를 인코딩하는 래스터 타일을 사용하는 것이 가장 좋지만, Amazon Location에서는 아직 이 기능을 지원하지 않습니다.

중요

다음 튜토리얼의 Tangram 스타일은 VectorHereContrast 스타일로 구성된 Amazon Location 맵 리소스와만 호환됩니다.

애플리케이션 빌드: 스캐폴딩

애플리케이션은 웹 애플리케이션에 맵을 구축하는 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를 위한 것입니다. 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. 맵 사용 - 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, });
  3. 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 함수를 만듭니다. 이렇게 하면 제공된 JavaScript 코드가 서비스 워커 index.html 제어라는 sw.js 별도의 파일에 등록됩니다.

보안 인증 정보는 Amazon Cognito에서 로드되고 리전과 함께 서비스 작업자로 전달되어 Signature Version 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 이벤트를 수신하여 프록시 서버 역할을 합니다. 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를 지원하는 호스트를 사용합니다.

  • 로컬 웹 서버를 사용하여 서비스 작업자 보안 제한 사항을 준수합니다.

로컬 웹 서버를 사용하려면 npx를 사용할 수 있습니다. npx는 Node.js 일부로 설치되기 때문입니다. index.htmlsw.js와 동일한 디렉터리 내에서 npx serve를 사용할 수 있습니다. 이는 localhost: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.