D3 has a variety of layouts for directed graphs that are strict trees, such as the following:
A |\ B C / \ D E
I need to draw a hierarchy of nodes that is not a tree, but is a directed acyclic graph. This is a problem for a tree layout, because several of the branches converge:
A |\ B C \| D
Does anyone know of a D3 layout for general hierarchies? Or alternatively, some clever hack to the existing treelayout? I've noticed GraphVis handles this situation well, but D3 produces a graph that better suits the requirements here.
hierarchy() function in D3. js library is used to construct a root node data from a given hierarchical data. The data that is given must be of an object and must represent a root node.
In the update() function, links = d3. layout. tree(). links(nodes); is 'selecting' the nodes in the tree and their specified links in the JSON file.
But D3 layouts don't result in charts; they result in the settings necessary for charts. You have to put in a bit of extra work for charts, but you have enormous flexibility (as you'll see in this and later chapters) that allows you to make diagrams and charts that you can't find in other libraries.
d3. stratify transforms a tree from link representation to hierarchy. a b c d e f g h i. Here's a typical tree, visualized as a root node a with links to other nodes at deeper levels. It can be represented by a flat list of links, connecting labels from parent to child.
You could create your own code without having to rely on a D3 layout in order to get it done.
I've provided an example in a jsFiddle. The example is pretty simplistic and would need to be worked a little bit to accommodate more complex examples.
The example could be re-worked to process hierarchical data as well with relatively little effort.
Here is the code I have used in the jsFiddle:
// Sample data set var json = { nodes: [{ name: 'A'}, { name: 'B'}, { name: 'C'}, { name: 'D'}], links: [{ source: 'A', target: 'B'}, { source: 'A', target: 'C'}, { source: 'B', target: 'D'}, { source: 'C', target: 'D'} ] }; var vis = d3.select('#vis').attr('transform', 'translate(20, 20)'); // Build initial link elements - Build first so they are under the nodes var links = vis.selectAll('line.link').data(json.links); links.enter().append('line').attr('class', 'link').attr('stroke', '#000'); // Build initial node elements var nodes = vis.selectAll('g.node').data(json.nodes); nodes.enter().append('g').attr('class', 'node').append('circle').attr('r', 10).append('title').text(function(d) { return d.name; }); // Store nodes in a hash by name var nodesByName = {}; nodes.each(function(d) { nodesByName[d.name] = d; }); // Convert link references to objects links.each(function(link) { link.source = nodesByName[link.source]; link.target = nodesByName[link.target]; if (!link.source.links) { link.source.links = []; } link.source.links.push(link.target); if (!link.target.links) { link.target.links = []; } link.target.links.push(link.source); }); // Compute positions based on distance from root var setPosition = function(node, i, depth) { if (!depth) { depth = 0; } if (!node.x) { node.x = (i + 1) * 40; node.y = (depth + 1) * 40; if (depth <= 1) { node.links.each(function(d, i2) { setPosition(d, i2, depth + 1); }); } } }; nodes.each(setPosition); // Update inserted elements with computed positions nodes.attr('transform', function(d) { return 'translate(' + d.x + ', ' + d.y + ')'; }); links.attr('x1', function(d) { return d.source.x; }).attr('y1', function(d) { return d.source.y; }).attr('x2', function(d) { return d.target.x; }).attr('y2', function(d) { return d.target.y; });
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