Chapter 06Partition Layouts

The partition layout represents nodes in a hierarchy model as rectangles with rectangles for child nodes positioned adjacent to the rectangles for their parents’ and the length of a rectangle being proportional to the value of the node’s value property.

Below we show a tree and a partition, both created using the same hierarchal model.

<script>
var data = {"name": "A", "children": [
                {"name": "B"},
                {"name": "C", "children": [
                    {"name": "E"},
                    {"name": "F"}
                ]},
                {"name": "D"},
                {"name": "G"},
                {"name": "H", "children": [
                    {"name": "I"},
                    {"name": "J"}
                ]},
                {"name": "K"},
                {"name": "L"},
                {"name": "M", "children": [
                  {"name": "M"},
                  {"name": "N"}
                ]},
                {"name": "O"}
            ]};

var drawTree = function() {
  let root = d3.hierarchy(data)
    .count();

  let treeLayout4 = d3.tree()
    .size([200, 200]);

  treeLayout4(root);

  let svg = d3.select("#demo1");

  // draw links
  svg.select('g.links')
    .selectAll('line.link')
    .data(root.links())
    .enter()
    .append('line')
    .classed('link', true)
    .attr('x1', function(d) {return d.source.x;})
    .attr('y1', function(d) {return d.source.y;})
    .attr('x2', function(d) {return d.target.x;})
    .attr('y2', function(d) {return d.target.y;})
    .attr('stroke', "darkgray")
    .attr('stroke-width', 2);

  // draw nodes
  svg.select('g.nodes')
    .selectAll('circle.node')
    .data(root.descendants())
    .enter()
    .append('circle')
    .classed('node', true)
    .attr('cx', function(d) {return d.x;})
    .attr('cy', function(d) {return d.y;})
    .attr('r', 6)
    .attr("fill", "lightblue")
    .attr('stroke', "darkgray")
    .attr('stroke-width', 1);
};

var drawPartition = function() {
  let root = d3.hierarchy(data)
  .count();

  let partition = d3.partition()
    .size([210,210]);

  partition(root);

  d3.select("#demo2")
    .selectAll('rect.node')
    .data(root.descendants())
    .enter()
    .append('rect')
    .classed('node', true)
    .attr('x', d => d.x0)
    .attr('y', d => d.y0)
    .attr('width', d => d.x1 - d.x0)
    .attr('height', d => d.y1 - d.y0);
};

drawTree();
drawPartition();

</script>

<svg id="demo1" width=250 height=250>
    <g transform="rotate(-90), translate(-220,10)">
      <g class="rects"></g>
      <g class="links"></g>
      <g class="nodes"></g>
    </g>
</svg>

<svg id="demo2" width=250 height=250 transform="rotate(-90), translate(20,0)"></svg>

Creating the Layout

We generate a partion layout using d3.partition() and can specify the height and width of the layout region by chaining a call to partion.size([width,height]).

The partition.round([boolean]) method takes a boolean as an argument and sets whether or not the layout will round to exact pixel boundaries.

Below we illustrate the partition.padding([padding]) method which puts space between adjacent children and between children and their parent. Note that we also sort the nodes in the hierarchal model before we call the partition layout function.

<script>
var data = {"name": "A", "children": [
                {"name": "B"},
                {"name": "C", "children": [
                    {"name": "E"},
                    {"name": "F"}
                ]},
                {"name": "D"},
                {"name": "G"},
                {"name": "H", "children": [
                    {"name": "I"},
                    {"name": "J"}
                ]},
                {"name": "K"},
                {"name": "L"},
                {"name": "M", "children": [
                  {"name": "M"},
                  {"name": "N"}
                ]},
                {"name": "O"}
            ]};

let root = d3.hierarchy(data)
  .sort((a,b) => b.height - a.height || a.data.name.localeCompare(b.data.name))
  .count();

let partition = d3.partition()
  .size([210,210])
  .padding(10);

partition(root);

d3.select("#demo3")
  .selectAll('rect.node')
  .data(root.descendants())
  .enter()
  .append('rect')
  .classed('node', true)
  .attr('x', d => d.x0)
  .attr('y', d => d.y0)
  .attr('width', d => d.x1 - d.x0)
  .attr('height', d => d.y1 - d.y0);

</script>

<svg id="demo3" width=250 height=250 transform="rotate(-90), translate(20,0)"></svg>

The partition layout, when called, adds x0, y0, x1, and y1 properties, representing rectangles, in each node of the hierarchal model that is passed as an argument to the layout function. The values of these properties are dependent on the values in the nodes’ value properties. As mentioned in the hierarchy section, the node.sum and node.count methods can be called on the root node to set the value properties of each node in the hierarchy.