A choropleth map is the most common thematic map you will see in the news, in policy reports, and on company dashboards. If you have ever looked at an election map shaded by margin, a COVID map shaded by case rate, or a GDP map shaded by income, you have read a choropleth.
This guide explains what a choropleth map is, when to use one, the most common mistakes, and how to build a production choropleth map with a vector tile API.
The One-Sentence Definition
A choropleth map shades each region of the map according to a single numeric value measured for that region, using a colour scale that encodes magnitude.
The word comes from the Greek khoros (area) and plethos (multitude). It dates back to French statisticians in the 1820s and has been the workhorse of thematic cartography ever since.
Choropleth Map Examples
Some classic uses:
- Population density by country, state, or postal code
- Election results by district, with colour intensity showing margin of victory
- GDP per capita by country, often using a logarithmic scale
- Unemployment rate by region
- COVID case rate per 100,000 residents by county
- Average property price by neighbourhood
- Broadband adoption by census tract
- Crop yield by agricultural region
The unifying feature is that the data is a single number per pre-defined region.
Choropleth Map vs Heat Map
The two are often confused. The difference matters.
A choropleth map needs predefined regions. The data is already aggregated. Each region gets one colour. The boundaries are part of the message.
A heat map needs point data. Density is computed by binning or kernel smoothing. The output is a continuous gradient that ignores administrative boundaries. Use it for cluster discovery, not for region comparison.
If you have one number per postal code, use a choropleth. If you have a cloud of GPS pings, use a heat map.
How a Choropleth Map Is Built
Three ingredients:
- Boundary geometry. A set of GeoJSON polygons (or vector tile layers) for the regions you want to colour, each with a stable identifier (e.g. ISO country code, US state FIPS, EU NUTS code, postal code).
- A data value per region. A simple JSON object or CSV keyed by the same identifier.
- A renderer that joins them. Modern web mapping libraries (MapLibre GL, Mapbox GL, deck.gl, OpenLayers) can join data to boundaries at runtime using a feature-state or property expression.
Here is the conceptual pattern in MapLibre GL JS:
map.addSource('regions', {
type: 'vector',
url: 'https://api.mapatlas.xyz/v1/tiles/regions.json',
});
map.addLayer({
id: 'regions-fill',
type: 'fill',
source: 'regions',
'source-layer': 'regions',
paint: {
'fill-color': [
'interpolate',
['linear'],
['feature-state', 'value'],
0, '#f7fbff',
50, '#6baed6',
100, '#08306b',
],
'fill-opacity': 0.8,
},
});
// Push your data to each feature at runtime
for (const [regionId, value] of Object.entries(yourData)) {
map.setFeatureState(
{ source: 'regions', sourceLayer: 'regions', id: regionId },
{ value }
);
}
The map fetches boundary tiles once and re-styles them as your data changes. No server-side re-rendering.
Choosing the Right Colour Scale
Three rules:
Sequential for single-direction data. Light to dark in one hue. Used for quantities that go from low to high with no meaningful midpoint (population, income, rate).
Diverging for two-direction data. A neutral midpoint and two hues fanning out (red and blue, brown and teal). Used when zero or the average is meaningful and values can sit on either side (year-over-year growth, election margin, deviation from mean).
Avoid rainbow scales. They imply an ordering that human perception does not match uniformly. They also fail for colour-blind viewers. ColorBrewer (colorbrewer2.org) is the cartographer-tested standard for choropleth palettes.
A common mistake is to use too many bins. Five to seven is the sweet spot. More than that and the reader cannot distinguish adjacent shades.
The Classification Question
Once you have a scale, you must decide where to draw the bin boundaries:
- Equal interval. Splits the range into even chunks. Works for uniformly distributed data, fails when there are outliers.
- Quantile. Each bin holds the same number of regions. Useful when you want every shade visible, but it can hide the magnitude of differences.
- Natural breaks (Jenks). Minimises variance within bins. The most data-driven default.
- Manual. When the bin boundaries carry meaning (e.g. tax brackets, demographic thresholds).
The choice has a real effect on the story the map tells. Publish the classification method in any serious report.
Common Pitfalls
Area bias. Large, low-density regions visually dominate the map. A choropleth of US presidential election results coloured by margin will look mostly red because rural counties are huge. Cartograms or dot-density maps fix this.
Raw counts instead of rates. A map of "COVID cases per state" is mostly a map of population. Always normalise by population, area, or another denominator unless the absolute count is what you want to convey.
Misleading scale. Quantile scales make every map look interesting even when the underlying spread is tiny. Equal-interval scales make every map look flat when there are outliers. Match the classification to the question.
Missing data. Decide upfront whether a region with no data is shown as grey, omitted, or coloured the same as zero. Each choice changes the reading.
When to Pick a Different Chart
If your data is multivariate per region, a choropleth flattens it. Consider:
- Small multiples. A grid of choropleth maps, one per variable.
- Bivariate choropleth. Two variables encoded as a 2D colour grid. Hard to read but powerful when it works.
- Cartogram. Distorts region size by the value, useful when area bias dominates.
- Bar chart on a map. Sometimes the data is just a table; the map is decoration.
Cartography is communication. The choropleth is one tool among many.
Where MapAtlas Fits
The Dynamic Maps API ships vector tile sources for country, region, and postal code boundaries that you can style at runtime with your own data, exactly as in the MapLibre snippet above. The boundaries are EU-hosted, version-stamped, and designed to join cleanly against the standard identifiers (ISO 3166, NUTS, national postal code systems).
For pages that need to display aggregate stats by region (real estate price per postal code, delivery cost per region, sales by territory) the GeoEnrich API returns the per-region values keyed to the same boundary IDs, so the same join works for both the map and your tables. The full thematic-map background is covered in the Thematic Map Guide.
Frequently Asked Questions
What is a choropleth map?
A choropleth map is a thematic map where geographic regions (countries, states, postal codes, census tracts) are shaded or coloured in proportion to a statistical variable measured for each region. The colour intensity encodes the value, so a darker shade typically means a higher value and a lighter shade a lower one. Choropleth maps are the standard way to visualise population density, election results, GDP per capita, unemployment rates, and any other quantity that is naturally aggregated by administrative boundary.
What is the difference between a choropleth map and a heat map?
A choropleth map colours predefined regions (countries, postal codes, districts) based on an aggregated value for each region. A heat map shows the intensity of point data continuously across space, without respecting administrative boundaries. Use a choropleth when your data is already aggregated by region (e.g. votes per state, sales per postal code). Use a heat map when your data is a cloud of points and you want to see where they cluster (e.g. crime incidents, customer pins, sensor readings).
When should I use a choropleth map?
Use a choropleth map when your data is a single numeric value per region and the regions are roughly comparable in size or you can normalise by area or population. Good examples: percentage of households with broadband by county, COVID cases per 100k people by region, average rent by postal code. Avoid choropleth when regions vary wildly in area (large rural regions visually dominate even if they hold less population), or when you have multiple variables per region (use small multiples or a different chart type).
How do I build a choropleth map?
You need three things: boundary geometry for your regions (GeoJSON polygons), a data value for each region keyed by a stable ID, and a renderer that joins the two and applies a colour scale. Common stacks: MapLibre GL or Mapbox GL with a fill-color expression, deck.gl GeoJsonLayer with getFillColor, D3.js with d3-geo for static SVG, or a hosted dynamic map API. The MapAtlas Dynamic Maps API ships boundary tiles for countries, regions, and postal codes that you can style at runtime with your own data.
What is a good colour scale for a choropleth map?
Use a sequential single-hue scale (e.g. light blue to dark blue) when the data is a single quantity with a natural zero. Use a diverging two-hue scale (e.g. red to white to blue) when values diverge around a meaningful midpoint (election margin, year-over-year change). Avoid rainbow scales: they imply ordering that humans do not perceive uniformly and they fail for colour-blind users. ColorBrewer is the standard reference for cartographer-tested palettes.

