d3.js Maximum call stack size exceeded error - javascript

When I try to layout my force-directed graph, the following is the error I receive. I read about this issue in Mike Bostock's github page and found that it might be due to NaN values of coordinates or all the points drawn at the same point. I checked into the console and I found that all of my points are getting drawn at the same X and Y values.
On an example data, the code worked perfectly well but no more now. My data goes like 45-50 levels away from the center node. I have successfully made a tree layout with this data. Wanted to try the force directed layout but it did not work.
Any help regarding how to draw the nodes on separate coordinates would be much appreciated.
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.node {
stroke: #fff;
stroke-width: 1.5px;
}
.link {
stroke: #999;
stroke-opacity: .6;
}
</style>
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
var margin = {top: 10, right: 10, bottom: 20, left: 40},
width = 1300 - margin.left - margin.right,
height = 800 - margin.top - margin.bottom;
var color = d3.scale.category20();
var force = d3.layout.force().charge(-120)
.linkDistance(30)
.size([width, height]);
var svg = d3.select("body").append("svg:svg")
.attr("width", width)
.attr("height", height)
//.attr("pointer-events","all")
.append('svg:g')
//.call(d3.behavior.zoom().translate([100,50]).scale(.5).on("zoom",redraw))
.append('svg:g')
.attr("transform","translate(100,50)scale(.5,.5)");
svg.append('svg:rect')
.attr('width', width)
.attr('height', height)
.attr('fill','white')
.attr('opacity',0);
function redraw() {
var trans = d3.event.translate;
var scale = d3.event.scale;
svg.attr("transform",
"translate(" + trans + ")"
+ " scale(" + scale + ")");
};
d3.json("test_data.json", function(error, graph) {
var nodeMap = {};
graph.nodes.forEach(function(x) { nodeMap[x.name] = x; });
graph.links = graph.links.map(
function(x)
{
return {
source: nodeMap[x.source],
target: nodeMap[x.target],
value: x.value
};
});
console.log(graph.nodes);
console.log(graph.links);
force
.nodes(graph.nodes)
.links(graph.links)
.start();
var link = svg.selectAll(".link")
.data(graph.links)
.enter().append("line")
.attr("class", "link")
.style("stroke", function(d) { return color(d.value); })
.style("stroke-width", function(d) {
//console.log(d.value);
return Math.sqrt(d.value); });
var node = svg.selectAll(".node")
.data(graph.nodes)
.enter().append("circle")
.attr("class", "node")
.attr("r", 5)
.style("fill", function(d) { return color(d.value); })
.call(force.drag)
.on("mousedown",
function(d) {
d.fixed = true;
d3.select(this).classed("sticky", true);
}
)
.on("mouseover",fade(0.1))
.on("mouseout",fade(1));
var linkedByIndex = {};
graph.links.forEach(function(d) {
linkedByIndex[d.source.index + "," + d.target.index] = 1;
});
function isConnected(a, b) {
return linkedByIndex[a.index + "," + b.index] || linkedByIndex[b.index + "," + a.index] || a.index == b.index;
}
node.append("title")
.text(function(d) {
return "Name : "+d.name+"\n"+"Parent: "+d.parent +"\n"+"Relationship: "+ d.relationship +"\n"+ "Creation Date: "+ d.cod +"\n"; });
function fade(opacity)
{
return function(d) {
node.style("stroke-opacity", function(o) {
thisOpacity = isConnected(d, o) ? 1 : opacity;
this.setAttribute('fill-opacity', thisOpacity);
return thisOpacity;
});
link.style("stroke-opacity", opacity).style("stroke-opacity", function(o) {
return o.source === d || o.target === d ? 1 : opacity;
});
};
}
force.on("tick", function() {
link.attr("x1", function(d) { return d.source.x; })
.attr("y1", function(d) { return d.source.y; })
.attr("x2", function(d) { return d.target.x; })
.attr("y2", function(d) { return d.target.y; });
node
.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });
/*node.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; });*/
});
});
</script>
The JSON looks like:
{
"links": [
{
"source": "Me",
"target": "Adam",
"value": 10
},
{
"source": "Me",
"target": "You",
"value": 10
}
], "nodes": [
{
"ancestor": "Adam",
"cod": 19061964,
"distance": 0,
"name": "Adam",
"parent": null,
"relationship": null,
"value": 10
},
{
"ancestor": "Adam",
"cod": 13032003,
"distance": 1,
"name": "Me",
"parent": "You",
"relationship": "Father",
"value": 10
}
]
}
EDIT: I get the error in the following statement:
force
.nodes(graph.nodes)
.links(graph.links)
.start();
highlighting:
"start();"

In your data your links are to names as opposed to indices. Check out the example data from http://bl.ocks.org/mbostock/4062045.
Links have the form: {"source":1,"target":0,"value":1}, which points to the index of the node.

Related

How to display nested nodes from nested data in d3js?

I'm trying to display a network graph with clustered nodes but I'm having trouble with the nested nodes in D3. The first "layer" contains clusters and each node of the first layer can contain multiple nodes. Links in the network would probably only occur between clusters (meaning between nodes of the first layer).
Here is the code I have so far. I'm able to display the first level of nodes. I cannot figure out how to display the nested nodes (see code in const data in each node children).
const node_radius = 100;
const width = 800;
const height = 400;
const links = [
{ "source": 1, "target": 6}
] ;
const data = [
{
"id":1,
"level": "cluster",
"name": "analytics1",
"children": [
{
"id":2,
"name": "animate1",
"level": "leaf",
"size": 15,
"parent": 1
},
{
"id":3,
"name": "animate2",
"level": "leaf",
"size": 15,
"parent": 1
},
{
"id":4,
"name": "animate3",
"level": "leaf",
"size": 15,
"parent": 1
}
]
},
{
"id":6,
"name": "analytics2",
"level": "cluster",
"children": [
{
"id":7,
"name": "animate4",
"level": "leaf",
"size": 10,
"parent": 6
}
]
}
]
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
var simulation = d3.forceSimulation()
// pull nodes together based on the links between them
.force("link", d3.forceLink().id(function(d) { return d.id; }).strength(0.0001))
// push nodes apart to space them out
.force("charge", d3.forceManyBody().strength(-10))
// add some collision detection so they don't overlap
.force("collide", d3.forceCollide().radius(node_radius))
// and draw them around the centre of the space
.force("center", d3.forceCenter(width / 2, height / 2));
var link = svg.append("g")
.attr("class", "links")
.selectAll("line")
.data(links).enter().append("line")
.attr("stroke-width", 5)
.attr("stroke","#000");
var node = svg.append("g")
.attr("class", "nodes")
.selectAll("circle")
.data(data)
.enter().append("circle")
.attr("id", function(d) {return "circle"+d.id;})
.attr("class", "node")
.attr("r", node_radius)
.style("opacity", 0.2)
.attr("dx", 12)
.attr("dy", ".35em");
var text = svg.append("g")
.attr("class", "label")
.selectAll("text")
.data(data)
.enter().append("text")
.text(function(d) { return d.name });
// Update and restart the simulation.
simulation.nodes(data).on("tick", ticked);
simulation.force("link").links(links);
simulation.alpha(1).restart();
function ticked() {
link
.attr("x1", function(d) { return d.source.x; })
.attr("y1", function(d) { return d.source.y; })
.attr("x2", function(d) { return d.target.x; })
.attr("y2", function(d) { return d.target.y; });
node.attr("transform", positionNode);
text
.attr("dx", function(d) { return d.x - 30; })
.attr("dy", function(d) { return d.y + 15; });
}
// move the node based on forces calculations
function positionNode(d) {
// keep the node within the boundaries of the svg
if (d.x < node_radius) {
d.x = 2*node_radius
};
if (d.y < node_radius) {
d.y = 2*node_radius
};
if (d.x > width-node_radius) {
d.x = width-(2*node_radius)
};
if (d.y > height-node_radius) {
d.y = height-(2*node_radius)
};
return "translate(" + d.x + "," + d.y + ")";
}
<script src="https://d3js.org/d3.v4.min.js"></script>
I would like to have something like the following image. The two clusters are displayed and in each group, all children (leaf nodes) are represented by a smaller node. Node size should be customizable for both "layers" from data input.
Example I'm trying to follow on Fiddle.
I have also tried to use d3.pack() to pack circles inside of other circles. Here is an example. The problem I have currently with this approach is that I did not succeed in adding space and links between nodes of the first "layer" (between clusters). The high level clusters are also packed together and it would be impossible to add comprehensible links betwen them.
I finally succeeded in merging the d3.pack() example with the clustering example. Here is my solution.
var data = [
{
"id":1,
"level": "cluster",
"name": "analytics1",
"children": [
{
"id":2,
"name": "animate1",
"level": "leaf",
"size": 8,
"parent": 1,
"icon":"https://image.freepik.com/free-icon/apple-logo_318-40184.jpg"
},
{
"id":3,
"name": "animate2",
"level": "leaf",
"size": 10,
"parent": 1,
"icon": "https://www.freelogodesign.org/img/logo-ex-7.png"
},
{
"id":4,
"name": "animate3",
"level": "leaf",
"size": 5,
"parent": 1,
"icon": "http://brandmark.io/logo-rank/random/pepsi.png"
}
]
},
{
"id":6,
"name": "analytics2",
"level": "cluster",
"children": [
{
"id":7,
"name": "animate4",
"level": "leaf",
"size": 10,
"parent": 6,
"icon":"https://www.seoclerk.com/pics/558390-11FO8A1505384509.png"
}
]
}
]
var links = [
{ "source": 1, "target": 6}
] ;
var w = 1200, h = 500;
var cluster_padding = 5;
var node_padding = 2;
var size_ratio =100;
var color = d3.scaleOrdinal(d3.schemeCategory20c);
let sumSizes = 0;
data.forEach(function(cluster){
cluster.children.forEach(function(node){
sumSizes += node.size;
});
});
// Compute sum of sizes for cluster size.
data.forEach(function(cluster){
cluster.size = (
cluster.children.map(function(d){return d.size;})
.reduce(function(acc, val){ return acc+val+node_padding; })/sumSizes
)*size_ratio + cluster_padding;
cluster.children = cluster.children.sort(function(a,b){
return (a.size < b.size) ? 1 : ((b.size < a.size) ? -1 : 0);
})
cluster.children.forEach(function(node){
node.parentSize = cluster.size;
node.size = node.size*size_ratio/sumSizes;
});
});
var svg = d3.select("body").append("svg")
.attr("width", w)
.attr("height", h);
////////////////////////
// outer force layout
var outerSimulation = d3.forceSimulation()
// pull nodes together based on the links between them
.force("link", d3.forceLink().id(function(d) { return d.id; }).strength(0.001))
// push nodes apart to space them out
.force("charge", d3.forceManyBody().strength(-5))
// add some collision detection so they don't overlap
.force("collide", d3.forceCollide().radius(function(d){return d.size+cluster_padding;}))
// and draw them around the centre of the space
.force("center", d3.forceCenter(w / 2, h / 2));
var outerLinks = svg.selectAll("line")
.data(links)
.enter().append("line")
.attr("class", "links")
.attr("stroke-width", 5);
var outerNodes = svg.selectAll("g.outer")
.data(data, function (d) {return d.id;})
.enter()
.append("g")
.attr("class", "outer")
.attr("id", function (d) {return "cluster"+d.id;})
.attr("x", w/2)
.attr("y", w/2)
.call(d3.drag());
outerNodes.append("circle")
.style("fill", function(d,i){return color(i);})
.style("stroke", "blue")
.attr("r", function(d){return d.size});
// Update and restart the simulation.
outerSimulation.nodes(data).on("tick", outerTick);
outerSimulation.force("link").links(links);
outerSimulation.alpha(1).restart();
////////////////////////
// inner force layouts
var innerNodes = [];
var innerTexts = [];
var packs = [];
var margin = 20;
data.forEach(function(n){
// Pack hierarchy definition
var pack = d3.pack()
.size([2*n.size, 2*n.size])
.padding(cluster_padding);
var root = d3.hierarchy(n)
.sum(function(d) { return d.size; })
.sort(function(a, b) { return b.value - a.value; });
var nodes = pack(root).descendants();
// Round images
var defs = svg.append("defs").attr("id", "imgdefs")
var pattern = defs
.selectAll("pattern")
.data(nodes.filter(function(d) { return d.parent }))
.enter().append("pattern")
.attr("id", function(d){return "photo"+d.data.name})
.attr("height", 1)
.attr("width", 1)
.attr("x", "0")
.attr("y", "0");
var image = pattern.append('image')
.attr("class","roundImg")
.attr("id", function(d){return "photo"+d.data.name;})
.attr("xlink:href", function(d){return d.data.icon ? d.data.icon : "";})
.attr("height", function(d){return 3.2*d.r ;})
;
// Nodes
var circle = svg.select("g.outer#cluster"+n.id).selectAll("g.inner")
.data(nodes.filter(function(d) { return d.parent }))
.enter().append("circle")
.attr("class", "node node--leaf ")
.attr("id", function(d) {return d.data.name})
.style("fill", function(d) { return "url(#photo"+d.data.name+")";})
.attr("r", function(d) { return d.r; })
.attr("transform", function(d) { return "translate("+(d.x-n.size) +","+ (d.y-n.size)+")"; })
;
});
////////////////////////
// functions
function outerTick (e) {
outerNodes.attr("transform", positionNode);
outerLinks
.attr("x1", function(d) { return d.source.x; })
.attr("y1", function(d) { return d.source.y; })
.attr("x2", function(d) { return d.target.x; })
.attr("y2", function(d) { return d.target.y; });
}
function positionNode(d) {
// keep the node within the boundaries of the svg
if (d.x - d.size < 0) {
d.x = d.size + 2
};
if (d.y - d.size < 0) {
d.y = d.size + 2
};
if (d.x + d.size > w) {
d.x = w - d.size - 2
};
if (d.y + d.size > h) {
d.y = h - d.size - 2
};
return "translate(" + d.x + "," + d.y + ")";
}
<!DOCTYPE html>
<meta charset="utf-8">
<script src="https://d3js.org/d3.v4.min.js"></script>
<link rel="stylesheet" type="text/css" href="css/pack.css">
<body>
<div class="packed" id="packed"></div>
</body>

bouncing is not working in force layout

I have four nodes which is displaying as position given in data json. After dragging each node I have managed to set each node back to its given position . It works fine but the default bouncing is not working rather it is quickly set to fixed position. To give a bouncing effect into this what need to change my code.
Please take a look example
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.link {
stroke: #dfdfdf;
}
.node text {
pointer-events: none;
font: 10px sans-serif;
}
.link.red {
stroke: blue;
}
</style>
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
var width = 500,
height = 500
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
var force = d3.layout.force()
.gravity(0)
.linkDistance (200)
.charge(0)
.size([width, height]);
var node_psoition = [{"x":65, "y":43},{"x":365, "y":43},{"x":265, "y":243},{"x":65, "y":243}]
var datajson = {
"nodes" : [ {"id":1,"name" : "a", "group" : 2, "x":65, "y":43 , "fixed":"TRUE"} , {"id":2,"name" : "b", "group" : 1,"x":265, "y":43, "fixed":"TRUE"}, { "id":3,"name" : "c", "group" : 1, "x":265, "y":243, "fixed":"TRUE" }, {"id":4,"name" : "d", "group" : 2,"x":65, "y":243, "fixed":"TRUE"} ],
"links" : [{"source": 0 ,"target" : 1 , "value" : 1},{"source": 0 ,"target" : 3 , "value" : 1},{"source": 2 ,"target" : 3 , "value" : 1},
{"source": 2 ,"target" : 1 , "value" : 1}
]
}
force
.nodes(datajson.nodes)
.links(datajson.links)
.start();
var drag = force.drag()
.on("dragend", dragend);
var link = svg.selectAll(".link")
.data(datajson.links)
.enter().append("line")
.attr("class", "link");
var node = svg.selectAll(".node")
.data(datajson.nodes)
.enter().append("g")
.attr("class", "node")
.call(force.drag);
node.append("image")
.attr("x", -8)
.attr("y", -8)
.attr("class", 'bounce')
.attr("width", 45)
.attr("height", 45)
.attr("xlink:href", function(d) {
var rnd = Math.floor(Math.random() * 64 + 1);
var imagePath =
"http://www.bigbiz.com/bigbiz/icons/ultimate/Comic/Comic"
+ rnd.toString() + ".gif";
return imagePath;
});
node.append("text")
.attr("dx", 12)
.attr("dy", ".35em")
.text(function(d) { return d.name });
force.on("tick", function() {
link.attr("x1", function(d) { return d.source.x; })
.attr("y1", function(d) { return d.source.y; })
.attr("x2", function(d) { return d.target.x; })
.attr("y2", function(d) { return d.target.y; });
node.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });
});
function dragend(d) {
force.stop();
position = (d.id - 1);
temp_x = node_psoition[position].x;
temp_y = node_psoition[position].y;
d.x = d.px = temp_x;
d.y = d.py = temp_y;
d.fixed = true;
force.start();
}
</script>
To give a bouncing effect each node what need to update my code. Please advice.
Thanks

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>

D3 Tree Chart with D3 Donut Chart Node

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>

Building a graph node by node with D3

I have a graph structure that stored in json format that looks like this:
{
"links": [
{
"source": 1,
"target": 0,
"value": 1
},
{
"source": 2,
"target": 0,
"value": 1
},
{
"source": 3,
"target": 0,
"value": 1
}
],
"nodes": [
{
"group": 3,
"name": "justintimberlake"
},
{
"group": 2,
"name": "Anastacia Lyn Newton"
},
{
"group": 2,
"name": "Maria Do Carmo"
}
],
"time": [
{
"source": 1,
"time": 6.854456018518518
},
{
"source": 2,
"time": 6.320115740740741
},
{
"source": 3,
"time": 5.962986111111111
}
]
}
And I have D3 code that draws this network:
<!DOCTYPE html xmlns:xlink="http://www.w3.org/1999/xlink">
<meta charset="utf-8">
<style>
// style here
</style>
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<div id="animviz"></div>
<script>
d3.json("post000.json", function(error, graph) {
var vv = window,
w = vv.innerWidth,
h = vv.innerHeight;
var svg = d3.select("#animviz")
.append("svg")
.attr("width", w)
.attr("height", h)
.append("g")
.call(d3.behavior.zoom().scaleExtent([0, 8]).on("zoom", zoom))
.append("g");
var color = d3.scale.category10();
var force = d3.layout.force()
.charge(-200)
.linkDistance(50)
.size([w, h]);
force
.nodes(graph.nodes)
.links(graph.links)
.start();
var link = svg.selectAll(".link")
.data(graph.links)
.enter().append("line")
.attr("class", "link")
.attr("transform", function(d) { return "translate(" + d + ")"; });
function zoom() {
svg.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
}
var myMouseoverFunction = function() {
var circle = d3.select(this);
circle.transition().duration(100)
.attr("r", 20 )
node.append("title")
.text(function(d) { return d.name});
}
var myMouseoutFunction = function() {
var circle = d3.select(this);
circle.transition().duration(500)
.attr("r", 10 );
}
var node = svg.selectAll(".node")
.data(graph.nodes)
.enter().append("circle")
.attr("class", "node")
.attr("r", 10)
.style("fill", function(d) { return color(d.group); })
.call(force.drag)
.on("mouseover", myMouseoverFunction)
.on("mouseout", myMouseoutFunction);
force.on("tick", function() {
link.attr("x1", function(d) { return d.source.x; })
.attr("y1", function(d) { return d.source.y; })
.attr("x2", function(d) { return d.target.x; })
.attr("y2", function(d) { return d.target.y; });
node.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; });
});
});
</script>
</body>
What I want is to draw this graph node by node according to time parameter (i.e. source: 1 should be drawn after 6.854456018518518 sec after node = 0 was drawn).
If it's not possible to draw them after special number of seconds, I'd like at least to draw them in order, so that I can see how nodes appear one after the other.
I checked similar questions (here, here, and here) and this tutorial but wasn't able to solve my problem. Ideally I would love to have similar to this but for my data from json file and not in infinite loop.
How can I draw a graph stored in json node by node?
one way to achieve this is to create nodes with radius = 0, and then use delay for showing each node (giving it radius = 12):
node.attr("r", 0);
var totalDelay = 0;
node
.transition()
.duration(0)
.delay(function(d, i) {
totalDelay += graph.time[i].time * 1000;
return totalDelay
})
.attr("r", 12);
See this jsFiddle
The problem with this solution is that all the links appear immediately, without waiting for its nodes to appear.
Added:
to deal with links problem, you may want to redraw graph after each interval, every time adding one node, and calculating the array of links for the nodes, displayed in each iteration:
var i = 0;
function redraw() {
if (i === graph.time.length) return;
setTimeout(function() {
var nodes = graph.nodes.slice(0, i + 1);
var links = graph.links.filter(function(link) {
return (link.source <= i && link.target <= i)
});
draw(nodes, links);
i += 1;
redraw();
}, graph.time[i].time * 1000);
}
See improved jsFiddle
For big datasets might be more efficient to keep the same nodes array and do nodes.push(graph.nodes[i]), instead of creating a new array in each iteration.

Categories