Aplique estilos basado en los datos y mucho más en la última actualización del SDK Web de Azure Maps

Publicado el 24 septiembre, 2018

Principal Technical Program Manager, Azure Maps

Nos complace anunciar la última actualización del SDL Web de Azure Maps. El equipo ha trabajado mucho durante el verano para agregar muchas características eficaces y mejorar el rendimiento general del SDK Web de Maps. Algunas mejoras clave que se agregaron en esta actualización son las siguientes:

  • Mejoras en la API centradas en los desarrolladores
  • Nuevo modelo de capas y origen de datos
  • Conectar diversas capas a un origen de datos
  • Facilitar la administración de la clase de forma
  • Estilo de las capas basado en datos
  • Biblioteca de matemáticas espacial
  • Soporte para círculos geoespacialmente precisos

Mejoras en la API centradas en los desarrolladores

Hemos mejorado nuestra interfaz de la API para que sea más intuitiva y obtener un mejor rendimiento. Antes, la mayoría de las funcionalidades del SDK se exponían como funciones en la clase Maps. Sin contar con las funciones relacionadas reunidas, a menudo los desarrolladores tenían que examinar casi cada función y propiedad en el SDK para encontrar la función que necesitaban. Con esta versión, hemos agrupado muchas funcionalidades relacionadas y se exponen a través de las propiedades del administrador en la clase Maps. En la última versión, había más de 30 funciones en la clase Maps, de las cuales siete estaban relacionadas con las capas de datos. En esta actualización, estas funcionalidades ahora se exponen a través de la propiedad “capas” del mapa junto con muchas funcionalidades adicionales que permiten trabajar más fácilmente con capas. También agregamos propiedades de administradores al mapa con controles, eventos, sprite de imágenes del mapa, marcadores HTML y orígenes de datos.

Asimismo, también agregamos la opción para establecer muchos parámetros predeterminados del SDK del mapa directamente en el espacio de nombres de la raíz del atlas del SDK. Gracias a esto, todas las instancias del mapa que se creen en una página y cualquier cliente de servicios creado usarán esta configuración como predeterminada. Anteriormente, cada instancia del mapa y cliente de servicios agregado a su aplicación debía entregar la clave de Azure Maps en la inicialización de esa característica. Esto significaba que tendría que haber establecido la misma configuración muchas veces en su aplicación, lo que puede ser engorroso. Al establecer su clave en el espacio de nombres de la raíz del atlas, solo debe especificarla una vez en la aplicación, lo que facilita las actualizaciones futuras, si son necesarias.

atlas.setSubscriptionKey('Your Azure Maps Key');

Se pueden establecer otras configuraciones, como la información de idioma o región del usuario, como predeterminadas en el espacio de nombres de la raíz del atlas. Cabe destacar que estos son valores predeterminados que se usarán si la opción equivalente no está especificada al iniciar una instancia del mapa o clientes de servicio. Por ejemplo, si indica otro idioma cuando inicia el mapa, se usará ese idioma en su lugar. Esto es útil en escenarios donde puede querer mostrar diversos mapas en una página en distintos idiomas.

Todas estas mejoras destinadas a los desarrolladores permiten desarrollar aplicaciones mucho más fácilmente con Azure Maps mientras que, a su vez, mejoran el rendimiento y reducen la cantidad de código necesario para escribir su aplicación.

Nuevo modelo de capas y origen de datos

Antes, el control del mapa solo brindaba la capacidad de agregar al mapa datos espaciales del vector que estaba en formato GeoJSON. Estos datos se agregaban a una capa que creaba y administraba su propio origen de datos tras bambalinas. Modificar cualquier dato en una capa requería sobrescribir la capa existente, lo que es poco eficaz y necesita código que no es intuitivo, como puede ver en el ejemplo de código que aparece a continuación.

var myPins = [/* Array filled with pin data */];

//Agrega chinchetas al mapa.
map.addPins(pins, {
     name: 'MyPinLayer'
});

//Crea una nueva chincheta para agregar a la capa.
var pin = new atlas.data.Feature(new atlas.data.Point(lon, lat));

//Agrega la chincheta a una matriz de chinchetas.
myPins.push(pin);

//Actualiza la capa al sobrescribir todos los datos en la capa. Esto no es intuitivo y afecta el rendimiento.
map.addPins(pins, {
     name: 'MyPinLayer',  
     overwrite: true
});

Con esta versión, separamos el origen de datos de la capa, lo que ofrece muchos beneficios, como la capacidad de representar un solo origen de datos con múltiples capas mientras solo se mantiene una sola instancia del origen de datos. A su vez, esto disminuye el uso de memoria, mejora el rendimiento y crea una API que es mucho más fácil de comprender, como puede ver a continuación.

//Crea un origen de datos y lo agrega al mapa.
var dataSource = new atlas.source.DataSource();
map.sources.add(dataSource);

//Crea una capa que define cómo representar las formas en el origen de datos y agregarlas al mapa.
var myPinLayer = new atlas.layer.SymbolLayer(dataSource);
map.layers.add(myPinLayer);

//Agrega chinchetas al origen de datos.
dataSource.add([/* Matriz rellenada con datos de la chincheta */]);

//Crea una nueva chincheta para agregar al mapa.
var pin = new atlas.data.Feature(atlas.data.Point([lon, lat]));

//Agrega la chincheta al origen de datos; el mapa se actualiza automáticamente de la forma más eficaz posible.
dataSource.add(pin);

Además de tener la clase DataSource para los datos con formato GeoJSON, también añadimos soporte para los servicios de mosaicos de vector mediante una nueva clase VectorTileSource. Estos orígenes de datos pueden adjuntarse a las siguientes capas, que definen cómo se representan los datos en el mapa.

  • Capa de burbuja: representa los datos de puntos como círculos a escala mediante un radio de píxeles.
  • Capa de línea: representa esquemas de líneas y polígonos.
  • Capa de polígono: representa el área rellena de polígonos.
  • Capa de símbolo: representa los datos del punto como íconos y texto.

Además, existe la clase TileLayer, que le permite superponer imágenes de trama de mosaicos en la parte superior del mapa. En lugar de adjuntar esta capa a un origen de datos, la información del servicio de mosaicos se especifica como opciones de la capa.

Al crear este nuevo modelo de capas y origen de datos en el SDK, también más que duplicamos las características funcionales y opciones de representación para visualizar datos en el mapa.

Conectar diversas capas a un origen de datos

Como se mencionó anteriormente, ahora puede adjuntar varias capas al mismo origen de datos. Esto puede sonar extraño, pero hay diversos escenarios en los que esto resulta útil. Por ejemplo, tomemos el escenario en el que crea la experiencia de dibujo de un polígono. Cuando se le permite a un usuario dibujar un polígono, debemos representar el área de relleno del polígono a media que el usuario agrega puntos al mapa. Agregar una línea con estilo que defina el polígono hará que sea más fácil ver los bordes del polígono mientras se dibuja. Finalmente, agregar algún tipo de manipulador, como un marcador o una chincheta, sobre cada posición en el polígono permitiría editar más fácil cada posición. En la siguiente imagen se muestra este escenario.

Conectar varias capas a un origen de datos

Para lograr este objetivo en la mayoría de las plataformas de mapas, tendría que crear un objeto de polígono, un objeto de línea y una chincheta para cada posición en el polígono. A medida que le polígono se modifica, tendría que actualizar manualmente las líneas y las chinchetas. Rápidamente, el trabajo necesario para hacer esto se vuelve complejo.

Con Azure Maps, lo único que necesita es un solo polígono en un origen de datos, como se muestra en el siguiente código.

//Crea un origen de datos y lo agrega al mapa.
var dataSource = new atlas.source.DataSource();
map.sources.add(dataSource);

//Crea un polígono y lo agrega al origen de datos.
dataSource.add(new atlas.data.Polygon([[[/* Coordinates for polygon */]]]));

//Crea una capa de polígono para representar el área llena del polígono.
var polygonLayer = new atlas.layer.PolygonLayer(dataSource, 'myPolygonLayer', {
     fillColor: 'rgba(255,165,0,0.2)'
});

//Crea una capa de línea para un mayor control de la representación del contorno del polígono.
var lineLayer = new atlas.layer.LineLayer(dataSource, 'myLineLayer', {
     color: 'orange',
     width: 2
});

//Crea una capa de burbuja para representar los vértices del polígono como círculos a escala.
var bubbleLayer = new atlas.layer.BubbleLayer(dataSource, 'myBubbleLayer', {
     color: 'orange',
     radius: 5,
     outlineColor: 'white',
     outlineWidth: 2
});

//Agrega todas las capas al mapa.
map.layers.add([polygonLayer, lineLayer, bubbleLayer]);

Vea ejemplos en vivo.

Facilitar la administración de la clase Shape

Todos los datos basados en vectores en el SDK Web de Azure Maps consisten en objetos GeoJSON, los que al final del día son solo objetos JSON que siguen un esquema definido. Una limitación del uso de datos de GeoJSON es que si modifica los datos, el mapa no reconoce el cambio hasta que elimina y reemplaza el objeto en el mapa. Para que todo sea más fácil e intuitivo, hemos agregado una nueva clase Shape que puede encapsular cualquier característica o geometría de GeoJSON. Esta clase ofrece diversas funciones que facilitan la actualización de datos GeoJSON y refleja esos cambios de forma instantánea en el origen de datos al que se agregó la forma. Nos percatamos de que esto resulta tan útil que automáticamente encapsulamos todos los objetos GeoJSON añadidos a la clase DataSource.

Tome, por ejemplo, el escenario donde desea actualizar la posición de un punto de datos en el mapa. Anteriormente, tenía que administrar los datos en la capa de forma independiente y, luego, sobrescribir la capa, como se muestra en el siguiente código.

//Crea una chincheta desde una característica de punto.
var pin = new atlas.data.Feature(new atlas.data.Point([-110, 45]));

//Agrega una chincheta al mapa.
map.addPins([pin], {
     name: 'MyPinLayer'
});

//Actualiza las coordenadas de las chinchetas... El mapa no se actualiza.
pin.geometry.coordinates = [-120, 30];

//Sobrescribe todas las chinchetas en la capa para actualizar el mapa.
map.addPins([pin], {
     name: 'MyPinLayer',
     overwrite: true
});

Esto no es intuitivo y más engorroso de lo que debería ser. Al encapsular el punto de datos con la clase de forma, solo se necesita una línea de código para actualizar la posición del punto de datos en el mapa, como se muestra en el siguiente código.

//Crea un origen de datos y lo agrega al mapa.
var dataSource = new atlas.source.DataSource();
map.sources.add(dataSource);

//Crea una capa que define cómo representar las formas en el origen de datos y agregarlas al mapa.
var myPinLayer = new atlas.layer.SymbolLayer(dataSource);
map.layers.add(myPinLayer);

//Crea una chincheta y un encapsulado con la clase Shape y los agrega al origen de datos.
var pin = new atlas.Shape(new atlas.data.Point([-110, 45]));
dataSource.add(pin);

//Actualiza las coordenadas de la chincheta; el mapa se actualiza automáticamente.
pin.setCoordinates([-120, 30]);

Sugerencia: Puede recuperar fácilmente la versión encapsulada de la forma de sus datos desde el origen de datos en lugar de encapsular cada objeto de forma individual.

Estilo de las capas basado en datos

Una nueva característica clave de esta actualización son las nuevas funcionalidades de estilo basado en datos con funciones de propiedad. Esto le permite agregar lógica comercial a las opciones individuales de estilo, que toma en cuenta las propiedades definidas en cada forma individual en el origen de datos adjunto. El nivel de zoom también se puede tener en cuenta cuando se representa la capa. Los estilos basados en datos pueden disminuir bastante la cantidad de código que necesitaría habitualmente para escribir y definir este tipo de lógica comercial al usar instrucciones “if” y supervisar eventos del mapa.

Como ejemplo, tenga en cuenta los datos de los terremotos. Cada punto de datos tiene una propiedad de magnitud. Para mostrar la magnitud relacionada de cada punto de datos en un mapa, dibujaríamos círculos a escala mediante la BubbleLayer, donde cuanto mayor sea la magnitud del punto de datos, mayor será el radio del círculo. El siguiente código demuestra cómo aplicar el estilo basado en datos en la opción de radio en el BubbleLayer, que escalará el radio según la propiedad de magnitud de cada punto de datos en una escala lineal desde 2 píxeles con magnitud 0 hasta 40 píxeles con magnitud 8. 

var earthquakeLayer = new atlas.layer.BubbleLayer(dataSource, null, {
     radius: ['interpolate', ['linear'], ['get', 'magnitude'],
         0, 2,
         8, 40
     ]
});

También podríamos aplicar un estilo similar basado en datos que defina el color de cada círculo y genere un mapa que se vea como el siguiente.

Estilo de mapa basado en datos

Vea un ejemplo en vivo.

Biblioteca de matemáticas espacial

Se agregó una nueva biblioteca de matemáticas espacial al espacio de nombres atlas.math, que ofrece una colección de cálculos útiles que normalmente se necesitan en muchas aplicaciones de mapas. Algunas de las funcionalidades brindan la capacidad de calcular lo siguiente:

  • Distancia de línea recta entre posiciones.
  • El largo de una línea o ruta.
  • El rumbo entre posiciones.
  • Conversiones de distancia.
  • Curvas cardinales, que permiten calcular rutas con leves curvas entre un conjunto de puntos.
  • Rutas geodésicas, que es la ruta directa entre dos puntos teniendo en cuenta la curvatura de la Tierra.
  • Posiciones intermedias en una ruta.

Vea un ejemplo en vivo.

Esta biblioteca proporciona muchos cálculos comunes espaciales simples. Si necesita cálculos espaciales más avanzados, como uniones o intersecciones geométricas, puede resultarle útil la biblioteca de código abierto Turf.js. Turf.js está diseñado para trabajar directamente con datos GeoJSON, que es el formato base para todos los datos de vectores en Azure Maps, lo que permite usarlo fácilmente con Azure Maps.

Soporte para círculos geoespacialmente precisos

El esquema GeoJSON no brinda una forma estandarizada para definir un círculo preciso de forma geoespacial. Por este motivo, el equipo de Azure Maps estandarizó una manera común para definir un círculo preciso de forma geoespacial en GeoJSON sin romper el esquema, como aparece en nuestra documentación. Puede definir objetos de GeoJSON al usar solo JSON en el control web de Azure Maps o al usar las clases asistentes en el espacio de nombres atlas.data. Aquí puede ver un ejemplo de cómo definir un círculo con un radio de 1000 metros sobre Seattle.

Usando solo JSON

var circle = {
     "type": "Feature",
     "geometry": {
         "type": "Point",
         "coordinates": [-122.33, 47.6]
     },
     "properties": {
         "subType": "Circle",
         "radius": 1000
     }
};

Usando clases asistentes en el espacio de nombres atlas.data

var circle = new atlas.data.Feature(new atlas.data.Point([-122.33, 47.6]), {
     subType: "Circle",
     radius: 1000
});

Cuando representa estos círculos, el control web de Azure Maps convierte esta característica de punto en un polígono circular que puede usarse con muchas capas de representación diferentes. Aquí puede ver un mapa donde el círculo se representó como un polígono relleno.

Mapa de Seattle

Vea un ejemplo en vivo.

Una diferencia clave entre los círculos precisos geoespacialmente y los círculos que genera BubbleLayer es que la capa de burbuja asigna un radio de píxel a cada burbuja. Cuando el usuario hace zoom en el mapa, el radio del píxel no cambia, por lo tanto, el área del mapa que abarca la burbuja se modifica. Un círculo preciso geográficamente tiene sus vértices enlazados con coordenadas en el mapa. Cuando se hace zoom en el mapa, el círculo escala y permanece en el área que abarca.

Es importante tener en cuenta que estos círculos no siempre aparecen circulares debido a la proyección de Mercator que usa el mapa. De hecho, cuanto más cerca de los polos norte o sur esté el círculo, más grande y elíptico se verá. Sin embargo, el área que representa en el globo terráqueo es circular. En el siguiente mapa, se muestran dos círculos que tienen un radio de 750 km (750 000 metros). Un círculo está representado sobre Groenlandia, que está cerca del Polo Norte, mientras que el otro está representado sobre Brasil, que está cerca del ecuador.

Ejemplo de círculo geográfico de Azure Maps

Vea un ejemplo en vivo.

Compatibilidad con versiones anteriores

Si ya desarrolló na aplicación con Azure Maps, puede que se pregunte si esto significa que tiene que volver a escribir toda la aplicación. La respuesta es no. Hemos trabajado duro para mantener la compatibilidad con versiones anteriores. En nuestra documentación, hemos marcado muchas funciones antiguas como “en desuso” para evitar que los desarrolladores las usen en aplicaciones futuras, pero seguiremos permitiendo estas características como son en la versión 1 del SDK.

Dicho esto, hemos detectado algunas aplicaciones que han omitido un paso importante al usar el control del mapa. Cuando se crea una instancia del control del mapa, debe cargar diversos recursos, como un lienzo Web-GL. Esto se realiza bastante rápido, pero ocurre de forma asincrónica, lo que significa que la próxima línea de código después de crear la instancia del mapa puede llamarse antes de que el mapa termine de cargarse. Si esa línea de código intenta interactuar con el mapa antes de que se cargue, puede producirse un error. Para solucionar este problema, el evento de “carga” debe adjuntarse al mapa, y la funcionalidad que debe ejecutarse después de que se cargue el mapa debe agregarse a la devolución de llamada del evento. Actualmente, si no está usando el evento de “carga” del mapa, es posible que su aplicación funcione bien la mayor parte del tiempo, pero puede que no sea así en el dispositivo de otro usuario. Aquí encontrará código que muestra el problema y cómo resolverlo.

Problema

//Inicializa una instancia del mapa.
var map = new atlas.Map('myMap', {
     'subscription-key': 'Your Azure Maps Key'
});

//Código adicional que interactúa con el mapa. Es posible que el mapa no haya terminado de cargarse todavía.

Resolución mediante la interfaz antigua de la API (aún compatible)

//Inicializa una instancia del mapa.
var map = new atlas.Map('myMap', {
     'subscription-key': 'Your Azure Maps Key'
});

//Espere hasta que los recursos del mapa se hayan cargado por completo.
map.addEventListener("load", function (e) {
     //Add your additional code that interacts with the map here.
});

Resolución mediante la nueva interfaz de la API

//Agregue la clave de su suscripción a Azure Maps al SDK del mapa.
atlas.setSubscriptionKey('Your Azure Maps Key');

//Inicializa una instancia del mapa.
var map = new atlas.Map('myMap');

//Espere hasta que los recursos del mapa se hayan cargado por completo.
map.events.add('load', function (e) {
     //Add your additional code that interacts with the map here.
});

Queremos saber de usted.

Estamos constantemente trabajando para crecer y mejorar la plataforma de Azure Maps, y nos gustaría escuchar sus comentarios.

  • ¿Tiene alguna solicitud de característica? Agréguela o vote a favor de la solicitud en nuestro sitio de comentarios.
  • ¿Detectó un problema con los datos del mapa? Envíelo directamente a nuestro proveedor de datos a través de la herramienta Map Share Reporter de TomTom.
  • ¿Tiene algún problema con el funcionamiento de su código? ¿Tiene algún tema que le gustaría que tratemos en el blog de Azure? Consúltenos en los foros de Azure Maps. Estamos aquí para ayudarle y queremos asegurarnos de que saque el mayor provecho posible de la plataforma de Azure Maps.
  • ¿Está buscando ejemplos de código o escribió uno que desea compartir? Únase a GitHub.