Voronoi Diagram

Dataviz logo representing a Voronoi chart.

A voronoi diagram is a partition of a plane into regions called voronoi cells. A voronoi cell consists of every point in the plane whose distance to its linked data point is less than or equal to its distance to any other data point.

This page is a step-by-step guide on how to build your own voronoi diagram for the web, using React and D3.js.

It starts by describing how the data should be organized and explains how to run and plot a Delaunay triangulation. Based on this, it explains how to build the voronoi diagram. Finally it shows how this can be used for real life application like for a scatterplot or to build a voronoi treemap.

Useful links

The Data

Everything starts with a set of two-dimensional points. Their coordinates are available with x representing the position on the horizontal axis and y being for the vertical axis.

As a result, the dataset is pretty simple: an array of objects that looks like this:


export const data = [
  { x: 10, y: 10 },
  { x: 4, y: 4 },
  { x: 35, y: 90 },
  { x: 67, y: 34 },
  ...
];

Note: this is the same dataset as the one used for a scatterplot.

A set of two-dimensional points

Let's start by plotting those data points on a two-dimensional points. This is basically a scatterplot except that we're not drawing the axes.

If you're not familiar with the basic steps used in the following sandbox, please take a look at the scatterplot section of the gallery that goes in deep on what's going on here.

Scatterplot section


You need a good understanding about d3 scales, how to loop through a data array to create svg elements and how to make the component renders them.

A voronoi diagram starts with a set of 2d coordinate points plotted on a plane.

Delaunay triangulation

The first required step to build a voronoi diagram is to run a Delaunay triangulation.

You don't necessarily need to understand what a Delaunay triangulation is. But if you're interested in the topic, it will be covered in the dataviz universe newsletter soon.

Fortunately, d3.js has a module called d3-delaunay that does this triangulation for us. You can import this module as follow. You can also read its complete doc on github.

import { Delaunay } from "d3";

Start by creating a delaunay object thanks to the Delaunay.from() function. This function expects an array of array of numbers, so there is a tiny bit of data wrangling to do here.

const delaunay = useMemo(() => {
  const formattedData = data.map((d) => [xScale(d.x), yScale(d.y)]);
  return Delaunay.from(formattedData);
}, []);

This delaunay object contains all the information about the triangulation 🎉. It also has a method called render() that provides the svg path of all the adjacent triangles.

const delaunayPath = delaunay.render();

This path is provided as a string that we can pass to a path svg element as follow:

const allDelaunayShapes = (
  <path d={delaunayPath} stroke="grey" fill="transparent" opacity={0.2} />
);

We can now render this inside an svg element to get an overview of this delaunay triangulation:

Second step: run a Delaunay triangulation on the set of two-dimensional points.

Voronoi diagram

The Delaunay triangulation above corresponds to the dual graph of the Voronoi diagram. Basically, it means that the circumcenters of the Delaunay triangles are the vertices of the Voronoi diagram.

But no worries, the delaunay object we built in the previous section has a voronoi() method. It computes the voronoi cell coordinates based on the delaunay information:

const voronoi = useMemo(() => {
  return delaunay.voronoi([0, 0, width, height]);
}, [data]);

We can plot the voronoi cells using the same kind of code as for the delaunay triangles. Resulting in our first voronoi diagram! 🎉


Last step: join the circumcenters of each triangle to get the voronoi diagram.

Responsive Voronoi with react

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

Making the Voronoi 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.




Voronoi inspiration

If you're looking for inspiration to create your next Voronoi, 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 Voronoi looks good!

visit

Closest point detection

The voronoi diagram is commonly used to detect the closest data point of the mouse position. This can be pretty useful to highlight the closest point without having to hover exactly over it.

In the example below, the closest dot will be highlighted with a red circle ⭕️ using the voronoi cells.


Use the voronoi algorithm to detect the closest point of the mouse position.

Variations

A glimpse of what it is possible to do using the voronoi diagram for data visualization.

Click on the overview below to get details and code.


gif of a scatterplot with voronoi diagram for closest point detection

Closest point detection with Voronoi

Using a voronoi diagram in the scatterplot background is a smart way to efficiently find the mouse closest point

ToDoAdd voronoi treemap example

Part Of A Whole

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!