Network of pie charts with legend or tooltip? - d3.js

I am working on plotting a network of pie charts and I have been working all weekend to try and put either a legend or a tooltip for each pie wedge. Here is my working Fiddle without labels and I have pasted the code below. How do I either put in the group numbers or labels from the labels variable?
Thanks in advance!!
Code:
<!DOCTYPE html>
<html lang="en">
<head>
<script type="text/javascript" src="http://d3js.org/d3.v3.min.js"></script>
<style>
.node {
stroke: #fff;
stroke-width: 1.5px;
}
.link {
stroke: #808080;
stroke-opacity: .6;
}
</style>
</head>
<body>
<script type="text/javascript">
graph = { "nodes":[{"proportions": [
{"group":1, "value": 25 },
{"group":2, "value": 0 },
{"group":3, "value": 0 },
{"group":4, "value": 0 }],"radius":25,"x":1000,"y":262.5},{"proportions": [
{"group":1, "value": 0 },
{"group":2, "value": 25 },
{"group":3, "value": 0 },
{"group":4, "value": 0 }],"radius":25,"x":191.3501125,"y":70.44565171875},{"proportions": [
{"group":1, "value": 0 },
{"group":2, "value": 0 },
{"group":3, "value": 25 },
{"group":4, "value": 0 }],"radius":25,"x":25,"y":30.9375},{"proportions": [
{"group":1, "value": 0 },
{"group":2, "value": 0 },
{"group":3, "value": 0 },
{"group":4, "value": 25 }],"radius":25,"x":833.572375,"y":222.9734390625}],"links": [{ "source":0, "target":1, "length":900, "width":9},
{ "source":0, "target":3, "length":900, "width":9},
{ "source":1, "target":2, "length":900, "width":9},
{ "source":2, "target":3, "length":900, "width":9}]
}
var labels = ['mycave1','mycave2','mycave3','mycave4'];
var width = 4000,
height = 1000,
radius = 25,
color = d3.scale.category10();
var pie = d3.layout.pie()
.sort(null)
.value(function(d) { return d.value; });
var arc = d3.svg.arc()
.outerRadius(radius)
.innerRadius(0);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
var force = d3.layout.force()
.charge(-120)
.linkDistance(4 * radius)
.size([width, height]);
force.nodes(graph.nodes)
.links(graph.links)
.start();
var link = svg.selectAll(".link")
.data(graph.links)
.enter().append("line")
.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; })
.attr("class", "link");
var node = svg.selectAll(".node")
.data(graph.nodes)
.enter().append("g")
.attr("class", "node")
.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"});
node.each(function(d){
arc = arc.outerRadius(d.radius);
d3.select(this)
.selectAll("path")
.data(function(d){ return pie(d.proportions); })
.enter().append("svg:path")
.attr("d", arc)
.style("fill", function(d,i){ return color(d.data.group) });
});
</script>
</body>
</html>

For tooltips, just append a title element to each path.
.append("svg:title")
.text(function(d) {return "Group "+d.data.group;});
Here's a fiddle: http://jsfiddle.net/ctsprjyq/18/
If you want the corresponding labels instead (Group 1=mycave1, Group 2=mycave2, etc.), just use the group value as an index to the labels array.
.append("svg:title")
.text(function(d) {return labels[d.data.group-1];});

Related

D3.js various link distance

I am in need to implement a various link distance. I added a function call during the initialization of the force / simulation. Still, it looks like the settings are ignored. I want to add another node very close, that barely a hair would fit between. Is this even possible?
I noticed a similar question on stackoverflow but this requires another plugin, furthermore this question was created 4 years ago. Maybe vanilla d3.js already added the feature.
var graph = {
"nodes": [
{
"id": 0
},
{
"id": 1
},
{
"id": 2
},
{
"id": 3
}
],
"links": [
{
"source": 0,
"target": 1,
"distance": 0.1
},
{
"source": 0,
"target": 2,
"distance": 150
},
{
"source": 0,
"target": 3,
"distance": 20
}
]
}
var svg = d3.select("svg")
.attr("class", "canvas")
.attr("width", window.innerWidth)
.attr("height", window.innerHeight)
.call(d3.zoom().on("zoom", function (event) {
svg.attr("transform", event.transform)
}))
.append("g")
var linksContainer = svg.append("g").attr("class", linksContainer)
var nodesContainer = svg.append("g").attr("class", nodesContainer)
var sourceNode;
var force = d3.forceSimulation()
//.force("link", d3.forceLink().id(function (d) { return d.id }).distance(80))
.force("link", d3.forceLink().distance(linkDistance).strength(0.1))
.force("charge", d3.forceManyBody().strength(-100))
.force("center", d3.forceCenter(window.innerWidth / 2, window.innerHeight / 2))
.force("collision", d3.forceCollide().radius(90))
function linkDistance(d) {
console.log(d.distance)
return d.distance;
}
initialize()
function initialize() {
link = linksContainer.selectAll(".link")
.data(graph.links)
.join("line")
.attr("class", "link")
.style("stroke", "black")
.style("stroke-width", 1)
node = nodesContainer.selectAll(".node")
.data(graph.nodes, d => d.id)
.join("g")
.attr("class", "node")
.call(d3.drag()
.on("start", dragStarted)
.on("drag", dragged)
.on("end", dragEnded)
)
.on("click", addNode)
node.selectAll("circle")
.data(d => [d])
.join("circle")
.attr("r", 30)
.style("fill", "whitesmoke")
node.append("text")
.attr("dominant-baseline", "central")
.attr("text-anchor", "middle")
.attr("font-size", 15)
.attr("pointer-events", "none")
.text(function (d) {
return d.id
})
force
.nodes(graph.nodes)
.on("tick", ticked);
force
.force("link")
.links(graph.links)
}
function ticked() {
// update link positions
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;
});
// update node positions
node
.attr("transform", function (d) {
return "translate(" + d.x + ", " + d.y + ")";
});
}
function dragStarted(event, d) {
if (!event.active) force.alphaTarget(0.3).restart();
d.fx = d.x;
d.fy = d.y;
PosX = d.x
PosY = d.y
}
function dragged(event, d) {
d.fx = event.x;
d.fy = event.y;
}
function dragEnded(event, d) {
if (!event.active) force.alphaTarget(0);
d.fx = undefined;
d.fy = undefined;
}
function addNode(event, d) {
const nodeId = graph.nodes.length
graph.nodes.push({id: nodeId})
graph.links.push({source: d, target: nodeId, distance: 1})
initialize()
}
body {
height: 100%;
background: #e6e7ee;
overflow: hidden;
margin: 0px;
}
.faded {][1]
opacity: 0.1;
transition: 0.3s opacity;
}
.highlight {
opacity: 1;
}
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- d3.js framework -->
<script src="https://d3js.org/d3.v6.js"></script>
<!-- fontawesome stylesheet https://fontawesome.com/ -->
<script src="https://kit.fontawesome.com/39094309d6.js" crossorigin="anonymous"></script>
</head>
<style>
</style>
<body>
<svg id="svg"></svg>
</body>
The distance function is correctly set and specified.
Problem comes from the forceCollide, which is set to a radius of 90, and pushes the nodes apart regardless of specified distance.
Decreasing the radius of forceCollide fixes the problem, as illustrated below :)
var graph = {
"nodes": [
{
"id": 0
},
{
"id": 1
},
{
"id": 2
},
{
"id": 3
}
],
"links": [
{
"source": 0,
"target": 1,
"distance": 0.1
},
{
"source": 0,
"target": 2,
"distance": 150
},
{
"source": 0,
"target": 3,
"distance": 20
}
]
}
var svg = d3.select("svg")
.attr("class", "canvas")
.attr("width", window.innerWidth)
.attr("height", window.innerHeight)
.call(d3.zoom().on("zoom", function (event) {
svg.attr("transform", event.transform)
}))
.append("g")
var linksContainer = svg.append("g").attr("class", linksContainer)
var nodesContainer = svg.append("g").attr("class", nodesContainer)
var sourceNode;
var force = d3.forceSimulation()
//.force("link", d3.forceLink().id(function (d) { return d.id }).distance(80))
.force("link", d3.forceLink().distance(linkDistance).strength(.1))
.force("charge", d3.forceManyBody().strength(-100))
.force("center", d3.forceCenter(window.innerWidth / 2, window.innerHeight / 2))
.force("collision", d3.forceCollide().radius(9))
function linkDistance(d) {
console.log('distance: ', d.distance)
return d.distance;
}
initialize()
function initialize() {
link = linksContainer.selectAll(".link")
.data(graph.links)
.join("line")
.attr("class", "link")
.style("stroke", "black")
.style("stroke-width", 1)
node = nodesContainer.selectAll(".node")
.data(graph.nodes, d => d.id)
.join("g")
.attr("class", "node")
.call(d3.drag()
.on("start", dragStarted)
.on("drag", dragged)
.on("end", dragEnded)
)
.on("click", addNode)
node.selectAll("circle")
.data(d => [d])
.join("circle")
.attr("r", 30)
.style("fill", "whitesmoke")
node.append("text")
.attr("dominant-baseline", "central")
.attr("text-anchor", "middle")
.attr("font-size", 15)
.attr("pointer-events", "none")
.text(function (d) {
return d.id
})
force
.nodes(graph.nodes)
.on("tick", ticked);
force
.force("link")
.links(graph.links)
}
function ticked() {
// update link positions
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;
});
// update node positions
node
.attr("transform", function (d) {
return "translate(" + d.x + ", " + d.y + ")";
});
}
function dragStarted(event, d) {
if (!event.active) force.alphaTarget(0.3).restart();
d.fx = d.x;
d.fy = d.y;
PosX = d.x
PosY = d.y
}
function dragged(event, d) {
d.fx = event.x;
d.fy = event.y;
}
function dragEnded(event, d) {
if (!event.active) force.alphaTarget(0);
d.fx = undefined;
d.fy = undefined;
}
function addNode(event, d) {
const nodeId = graph.nodes.length
graph.nodes.push({id: nodeId})
graph.links.push({source: d, target: nodeId, distance: 1})
initialize()
}
body {
height: 100%;
background: #e6e7ee;
overflow: hidden;
margin: 0px;
}
.faded {][1]
opacity: 0.1;
transition: 0.3s opacity;
}
.highlight {
opacity: 1;
}
<!-- d3.js framework -->
<script src="https://d3js.org/d3.v6.js"></script>
<!-- fontawesome stylesheet https://fontawesome.com/ -->
<script src="https://kit.fontawesome.com/39094309d6.js" crossorigin="anonymous"></script>
<svg id="svg"></svg>

D3 js - is it possible to draw straight lines between tree nodes?

I have created a horizontal tree diagram as shown in below image. I want straight lines between nodes. The curved lines between nodes are default in d3 js. I saw some answers on google for this but did not found any satisfactory result. So is it possible to draw straight lines between nodes in d3 js? If yes then how can I do that?
enter image description here
<!DOCTYPE html>
<html lang="en">
<head>
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.3.1/css/all.css" integrity="sha384-mzrmE5qonljUremFsqc01SB46JvROS7bZs3IO2EmfFsd15uHvIt+Y8vEf7N7fWAU" crossorigin="anonymous">
<script src="https://d3js.org/d3.v3.min.js"></script>
<style>
.node circle {
fill: #ff9900;
stroke: #ff9900;
stroke-width: 1px;
}
.node text {
font: 16px sans-serif;
}
.link {
fill: none;
stroke: #ccc;
stroke-width: 2px;
}
</style>
</head>
<body>
<script language="javascript">
var treeData = [{
"name": "1",
"parent": "null",
"children": [{
"name": "2",
"parent": "Persons",
"children": [{
"name": "3",
"parent": "Country of residence"
}, {
"name": "4",
"parent": "Country of residence"
}, {
"name": "5",
"parent": "Country of residence"
}, {
"name": "6",
"parent": "Country of residence"
}]
}]
}];
// ************** 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 line = d3.svg.line()
.x(function(d) {
return d.lx;
})
.y(function(d) {
return d.ly;
});
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 * 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", 40)
.style("fill", "#ff9900");
// append icon inside circle
nodeEnter.append("image")
.attr("xlink:href", "http://localhost/d3/user2.jpg")
.attr("x", "-18px")
.attr("y", "-18px")
.attr("width", "35px")
.attr("height", "35px");
nodeEnter.append("text")
.attr("x", function(d) {
return d.children || d._children ? -40 : -50;
})
.attr("y", function(d) {
return d.children || d._children ? 55 : 55;
})
.attr("dy", ".35em")
.attr("text-anchor", function(d) {
return d.children || d._children ? "start" : "start";
})
.text(function(d) {
return d.name;
})
.style("fill-opacity", 1);
// Declare the links…
var link = svg.selectAll("path.link")
.data(links, function(d) {
return d.target.id;
});
// Enter the links.
link.enter().insert("path", "g")
.attr("class", "link")
.attr("d", diagonal);
}
</script>
</body>
</body>
</html>
Define your line :
var line = d3.svg.line()
.x(function(d) {
return d.y; // because tree is horizontal
})
.y(function(d) {
return d.x; // because tree is horizontal
});
Change your links function to this because d3.svg.line() takes array of points as argument
Hope this helps
// Enter the links.
link.enter().insert("path", "g")
.attr("class", "link")
.attr("d", function(d) {
return line([d.source, d.target]);
});
<!DOCTYPE html>
<html lang="en">
<head>
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.3.1/css/all.css" integrity="sha384-mzrmE5qonljUremFsqc01SB46JvROS7bZs3IO2EmfFsd15uHvIt+Y8vEf7N7fWAU" crossorigin="anonymous">
<script src="https://d3js.org/d3.v3.min.js"></script>
<style>
.node circle {
fill: #ff9900;
stroke: #ff9900;
stroke-width: 1px;
}
.node text {
font: 16px sans-serif;
}
.link {
fill: none;
stroke: #ccc;
stroke-width: 2px;
}
</style>
</head>
<body>
<script language="javascript">
var treeData = [{
"name": "1",
"parent": "null",
"children": [{
"name": "2",
"parent": "Persons",
"children": [{
"name": "3",
"parent": "Country of residence"
}, {
"name": "4",
"parent": "Country of residence"
}, {
"name": "5",
"parent": "Country of residence"
}, {
"name": "6",
"parent": "Country of residence"
}]
}]
}];
// ************** 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 line = d3.svg.line()
.x(function(d) {
return d.y;
})
.y(function(d) {
return 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];
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", 40)
.style("fill", "#ff9900");
// append icon inside circle
nodeEnter.append("image")
.attr("xlink:href", "http://localhost/d3/user2.jpg")
.attr("x", "-18px")
.attr("y", "-18px")
.attr("width", "35px")
.attr("height", "35px");
nodeEnter.append("text")
.attr("x", function(d) {
return d.children || d._children ? -40 : -50;
})
.attr("y", function(d) {
return d.children || d._children ? 55 : 55;
})
.attr("dy", ".35em")
.attr("text-anchor", function(d) {
return d.children || d._children ? "start" : "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", function(d) { return line([d.source, d.target])});
}
</script>
</body>
</body>
</html>

Release all sticky nodes

How can I modify the following example so that all sticky nodes are released when a button is pressed? I've seen this implemented for nodes to be released (unstuck) with a doubleclick (here), but I want to release them all at the same time (my plan is to include this in a restart() function for the graph).
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.links line {
stroke: #999;
stroke-opacity: 0.6;
}
.nodes circle {
stroke: #fff;
stroke-width: 1.5px;
}
</style>
<svg width="960" height="600"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
//create somewhere to put the force directed graph
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height");
var nodes_data = [{
"name": "Travis",
"sex": "M"
},
{
"name": "Rake",
"sex": "M"
},
{
"name": "Diana",
"sex": "F"
},
{
"name": "Rachel",
"sex": "F"
},
{
"name": "Shawn",
"sex": "M"
},
{
"name": "Emerald",
"sex": "F"
}
]
var links_data = [{
"source": "Travis",
"target": "Rake"
},
{
"source": "Diana",
"target": "Rake"
},
{
"source": "Diana",
"target": "Rachel"
},
{
"source": "Rachel",
"target": "Rake"
},
{
"source": "Rachel",
"target": "Shawn"
},
{
"source": "Emerald",
"target": "Rachel"
}
]
//set up the simulation
var simulation = d3.forceSimulation()
//add nodes
.nodes(nodes_data);
//add forces
//we're going to add a charge to each node
//also going to add a centering force
//and a link force
var link_force = d3.forceLink(links_data)
.id(function(d) {
return d.name;
});
simulation
.force("charge_force", d3.forceManyBody())
.force("center_force", d3.forceCenter(width / 2, height / 2))
.force("links", link_force);
//add tick instructions:
simulation.on("tick", tickActions);
//draw circles for the links
var node = svg.append("g")
.attr("class", "nodes")
.selectAll("circle")
.data(nodes_data)
.enter()
.append("circle")
.attr("r", 10)
.attr("fill", "red");
//draw lines for the links
var link = svg.append("g")
.attr("class", "links")
.selectAll("line")
.data(links_data)
.enter().append("line")
.attr("stroke-width", 2);
var drag_handler = d3.drag()
.on("start", drag_start)
.on("drag", drag_drag)
.on("end", drag_end);
//same as using .call on the node variable as in https://bl.ocks.org/mbostock/4062045
drag_handler(node)
//drag handler
//d is the node
function drag_start(d) {
if (!d3.event.active) simulation.alphaTarget(0.3).restart();
d.fx = d.x;
d.fy = d.y;
}
function drag_drag(d) {
d.fx = d3.event.x;
d.fy = d3.event.y;
}
function drag_end(d) {
if (!d3.event.active) simulation.alphaTarget(0);
d.fx = d.x;
d.fy = d.y;
}
function tickActions() {
//update circle positions each tick of the simulation
node
.attr("cx", function(d) {
return d.x;
})
.attr("cy", function(d) {
return d.y;
});
//update link positions
//simply tells one end of the line to follow one node around
//and the other end of the line to follow the other node around
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;
});
}
</script>
That's not a Bostock's bl.ocks, and there is no double click there to release the nodes.
Anyway, you just need to remove the fx and fy properties, and reheat the simulation again:
d3.select("button").on("click", function() {
node.each(function(d) {
d.fx = d.fy = null;
})
simulation.alphaTarget(0.3).restart();
})
I'm using alphaTarget because that's what you have in your code, but you should consider using alpha instead.
Here is the code with that change:
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.links line {
stroke: #999;
stroke-opacity: 0.6;
}
.nodes circle {
stroke: #fff;
stroke-width: 1.5px;
}
</style>
<button>Release</button>
<svg width="600" height="400"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
//create somewhere to put the force directed graph
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height");
var nodes_data = [{
"name": "Travis",
"sex": "M"
},
{
"name": "Rake",
"sex": "M"
},
{
"name": "Diana",
"sex": "F"
},
{
"name": "Rachel",
"sex": "F"
},
{
"name": "Shawn",
"sex": "M"
},
{
"name": "Emerald",
"sex": "F"
}
]
var links_data = [{
"source": "Travis",
"target": "Rake"
},
{
"source": "Diana",
"target": "Rake"
},
{
"source": "Diana",
"target": "Rachel"
},
{
"source": "Rachel",
"target": "Rake"
},
{
"source": "Rachel",
"target": "Shawn"
},
{
"source": "Emerald",
"target": "Rachel"
}
]
//set up the simulation
var simulation = d3.forceSimulation()
//add nodes
.nodes(nodes_data);
//add forces
//we're going to add a charge to each node
//also going to add a centering force
//and a link force
var link_force = d3.forceLink(links_data)
.id(function(d) {
return d.name;
});
simulation
.force("charge_force", d3.forceManyBody())
.force("center_force", d3.forceCenter(width / 2, height / 2))
.force("links", link_force);
//add tick instructions:
simulation.on("tick", tickActions);
//draw circles for the links
var node = svg.append("g")
.attr("class", "nodes")
.selectAll("circle")
.data(nodes_data)
.enter()
.append("circle")
.attr("r", 10)
.attr("fill", "red");
//draw lines for the links
var link = svg.append("g")
.attr("class", "links")
.selectAll("line")
.data(links_data)
.enter().append("line")
.attr("stroke-width", 2);
var drag_handler = d3.drag()
.on("start", drag_start)
.on("drag", drag_drag)
.on("end", drag_end);
//same as using .call on the node variable as in https://bl.ocks.org/mbostock/4062045
drag_handler(node)
//drag handler
//d is the node
function drag_start(d) {
if (!d3.event.active) simulation.alphaTarget(0.3).restart();
d.fx = d.x;
d.fy = d.y;
}
function drag_drag(d) {
d.fx = d3.event.x;
d.fy = d3.event.y;
}
function drag_end(d) {
if (!d3.event.active) simulation.alphaTarget(0);
d.fx = d.x;
d.fy = d.y;
}
function tickActions() {
//update circle positions each tick of the simulation
node
.attr("cx", function(d) {
return d.x;
})
.attr("cy", function(d) {
return d.y;
});
//update link positions
//simply tells one end of the line to follow one node around
//and the other end of the line to follow the other node around
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;
});
}
d3.select("button").on("click", function() {
node.each(function(d) {
d.fx = d.fy = null;
})
simulation.alphaTarget(0.3).restart();
})
</script>

D3 Adding text to Node SVG

I am trying to get the below code working with adding labels, still no luck, seems that my labels are written on top of the page instead of on the node itself. I think it is a binding issue with SVG.
I tried to add the title but I didn't manage to display it.
<!-- INITAL SETTINGS ------------------------------------------------------------------------------->
<!-------------------------------------------------------------------------------------------------->
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.links line {
stroke: #999;
stroke-opacity: 0.5;
}
.nodes circle {
stroke: #fff;
stroke-width: 1.2px;
}
</style>
<!-- the SVG viewport will be 1100px by 900px -->
<svg width="1100" height="900" viewbox="0 0 1100 900">
<text> </text>
<!-- SVG content drawn onto the SVG canvas -->
</svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
// DATA DEFINITIONS NODES & LINKS ------------------------------------------------------------------
// -------------------------------------------------------------------------------------------------
var graph = {
"nodes": [
{ "id": "xxx1", "group": 3 },
{ "id": "xxx2", "group": 1 },
{ "id": "xxx3", "group": 1 }
],
"links": [
{ "source": "xxx1", "target": "xxx2", "value": 42 },
{ "source": "xxx2", "target": "xxx3", "value": 6 },
{ "source": "xxx1", "target": "xxx3", "value": 13 }
]
};
// ---------------------------------------------------------------------------------------------------------
// ---------------------------------------------------------------------------------------------------------
// D3 CODE
// ---------------------------------------------------------------------------------------------------------
// ---------------------------------------------------------------------------------------------------------
// create the construct/canvas for drawing
var svg = d3.select("svg")
//set width/height
width = +svg.attr("width"),
height = +svg.attr("height")
preserveAspectRatio = "xMinYMin meet"
//color definition
var color = d3.scaleOrdinal(d3.schemeCategory20);
//Creates a new simulation with the specified array of nodes
var simulation = d3.forceSimulation()
.force("link", d3.forceLink().id(function (d) { return d.id; }))
.force("charge", d3.forceManyBody())
.force("center", d3.forceCenter(width/2.3, height/2.3))
// adding links
//The g element is a container element for grouping related graphics together
var link = svg.append("g")
.attr("class", "links")
.selectAll("line")
.data(graph.links)
.enter().append("line")
.attr("stroke-width", function (d) { return Math.sqrt(d.value); });
//adding nodes
var node = svg.append("g")
.attr("class", "nodes")
.selectAll("circle")
.data(graph.nodes)
.enter().append("circle")
.attr("r", 6)
.attr("fill", function (d) { return color(d.group); })
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended));
//var text = svg.append("g")
// .attr("class", "text")
// .selectAll("circle")
// .data(graph.nodes)
// .enter().append("id")
// .attr("cx", "12")
// .attr("cy", ".35em")
// .attr("text-anchor", "middle")
// .text(function(d) { return d.id });
// adding title
node.append("title")
.text(function (d) { return (d.id); });
var text = svg.append("g")
.attr("class", "labels")
.selectAll("text")
.data(graph.nodes)
.enter().append("text")
.attr("dx", 12)
.attr("dy", ".35em")
.text(function(d) { return d.id });
simulation
.nodes(graph.nodes)
.on("tick", ticked);
simulation.force("link")
.links(graph.links);
// ticked functionality
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("cx", function (d) { return d.x; })
.attr("cy", function (d) { return d.y; })
.call(force.drag);
}
//The data and the circle element’s position are updated during the drag event
// when dragged
function dragstarted(d) {
if (!d3.event.active) simulation.alphaTarget(0.3).restart();
d.fx = d.x;
d.fy = d.y;
}
// when dragged completed
function dragged(d) {
d.fx = d3.event.x;
d.fy = d3.event.y;
}
// when dragged ended
function dragended(d) {
if (!d3.event.active) simulation.alphaTarget(0);
d.fx = null;
d.fy = null;
}
You have to move the texts as well in the tick function:
text.attr("x", function(d) {
return d.x;
})
.attr("y", function(d) {
return d.y;
});
Here is your code with that change:
var graph = {
"nodes": [{
"id": "xxx1",
"group": 3
}, {
"id": "xxx2",
"group": 1
}, {
"id": "xxx3",
"group": 1
}
],
"links": [{
"source": "xxx1",
"target": "xxx2",
"value": 42
}, {
"source": "xxx2",
"target": "xxx3",
"value": 6
}, {
"source": "xxx1",
"target": "xxx3",
"value": 13
}]
};
// create the construct/canvas for drawing
var svg = d3.select("svg")
//set width/height
width = +svg.attr("width"),
height = +svg.attr("height")
preserveAspectRatio = "xMinYMin meet"
//color definition
var color = d3.scaleOrdinal(d3.schemeCategory20);
//Creates a new simulation with the specified array of nodes
var simulation = d3.forceSimulation()
.force("link", d3.forceLink().id(function(d) {
return d.id;
}))
.force("charge", d3.forceManyBody())
.force("center", d3.forceCenter(width / 2.3, height / 2.3))
// adding links
//The g element is a container element for grouping related graphics together
var link = svg.append("g")
.attr("class", "links")
.selectAll("line")
.data(graph.links)
.enter().append("line")
.attr("stroke-width", function(d) {
return Math.sqrt(d.value);
});
//adding nodes
var node = svg.append("g")
.attr("class", "nodes")
.selectAll("circle")
.data(graph.nodes)
.enter().append("circle")
.attr("r", 6)
.attr("fill", function(d) {
return color(d.group);
})
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended));
// adding title
node.append("title")
.text(function(d) {
return (d.id);
});
var text = svg.append("g")
.attr("class", "labels")
.selectAll("text")
.data(graph.nodes)
.enter().append("text")
.attr("dx", 12)
.attr("dy", ".35em")
.text(function(d) {
return d.id
});
simulation
.nodes(graph.nodes)
.on("tick", ticked);
simulation.force("link")
.links(graph.links);
// ticked functionality
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("cx", function(d) {
return d.x;
})
.attr("cy", function(d) {
return d.y;
});
text.attr("x", function(d) {
return d.x;
})
.attr("y", function(d) {
return d.y;
});
}
//The data and the circle element’s position are updated during the drag event
// when dragged
function dragstarted(d) {
if (!d3.event.active) simulation.alphaTarget(0.3).restart();
d.fx = d.x;
d.fy = d.y;
}
// when dragged completed
function dragged(d) {
d.fx = d3.event.x;
d.fy = d3.event.y;
}
// when dragged ended
function dragended(d) {
if (!d3.event.active) simulation.alphaTarget(0);
d.fx = null;
d.fy = null;
}
.links line {
stroke: #999;
stroke-opacity: 0.5;
}
.nodes circle {
stroke: #fff;
stroke-width: 1.2px;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<svg width="500" height="400" viewbox="0 0 500 400">
<text> </text>
</svg>

Deleting node from graph is wrong

Please see
my editable graphs model. I add function to delete node from graph by doubleclick on it. But when I double_click
a colored circle, disappeared not clicked node, but other node. What’s wrong?
I’m sorry
for my pure English.
<!DOCTYPE html>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<html>
<head>
<title>Animating Changes in Force Diagram</title>
<script src="JS/d3.v3.min.js"></script>
<style>
.node {}
</style>
</head>
<body>
<div id="div0"></div>
<script>
var graph= {
"nodes" : [
{"id": 1, "color": "red"},
{"id": 2, "color": "green"},
{"id": 3, "color": "grey"},
{"id": 4, "color": "blue"},
{"id": 5, "color": "orange"}
]
}
svg = d3.select("#div0")
.append("svg:svg")
.attr("width", 960)
.attr("height", 450)
.attr("id", "svg")
.attr("viewBox", "0 0 " + w + " " + h);
force = d3.layout.force()
.size([w, h])
.start();
force.on("tick", tick);
nodes = force.nodes();
node = svg.selectAll(".node")
.data(nodes);
for (i = 0; i < graph.nodes.length; i++) {
addNode(graph.nodes[i].id, graph.nodes[i].color);
}
restart();
function restart() {
node = node.data(nodes)
var g=node.enter().insert("circle")
.attr("class", "node")
.on("tick", tick)
.on("dblclick", dblClick)
.call(force.drag)
.attr("fill", function (d) {return d.color;})
.attr("r", 10)
.attr("style", "cursor: pointer");
node.exit().remove();
force.start();
}
function addNode(idd, color, xx, yy) {
nodes.push({id: idd, color: color});
}
function dblClick(d, i) {
nodes.splice(i, 1);
restart();
}
function tick() {
node.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; }); //
}
</script>
</body>
</html>
The issue is with the Enter, Update and Exit functionalities in your code. I have resolved the issue and added proper comments in the code. Hope this helps.
var w = 500,
h = 300;
var graph = {
"nodes": [{
"id": 1,
"color": "red"
},
{
"id": 2,
"color": "green"
},
{
"id": 3,
"color": "grey"
},
{
"id": 4,
"color": "blue"
},
{
"id": 5,
"color": "orange"
}
]
}
var svg = d3.select("#div0")
.append("svg:svg")
.attr("width", 960)
.attr("height", 450)
.attr("id", "svg")
.attr("viewBox", "0 0 " + w + " " + h);
force = d3.layout.force()
.size([w, h])
.nodes(graph.nodes)
.on("tick", tick)
.start();
restart();
function restart() {
var node = svg.selectAll(".node").data(graph.nodes)
//Add new nodes
node.enter().insert("circle")
.attr("class", "node")
.call(force.drag)
.on("tick", tick)
.on("dblclick", dblClick)
.attr("r", 10)
.attr("style", "cursor: pointer");
//Update nodes
svg.selectAll(".node")
.attr("fill", function(d) {
return d.color;
});
//Delete nodes
node.exit().remove();
force.start();
}
function dblClick(d, i) {
graph.nodes.splice(i, 1);
restart();
}
function tick() {
svg.selectAll(".node").attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
});
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<div id="div0"></div>

Resources