Skip to content

D3.js Geo Maps — Geographic Data Visualization

DodaTech Updated 2026-06-28 4 min read

In this tutorial, you will learn about D3.js Geo Maps. We cover key concepts, practical examples, and best practices to help you master this topic.

D3.js geo maps render geographic data using GeoJSON and TopoJSON formats with configurable projections for creating choropleths, point maps, and interactive cartographic visualizations.

What You'll Learn

By the end of this guide, you will load TopoJSON data, configure d3.geoMercator and other projections, draw country boundaries, create choropleth maps with color scales, add zoom behavior, and plot data points on maps.

Why Geo Maps Matter

Geographic context makes data meaningful. Durga Antivirus Pro uses geo maps to show the geographic origin of cyber attacks in real time, with color gradients indicating threat intensity by region.

flowchart LR
    A[GeoJSON/TopoJSON] --> B[Map Projection]
    B --> C[d3.geoPath]
    C --> D[SVG Path Elements]
    E[Data Values] --> F[Color Scale]
    F --> D
    D --> G[Interactive Map]

Basic Map Setup

var width = 960, height = 600;
var projection = d3.geoMercator()
    .scale(150)
    .translate([width / 2, height / 1.5]);

var path = d3.geoPath().projection(projection);

var svg = d3.select('#map')
    .append('svg')
    .attr('width', width)
    .attr('height', height);

Drawing Countries From TopoJSON

d3.json('https://unpkg.com/world-atlas@2/countries-110m.json')
    .then(function(world) {
        var countries = topojson.feature(world, world.objects.countries);

        svg.selectAll('path')
            .data(countries.features)
            .enter()
            .append('path')
            .attr('d', path)
            .attr('fill', '#4ecdc4')
            .attr('stroke', '#fff')
            .attr('stroke-width', 0.5);
    });

Choropleth Map

var colorScale = d3.scaleThreshold()
    .domain([10, 50, 100, 200, 500, 1000])
    .range(d3.schemeBlues[6]);

d3.json('https://unpkg.com/world-atlas@2/countries-110m.json')
    .then(function(world) {
        var countries = topojson.feature(world, world.objects.countries);

        svg.selectAll('path')
            .data(countries.features)
            .enter()
            .append('path')
            .attr('d', path)
            .attr('fill', function(d) {
                var value = dataMap.get(d.id) || 0;
                return colorScale(value);
            })
            .attr('stroke', '#fff')
            .attr('stroke-width', 0.5)
            .append('title')
            .text(function(d) {
                return d.properties.name + ': ' + (dataMap.get(d.id) || 0);
            });
    });

Zoom and Pan

var zoom = d3.zoom()
    .scaleExtent([1, 8])
    .on('zoom', function(event) {
        svg.selectAll('path').attr('transform', event.transform);
    });

svg.call(zoom);

Adding Data Points

var cities = [
    { name: 'London', coords: [-0.1278, 51.5074] },
    { name: 'Tokyo', coords: [139.6917, 35.6895] },
    { name: 'New York', coords: [-74.0060, 40.7128] }
];

svg.selectAll('circle')
    .data(cities)
    .enter()
    .append('circle')
    .attr('cx', function(d) { return projection(d.coords)[0]; })
    .attr('cy', function(d) { return projection(d.coords)[1]; })
    .attr('r', 4)
    .attr('fill', '#ff6b35');

Common Mistakes

1. Using Wrong Projection for the Region

Mercator distorts areas near the poles. Use d3.geoAlbersUsa for US maps, d3.geoOrthographic for globe view.

2. Forgetting topojson.feature

TopoJSON stores data as arcs. Convert to GeoJSON features with topojson.feature() before passing to d3.geoPath.

3. Path Data Not Visible

If path fills are invisible, check the projection scale and translate. Features may be rendered off-screen.

4. Not Handling Missing Data

Choropleth maps need data for all regions. Use default values for regions with no data.

5. Map Not Centered

Adjust the projection translate to center your map on the region of interest.

Practice Questions

Q1: What is the difference between GeoJSON and TopoJSON? A: GeoJSON stores geometry as coordinates. TopoJSON stores arcs and deduplicates shared boundaries, making files smaller.

Q2: What does d3.geoPath do? A: It converts GeoJSON geometry to SVG path data strings, using the specified projection.

Q3: How do you create a choropleth color scale? A: Use d3.scaleThreshold or d3.scaleQuantize with domain values and a color scheme like d3.schemeBlues.

Q4: How do you add tooltips to map regions? A: Append title elements inside path elements, or use SVG text labels that appear on hover.

Q5: What projection is best for world maps? A: d3.geoEqualEarth or d3.geoNaturalEarth1 for visually pleasing world maps. Mercator for navigation.

Challenge: Build a map showing the locations of the top 10 most populous cities. Size the circles by population. Add a legend showing population ranges.

FAQ

Can I use custom GeoJSON files?

Yes. Load any GeoJSON file with d3.json(). Ensure it uses WGS84 coordinates (longitude, latitude).

How do I handle TopoJSON files?

Load with d3.json() and convert with topojson.feature() and topojson.mesh(). Include the TopoJSON library.

What is the best projection for a US map?

d3.geoAlbersUsa is optimized for the 50 states and Alaska/Hawaii insets.

How do I add map labels?

Project city coordinates with projection() and append text elements at the projected positions.

Can I animate map transitions?

Yes. Transition the fill color of paths or the projection during a zoom transition.

Try It Yourself

Build a choropleth world map with color-coded countries and hover tooltips.

<!DOCTYPE html>
<html>
<head>
    <title>World Choropleth</title>
    <style>
        body { font-family: sans-serif; padding: 20px; background: #1a1a2e; }
        svg { display: block; margin: auto; background: #16213e; border-radius: 12px; }
        .country { stroke: #fff; stroke-width: 0.3; cursor: pointer; }
        .country:hover { stroke-width: 2; }
        #tooltip { position: absolute; background: rgba(0,0,0,0.8); color: white; padding: 8px 12px; border-radius: 6px; font-size: 13px; pointer-events: none; display: none; }
    </style>
</head>
<body>
<div id="tooltip"></div>
<svg width="800" height="500" id="map"></svg>
<script src="https://d3js.org/d3.v7.min.js"></script>
<script src="https://unpkg.com/topojson-client@3"></script>
<script>
var width = 800, height = 500;
var svg = d3.select('#map');
var tooltip = d3.select('#tooltip');
var projection = d3.geoNaturalEarth1().scale(170).translate([width / 2, height / 1.3]);
var path = d3.geoPath().projection(projection);

d3.json('https://unpkg.com/world-atlas@2/countries-110m.json').then(function(world) {
    var countries = topojson.feature(world, world.objects.countries);
    var values = new Map();
    countries.features.forEach(function(d) {
        values.set(d.id, Math.random() * 500);
    });

    var color = d3.scaleSequential(d3.interpolateBlues).domain([0, 500]);

    svg.selectAll('path')
        .data(countries.features).enter()
        .append('path').attr('class', 'country')
        .attr('d', path)
        .attr('fill', function(d) { return color(values.get(d.id)); })
        .on('mouseover', function(e, d) {
            d3.select(this).attr('stroke', '#ff6b35');
            tooltip.style('display', 'block')
                .html('<strong>' + d.properties.name + '</strong><br>Value: ' + values.get(d.id).toFixed(0))
                .style('left', (e.pageX + 12) + 'px').style('top', (e.pageY - 28) + 'px');
        })
        .on('mouseout', function() {
            d3.select(this).attr('stroke', '#fff');
            tooltip.style('display', 'none');
        });
});
</script>
</body>
</html>

What's Next

Load external data from APIs and files.

Request Data — Loading data with D3.js. Localization — Localization and formatting.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro