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>.
Related
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>
.
var treeData = [
{
"name": "glucose_tol",
"directions": ">",
"thresholds": "126",
"exits": 0.0,
"children": [
{
"name": "age",
"directions": ">",
"thresholds": "29",
"exits": 1.0,
"children": [
{
"name": true
},
{
"name": "mass_index",
"directions": ">",
"thresholds": "29.7",
"exits": 0.5,
"children": [
{
"name": true
},
{
"name": false
}
]
}
]
},
{
"name": false
}
]
},
];
// ************** 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.x, d.y]; });
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 * 120; });
// 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.x + "," + d.y + ")"; });
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);
// Add threshold and directions
link.enter().insert("text")
.attr("font-family", "Arial, Helvetica, sans-serif")
.attr("fill", "Black")
.style("font", "normal 12px Arial")
.attr("transform", function(d) {
return "translate(" +
((d.source.x + d.target.x)/2) + "," +
((d.source.y + d.target.y)/2) + ")";
})
.attr("dy", ".35em")
.attr("text-anchor", "middle")
.text(function(d) {
//check whether thresholds is not undefined && that target.thresholds is not undefined as it will print on both sides
if (d.source.thresholds !== undefined)
if(d.target.thresholds !== undefined)
return d.source.thresholds + ' ' + d.source.directions;
})
}
.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://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js"></script>
I am having a problem adding text in middle of the links to a tree, because the last level of the tree doesn't give me any text. This is the code I have to add the text in the links:
// Add threshold and directions
link.enter().insert("text")
.attr("font-family", "Arial, Helvetica, sans-serif")
.attr("fill", "Black")
.style("font", "normal 12px Arial")
.attr("transform", function(d) {
return "translate(" +
((d.source.x + d.target.x)/2) + "," +
((d.source.y + d.target.y)/2) + ")";
})
.attr("dy", ".35em")
.attr("text-anchor", "middle")
.text(function(d) {
//check whether thresholds is not undefined && that target.thresholds is not undefined as it will print on both sides
if (d.source.thresholds !== undefined)
if(d.target.thresholds !== undefined)
return d.source.thresholds + ' ' + d.source.directions;
})
if I comment these two lines : if (d.source.thresholds !== undefined) and if(d.target.thresholds !== undefined) then I get the text on all the links but on both sides, which I don't want. How do I get the text only on one side but also on the last level.
First of all you need to have a way to find out whether the current node is on the last level, or if there are more levels under it. For this we will implement a maxDepth variable that will store a maximum value of depth attribute for all nodes. This is done in the update(root) function:
// Normalize for fixed-depth.
var maxDepth = 0;
nodes.forEach(function(d) { d.y = d.depth * 120; maxDepth = (d.depth>maxDepth)?d.depth:maxDepth;});
Next we add two additional checks to the second if statement like this:
if(d.target.thresholds !== undefined || d.target.name === true && d.target.depth == maxDepth)
It will make sure the text is added to true node only on the last level.
var treeData = [
{
"name": "glucose_tol",
"directions": ">",
"thresholds": "126",
"exits": 0.0,
"children": [
{
"name": "age",
"directions": ">",
"thresholds": "29",
"exits": 1.0,
"children": [
{
"name": true
},
{
"name": "mass_index",
"directions": ">",
"thresholds": "29.7",
"exits": 0.5,
"children": [
{
"name": true
},
{
"name": false
}
]
}
]
},
{
"name": false
}
]
},
];
// ************** 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.x, d.y]; });
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.
var maxDepth = 0;
nodes.forEach(function(d) { d.y = d.depth * 120; maxDepth = (d.depth>maxDepth)?d.depth:maxDepth;});
// 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.x + "," + d.y + ")"; });
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);
// Add threshold and directions
link.enter().insert("text")
.attr("font-family", "Arial, Helvetica, sans-serif")
.attr("fill", "Black")
.style("font", "normal 12px Arial")
.attr("transform", function(d) {
return "translate(" +
((d.source.x + d.target.x)/2) + "," +
((d.source.y + d.target.y)/2) + ")";
})
.attr("dy", ".35em")
.attr("text-anchor", "middle")
.text(function(d) {
//check whether thresholds is not undefined && that target.thresholds is not undefined as it will print on both sides
if (d.source.thresholds !== undefined)
if(d.target.thresholds !== undefined || d.target.name === true && d.target.depth == maxDepth)
return d.source.thresholds + ' ' + d.source.directions;
})
}
.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://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js"></script>
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").
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>
I want to add Donut chart in each node of D3 tree chart (replace circle node in tree with Donut Chart)
Below is my Code. I want to replace the circle node with Donut chart in each node with different data that sholud be read from sample json.
// Sample JSON Data
var jsondata = {
"name": "CERT",
"children": [{
"name": ""
}, {
"name": ""
}, {
"name": "DNS",
"children": [{
"name": "FW",
"children": [{
"name": ""
}, {
"name": ""
}, {
"name": "ADC",
"children": [{
"name": ""
}, {
"name": ""
}, {
"name": "SERVERS",
"children": [{
"name": ""
}, {
"name": ""
}]
}]
}]
}]
}, {
"name": "FW",
"children": [{
"name": ""
}, {
"name": ""
}, {
"name": "DNS",
"children": [{
"name": ""
}, {
"name": ""
}, {
"name": "ADC",
"children": [{
"name": "SERVERS"
}]
}]
}]
}]
};
var margin = {
top: 20,
right: 120,
bottom: 20,
left: 120
},
//width = 960 - margin.right - margin.left, height = 800 - margin.top - margin.bottom;
width = screen.width;
height = 800 - margin.top - margin.bottom;
var i = 0,
duration = 750,
root;
var tree = d3.layout.tree()
.separation(function(a, b) {
return a.parent === b.parent ? 1 : 2;
})
.children(function(d) {
return d.children;
})
.size([height, width]);
var diagonal = d3.svg.diagonal()
.projection(function(d) {
return [d.y, d.x];
});
var line = d3.svg.line()
.x(function(d) {
return d.x;
})
.y(function(d) {
return d.y;
});
var svg = d3.select("body").append("svg")
.attr("width", width + margin.right + margin.left)
.attr("height", height + margin.top + margin.bottom)
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.call(zm = d3.behavior.zoom().scaleExtent([1, 13]).on("zoom", redraw)).append("g")
.attr("transform", "translate(" + 350 + "," + 20 + ")");;
// put JSON data to root variable
root = jsondata;
root.x0 = height / 2;
root.y0 = 0;
//necessary so that zoom knows where to zoom and unzoom from
zm.translate([350, 20]);
update(root);
//d3.select(self.frameElement).style("height", "800px");
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;
});
// 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)
.on("mouseover", function(d) {
var g = d3.select(this); // The node
// The class is used to remove the additional text later
var info = g.append('text')
.classed('info', true)
.attr('x', 30)
.attr('y', 10)
.text(function(d) {
return d.name;
});
})
// Remove the info text on mouse out.
.on("mouseout", function() {
d3.select(this).select('text.info').remove();
});
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 ? -10 : 10;
})
.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", function(d) {
return d.children ? 20 : 10;
})
.style("fill", function(d) {
return d._children ? "lightsteelblue" : "#fff";
});
nodeUpdate.select("text")
.style("fill-opacity", 1);
// 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) {
return line([{
x: d.source.y,
y: d.source.x
}, {
x: d.target.y,
y: d.target.x
}]);
});
// Transition links to their new position.
link.transition()
//.duration(900)
.attr("d", function(d) {
return line([{
x: d.source.y,
y: d.source.x
}, {
x: d.target.y,
y: d.target.x
}]);
});
// Transition exiting nodes to the parent's new position.
link.exit().transition()
// .duration(duration)
.attr("d", function(d) {
return line([{
x: d.source.y,
y: d.source.x
}, {
x: d.target.y,
y: d.target.x
}]);
})
.remove();
// 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);
// Stash the old positions for transition.
nodes.forEach(function(d) {
d.x0 = d.x;
d.y0 = d.y;
});
}
function elbow(d, i) {
return "M" + d.source.y + "," + d.source.x + "H" + d.target.y + "V" + d.target.x + (d.target.children ? "" : "h" + margin.right);
}
// 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);
}
//redraw graph after zoom
function redraw() {
svg.attr("transform", "translate(" + d3.event.translate + ")" + " scale(" + d3.event.scale + ")");
}
.node {
cursor: pointer;
}
.node circle {
fill: #fff;
stroke: steelblue;
stroke-width: 1.5px;
}
.node text {
font: 10px sans-serif;
}
.link {
fill: none;
stroke: #ccc;
stroke-width: 1.5px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.3/d3.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>