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
Related
Below is my code snippet where I would like to have multiple arrowhead markers at the end of my path pointing it towards the children instead of the parent. But I have been able to point a single arrow marker towards my parent but not a child. Please let me know what needs to be done
The below image shows the arrow pointing towards the parent. I would like to have it point towards children.
The below image shows is the plain code without arrow markers
I would like to see the below image like type functionality to my code
<!DOCTYPE html>
<meta charset="UTF-8">
<style>
.node circle {
fill: #fff;
stroke: steelblue;
stroke-width: 3px;
}
.node text {
font: 12px sans-serif;
}
.link {
fill: none;
stroke: #ccc;
stroke-width: 2px;
}
.link path {
fill: none;
stroke: #ccc;
stroke-width: 2px;
}
.link text {
font: 12px sans-serif;
stroke: #333;
stroke-width: 1;
}
</style>
<body>
<!-- load the d3.js library -->
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
var treeData =
{
"name": "Top Level",
"linkname": "null",
"children": [
{
"name": "Level 2: A",
"linkname": "Link_1",
"children": [
{ "name": "Son of A", "linkname": "Link_2.1" },
{ "name": "Daughter of A", "linkname": "Link_2.2" }
]
},
{ "name": "Level 2: B", "linkname": "Link_3", }
]
};
// Set the dimensions and margins of the diagram
var margin = { top: 20, right: 90, bottom: 30, left: 90 },
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
// 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("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 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
}
}
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.filter(function (d) {
return (!d.data.type || d.data.type !== 'data');
}).append('circle')
.attr('class', 'node')
.attr('r', 1e-6)
.style("fill", function (d) {
return d._children ? "lightsteelblue" : "#fff";
});
nodeEnter.filter(function (d) {
return (d.data.type && d.data.type === 'data');
}).append('rect')
.attr('class', 'node')
.attr('width', 20)
.attr('height', 20)
.attr('y', -10)
.attr('x', -10)
.style("fill", function (d) {
return d._children ? "lightsteelblue" : "#fff";
});
// Add labels for the nodes
nodeEnter.append('text')
.attr("dy", "2em")
.attr("x", function (d) {
return d.children || d._children ? 13 : 13;
})
.attr("text-anchor", function (d) {
return d.children || d._children ? "start" : "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('g.link')
.data(links, function (d) {
return d.id;
});
// Enter any new links at the parent's previous position.
var linkEnter = link.enter().insert('g', 'g')
.attr("class", "link");
linkEnter.append('text')
.attr("class","linkLabels")
.text(function (d, i) {
if (d.parent && d.parent.children.length > 1) {
if (!d.parent.index) d.parent.index = 0;
return d.data.linkname;
}
})
.attr("opacity",0)
.attr('dy', "-1em");
linkEnter.append('path')
.attr('d', function (d) {
var o = {
x: source.x0,
y: source.y0
}
return diagonal(o, o)
})
.on("mouseover", function(){
d3.select(this.parentNode).select("text").attr("opacity",1);
})
.on("mouseleave", function(){
d3.select(this.parentNode).select("text").attr("opacity",0);
})
// UPDATE
var linkUpdate = linkEnter.merge(link);
// Transition back to the parent element position
linkUpdate.select('path').transition()
.duration(duration)
.attr('d', function (d) {
return diagonal(d, d.parent)
});
linkUpdate.select('text').transition()
.duration(duration)
.attr('transform', function (d) {
if (d.parent) {
return 'translate(' + ((d.parent.y + d.y) / 2) + ',' + ((d.parent.x + d.x) / 2) + ')'
}
})
// Remove any exiting links
link.exit().each(function (d) {
d.parent.index = 0;
})
var linkExit = link.exit()
.transition()
.duration(duration);
linkExit.select('path')
.attr('d', function (d) {
var o = {
x: source.x,
y: source.y
}
return diagonal(o, o)
})
linkExit.select('text')
.style('opacity', 0);
linkExit.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);
}
}
</script>
</body>
Probably not 100% what you want, but can be a starting point for you, basically I did it by manipulating marker attributes .attr("orient", "auto-start-reverse") check the MDN documentation for more information about this attribute, also refX, and refY finally the path attribute I used marker-start instead of marker-end since in this example the path drawn from the children to the parent not the reverse
if this is not what you want, there is another stack-overflow question that is a bit close to your case but its not a tree but can help:
Align Marker on node edges D3 Force Layout
var treeData = {
name: "Top Level",
linkname: "null",
children: [
{
name: "Level 2: A",
linkname: "Link_1",
children: [
{ name: "Son of A", linkname: "Link_2.1" },
{ name: "Daughter of A", linkname: "Link_2.2" }
]
},
{ name: "Level 2: B", linkname: "Link_3" }
]
};
// Set the dimensions and margins of the diagram
var margin = { top: 20, right: 90, bottom: 30, left: 90 },
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
// 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("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 + ")");
svg
.append("svg:defs")
.selectAll("marker")
.data(["end"]) // Different link/path types can be defined here
.enter()
.append("svg:marker") // This section adds in the arrows
.attr("id", String)
.attr("viewBox", "0 -5 10 10")
.attr("refX", 15)
.attr("refY", 0)
.attr("markerWidth", 6)
.attr("markerHeight", 6)
.attr("orient", "auto-start-reverse")
.append("svg:path")
.attr("d", "M0,-5L10,0L0,5");
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;
}
}
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
.filter(function(d) {
return !d.data.type || d.data.type !== "data";
})
.append("circle")
.attr("class", "node")
.attr("r", 1e-6)
.style("fill", function(d) {
return d._children ? "lightsteelblue" : "#fff";
});
nodeEnter
.filter(function(d) {
return d.data.type && d.data.type === "data";
})
.append("rect")
.attr("class", "node")
.attr("width", 20)
.attr("height", 20)
.attr("y", -10)
.attr("x", -10)
.style("fill", function(d) {
return d._children ? "lightsteelblue" : "#fff";
});
// Add labels for the nodes
nodeEnter
.append("text")
.attr("dy", "2em")
.attr("x", function(d) {
return d.children || d._children ? 13 : 13;
})
.attr("text-anchor", function(d) {
return d.children || d._children ? "start" : "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("g.link").data(links, function(d) {
return d.id;
});
// Enter any new links at the parent's previous position.
var linkEnter = link
.enter()
.insert("g", "g")
.attr("class", "link");
linkEnter
.append("text")
.attr("class", "linkLabels")
.text(function(d, i) {
if (d.parent && d.parent.children.length > 1) {
if (!d.parent.index) d.parent.index = 0;
return d.data.linkname;
}
})
.attr("opacity", 0)
.attr("dy", "-1em");
linkEnter
.append("path")
.attr("d", function(d) {
var o = {
x: source.x0,
y: source.y0
};
return diagonal(o, o);
})
.on("mouseover", function() {
d3.select(this.parentNode)
.select("text")
.attr("opacity", 1);
})
.on("mouseleave", function() {
d3.select(this.parentNode)
.select("text")
.attr("opacity", 0);
})
.attr("stroke-linecap", "round")
.attr("marker-start", "url(#end)");
// UPDATE
var linkUpdate = linkEnter.merge(link);
// Transition back to the parent element position
linkUpdate
.select("path")
.transition()
.duration(duration)
.attr("d", function(d) {
return diagonal(d, d.parent);
});
linkUpdate
.select("text")
.transition()
.duration(duration)
.attr("transform", function(d) {
if (d.parent) {
return (
"translate(" +
(d.parent.y + d.y) / 2 +
"," +
(d.parent.x + d.x) / 2 +
")"
);
}
});
// Remove any exiting links
link.exit().each(function(d) {
d.parent.index = 0;
});
var linkExit = link
.exit()
.transition()
.duration(duration);
linkExit.select("path").attr("d", function(d) {
var o = {
x: source.x,
y: source.y
};
return diagonal(o, o);
});
linkExit.select("text").style("opacity", 0);
linkExit.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: 2px;
}
.link path {
fill: none;
stroke: #ccc;
stroke-width: 2px;
}
.link text {
font: 12px sans-serif;
stroke: #333;
stroke-width: 1;
}
<!DOCTYPE html>
<meta charset="UTF-8">
<style>
</style>
<body>
<!-- load the d3.js library -->
<script src="https://d3js.org/d3.v4.min.js"></script>
</body>
I am working with d3 force diagrams at the moment, I am wanting to plot my child nodes around a parent node equally spaced, so for example if I have a parent node, and 4 linked child nodes, I would want each those node positioned at 90 degree intervals? Is that possible?
Here is my current force code,
app.force
.nodes(nodes)
.links(app.edges)
.on("tick", tick)
.start();
function tick(e) {
// console.log(link);
var k = 6 * e.alpha;
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; });
linkText
.attr("x", function(d) {
return ((d.source.x + d.target.x)/2);
})
.attr("y", function(d) {
return ((d.source.y + d.target.y)/2);
});
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 })
}
app.force = d3.layout.force()
.charge(-300)
.linkDistance(85)
.size([width, height]);
//Where we will draw our visualisation
app.svg = d3.select(".visualisation").append("svg")
.attr('width', width)
.attr('height', height);
d3.layout.force() was not created with such customisation in mind. Of course you can set some parameters, but most of the positions are automatic calculated, and changing them can be very difficult (unless you create your own force function). Version 4.x is better in that matter, but not much.
In your specific case, you can set a very high (mathematically speaking, "very low", since it is negative) charge:
var force = d3.layout.force()
.charge(-3000)
But even doing that the angles are not exactly right angles, and they vary: if you click "run snippet" you can get an almost perfect cross, but if you click it again it's not that perfect the next time. And it will not work as expected if you have data with several levels of depth.
Here is a demo:
<script src="https://d3js.org/d3.v2.min.js?2.9.3"></script>
<style>
.link {
stroke: #aaa;
}
.node text {
stroke: #333;
cursor: pointer;
}
.node circle {
stroke: #fff;
stroke-width: 3px;
fill: #555;
}
</style>
<body>
<script>
var width = 400,
height = 300
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
var force = d3.layout.force()
.distance(50)
.charge(-3000)
.size([width, height]);
var json = {
"nodes": [{
"name": "node1"
}, {
"name": "node2"
}, {
"name": "node3"
}, {
"name": "node4"
}, {
"name": "node5"
}],
"links": [{
"source": 0,
"target": 1
}, {
"source": 0,
"target": 2
}, {
"source": 0,
"target": 3
}, {
"source": 0,
"target": 4
}]
};
force
.nodes(json.nodes)
.links(json.links)
.start();
var link = svg.selectAll(".link")
.data(json.links)
.enter().append("line")
.attr("class", "link")
.style("stroke-width", 2);
var node = svg.selectAll(".node")
.data(json.nodes)
.enter().append("g")
.attr("class", "node")
.call(force.drag);
node.append("circle")
.attr("r", 8);
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 + ")";
});
});
</script>
I am trying to add text that appears next to each edge/link on mouseover, to the below d3 graph. I would also like to add a filtering capability that allows a user to click on a node, and only see its direct connections. Finally I would like to add a zoom functionality.
I have tried adding to the links directly by .append("text") (see below) and trying other things from various other posts (see here for example), but am not having any success. (Whenever I add an attribute to the link to hold the next, the links themselves disappear). Does anyone know how I can proceed here?
For the filtering and zooming I don't have a clue how to start, so any tips here would also be great.
<!DOCTYPE html>
<meta charset="utf-8">
<body>
<style>
.link {
stroke: #666;
opacity: 0.6;
stroke-width: 1.5px;
}
.node circle {
stroke: #fff;
opacity: 0.6;
stroke-width: 1.5px;
}
text {
font: 7px serif;
opacity: 0.6;
pointer-events: none;
}
</style>
<script src=http://d3js.org/d3.v3.min.js></script>
<script>
var links = [ { "source" : "A", "target" : "B" }, { "source" : "A", "target" : "C" }, { "source" : "A", "target" : "D" }, { "source" : "A", "target" : "J" }, { "source" : "B", "target" : "E" }, { "source" : "B", "target" : "F" }, { "source" : "C", "target" : "G" }, { "source" : "C", "target" : "H" }, { "source" : "D", "target" : "I" } ] ;
var nodes = {}
// Compute the distinct nodes from the links.
links.forEach(function(link) {
link.source = nodes[link.source] ||
(nodes[link.source] = {name: link.source});
link.target = nodes[link.target] ||
(nodes[link.target] = {name: link.target});
link.value = +link.value;
});
var width = 400
height = 250;
var force = d3.layout.force()
.nodes(d3.values(nodes))
.links(links)
.size([width, height])
.linkDistance(50)
.charge(-500)
.on("tick", tick)
.start();
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
var link = svg.selectAll(".link")
.data(force.links())
.enter().append("line")
.attr("class", "link");
link.append("text")
.attr("font-family", "Arial, Helvetica, sans-serif")
.attr("fill", "Black")
.style("font", "normal 12px Arial")
.attr("transform", function(d) {
return "translate(" +
((d.source.y + d.target.y)/2) + "," +
((d.source.x + d.target.x)/2) + ")";
})
.attr("dy", ".35em")
.attr("text-anchor", "middle")
.text(function(d) {
console.log(d.target.rule);
return d.target.rule;
});
var node = svg.selectAll(".node")
.data(force.nodes())
.enter().append("g")
.attr("class", "node")
.on("mouseover", mouseover)
.on("mouseout", mouseout)
.on("click", click)
.on("dblclick", dblclick)
.call(force.drag);
node.append("circle")
.attr("r", 8)
.style("fill", "#3182bd");
node.append("text")
.attr("x", 12)
.attr("dy", ".35em")
.style("fill", "#3182bd")
.text(function(d) { return d.name; });
function tick() {
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 mouseover() {
d3.select(this).select("circle").transition()
.duration(750)
.attr("r", 16);
}
function mouseout() {
d3.select(this).select("circle").transition()
.duration(750)
.attr("r", 8);
}
// action to take on mouse click
function click() {
d3.select(this).select("text").transition()
.duration(750)
.attr("x", 22)
.style("stroke-width", ".5px")
.style("opacity", 1)
.style("fill", "#E34A33")
.style("font", "17.5px serif");
d3.select(this).select("circle").transition()
.duration(750)
.style("fill", "#E34A33")
.attr("r", 16)
}
// action to take on mouse double click
function dblclick() {
d3.select(this).select("circle").transition()
.duration(750)
.attr("r", 6)
.style("fill", "#E34A33");
d3.select(this).select("text").transition()
.duration(750)
.attr("x", 12)
.style("stroke", "none")
.style("fill", "#E34A33")
.style("stroke", "none")
.style("opacity", 0.6)
.style("font", "7px serif");
}
</script>
</body>
Didn't have enough room on the comments.
If you didnt want to add a div for you tooltip and just append text I would do it like this :
var links = svg.selectAll(".link")
.data(force.links())
.enter();
var link = links.append("line")
.attr("class", "link");
//and for text
var linkText = links.append('text') //not append to the link directly but the link container
//and so on
As I have mentioned, you cant append text to a shape so you have to append it to the element the shape is appended to.
I was working on the chord diagram and I am unable to show the total value for each product. I want it to display the total count for each category when I hover over the radial arc for a particular category. The example is taken from http://bl.ocks.org/mbostock/4062006. Any prompt help would be greatly appreciated. Thank you :)
<!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
font: 10px sans-serif;
}
.chord path {
fill-opacity: .67;
stroke: #000;
stroke-width: .5px;
}
</style>
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
// From http://mkweb.bcgsc.ca/circos/guide/tables/
var matrix = [
[11975, 5871, 8916, 2868],
[ 1951, 10048, 2060, 6171],
[ 8010, 16145, 8090, 8045],
[ 1013, 990, 940, 6907]
];
var chord = d3.layout.chord()
.padding(.05)
.sortSubgroups(d3.descending)
.matrix(matrix);
var width = 960,
height = 500,
innerRadius = Math.min(width, height) * .41,
outerRadius = innerRadius * 1.1;
var fill = d3.scale.ordinal()
.domain(d3.range(4))
.range(["#000000", "#FFDD89", "#957244", "#F26223"]);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
svg.append("g").selectAll("path")
.data(chord.groups)
.enter().append("path")
.style("fill", function(d) { return fill(d.index); })
.style("stroke", function(d) { return fill(d.index); })
.attr("d", d3.svg.arc().innerRadius(innerRadius).outerRadius(outerRadius))
.on("mouseover", fade(.1))
.on("mouseout", fade(1));
var ticks = svg.append("g").selectAll("g")
.data(chord.groups)
.enter().append("g").selectAll("g")
.data(groupTicks)
.enter().append("g")
.attr("transform", function(d) {
return "rotate(" + (d.angle * 180 / Math.PI - 90) + ")"
+ "translate(" + outerRadius + ",0)";
});
ticks.append("line")
.attr("x1", 1)
.attr("y1", 0)
.attr("x2", 5)
.attr("y2", 0)
.style("stroke", "#000");
ticks.append("text")
.attr("x", 8)
.attr("dy", ".35em")
.attr("transform", function(d) { return d.angle > Math.PI ? "rotate(180)translate(-16)" : null; })
.style("text-anchor", function(d) { return d.angle > Math.PI ? "end" : null; })
.text(function(d) { return d.label; });
svg.append("g")
.attr("class", "chord")
.selectAll("path")
.data(chord.chords)
.enter().append("path")
.attr("d", d3.svg.chord().radius(innerRadius))
.style("fill", function(d) { return fill(d.target.index); })
.style("opacity", 1)
.append("svg:title").text(function(d, i) { return "Value : " + d.source.value });;
// Returns an array of tick angles and labels, given a group.
function groupTicks(d) {
var k = (d.endAngle - d.startAngle) / d.value;
return d3.range(0, d.value, 1000).map(function(v, i) {
return {
angle: v * k + d.startAngle,
label: i % 5 ? null : v / 1000 + "k"
};
});
}
// Returns an event handler for fading a given chord group.
function fade(opacity) {
return function(g, i) {
svg.selectAll(".chord path")
.filter(function(d) { return d.source.index != i && d.target.index != i; })
.transition()
.style("opacity", opacity);
};
}
</script>
`
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.