Raspberry Pi Pico Tips and Tricks

Saturday, 11 January 2014

Tree diagrams in d3.js

The following post is a portion of the D3 Tips and Tricks book which is free to download. To use this post in context, consider it with the others in the blog or just download the the book as a pdf / epub or mobi .
----------------------------------------------------------

What is a Tree Diagram?

The ‘Tree layout’ is not a distinct type of diagram per se. Instead, it’s representative of D3’s family of hierarchical layouts.
It’s designed to produce a ‘node-link’ diagram that lays out the connection between nodes in a method that displays the relationship of one node to another in a parent-child fashion.
For example, the following diagram shows a root node (the starting position) labelled ‘Top Node’ which has two children (Bob: Child of Top Node and Sally: Child of Top Node). Subsequently, Bob:Child of Top Node has two dependent nodes (children) ‘Son of Bob’ and ‘Daughter of Bob’.
The clear advantage to this style of diagram is that describing it in text is difficult, but representing it graphically makes the relationships easy to determine.
The data required to produce this type of layout needs to describe the relationships, but this is not necessarily an onerous task. For example, the following is the data (in JSON form) for the diagram above and it shows the minimum information required to form the correct layout hierarchy.
  {
    "name": "Top Node",
    "children": [
      {
        "name": "Bob: Child of Top Node",
        "parent": "Top Node",
        "children": [
          {
            "name": "Son of Bob",
            "parent": "Bob: Child of Top Node"
          },
          {
            "name": "Daughter of Bob",
            "parent": "Bob: Child of Top Node"
          }
        ]
      },
      {
        "name": "Sally: Child of Top Node",
        "parent": "Top Node"
      }
    ]
  }
It shows each node as having a name that identifies it on the tree and where appropriate, the children it has (as an array) and its parent.
The data shown above is arranged as a hierarchy and it is not always possible to source data that is organised so nicely. As we go through use examples for this type of diagram we will look at options for importing ‘flat’ data and converting it into a hierarchical form.
There is a wealth of examples of tree diagrams on the web, but I would recommend a visit to the D3.js gallery maintained by Christophe Viau as a starting point to get some ideas.
In this chapter we’re going to look at a very simple piece of code to generate a tree diagram before looking at different ways to adapt it. Including rotating it to be vertical, adding some dynamic styling to the nodes, importing from a flat file and from an external source. Finally we’ll look at a more complex example that is more commonly used on the web that allows a user to expand and collapse nodes interactively.

A simple Tree Diagram explained

We are going to work through a simple example of the code that draws a tree diagram, This is more for the understanding of the process rather than because it is a good example of code for drawing a tree diagram. It is a very limited example that lacks any real interactivity which is one of the strengths of d3.js graphics. However, we will outline the operation of an interactive version towards the end of the chapter once we have explored some possible configuration options that we might want to make.
The graphic that we are going to generate will look like this…

And the full code for it looks like this;
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">

    <title>Collapsible Tree Example</title>

    <style>

 .node circle {
   fill: #fff;
   stroke: steelblue;
   stroke-width: 3px;
 }

 .node text { font: 12px sans-serif; }

 .link {
   fill: none;
   stroke: #ccc;
   stroke-width: 2px;
 }
 
    </style>

  </head>

  <body>

<!-- load the d3.js library --> 
<script src="http://d3js.org/d3.v3.min.js"></script>
 
<script>

var treeData = [
  {
    "name": "Top Level",
    "parent": "null",
    "children": [
      {
        "name": "Level 2: A",
        "parent": "Top Level",
        "children": [
          {
            "name": "Son of A",
            "parent": "Level 2: A"
          },
          {
            "name": "Daughter of A",
            "parent": "Level 2: A"
          }
        ]
      },
      {
        "name": "Level 2: B",
        "parent": "Top Level"
      }
    ]
  }
];

// ************** Generate the tree diagram  *****************
var margin = {top: 20, right: 120, bottom: 20, left: 120},
 width = 960 - margin.right - margin.left,
 height = 500 - margin.top - margin.bottom;
 
var i = 0;

var tree = d3.layout.tree()
 .size([height, width]);

var diagonal = d3.svg.diagonal()
 .projection(function(d) { return [d.y, d.x]; });

var svg = d3.select("body").append("svg")
 .attr("width", width + margin.right + margin.left)
 .attr("height", height + margin.top + margin.bottom)
  .append("g")
 .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

root = treeData[0];
  
update(root);

function update(source) {

  // Compute the new tree layout.
  var nodes = tree.nodes(root).reverse(),
   links = tree.links(nodes);

  // Normalize for fixed-depth.
  nodes.forEach(function(d) { d.y = d.depth * 180; });

  // Declare the nodes…
  var node = svg.selectAll("g.node")
   .data(nodes, function(d) { return d.id || (d.id = ++i); });

  // Enter the nodes.
  var nodeEnter = node.enter().append("g")
   .attr("class", "node")
   .attr("transform", function(d) { 
    return "translate(" + d.y + "," + d.x + ")"; });

  nodeEnter.append("circle")
   .attr("r", 10)
   .style("fill", "#fff");

  nodeEnter.append("text")
   .attr("x", function(d) { 
    return d.children || d._children ? -13 : 13; })
   .attr("dy", ".35em")
   .attr("text-anchor", function(d) { 
    return d.children || d._children ? "end" : "start"; })
   .text(function(d) { return d.name; })
   .style("fill-opacity", 1);

  // Declare the links…
  var link = svg.selectAll("path.link")
   .data(links, function(d) { return d.target.id; });

  // Enter the links.
  link.enter().insert("path", "g")
   .attr("class", "link")
   .attr("d", diagonal);

}

</script>
 
  </body>
</html>
The full code for this example can be found on github, in the appendices of  D3 Tips and Tricks  or in the code samples bundled with  D3 Tips and Tricks  (simple-tree-diagram.html). A working example can be found on bl.ocks.org.
In the course of describing the operation of the file I will gloss over the aspects of the structure of an HTML file which have already been described at the start of D3 Tips and Tricks. Likewise, aspects of the JavaScript functions that have already been covered will only be briefly explained.
The start of the file deals with setting up the documents html head, body loading the d3.js script and setting up the css in the <style> section.
The css section sets styling for the circle that represents the nodes, the text alongside them and the links between them.
 .node circle {
   fill: #fff;
   stroke: steelblue;
   stroke-width: 3px;
 }

 .node text { font: 12px sans-serif; }

 .link {
   fill: none;
   stroke: #ccc;
   stroke-width: 2px;
 }
Then our JavaScript section starts and the first thing that happens is that we declare our array of data in the following code;
var treeData = [
  {
    "name": "Top Level",
    "parent": "null",
    "children": [
      {
        "name": "Level 2: A",
        "parent": "Top Level",
        "children": [
          {
            "name": "Son of A",
            "parent": "Level 2: A"
          },
          {
            "name": "Daughter of A",
            "parent": "Level 2: A"
          }
        ]
      },
      {
        "name": "Level 2: B",
        "parent": "Top Level"
      }
    ]
  }
];
As outlined at the start of the chapter, this data is encoded hierarchically in JavaScript Object Notation (JSON). Each node must have a name and either a parent or child node(s) or both. There are many examples of hierarchical data that can be encoded in this way. From the traditional parent - offspring example to directories on a hard drive or a breakdown of materials for a complex object. Any system of encoding where there is a single outcome from multiple sources like an election or an alert encoding system dependent on multiple trigger points.
The next section of our code declares some of the standard features for our diagram such as the size and shape of the svg container with margins included.
var margin = {top: 20, right: 120, bottom: 20, left: 120},
 width = 960 - margin.right - margin.left,
 height = 500 - margin.top - margin.bottom;
 
var i = 0;

var tree = d3.layout.tree()
 .size([height, width]); 
It also assigns the variable / function tree to the d3.js function that is used to assign and calculate the data required for the nodes and links for our diagram. We will be calling that later.
The next block of code declares the function that will be used to draw the links between the nodes. This isn’t the part of the code where the links are drawn, this is just declaring the variable/function that will be used when it does happen.
var diagonal = d3.svg.diagonal()
 .projection(function(d) { return [d.y, d.x]; });
This uses the d3.js diagonal function to help draw a path between two points such that the line exhibits some nice flowing curves (cubic Bézier ) to make the connection.
The next block of code appends our SVG working area to the body of our web page and creates a group elements (<g>) that will contain our svg objects (our nodes, text and links).
var svg = d3.select("body").append("svg")
 .attr("width", width + margin.right + margin.left)
 .attr("height", height + margin.top + margin.bottom)
  .append("g")
 .attr("transform", "translate(" + margin.left + "," + margin.top + ")");
The next line is one that vexed me for a while and one that I think means there are other areas of my code that could be improved (for a short interlude on why this tried me, feel free to catch this question on Stack Overflow).
root = treeData[0];
It might not look like much and to those familiar with JavaScript, it will bee a no-brainer, but what line is doing is defining what ‘tree’ from our data is going to be used. Because our data is an array, the first level of the array istreeData. The name of the first object on the first level of treeData is ‘Top Level’. This (being the first object) is object 0. Therefore our starting point is treeData[0]. We could confirm this by changing the declaration to …
root = treeData[0].children[0];
This will take the root point for our diagram as being the first child (child[0]) of the the first level of treeData. As a result, our tree diagram will look like this…

… since ‘Level 2: A’ is the first child of ‘Top Level’.
Then we call the function that draws our tree diagram.
update(root);
This calls the function update and uses the data root to create our tree.
The last significant part of the code is the function update. This is the part of the code that pulls together the functions and data that we have declared and draws our tree.
The first step in that process is to assign our nodes and links.
  var nodes = tree.nodes(root),
   links = tree.links(nodes);
This uses our previously declared tree function to work its d3.js magic on our data (root) and to determine the nodes details and from the node details we can determine the link details.
If you’re wondering how this all works, I’m afraid that I won’t be able to help much, but a starting point would be the results that the process produces which is a set of nodes, each of which has a set of characteristics. Those characteristics are; - .children: Which is an array of any children that exist for that node. - .depth: Which is the depth (described in a few paragraphs time). - .id: Which is a unique number identifier for each node. - .name: The name we have assigned from our data. - .parent: The name of the parent of the node. - .x and .y: Which are the x and y positions on the screen of the node.
From this node data a set of links joining the nodes is created. Each link consists of a .source and .target. Each of which is a node.
We then determine the horizontal spacing of the nodes.
  nodes.forEach(function(d) { d.y = d.depth * 180; });
This uses the depth of the node (as determined for each node in the nodes = tree.nodes(root) line) to calculate the position on the y axis of the screen.
The depth refers to the position in the tree relative to the root node on the left. The following picture shows how the depth relates to the position of the node in the tree.

So by adjusting our ‘expansion factor’ (currently set to 180) we can adjust the spacing of the nodes. For instance, here is the spacing changed to 80.

We then declare the variable / function node so that when we call it later it will know to select the appropriate object (a node) with the appropriate .id.
  var node = svg.selectAll("g.node")
   .data(nodes, function(d) { return d.id || (d.id = ++i); });
The next block of code assigns the variable / function nodeEnter to the action of appending a node to a particular position.
  var nodeEnter = node.enter().append("g")
   .attr("class", "node")
   .attr("transform", function(d) { 
    return "translate(" + d.y + "," + d.x + ")"; });
Then we get to the piece of code that appends the circle that comprises the node (using nodeEnter).
  nodeEnter.append("circle")
   .attr("r", 10)
   .style("fill", "#fff");
(using a radius of 10 pixels and a white fill).
And we add in the text for each node…
  nodeEnter.append("text")
   .attr("x", function(d) { 
    return d.children || d._children ? -13 : 13; })
   .attr("dy", ".35em")
   .attr("text-anchor", function(d) { 
    return d.children || d._children ? "end" : "start"; })
   .text(function(d) { return d.name; })
   .style("fill-opacity", 1);
This is a neat piece of code that allows the text to be placed on the left side of the node if it has children (d.children) or on the right if it has has no children or d._children. This is a slightly redundant piece of code (the d._children piece) for this diagram, but it becomes more useful in the interactive version towards the end of the chapter. It also aligns the text correctly and makes sure it is visible.
Then we declare the link variable / function and tell it to make a link based on all the links that have unique target id’s.
  var link = svg.selectAll("path.link")
   .data(links, function(d) { return d.target.id; });
This might not be obvious at first glance, but we only want to draw links between a node and it’s parent. There should be one less link than the total number of nodes since the root node (‘Top Level’) has no parent. Therefore only those links with unique target id’s in the data need to have links produced. If we were to replace the .target in the above code with .source we would have only two unique .source id’s. It would therefore look like this;

Our final block of JavaScript adds in our link as a diagonal path (as declared early in the JavaScript portion of the code).
  link.enter().insert("path", "g")
   .attr("class", "link")
   .attr("d", diagonal);
There are only a couple of lines of HTML to close off the file and we are left with our tree diagram!

Don’t forget, the full code for this example can be found on github, in the appendices of D3 Tips and Tricks or in the code samples bundled with D3 Tips and Tricks (simple-tree-diagram.html). A working example can be found on bl.ocks.org.

Styling nodes in a tree diagram

The nodes in a tree diagram are objects that exist to provide a representation of the structure of data, but on a tree diagram they should also be viewed as an opportunity to encode additional information about the underlying data.
From the initial simple example that we covered at the start of the chapter we have encoded a certain amount of information already. The position of the text relative to each node is determined by whether or not the node is the parent of another node (if it’s a parent it’s on the left) or a child that is on the edge of the tree (in which case it is on the right of the node).

Now, that’s nice, but are we going to be satisfied with that??? (The answer is “No” by the way.)
This example is fairly simple, but it is an example of applying different styles to the nodes to convey additional information. I should be clear at this stage that I am not advocating turning your tree diagram into something that looks like it came out of a circus, because that would be a crime against style, so don’t repeat my upcoming example, but let some of the features be a trigger for developing your own subtle, yet compelling visualizations.
Brace yourself. Here’s a picture of the tree diagram that we’re going to generate. Those with weaker constitutions should look away and flip forward a few pages.

The changes that have been made are as a result of additional data fields that have been added to the JSON array and these fields have been applied to various style options throughout the code.
The types of style changes we have made are - Variation of the diameter of nodes - Changing the fill and stroke colour of nodes - Changing the colour of links depending on the associated target node they connect to.
We’ll start by looking at the new JSON data set;
  {
    "name": "Top Level",
    "parent": "null",
    "value": 10,
    "type": "black",
    "level": "red",
    "children": [
      {
        "name": "Level 2: A",
        "parent": "Top Level",
        "value": 15,
        "type": "grey",
        "level": "red",
        "children": [
          {
            "name": "Son of A",
            "parent": "Level 2: A",
            "value": 5,
            "type": "steelblue",
            "level": "orange"
          },
          {
            "name": "Daughter of A",
            "parent": "Level 2: A",
            "value": 8,
            "type": "steelblue",
            "level": "red"
          }
        ]
      },
      {
        "name": "Level 2: B",
        "parent": "Top Level",
        "value": 10,
        "type": "grey",
        "level": "green"
      }
    ]
  }
Each node now has a value which might represent a degree of importance (we will use this to affect the radius of the nodes), a type which might indicate a difference in the type of node (they might be in an active, inactive or undetermined states) and a level which might indicate an alert level for determining problems (red = bad, orange = caution and green = normal).
Irrespective of the contrived nature of our styling options, they are applied to our tree in fairly similar ways with some subtle differences.
Remember, the full code for this example can be found on github or in the code samples bundled with D3 Tips and Tricks  (simple-tree-features.html). A working example can be found on bl.ocks.org.
The first change is to the node radius, stroke colour and fill colour.
We simply change the portion of the code that appends the circle from this…
  nodeEnter.append("circle")
   .attr("r", 10)
   .style("fill", "#fff");
… to this …
  nodeEnter.append("circle")
   .attr("r", function(d) { return d.value; })
   .style("stroke", function(d) { return d.type; })
   .style("fill", function(d) { return d.level; });
The changes return the radius attribute as a function using value, the stroke colour is returned using type and the fill colour is returned with level. This is nice and simple, but we do need to make a slight adjustment to the code that sets the distance that the text is from the nodes so that when the radius expands or contracts, the text distance from the edge of the node adjusts as well.
To do this we take the clever piece of code that adjusts the distance that the text is in the x dimension from the node that looks like this …
   .attr("x", function(d) { 
    return d.children || d._children ? -13 : 13; })
… and we add in a dynamic aspect using the value field.
   .attr("x", function(d) { 
    return d.children || d._children ? 
    (d.value + 4) * -1 : d.value + 4 })
The last thing we wanted to do is to change the colour of the link based on the colour of the target node. We accomplish this by taking the code that inserts the links…
  link.enter().insert("path", "g")
   .attr("class", "link")
   .attr("d", diagonal);
… and adding in a line that styles the link colour (the stroke) based on the level colour of the target end of the link d.target.level).
  link.enter().insert("path", "g")
   .attr("class", "link")
   .style("stroke", function(d) { return d.target.level; })
   .attr("d", diagonal);
Use the concepts here wisely. I don’t want to see any heinously styled tree diagrams floating around the internet with “Thanks to the help from D3 Tips and Tricks” next to them. Be subtle, be thoughtful :-).

Making a vertical tree diagram

Changing a tree diagram from a horizontal view to a vertical one is fairly easy. There are only three things to change from the code that we used for our original simple tree diagram.
The first is to change the orientation of the nodes by transposing the x and y coordinates.
That means taking the section of code that appends the nodes…
  var nodeEnter = node.enter().append("g")
   .attr("class", "node")
   .attr("transform", function(d) { 
    return "translate(" + d.y + "," + d.x + ")"; });
… and swapping the d.x and d.y designators so that it looks like this…
  var nodeEnter = node.enter().append("g")
   .attr("class", "node")
   .attr("transform", function(d) { 
    return "translate(" + d.x + "," + d.y + ")"; });
Because the vertical version of the tree diagram can be a lot more compact, we can adjust our difference between the depths to a more rational value. In our example we can change the separation from 180 to 100 pixels in the following line of code…
  nodes.forEach(function(d) { d.y = d.depth * 100; });
The second is to do the same adjustment for the links. We take the block of code that generates the curvy diagonal paths…
var diagonal = d3.svg.diagonal()
 .projection(function(d) { return [d.y, d.x]; });
… and swap the d.x and d.y designators so that it looks like this…
var diagonal = d3.svg.diagonal()
 .projection(function(d) { return [d.x, d.y]; });
At this point we have our tree diagram ready to go except for one small detail…

The text is still aligned to the left and right of the nodes. On this example, it looks pretty good, but if we were to introduce a few more nodes, it would start to get pretty cramped, so we can place the text above and below the nodes dependent on whether the node is a parent (above) or a child on the bottom level (below).
To do this we take the original text appending code…
  nodeEnter.append("text")
   .attr("x", function(d) { 
    return d.children || d._children ? -13 : 13; })
   .attr("dy", ".35em")
   .attr("text-anchor", function(d) { 
    return d.children || d._children ? "end" : "start"; })
   .text(function(d) { return d.name; })
   .style("fill-opacity", 1);
… and change the x attribute to a y attribute, anchor the text in the middle (which is actually a simplification of the code) and extend the distance between the node and the anchor point slightly to 18 (and -18) pixels.
  nodeEnter.append("text")
   .attr("y", function(d) { 
    return d.children || d._children ? -18 : 18; })
   .attr("dy", ".35em")
   .attr("text-anchor", "middle")
   .text(function(d) { return d.name; })
   .style("fill-opacity", 1);
And there we have it! A vertical tree diagram.

The full code for this example can be found on github or in the code samples bundled with  D3 Tips and Tricks  (simple-tree-vertical.html). A working online example can be found on bl.ocks.org.

Generating a tree diagram from ‘flat’ data

Tree diagrams are a fantastic way of displaying information, but one of the drawbacks (to the examples we’ve been using so far) is the need to have your data encoded hierarchically. Most data in a raw form will be flat. That is to say, it won’t be formatted as an array with the parent - child relationships. Instead it will be a list of objects (which we will want to turn into nodes) that might describe the relationship to each other, but they won’t be encoded that way. For example, the following is the flat representation of the example data we have been using thus far.
    { "name" : "Level 2: A", "parent":"Top Level" },
    { "name" : "Top Level", "parent":"null" },
    { "name" : "Son of A", "parent":"Level 2: A" },
    { "name" : "Daughter of A", "parent":"Level 2: A" },
    { "name" : "Level 2: B", "parent":"Top Level" }
It is actually fairly simple and consists of only the name of the node and the name of it’s parent node. It’s easy to see how this data could be developed into a hierarchical form, but it would take a little time and for a larger data set, that would be tiresome.
Luckily computers are built for shuffling data about and with kudos to ‘nrabinowitz’ for answering a question (and Prateek Tandon for asking) on Stack Overflow (and Jesus Ruiz with AmeliaBR for setting me on the right path), here is how we can take our flat data and convert it for use in our tree diagram.
We will be using the simple example that we started with at the start of the chapter and the first change we need to make is to replace our original data…
var treeData = [
  {
    "name": "Top Level",
    "parent": "null",
    "children": [
      {
        "name": "Level 2: A",
        "parent": "Top Level",
        "children": [
          {
            "name": "Son of A",
            "parent": "Level 2: A"
          },
          {
            "name": "Daughter of A",
            "parent": "Level 2: A"
          }
        ]
      },
      {
        "name": "Level 2: B",
        "parent": "Top Level"
      }
    ]
  }
];
… with our flat data array…
var data = [
    { "name" : "Level 2: A", "parent":"Top Level" },
    { "name" : "Top Level", "parent":"null" },
    { "name" : "Son of A", "parent":"Level 2: A" },
    { "name" : "Daughter of A", "parent":"Level 2: A" },
    { "name" : "Level 2: B", "parent":"Top Level" }
    ];
It’s worth noting here that we have also changed the name of the array (to data) since we are going to convert, then declare our newly massaged data with our original variable name treeData so that the remainder of our code thinks there have been no changes.
Then we create a name-based map for the nodes. In his answer on Stack Overflow, ‘nrabinowitz’ uses the.reduce method, which starts with an empty object and iterates over the data array, adding an entry for each node.
var dataMap = data.reduce(function(map, node) {
 map[node.name] = node;
 return map;
}, {});
Don’t feel upset if you don’t understand exactly how it works. I struggle to understand internal combustion engines, but I’m ok at driving a car :-). Think of this in the same way.
Then we iteratively add each child to its parents, or to the root array if no parent is found;
var treeData = [];
data.forEach(function(node) {
 // add to parent
 var parent = dataMap[node.parent];
 if (parent) {
  // create child array if it doesn't exist
  (parent.children || (parent.children = []))
   // add node to child array
   .push(node);
 } else {
  // parent is null or missing
  treeData.push(node);
 }
});
The code is essentially working through each node in the array and if it has a child it adds it to the childrensub-array and if necessary creates the array. Likewise, if the node has no parent, it simply add it as a root node.
That’s it!
The brevity of the code to do this should not detract from its elegance. It really is very clever. The end result doesn’t look any different from our original diagram…

… but it adds a significant capability for use of additional data.
The full code for this example can be found on github or in the code samples bundled with D3 Tips and Tricks  (simple-tree-from-flat.html). A working example can be found on bl.ocks.org.

Generating a tree diagram from external data

In all the examples we have looked at so far we have used data that we have declared from within the file itself. Being able to import data from an external file is an important feature that we need to know how to implement.
Starting from the simple tree diagram example that we began with at the start of the chapter, the first change that we need to make is to remove the section of code that declares our data. But don’t throw it away since we will use it to create a separate file called treeData.json. It’s contents will be;
[
  {
    "name": "Top Level",
    "parent": "null",
    "children": [
      {
        "name": "Level 2: A",
        "parent": "Top Level",
        "children": [
          {
            "name": "Son of A",
            "parent": "Level 2: A"
          },
          {
            "name": "Daughter of A",
            "parent": "Level 2: A"
          }
        ]
      },
      {
        "name": "Level 2: B",
        "parent": "Top Level"
      }
    ]
  }
]
(don’t include the treeData = part, or the semicolon at the end (you can delete those))
Then all we need to do is change the portion of the code that declared the root variable and updates the diagram;
root = treeData[0];
  
update(root);
… into a small section that uses the d3.json accessor to load the file treeData.json (Remember to correctly address the file. This one assumes that the treeData,json file is in the same directory as the html file we are opening).
d3.json("treeData.json", function(error, treeData) {
  root = treeData[0];
  update(root);
});
It then declares the variable root in the same way and calls the update function to draw the tree diagram. Viola!
The full code for this example can be found on github or in the code samples bundled with D3 Tips and Tricks  (simple-tree-from-external.html and treeData.json). A working example can be found on bl.ocks.org.

An interactive tree diagram

The examples presented thus far have all been static in the sense that they present information on a web page, but that’s where they stop. One of the strengths of web content is the ability to involve the reader to a greater extent. Therefore the following tree diagram example includes an interactive element where the user can click on any parent node and it will collapse on itself to make more room for others or to simplify a view. Additionally, any collapsed parent node can be clicked on and it will re-grow to its previous condition.
The example included here is a close derivative of Mike Bostock’s example. I won’t fully explain the operation of this file, but we will consider parts of it for interests sake.
The full code for this example can be found on github, in the appendices of  D3 Tips and Tricks or in the code samples bundled with  D3 Tips and Tricks (interactive-tree.html). A working online example can be found on bl.ocks.org.
For a brief visual description of the action. The diagram will initially display the complete tree...

Then when clicking on the ‘Level 2: A’ node, the tree partially collapses to...

We could also click on the root node (`Top Level’) to fully collapse the tree....
Then clicking on the nodes opens the diagram back up again.
One of the important changes to start with is to make each node responsive to the mouse pointer. This is done by including the following in the <style> section.
 .node {
  cursor: pointer;
 }
The code then adds sections to allow the diagram to follow the d3.js model of enter - update - exit for the nodes with a suitable transition in between.
Nodes are coloured (“steelblue”) if they have been collapsed and at the end of the script we have a function that makes use of the d._children reference we have been using in most of our examples.
function click(d) {
  if (d.children) {
 d._children = d.children;
 d.children = null;
  } else {
 d.children = d._children;
 d._children = null;
  }
  update(d);
}
This allows the action of clicking on the nodes to update the data associated with the node and as a consequence change it’s properties in the script based on if statements (Such as"fill", function(d) { return d._children ? "lightsteelblue" : "#fff"; } which will fill the node with “lightsteelblue” if d._children exists, otherwise make it white.)
The examples we have looked at in the previous sections in this chapter are all applicable to this interactive version, so this should provide you with the capability to generate some interesting visualizations.
Enjoy.

The description above (and heaps of other stuff) is in the D3 Tips and Tricks book that can be downloaded for free (or donate if you really want to :-)).

128 comments:

  1. Thanks for this helpful description. I do have one question that I think you might be able to answer: Could you explain how program multiple shapes (e.g., circle and rect) based on values in the data? (i.e., say there was a field that was labeled "shape", with values of 1 and 2.

    Thanks a lot!!

    ReplyDelete
    Replies
    1. That is a really good question! I can think of a couple of directions I would try. First would be to look at symbols (there's some use of them in force diagrams in particular) Second would be to encode the shapes as paths and then call whichever one with an appropriate if statement. The question has piqued my curiosity, I will have a browse.

      Delete
    2. OK, just a quick update. The answer is yes, it can be done. At least with paths. I will work on an example and post in a day or so. Great question!

      Delete
    3. Oki Doki. There is a working example of a script that uses the d3 symbol generator to place different nodes depending on the value assigned to the node here http://bl.ocks.org/d3noob/11137963. I have added a section to the book and it is publishing itself as I type. Great suggestion again Josiah. Thanks.

      Delete
    4. i saw your eg: http://bl.ocks.org/d3noob/11137963 , can you also explain how to add rectangle and hexagon shapes ?

      Delete
    5. Ahh.. Good question, but I would direct you to the following since they already have some good direction. http://stackoverflow.com/questions/24539234/list-of-d3-symbols-available-to-us and http://stackoverflow.com/questions/25332120/create-additional-d3-js-symbols
      Best of luck

      Delete
  2. Good one on visualizing the trees. Is there a way that i can use various images instead of various shapes? Thank in advance..

    ReplyDelete
    Replies
    1. Great follow on question! At first I wasn't sure, but I recall seeing some work done using images on force diagrams... So after some quick googling...
      http://stackoverflow.com/questions/11269067/d3-js-nodes-as-images
      http://stackoverflow.com/questions/7306250/does-force-directed-layout-of-d3-js-support-image-as-node
      Both of these will set you in the right direction I believe. If I have time I will try to do an example, but it will probably be a couple of days. If you beat me to it, post a link to your example here!
      Great question.

      Delete
    2. Sorry, couldn't help myself. See here for an example using images http://bl.ocks.org/d3noob/9662ab6d5ac823c0e444
      I will write it up for the book and blog in a couple of days.
      Thanks again for the question.

      Delete
  3. How can we collapse all nodes from a particulate depth onwards??

    ReplyDelete
    Replies
    1. Good question. there will be a way, but this is an area where my lack of formal training is exposed. I suspect that there is a clever mechanism that involves identification of the particular node and subsequent recursion through the lower nodes, but it would be beyond me to develop that independently. I'd ask a question on Stack Overflow, but first I'd probably experiment with something along the lines of the root = treeData[0]; and update(root); pieces of code. Some form of node selection and then recursion thing. Hmm... Tricky... Good question!

      Delete
  4. Can we have the path between current clicked node and stating node have highlighted?

    ReplyDelete
    Replies
    1. I've never tried to, but the following post has a demonstration of how it can be done http://blog.pixelingene.com/2011/08/progressive-reveal-animations-in-svg-using-a-svgclippath/

      Delete
    2. Please could you show me an entire example? I need to do it in a vertical tree collapse, here it is a little example :
      http://jsfiddle.net/ctz358f6/784/

      Thanks a lot

      Delete
    3. Sorry, I don't have any time available at the moment to develop an example (although it would be a good addition to the book). I suggest that you continue to persevere and keep trying to get closer to the solution by experimenting with the code. I'ts honestly a great way to learn a bit more about how the code is structured and you would be breaking new ground for the community. Good luck.

      Delete
  5. Hi,thanks for your tutorial ,I have an another question.like this page http://bl.ocks.org/jdarling/2503502
    i want to use your Tree Diagram but I also want the layout like http://bl.ocks.org/jdarling/2503502
    do you have any suggestion?
    thank you very much

    ReplyDelete
    Replies
    1. That's a good question. Sorry for the delay in replying. I believe that the mechanism that drives the connection between the nodes is integrated into the ds.js file itself At least that is what it appears to be based on a look at the wiki (https://github.com/mbostock/d3/wiki/Tree-Layout) and a quick look through the code. If this is the case, then you would be able to alter it, but that would leave you in the position of using a file that you would need to update every time d3.js improved.

      Delete
  6. how can we show the images in place of circle?

    ReplyDelete
    Replies
    1. A good question! So good that you should see the same thing asked a few questions up the page :-). Anyhoo, there's an example right here http://bl.ocks.org/d3noob/9662ab6d5ac823c0e444 Enjoy

      Delete
  7. How can we call data from database using mysql ?
    How to make the diagram dragable if it extends outside the area ?

    ReplyDelete
    Replies
    1. You can call the data from MySQL using the technique described in the book here (https://leanpub.com/D3-Tips-and-Tricks/read#leanpub-auto-using-php-to-extract-json-from-mysql) Panning a tree diagram is something I haven't tried before, but I note that someone has an exmple here http://stackoverflow.com/questions/17405638/d3-js-zooming-and-panning-a-collapsible-tree-diagram Good luck

      Delete
  8. Phenomenal post. Thanks a ton!

    ReplyDelete
  9. Really good post! Thank you.

    ReplyDelete
  10. hi please guide me through how can i extract these tree into pdf file .

    Thanks

    ReplyDelete
    Replies
    1. I haven't done this before, but I did Google it (https://www.google.co.nz/webhp?ion=1&espv=2&ie=UTF-8#q=d3.js%20into%20pdf) and there were several examples there. Have a play with those and see how you get on.

      Delete
  11. hi please guide me through if it is possible to generate two root node of two different tree in the same page with the same script code

    Thanks in Advance

    ReplyDelete
    Replies
    1. Wow. Interesting question. I really don't know how that would be done. Thinking about the problem, I believe that it probably is possible, but I wouldn't be clever enough to know where to start. One possibility that springs to mind (and this would depend on the source data you have) would be to create a tree but to then remove the root node (or make it invisible) and it would appear to be two separate trees? I don't think that idea would be the best direction because of the restrictions on the way that you would need to manipulate the data source. Good luck and sorry I couldn't really help much.

      Delete
  12. when i try to collapse or expand the nodes the circles take different positions and the links are elsewhere..can u help me to fix this

    ReplyDelete
    Replies
    1. Hi Samiksha. Sorry for the late reply. I would struggle to solve the problem you describe in an efficient way, but if you were to lay out the issue on Stack Overflow there are a heap of people that would be able to assist. Important things to remember when asking on SO is to provide a good description of what you are trying to do, what your results are so far and the code you are using. If possible include a working example from a JSfiffle or similar. Good luck.

      Delete
  13. Hi,

    If I want a transition for the node text as follow:
    - Text should be on the left if the node is expanded.
    - Text should be on the right if the node is collapsed.

    I have it working already, but the text just disappears from one side and appears on the other, it just doesn't look good. Can I use a transition for this?
    Any guidance as to how to make it happen?

    I'm already trying, but since I'm just learning the details of d3 and this stuff, it will probably take me a while, any help appreciated.

    Thanks.

    ReplyDelete
    Replies
    1. Apologies for the extremely late answer to your question. I expect that you have moved on, but I'll try and provide some guidance for anyone else who might read.
      A nice transition should be entirely possible. In theory, the place that it would be put would be in the `click` function and it would select the text an transition just the x positioning attribute.
      Again, sorry for the late reply

      Delete
  14. Thank you for a very interesting and informative post and discussion. Hugely helpful as I experiment with this tree layout for some hierarchical data I am trying to present.

    I have a couple of questions along these lines:

    1) My data at level 2 (if root/flare is level 0) is a long list (>200) of items---children of level 1. When I render this number of items at level 2 and do not adjust the dimensions of the working area, the level 2 list of items are overlapping and unreadable. If I adjust the height of the area to a larger number, the level 0 and level 1 parts of the tree are too spread out to be readable. Wondering if anyone knows of a way to manage both the long list of items at level 2 and still keep the level 0 and level 1 items compact.

    2) The parent-child relationships rendered in this tree format are very helpful for presenting nested data. I'm wondering if anyone knows of a related d3 library that would render level 1 data as a flow chart (lines connecting level 1 data to describe the steps in a process). Then each level 1 node has children (level 2) that would be connected to the parent at level 1 to designate inputs to that process step.

    Looking forward to feedback on these. Thank you again for a great post and discussion.

    ReplyDelete
    Replies
    1. A good question. This might also make a good question on the d3.js Google Groups forum.

      Delete
  15. Is there a way in highlighting the path of the selected node from the root?

    ReplyDelete
    Replies
    1. The following post describes a technique although I have never used it myself. http://blog.pixelingene.com/2011/08/progressive-reveal-animations-in-svg-using-a-svgclippath/ Good luck.

      Delete
    2. I checked out the above link, but still its not working. Can you please provide an example ?

      Delete
    3. Hi Piyush, I would struggle to do this as an example at the moment. I recommend that you get in touch with Pavan Podila who did the example above. At least that way you will get the answer from someone who is closest to the solution. Good luck.

      Delete
  16. Hi,

    I have used the collapsible tree library for displaying the data with some modifications. I would like to add edit functionality to the nodes (i.e. edit the node text and save it on clicking enter.). Can you please suggest me how to add such functionality to this library.


    Thanks

    ReplyDelete
    Replies
    1. Apologies for the extremely late answer to your question. I expect that you have moved on, but I'll try and provide some guidance for anyone else who might read.
      This isn't something that I've experimented with before, but a quick google would lead me to believe that it would be possible. Be aware that it may be more 'web' orientated than 'd3'. but that doesn't mean that it can't be done, just that it will most likely be done in a particular way. The best page for some inspiration might be https://bl.ocks.org/cjrd/6863459

      Delete
  17. How can we make this tree responsive.I have read some articles (http://demosthenes.info/blog/744/Make-SVG-Responsive) regarding making an SVG element responsive, but it requires that the width & height should NOT be set. But when I remove the width & height, its not working.

    ReplyDelete
    Replies
    1. The last section in the post above describes making the tree interactive if that's what you mean. Check out this example to see if it's what you mean http://bl.ocks.org/d3noob/8375092

      Delete
    2. By making the tree responsive- i meant to make it mobile friendly. I have to implement bootstrap in the page containing the tree. But currently the tree dimensions are fixed .

      Delete
    3. Ahh... Thanks for the explanation. That's a good question and one that applies to any visualization. Unfortunately that's not something I've ever explored. I woulls be interested if you come across an answer though. Sorry I couldn't be more help.

      Delete
  18. Hi. This is a wonderful post. I have a case where I need to add nodes to a tree as they are discovered. I have a search algorithm done in Java to search all nodes (folder/directory) in a system based on a criteria and respond to a web-search as JSON.
    I do not want to wait for all nodes and tree structure to be discovered before plotting the tree.
    Kindly guide me on how i can achieve this.

    ReplyDelete
    Replies
    1. Firstly apologies for the tardy reply. This is a really interesting question and the best answer I can give it to point you to a question that was asked on Stack Overflow here; http://stackoverflow.com/questions/27177193/d3-update-tree-layout-with-dynamic-data

      Delete
  19. Terrific tutorial! This has helped me tremendously. Any chance of adding a section on how to add a mouseover tooltip to each node to display additional information? It should be easy but it's proving somewhat difficult. Thanks in advance!

    ReplyDelete
    Replies
    1. Sorry about the late reply. The question was a good one and by chance I have done an example which I think might be useful. http://bl.ocks.org/d3noob/9692795

      Delete
  20. Hi, I tried using SFDC tree from d3. I am populating records from mySQL data by constructing JSON at runtime. I am able to get the tree control rendered on Chrome browser. However, it does not auto expand on its own when ready is available. Its only when user clicks on Circle, the entire tree gets shown. Please help and advice how auto open of tree can be achieved.

    ReplyDelete
    Replies
    1. The best way to solve this is probably to step back and look at a simpler example. Check this one out that has an expanded tree http://bl.ocks.org/d3noob/8329447 and then check out the interactive function per this example http://bl.ocks.org/d3noob/8375092. When in doubt check out the chapter on Tree Diagrams in the book (https://leanpub.com/D3-Tips-and-Tricks/read#leanpub-auto-tree-diagrams)

      Delete
  21. Can we add drag drop support to nodes.i mean chaning parent of a node just by dragging under another one?

    ReplyDelete
    Replies
    1. What a cool idea! Sorry for the late reply, but the question is really quite good. It almost sounds a bit more like a force diagram question since there's kind of two parts to it. One is the ability to drag the nodes and the second is the process of replacing the data from one node with the overlapping object. The force graphj already has the ability to drag nodes, but the data layout may not be too your liking. As to swapping the data, that would be a bit beyond my experience level I'm afraid. However. I suggest that you run a few experiments. If you'r unsuccessful, and armed with what you find out ask on stackoverflow if anyone can see a better way. Good luck.

      Delete
  22. Is it possible to represent family tree using d3. As in, child node vil come from 2 parent nodes(father & mother)

    ReplyDelete
    Replies
    1. Not to my knowledge. It's a good question and I think that there would be a degree of support for this style of graph, but it would be a different approach to a tree graph.

      Delete
  23. Regarding the flat data section. What if my data doesn't come in a ({ "name" : "Level 2: A", "parent":"Top Level" }) format. I have objects like ({id:110,name:frank, managerid:101}). How does that code know what the "name" is and what the "parent" is?

    ReplyDelete
    Replies
    1. Your data will need to be encoded with a minimum of name, parent and children in order to work correctly. This is a requirement in the d3 code. However, to make your data compliant if it has different labels for the values is to run it through a quick 'forEach' before processing it and use something like d.name = d.id.

      Delete
    2. Still have issue with following type of data

      {"EventTime":"2016-09-15 17:18:51","Hostname":"computer01","Keywords":-9223372036854775808,"EventType":"INFO","SeverityValue":2,"Severity":"INFO","EventID":1,"SourceName":"Microsoft-Windows-Sysmon","ProviderGuid":"{5770385F-C22A-43E0-BF4C-06F5698FFBD9}","Version":3,"Task":1,"OpcodeValue":0,"RecordNumber":53530648,"ProcessID":4928,"ThreadID":4996,"Channel":"Microsoft-Windows-Sysmon/Operational","Domain":"NT AUTHORITY","AccountName":"SYSTEM","UserID":"SYSTEM","AccountType":"User","Opcode":"Info","UtcTime":"15/09/2016 16:18","ProcessGuid":"{00000000-C9EB-57DA-0000-0010B215DB01}","Image":"C:\\Windows\\system32\\LogonUI.exe","CommandLine":"\"LogonUI.exe\" /flags:0x0","User":"NT AUTHORITY\\SYSTEM","LogonGuid":"{00000000-4A0A-57DA-0000-0020E7030000}","LogonId":"0x3e7","TerminalSessionId":"1","IntegrityLevel":"System","HashType":"SHA1","Hash":"F5778343588DF78FDD55C9CFCD49E5F39F959DD9","ParentProcessGuid":"{00000000-4A08-57DA-0000-00104BB80000}","ParentProcessId":"564","ParentImage":"C:\\Windows\\system32\\winlogon.exe","ParentCommandLine":"winlogon.exe","EventReceivedTime":"2016-09-15 17:27:36","SourceModuleName":"in","SourceModuleType":"im_msvistalog"},

      #added this before the map.reduce now get blank screen.

      data.forEach(function(d) {
      d.name = d.Image;
      d.parent = d.ParentProcessGuid;
      d.children = d.ProcessGuid;
      });

      Delete
    3. Hmm.... Interesting data set!
      Your foreach looks fine, so what I would do from here is to create a small fake data set (with some simple benign characters) with d.Image, d.ParentProcessGuid,and d.ProcessGuid in it and see if it process properly. This should test the code integrity and if it process fine then the problem will most likely lay in the characters in the json. It's possible that some of the more complex elements like "CommandLine" might cause problems, I'm not sure, but remove whatever doesn't need to be there and work through the rest methodically.

      Delete
  24. Hi,
    Great guide - thanks!
    I was wondering - normally in these diagrams nodes expand into more nodes. Is it possible to merge them together like in this image?
    http://imgur.com/J9vwsYt

    Assume it would mean creating the node from one point then manually adding a link from node x to y?

    ReplyDelete
    Replies
    1. I see what you have there, and it wouldn't fit the requirements for a tree diagram for d3.js. You could use a sankey diagram or it might possibly be abe to use a network diagram. It kind of depends on the end use. I'd heck out the sankey first http://www.d3noob.org/2013/02/sankey-diagrams-description-of-d3js-code.html. Good luck

      Delete
  25. How to decrease length of arc..can u please guide me step by step process

    ReplyDelete
    Replies
    1. I won't be able to provide a step by step breakdown sorry, but I suggest that you experiment with the step in the above explanation where we decrease the separation of the nodes in a vertical representation. Don't think of changing the length of the arcs so much as changing the separation of the nodes :-).

      Delete
  26. Thanks for the explanation; do you know if it is possible to configure this to show multiple parents of nodes? If not using this library, what do you recommend that maintains the hierarchy in a structured (non hairball-prone) way?

    ReplyDelete
    Replies
    1. I don't believe that it is possible to use the tree diagram to display multiple parents. You might possibly be able to display the data you want with a sankey diagram. There are some interesting configuration options for it so have a look http://www.d3noob.org/2013/02/sankey-diagrams-description-of-d3js-code.html. Good luck

      Delete
  27. If my treeData json has property Children(big C) instead of children(small c),its not working. I see something here(https://github.com/mbostock/d3/wiki/Tree-Layout#children), but I could not makeout what to do. Can you please help.

    ReplyDelete
    Replies
    1. You will definitely need to have the data specified with 'children' with a little 'c'. Start slowly with a simple example and expand it from there. Many people rush into trying to get code to work without understanding the impact of small changes in the code. Start with something simple and repeatable and build from there. Good luck.

      Delete
  28. Hi,

    A good tutorial to start on.
    Can you tell me, if I can fix the position of the root.
    Basically what i want to do is, I want to link two nodes say A and B. and both A and B are a tree layout in essence. So, I want to fix the position of A and B (root nodes of both the tree root) and the on click of each, render the tree that follow.
    A ------- B
    i |
    i P
    C
    |
    D

    ReplyDelete
    Replies
    1. That's an interesting question. I believe (although I'd be happy to be proven wrong) that this would require modification of the key functionality of the `d3.layout.tree` code. It could probably be done after the fact, but the effort to do it would probably outweigh the utility that you would get. Tricky one and I think it would be one where a degree of experimentation would allow you to determine how much difficulty the final result would be. Good luck

      Delete
  29. Excellent explanation, the code working well and I believe some examples on the web use it as a base too.

    Now my question is how to save the data into its flat representation (so adding a reverse process of above flat-to-tree function). I would like to save the tree data (including some changes by the administrator user) to a database and then retrieve it back, possibly using your data mapping code to turn it back to a tree and render it.

    At this stage I don't believe there is a more straight way with using a NoSQL database (I'd like to use Mongo). Any pointers would be helpful. Of course a flat representation would have name, parent and all other specific key-value pairs of a node in a custom solution (e.g. having additional attributes to your example).

    ReplyDelete
    Replies
    1. Good question!
      I think your reasoning is sound on the NoSQL option. Although I haven't experimented with NoSQL yet, it seems like overfill for this purpose. The way I would approach it is to save out the data for each node into a standard MySQL database with the form 'name,parent,value1,value2,value-etc'. This format will preserve the structure of the tree. Then from the MySQL export as csv or something like that. Thinking about it, this seems like a bit of a dirty hack, but I would do it anyway so that I would think of better ways while working through the problem.

      Delete
    2. Simple and universal answer, thanks (saved data can be read and changed to a tree with the same algorithm as above). I guess some sleep is in order.

      I actually was asked if a tree (displayed with D3.js) can be retrieved and parsed from a Mongo database. Better to ask others but definitely multilevel hierarchical structure and NoSQL database are a strange match by common sense.

      Delete
    3. Ahh... Sorry for not reading the question correctly. I wish I knew the answer. It's something that I'd like to pursue at some stage because I am disturbingly deficient in NoSQL experience.

      Delete
  30. Very nice tutorial, but I have a question.
    Is it possible to have multiple nodes ended with a single node?

    http://i.imgur.com/eRsLuVz.png

    ReplyDelete
    Replies
    1. I haven't seen that done and I suspect it would involve some serious code. However, I reccomend that you check out Sankey diagrams that could be a solution. http://blockbuilder.org/search#text=sankey

      Delete
  31. Thanks for the great article. Is it possible to display the node text with breaks? Like for example instead of "Top Level" will I be able to show

    Top
    Level

    Thanks,
    AR

    ReplyDelete
    Replies
    1. Good question. I haven't done it before, but the first thing I would try is to add the text as an html div. Check out the way that I did it for a simple tool tip here http://www.d3noob.org/2013/01/adding-tooltips-to-d3js-graph.html Live version here http://bl.ocks.org/d3noob/a22c42db65eb00d4e369

      Delete
  32. Thanks a lot for the post. I finally was able to understand how those trees are constructed.
    There is one thing that has been bothering me for a long time. Let s assume you have a "Daughter of Top level", which should be displayed vertically aligned with the other daughters, i.e. it has no Level 2 parent. For how much I try it is always displayed in the second level. Is it possible to change that? Thanks

    ReplyDelete
    Replies
    1. Ahh... That's a really good question... The short answer is that I don't know how to achieve what you're describing, but it's a really cool idea. My first reaction is that the code to build the trees as above (i.e in the ds.js code as d3.layout.tree) will require some changes. This would be a bit of a big ask, but it's an interesting problem that you might get some traction on in the d3 section on Staack Overflow. Apologies for the tardy reply.

      Delete
  33. Hi! Quick question, how would I keep the vertical diagram centred in the middle of the browser? For example when I adjust my browser width, it will automatically reposition to the centre.

    ReplyDelete
    Replies
    1. since it's in a svg element, the question could be asked how to center an svg element on a web page. Try this.. http://stackoverflow.com/questions/13373169/align-svg-to-center-of-screen

      Delete
  34. I tried the above code with
    var treeData =[ {
    "name":"start",
    "children":[
    {
    "name": "IF CREATIVE_SIZE EQUAL \"728x90\" ",
    "children": [{
    "name": "IF SITE_DOMAIN EQUAL \"cw39.com\" bid \u003d 3.9972",
    "children": [{
    "name": "ELSE SITE_DOMAIN EQUAL \"9jumpin.com.au\" bid \u003d 3.9971",
    "children": [{
    "name": "ELSE SITE_DOMAIN EQUAL \"financegourmet.com\" bid \u003d 3.997",
    "children": [{
    "name": "ELSE bid \u003d 3.9914",
    "size": 50
    }],
    "size": 50
    }],
    "size": 50
    }],
    "size": 50
    }],
    "size": 50
    },{
    "name": "ELSE CREATIVE_SIZE EQUAL \"300x250\" ",
    "children": [{
    "name": "IF SITE_DOMAIN EQUAL \"earplugsguide.com\" bid \u003d 3.9974",
    "children": [{
    "name": "ELSE SITE_DOMAIN EQUAL \"co-optimus.com\" bid \u003d 3.9973",
    "children": [{
    "name": "ELSE SITE_DOMAIN EQUAL \"thebsbnumbers.com\" bid \u003d 3.9973",
    "children": [{
    "name": "ELSE SITE_DOMAIN EQUAL \"changathi.com\" bid \u003d 3.9976",
    "children": [{
    "name": "ELSE bid \u003d 3.9914",
    "children": [],
    "size": 50
    }],
    "size": 50
    }],
    "size": 50
    }],
    "size": 50
    }],
    "size": 50
    }],"size": 50
    }
    ];
    but its not working. Can you help?

    ReplyDelete
    Replies
    1. Hmm.... Cool data set!
      What I would do from here is to create a small fake data set with some simple benign characters in it and see if it process properly. This should test the code integrity and if it process fine then the problem will most likely lay in the characters in the json. It's possible that some of the more complex elements might be causing problems, I'm not sure, but remove whatever doesn't need to be there and work through the rest methodically.

      Delete
  35. Hi,

    thanks for your tutorial,

    I have an another question.

    Plz tell how to convert JSON format into d3 JSON format using java, thanks.

    I'm already trying, but since I'm just learning the details of d3 and this stuff, it will probably take me a while, any help appreciated.

    Thanks.

    ReplyDelete
    Replies
    1. Hi there. JSON is just JSON. There isn't a different d3 version of it. I'm afraid that I have no experience with Java sorry.

      Delete
  36. Hi,
    Thanks for the explanation. This is exactly what i was looking for.
    The one thing I am still not clear about is the reverse() process applied to nodes. I am sure what it does.
    Request an explanation for the same. Apologize If I dont know that due to ignorance. :-)

    ReplyDelete
    Replies
    1. Hi there. To tell the truth I'm not entirely sure. From what I've read it appears to help in the definition of the depth of the nodes on the tree, but I don't recall a particularly in depth explanation sorry.

      Delete
  37. Hello D3noob,

    Thank you so much for your tutorial! I am trying to use the svg.line() path generator to generate the links instead of the diagonal as you showed here, but it doesn't seem to work. I am using it as shown in th3 D3 V3.1 API reference. Could you please give me some pointers on what I might need to change in order to get it working?

    Thanks!

    ReplyDelete
    Replies
    1. Hi there,
      That may be a bit tricky since the code that draws the links is within the d3.js code base. Have you considered using a network diagram instead?

      Delete
  38. Hello,

    Thanks for the great post. I'm trying to get the leaf node value on click event. Basically, I'm able get each node value by clicking on it. But I need only leaf node value. How can I get that value in Collapsible tree?

    Thanks

    ReplyDelete
    Replies
    1. Hmm.... Interesting question. I haven't tried this before, but I suspect that if you run an if statement to check the node for the presence of children. If it has no children it's a leaf. For an example see the interactive tree version where the click function checks to see if the node has children (http://bl.ocks.org/d3noob/8375092). If you're asking how to get leaf node values from nodes higher in the hierarchy, I'm afraid that would be out of my depth.

      Delete
  39. I already have a Java program that generates a tree. It has a class named Tree whose object I'd like to use for this visualization.

    class Node implements Comparable
    {
    String tag;
    int count;
    boolean leaf;
    ListchildNodes;
    }
    class Tree
    {
    Node root;
    }

    How would I do this?

    ReplyDelete
    Replies
    1. Sorry, I am unfamiliar with Java and I wouldn't know where to start.

      Delete
  40. How would i add scroll to this? My json file is really big.

    ReplyDelete
    Replies
    1. Check this out from Rob Schmuecker http://bl.ocks.org/robschmuecker/7880033

      Delete
    2. I don't really want to pann or zoom. Could you point me to where in the code the scroll is enabled such that the nodes don't overlap. Because my code has scroll because of the following code:

      window.setTimeout(function() {
      var max = d3.max(d3.selectAll(".node")[0], function(g) {
      return d3.transform(d3.select(g).attr("transform")).translate[1];
      });
      d3.select("svg").attr("height", max + 100)
      console.log(max)
      }, 800)

      But the nodes still overlap when a lot of the parents are clicked. How do I get the tree to resize?

      Delete
    3. Hmm... You're definitely venturing into a realm where I haven't gone before, but I think I get what you're wanting. The svg variable will determine the size of the 'container' for the tree, but the tree size (and therefore the overlap is set by the
      var tree = d3.layout.tree()
      .size([height, width]);
      Portion of the code. Have a play with that and see how you get on.

      Delete
  41. I was also wondering if there's a way to not restrict the tree to fixed size. i've come to realize that even scroll will not help because it would be restricted by the size of the svg element and that of the tree.

    ReplyDelete
    Replies
    1. Try adjusting the svg and tree size. Failing that you are facing the eternal problem of too much data and not enough room on the page.

      Delete
  42. how to replace circle node to image and image should be appear as circle.

    ReplyDelete
    Replies
    1. Hmm... I'm not sure that I'm totally sure what your asking for, but the process of changing a node to an image is well explained in the book (https://leanpub.com/d3-t-and-t-v4) in the section called using images as nodes. Likewise there is plenty in there explaining manipulating and styling nodes

      Delete
  43. Adding the text block should be as simple as an additional append block, with an attr (or perhaps two) setting the offset below or right depending on whether or not the node is d.children or d._children. The tricky part here I think would be getting the spacing right. I suspect that this would be a function of the tree module in d3 and as such might be 'problematic'. Not that it couldn't be done, but it would be at the expense of using the 'stock' d3 module I suspect. The best way forward is probably to hack about with some experimentation and see if something starts working :-).

    ReplyDelete
  44. Can i use svg images instead of those circles .?

    ReplyDelete
    Replies
    1. Definitely, I don't have an example at hand, but check out the different tree examples here http://bl.ocks.org/d3noob/11137963, http://bl.ocks.org/d3noob/9662ab6d5ac823c0e444 and the svg shape example here http://bl.ocks.org/d3noob/9167301 which will get you started.

      Delete
  45. Fantastic work here. Could I ask you some question about the coordinate?

    Why in the code:

    tree = d3.layout.tree().size([height, width]);

    We use the height and width instead of width and height as usually? Because the d3 documentation claims, size() is for (width and height) and it may also represent an arbitrary coordinate system. (https://github.com/d3/d3-3.x-api-reference/blob/master/Tree-Layout.md#tree)

    And a lot of translate properties in svg are set in (d.y, d.x), not as normal (d.x, dy). What surprised me most is that here the level's coordinate is bound to d.y, not d.x, and even if we change the graph to a vertical tree, it is still bound to d.y, not d.x. But intuitive I think the level's coordinate should be relevant to the coordinate on the x-axis.

    Thank you for your time and any explanation. I try to figure it out my self but still don't have a clue. What I will do next is to try to understand the tidy algorithm, which is responsible for setting the d.x and d.y in the nodes from the layout() function.

    ReplyDelete
    Replies
    1. Ahh... A very good question. When we look at the code for drawing a vertical tree diagram it will look logical and we will be able to describe it beautifully. That’s because the default standard when D3 is drawing a tree diagram is to have it going from top to bottom. When we look at a horizontal tree diagram, the diagram and the code has to be rotated by 90 degrees. This means that when we go to move or draw something in the x direction we will need to move in the y direction in the code and vice versa. I tried to describe the reasoning for this 'weirdness' in the updated book here; https://leanpub.com/d3-t-and-t-v4

      Delete
  46. My requirement is the D3 collapsible tree align parent nodes to the left/top in a tree.is it possible in the same graph?

    Example - https://tex.stackexchange.com/questions/115403/how-to-align-parent-nodes-to-the-left-top-in-a-tree

    ReplyDelete
    Replies
    1. This would be the closes thing that I have seen that might fit your requirements https://bl.ocks.org/mbostock/1093025 or https://bl.ocks.org/msd05/46ade3410db036fdcdd807a4fe9e2d9b/69bd1f4dda706f94b520eb9d423fc72a8a57002d although this one is pretty good http://blockbuilder.org/nitaku/0667e50795041cdcd729fa51436085b4. Check out the search function on block builder for some alternatives. http://blockbuilder.org/search#text%3Dindented%20tree. Good luck.

      Delete
  47. is there way to make the lines between the nodes straight lines?

    ReplyDelete
    Replies
    1. Yes there is, (as with most things D3) but it involves editing the d3 code directly (the d3.js code that is). It could be done with slightly less complexity in the v4 version (since it is modular), but will still involve editing the code. It would be worth it for a major project, just don't count on upgrading the code quickly.

      Delete
  48. Very helpful article!

    Can you provide any insights about achieving the feature showcased in the following link using d3.

    https://github.com/almende/vis/issues/3293

    ReplyDelete
    Replies
    1. Sorry Arun. I haven't come across this type of visualisation before. I see that the comments thread is pretty active, so at the very least there is a good group of people interested in a solution. Good luck.

      Delete
    2. Thank you so much for looking into it.

      Delete
  49. Very helpful article and thanks. How to set default display to Depth 0 and Depth 1? User will need to click on the node (depth 1) to expand.

    ReplyDelete
    Replies
    1. Interesting. The same question was asked in 2014. Wow. This post has been around a long time. I didn't know the answer way back then. And I don't remember seeing anything since. However, a bit of a google led me to the following question on Stack Overflow https://stackoverflow.com/questions/46864324/collapse-d3js-tree-to-a-specified-depth That would be the best starting point.

      Delete
  50. Hello D3noob! Thanks for the great work.
    I am new to D3 library. For example in this tutorial code, How can reduce the vertical space between two child nodes? I hitting the situation where there are large number of children.

    ReplyDelete
    Replies
    1. believe it or not the nodes will compress themselves closer or further apart depending on the amount of vertical space they have available. If you make `height` a different value it will vary what is presented. If you wanted to have the value fixed, you would be in for some more difficult coding (i.e. I haven't done it before).

      Delete
  51. how do i collapse all nodes on loading the page.

    ReplyDelete
    Replies
    1. Google led me to this page https://stackoverflow.com/questions/19423396/d3-js-how-to-make-all-the-nodes-collapsed-in-collapsible-indented-tree and I see a link there that seems to show a variation of what you're after? http://bl.ocks.org/larskotthoff/7022289

      Delete
  52. This comment has been removed by the author.

    ReplyDelete
    Replies
    1. This probably isn't the right forum to find assistance with Yellow fin. I would recommend looking to Stack Overflow. That might help out more.

      Delete
  53. Great article - very useful. Is it possible to update data associated with each tree node (e.g. a score). Not in terms of display but the actual underlying data associated with the node?

    ReplyDelete
    Replies
    1. I think that would be possible using the update pattern https://bl.ocks.org/mbostock/3808218

      Delete
  54. How to make 2 input node? or multiple input node?
    Can we group 2 nodes in one shape like a 2 input AND Gate ?

    ReplyDelete
    Replies
    1. Hi there. This is a relativly common question. Unfortunately however, this particular style of diagram doesn't support it. However, I would reccomend that you look into using a Sankey diagram or similar. Best of luck.

      Delete