MorganFR
MorganFR

Reputation: 341

D3.js Adding links between elements in a radial tree (Hierarchical edge bundling elements)

A couple of months ago, I tried combining Hierarchical Edge Bundling and Radial Reingold–Tilford Tree using d3.js

enter image description here

I started from the HEB and tried to make it into a tree. Things have not worked out the way I wanted, and I realized it might be better to start from a collapsible radial tree (not Reingold Tilford), with a different angle.

enter image description here

Here is a JSFiddle of the radial tree

The data model has also changed, as elements now have a name, children and imports (links).

var flare =
{
    "name": "root",
    "children": [
        {
            "name": "test1.parent1","children": [
                {"name": "test1.child11","children": [
                    {"name": "test1.child111"},
                    {"name": "test1.child112"}
                ]}
            ],"imports": ["test2.parent2","test3.parent3","test4.parent4"]
        },
        {
            "name": "test2.parent2","children": [
                {"name": "test2.child21"},
                {"name": "test2.child22"},
                {"name": "test2.child28","children":[
                    {"name": "test2.child281"},
                    {"name": "test2.child282"}
                ]}
            ],"imports": ["test3.parent3"]
        },
        {"name": "test3.parent3","imports": ["test4.parent4"]},
        {
            "name": "test4.parent4","children": [
                {"name": "test4.child41"},
                {"name": "test4.child42"}
            ]
        }
    ]
};

To start slowly, I would like to combine the non-interactive Hierarchical edge bundling from Mike Bostock with the current JSFiddle, but keeping in mind that the interactions will be part of it later on.

Also, only the first level has to have links (parent-parent link) as shown below (the result that I want):

enter image description here

My current biggest issue is that the HEB has no "root", but the tree starts with a single item. So everything I have tried so far has led to a big mess at the center of the tree.

Note that there is a circle at the center of the tree to cover the root to level 1 links, so the tree starts at level 1 (parents).

var circle = svg.append("circle")
  .attr("cx", 0)
  .attr("cy", 0)
  .attr("r", diameter - 725.3)
  .style("fill", "#F3F5F6")
  .style("stroke-width", 0.2)
  .style("stroke", "black");

Ideally, the links between parents have to update when a level is (un)collapsed, like it does for the nodes and the links between levels, but that can come later and might not be that difficult after initially getting the first level links to show. Also, the data template might change if necessary, but all 3 pieces of information are important (name, children and imports).

Another way to do this would be to be able to change the data to not include the root part, and that it behaves exactly as it does now Partial answers are also welcome.

Upvotes: 10

Views: 2341

Answers (2)

MorganFR
MorganFR

Reputation: 341

I have managed to add links between elements in this JSFiddle using parts of the code found in the original hierarchical edge bundling by Mike Bostock and added them to radial version of the collapsible tree. However, they do not update when collapsing or expanding children!

var bundle = d3.layout.bundle();

var line = d3.svg.line.radial()
    .interpolate("bundle")
    .tension(.85)
    .radius(function(d) { return d.y; })
    .angle(function(d) { return d.x / 180 * Math.PI; });

then in update(source):

var middleLinks = packageImports(root);

svg.selectAll("path.middleLink")
          .data(bundle(middleLinks))
        .enter().append("path")
          .attr("class", "middleLink")
          .attr("d", line);

The "packageImport" function can be found at the bottom.

function packageImports(json) {
      var map = {},
          imports = [];

      console.log(json.children);

      // Compute a map from name to node.
      json.children.forEach(function(d) {
        console.log(d.name)
        map[d.name] = d;
      });

      // For each import, construct a link from the source to target node.
      json.children.forEach(function(d) {
        if (d.imports) d.imports.forEach(function(i) {
          imports.push({source: map[d.name], target: map[i]});
        });
      });

      console.log(imports);
      return imports;
    }

Upvotes: 1

Max von Hippel
Max von Hippel

Reputation: 2970

My current biggest issue is that the HEB has no "root", but the tree starts with a single item. So everything I have tried so far has led to a big mess at the center of the tree.

Given that you have a root, why not set that as the center "node" and make it a radial tree, like this? Here is another example, but with multiple roots.

That said, you did not ask for a radial tree, even if it sounds like that would answer your question. If you are determined to keep things arranged in circles of varying radium, maybe the problem to be solved is that of the criss-crossing of the lines looking so "messy". If that is the case, then a different sorting algorithm might do the trick. Here is an article that expands on using different sorting algorithms in a similar scenario with D3.js. D3 layout algorithms are also open source online.

The best resource I was able to find for you was this Stack Mathematics Answer. A few points especially stand out for me in it:

  1. You can have a total mess of connections/branches, but if it's interactive and the branches to and from a specific node light up when you hover over that node, then it can still be a readable/usable graph.
  2. You can try different sorting algorithms until you find one that works well with your data.
  3. You can have one or more nodes inside the circle, and in this case putting the root node in the middle might make a lot of sense.

It is not a practically usable answer, since it's in Mathematica, but I think its explanation of the underlying theories and techniques is useful.

A perhaps more artistic approach, if you do not want to put the root of the tree in the center, would be to make all branches from parents to the root parent be semi-transparent, and possibly a different color.

(Sorry for the low-quality photoshop)

Hope this helps!

Upvotes: 0

Related Questions