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>
Related
I have faced the below issue when we try to zoom the tree view control after dragging and dropping it into some other location of the HTML body it goes to the initial position and performed zooming. Can you please advise me to zoom the tee-view in the drag and drop location itself? I have attached the sample for your reference.
var treeData =
{
"name": "Root",
"children": [
{
"name": "Item 1",
"children": [
{ "name": "Item 3" },
{ "name": "Item 4" }
]
},
{
"name": "Item 2",
"children": [
{ "name": "Item 5" },
{ "name": "Item 6" }
]
}
]
};
// Set the dimensions and margins of the diagram
var margin = {top: 20, right: 10, bottom: 30, left: 50},
width = 1520 - margin.left - margin.right,
height = 705 - margin.top - margin.bottom;
function dragged(d) {
debugger;
var test = d3.select(this).attr("transform");
d3.select(this).attr("transform", 'translate(' + (d3.event.sourceEvent.screenX - 400) + ',' + (d3.event.sourceEvent.screenY - 400) + ') ' + test.slice(test.indexOf('scale(')));
}
// append the svg object to the body of the page
// appends a 'group' element to 'svg'
// moves the 'group' element to the top left margin
var svg = d3.select("#TreeView").append("svg")
.attr("id", ("D3TreeViewSvg"))
.attr("width", 1520)
.attr("height", 705)
.attr("style", "margin: auto;display: block;overflow: visible;")
.append("g")
.attr("class", "D3TreeViewG")
.attr("transform", "translate("+ margin.left + "," + margin.top + ") scale(0.99)")
.call(d3.drag().on("drag", dragged))
.call(d3.zoom().on("zoom", function () {
debugger;
svg.attr('transform', 'translate(' + d3.event.transform.x + ',' + d3.event.transform.y + ') scale(' + d3.event.transform.k + ')');
}));
var rect = svg.append("rect")
.attr("width", width)
.attr("height", height)
.style("fill", "none")
.style("pointer-events", "all");
var i = 0,
duration = 750,
root;
// declares a tree layout and assigns the size
var treemap = d3.tree().size([height, width]);
// Assigns parent, children, height, depth
root = d3.hierarchy(treeData, function(d) { return d.children; });
root.x0 = height / 2;
root.y0 = 0;
// Collapse after the second level
//root.children.forEach(collapse);
update(root);
// 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
}
}
setTimeout(function(){
$('#' + 'D3TreeViewSvg').css('width', (($(".D3TreeViewG")[0].getBoundingClientRect().width) + 'px'));
}, 1000);
function update(source) {
// Assigns the x and y position for the nodes
var treeData = treemap(root);
// Compute the new tree layout.
var nodes = treeData.descendants(),
links = treeData.descendants().slice(1);
// Normalize for fixed-depth.
nodes.forEach(function(d){ d.y = d.depth * 180});
// ****************** Nodes section ***************************
// Update the nodes...
var node = svg.selectAll('g.node')
.data(nodes, function(d) {return d.id || (d.id = ++i); });
// Enter any new modes 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);
// Add Circle for the nodes
nodeEnter.append('circle')
.attr('class', 'node')
.attr('r', 1e-6)
.style("fill", function(d) {
return d._children ? "lightsteelblue" : "#fff";
});
// Add labels for the nodes
nodeEnter.append('text')
.attr("dy", ".35em")
.attr("x", function(d) {
return d.children || d._children ? -13 : 13;
})
.attr("text-anchor", function(d) {
return d.children || d._children ? "end" : "start";
})
.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.y + "," + d.x + ")";
});
// Update the node attributes and style
nodeUpdate.select('circle.node')
.attr('r', 10)
.style("fill", function(d) {
return d._children ? "lightsteelblue" : "#fff";
})
.attr('cursor', 'pointer');
// 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);
// ****************** links section ***************************
// Update the links...
var link = svg.selectAll('path.link')
.data(links, function(d) { return d.id; });
// Enter any new links at the parent's previous position.
var linkEnter = link.enter().insert('path', "g")
.attr("class", "link")
.attr('d', function(d){
var o = {x: source.x0, y: source.y0}
return diagonal(o, o)
});
// UPDATE
var linkUpdate = linkEnter.merge(link);
// Transition back to the parent element position
linkUpdate.transition()
.duration(duration)
.attr('d', function(d){ return diagonal(d, d.parent) });
// Remove any exiting links
var linkExit = link.exit().transition()
.duration(duration)
.attr('d', function(d) {
var o = {x: source.x, y: source.y}
return diagonal(o, o)
})
.remove();
// Store the old positions for transition.
nodes.forEach(function(d){
d.x0 = d.x;
d.y0 = d.y;
});
// Creates a curved (diagonal) path from parent to the child nodes
function diagonal(s, d) {
path = `M ${s.y} ${s.x}
C ${(s.y + d.y) / 2} ${s.x},
${(s.y + d.y) / 2} ${d.x},
${d.y} ${d.x}`
return path
}
// 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);
}
}
.node circle {
fill: #fff;
stroke: steelblue;
stroke-width: 3px;
}
.node text {
font: 12px sans-serif;
}
.link {
fill: none;
stroke: #ccc;
stroke-width: 20px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script src="https://d3js.org/d3.v4.min.js"></script>
<div id="TreeViewParent" style="height:705px;width:1520px;">
<div id="TreeView" style="height:100%;width:100%;"></div>
</div>
This question already has answers here:
Where is d3.svg.diagonal()?
(5 answers)
Closed 5 years ago.
I've been trying to migrate from D3.js v3 to version 4. I've reviewed the changelog and updated all functions, but I'm unable to render the path from source to target nodes now that the diagonal function is removed.
I'm using a Parse Tree generated by a Python Script via HTML and d3.js. The Python Script generates an HMTL document, here it is running with D3.js version 3
function drawTree(){
var margin = {top: 20, right: 120, bottom: 20, left: 120},
width = 1060 - margin.right - margin.left,
height = 600 - margin.top - margin.bottom;
var i = 0,
duration = 750,// animation duration
root;// stores the tree structure in json format
var tree = d3.layout.tree()
.size([height, width]);
var edge_weight = d3.scale.linear()
.domain([0, 100])
.range([0, 100]);
var diagonal = d3.svg.diagonal()
.projection(function(d) { return [d.y, d.x]; });
// adding the svg to the html structure
var svg = d3.select("div#viz").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 + ")");
var treeData =
{
"name": "Grandparent",
"size" : 100,
"children": [
{
"name": "Parent A",
"size": 70,
"children": [
{ "name": "Son of A",
"size" : 30,
"children": [
{ "name": "grandson of A",
"size" : 3},
{ "name": "grandson 2 of A",
"size" : 2},
{ "name": "grandson 3 of A",
"size" : 5},
{ "name": "grandaughter of A",
"size" : 20,
"children": [
{ "name": "great-grandson of A",
"size" : 15},
{ "name": "great-grandaughter of A",
"size" : 5}
]
}
],
},
{ "name": "Daughter of A" ,
"size" : 40
}
]
},
{ "name": "Parent B",
"size" : 30 }],
};
edge_weight.domain([0,treeData.size]);
// Assigns parent, children, height, depth
root = treeData;
root.x0 = height / 2;
root.y0 = 0;
root.children.forEach(collapse);
update(root);
d3.select(self.frameElement).style("height", "800px");
/**
* Updates the node.
* cloppases and expands the node bases on the structure of the source
* all 'children' nodes are expanded and '_children' nodes collapsed
* #param {json structure} source
*/
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);
nodeEnter.append("circle")
.attr('class', 'node')
.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){ console.log(">>>>>>>>", d);return edge_weight(d.size/2);})
.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…
// 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("stroke-width", function(d){
return 1;
})
.attr("d", function(d) {
var o = {x: source.x0, y: source.y0};
return diagonal({source: o, target: o});
})
.attr("stroke", function(d){
return "lavender";});
// Transition links to their new position.
link.transition()
.duration(duration)
.attr("d", function(d){
/* calculating the top shift */
var source = {x: d.source.x - edge_weight(calculateLinkSourcePosition(d)), y: d.source.y};
var target = {x: d.target.x, y: d.target.y};
return diagonal({source: source, target: target});
})
.attr("stroke-width", function(d){
return edge_weight(d.target.size);
});
// 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;
});
}
/**
* Calculates the source y-axis position of the link.
* #param {json structure} link
*/
function calculateLinkSourcePosition(link) {
targetID = link.target.id;
var childrenNumber = link.source.children.length;
var widthAbove = 0;
for (var i = 0; i < childrenNumber; i++)
{
if (link.source.children[i].id == targetID)
{
// we are done
widthAbove = widthAbove + link.source.children[i].size/2;
break;
}else {
// keep adding
widthAbove = widthAbove + link.source.children[i].size
}
}
return link.source.size/2 - widthAbove;
}
/*
* Toggle children on click.
* #param {node} d
*/
function click(d) {
if (d.children) {
d._children = d.children;
d.children = null;
} else {
d.children = d._children;
d._children = null;
}
update(d);
}
/*
* Collapses the node d and all the children nodes of d
* #param {node} d
*/
function collapse(d) {
if (d.children) {
d._children = d.children;
d._children.forEach(collapse);
d.children = null;
}
}
/*
* Collapses the node in the tree
*/
function collapseAll() {
root.children.forEach(collapse);
update(root);
}
/*
* Expands the node d and all the children nodes of d
* #param {node} d
*/
function expand(d) {
if (d._children) {
d._children = null;
}
if (d.children) {
d.children.forEach(expand);
}
}
/*
* Expands all the nodes in the tree
*/
function expandAll() {
root.children.forEach(expand);
update(root);
}
}
.node {
cursor: pointer;
}
.node circle {
fill: #fff;
stroke: steelblue;
stroke-width: 1.5px;
}
.node text {
font: 10px sans-serif;
}
.link {
fill: none;
/*stroke: steelblue;*/
opacity: 0.3;
/*stroke-width: 1.5px;*/
}
#levels{
margin-left: 120px;
}
<!DOCTYPE html>
<meta charset="utf-8">
<body onLoad="drawTree()">
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.0.0/d3.min.js"></script>
<button type="button" onclick="collapseAll()">Collapse All</button>
<button type="button" onclick="expandAll()">Expand All</button>
<div id="viz"></div>
</body>
And here's as far as I got with the v4 migration.
var margin = {
top: 20,
right: 120,
bottom: 20,
left: 120
},
width = 900 - margin.right - margin.left,
height = 400 - margin.top - margin.bottom;
var i = 0,
duration = 750, // animation duration
root; // stores the tree structure in json format
// declares a tree layout and assigns the size
var treemap = d3.tree().size([height, width]);
var edge_weight = d3.scaleLinear()
.domain([0, 100])
.range([0, 100]);
// Creates a curved (diagonal) path from parent to the child nodes
function diagonal(s, d) {
path = `M ${s.y} ${s.x}
C ${(s.y + d.y) / 2} ${s.x},
${(s.y + d.y) / 2} ${d.x},
${d.y} ${d.x}`
return path
}
// adding the svg to the html structure
var svg = d3.select("div#viz").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 + ")");
var treeData = {
"name": "Grandparent",
"size": 100,
"children": [{
"name": "Parent A",
"size": 70,
"children": [{
"name": "Son of A",
"size": 30,
"children": [{
"name": "grandson of A",
"size": 3
},
{
"name": "grandson 2 of A",
"size": 2
},
{
"name": "grandson 3 of A",
"size": 5
},
{
"name": "grandaughter of A",
"size": 20,
"children": [{
"name": "great-grandson of A",
"size": 15
},
{
"name": "great-grandaughter of A",
"size": 5
}
]
}
],
},
{
"name": "Daughter of A",
"size": 40
}
]
},
{
"name": "Parent B",
"size": 30
}
],
};
edge_weight.domain([0, treeData.size]);
// Assigns parent, children, height, depth
root = d3.hierarchy(treeData, function(d) {
return d.children;
});
root.x0 = height / 2;
root.y0 = 0;
root.children.forEach(collapse);
update(root);
d3.select(self.frameElement).style("height", "800px");
/**
* Updates the node.
* cloppases and expands the node bases on the structure of the source
* all 'children' nodes are expanded and '_children' nodes collapsed
* #param {json structure} source
*/
function update(source) {
// Assigns the x and y position for the nodes
var treeData = treemap(root);
// Compute the new tree layout.
var nodes = treeData.descendants(),
links = treeData.descendants().slice(1);
// 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);
nodeEnter.append("circle")
.attr('class', 'node')
.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 = nodeEnter.merge(node);
nodeUpdate.transition()
.duration(duration)
.attr("transform", function(d) {
return "translate(" + d.y + "," + d.x + ")";
});
nodeUpdate.select("circle")
.attr("r", function(d) {
return edge_weight(d.data.size / 2);
})
.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.id;
});
//.data(links, function(d) { return d.target.id; });
// Enter any new links at the parent's previous position.
var linkEnter = link.enter().insert('path', "g")
.attr("class", "link")
.attr('d', function(d) {
console.log("linkEnter", d);
var o = {
x: source.x,
y: source.y
}
console.log("o", o);
return diagonal(o, o)
})
.attr("stroke", function(d) {
return "cyan";
});
// Transition links to their new position.
link.transition()
.duration(duration)
.attr("d", function(d) {
console.log("lala", d);
/* calculating the top shift */
var source = {
x: d.x - edge_weight(calculateLinkSourcePosition(d)),
y: d.y
};
var target = {
x: d.parent.x,
y: d.parent.y
};
return diagonal({
source: source,
target: target
});
})
.attr("stroke-width", function(d) {
return edge_weight(d.target.size);
});
// 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) {
console.log("stash", d);
d.x0 = d.x;
d.y0 = d.y;
});
}
/**
* Calculates the source y-axis position of the link.
* #param {json structure} link
*/
function calculateLinkSourcePosition(link) {
targetID = link.target.id;
var childrenNumber = link.source.children.length;
var widthAbove = 0;
for (var i = 0; i < childrenNumber; i++) {
if (link.source.children[i].id == targetID) {
// we are done
widthAbove = widthAbove + link.source.children[i].size / 2;
break;
} else {
// keep adding
widthAbove = widthAbove + link.source.children[i].size
}
}
return link.source.size / 2 - widthAbove;
}
/*
* Toggle children on click.
* #param {node} d
*/
function click(d) {
if (d.children) {
d._children = d.children;
d.children = null;
} else {
d.children = d._children;
d._children = null;
}
update(d);
}
/*
* Collapses the node d and all the children nodes of d
* #param {node} d
*/
function collapse(d) {
if (d.children) {
d._children = d.children;
d._children.forEach(collapse);
d.children = null;
}
}
/*
* Collapses the node in the tree
*/
function collapseAll() {
root.children.forEach(collapse);
update(root);
}
/*
* Expands the node d and all the children nodes of d
* #param {node} d
*/
function expand(d) {
if (d._children) {
d.children = d._children;
d._children = null;
}
if (d.children) {
d.children.forEach(expand);
}
}
/*
* Expands all the nodes in the tree
*/
function expandAll() {
root.children.forEach(expand);
update(root);
}
.node {
cursor: pointer;
}
.node circle {
fill: #fff;
stroke: steelblue;
stroke-width: 1.5px;
}
.node text {
font: 10px sans-serif;
}
.link {
fill: none;
/*stroke: steelblue;*/
opacity: 0.3;
/*stroke-width: 1.5px;*/
}
#levels {
margin-left: 120px;
}
<body>
<script src="http://d3js.org/d3.v4.min.js"></script>
<button type="button" onclick="collapseAll()">Collapse All</button>
<button type="button" onclick="expandAll()">Expand All</button>
<div id="viz"></div>
</body>
Most likely I'm just blind and you'll instantly see what I did wrong, but I've been staring at this for a while now...
Thanks in advance for any help!
I'm currently working on a similar project myself and figured this could be pretty helpful.. It seems you were really close, just needed to adjust a few minor paths to reach parent/child nodes..
Basically "source" is now "parent" and there is no "target". You can see most of the updates where commented out lines are v3 with v4 updates just below. For example:
link.target.id --> link.id
link.source.children.length --> link.parent.children.length
link.source.size --> link.parent.data.size
There's a few other miscellaneous updates throughout the code as well. The one thing I couldn't get working fully were the "Expand All/Collapse All" buttons. "Expand All" seems to work ok, but "Collapse All" seems to leave the link paths alone..
Here's a working fiddle: https://jsfiddle.net/jufra0b2/
I suspect there's something that can be done to the exit link but not sure. Anyways it's a step in the right direction. Hope you figure out the rest ok..
I'm following this
tutorial to learn d3 js tree layout and i'm playing around with it. On clicking a child node I'm trying to select all the ancestor nodes and the paths connecting them.
I have changed the default click function in the tutorial to something that looks like this.
function click(d) {
console.log(d.parent);
while(d.parent)
{
d = d.parent;
console.log(d.parent);
}
}
This select all the ancestor nodes one by one , but how can i select all the connecting paths between them ?
For eg: If i want to color all the ancestor nodes and the connecting paths , how can i do that ?
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,
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);
d3.select(self.frameElement).style("height", "500px");
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);
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) {
console.log(d.parent);
while (d.parent) {
d = d.parent;
console.log(d.parent);
}
}
.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;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
First give Ids to link path like this:
link.enter().insert("path", "g")
.attr("class", "link")
.attr("id", function(d){ return ("link" + d.source.id + "-" + d.target.id)})//unique id
Then give id to all nodes.
nodeEnter.append("circle")
.attr("r", 1e-6)
.attr("id", function(d){return "node" + d.id;})//id of the node.
Then in the click function do the selection based on the ids:
function click(d) {
//reset all nodes color
d3.selectAll("circle").style("fill", "white");//reset all node colors
d3.selectAll("path").style("stroke", "#c3c3c3");//reset the color for all links
while (d.parent) {
d3.selectAll("#node"+d.id).style("fill", "red");//color the node
if (d.parent != "null")
d3.selectAll("#link"+d.parent.id + "-" + d.id).style("stroke", "red");//color the path
d = d.parent;
}
}
Working code here
EDIT: Plunker adapted to stack snippet:
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 = 180 - 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);
d3.select(self.frameElement).style("height", "500px");
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);
nodeEnter.append("circle")
.attr("r", 1e-6)
.attr("id", function(d){return "node" + d.id;})
.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("id", function(d){ return ("link" + d.source.id + "-" + d.target.id)})
.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) {
//reset all nodes color
d3.selectAll("circle").style("fill", "white");
d3.selectAll("path").style("stroke", "#c3c3c3");
while (d.parent) {
d3.selectAll("#node"+d.id).style("fill", "red")
if (d.parent != "null")
d3.selectAll("#link"+d.parent.id + "-" + d.id).style("stroke", "red")
d = d.parent;
}
}
.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;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
D3 v6 example here.
I am a noob in web-development. I'm trying to create a tree-like hierarchical company org chart. I tried both google's visualization chart and Mike Bostock's D3 Reingold tree.
I want these features :
tree structure : either top-down (google) or left-right (D3)
online/dynamic : viewable in browser and able to read data from json (both google & D3), not static visio or ppt diagram
collapsible : able to hide subtrees (both)
space-adjusting : nodes should fill visible area, to reduce scrolling (only D3)
attributes : display name, title & possibly picture (only google)
Above I've marked which tool allows which features, afaik.
I prefer the D3 version because it looks cool.
I can modify the .json to include additional fields (title, url to photo etc.) - here is a sample
My question is - how do I modify the D3 code to display an employee's name, then title in the next line, and maybe picture too ?
Or if that's not feasible - how do I modify the google code to automatically adjust spacing, so that all children of a node are close together, and I don't have to horizontally scroll ?
Here's a quick example. It modifies this example, to add in first name, last name, a title and a picture.
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.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;
}
</style>
<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
<script>
var margin = {top: 20, right: 120, bottom: 20, left: 120},
width = 960 - margin.right - margin.left,
height = 300 - 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 + ")");
var data = {
"fname": "Rachel",
"lname": "Rogers",
"title": "CEO",
"photo": "http://lorempixel.com/60/60/cats/1",
"children": [{
"fname": "Bob",
"lname": "Smith",
"title": "President",
"photo": "http://lorempixel.com/60/60/cats/2",
"children": [{
"fname": "Mary",
"lname": "Jane",
"title": "Vice President",
"photo": "http://lorempixel.com/60/60/cats/3",
"children": [{
"fname": "Bill",
"lname": "August",
"title": "Dock Worker",
"photo": "http://lorempixel.com/60/60/cats/4"
}, {
"fname": "Reginald",
"lname": "Yoyo",
"title": "Line Assembly",
"photo": "http://lorempixel.com/60/60/cats/5"
}]
}, {
"fname": "Nathan",
"lname": "Ringwald",
"title": "Comptroller",
"photo": "http://lorempixel.com/60/60/cats/6"
}]
}]
}
root = data;
root.x0 = height / 2;
root.y0 = 0;
function collapse(d) {
if (d.children) {
d._children = d.children;
d._children.forEach(collapse);
d.children = null;
}
}
root.children.forEach(collapse);
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; });
// 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);
// add picture
nodeEnter
.append('defs')
.append('pattern')
.attr('id', function(d,i){
return 'pic_' + d.fname + d.lname;
})
.attr('height',60)
.attr('width',60)
.attr('x',0)
.attr('y',0)
.append('image')
.attr('xlink:href',function(d,i){
return d.photo;
})
.attr('height',60)
.attr('width',60)
.attr('x',0)
.attr('y',0);
nodeEnter.append("circle")
.attr("r", 1e-6)
.style("fill", function(d) { return d._children ? "lightsteelblue" : "#fff"; });
var g = nodeEnter.append("g");
g.append("text")
.attr("x", function(d) { return d.children || d._children ? -35 : 35; })
.attr("dy", "1.35em")
.attr("text-anchor", function(d) { return d.children || d._children ? "end" : "start"; })
.text(function(d) { return d.fname + " " + d.lname; })
.style("fill-opacity", 1e-6);
g.append("text")
.attr("x", function(d) { return d.children || d._children ? -35 : 35; })
.attr("dy", "2.5em")
.attr("text-anchor", function(d) { return d.children || d._children ? "end" : "start"; })
.text(function(d) { return d.title; })
.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", 30)
.style("fill", function(d,i){
return 'url(#pic_' + d.fname + d.lname+')';
});
nodeUpdate.selectAll("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>
Reversed Direction:
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.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;
}
</style>
<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
<script>
var margin = {top: 20, right: 120, bottom: 20, left: 120},
width = 960 - margin.right - margin.left,
height = 300 - 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.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 + ")");
var data = {
"fname": "Rachel",
"lname": "Rogers",
"title": "CEO",
"photo": "http://lorempixel.com/60/60/cats/1",
"children": [{
"fname": "Bob",
"lname": "Smith",
"title": "President",
"photo": "http://lorempixel.com/60/60/cats/2",
"children": [{
"fname": "Mary",
"lname": "Jane",
"title": "Vice President",
"photo": "http://lorempixel.com/60/60/cats/3",
"children": [{
"fname": "Bill",
"lname": "August",
"title": "Dock Worker",
"photo": "http://lorempixel.com/60/60/cats/4"
}, {
"fname": "Reginald",
"lname": "Yoyo",
"title": "Line Assembly",
"photo": "http://lorempixel.com/60/60/cats/5"
}]
}, {
"fname": "Nathan",
"lname": "Ringwald",
"title": "Comptroller",
"photo": "http://lorempixel.com/60/60/cats/6"
}]
}]
}
root = data;
root.x0 = height / 2;
root.y0 = 0;
function collapse(d) {
if (d.children) {
d._children = d.children;
d._children.forEach(collapse);
d.children = null;
}
}
root.children.forEach(collapse);
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; });
// 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.x0 + "," + source.y0 + ")"; })
.on("click", click);
// add picture
nodeEnter
.append('defs')
.append('pattern')
.attr('id', function(d,i){
return 'pic_' + d.fname + d.lname;
})
.attr('height',60)
.attr('width',60)
.attr('x',0)
.attr('y',0)
.append('image')
.attr('xlink:href',function(d,i){
return d.photo;
})
.attr('height',60)
.attr('width',60)
.attr('x',0)
.attr('y',0);
nodeEnter.append("circle")
.attr("r", 1e-6)
.style("fill", function(d) { return d._children ? "lightsteelblue" : "#fff"; });
var g = nodeEnter.append("g");
g.append("text")
.attr("x", function(d) { return d.children || d._children ? -35 : 35; })
.attr("dy", "1.35em")
.attr("text-anchor", function(d) { return d.children || d._children ? "end" : "start"; })
.text(function(d) { return d.fname + " " + d.lname; })
.style("fill-opacity", 1e-6);
g.append("text")
.attr("x", function(d) { return d.children || d._children ? -35 : 35; })
.attr("dy", "2.5em")
.attr("text-anchor", function(d) { return d.children || d._children ? "end" : "start"; })
.text(function(d) { return d.title; })
.style("fill-opacity", 1e-6);
// Transition nodes to their new position.
var nodeUpdate = node.transition()
.duration(duration)
.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });
nodeUpdate.select("circle")
.attr("r", 30)
.style("fill", function(d,i){
return 'url(#pic_' + d.fname + d.lname+')';
});
nodeUpdate.selectAll("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.x + "," + source.y + ")"; })
.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>
If you like to create your project with D3js, Just need to use the script codes from this and replaced3.json("/mbostock/raw/4063550/flare.json", function(error, flare) with this :
d3.json("yourJsonFile/jsonFileName.json", function(error, flare)
The jsonFileName.json is your customized json file like this sample.
Also you can insert name of your pictures into the json file and replace the img tag src with it.
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>