Interact with VectorTile features

If we want to style the layer we just created, it would be nice to get some information about each geometry we see on the map. The nice thing about vector tile layers is that we can interact with them just like with vector layers. So it is easy to add a listener for clicks to the map, query the features at the clicked position, and display a popup with the attributes of each feature.

Adding a popup

We create a popup style by going to I decided to choose a darker popup. The css is added to the <style> section of our index.html:

.arrow_box {
  position: relative;
  background: #000;
  border: 1px solid #003c88;
.arrow_box:after, .arrow_box:before {
  top: 100%;
  left: 50%;
  border: solid transparent;
  content: " ";
  height: 0;
  width: 0;
  position: absolute;
  pointer-events: none;

.arrow_box:after {
  border-color: rgba(0, 0, 0, 0);
  border-top-color: #000;
  border-width: 10px;
  margin-left: -10px;
.arrow_box:before {
  border-color: rgba(0, 60, 136, 0);
  border-top-color: #003c88;
  border-width: 11px;
  margin-left: -11px;

.arrow_box {
  border-radius: 5px;
  padding: 10px;
  opacity: 0.8;
  background-color: black;
  color: white;

We are going to render scrollable HTML tables into our popup, so our markup for the popup also needs a <div> for the popup content:

<div class="arrow_box" id="popup-container">
  <div id="popup-content"></div>

To style the tables, we add some more style to the <style> section of index.html:

#popup-content {
  max-height: 200px;
  overflow: auto;
#popup-content th {
  text-align: left;
  width: 125px;

In the application's main.js import the Overlay class:

import Overlay from 'ol/Overlay';

Again in the application's main.js, we can now append the code for the popup's Overlay:

const overlay = new Overlay({
  element: document.getElementById('popup-container'),
  positioning: 'bottom-center',
  offset: [0, -10],
  autoPan: true

To make it easy to close the popup so it does not cover other features we might want to click, we add a click listener to the overlay, so it closes when we click on it:

overlay.getElement().addEventListener('click', function() {

Calling setPosition() on an overlay sets an undefined position, which causes the overlay to disappear.

Fill the popup with feature attributes

Now it is time to connect the popup to a click listener on the map. We append more code at the bottom of main.js:

map.on('click', function(e) {
  let markup = '';
  map.forEachFeatureAtPixel(e.pixel, function(feature) {
    markup += `${markup && '<hr>'}<table>`;
    const properties = feature.getProperties();
    for (const property in properties) {
      markup += `<tr><th>${property}</th><td>${properties[property]}</td></tr>`;
    markup += '</table>';
  }, {hitTolerance: 1});
  if (markup) {
    document.getElementById('popup-content').innerHTML = markup;
  } else {

By iterating through all the features we get at the clicked position (map.forEachFeatureAtPixel), we build a separate table for each feature. With each feature, we iterate through its properties (feature.getProperties()), and add a table row (<tr>) for each property. We also set a hitTolerance of 1 pixel to make it easier to click on lines.

Using the interactivity to build a style for our map

Now we can click on any geometry in the map, and use the information we get in the popup to create styles in the next exercise. Note that vector tile features have a special layer property, which indicates the source layer (i.e. the layer the feature belongs to in the vector tile's structure, which is a layer -> feature hierarchy).

Getting feature information
Getting feature information

results matching ""

    No results matching ""