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 → create → Publish.
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 GridLayer s and TileLayer s |
overlayPane |
400 |
Pane for overlay shadows (e.g., Marker shadows) |
shadowPane |
500 |
Pane for vectors (Path s, like Polyline s and Polygon s), ImageOverlay s, VideoOverlay s, circles, and GeoJSONs |
markerPane |
600 |
Pane for Icon s of Marker s |
tooltipPane |
650 |
Pane for Tooltip s. |
popupPane |
700 |
Pane for Popup s. |
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 ? '–' + 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.
Useful links
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
Related guides
- Automatically created API key
- Check if MapLibre GL JS is supported
- Coordinates API
- Dataset upload - formats and limits
- Difference between 256x256, 512x512, and HiDPI/Retina rasterized tiles
- Exported Tiles Multiplier
- Generalization in maps
- How are the tile requests cached in web browser?
- How MapTiler map tiles are Generated and Delivered
- How to add Geocoding control to Maplibre GL JS map