Collapsible/hierarchical AND force-directed graph in d3.js - d3.js

There are numerous examples of force-directed graphs (i.e. nodes and links) and collapsible trees (i.e. parent-child nodes) but I cant find an example of the combination of these - other than some 1-level clustered networks like this - http://static.cybercommons.org/js/d3/examples/force/force-cluster.html.
That is I need a full hierarchy of nodes (with any number of levels) with links between various nodes across the hierarchy.
Has anyone got an example of this?
And if so I'd ultimately like to see the hierarchies be collapsible and any of the links from the children are 'elevated' up to the parent when it is collapsed.
Cheers,
Tim
This is similar to what I'd expect the jsonData to look like ...
{
"nodes": [
{
"name": "Parent 1",
"children": [
{
"name": "Child 1",
},
},
{
"name": "Parent 2",
"children": [
{
"name": "Child 2",
},
.
.
.
"links": [
{
source: "Child 1",
target: "Child 2"
},
.
.

i try to merge both examples here my fiddle
// Toggle children on click.
function click(d) {
if (d.children) {
d._children = d.children;
d.children = null;
} else {
d.children = d._children;
d._children = null;
}
update();
}

Here is a nice example that exhibits both properties http://bl.ocks.org/mbostock/1093130

I'm also interested in this.
I have found two examples, that I'd like to combine.
http://bl.ocks.org/mbostock/1062288
http://graus.nu/d3/

Related

Using d3.js v4 - using parentId to resolve ambiguity

I am trying to understand the right usage to achieve my collapsible tree d3 but unable to establish the proper parent/child references since I cannot use "parent". Attempting to use parentID.
This is my dataset I am testing with:
var result = [
{ "id": 1, "name": "Top Level", "parent": null, "parentId": "" },
{ "id": 2, "name": "PROD", "parent": "Top Level", "parentId": 1 },
{ "id": 3, "name": "QAT", "parent": "Top Level", "parentId": 1 },
{ "id": 4, "name": "App1", "parent": "PROD", "parentId": 2 },
{ "id": 5, "name": "App1", "parent": "QAT", "parentId": 3 },
{ "id": 6, "name": "ServerPROD001", "parent": "App1", "parentId": 4 },
{ "id": 7, "name": "ServerQAT001", "parent": "App1", "parentId": 5 }
];
and based on the collapsible tree:
// convert the flat data into a hierarchy
var treeData = d3.stratify()
.id(function (d) { return d.name; })
.parentId(function (d) { return d.parent })
(result);
This works fine if I do not include items 6 and 7. If I do include these I get an ambiguity error which makes sense because it cannot determine which "App1" to associate to.
I tried changing the code to use the parentId but just get an error of "missing:1" now.
// convert the flat data into a hierarchy
var treeData = d3.stratify()
.id(function (d) { return d.name; })
.parentId(function (d) { return d.parentId })
(result);
Note - I cannot change the "App1" name values to something unique as they will exist in multiple areas with that given name.
Since you have the id that is unique and not the name:
// convert the flat data into a hierarchy
var treeData = d3.stratify()
.id(function (d) { return d.id; }) // return the id instead of the name
.parentId(function (d) { return d.parentId })
(result);
and then set the name you need to be displayed like this:
// assign the name to each node as the initial name
treeData.each(function(d) {
d.name = d.data.name;
});
A working example can be found here, based on this
:)
Good luck!

D3 Tree Graph Modified Data Model

I'm hoping to get opinions on what would be a good data model to drawing something like d3 tree graph. Tree graph won't work for me as is because I have scenarios where a child node could be linked with two parent nodes, sometimes, the parent nodes from different levels of hierarchy. I'm planning to modify d3 tree graph to use a different data model, and here's where I'd really appreciate expert opinions. Following is a simple representation of what I think the data model could be. One is hierarchical model and the other is flat. Has anyone actually meddled with d3 tree data model? Any help/opinions are greatly appreciated! Thanks in advance for the help!
var hierarchicalData = [
{
"id": "n1",
"children": [
{
"id": "n1-a",
"children":[
{
"id": "n1-a-1"
}
]
},
{
"id": "n1-b",
"children":[
{
"id": "n1-b-1"
}
]
}
]
},
{
"id": "n2",
"children": [
{
"id": "n2-a",
"children":[
{
"id": "n2-a-1"
}
]
}
]
}
];
The following is a flat representation of the exact same hierarchical model but contains "level" that represents hierarchy.
{
"n1":{
"level": 0,
"children": ["n1-a", "n1-b"],
},
"n1-a":{
"level": 1,
"children":["n1-a-1"]
},
"n1-a-1":{
"level": 2,
"children":[]
},
"n1-b":{
"level": 1,
"children":["n1-b-1"]
},
"n1-b-1":{
"level": 2,
"children":[]
},
"n2":{
"level": 0,
"children": ["n2-a"]
},
"n2-a":{
"level": 1,
"children": ["n2-a-1"]
},
"n2-a-1":{
"level": 2,
"children":[]
}
}
If a child node can have more than one parent, then it's not a tree graph by definition.
There are several UI approaches you may take if you want to have a tree but it really depends on what you're trying to accomplish.
I worked with d3 tree to present a company Org-chart.
Several companies have employees with a direct manager and secondary manager.
What we did is showing the connection only to the direct manager.
But we presented the link to the other manager on mouse-over on the employee node.
This is more of a UI solution than a data model solution and there are many other possibilities.
Another option is to do what My-heritage did with family tree. They're showing both parents of each node, but only one of them is connected to the rest of the tree presented.

d3.js Tree Layout not expanded on creation

I've been digging d3.js for a few days by now and so far I love it.
I got into it while searching for a tree layout since I need one for my project. I came accross this example
I've accomplished the data-fetching and the stylish aspects to fit my site.
My question, since I wasn't able to get away with it is: How can I tell the tree not to be expanded at the beggining? I want the user to go clicking on the nodes as desired, because I always find myself un-clicking at the very start just to get the "flare" node alone.
Thanks in advance, if you know how to do it you can just add the code or re-work the one in the sample, I'll then apply it to the project I'm working on.
That example toggles children open or closed with this code:
// Toggle children on click.
function click(d) {
if (d.children) {
d._children = d.children;
d.children = null;
} else {
d.children = d._children;
d._children = null;
}
update(d);
}
So you can initialize the tree to be closed at a particular level by changing the original dataset from children to _children on any level you wish to start out as collapsed.
For example (from flare.json):
{
"name": "flare",
"_children": [
{
"name": "analytics",
"_children": [
{
"name": "cluster",
"_children": [
{"name": "AgglomerativeCluster", "size": 3938},
{"name": "CommunityStructure", "size": 3812},
{"name": "HierarchicalCluster", "size": 6714},
{"name": "MergeEdge", "size": 743}
]
}
...
...

Dendrogram from hierarchical clustering in D3

I'm quite new to D3 so I apologize if this is a very basic question. I wish to implement a dendrogram that shows the result of a hierarchical clustering algorithm. This layout differs in a major point from the examples I have been able to find: Except for the leaves of the tree, the nodes does not have any identity, but merely joins subtrees at specific heights relative to their like-ness.
As an example look at:
http://r.789695.n4.nabble.com/file/n2293207/Dendrogram.jpeg
Compared to http://bl.ocks.org/mbostock/4063570 this dendrogram does not have the 'n-partite nature' (defined layers for each level of nodes).
The question is thus how to define a dendrogram with arbitrary join positions of subtrees?
Thanks
Thomas
edit:
It seems it was not as difficult as anticipated and it did not require development of a new layout. In my input data I included an additional parameter with the calculated height of the join. An example json file would be something like this:
{
"height": "1",
"children": [
{
"height": "0.8",
"children": [
{
"name": "leaf 1",
"height": "0"
},
{
"height": "0.35",
"children": [
{
"name": "leaf 2",
"height": "0"
},
{
"name": "leaf 3",
"height": "0"
}
]
]
},
{
"name": "leaf 4",
"height": "0"
}
]
}
and then, when calculating the node object, transform the y value using map:
var nodes = cluster.nodes(root).map(function(d) {d.y = scale(d.height); return d});
where cluster is your cluster layout object and scale is a suitable scale for the dendrogram height.

Hyperlinks in d3.js objects

I am a complete novice at d3.js or java in general. I am using the indented tree example from http://bl.ocks.org/1093025. It took me two hours to get this to work on my local computer, so that should give you an idea of my skill level.
I opened the flare.json file and started messing with it and was able to manipulate it successfully. It looks like this
{
"name": "Test D3",
"children": [
{
"name": "News",
"children": [
{
"name": "CNN",
"size": 1000
},
{
"name": "BBC",
"size": 3812
}
]
},
{
"name": "Blogs",
"children": [
{
"name": "Engaget",
"size": 3938
}
]
},
{
"name": "Search",
"children": [
{
"name": "Google",
"size": 3938
},
{
"name": "Bing",
"size": 3938
}
]
}
]
}
What I want to do now, is to try to add hyperlinks. For example, I want to be able to click on "CNN" and go to CNN.com. Is there a modification I can make to flare.json that will do that?
It is quite easy, just add some more "key" : "value" pairs. Example:
"children": [
{
"name": "Google",
"size": 3938,
"url": "https://www.google.com"
},
{
"name": "Bing",
"size": 3938,
"url": "http://www.bing.com"
}
]
Of course, in your d3 code you then need to append <svg:a> tags and set their xlink:href attribute.
Here is some html and d3-code that might be of help to you. First you need to import the xlink namespace in your html file:
<html xmlns:xlink="http://www.w3.org/1999/xlink">
...
</html>
Then in the d3 drawing code where you append nodes for each data element you wrap the element you want to be clickable links with an svg:a tag:
nodeEnter.append("svg:a")
.attr("xlink:href", function(d){return d.url;}) // <-- reading the new "url" property
.append("svg:rect")
.attr("y", -barHeight / 2)
.attr("height", barHeight)
.attr("width", barWidth)
.style("fill", color)
.on("click", click); // <- remove this if you like
You might want to remove the click handler (which is present in the original example) by deleting the .on("click", click) as it might interfere with the default behavior of SVG links.
Clicking on your rects should now lead you to the appropriate url.
SVG links might not be fully implemented in all browsers.
Alternatively you could modify the click handler to read the URL from d.url and use that one to manually redirect the browser to that URL via JavaScript: window.location = d.url;. Then you do not need the svg:a tag and the xlink code. Though adding a real link (not a scripted one) has the benefit that the user/browser can decide what to do (e.g., open in new tab/page). It also helps if some of your users have JavaScript disabled.

Resources