Zoomable Choropleth Map from GeoJSON with Leaflet

This article shows how to overlay the GeoJSON on the web map with labels above it. You will also learn how to style GeoJSON with polygon features. We will also guide you through adding a pop-up and legend so that your final choropleth map can be easily understandable to users. This article uses a JavaScript library Leaflet. Example code of shown choropleth map is available on Github.

We will work with the JavaScript library Leaflet. If you don’t have preferences for the JavaScript map library and want to make a choropleth map most easily, we recommend using the MapLibre library (link at the end of this article). For the choropleth map in the OpenLayers library, follow the link at the end of this article. We test the code in the chrome browser.

Click here to see how to prepare data used in this tutorial and how to upload GeoJSON to MapTiler Cloud.

Prepare a basemap and label layer

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 (or new map → open customize). This version of the Streets map 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.

If we want to display labels over GeoJSON, we will need 2 separate tile layers: basemap without labels and just labels. You can create them easily with the MapTiler Cloud customize tool.
In customize tool click on streets → Dark version → switch off placenames → save → name your new basemap → createPublish.

customize.png

Similarly, you can create labels “map”. However, we recommend creating a label “map” from the Satelite Hybrid map in the advanced editor. Go to MapTiler Cloud → new map → open editor → select Satelite Hybrid, edit a copy → remove all unnecessary layers (Satellite, tunnel, path, road, railway, and road labels) and save and publish.

Create Choropleth Web map with Leaflet Library

Go to MapTiler Cloud and select your basemap (without labels). Under Use vector styles, select Leaflet GL and copy the sample code. You can also get a code for a raster map (raster tiles → Leaflet), but vectors look nicer and have a smoother transition when you zoom to the map.

The sample code already has an HTML head and body structure. The body is <div> element that holds the map and script (with a defined map view and map style with your unique MapTiler API key). MapTiler offers several methods to give you control over the key to avoid misuse. Read more about the possible protection of keys in this article.

Add GeoJSON to Sample Code.

To add a GeoJSON from an external URL to the leaflet, we will need to use the Leaflet- AJAX plugin. To install this plugin, paste to the head of your code the following line:

<script type="text/javascript" src="https://calvinmetcalf.github.io/leaflet-ajax/dist/leaflet.ajax.js"></script>

Now create a new variable, GeoJSON, which will hold new L.GeoJSON.AJAX and define its source.
The second line adds GeoJSON to a map.

var geojson = new L.GeoJSON.AJAX("https://api.maptiler.com/data/55336072-ef0f-4cb5-8671-b8d6ebbb0a05/features.json?key=slEr7lXnTotdWgNA0oLf",{});
geojson.addTo(map);

Create a pane that will display labels on the top of GeoJSON

First, we will need to add a tile layer with labels. Go to MapTiler Cloud and select your label map (all other layers are turned off). Under Raster tiles, select Leaflet. From sample code, copy var map and place it between basemap code and Gadd GeoJSON code.

var map = L.map('map').setView([24.65605, 9.16266], 2);
      L.tileLayer('https://api.maptiler.com/maps/e33ecb33-c503-4fb5-984a-842dfb7db712/{z}/{x}/{y}.png?key=<get-your-own>',{
        tileSize: 512,
        zoomOffset: -1,
        minZoom: 1,
        attribution: "\u003ca href=\"https://www.maptiler.com/copyright/\" target=\"_blank\"\u003e\u0026copy; MapTiler\u003c/a\u003e \u003ca href=\"https://www.openstreetmap.org/copyright\" target=\"_blank\"\u003e\u0026copy; OpenStreetMap contributors\u003c/a\u003e",
        crossOrigin: true
      }).addTo(map);

Now is the time to create a pane. Pane define in which order will be layers displayed. In the following table, you can see default pane settings.

Pane Z-index Description
mapPane 'auto' The pane that contains all other map panes
tilePane 200 Pane for GridLayers and TileLayers
overlayPane 400 Pane for overlay shadows (e.g., Marker shadows)
shadowPane 500 Pane for vectors (Paths, like Polylines and Polygons), ImageOverlays,  VideoOverlays, circles, and GeoJSONs
markerPane 600 Pane for Icons of Markers
tooltipPane 650 Pane for Tooltips.
popupPane 700 Pane for Popups.

We will create a pane for the labels layer. We must define the pane before it is called. For the pane, we define z-index, which defines which panes can overlap our new labels pane. With z-index 550, our labels can be overlapped with markers, tooltips, and popups. One of the problems of having a raster label on top of the basemap is that the tiles will capture clicks and touches. If you click anywhere on the map, the web browser will assume that you clicked on the labels and not on the GeoJSON. This can be solved using the pointer-events.

map.createPane('labels');
map.getPane('labels').style.zIndex = 550;
map.getPane('labels').style.pointerEvents = 'none';

Assign label pane to the label tiles definition.

var darkStreetsLabels = L.tileLayer(
        ...
        crossOrigin: true,
        pane: 'labels'
      }).addTo(map);

Set colors of GeoJSON based on attribute values

To set the color of GeoJSON, we need to create a function which returns style properties. In fillColor we will refer to functions getColor that will take values from the GeoJSON properties value of age.

function getStyle(geojson) {
	return {
		weight: 1, //size of outline
		opacity: 1, //outline opacity
		color: 'black', //colour of outline
		fillOpacity: 1,
		fillColor: getColor(geojson.properties.age),
	};
}

We must define the getColor function before getStyle functions. GetColor function defines how to color GeoJSON in different values of the age.

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 your audience’s right color.

function getColor(age) {
	return age > 30 ? '#8c2d04' :
			age > 29  ? '#d94801' :
			age > 28  ? '#f16913' :
			age > 27  ? '#fd8d3c' :
			age > 26  ? '#fdae6b' :
			age > 25  ? '#fdd0a2' :
			age > 24  ? '#fee6ce' :
						'#fff5eb';
}

Add Legend

Legend helps users to understand a map. First, we will create a control for our legend.

var legend = L.control({position: 'bottomright'});

Then, we need to fill it with content. Legend is contained of the header, defined in div.innerHTML and pairs of colors and labels.

legend.onAdd = function (map) {
	var div = L.DomUtil.create('div', 'info legend'),
		grades = [23, 24, 25, 26, 27, 28, 29, 30],
		labels = [],
		from, to;
    for (var i = 0; i < grades.length; i++) {
		from = grades[i];
		to = grades[i + 0.9];
		labels.push(
			'<i style="background:' + getColor(from + 1) + '"></i> ' +
			from + (to ? '&ndash;' + to : ' - ' + grades[i] + '.9')
		);
    }
	div.innerHTML ='<h3>Mean age of </br> women at first marriage </br> in 2019</h3>' + labels.join('<br>');
	return div;
};

To display our legend, we need to add it to a map.

legend.addTo(map);

Add a popup

Choropleth maps are great for showing the general pattern of some phenomena. This usually interests the user and lets him think more about details or concrete values. In our case, we can see that Scandinavian countries have a mean age of women at first marriage higher than 30, but we cannot see which of them have the highest. We can show the absolute value of the mean age of women at first marriage in a popup.

For displaying popup with values from GeoJSON, we need a function that will be applied to each part of GeoJSON and will display required GeoJSON properties.

function popup(geojson, layer){
	onEachFeature: {var out = [];
    layer.bindPopup('<h3>'+geojson.properties.NAME_ENGL+'</h3><p>age: '+geojson.properties.age+'</p>')
  	}
  }

Conclusion

Now you can create a basic choropleth map. You can add GeoJSON to a web map under raster labels. Change style of GeoJSON based on GeoJSON properties values. Add popups to GeoJSON feature with its properties and add a legend that will help the users understand the map and information you are presenting on the map.

Zoomable choropleth map from GeoJSON with MapLibre
Prepare GeoJSON with attributes for choropleth map and upload GeoJSON to MapTiler Cloud
MapTiler Cloud
GitHub - Leaflet Ajax
Leaflet interactive choropleth map tutorial
Leaflet custom panes tutorial