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
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
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With