Reputation: 1053
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
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