How to Use D3.js pack

Explains pack in D3.js for visualizing hierarchical structures.

Example program

View code

Example code

<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
  <title>D3 hierarchy pack</title>
  <script src="https://d3js.org/d3.v7.min.js"></script>
</head>

<body>
  <svg width="800" height="600"></svg>

  <script>
    // 1. Prepare data
    var width = document.querySelector("svg").clientWidth;
    var height = document.querySelector("svg").clientHeight;
    var data = {
      "name": "A",
      "children": [
        { "name": "B", "value": 25 },
        {
          "name": "C",
          "children": [
            { "name": "D", "value": 10 },
            { "name": "E", "value": 15 },
            { "name": "F", "value": 10 }
          ]
        },
        { "name": "G", "value": 15 },
        {
          "name": "H",
          "children": [
            { "name": "I", "value": 20 },
            { "name": "J", "value": 10 }
          ]
        },
        { "name": "K", "value": 10 }
      ]
    };

    // 2. Convert the data to draw
    root = d3.hierarchy(data);
    root.sum(function (d) { return d.value; });

    var pack = d3.pack()
      .size([width, height])
      .padding(0);

    pack(root);

    // 3. Configure SVG elements
    var node = d3.select("svg").selectAll(".node")
      .data(root.descendants())
      .enter()
      .append("g")
      .attr("transform", function (d) { return "translate(" + d.x + "," + (d.y) + ")"; });

    var color = ["Orange", "Khaki", "Ivory"];
    node.append("circle")
      .attr("r", function (d) { return d.r; })
      .attr("stroke", "black")
      .attr("fill", function (d) { return color[d.depth]; });

    node.append("text")
      .style("text-anchor", function (d) { return d.children ? "end" : "middle"; })
      .attr("font-size", "150%")
      .text(function (d) { return d.children ? "" : d.data.name; });
  </script>
</body>

</html>

Example code explanation

1. Prepare data

  var data = {
    "name": "A",
    "children": [
      { "name": "B", "value": 25 },
      {
        "name": "C",
        "children": [
          {"name": "D", "value": 10 },
          { "name": "E", "value": 15 },
          { "name": "F", "value": 10 }
        ]
      },
      { "name": "G", "value": 15 },
      {
        "name": "H",
        "children": [
          { "name": "I", "value": 20 },
          { "name": "J", "value": 10 }
        ]
      },
      { "name": "K", "value": 10 }
    ]
  };

Prepare the data to draw. For details about the data structure, see this page. Set a value for each terminal node to represent the bubble size. The size of each parent node is set during the next data conversion as the sum of the value values of its child and grandchild nodes.

2. Convert the data to draw

root = d3.hierarchy(data);
root.sum(function(d) { return d.value; });

var pack = d3.pack()
  .size([width, height])
  .padding(0);

pack(root);

Convert the prepared data into the data structure used for drawing. Two conversions are required: prepared data -> hierarchy data -> data for the drawing type, which is pack here.

root = d3.hierarchy(data);
root.sum(function(d) { return d.value; });

After converting data into the hierarchy root, calculate the sum of descendant value values for nodes that do not have their own value.

Then call the function that converts it into pack data.

  var pack = d3.pack()
    .size([width, height])
    .padding(0);

  pack(root);

When root is passed to the function returned by d3.pack(), the following data is assigned to the root.

Variable Description
x Center x coordinate of the circle.
y Center y coordinate of the circle.
r Circle radius.

The following settings are also available on d3.pack().

pack.radius() Specifies the circle radius as a function passed as an argument, such as function(d) { return d.value; }.
The default is null.
pack.size() Sets the overall size of the pack layout as a two-element array: [width, height].
If no argument is specified, it returns the current size.
The default is [1, 1].
pack.padding() Specifies the distance between tangent circles.
The default is 0.

3. Configure SVG elements

// 3. Configure SVG elements
var node = d3.select("svg").selectAll(".node")
  .data(root.descendants())
  .enter()
  .append("g")
  .attr("transform", function(d) { return "translate(" + d.x + "," + (d.y) + ")"; });

var color = ["orange", "Khaki", "Ivory"];
node.append("circle")
  .attr("r", function(d) { return d.r; })
  .attr("stroke", "black")
  .attr("fill", function(d) { return color[d.depth]; });

node.append("text")
  .style("text-anchor", function(d) { return d.children ? "end" : "middle"; })
  .attr("font-size", "150%")
  .text(function(d) { return d.children ? "" : d.data.name; });

Configure the SVG elements for circles. First add a "g" element, then add "circle" and "text" inside it.

The data assignment uses the following function.

root.descendants()

This function arranges nested nodes into an array.

For "circle", a color array is set first, and the "fill" attribute uses the node’s depth position to choose a color. The "text-anchor" style set on "text" controls the text position.