How to visualize JSON data as tree diagrams using D3.js? - javascript

I want to visualize huge nested JSON objects as tree diagrams using D3.js.
All example are using JSON files which contain their hierarchy as explicit information, for example here the children attribute:
{
"name":"Alex",
"children":[
{
"name":"Josh",
"children":[
{
"name":"Joelle"
}
]
},
{
"name":"David",
"children":[
{
"name":"Lina"
},
{
"name":"Martha"
}
]
},
{
"name":"Lara",
"children":[
]
}
]
}
In the JSON data I want to visualize, there are many different attributes containing arrays of child-objects.
{
"fruit":"Apple",
"vitamins":[
{
"name":"Vitamin C",
"consistsOf":[
{
"id":"H",
"name":"Hydrogen",
"colors":[
{
"name":"colorless"
}
]
},
{
"id":"O",
"name":"Oxygen",
"colors":[
{
"name":"colorless"
},
{
"name":"blue"
}
]
}
]
},
{
"name":"Vitamin D",
"consistsOf":[
{
"id":"H",
"name":"Hydrogen",
"colors":[
{
"name":"colorless"
}
]
},
{
"id":"O",
"name":"Oxygen",
"colors":[
{
"name":"colorless"
},
{
"name":"blue"
}
]
},
{
"id":"C",
"name":"Carbon",
"colors":[
{
"name":"black"
},
{
"name":"grey"
}
]
}
]
}
]
}
Doesn't the JSON language implicitly show relationships between the objects, so that a tree can be drawn by D3.js? How do I achieve that?

All what you have to do is to create your page HTML, like:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title> 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="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js"></script>
<script>
// ************** 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 + ")");
// load the external data
d3.json("YourJsonFile.json", function(error, treeData) {
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>
Wish that it could help!

Related

d3 v5 tree layout data update recreates tree instead of updating nodes

I am working on an application where I want to show my hierarchical data in tree structure. This data keeps updating and I want to update tree as per newly received data. I have implemented it successfully using D3 V3 by merging the new data with new data.
I am now wanting to upgrade to d3 V5. So far I have been able to create the tree but I am unable to handle data update. Whenever I get new data, entire tree is recreated, I see that the enter() and exit() events are triggered each time for each node.
Note: Please ignore if the expand-collapse is not working, I am still working on this.
I have extracted the code created below working snippet to depict my problem. I do not want to recreate tree every time I receive new data, I only want to update the existing nodes if there is any change in the data, otherwise the nodes remain as is.
Where I am going wrong? Can you one suggest please?
// Code goes here
// find elements
var margin = {
top: 20,
right: 90,
bottom: 30,
left: 90
},
width = 660 - margin.left - margin.right,
height = 300 - margin.top - margin.bottom;
i = 0,
duration = 750,
redius = 10;
var node = {};
var root = {};
var links = {};
var nodes = [];
var links = [];
// handle click and add class
$("#btnLoadData").click(function() {
refreshData();
});
function refreshData() {
var data = {
"name": "Central",
"connectionState": Math.random() > 0.5 ? "connected" : "disconnected",
"parent": null,
"envStatus": {
"cpu": 45.575,
"mem": 55.8,
"disk": 85.5
},
"subState": "",
"children": [{
"name": "UK-STORE1",
"connectionState": Math.random() > 0.5 ? "connected" : "disconnected",
"subState": "",
"envStatus": {
"cpu": 45.650000000000006,
"mem": 55.8,
"disk": 85.5
},
"children": [{
"name": "UK-TILL1",
"connectionState": Math.random() > 0.5 ? "connected" : "disconnected",
"subState": null,
"envStatus": {
"cpu": 46.025000000000006,
"mem": 55.8,
"disk": 85.5,
},
"children": null,
"stateChangedAt": "2020-02-08 20:59:35.226769"
},
{
"name": "UK-TILL2",
"connectionState": Math.random() > 0.5 ? "connected" : "disconnected",
"subState": null,
"envStatus": {
"cpu": 45.775000000000006,
"mem": 56.1,
"disk": 85.5
},
"children": null,
"stateChangedAt": "2020-02-08 20:59:35.226769"
}
],
"stateChangedAt": "2020-02-08 20:59:35.226769"
}]
}
buildRoot(data);
}
function buildRoot(newSource) {
root = d3.hierarchy(newSource, function(d) {
return d.children;
});
root.x = 0;
root.y = width / 2;
root.x0 = 0;
root.y0 = width / 2;
updateTree(root)
}
var svg = d3.select("#tree").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom);
var mainG = svg.append("g")
.attr("transform",
"translate(" + margin.left + "," + margin.top + ")");
var treeLayout = d3.tree()
.size([width, height]);
//.nodeSize([100, 50]);
// Collapse the node and all it's children
function collapse(d) {
if (d.children) {
d._children = d.children
d._children.forEach(collapse)
d.children = null
}
}
function updateTree(source) {
var treeData = treeLayout(root);
var newNodes = treeData.descendants();
_.merge(nodes, newNodes)
var newlinks = treeData.descendants().slice(1);
_.merge(links, newlinks)
nodes.forEach(function(d) {
dy = d.depth * 180
});
//links
var linkPaths = mainG.selectAll(".link")
.data(links, function(d) {
return d.id;
});
var linkEnter = linkPaths.enter().append("path")
.attr("class", "link")
.attr('d', function(d) {
var o = {
x: source.x0,
y: source.y0
}
return diagonal(o, o);
});
var linkUpdate = linkEnter.merge(linkPaths);
linkUpdate.transition()
.duration(duration)
.attr('d', function(d) {
return diagonal(d, d.parent)
});
var linkExit = linkPaths.exit()
.transition()
.duration(duration)
.attr('d', function(d) {
var o = {
y: source.y0,
x: source.x0
}
return diagonal(o, o)
})
.remove();
//Nodes
var node = mainG.selectAll('g.node')
.data(nodes, function(d) {
return d.id || (d.id = ++i);
});
//update nodes
var nodeEnter = node.enter().append('g')
.attr('class', 'node')
.attr("transform", function(d) {
return "translate(" + source.x0 + "," + source.y0 + ")";
})
.on('click', click);
//Append circle
nodeEnter.append("circle")
.attr("class", "circle")
.attr("r", redius);
//Append circle
nodeEnter.append("text")
.attr("class", "nodeName")
.text(function(d) {
return d.data.name;
});
// UPDATE
var nodeUpdate = nodeEnter.merge(node);
// Transition to the proper position for the node
nodeUpdate.transition()
.duration(duration)
.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
});
nodeUpdate.select("circle")
.attr("class", function(d) {
console.log(d.data.connectionState);
return d.data.connectionState == "connected" ? "circle-connected" : "circle-disconnected"
})
// Remove any exiting nodes
var nodeExit = node.exit().transition()
.duration(duration)
.attr("transform", function(d) {
return "translate(" + source.y + "," + source.x + ")";
})
.remove();
// On exit reduce the node circles size to 0
nodeExit.select('circle')
.attr('r', 1e-6);
// On exit reduce the opacity of text labels
nodeExit.select('text')
.style('fill-opacity', 1e-6);
function diagonal(s, d) {
path = `M ${s.x} ${s.y}
C ${s.x} ${(d.y+ s.y)/2},
${s.x} ${(d.y+ s.y)/2 },
${d.x} ${d.y}`
return path;
}
function click(d) {
if (d.children) {
d._children = d.children;
d.children = null;
} else {
d.children = d._children;
d._children = null;
}
updateTree(d);
}
} // update tree ends
refreshData()
/* Styles go here */
body {
background: #20262E;
padding: 20px;
font-family: Helvetica;
color: #fff;
}
button {
background: #0084ff;
border: none;
border-radius: 5px;
padding: 8px 14px;
font-size: 15px;
color: #fff;
}
svg {
background-color: forestgreen;
}
.node {
cursor: pointer;
}
.circle {
fill: #fff;
transition: fill 2s;
r: 10;
}
.circle-connected {
fill: #14A76C;
transition: fill 2s;
}
.circle-disconnected {
fill: #a72314;
transition: fill 2s;
}
.link {
fill: none;
stroke: #ccc;
}
<!DOCTYPE html>
<html>
<head>
<script data-require="lodash.js#4.17.4" data-semver="4.17.4" src="https://cdn.jsdelivr.net/npm/lodash#4.17.4/lodash.min.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="https://cdn.jsdelivr.net/npm/jquery#3.2.1/dist/jquery.min.js"></script>
<script src="https://d3js.org/d3.v5.min.js"></script>
</head>
<body>
<div id="banner-message">
<p>D3 V5 Tree</p>
<button id="btnLoadData">Update data</button>
</div>
<div id="tree"></div>
<script src="script.js"></script>
</body>
</html>
.

Put a timeline to a D3.js Tree graph

I am completely new to the D3.js so this might be an easy question. I have a simple D3 tree graph and in my treeData json I have a “date” component for each node. How can I glue the time line below the tree graph so that each node corresponds to the date in the timeline?
Below is the complete code. I have tried to find a similar example that works, couldn't find anything. Found an example here but it does not work:
d3.js - Having a tree layout, how to change the X-axis to use a time scale in D3?
var treeData = [{
"name": "Top Level",
"date": "12-Jan-15",
"parent": "null",
"children": [{
"name": "Level 2: A",
"date": "13-Mar-16",
"parent": "Top Level",
"children": [{
"name": "Son of A",
"date": "1-Aug-16",
"parent": "Level 2: A"
}, {
"name": "Daughter of A",
"date": "5-Sep-16",
"parent": "Level 2: A"
}]
}, {
"name": "Level 2: B",
"date": "12-Jan-17",
"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("#tree").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);
}
.node circle {
fill: #fff;
stroke: steelblue;
stroke-width: 3px;
}
.node text {
font: 12px sans-serif;
}
.link {
fill: none;
stroke: #ccc;
stroke-width: 2px;
}
<script src="https://d3js.org/d3.v3.min.js"></script>
<div id="tree">
</div>
<div id="time">
</div>
Here is the answer that can be used when creating the timeline to a D3 tree graph. The code has comments, it should be clear what it does. Save it as .html and it should work in browser.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Simple Tree Example</title>
<style>
.node circle {
fill: steelblue;
stroke: grey;
stroke-width: 3px;
}
.node text {
font: 12px sans-serif;
}
/* link is for the lines */
.link {
fill: none;
stroke: #ccc;
stroke-width: 2px;
z-index: -1;
}
.axis path,
.axis line {
fill: none;
stroke: slategray;
shape-rendering: crispEdges;
}
.x.axis line,
.x.axis path {
fill: none;
stroke: #000;
}
</style>
</head>
<body>
</div>
<!-- load the d3.js library -->
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
var treeData = [{
"name": "Root",
"date": "01-01-2017",
"children": [{
"name": "A",
"date": "01-02-2017",
"children": [
{
"name": "B",
"date": "01-05-2017",
"children": [{
"name": "C",
"date": "01-06-2017",
"children": [{
"name": "D",
"date": "01-07-2017",
}]
}]
}
]
}
]
}];
// ************** Generate the tree diagram *****************
var margin = {
top: 20,
right: 60,
bottom: 20,
left: 120
},
width = 1000 - margin.right - margin.left,
height = 200 - 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),
g = svg.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
root = treeData[0];
var nodes = tree.nodes(root).reverse();
var maxdate = d3.max(nodes, function (d) {
return new Date(d.date.replace(/(\d{2})-(\d{2})-(\d{4})/, "$1/$2/$3"));
});
var mindate = d3.min(nodes, function (d) {
return new Date(d.date.replace(/(\d{2})-(\d{2})-(\d{4})/, "$1/$2/$3"));
});
mindate.setMonth(mindate.getMonth() + 1);
maxdate.setMonth(maxdate.getMonth() + 1);
maxdate.setDate(maxdate.getDate() + 5);
var x = d3.time.scale()
.domain([mindate, maxdate])
.range([0, width]);
var xAxis = d3.svg.axis()
.orient("bottom")
.scale(x)
.ticks(10);
g.append('g')
.attr('transform', 'translate(0,' + height + ')') .attr("class", "axis")
.call(customXAxis);
var linksg = g.append("g");
function customXAxis(g) {
g.call(xAxis);
//g.select('.domain').remove();
};
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 * 80;
});
// Declare the nodes…
var node = g.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) {
var ddate = d.date.split("-");
var t = new Date(ddate[2], ddate[0], ddate[1]);
return "translate(" + x(t) + "," + d.x + ")";
});
nodeEnter.append("circle")
.attr("r", 15)
.transition()
.delay(350)
.style("fill", "steelblue");
nodeEnter.append("text")
.attr("x", function (d) {
return d.children || d._children ? -20 : 20;
})
.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 = linksg.selectAll('.link')
.data(nodes)
.enter().append('path')
.attr('class', 'link')
.attr('d', function (d) {
if (d.parent != undefined) {
var res = d.date.split("-");
var nodeDate = new Date(+res[2], +res[0], +res[1]);
var res = d.parent.date.split("-");
var parentDate = new Date(+res[2], +res[0], +res[1]);
return 'M' + x(nodeDate) + ',' + d.x +
'C' + (x(nodeDate) + x(parentDate)) / 2 + ',' + d.x +
' ' + (x(nodeDate) + x(parentDate)) / 2 + ',' + d.parent.x +
' ' + x(parentDate) + ',' + d.parent.x;
}
});
} // end update () function
</script>
</body>
</html>.

How to append element to div with id using d3js javascript

I am using http://d3js.org/d3.v3.min.js.
I have code that append svg to my body html element.
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 + ")");
This code append <svg> to <body>
I am new in javascript and i don't know how to write code that append this svg to my div: <div id="svg">
I try: var svg = d3.select(document.getElementById("svg")).append("svg")... but it not working.
Thank you for any help!
Edit:
There is a full code for testing
<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>
<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("div#tree").append("svg")
var svg = d3.select("#tree").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>
Simply use d3.select("#svg").

D3 chart works works perfectly on jsfiddle but not on localhost

So I'm trying to get this jsfiddle working:
http://jsfiddle.net/davetaz/Xc8nT/
It works perfectly on there.
However, when I go to my machine and use the code(just to see if it works before making my own changes) it doesn't work, just get the grey div. There are no errors in the console.
The order of my code is as follows:
<script src="https://code.jquery.com/jquery-2.2.2.min.js"></script>
<script src="http://d3js.org/d3.v2.min.js" charset="utf-8"></script>
then the div
<div id="chart"></div>
then the script.
I've tried wrapping in $( document ).ready, tried it in an external file with that as well. Nothing seems to work. I'm at wits end trying to figure this out, any help would be appreciated.
Is your HTML page properly formatted? The following works fine for me:
<html>
<head>
<script src="https://code.jquery.com/jquery-2.2.2.min.js"></script>
<script src="http://d3js.org/d3.v2.min.js" charset="utf-8"></script>
<style>
#chart {
width: 500px;
height: 400px;
background: #bbb;
font-size: 10px;
}
text {
pointer-events: none;
}
.grandparent text {
font-weight: bold;
font-size: 16px;
}
rect {
fill: none;
stroke: #fff;
}
rect.parent,
.grandparent rect {
stroke-width: 2px;
}
.grandparent rect {
fill: #fff;
}
.children rect.parent,
.grandparent rect {
cursor: pointer;
}
rect.parent {
pointer-events: all;
}
.children:hover rect.child,
.grandparent:hover rect {
fill: #aaa;
}
</style>
</head>
<body>
<div id="chart"></div>
<script>
var data = {
"name": "projects",
"children": [
{
"name": "Department of Agriculture",
"children": [
{
"name": "Annual Agency Operations.",
"value": 15.297
},
{
"name": "Program Areas will migrate their data over to the agencys virtual servers and shut down their current server.",
"value": 0.179
},
{
"name": "Programs Areas will replace 1/3 of their computers which have an expired warranty.",
"value": 1.46
},
{
"name": "Production Support including Analysis, Software Fixes, System Software Upgrades, Help Desk.",
"value": 1.8205
},
{
"name": "Production Support including Analysis, Software Fixes, System Software Upgrades, Help Desk. (1)",
"value": 1.713
}
]
},
{
"name": "Department of Commerce",
"children": [
{
"name": "Parallel test components of new BP system.",
"value": 1.24
},
{
"name": "Initiate real-time parallel testing of integrated system for producing monthly GDP and personal income estimates.",
"value": 0.413
},
{
"name": "Produce timely, relevant, and accurate economic accounts data in an objective and cost-effective manner.",
"value": 9.696
},
{
"name": "Maintain the integrity of BEA Statistics by completing independent testing of security controls.",
"value": 0.59
},
{
"name": "Produce timely, relevant, and accurate economic accounts data in an objective and cost-effective manner. (1)",
"value": 9.76
}
]
}
]
};
// This example has been adapted from Mike Bostocks Zooming treemap example at http://bost.ocks.org/mike/treemap/. Many thanks to Mike for his excellent work in this area.
var margin = {top: 30, right: 0, bottom: 0, left: 0},
width = 500,
height = 400 - margin.top - margin.bottom,
formatNumber = d3.format(",d"),
transitioning;
var x = d3.scale.linear()
.domain([0, width])
.range([0, width]);
var y = d3.scale.linear()
.domain([0, height])
.range([0, height]);
var treemap = d3.layout.treemap()
.children(function(d, depth) { return depth ? null : d.children; })
.sort(function(a, b) { return a.value - b.value; })
.ratio(height / width * 0.5 * (1 + Math.sqrt(5)))
.round(false);
var svg = d3.select("#chart").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.bottom + margin.top)
.style("margin-left", -margin.left + "px")
.style("margin.right", -margin.right + "px")
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.style("shape-rendering", "crispEdges");
var grandparent = svg.append("g")
.attr("class", "grandparent");
grandparent.append("rect")
.attr("y", -margin.top)
.attr("width", width)
.attr("height", margin.top);
grandparent.append("text")
.attr("x", 6)
.attr("y", 6 - margin.top)
.attr("dy", ".75em");
initialize(data);
accumulate(data);
layout(data);
display(data);
function initialize(root) {
root.x = root.y = 0;
root.dx = width;
root.dy = height;
root.depth = 0;
}
// Aggregate the values for internal nodes. This is normally done by the
// treemap layout, but not here because of our custom implementation.
function accumulate(d) {
return d.children
? d.value = d.children.reduce(function(p, v) { return p + accumulate(v); }, 0)
: d.value;
}
// Compute the treemap layout recursively such that each group of siblings
// uses the same size (1×1) rather than the dimensions of the parent cell.
// This optimizes the layout for the current zoom state. Note that a wrapper
// object is created for the parent node for each group of siblings so that
// the parent’s dimensions are not discarded as we recurse. Since each group
// of sibling was laid out in 1×1, we must rescale to fit using absolute
// coordinates. This lets us use a viewport to zoom.
function layout(d) {
if (d.children) {
treemap.nodes({children: d.children});
d.children.forEach(function(c) {
c.x = d.x + c.x * d.dx;
c.y = d.y + c.y * d.dy;
c.dx *= d.dx;
c.dy *= d.dy;
c.parent = d;
layout(c);
});
}
}
function display(d) {
grandparent
.datum(d.parent)
.on("click", transition)
.select("text")
.text(name(d));
var g1 = svg.insert("g", ".grandparent")
.datum(d)
.attr("class", "depth");
var g = g1.selectAll("g")
.data(d.children)
.enter().append("g");
g.filter(function(d) { return d.children; })
.classed("children", true)
.on("click", transition);
g.selectAll(".child")
.data(function(d) { return d.children || [d]; })
.enter().append("rect")
.attr("class", "child")
.call(rect);
g.append("rect")
.attr("class", "parent")
.call(rect)
.append("title")
.text(function(d) { return formatNumber(d.value); });
g.append("text")
.attr("dy", ".75em")
.text(function(d) { return d.name; })
.call(text);
var color = d3.scale.category20c();
var colors = {};
g.filter(function(d) {
var parent_depth = (d.parent).depth;
var current_node = d;
if (parent_depth != 0) {
current_node = d.parent;
}
if (!current_node.color) {
current_node.color = color(current_node.name);
}
$(this).find(".child").css("fill",current_node.color);
});
function transition(d) {
if (transitioning || !d) return;
transitioning = true;
var g2 = display(d),
t1 = g1.transition().duration(750),
t2 = g2.transition().duration(750);
// Update the domain only after entering new elements.
x.domain([d.x, d.x + d.dx]);
y.domain([d.y, d.y + d.dy]);
// Enable anti-aliasing during the transition.
svg.style("shape-rendering", null);
// Draw child nodes on top of parent nodes.
svg.selectAll(".depth").sort(function(a, b) { return a.depth - b.depth; });
// Fade-in entering text.
g2.selectAll("text").style("fill-opacity", 0);
// Transition to the new view.
t1.selectAll("text").call(text).style("fill-opacity", 0);
t2.selectAll("text").call(text).style("fill-opacity", 1);
t1.selectAll("rect").call(rect);
t2.selectAll("rect").call(rect);
// Remove the old node when the transition is finished.
t1.remove().each("end", function() {
svg.style("shape-rendering", "crispEdges");
transitioning = false;
});
}
return g;
}
function text(text) {
text.attr("x", function(d) { return x(d.x) + 6; })
.attr("y", function(d) { return y(d.y) + 6; });
}
function rect(rect) {
rect.attr("x", function(d) { return x(d.x); })
.attr("y", function(d) { return y(d.y); })
.attr("width", function(d) { return x(d.x + d.dx) - x(d.x); })
.attr("height", function(d) { return y(d.y + d.dy) - y(d.y); })
.attr("id", function(d) { return d.name; });
}
function name(d) {
return d.parent
? name(d.parent) + "." + d.name
: d.name;
}
</script>
</body>
</html>

D3 Tree orientation

I'm working with the D3 Tree layout, similar to this jsfiddle, the only problem is that I need the orientation of each node to start at the top left of the layout and not on the top center.
var nodeUpdate = node.transition()
.duration(duration)
.attr("transform", function(d) {
return "translate(" + d.y + "," + d.x + ")";
});
Something similar to this:
Any ideas on how to do that?
Thank you in advance.
Building off of this example.
// Compute the new tree layout.
var nodes = tree.nodes(root).reverse(),
links = tree.links(nodes);
// figure out the placement of the "top-most" node at each depth
var nodeMap = {};
nodes.forEach(function(d) {
if (!nodeMap[d.depth] || d.x < nodeMap[d.depth]){
nodeMap[d.depth] = d.x;
}
});
// shift all nodes up
nodes.forEach(function(d) {
d.y = d.depth * 100;
d.x -= nodeMap[d.depth];
});
This produces:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Tree Example</title>
<style>
.node {
cursor: pointer;
}
.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",
"children": [
{
"name": "Son of B",
"parent": "Level 2: B"
},
{
"name": "Daughter of B",
"parent": "Level 2: B"
}
]
}
]
}
];
// ************** 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,
duration = 750,
root;
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];
root.x0 = height / 2;
root.y0 = 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.
var nodeMap = {};
nodes.forEach(function(d) {
if (!nodeMap[d.depth] || d.x < nodeMap[d.depth]){
nodeMap[d.depth] = d.x;
}
});
nodes.forEach(function(d) {
d.y = d.depth * 100;
d.x -= nodeMap[d.depth];
});
// Update the nodes…
var node = svg.selectAll("g.node")
.data(nodes, function(d) { return d.id || (d.id = ++i); });
// Enter any new nodes at the parent's previous position.
var nodeEnter = node.enter().append("g")
.attr("class", "node")
.attr("transform", function(d) { return "translate(" + source.y0 + "," + source.x0 + ")"; })
.on("click", click);
nodeEnter.append("circle")
.attr("r", 1e-6)
.style("fill", function(d) { return d._children ? "lightsteelblue" : "#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", 1e-6);
// Transition nodes to their new position.
var nodeUpdate = node.transition()
.duration(duration)
.attr("transform", function(d) { return "translate(" + d.y + "," + d.x + ")"; });
nodeUpdate.select("circle")
.attr("r", 10)
.style("fill", function(d) { return d._children ? "lightsteelblue" : "#fff"; });
nodeUpdate.select("text")
.style("fill-opacity", 1);
// Transition exiting nodes to the parent's new position.
var nodeExit = node.exit().transition()
.duration(duration)
.attr("transform", function(d) { return "translate(" + source.y + "," + source.x + ")"; })
.remove();
nodeExit.select("circle")
.attr("r", 1e-6);
nodeExit.select("text")
.style("fill-opacity", 1e-6);
// Update the links…
var link = svg.selectAll("path.link")
.data(links, function(d) { return d.target.id; });
// Enter any new links at the parent's previous position.
link.enter().insert("path", "g")
.attr("class", "link")
.attr("d", function(d) {
var o = {x: source.x0, y: source.y0};
return diagonal({source: o, target: o});
});
// Transition links to their new position.
link.transition()
.duration(duration)
.attr("d", diagonal);
// Transition exiting nodes to the parent's new position.
link.exit().transition()
.duration(duration)
.attr("d", function(d) {
var o = {x: source.x, y: source.y};
return diagonal({source: o, target: o});
})
.remove();
// Stash the old positions for transition.
nodes.forEach(function(d) {
d.x0 = d.x;
d.y0 = d.y;
});
}
// 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);
}
</script>
</body>
</html>
not only the nodes (x,y) should be interchanged but also the links connecting the nodes i.e.
var diagonal = d3.svg.diagonal()
.projection(function(d) {return [d.y,d.x];});
have a look at this example i made
<!doctype html>
<html>
<head>
<script src="http://d3js.org/d3.v3.min.js"></script>
</head>
<body>
<script>
var data = {
"name" : "A",
"children" : [
{
"name" : "B",
"children" : [
{
"name" : "F"
},
{
"name" : "G"
}
]
},
{
"name" : "C",
"children" : [
{
"name" : "H"
},
{
"name" : "I"
},
{
"name" : "J"
}
]
},
{
"name" : "D",
"children" : [
{
"name" : "K"
},
{
"name" : "L"
}
]
},
{
"name" : "E"
}
]
};
var canvas = d3.select("body")
.append("svg")
.attr("width",600)
.attr("height",500)
.append("g")
.attr("transform","translate(50,50)");
var cluster = d3.layout.cluster()
.size([400,400]);
var nodes = cluster.nodes(data);
var links = cluster.links(nodes);
var node = canvas.selectAll(".node")
.data(nodes)
.enter()
.append("g")
.attr("class","node")
.attr("transform",function(d) {return "translate(" + d.y + "," + d.x + ")"});
node.append("circle")
.attr("r",5)
.attr("fill","silver");
node.append("text")
.text(function(d) {return d.name;});
var diagonal = d3.svg.diagonal()
.projection(function(d) {return [d.y,d.x];});
canvas.selectAll(".link")
.data(links)
.enter()
.append("path")
.attr("class","link")
.attr("fill","none")
.attr("stroke","#ADADAD")
.attr("d",diagonal);
</script>

Categories