Chris vCB
Chris vCB

Reputation: 1053

d3.js: size of parents = sum of size of children

I am building something quite similar to this. What I would love is to make every node either their size as defined in the json file, OR, if it has no size attribute but a children attribute in json, the sum of all of its children's sizes. How would one go about doing that? I have tried various methods but short of adding things up and hardcoding it in JSON, which is a bit lame, I haven't found anything that really would have worked ;( Any suggestions, hive mind?

Upvotes: 1

Views: 2976

Answers (1)

AmeliaBR
AmeliaBR

Reputation: 27544

If your data is a tree structure, you could use a Partition Layout to initialize positions and sizes of nodes. The d.value returned by partition for parent nodes is by default the sum of values for all children nodes, assuming you've properly set the value accessor function to return the data variable that you want to use for size for leaf nodes.

Although the standard display in partition examples is to have space-filling rectangles or arcs instead of nodes and links, it still has all the basic functionality of the other hierarchy layouts. So once you've run the layout on your root to generate your array of nodes, you can run the links function to calculate the links.

If you still want a force-based layout instead of a static tree, you can just pass in your nodes and links to the force layout and start it up.

In sum:

var root = /*root object read from JSON data*/;

var w,h; /*set appropriately */

var partition = d3.layout.partition()
           .size([w,h])
           .value(function(d){return d.size;})
           .children(function(d){return d.children;}) 
                  //optional: this is the default, change as needed
           .sort(null); 
                 //optional: turns off sorting, 
                 //the default is to sort children by descending size

var nodes = partition(root);

var links = partition.links(nodes);

initialize();

var force = d3.layout.force()
            .nodes(nodes)
            .links(links)
            .size([w,h])
             /*and any other customization*/
            .start();

svg.on("tick", update);

One thing to note. The x value created by partition layout is designed to be the corner of a rectangle instead of the centre of a circle. So if you position your nodes based on the original x value, you'll end up with parents off to the far left of their children. If you're running everything through a force-based layout afterwards, it will sort itself out eventually, but you can centre them from the beginning by setting d.x = d.x + d.dx/2 and d.y = d.y + d.dy/2 on all your nodes during initialization (e.g., using an .each() call in your enter() chain). And of course, use d.value to initialize your node size (with an appropriate scale).

Upvotes: 6

Related Questions