D3 Map Visualization Example
A choropleth is a bar chart wearing country shapes. Here's a live D3 world map with geoPath, Equal Earth, and a hover tooltip.
A D3 map visualization example is just a chart whose “bars” happen to be country shapes. You still have values. You still have a color scale. The extra piece is a projection that turns longitude and latitude into x and y.
The live map below loads world-atlas
country outlines, projects them with geoEqualEarth, and colors
a dummy index. Same API you'd use for a GeoJSON export from QGIS. For the
rest of D3, see the D3.js page or the
graph examples.
What you need
- Features. A GeoJSON FeatureCollection. Each feature is a place.
- A projection.
geoMercatoris the one everyone recognizes.geoEqualEarthis the better default for a choropleth — area stays honest. - geoPath. It writes the SVG
dfor you. - A value. Population, rate, whatever. That's what the fill encodes.
I do not start from shapefiles in the browser. Convert once, ship GeoJSON or TopoJSON, draw that.
A D3 map visualization example
Hover a country for the name and a dummy index. Brighter gold is higher. The outlines are Natural Earth 110m. The numbers are fake so you can see the join, the projection, and the scale without a CSV in the way.
Preview
Equal Earth projection · world-atlas countries-110m · hover for the value
The code
fitExtent is the line I forget, then I stare at a tiny smudge
in the corner. It scales the projection so the whole FeatureCollection
fills the SVG. Drop Antarctica or the fit will leave a huge empty south.
Code
const world = await d3.json(
"https://cdn.jsdelivr.net/npm/world-atlas@2/countries-110m.json"
);
const countries = topojson.feature(world, world.objects.countries);
countries.features = countries.features.filter(
(d) => d.properties.name !== "Antarctica"
);
const scores = { "United States of America": 96, China: 88, India: 84 };
countries.features.forEach((d) => {
d.properties.value = scores[d.properties.name] ?? 40;
});
const width = 800;
const height = 440;
const svg = d3.select("#chart").append("svg").attr("viewBox", `0 0 ${width} ${height}`);
const projection = d3.geoEqualEarth()
.fitExtent([[18, 10], [width - 18, height - 52]], countries);
const path = d3.geoPath(projection);
const color = d3.scaleSequential(d3.interpolateYlOrBr).domain([0, 100]);
svg.append("path").datum({ type: "Sphere" }).attr("d", path).attr("fill", "#0e141c");
svg.append("path")
.datum(d3.geoGraticule10())
.attr("d", path)
.attr("fill", "none")
.attr("stroke", "rgba(255,255,255,0.08)");
svg.selectAll("path.country")
.data(countries.features)
.join("path")
.attr("class", "country")
.attr("d", path)
.attr("fill", (d) => color(d.properties.value))
.attr("stroke", "#0a0c10");
Join your own numbers
The preview colors a lookup by properties.name. In a real
chart you load a CSV, Map it on the country name or ISO code,
then color(valueByName.get(d.properties.name)). Names in
Natural Earth are picky: it's
United States of America, not United States.
Need a closer zoom? Use countries-50m.json or a GeoJSON export. If the “map” is really a flow between countries, a D3 sankey chart is usually clearer than drawing lines across the ocean. And if you meant a node-link diagram, that's a force layout, not a choropleth.
Questions I get
How do I make a D3 map visualization?
Give D3 GeoJSON (or TopoJSON you convert first). geoMercator or geoEqualEarth projects lon/lat to pixels. geoPath turns each feature into an SVG d attribute. A sequential scale colors the regions.
Do I need TopoJSON for a D3 map?
No. GeoJSON is enough. TopoJSON is smaller on the wire. world-atlas and us-atlas ship TopoJSON; topojson.feature() turns it into GeoJSON D3 can draw.
Why does my D3 map look empty?
Usually the projection is not fitted to the data, or the coordinates are lat/lon swapped. fitExtent or fitSize on the FeatureCollection fixes the first. GeoJSON is [longitude, latitude].