MapSnippets logoMapSnippets logo

Data-Driven Choropleth with MapLibre GL JS

MapLibre GL JS

Create a data-driven choropleth map using GeoJSON polygons and match expressions to color regions by value.

progress_activityLoading map...
const map = new maplibregl.Map({
  container: 'map',
  style: 'https://api.maptiler.com/maps/streets-v4/style.json?key=YOUR_MAPTILER_KEY',
  center: [10, 50],
  zoom: 4
});
map.on('load', () => {
  const data = { type: 'FeatureCollection', features: [
    { type: 'Feature', properties: { name: 'France', value: 67 }, geometry: { type: 'Polygon', coordinates: [[[-5,42],[8,42],[8,51],[-5,51],[-5,42]]] } },
    { type: 'Feature', properties: { name: 'Germany', value: 83 }, geometry: { type: 'Polygon', coordinates: [[[6,47],[15,47],[15,55],[6,55],[6,47]]] } },
    { type: 'Feature', properties: { name: 'Spain', value: 47 }, geometry: { type: 'Polygon', coordinates: [[[-10,36],[4,36],[4,44],[-10,44],[-10,36]]] } },
    { type: 'Feature', properties: { name: 'Italy', value: 60 }, geometry: { type: 'Polygon', coordinates: [[[7,37],[19,37],[19,47],[7,47],[7,37]]] } }
  ] };
  map.addSource('countries', { type: 'geojson', data });
  const labels = map.getStyle().layers.find(l => l.type === 'symbol' && /label/.test(l.id));
  map.addLayer({ id: 'choropleth', type: 'fill', source: 'countries',
    paint: { 'fill-color': ['interpolate', ['linear'], ['get', 'value'], 40, '#fee5d9', 60, '#fcae91', 80, '#fb6a4a', 100, '#cb181d'], 'fill-opacity': 0.6 }
  }, labels ? labels.id : undefined);
  map.addLayer({ id: 'choropleth-border', type: 'line', source: 'countries', paint: { 'line-color': '#333', 'line-width': 1 } });
  map.on('click', 'choropleth', (e) => {
    const f = e.features[0];
    new maplibregl.Popup().setLngLat(e.lngLat).setHTML(`<b>${f.properties.name}</b><br>Pop: ${f.properties.value}M`).addTo(map);
  });
});

How it works

A choropleth map colors regions based on a numeric data value. This example uses inline GeoJSON with simplified European country boundaries and a population property.

The interpolate expression creates a continuous color ramp from the value property. Low values get light colors, high values get dark red. You can also use step for discrete color breaks instead of a gradient.

Clicking a region shows a popup with the country name and value. The fill layer is inserted below labels using beforeId detection.

Key APIs

API Description Docs
interpolate expression Map numeric values to colors maplibre.org docs
step expression Discrete color breaks maplibre.org docs
['get', 'prop'] Read a feature property maplibre.org docs

FAQ

Should I use interpolate or step for a choropleth?

Use step for discrete categories (e.g., low/medium/high). Use interpolate for a smooth gradient across a numeric range.

How do I load real country boundaries?

Use an external GeoJSON file with accurate geometries. Load it via a URL in the source data property or fetch it and pass the parsed object.

Common mistakes

  • Forgetting to insert the fill layer below labels with beforeId
  • Using colors that are too similar: ensure enough contrast between value ranges
  • Not closing polygon rings: the first and last coordinate must be identical
auto_awesome

Build maps using MapLibre GL JS with AI

Generate code like this data-driven choropleth with maplibre gl js example using the MapLibre GL JS skill in your IDE.