Uso de Tangram con Amazon Location Service - Amazon Location Service

Las traducciones son generadas a través de traducción automática. En caso de conflicto entre la traducción y la version original de inglés, prevalecerá la version en inglés.

Uso de Tangram con Amazon Location Service

Tangram es un motor de mapeo flexible, diseñado para la representación en tiempo real de mapas 2D y 3D a partir de mosaicos vectoriales. Se puede usar con los estilos diseñados por Mapzen y los mosaicos HERE proporcionados por la API de mapas de Amazon Location Service. Esta guía describe cómo integrar Tangram con Amazon Location en una JavaScript aplicación HTML/ básica, aunque las mismas bibliotecas y técnicas también se aplican cuando se utilizan marcos como React y Angular.

Tangram se basa en Leaflet, una biblioteca de código abierto JavaScript para mapas interactivos aptos para dispositivos móviles. Esto significa que muchos complementos y controles compatibles con Leaflet también funcionan con Tangram.

Los estilos de Tangram diseñados para funcionar con el esquema de Tilezen son en gran medida compatibles con Amazon Location cuando se utilizan mapas de HERE. Entre ellos se incluyen:

  • Bubble Wrap: un estilo de orientación con todas las funciones e íconos útiles para los puntos de interés

  • Cinnabar: un diseño clásico, ideal para aplicaciones generales de cartografía

  • Refill: un estilo de mapa minimalista diseñado para superposiciones de visualización de datos, inspirado en el popular estilo de Toner de Stamen Design

  • Tron: una exploración de las transformaciones de escala en el lenguaje visual de TRON

  • Walkabout: un estilo centrado en actividades al aire libre que es perfecto para practicar senderismo o salir a pasear

Esta guía describe cómo integrar Tangram con Amazon Location en una JavaScript aplicación HTML/básica utilizando el estilo Tangram llamado Bubble Wrap. Este ejemplo está disponible como parte del repositorio de muestras de Amazon Location Service en GitHub.

Si bien otros estilos de Tangram se combinan mejor con mosaicos ráster, que codifican la información del terreno, Amazon Location aún no admite esta función.

importante

Los estilos Tangram del siguiente tutorial sólo son compatibles con los recursos de mapa de Amazon Location configurados con el estilo VectorHereContrast.

Creación de la aplicación: andamiaje

La aplicación es una página HTML con la JavaScript que crear el mapa en su aplicación web. Cree una página HTML (index.html) y cree el contenedor del mapa:

  • Introduzca un elemento div con un id de mapa para aplicar las dimensiones del mapa a la vista del mapa.

  • Las dimensiones se heredan de la ventana de visualización.

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

Creación de la aplicación: adición de dependencias

Agregue la siguiente dependencia:

  • Leaflet y su CSS asociado.

  • Tangram.

  • AWS SDK para 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>

Esto crea una página vacía con los requisitos previos necesarios. El siguiente paso le guiará a través de la escritura del JavaScript código de su aplicación.

Creación de la aplicación: configuración

Para configurar su aplicación con sus recursos y credenciales:

  1. Introduzca los nombres e identificadores de sus recursos.

    // 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. Cree una instancia de un proveedor de credenciales mediante el grupo de identidades no autenticadas que creó en Uso de mapas: paso 2, configurar la autenticación. Como se utilizan credenciales ajenas al flujo de trabajo normal del SDK de AWS, las sesiones caducan al cabo de una hora.

    // 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. Si bien Tangram le permite anular las URL utilizadas para recuperar los mosaicos, no incluye la posibilidad de interceptar las solicitudes para firmarlas.

    Para solucionar este problema, anule sources.mapzen.url para que apunte a Amazon Location con un nombre de host sintético amazon.location, que será gestionado por un empleado del servicio. El siguiente es un ejemplo de configuración de escena que usa 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, }, }, };

Creación de la aplicación: solicite la transformación

Para registrar e inicializar el service worker, cree una función registerServiceWorker a la que se pueda llamar antes de inicializar el mapa. Esto registra el JavaScript código proporcionado en un archivo independiente sw.js denominado service worker controllerindex.html.

Las credenciales se cargan desde Amazon Cognito y se transmiten al empleado del servicio junto con la región para proporcionar la información necesaria para firmar las solicitudes de mosaicos con la versión 4 de Signature.

/** * 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"); } }

La implementación de Service Worker en sw.js escucha los message eventos para detectar los cambios en la configuración de las credenciales y la región. También actúa como un servidor proxy al fetch escuchar los eventos. fetchlos eventos que se amazon.location dirijan al nombre de host sintético se reescribirán para que se dirijan a la API Amazon Location correspondiente y se firmarán con 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)); });

Para renovar automáticamente las credenciales y enviárselas al empleado del servicio antes de que caduquen, utilice la siguiente función: 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()); }

Creación de la aplicación: inicialización del mapa

Para que el mapa se muestre después de cargar la página, debe inicializarlo. Tiene la opción de ajustar la ubicación inicial del mapa, agregar controles adicionales y superponer datos.

nota

Debe proporcionar una marca denominativa o una atribución de texto para cada proveedor de datos que utilice, ya sea en su solicitud o en su documentación. Los string de atribución se incluyen en la respuesta del descriptor de estilo, debajo de las claves sources.esri.attribution, sources.here.attribution, y source.grabmaptiles.attribution.

Como Tangram no solicita estos recursos y solo es compatible con los mapas de HERE, use “© 2020 HERE”. Cuando utilice los recursos de Amazon Location con proveedores de datos, asegúrese de leer los términos y condiciones del servicio.

/** * 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();

Ejecución de la aplicación

Para ejecutar este ejemplo, puede:

  • Utilizar un host que admita HTTPS,

  • Utilizar un servidor web local para cumplir con las restricciones de seguridad de los trabajadores del servicio.

Para usar un servidor web local, puede usar npx, porque está instalado como parte de Node.js. Puede usar npx serve desde el mismo directorio que index.html y sw.js. Esto sirve a la aplicación en localhost:5000.

El archivo es el siguiente: 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>

El archivo es el siguiente: 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)); });

Este ejemplo está disponible como parte del repositorio de muestras de Amazon Location Service en GitHub.