Join MapTiler Countries with your own custom data and make a choropleth map
A Choropleth map is the type of thematic map used for the visualization of statistical data on areas with colors or structured symbology. It is used to show the population in each country, election maps, etc. MapTiler Countries is designed for that use. Read more about countries if you need general information or see schema documentation. This article shows how to visualize your data with the MapLibre JavaScript library to make an interactive choropleth map.
Add layer and styling
Get the link to the layer and your key from MapTiler Cloud.
// Add MapTiler Countries dataset
map.addSource('statesData', {
type: 'vector',
url: 'https://api.maptiler.com/tiles/countries/tiles.json?key=' + key
});
// Styling for countries layer with linear interpolation of data
map.addLayer({
id: 'countries',
type: 'fill',
source: 'statesData',
'source-layer': 'administrative',
filter: ['==', 'level', 0],
paint: {
'fill-color':
['case',
['!=', ['to-number', ['feature-state', 'population']], 0],
['interpolate', ['linear'], ['feature-state', 'population'], 5000000, 'rgba(222,235,247,1)', 90000000, 'rgba(49,130,189,1)'],
'rgba(0, 0, 0, 0)'
]
}
}, 'state_label');
Prepare your data
Each feature in the dataset has its own identification for pairing. You need to mark your data with the same identifiers as are stored by features. We are using iso_n3 identifiers for countries. Alternatively, you can use one of the properties: name, country codes (ISO A2), or wiki data-id. Here is a table in CSV with the list.
var vizData = {
"8":{"name":"Albania","population":2829741},
"40":{"name":"Austria","population":8932664},
"56":{"name":"Belgium","population":11566041},
"100":{"name":"Bulgaria","population":6916548},
...
};
Attribution join of the data
First, you need to use querySourceFeatures to get all features from the layer for visualization. We are also filtering for all countries with [’==’, ‘level’, 0] using style expressions. The second part of the following code goes throw these features and adds attributes from our data array (vizData) to relevant features.
// Join the data to coresponding features
function setStates(e) {
var countries = map.querySourceFeatures('statesData', {
sourceLayer: 'administrative',
filter: ['==', 'level', 0],
});
countries.forEach(function(row) {
if(row.id && vizData[row.id]) {
map.setFeatureState({
source: 'statesData',
sourceLayer: 'administrative',
id: row.id
}, {
population: vizData[row.id].population
});
}
});
}
Join by properties, name or iso_a2
Based on your data, you can use other properties of the feature to join. Look at the following sample with joining by iso_a2 identifier:
var vizData = {
"CZ":{"name":"Czechia","population":10701777},
"SK":{"name":"Slovakia","population":5459781},
...
}
...
countries.forEach(function(row) {
if(row.id && vizData[row.properties.iso_a2]) {
map.setFeatureState({
source: 'statesData',
sourceLayer: 'administrative',
id: row.id
}, {
population: vizData[row.properties.iso_a2].population
});
}
});
Full example
You can open this map in a new window for a better browsing experience.
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no" />
<meta name="author" content="MapTiler AG">
<meta name="keywords" content="map, choropleth, maptiler">
<meta name="description" content="Sample choropleth map with MapTiler Countries.">
<title>MapTiler Countries with own data</title>
<script src="https://cdn.maptiler.com/maplibre-gl-js/v1.14.0/maplibre-gl.js"></script>
<link href="https://cdn.maptiler.com/maplibre-gl-js/v1.14.0/maplibre-gl.css" rel="stylesheet" />
<style>
#map {position: absolute; top: 0; right: 0; bottom: 0; left: 0;}
</style>
</head>
<body>
<div id="map"></div>
<p><a href="https://www.maptiler.com/copyright/" target="_blank">© MapTiler</a> <a href="https://www.openstreetmap.org/copyright" target="_blank">© OpenStreetMap contributors</a></p>
<script>
var key = '<your_key>';
var map = new maplibregl.Map({
container: 'map',
style: 'https://api.maptiler.com/maps/hybrid/style.json?key=' + key,
center: [15.34, 49.77],
zoom: 3
});
// Data for visualization
var vizData = {
"8":{"name":"Albania","population":2829741},
"40":{"name":"Austria","population":8932664},
"56":{"name":"Belgium","population":11566041},
"100":{"name":"Bulgaria","population":6916548},
"191":{"name":"Croatia","population":4036355},
"196":{"name":"Cyprus","population":896005},
"203":{"name":"Czechia","population":10701777},
"208":{"name":"Denmark","population":5840045},
"233":{"name":"Estonia","population":1330068},
"246":{"name":"Finland","population":5533793},
"250":{"name":"France","population":67439599},
"276":{"name":"Germany","population":83155031},
"300":{"name":"Greece","population":10682547},
"348":{"name":"Hungary","population":9730772},
"352":{"name":"Iceland","population":368792},
"372":{"name":"Ireland","population":5006907},
"380":{"name":"Italy","population":59257566},
"428":{"name":"Latvia","population":1893223},
"438":{"name":"Liechtenstein","population":39055},
"440":{"name":"Lithuania","population":2795680},
"442":{"name":"Luxembourg","population":634730},
"470":{"name":"Malta","population":516100},
"499":{"name":"Montenegro","population":620739},
"528":{"name":"Netherlands","population":17475415},
"578":{"name":"Norway","population":5391369},
"616":{"name":"Poland","population":37840001},
"620":{"name":"Portugal","population":10298252},
"642":{"name":"Romania","population":19186201},
"688":{"name":"Serbia","population":6871547},
"703":{"name":"Slovakia","population":5459781},
"705":{"name":"Slovenia","population":2108977},
"724":{"name":"Spain","population":47394223},
"752":{"name":"Sweden","population":10379295},
"756":{"name":"Switzerland","population":8667088},
"792":{"name":"Turkey","population":83614362},
"807":{"name":"North Macedonia","population":2068808}
};
map.on('load', function () {
// Add MapTiler Countries dataset
map.addSource('statesData', {
type: 'vector',
url: 'https://api.maptiler.com/tiles/countries/tiles.json?key=' + key
});
// Styling for countries layer with linear interpolation of data
map.addLayer({
id: 'countries',
type: 'fill',
source: 'statesData',
'source-layer': 'administrative',
filter: ['==', 'level', 0],
paint: {
'fill-color':
['case',
['!=', ['to-number', ['feature-state', 'population']], 0],
['interpolate', ['linear'], ['feature-state', 'population'], 5000000, 'rgba(222,235,247,1)', 90000000, 'rgba(49,130,189,1)'],
'rgba(0, 0, 0, 0)'
]
}
}, 'state_label');
// Join the data to coresponding features
function setStates(e) {
var countries = map.querySourceFeatures('statesData', {
sourceLayer: 'administrative',
filter: ['==', 'level', 0],
});
countries.forEach(function(row) {
if(row.id && vizData[row.id]) {
map.setFeatureState({
source: 'statesData',
sourceLayer: 'administrative',
id: row.id
}, {
population: vizData[row.id].population
});
}
});
}
function afterLoad(e) {
if (e.sourceId === 'statesData' && e.isSourceLoaded) {
setTimeout(setStates, 1000);
map.off('sourcedata', afterLoad);
}
}
if (map.isSourceLoaded('statesData')) {
setStates();
} else {
map.on('sourcedata', afterLoad);
}
});
</script>
</body>
</html>
Useful links
Download Administrative Level 0 Table
Download Administrative Level 1 Table
Download Administrative Level 2 Table
Download Administrative Level 3 Table
Download Postal Level 0 Table
Download Postal Level 1 Table
Download Postal Level 1 (extra) Table
Related guides
- Automatically created API key
- Check if MapLibre GL JS is supported
- Dataset upload - formats and limits
- Difference between 256x256, 512x512, and HiDPI/Retina rasterized tiles
- Disputed borders on your maps
- Exported Tiles Multiplier
- Generalization in maps
- How are maps used in NFT tokens
- How are the tile requests cached in web browser?
- How MapTiler map tiles are Generated and Delivered