OpenLayers es una librería JavaScript de código abierto ampliamente utilizada para mostrar mapas de forma interactiva en la web. Actualmente forma parte de los proyectos de Open Source Geospatial Foundation.
Permite utilizar un amplio número de recursos como son las capas de teselas de OSM, Bing, MapBox, Stamen así como los servicios de mapas de OGC. Podemos también utilizar capas vectoriales en múltiples formatos como: GeoJSON, TopoJSON, KML, GML, etc y por supuesto imágenes raster.
En el caso de las imágenes raster, OpenLayers tiene una función añadida: se puede realizar un procesamiento de la imagen para obtener una imagen nueva. Veamos cómo hacerlo.
ol.source.Raster
Para utilizar OpenLayers empezaremos por incluir en la cabecera <head> de una página web la hoja de estilo ol.css y la librería JavaScript ol.js. Para obtener más información sobre cómo crear un mapa con OpenLayers puedes consultar este otro este artículo de nuestro blog.
<link rel="stylesheet" href="https://openlayers.org/en/v4.6.4/css/ol.css" type="text/css"> <script src="https://cdn.polyfill.io/v2/polyfill.min.js?features=requestAnimationFrame,Element.prototype.classList,URL"></script> <script src="https://openlayers.org/en/v4.6.4/build/ol.js"></script>
El elemento de la librería encargado del tratamiento de los datos raster en OpenLayers es: ol.source.Raster. Básicamente se trata de una función que realiza algún tipo de transformación en el valor de los pixel, es decir toma el valor de entrada de un pixel y nos devuelve un valor de salida. Se pueden definir varias opciones. Nosotros aplicaremos dos:
- sources: es donde proporcionamos la capa de entrada que contiene los datos raster.
- operation: es donde definimos la trasformación que haremos sobre los datos de entrada con el fin de obtener el dato de salida procesado.
Crear un mapa de elevaciones a partir de una imagen raster
Como ejemplo de las posibilidades de OpenLayers en el tratamiento de imágenes raster, vamos a crear un mapa de elevación del terreno a partir de las imágenes de mapbox. Este recurso contiene un dato con la altitud de cada pixel que utilizaremos para crear nuestro mapa.
En primer lugar creamos el recurso a la capa de Mapbox.
var key = 'Tu_clave_mapbox'; var elevation = new ol.source.XYZ({ url: 'https://api.mapbox.com/v4/mapbox.terrain-rgb/{z}/{x}/{y}.pngraw?access_token=' + key, crossOrigin: 'anonymous', transition: 0 });
A continuación, aplicamos la clase ol.source.Raster:
var raster = new ol.source.Raster({ sources: [elevation], operation: flood });
Como opciones pasamos el objeto elevation que habíamos creado anteriormente y como operación, asignamos el valor flood que explicamos a continuación. Como indicamos en el primer apartado podemos definir una función que transforme el valor del pixel. Eso lo realizamos con la siguiente función a la que llamamos flood:
function flood(pixels, data) { var pixel = pixels[0]; if (pixel[3]) { var height = -10000 + ((pixel[0] * 256 * 256 + pixel[1] * 256 + pixel[2]) * 0.1); if (height <= data.level && height >0) { pixel[0] = 200; pixel[1] = 175; pixel[2] = 186; pixel[3] = 255; } else { pixel[3] = 0; } } return pixel; }
Con esta función lo que buscamos es obtener un valor de pixel inicial. Establecemos un valor condicional en función de la altura, que almacenamos en la variable heigth. Si el valor heigth es mayor de 0 y menor del valor definido en data.level se establece una función de transformación en donde se asignan un nuevo valor a cada pixel.
Ahora hay que crear el mapa con OpenLayers:
var map = new ol.Map({ target: 'map', layers: [ new ol.layer.Tile({ source: new ol.source.XYZ({ url: 'https://api.mapbox.com/styles/v1/mapbox/streets-v9/tiles/256/{z}/{x}/{y}?access_token=' + key, crossOrigin: 'anonymous', }) }), new ol.layer.Image({ opacity: 0.5, source: raster }) ], view: new ol.View({ center: ol.proj.fromLonLat([-5.664, 40.965]), zoom: 6 }) });
Añadiendo un control para las elevaciones
Creamos un control slider que nos permita al desplazar el cursor, seleccionar la altitud. En el mapa se muestra el área que se encuentra por debajo de la cota seleccionada. Esto lo hacemos de la siguiente forma:
var control = document.getElementById('level'); var output = document.getElementById('output'); control.addEventListener('input', function() { output.innerText = control.value; raster.changed(); }); output.innerText = control.value; raster.on('beforeoperations', function(event) { event.data.level = control.value; });
De esta parte del código destacamos dos elementos:
1.- El método .changed(), que sería el encargado de disparar los eventos cuando se producen modificaciones en el mapa. En este caso cuando modificamos la altura en el control de elevaciones.
2.-El evento beforeoperations, que es el que se muestra antes de que ninguna acción sea realizada sobre el mapa. En evento contrario es afteroperations que es el que se dispara una vez que la trasformación ha ocurrido.
Acompañando a esta parte JavaScript, necesitamos crear los elementos html para el control.
<label> Altitud (Rango 0 a 2000 m, intervalo 100 m) <input id="level" type="range" min="0" max="2000" step="100" value="100"/> +<span id="output"></span> m </label>
Por último creamos unos estilos css para el control.
<style> #level { display: inline-block; width: 200px; vertical-align: text-bottom; background-color: grey; } a.location { cursor: pointer; } </style>
Ejemplo al completo
Con el fin de facilitar la compresión del código, lo escribimos a continuación de forma completa:
<!DOCTYPE html> <html> <head> <title>Altitud cada 100 m</title> <meta charset="utf-8" /> <link rel="stylesheet" href="https://openlayers.org/en/v4.6.4/css/ol.css" type="text/css"> <script src="https://cdn.polyfill.io/v2/polyfill.min.js?features=requestAnimationFrame,Element.prototype.classList,URL"></script> <script src="https://openlayers.org/en/v4.6.4/build/ol.js"></script> <style> #level { display: inline-block; width: 200px; vertical-align: text-bottom; background-color: grey; } a.location { cursor: pointer; } </style> </head> <body> <div id="map" class="map"></div> <label> Altitud (Rango 0 a 2000 m, intervalo 100 m) <input id="level" type="range" min="0" max="2000" step="100" value="100"/> +<span id="output"></span> m </label> <br> <script> function flood(pixels, data) { var pixel = pixels[0]; if (pixel[3]) { var height = -10000 + ((pixel[0] * 256 * 256 + pixel[1] * 256 + pixel[2]) * 0.1); if (height <= data.level && height >0) { pixel[0] = 200; pixel[1] = 175; pixel[2] = 186; pixel[3] = 255; } else { pixel[3] = 0; } } return pixel; } var key = 'Tu_clave_mapbox'; var elevation = new ol.source.XYZ({ url: 'https://api.mapbox.com/v4/mapbox.terrain-rgb/{z}/{x}/{y}.pngraw?access_token=' + key, crossOrigin: 'anonymous', transition: 0 }); var raster = new ol.source.Raster({ sources: [elevation], operation: flood }); var map = new ol.Map({ target: 'map', layers: [ new ol.layer.Tile({ source: new ol.source.XYZ({ url: 'https://api.mapbox.com/styles/v1/mapbox/streets-v9/tiles/256/{z}/{x}/{y}?access_token=' + key, crossOrigin: 'anonymous', }) }), new ol.layer.Image({ opacity: 0.5, source: raster }) ], view: new ol.View({ center: ol.proj.fromLonLat([-5.664, 40.965]), zoom: 6 }) }); var control = document.getElementById('level'); var output = document.getElementById('output'); control.addEventListener('input', function() { output.innerText = control.value; raster.changed(); }); output.innerText = control.value; raster.on('beforeoperations', function(event) { event.data.level = control.value; }); </script> </body> </html>
Puedes ver el resultado a continuación:
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.
Muchas gracias Gonzalo por tu comentario.
En este artículo tratamos el procesamiento de imágenes desde OpenLayers, En el caso de QGIS debería buscar la solución a tu problema en las entradas del blog MappingGIS referentes a QGIS. También puedes plantear la consulta a Diego, Aurelio… que son los especialistas en QGIS.
Saludos
Buenos días,
enhorabuena por vuestra página. es fantástica para adentrarse en el mundo GIS.
Tengo una duda, no sé si es el lugar más adecuado para ponerla.
EStoy tratando de utilizar QGIS para comparar la evolución de un mismo cultivo en tiempos diferentes. Por ello, ,me vendría bien ver dos capas raster en ventanas paralelas, de modo que al trasladarme por una de ellas, la otra también se moviera y pudiera ver la misma ubicación espacial pero en dos momentos diferentes. ¿Es posible? gracias ¡¡