Voronoi Treemap

Dataviz logo representing a Tree chart.

This tutorial is an introduction to creating a Voronoi treemap with React and D3.js. A Voronoi treemap is a space-filling visualization that recursively partitions a plane into regions based on a set of points, where each region contains all points closer to one of the generating points than to any other.

This example will show you how to create a basic Voronoi treemap that displays hierarchical data in a visually appealing way, with each cell's size proportional to its value.

Useful links

Plot and code

Here's what we're going to create:

A Voronoi treemap is a powerful visualization that combines the space-filling properties of a treemap with the geometric elegance of a Voronoi diagram. It's particularly useful for displaying hierarchical data where the size of each cell represents a quantitative value.

A Voronoi treemap showing hierarchical data with cell sizes proportional to their values.

The Data

The dataset used for this example is a hierarchical structure where each node has:

  • A name property for identification
  • A value property determining the cell size
  • Optional children for nested data
const data = {
  name: "root",
  value: 100,
  children: [
    {
      name: "Category A",
      value: 40,
      children: [
        { name: "A1", value: 20 },
        { name: "A2", value: 20 }
      ]
    },
    {
      name: "Category B",
      value: 60,
      children: [
        { name: "B1", value: 30 },
        { name: "B2", value: 30 }
      ]
    }
  ]
};

Implementation

The implementation involves several key steps:

  1. Creating a Voronoi diagram using D3's d3-delaunay
  2. Recursively partitioning the space based on the data hierarchy
  3. Rendering the cells with appropriate colors and labels
import { useMemo } from 'react';
import * as d3 from 'd3';

const VoronoiTreemap = ({ data, width, height }) => {
  // Compute the Voronoi diagram
  const voronoi = useMemo(() => {
    const points = generatePoints(data, width, height);
    return d3.Delaunay.from(points).voronoi([0, 0, width, height]);
  }, [data, width, height]);

  return (
    <svg width={width} height={height}>
      {voronoi.cellPolygons().map((polygon, i) => (
        <path
          key={i}
          d={`M${polygon.join("L")}Z`}
          fill={d3.interpolateRainbow(i / data.children.length)}
          stroke="white"
        />
      ))}
    </svg>
  );
};

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!