Generalmente los mapas son una representación geográfica de una variable en un momento concreto. Sin embargo con el desarrollo de las nuevas tecnologías podemos construir mapas web animados que nos muestren la evolución de un factor a lo largo del tiempo.
Posiblemente el caso más conocido es el de las aplicaciones de meteorología, donde podemos ver el avance de un huracán o de un frente de lluvia mediante las imágenes de radar.
Una de las librerías JavaScript más utilizadas para la publicación web de mapas es Leaflet. Pues bien, Leaflet dispone de una herramienta que nos permite incluir la dimensión temporal a un mapa. Se trata del plugin Leaflet TimeDimension.
¿Por dónde empezar?
Leaflet TimeDimensión dispone de múltiples opciones y algunas particularidades que a primera vista lo hacen parecer más complejo que otros plugins. Sin embargo despojándolo de todos los elementos no sustanciales y reduciéndolo a lo básico, veremos que es sencillo de aplicar. Además el plugin nos proporciona una excelente documentación con varios ejemplos.
Los datos
Necesitaremos datos que además de la componente espacial tengan una componente temporal. Esos datos pueden ser principalmente de dos tipos:
– Una capa WMS que disponga de un parámetro temporal.
– Una combinación de capas geográficas en formato geoJSON que dispongan de un atributo con fecha/hora que permita realizar una distribución temporal.
En el ejemplo que vamos a describir utilizaremos la primera opción, es decir un WMS con componente temporal.
Requisitos
Para poder utilizar este plugin necesitamos cumplir varias condiciones, que detallamos a continuación:
Primera condición: Como en todos los plugins de Leaflet, lo primero que necesitamos son los archivos del plugin, que se compone de un archivo css y de varios archivos js.
<link rel="stylesheet" href="../dist/leaflet.timedimension.control.min.css" />
<script type="text/javascript" src="../src/leaflet.timedimension.js"></script> <script type="text/javascript" src="../src/leaflet.timedimension.util.js"></script> <script type="text/javascript" src="../src/leaflet.timedimension.layer.js"></script> <script type="text/javascript" src="../src/leaflet.timedimension.layer.wms.js"></script> <script type="text/javascript" src="../src/leaflet.timedimension.player.js"></script> <script type="text/javascript" src="../src/leaflet.timedimension.control.js"></script>
Segunda condición: jQuery
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
Tercera condición: Para el control TimeDimensión necesitamos los iconos proporcionado por Glyphicons.
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet" > <link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet">
Cuarta condición: Para que no se generen conflictos con los datos temporales el plugin Leaflet timeDimension necesita utilizar un formato normalizado según la norma ISO 8601 que es una norma internacional que abarca el intercambio de datos relacionados con la fecha y el tiempo. Hay una librería javascrip que nos proporciona esta normalización: iso8601-js-period
<script type="text/javascript" src="https://cdn.rawgit.com/nezasa/iso8601-js-period/master/iso8601.min.js"></script>
Una vez que hemos satisfecho los requisitos que requiere el plugin, podemos empezar a construir nuestra aplicación.
El plugin Leaflet timeDimension
Este plugin nos permite incorporar nuevas opciones a la clase L.map de Leaflet. Estas nuevas opciones son:
- timeDimension, que por defecto toma el valor: false. Nos crea automáticamente un nuevo objeto TimeDimension vinculado al mapa, para eso le asignamos el valor true.
- timeDimensionOptions . Son las opciones para el objeto TimeDimension.
- timeDimensionControl, que por defecto toma el valor: false. Si lo cambiamos a true nos añade automáticamente un control TimeDimension al mapa.
- timeDimensionControlOptions : Opciones para el objeto TimeDimension Control.
Resumiendo podemos decir que, con este plugin estamos creando un nuevo objeto timedimension y su control. También nos proporciona una batería de opciones para la configuración de ambos.
L.TimeDimension
El objeto TimeDimension gestiona el componente temporal de una capa. Dispone de varias opciones que se pueden configurar al crear el mapa mediante la opción timeDimensionOptions. Algunas de estas opciones son:
- times. Por defecto toma el valor: null. Puede ser:
a) Una matriz de tiempos (en milisegundos).
b) Cadena de fechas separadas por comas.
c) Cadena formada por fecha de inicio / fecha de finalización / período.
Si el valor de time es nulo, se construirá de acuerdo con timeInterval y period.
- timeInterval . Por defecto toma el valor «P1M /» + today. Se emplea para definir el intervalo de tiempo de la variable temporal, en el formato: ISO8601.
- period. Por defecto «P1D». Se utiliza para construir la matriz de tiempos disponibles a partir del primer tiempo disponible. Formato: ISO8601 Duración
- validTimeRange. Filtra la matriz de tiempos disponibles por hora de inicio y hora de fin (para cualquier fecha). Formato «HH: MM / HH: MM».
- currentTime. Tiempo disponible más cercano o tiempo actual a cargar. Tiempo en ms.
L.Control.TimeDimension
Es el control de Leaflet para gestionar un timeDimension. Nos permite insertar los controles de reproducción | pausa, siguiente, atrás, hora actual, control deslizante de tiempo y control deslizante de velocidad. La lista de opciones que nos proporciona es bastante amplia, y puede consultarse en la documentación del plugin. Aquí vamos a ilustrar el tema con un ejemplo y explicar a continuación las opciones que hemos utilizado.
var map = L.map('map', { zoom: 10, fullscreenControl: true, timeDimension: true, timeDimensionOptions: { timeInterval: "2015-09-01/2015-09-03", period: "PT1H", currentTime: Date.parse("2015-09-01T00:00:00Z") }, timeDimensionControl: true, timeDimensionControlOptions: { autoPlay: true, loopButton: true, timeSteps: 1, playReverseButton: true, limitSliders: true, playerOptions: { buffer: 0, transitionTime: 250, loop: true, } }, center: [38.705, 1.15], });
En el código precedente hemos creado una instancia a la clase L.map de Leaflet y definido unas opciones tales como zoom, o center, de la forma habitual. La novedad de este plugin es que nos permite incorporar nuevas opciones. Al asignar el valor true a timeDimesion y a timeDimensionControl, nos crea un nuevo objeto y un nuevo control. Las opciones que utilizamos en timeDimensionControl son las que nos dan el formato del tiempo, según explicamos antes. El periodo «PT1H» se refiere a una hora. Las opciones de timeDimensionControlOptions que hemos utilizado las explicamos a continuación:
- autoplay: anima el mapa automáticamente.
- loopButton: muestra el botón activar / parar .
- timeSteps: número de pasos de tiempo aplicados a la TimeDimension (hacia adelante o hacia atrás).
- playReverseButton: es el botón para reproducir la animación a la inversa.
- limitSliders: muestra el límite en el control deslizante de tiempo para restringir el rango de animación.
- playerOptions: son las opciones para el objeto (TimeDimension Player, transitionTime, buffer, minBufferReady, loop y startOver)
Añadiendo capas con dimensión temporal
El trabajo que hemos realizado hasta el momento tenia por objeto crear las condiciones para poder alojar una capa que tenga un dato de tiempo. Como dijimos al principio estas capa puede ser un WMS con dimensión temporal o unas geometrías geoJSON con un atributo de fecha y/o hora. En nuestro ejemplo utilizamos un WMS con dimensión temporal que nos muestra la variación de la velocidad del agua del mar Mediterráneo en las proximidades de la isla de Ibiza (Islas Baleares). Para completar el mapa se inserta como mapa geográfico de fondo el que nos proporciona OpenStreetMap.
L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', { attribution: '© <a href="http://osm.org/copyright">OpenStreetMap</a> contributors' }).addTo(map); var testWMS = "http://thredds.socib.es/thredds/wms/observational/hf_radar/hf_radar_ibiza-scb_codarssproc001_L1_agg/hf_radar_ibiza-scb_codarssproc001_L1_agg_best.ncd" var testLayer = L.tileLayer.wms(testWMS, { layers: 'sea_water_velocity', version: '1.3.0', format: 'image/png', transparent: true, styles: 'prettyvec/rainbow', markerscale: 15, markerspacing: 10, abovemaxcolor: "extend", belowmincolor: "extend", colorscalerange: "0,0.4", attribution: 'SOCIB HF RADAR | sea_water_velocity' });
L.TimeDimension.Layer.WMS
Implementa una capa TimeDimension para una capa WMS dada, que puede ser L.TileLayer.WMS o L.NonTiledLayer.WMS. Este componente sincroniza el WMS con un TimeDimension, modificando el parámetro time en las peticiones de WMS.
var testTimeLayer = L.timeDimension.layer.wms(testLayer, { updateTimeDimension: true }); testTimeLayer.addTo(map);
Mediante updateTimeDimension actualizamos las lista de tiempos con los que nos suministra el WMS obtenidos con getCapabilities
Añadiendo una leyenda
Como último paso, podemos añadir una leyenda al mapa utilizando los datos que nos proporciona el WMS. En este caso representamos la velocidad del agua del mar en m/s.
var testLegend = L.control({ position: 'topright' }); testLegend.onAdd = function(map) { var src = testWMS + "?SERVICE=WMS&VERSION=1.3.0&REQUEST=GetLegendGraphic&LAYER=sea_water_velocity&PALETTE=rainbow&colorscalerange=0,0.4"; var div = L.DomUtil.create('div', 'info legend'); div.innerHTML += '<img src="' + src + '" alt="legend">'; return div; }; testLegend.addTo(map);
Código completo
Vamos a reunir todo el código escrito hasta el momento para facilitar así su entendimiento. Como puedes ver hemos añadido un control que permite ver el mapa en pantalla completa.
<!DOCTYPE html><html> <head> <title>Leaflet TimeDimension</title> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" /> <link rel="stylesheet" href="https://unpkg.com/leaflet@1.2.0/dist/leaflet.css" /> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/leaflet.fullscreen/1.4.2/Control.FullScreen.min.css" /> <link rel="stylesheet" href="../dist/leaflet.timedimension.control.min.css" /> <link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet" > <link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet"> <style> #map { width: 700px; height: 600px; } </style> </head> <body> <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script> <script type="text/javascript" src="https://unpkg.com/leaflet@1.2.0/dist/leaflet.js"></script> <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/leaflet.fullscreen/1.4.2/Control.FullScreen.min.js"></script> <script type="text/javascript" src="https://cdn.rawgit.com/nezasa/iso8601-js-period/master/iso8601.min.js"></script> <script type="text/javascript" src="../src/leaflet.timedimension.js"></script> <script type="text/javascript" src="../src/leaflet.timedimension.util.js"></script> <script type="text/javascript" src="../src/leaflet.timedimension.layer.js"></script> <script type="text/javascript" src="../src/leaflet.timedimension.layer.wms.js"></script> <script type="text/javascript" src="../src/leaflet.timedimension.player.js"></script> <script type="text/javascript" src="../src/leaflet.timedimension.control.js"></script> <div id ="map"> </div> <script> var map = L.map('map', { zoom: 10, fullscreenControl: true, timeDimension: true, timeDimensionOptions: { timeInterval: "2015-09-01/2015-09-03", period: "PT1H", currentTime: Date.parse("2015-09-01T00:00:00Z") }, timeDimensionControl: true, timeDimensionControlOptions: { autoPlay: true, loopButton: true, timeSteps: 1, playReverseButton: true, limitSliders: true, playerOptions: { buffer: 0, transitionTime: 250, loop: true, } }, center: [38.705, 1.15], }); L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', { attribution: '© <a href="http://osm.org/copyright">OpenStreetMap</a> contributors' }).addTo(map); var testWMS = "http://thredds.socib.es/thredds/wms/observational/hf_radar/hf_radar_ibiza-scb_codarssproc001_L1_agg/hf_radar_ibiza-scb_codarssproc001_L1_agg_best.ncd" var testLayer = L.tileLayer.wms(testWMS, { layers: 'sea_water_velocity', version: '1.3.0', format: 'image/png', transparent: true, styles: 'prettyvec/rainbow', markerscale: 15, markerspacing: 10, abovemaxcolor: "extend", belowmincolor: "extend", colorscalerange: "0,0.4", attribution: 'SOCIB HF RADAR | sea_water_velocity' }); var testTimeLayer = L.timeDimension.layer.wms(testLayer, { updateTimeDimension: true }); testTimeLayer.addTo(map); var testLegend = L.control({ position: 'topright' }); testLegend.onAdd = function(map) { var src = testWMS + "?SERVICE=WMS&VERSION=1.3.0&REQUEST=GetLegendGraphic&LAYER=sea_water_velocity&PALETTE=rainbow&colorscalerange=0,0.4"; var div = L.DomUtil.create('div', 'info legend'); div.innerHTML += '<img src="' + src + '" alt="legend">'; return div; }; testLegend.addTo(map); </script> </body> </html>
El resultado lo puedes ver a continuación.
Si quieres aprender a crear visores web profesionales como éste, inscríbete ya en nuestro curso online visores webmapping con Leaflet.
Tutor del curso online de Análisis GeoEspacial con Python y de los cursos online de webmapping. Echa un vistazo a todos nuestros cursos de SIG online.
Muy buen aporte, tenia una duda talvez basica pero me gustaria porfavor me la puedan aclarar como creo o genero un WMS con dimensión temporal, o en otro caso un geojson que particularidad debe tener porfavor muchas gracias desde ya!!!!!!!
Hola Favio:
Según la especificación GeoJSON, las coordenadas geométricas solo pueden tener tres dimensiones: latitud, longitud y elevación. No hay por tanto una forma estándar de agregar información de dimensión temporal. Este plugin buscará algunos atributos dentro de las propiedades. Lo mejor, sin duda, es que consultes la información del plugin, que es muy completa.
Un saludo.
José Luis
Hola José Luis,
gracias por tu artículo! Trabajo en ICTS SOCIB, el observatorio oceanográfico que ha desarrollado y mantiene este plugin de Leaflet. Es una herramienta que necesitábamos para realizar las visualizaciones de algunos de los datos que tenemos (modelos oceanográficos, corrientes observadas por RADAR HF, trayectorias de boyas de deriva y otros). Y al compartirla en github, hemos conseguido que otras personas la puedan utilizar, y además, algunas de ellas nos ayudan en las mejoras.
Una corrección. Lo más adecuado sería incluir un sólo fichero js: dist/leaflet.timedimension.min.js. Este fichero es el resultado de concatenar, eliminar el logging y minimizar los 6 ficheros fuente del plugin. En los ejemplos están puesto los 6 ficheros de src para poder explorar mejor el plugin.
Ah! Y se puede utilizar esta imagen como logo del plugin:
https://github.com/socib/Leaflet.TimeDimension/blob/master/examples/img/logo-leaflettimedimension.png
Un saludo,
Biel
Muchas gracias, Biel, por tus comentarios y aportaciones. Enhorabuena por vuestro trabajo, es un gran plugin y los mapas son fantásticos.
Hola, te escribo desde argentina, mira lo que necesito saber es si se puede hacer que agregando mis clientes en google maps con My Maps, pueda agregar el mapa a mi sitio web con todos ellos y que tengo un buscador de búsqueda para que la persona que quiere buscar restaurantes, les aparezcan los restaurantes que yo cargue marcados en el mapa. Digamos que es como cuando pongo restaurantes en google maps y me aparecen los que tengo cerca. Muchas gracias saludos
Hola Alberto, te contesto yo, aunque el autor del post es José Luis García. Referente a tu pregunta me temo que con My Maps no se puede realizar, ya que su funcionalidad es limitada. Debes acudir a la API JavaScript para personalizar la funcionalidad mediante código. Un saludo!