D3 - How to make visualization with a partition layout have equally divided sectors?

I'm using D3 to achieve a visualization with a partition layout. What I need is the divided sections have equally the same width, like this:

Desired outcome

But what I currently have is this:

Current version

You can find the current version working here on Codepen, but I copy the code here also:

var w = 960,
    h = 500,
    x = d3.scale.linear().range([0, w]),
    y = d3.scale.linear().range([0, h]),
    color = d3.scale.ordinal().range(["rgb(255,247,236)","rgb(254,232,200)","rgb(253,212,158)","rgb(253,187,132)","rgb(252,141,89)","rgb(239,101,72)","rgb(215,48,31)","rgb(179,0,0)","rgb(127,0,0)"]);

var vis = d3.select("#chart").append("svg:svg")
    .attr("width", w)
    .attr("height", h);

var partition = d3.layout.partition()
    .children(function(d) { return isNaN(d.value) ? d3.entries(d.value) : null; })
    .value(function(d) { return d.value; });

var data = {
  'root': {

    'asdf': {
      'a': {
        'kdjfklsfj': 1,
        'kdjfklsfsj': 1
      },
      'b': 1
    },

    'bnm': {
      'c': 1,
      'd': {
        'lsdkjfdkljsf': {
          'lsdkfj': 1,
          'kldsjfdlk': 1
        },
        'kdjfkjsdfk': 1
      }
    }

  }
};

var rect = vis.selectAll("rect")
    .data(partition(d3.entries(data)[0]))
    .enter().append("svg:rect")
    .attr("x", function(d) { return x(d.x); })
    .attr("y", function(d) { return y(d.y); })
    .attr("width", function(d) { return x(d.dx); })
    .attr("height", function(d) { return y(d.dy); })
    .attr("fill", function(d) { return color((d.children ? d : d.parent).data.key); })
    .on("click", click);

function click(d) {
  x.domain([d.x, d.x + d.dx]);
  y.domain([d.y, 1]).range([d.y ? 20 : 0, h]);

  rect.transition()
    .duration(750)
    .attr("x", function(d) { return x(d.x); })
    .attr("y", function(d) { return y(d.y); })
    .attr("width", function(d) { return x(d.x + d.dx) - x(d.x); })
    .attr("height", function(d) { return y(d.y + d.dy) - y(d.y); });
}

What I found is that altering the data values (currently all items are valued "1"), increase the width of the elements naturally, but how can I make it have a equally divided boxes?

Upvotes: 1

Views: 5333

Answers (1)

David Pope
David Pope

Reputation: 6587

You're telling the partition function to calculate the width of each block as either the value (if it has one), or the sum of its children's values. It sounds like you want to use the number of children as the weight in the latter case, so that e.g. the top node is split into 1/2 and 1/2 instead of 4/7 and 3/7.

Edit: The d3's partition() object is designed to determine the size of a parent elements based on the sum of the sizes of its children, whereas (if I understand your use case) you want the sizes of the children to be apportioned from the size of their parents, based on the number of its siblings.

To get this to work you'll have to manage the leaf node object values to 'trick' d3 into doing what you want. Here's a data object that gets the first level to have even lengths:

var data = {
  'root': {

    'asdf': {
      'a': {
        'kdjfklsfj': 1/6,
        'kdjfklsfsj': 1/6
      },
      'b': 1/6
     },

    'bnm': {
      'c': 1/8,
      'd': {
        'lsdkjfdkljsf': {
          'lsdkfj': 1/8,
          'kldsjfdlk': 1/8
        },
        'kdjfkjsdfk': 1/8
      }
    }
  }
};

In other words, you have to manage the data to fit d3's expectations. It's not d3 manipulation that should do this, it's your data generator.

Upvotes: 1

Related Questions