Choropleth Map

Dataviz logo representing a Choropleth chart.

A choropleth map displays divided geographical areas or regions that are coloured in relation to a numeric variable. It enables the study of how a variable evolves across a geographical area.

Once you understood how to draw a map background from a geoJson file, it is just a matter of coloring each region with the appropriate color. On top of this, it is advised to add a color legend and some interactivity (hover effect and tooltip).

This webpage is a tutorial coming with explanation and code sandboxes. It explains how to build interactive choropleth map with React and D3.js.

Useful links

The Data

Two pieces of information are required to build a choropleth map:

→ Geographic information

The first thing you need to build a choropleth map is the 2d coordinates of the boundaries of the regions you want to represent. If you are trying to build a world map, you need to know where the country boundaries are located 🤷‍♀️.

Several formats exist to store such a piece of information. When working with d3.js, the expected format is geoJSON. A geoJSON file looks pretty much like this:


{
  "type": "FeatureCollection",
  "features": [
    {
      "type": "Feature",
      "geometry": {
        "type": "Point",
        "coordinates": [102.0, 0.5]
      },
      "properties": {
        "prop0": "value0"
      }
    },
    {
      "type": "Feature",
      "geometry": {
        "type": "LineString",
        "coordinates": [
          [102.0, 0.0],
          [103.0, 1.0],
          [104.0, 0.0],
          [105.0, 1.0]
        ]
      },
      "properties": {
        "prop0": "value0",
        "prop1": 0.0
      }
    },
    ...
  ]
}

It is basically an object, with a features property that is of great interest. This prop is an array, each item of the array being a shape that we will be able to draw.

This format is extensively described in the background map section of the gallery. It explains how it is structured, where to find it, how to work with it and how to draw its content. Check it out!

Map section

→ Numeric information

We need another piece of information that provides a numeric value for each region of the geoJson file. This information will be translated into a color on the choropleth map.

You can include any information in this array. But you need a prop (code here) that allows to make the link with the regions of the geoJson file.

[
  {
    "name": "Antigua and Barbuda",
    "code": "ATG",
    "pop": 83039
  },
  {
    "name": "Algeria",
    "code": "DZA",
    "pop": 32854159
  },
  ...
]
}

Basic background map

The first step is to build the background map. Basically, we need to transform the set of spherical coordinates of the geoJson file into a set of 2d coordinates and draw them.

Fortunately, the d3-geo module of d3.js has everything we need for this. It relies on the d3.geoPath() function and the whole process is extensively described in the background map section of the gallery.

Map section

First very basic map made with d3.js and React. Check the map section for more code explanation.

Note: I removed Antartica and chose the most famous yet highly criticiced: Mercator.

Adding color

Now, we need to color each region of the map according to its value to make it a choropleth.

The first step is to create a color scale, which is made for so many chart types of this gallery. To put it in a nutshell, the process looks as follow:

all supported projections


Setting up a projection always follow the same pattern:

const projection = d3
  .geoMercator()                              // name of the projection
  .scale(width / 2 / Math.PI)                 // scale: bigger value = more zoom
  .center([2.34, 48.86])                      // coordinate of the center of the map. e.g. 2 and 48 for Paris
  ...other options if needed

scale and center are in my opinion the 2 most useful options that you will have to setup for your projections. But once more, take a look at the doc to see every possibilities.

Now, here is a little playground to check some of the various offered projections.

Add a color scale to color each country and get your first choropleth map.

Responsive Choropleth with react

The component above is not responsive. It expects 2 props called width and height and will render a Choropleth of those dimensions.

Making the Choropleth responsive requires adding a wrapper component that gets the dimension of the parent div, and listening to a potential dimension change. This is possible thanks to a hook called useDimensions that will do the job for us.

useDimensions: a hook to make your viz responsive
export const useDimensions = (targetRef: React.RefObject<HTMLDivElement>) => {

  const getDimensions = () => {
    return {
      width: targetRef.current ? targetRef.current.offsetWidth : 0,
      height: targetRef.current ? targetRef.current.offsetHeight : 0
    };
  };

  const [dimensions, setDimensions] = useState(getDimensions);

  const handleResize = () => {
    setDimensions(getDimensions());
  };

  useEffect(() => {
    window.addEventListener("resize", handleResize);
    return () => window.removeEventListener("resize", handleResize);
  }, []);

  useLayoutEffect(() => {
    handleResize();
  }, []);

  return dimensions;
}

I'm in the process of writing a complete blog post on the topic. Subscribe to the project to know when it's ready.




Choropleth inspiration

If you're looking for inspiration to create your next Choropleth, note that dataviz-inspiration.com showcases many examples. Definitely the best place to get ... inspiration!

dataviz-inspiration.com showcases hundreds of stunning dataviz projects. Have a look to get some ideas on how to make your Choropleth looks good!

visit

Color legend

A choropleth map uses a color scale to encode a numeric value into a color. As a result, it is very much advised to add a color legend to explicit how this color scale works.

Let's consider a variable that goes from 0 to 100. We want to encode 0 in blue and 100 in purple. The color scale is built thanks to the scaleLinear() function of d3 as described above.

020406080100

A color legend built with react, canvas and d3.

The trick here is to create a canvas element of the desired width and height. Then, loop from left to right and add one rectangle for each pixel with the corresponding color using the same color scale as the one used on the chart. It's important to do it in canvas: you don't want to add 300 elements in your DOM if your legend is 300px wide.

Once the canvas element is instantiated with a ref, you can draw the color scale thanks to a useEffect like this:

useEffect(() => {
  const canvas = canvasRef.current;
  const context = canvas?.getContext("2d");

  if (!context) {
    return;
  }

  // Loop on every pixels
  for (let i = 0; i < width; ++i) {
    context.fillStyle = colorScale((max * i) / width); // max is the last value of the domain of the color scale
    context.fillRect(i, 0, 1, height);
  }
}, [width, height, colorScale]);

Then you probably want to add some ticks on top of the color graduation to make it insightful.

Fortunately, the d3 linearScale comes with a handy tick() function. Basically, calling xScale.ticks(4) will create an array with approximately 4 items, each providing everything you need to draw a smartly located tick.

Color Legend is a big topic. There is much more to say about it and I'll post a complete blog post on the topic soon. Subscribe to the gallery if interested!

ToDoHover effect section
ToDoTalk more about color scale. Hover effect linked with color scale
ToDoCanvas version. Add tooltip.

Map

Contact

👋 Hey, I'm Yan and I'm currently working on this project!

Feedback is welcome ❤️. You can fill an issue on Github, drop me a message on Twitter, or even send me an email pasting yan.holtz.data with gmail.com. You can also subscribe to the newsletter to know when I publish more content!