Cómo crear un popup en OpenLayers

Las ventanas emergentes o pop ups son elementos imprescindibles para añadir interacción al mapa y para mostrar los datos.

A diferencia de otras librerías Javascript para webmapping como Leaflet, OpenLayers no dispone de una clase popup para crear elementos emergentes. OpenLayers sí dispone de algunas librerías adicionales entre las que se encuentra una denominada OpenLayers Popup. Sin embargo, en esta ocasión veremos cómo crear un popup mediante Overlay, que es un elemento que se asocia a una ubicación concreta del mapa.

Crear un popup en OpenLayers

El código JavaScript con OpenLayers

Como hemos visto en otros artículos de este blog, la mejor forma de utilizar OpenLayers es mediante Node. Lo primero será crear un archivo que llamamos main.js que contenga los módulos que vamos a necesitar. En el caso de Overlay importamos el módulo de la siguiente forma:

import Overlay from ‘ol/Overlay’;

import 'ol/ol.css';
import Feature from 'ol/Feature';
import Map from 'ol/Map';
import Overlay from 'ol/Overlay';
import Point from 'ol/geom/Point';
import {fromLonLat} from 'ol/proj';
import VectorSource from 'ol/source/Vector';
import View from 'ol/View';
import {Fill, Stroke, Circle, Style} from 'ol/style'; 
import {Tile as TileLayer, Vector as VectorLayer} from 'ol/layer';
import OSM from 'ol/source/OSM';

En las líneas anteriores vemos los módulos que necesitamos en nuestra aplicación, entre los que se encuentra Overlay.

Creando las capas y el mapa

A continuación creamos las capas que van a dar el contenido al mapa. Se trata de una capa base que contiene el mapa de OpenStreetMap y una capa con un marcador que lleva asociados los datos que queremos mostrar en la ventana emergente:

const layerOSM = 
        new TileLayer({
          source: new OSM()
        });

const markerFeature = new Feature({
      geometry: new Point(fromLonLat([-4.72, 41.66])),
      name: 'Valladolid',
      poblacion: 299265,
      region: 'Castilla y León',
    });

Definimos un estilo al marcador y lo aplicamos:

const markerStyles = new Style({
          image: new Circle({
            radius: 8,
            fill: new Fill({
              color: 'orange'}),    
            stroke: new Stroke({
              color: 'black'})
          })
        })

markerFeature.setStyle(markerStyles);

Ahora solo nos resta crear la capa y el mapa con OpenLayers:

const vectorSource = new VectorSource({
  features: [markerFeature],
});

const vectorLayer = new VectorLayer({
  source: vectorSource,
});


const map = new Map({
  layers: [layerOSM, vectorLayer],
  target: document.getElementById('map'),
  view: new View({
    center: fromLonLat([-4.72, 41.66]),
    zoom: 5,
  }),
});

Crear el popup

Hasta ahora nos hemos limitado a crear las capas que forman el mapa. Es en este momento cuando comenzamos a desarrollar el popup. Necesitamos los elementos que contengan el popup, para hacer eso escribimos lo siguiente:

var container = document.getElementById('popup'),
    content_element = document.getElementById('popup-content'),
    closer = document.getElementById('popup-closer');

closer.onclick = function() {
    overlay.setPosition(undefined);
    closer.blur();
    return false;
};

Como podemos ver en el código anterior, tenemos un elemento para el popup, otro para el contenido del popup (popup-content) y por último el elemento para cerrar el popup (popup-closer). En la segunda parte del código escribimos un evento para cerrar el popup.

Definimos el popup utilizando la clase Overlay de Openlayers de la siguiente forma:

var overlay = new Overlay({
    element: container,
    autoPan: true,
    offset: [0, -10]
});
map.addOverlay(overlay);

La clase Overlay tiene muchas opciones. En este ejemplo hemos utilizado tres de ellas:

  • element: es el elemento de superposición, que en este caso lo hemos llamado container.
  • autopan: con esta opción se desplaza el mapa para hacer que el overlay (popup) quede encuadrado en una vista del mapa que permita se vea de forma completa.
  • offset: desplazamientos en píxeles utilizados al colocar la superposición. El primer elemento de la matriz es el desplazamiento horizontal. Un valor positivo desplaza la superposición a la derecha. El segundo elemento de la matriz es el desplazamiento vertical. Un valor positivo desplaza la superposición hacia abajo.

Creamos el evento que nos abre el popup al hacer clic sobre el marcador:

map.on('click', function(evt){
    var feature = map.forEachFeatureAtPixel(evt.pixel,
      function(feature, layer) {
        return feature;
      });
    if (feature) {
        var geometry = feature.getGeometry();
        var coord = geometry.getCoordinates();
        
        var content = '<h3>Nombre:' + feature.get('name') + '</h3>';
        content += '<h5> Población: ' + feature.get('poblacion') + '</h5>';
        content += '<h5> Región: ' + feature.get('region') + '</h5>';
        content_element.innerHTML = content;
        overlay.setPosition(coord);
        
        console.info(feature.getProperties());
    }
});

Como se puede ver en el código anterior, al hacer clic sobre el marcador estamos insertando los datos contenidos (name, población y región) en el elemento content. De esta forma obtenemos un popup como el que se muestra en la siguiente imagen:

Crear un popup en OpenLayers

Podemos añadir un «refinamiento» en el código, de tal manara que cambie el cursor cuando pasemos el ratón sobre el marcador. Esto lo hacemos de la siguiente forma:

map.on('pointermove', function (e) {
  const pixel = map.getEventPixel(e.originalEvent);
  const hit = map.hasFeatureAtPixel(pixel);
  map.getTarget().style.cursor = hit ? 'pointer' : '';
});

Código completo

Para que resulte más sencillo de seguir reunimos a continuación el código completo del archivo main.js

import 'ol/ol.css';
import Feature from 'ol/Feature';
import Map from 'ol/Map';
import Overlay from 'ol/Overlay';
import Point from 'ol/geom/Point';
import {fromLonLat} from 'ol/proj';
import VectorSource from 'ol/source/Vector';
import View from 'ol/View';
import {Fill, Stroke, Circle, Style} from 'ol/style'; 
import {Tile as TileLayer, Vector as VectorLayer} from 'ol/layer';
import OSM from 'ol/source/OSM';

const layerOSM = 
        new TileLayer({
          source: new OSM()
        });

const markerFeature = new Feature({
      geometry: new Point(fromLonLat([-4.72, 41.66])),
      name: 'Valladolid',
      poblacion: 299265,
      region: 'Castilla y León',
    });

const markerStyles = new Style({
          image: new Circle({
            radius: 8,
            fill: new Fill({
              color: 'orange'}),    
            stroke: new Stroke({
              color: 'black'})
          })
        })

markerFeature.setStyle(markerStyles);

const vectorSource = new VectorSource({
  features: [markerFeature],
});

const vectorLayer = new VectorLayer({
  source: vectorSource,
});


const map = new Map({
  layers: [layerOSM, vectorLayer],
  target: document.getElementById('map'),
  view: new View({
    center: fromLonLat([-4.72, 41.66]),
    zoom: 5,
  }),
});

//Creamos el popup

var container = document.getElementById('popup'),
    content_element = document.getElementById('popup-content'),
    closer = document.getElementById('popup-closer');

closer.onclick = function() {
    overlay.setPosition(undefined);
    closer.blur();
    return false;
};
var overlay = new Overlay({
    element: container,
    autoPan: true,
    offset: [0, -10]
});
map.addOverlay(overlay);

map.on('click', function(evt){
    var feature = map.forEachFeatureAtPixel(evt.pixel,
      function(feature, layer) {
        return feature;
      });
    if (feature) {
        var geometry = feature.getGeometry();
        var coord = geometry.getCoordinates();
        
        var content = '<h3>Nombre:' + feature.get('name') + '</h3>';
        content += '<h5> Población: ' + feature.get('poblacion') + '</h5>';
        content += '<h5> Región: ' + feature.get('region') + '</h5>';
        content_element.innerHTML = content;
        overlay.setPosition(coord);
        
        console.info(feature.getProperties());
    }
});

// Cambiamos el cursor al pasar sobre el elemento
map.on('pointermove', function (e) {
  const pixel = map.getEventPixel(e.originalEvent);
  const hit = map.hasFeatureAtPixel(pixel);
  map.getTarget().style.cursor = hit ? 'pointer' : '';
});

El archivo index.html

Continuamos la aplicación mediante el archivo index.html, cuya explicación nos llevará menos tiempo pues se trata de un código muy sencillo.

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <title>Popup</title>
    <link rel="stylesheet" href="style.css">  
    <style>
      .map {
        width: 100%;
        height:400px;
      }
    </style>
  </head>
  <body>
    <div id="map" class="map">
     
      <div id="popup" class="ol-popup">
            <a href="#" id="popup-closer" class="ol-popup-closer"></a>
            <div id="popup-content"></div>
        </div>

  </div>
    <script src="main.js"></script>
  </body>
</html>

Como podemos ver en el código anterior, el único elemento a destacar es que además del habitual id map, hemos creado otros tres identificadores: popup, popup-closer y popup-content, que como recordarás son los elementos que definimos en el archivo main.js.

En este código también hemos añadido la referencia al archivo de estilos style.css que describimos en el siguiente apartado.

Definiendo estilos para el popup

Necesitamos definir unos estilos para los elementos creado con el Overlay del OpenLayers. Estos estilos son los siguientes, obtenidos de los ejemplos de la documentación de OpenLayers. El contenido del archivo style.css es el que vemos a continuación:

 .ol-popup {
  position: absolute;
  background-color: white;
  box-shadow: 0 1px 4px rgba(0,0,0,0.2);
  padding: 15px;
  border-radius: 10px;
  border: 1px solid #cccccc;
  bottom: 12px;
  left: -50px;
  min-width: 280px;
}
.ol-popup:after, .ol-popup:before {
  top: 100%;
  border: solid transparent;
  content: " ";
  height: 0;
  width: 0;
  position: absolute;
  pointer-events: none;
}
.ol-popup:after {
  border-top-color: white;
  border-width: 10px;
  left: 48px;
  margin-left: -10px;
}
.ol-popup:before {
  border-top-color: #cccccc;
  border-width: 11px;
  left: 48px;
  margin-left: -11px;
}
.ol-popup-closer {
  text-decoration: none;
  position: absolute;
  top: 2px;
  right: 8px;
}
.ol-popup-closer:after {
  content: "✖";
}

Si quieres aprender a trabajar con OpenLayers + node.js, inscríbete ya en nuestro curso online desarrollo de aplicaciones webmapping.

Let’s connect!

Date de alta en nuestra newsletter y te enviaremos nuestra propuesta con las mejores
Soluciones GIS basadas en software libre

Tan solo una vez al mes recibirás las últimas novedades del sector GIS y de nuestros cursos

Deja un comentario