Cómo superponer teselas con la API de HERE


En este tutorial veremos cómo superponer teselas personalizadas utilizando la API de HERE.

En este ejemplo vamos a utilizar un mapa histórico del centro de Berlín de 1789 sobre un mapa base de HERE.


Se ha utilizado una imagen de dominio público como origen y se ha dividido en teselas de mapa individuales, estas teselas de ejemplo las podemos encontrar en el GitHub de HERE, o si preferís podéis crear vuestras propias teselas con un SIG (Sistema de Información Geográfica, también conocido, como GIS por las siglas de su nombre en inglés Geographical Information System.)

Las teselas adicionales solo se descargarán cuando cambie el nivel de zoom o la ubicación del mapa, recordar que necesitáis una imagen diferente para cada uno de los diferentes niveles de zoom, correctamente teselada.

¿Por si os preguntáis qué es una tesela?

Una tesela es cada una de las piezas con que se forma o se divide un mosaico o en este caso una imagen.

Os dejamos el código completo para que podáis hacerlo vosotros mismos:

						
<html>
  <head>
      <meta name="viewport" content="initial-scale=1.0,width=device-width" />
        <link rel="stylesheet" type="text/css" href="https://js.api.here.com/v3/3.1/mapsjs-ui.css"/>
        <script type="text/javascript" src="https://js.api.here.com/v3/3.1/mapsjs-core.js"></script>
        <script type="text/javascript" src="https://js.api.here.com/v3/3.1/mapsjs-service.js"></script>
        <script type="text/javascript" src="https://js.api.here.com/v3/3.1/mapsjs-ui.js"></script>
        <script type="text/javascript" src="https://js.api.here.com/v3/3.1/mapsjs-mapevents.js"></script>
    <style>
	body { margin:0; padding:0; }
	#map { position:absolute; top:0; bottom:0; width:100%; }
    </style>
  </head>
  <body>
    <div id="map"></div>
    <script type="text/javascript">
                function overlayHistoricalBerlin(map) {                  
                  var tileProvider = new H.map.provider.ImageTileProvider({
                    min: 12,
                    max: 15,
                    opacity: 0.5,
                    getURL: function (column, row, zoom) {
                      if (((zoom == 12) && (row != 1343 || column != 2200)) ||
                        ((zoom == 13) &&  (row < 2686 || column < 4400 || row > 2687 || column > 4401)) ||
                        ((zoom == 14) && (row < 5372 || column < 8800 || row > 5375 || column > 8803)) ||
                        ((zoom  == 15) && (row < 10744 || column < 17601 || row > 10750 || column > 17607))) {
                        return 'https://heremaps.github.io/maps-api-for-javascript-examples/custom-tile-overlay/tiles/blank.png';
                      } else {
                        return 'https://heremaps.github.io/maps-api-for-javascript-examples/custom-tile-overlay/tiles/'+ zoom+ '/'+ row + '/'+ column+ '.png';
                      }
                    }
                  });
                  tileProvider.getCopyrights = function (bounds, level) {
                    return [{
                      label: "Overlay derived from <a href='https://commons.wikimedia.org/wiki/File%3AMap_de_berlin_1789_%28georeferenced%29.jpg' target='_blank'>WikiMedia Commons</a>",
                      alt: 'Overlay Based on a WikiMedia Commons Image in the Public Domain'
                    }];
                  };
                  var overlayLayer = new H.map.layer.TileLayer(tileProvider, {
                    opacity: 0.5
                  });
                  map.addLayer(overlayLayer);
                }
                var platform = new H.service.Platform({
                  'apikey': 'Tu apikey de HERE'
                });
                var defaultLayers = platform.createDefaultLayers();
                var map = new H.Map(document.getElementById('map'), defaultLayers.vector.normal.map, {
                  center: new H.geo.Point(52.515, 13.405),
                  zoom: 14,
                  pixelRatio: window.devicePixelRatio || 1
                });
                window.addEventListener('resize', () => map.getViewPort().resize());
                var behavior = new H.mapevents.Behavior(new H.mapevents.MapEvents(map));
                var ui = H.ui.UI.createDefault(map, defaultLayers);
                overlayHistoricalBerlin(map);
              </script>
  </body>
</html>
						
					

Ahora vamos a desgranar y explicar el código:

El primer paso es cargar las bibliotecas JavaScript en el head y añadir los estilos CSS, para ello lo cargaremos a través de un CDN:

						
<head>
  <meta name="viewport" content="initial-scale=1.0,width=device-width" />
    <link rel="stylesheet" type="text/css" href="https://js.api.here.com/v3/3.1/mapsjs-ui.css"/>
    <script type="text/javascript" src="https://js.api.here.com/v3/3.1/mapsjs-core.js"></script>
    <script type="text/javascript" src="https://js.api.here.com/v3/3.1/mapsjs-service.js"></script>
    <script type="text/javascript" src="https://js.api.here.com/v3/3.1/mapsjs-ui.js"></script>
    <script type="text/javascript" src="https://js.api.here.com/v3/3.1/mapsjs-mapevents.js"></script>
<style>
	body { margin:0; padding:0; }
	#map { position:absolute; top:0; bottom:0; width:100%; }
</style>
</head>
						
					

Lo siguiente y una vez dentro del cuerpo o body, corresponde al contenedor, veréis que está directamente relacionado con los estilos (#map), ya que tiene un identificador id=map.

						
<div id="map"></div>
						
					

Nos vamos a poner con el Script:

Crearemos el proveedor de teselas a partir de nuestras imágenes históricas de Berlín, para ello utilizaremos la clase H.map.provider.ImageTileProvider para obtener nuestras imagenes personalizadas.

El método getUrl() construye la dirección URL de la imagen para la posición de columna/fila/zoom especificada. El resultado se muestra en el mapa como una capa adicional mediante una instancia de la clase H.map.layer.TileLayer que se ha creado pasando el objeto de proveedor de teselas de imagen:

						
function overlayHistoricalBerlin(map) {                  
  var tileProvider = new H.map.provider.ImageTileProvider({
    min: 12,
    max: 15,
    opacity: 0.5,
    getURL: function (column, row, zoom) {
      if (((zoom == 12) && (row != 1343 || column != 2200)) ||
        ((zoom == 13) &&  (row < 2686 || column < 4400 || row > 2687 || column > 4401)) ||
        ((zoom == 14) && (row < 5372 || column < 8800 || row > 5375 || column > 8803)) ||
        ((zoom  == 15) && (row < 10744 || column < 17601 || row > 10750 || column > 17607))) {
        return 'https://heremaps.github.io/maps-api-for-javascript-examples/custom-tile-overlay/tiles/blank.png';
      } else {
        return 'https://heremaps.github.io/maps-api-for-javascript-examples/custom-tile-overlay/tiles/'+ zoom+ '/'+ row + '/'+ column+ '.png';
      }
    }
  });
						
					

Debemos tener en cuenta las siguientes consideraciones:

  • Solo tenemos mosaicos para los niveles de zoom 12-15, así que para todos los demás niveles de zoom solo se mostrará el mapa base.
  • Si no se muestran las teselas se devolverá como resultado un mosaico en blanco.
  • Por especificación, los mosaicos deben seguir en el siguiente formato, http://server_address/zoom_level/x/y.png

Si nos fijamos en la carpeta de GitHub, para el zoom 12 tendremos una imagen, para el zoom 13 tendremos 4 teselas, para el zoom 14 tendremos 16 teselas y finalmente para el zoom 15 tendremos 49 teselas.

El siguiente apartado, indicaremos los créditos y atribuciones:

La API de Here dispone de un elemento para resolver esta parte, H.map.ICopyright.

						
tileProvider.getCopyrights = function (bounds, level) {
	return [{
	  label: "Overlay derived from <a href='https://commons.wikimedia.org/wiki/File%3AMap_de_berlin_1789_%28georeferenced%29.jpg' target='_blank'>WikiMedia Commons</a>",
	  alt: 'Overlay Based on a WikiMedia Commons Image in the Public Domain'
	}];
};
						
					

En este caso, los azulejos se han creado con una imagen de Berlín en 1789 de Wikimedia.

Las imágenes proporcionadas por Wikimedia Commons están bajo los derechos de autor de sus propietarios. Sin embargo, casi todo el contenido alojado en Wikimedia Commons puede ser reutilizado libremente sujeto a ciertas restricciones.

Ahora vamos a crear la capa que va a consumir los azulejos o teselas, y vamos a hacer que sea semitransparente (opacity: 0.5) :

						
var overlayLayer = new H.map.layer.TileLayer(tileProvider, {
	opacity: 0.5
});
						
					

A continuación, agregaremos la capa que contiene el mapa histórico de Berlín a nuestro mapa de Here:

						
map.addLayer(overlayLayer);
}
						
					

Si nos fijamos ahora veremos varias partes imprescindibles, la primera es la APIKEY que es única para cada usuario de HERE.

						
var platform = new H.service.Platform({
  'apikey': 'Tu apikey de HERE'
});
						
					

Si queréis saber cómo obtenerla os recomendamos el siguiente tutorial, Cómo crear un mapa básico con la API de HERE.

Inicializamos el mapa, con las coordenadas del punto central y el zoom inicial:

						
var defaultLayers = platform.createDefaultLayers();
var map = new H.Map(document.getElementById('map'), defaultLayers.vector.normal.map, {
  center: new H.geo.Point(52.515, 13.405),
  zoom: 14,
  pixelRatio: window.devicePixelRatio || 1
});
						
					

Nos aseguramos de que el mapa ocupe todo el contenedor con el siguiente Script:

						
window.addEventListener('resize', () => map.getViewPort().resize());
						
					

Habilitamos los eventos del mapa para que sea interactivo, interacciones predeterminadas para panorámica / zoom y también el comportamiento en entornos táctiles o móviles:

						
var behavior = new H.mapevents.Behavior(new H.mapevents.MapEvents(map));
						
					

Añadimos las capas por defecto del interfaz de usuario de Here(ui):

						
var ui = H.ui.UI.createDefault(map, defaultLayers);
						
					

Y para finalizar añadimos la lógica principal:

						
overlayHistoricalBerlin(map);
						
					

Y ya tenemos funcionando un mapa histórico, en este caso de Berlín, teselado y sobre un mapa de Here.

¿Alguna pregunta? ¿Quieres realizar sinergias o colaboraciones? ¿Te gustaría enviarnos feedback sobre el tutorial? No dudes en contactar a jgabas@geopois.com.



Sobre el Autor


Javier Gabás Jiménez

Soy un apasionado de los mapas y la infraestructura de datos espaciales, me divierto mucho buscando formas de comprender y presentar datos visualmente, así como entender la programación que hay debajo. Ingeniero en geomática y topografía por la Universidad Politécnica de Madrid, cuando no estoy trabajando o estudiando, estaré viajando y disfrutando de la vida junto a mi pareja.


¿Eres desarrollador? Únete a la red