Zoomable Choropleth Map from GeoJSON with MapLibre

This article demonstrates how to overlay the GeoJSON on the web map under labels. You will also learn how to style GeoJSON with polygon features. We will also guide you through the process of adding a pop-up and map legend so that your final choropleth map can be easily understandable to users. This article uses the open-source javascript library MapLibre. Example code of shown choropleth map is available on GitHub.

We will work with the open-source javascript map library MapLibre. If you want to create a choropleth map in Leaflet or OpenLayers library, follow the links at the end of this article. As an example data, we use countries with attributes from EUROSTAT in GeoJSON format. Here is explained how to prepare example data, which are used in this tutorial, and how to upload GeoJSON to MapTiler Cloud.

Create Choropleth Web map with MapLibre Library

Go to MapTiler Cloud and choose a base map. We chose the Dark Street map for this demo, which you can find under Streets → customize a copy. This version of the Streetmap was specially designed so your content can have the full focus of the user. The MapTiler Customize tool offers you an easy way to adjust the map’s colors to your needs.

customize.png

After you Save the map you name it. Click on Create and then on Publish. Now, under Use vector style, click on MapLibre GL JS.

use_vector.png

Here you can copy the sample code which has already defined the base map with your unique MapTiler API key. If you publish a map, you are also exposing your map key. MapTiler offers several methods to give you control over the key to avoid misuse. Read more about the possible protection of keys here.

Add GeoJSON to Sample Code.

To <script> section of this sample code, we will first add GeoJSON source. The path to GeoJSON itself is defined in data and leads to hosted GeoJSON on MapTiler Cloud (After you insert the following code, your GeoJSON will not be visible!) This source will be named age.

 map.on('load', function () {
      map.addSource('age', {
          'type': 'geojson',
          'data': 'https://api.maptiler.com/data/42ea42e6-6cf3-42a4-91c1-00dad9fb008d/features.json?key=<get-your-own>'
      });

We want to include GeoJSON under the label. That means we need to store labels from the base map style in some variable. In this example, the variable is called firstSymbolId.

    var layers = map.getStyle().layers;
    var firstSymbolId;
      for (var i = 0; i < layers.length; i++) {
      if (layers[i].type === 'symbol') {
      firstSymbolId = layers[i].id;
      break;
      }
    }

Now we can add GeoJSON source as a layer to our map and color it by mean age. We also need to include firsSymbolID to map.addLayer to display GeoJSON under labels.

    map.addLayer(
        {
            'id': 'IDage',
            'source': 'age',
            'type': 'fill',
            'paint': {
                'fill-color': [
                    'interpolate',
                    ['linear'],
                    ['get', 'age'],
                    23,
                    '#fff5eb',
                    24,
                    '#fee6ce',
                    25,
                    '#fdd0a2',
                    26,
                    '#fdae6b',
                    27,
                    '#fd8d3c',
                    28,
                    '#f16913',
                    29,
                    '#d94801',
                    30,
                    '#8c2d04'
                ],
                'fill-opacity': 1
            }
        },
     firstSymbolId   
    );

PRO TIP: Do not choose colors randomly. If you do, color-blind people might have problems reading your map. Use cartographic tools like a color brewer, which can help you find the right color for your audience.

The states with the same color (e.g. Portugal and Spain) might be hard to distinguish at this stage. We can fix that by adding an outline. Don’t forget to add firstSymbolId again. Without them, the outline might cut labels.

    map.addLayer({
      'id': 'outline',
      'type': 'line',
      'source': 'age',
      'layout': {},
      'paint': {
        'line-color': '#000', // black outline
        'line-width': 1
      }
    }, firstSymbolId );

Add popup to GeoJSON

To add a popup, we need to create a new map.on constructor, which will be type click.

map.on('click', 'IDage', function (e) {
      new maplibregl.Popup()
          .setLngLat(e.lngLat)
          .setHTML('<h3>' + "Average age of </br> women at first marriage" + '</h3><p>' + e.features[0].properties.age+ '</p>')
          .addTo(map);
      });

For the popup itself, we need to set Latitude and longitude. This is done by function (e), which takes coordinates of the place where the user clicks. We can fill popup with .setHTML(). This method combines normal text and part of our GeoJSON. Our GeoJSON has three properties: id, Age and NAME_ENGL. We can call them in .setHTML as e.features[0].properties.<name of propertie>

To alert users of popups, you might change the cursor to a pointer when the mouse is over the states layer and change it back when it leaves.

 map.on('mouseenter', 'IDage', function () {
        map.getCanvas().style.cursor = 'pointer';
    });

    // Change it back to a pointer when it leaves.
    map.on('mouseleave', 'IDage', function () {
        map.getCanvas().style.cursor = '';
    });
  });

Add a legend to a choropleth map

Legend helps users to understand a map, and it’s easy to make with HTML. Go to the <body> section of your code and include the following code, which defines the legend header, colors for each category, and their meaning.

<div id="state-legend" class="legend"> 
  <h4>Average age of </br> women at first marriage </h4>
    <div><span style="background-color: #fff5eb"></span>23</div>
    <div><span style="background-color: #fee6ce"></span>24</div>
    <div><span style="background-color: #fdd0a2"></span>25</div>
    <div><span style="background-color: #fdae6b"></span>26</div>
    <div><span style="background-color: #fd8d3c"></span>27</div>
    <div><span style="background-color: #f16913"></span>28</div>
    <div><span style="background-color: #d94801"></span>29</div>
    <div><span style="background-color: #8c2d04"></span>30</div>
</div>

To display colours we need to add some styling to our <style> in <head>.

    .legend {
      background-color: #000;
      border-radius: 3px;
      bottom: 30px;
      box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
      color: #fff ;
      font: 12px/20px 'Helvetica Neue', Arial, Helvetica, sans-serif;
      padding: 10px;
      position: absolute;
      right: 10px;
      z-index: 1;
  }

  .legend h4 {
      margin: 0 0 10px;
  }

  .legend div span {
      border-radius: 50%;
      display: inline-block;
      height: 10px;
      margin-right: 5px;
      width: 10px;
  }

Conclusion

Now you can add GeoJSON to a web map under a certain level of geometries from basemap and style it according to the values of GeoJSON properties. Your map can also have Popups indicated by the change between cursor and pointer and a legend that will help the users understand the map and information you are presenting.

Zoomable choropleth map from GeoJSON with Leaflet
Zoom levels, pixels, and scale - How they are connected
MapLibre documentation
Dark & Night map