Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sunburst partition data overwritten by second sunburst on same page

Posting both question & answer here to save somebody else the same trouble later...

When I create two sunburst charts using d3.layout.partition, the first sunburst's slice proportions are overwritten by the second sunburst's slice proportions upon resize of the slices.

The two charts pass different .value accessor functions into the partition layout, e.g.

d3.layout.partition()
.sort(null)
.value(function(d) { return 1; });

vs.

d3.layout.partition()
.sort(null)
.value(function(d) { return d.size; });

And they generate their own list of nodes that are not shared between the two sunbursts. However, if I re-call the d3.svg.arc generator to resize to larger radius (but not change overall proportions), the slice angles are suddenly overwritten.

See the example here: http://bl.ocks.org/explunit/ab8cf15534f7fec5ac6d

like image 942
explunit Avatar asked Jun 30 '14 22:06

explunit


2 Answers

The problem is that while partition.nodes() seems to generate a new data structure (e.g if you give it some .key functions, it writes the extra properties (e.g. .x, .y, .dx, dy) to the underlying data and does not make a copy of the data. Thus if the data structure is shared between the two charts, these .x, .y, .dx, dy properties will bleed through to the other graphs.

This seems like a bug to me, but in reading this old GitHub issue it seems to be treated as "by design". Perhaps it will be reconsidered in future versions.

One workaround is to use something like Lodash/Underscore cloneDeep or Angular's copy to make each chart have it's own copy of the data.

makeSunburst(null, _.cloneDeep(root), countAccessorFn);
makeSunburst(null, _.cloneDeep(root), sizeAccessorFn);

See example here: http://bl.ocks.org/explunit/e9efb830439247eea1be

like image 182
explunit Avatar answered Oct 13 '22 08:10

explunit


An alternative to copying the whole dataset for each chart would be to simply recompute the partition before re-rendering.

Instead of having makeSunburst() be a function of the accessor, make it a function of the partition. Pass a different partition function to each chart:

// create separate partition variables
var countPartition = d3.layout.partition().sort(null).value(countAccessorFn);
var sizePartition = d3.layout.partition().sort(null).value(sizeAccessorFn);

// make the charts as a function of partition
charts.push(makeSunburst(root, countPartition));
charts.push(makeSunburst(root, sizePartition));

Then before applying the transition, simply update the nodes variable to reflect the associated partition:

addToRadius: function(radiusChange) {
  radius += radiusChange;
  ringRadiusScale.range([0, radius]);

  // update the data before re-rendering each chart
  nodes = partition.nodes(dataRoot);
  path.transition().attr('d', arc);
}

Now when you update each chart, it is using the correct partition.

Here's an updated example.

like image 35
jshanley Avatar answered Oct 13 '22 10:10

jshanley