D3 curved link with arrow not appearing on screen - javascript

I am trying to add the curved links with the arrow as per this d3noob's block to my codepen.
After adding 2 nodes (Add Node button) and selecting the source node and target node from the select boxes when I press the Add Link button it does not show the link, however, the nodes readjust on the screen giving an idea that a link has been created.
I added the following code (including some variable definitions in codepen)
path.attr("d", function(d) {
var dx = d.target.x - d.source.x,
dy = d.target.y - d.source.y,
dr = Math.sqrt(dx * dx + dy * dy);
return "M" +
d.source.x + "," +
d.source.y + "A" +
dr + "," + dr + " 0 0,1 " +
d.target.x + "," +
d.target.y;
});
it was earlier :
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; });

It's because you haven't actually created any objects to change the attributes of, you tried to create the paths off of the force links at the top of your file, before any links had actually been created. You needed to create the paths based on the links in your 'restart' function and then update their attributes in the 'tick' function. Here's a codepen: https://codepen.io/anon/pen/RBazVp?editors=0010
Here's the relevant changes:
function restart() {
...
...
link = link.data(links);
link.enter()
.append('path')
.attr('class', 'link');
link.exit().remove();
force.start();
...
...
}
function tick() {
link.attr("d", function(d) {
var dx = d.target.x - d.source.x,
dy = d.target.y - d.source.y,
dr = Math.sqrt(dx * dx + dy * dy);
return (
"M" +
d.source.x +
"," +
d.source.y +
"A" +
dr +
"," +
dr +
" 0 0,1 " +
d.target.x +
"," +
d.target.y
);
});
...
...
}
To add arrows to the links:
var arrows = svg.append("defs")
.selectAll("marker")
.data(["arrow"])
.enter()
.append("marker")
.attr("id", String)
.attr("viewBox", "0 -5 10 10")
.attr("refX", 15)
.attr("refY", -1.5)
.attr("markerWidth", 6)
.attr("markerHeight", 6)
.attr("orient", "auto")
.append("path")
.attr("d", "M0,-5L10,0L0,5");
function restart() {
...
...
link = link.data(links);
link.enter()
.append('path')
.attr('class', 'link')
.attr('marker-end', 'url(#arrow)');
link.exit().remove();
force.start();
...
...
}
Interestingly, you only need to define and create the arrow once, and changing the 'marker-end' attribute will automatically generate another copy of the arrow. Pretty neat!

Related

D3 force layout - Links exceed the boundaries of the container, while dragging the nodes

I created a d3-force layout. The nodes are allowed to be dragged within a boundary box only. When the nodes are close to the boundaries, the nodes stay fixed at their positions, according to the following functions:
function boundX(x) {
return Math.max(0, Math.min(width - (nodeWidth+padding), x));
}
function boundY(y){
return Math.max(0, Math.min(height - (nodeHeight+padding), y))
}
The nodes are connected via polylines. Each polyline is characterized by two segments. The first segment is defined by the source node's (d.source.x+nodeWidth/2, d.source.y+nodeHeight/2) coordinates, which is the middle of the node and the intersection point of the line with the target node. The second segment starts at the intersection point with the target node and ends at the target nodes middle point(d.target.x+nodeWidth/2).The intersection of a line and a target node is where a marker is placed along the polyline. This is the part of the code -in the tick function- which is responsible for calculating the intersection points, and drawing the lines:
function tick() {
link.attr("points", function(d) {
var interForw = pointOnRect(d.source.x, d.source.y,
d.target.x - nodeWidth / 2, d.target.y - nodeHeight / 2,
d.target.x + nodeWidth / 2, d.target.y + nodeHeight / 2);
if(d.direction==="forward") {
return boundXInter((d.source.x+nodeWidth/2) + " "
+ boundYInter(((d.source.y) + nodeHeight / 2) + ","
+ boundXInter(((interForw.x+nodeWidth/2))) + " "
+ boundYInter((interForw.y) + nodeHeight / 2) + ","
+ boundXInter(d.target.x+nodeWidth/2) + " "
+ boundYInter((d.target.y) + nodeHeight / 2);
}
These are the functions for defining the boundaries for the links:
function boundXInter(x) {
return Math.max(nodeWidth/2, Math.min(width - (nodeWidth/2+padding),x));
}
function boundYInter(y){
return Math.max(nodeHeight/2, Math.min(height - (nodeHeight/2+padding), y));
}
When two nodes are one below the other like in the first image. It behaves as expected. .
However, when the nodes are placed as shown in the next figure, if the user continues dragging the nodes even if they not allowed to move further the boundaries, the nodes are blocked as wanted, but the links continue to move until the width-nodeWidth/2 point, according to the boundXInter function.
What I would like to achieve is the intersection point (the marker), the first segment of the line not to move further than it's actual position in this case as shown in the third figure. I want it to be fixed and not that segment of the line stretch to the width-nodeWidth/2 position as shown in the next figure. Probably, reformating the boundXInter function would do the job. But, I have tried many combinations and it did not. I would like to mention that if the user stops dragging the links return to the desired state (as shown in the second Figure)
Any ideas? What can I do, in this case, to get the correct result?
You can find a working snippet here: https://jsfiddle.net/yx2grm4s/39/.
You have mixed modelling relative to the center and relative to the top left of the rectangle. It is nicer to do it relative to the center (is the node position). Do not change the node position other then bounds check. The rectangle, label, link and dot are just decoration relative to the node position.
Also bound node position first before updating the "in between node" stuff so that will never need to be bound again. Use a nice even padding all around the box.
Removed code duplication.
Complete running code: https://jsfiddle.net/y0eox2vn/1/
Essential code part
var link = svg.append("g")
.selectAll(".link")
.data(links)
.enter()
// .append("line")
.append("polyline")
.attr("class", "link")
.style("stroke-width","1")
.style("stroke","black")
.style("fill","none")
.attr("id", function (d, i) { return 'link'+i; });
var markerFor = svg.append("defs")
.selectAll("marker")
.data(["forward"])
.enter()
.append("marker")
.attr("id", "dirArrowFor")
.attr("viewBox", "0 -5 10 10")
.attr("markerUnits", "strokeWidth")
.attr("markerWidth", 10)
.attr("markerHeight", 10)
.attr("refX",10)
.attr("refY", 0)
.attr("overflow", "visible")
.attr("orient", "auto")
.append("path")
.attr("d", "M0,-5L10,0L0,5")
.style("fill", "#000000");
link.attr("marker-mid", checkDir);
var linkNode = svg.append("g").selectAll(".link")
.data(links)
.enter()
.append("circle")
.attr("class","link-node")
.attr("r",4)
.style("fill","#c00");
linkNode.append("title")
.text(function(d) { return d.linkingWord; });
var node = svg.append("g").selectAll(".node")
.data(nodes)
.enter()
.append("rect")
.attr("class","node")
.attr("width", conceptWidth)
.attr("height", conceptHeight)
.attr("rx",20)
.attr("ry",20)
.style('fill',function(d){ return d.color;})
.call(d3.drag()
.on("start", dragStarted)
.on("drag", dragged)
.on("end", dragEnded));
var labels = svg.append("g")
.selectAll(".labels")
.data(nodes)
.enter()
.append("text")
.attr("class", "labels")
.text(function(d){ return d.name;})
.style("text-anchor","middle")
.attr("dy", 5);
var force = d3.forceSimulation()
.force("collision", d3.forceCollide(conceptWidthHalf +1).iterations(1))
.force("link", d3.forceLink().id(function(d){ return d.name;}))
.on("tick", tick);
force.nodes(nodes);
force.force("link").links(links);
function interForwRev(d) {
var interForw = pointOnRect(d.source.x, d.source.y,
d.target.x - conceptWidthHalf, d.target.y - conceptHeightHalf,
d.target.x + conceptWidthHalf, d.target.y + conceptHeightHalf);
var interRev = pointOnRect(d.target.x, d.target.y,
d.source.x - conceptWidthHalf, d.source.y - conceptHeightHalf ,
d.source.x + conceptWidthHalf, d.source.y + conceptHeightHalf);
return [interForw, interRev];
}
function tick() {
node.attr("x", function(d) { d.x=boundX(d.x); return d.x - conceptWidthHalf; })
.attr("y", function(d) { d.y=boundY(d.y); return d.y - conceptHeightHalf; });
labels.attr("x", function(d) { return d.x; })
.attr("y", function(d) { return d.y; });
linkNode.attr("cx", function (d) {
var interFR = interForwRev(d);
var interForw = interFR[0];
var interRev = interFR[1];
return d.cx = (interForw.x + interRev.x)*0.5;
})
.attr("cy", function (d) {
var interFR = interForwRev(d);
var interForw = interFR[0];
var interRev = interFR[1];
return d.cy = (interForw.y + interRev.y)*0.5;
});
// update the links after the nodes so we are already bounded by the box
link.attr("points", function(d) {
var interFR = interForwRev(d);
var interForw = interFR[0];
var interRev = interFR[1];
if(d.direction==="forward") {
return `${d.source.x} ${d.source.y},${interForw.x} ${interForw.y},${d.target.x} ${d.target.y}`;
}
});
}
function boundX(x) {
return Math.max(conceptWidthHalf+padding, Math.min(width - (conceptWidthHalf+padding), x));
}
function boundY(y){
return Math.max(conceptHeightHalf+padding, Math.min(height - (conceptHeightHalf+padding), y))
}
// NO other bound function needed
function dragStarted(d) {
if (!d3.event.active) force.alphaTarget(0.03).restart();
d.fx = d.x;
d.fy = d.y;
}

How to call JSON from a file instead of in-line for Multi-Edge D3 Example

I am following this example: http://bl.ocks.org/mbostock/1153292
I have row upon row of JSON data that looks like this:
{"source":"Michael Scott","target":"Jim Halpert","type":"pro"},
{"source":"Jim Halpert","target":"Dwight Schrute","type":"pro"}
Current code to render this data looks like this:
var links = [
{"source":"Michael Scott","target":"Jim Halpert","type":"pro"},
{"source":"Jim Halpert","target":"Dwight Schrute","type":"pro"}
];
var nodes = {};
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});
});
var width = 2000,
height = 1000;
var force = d3.layout.force()
.nodes(d3.values(nodes))
.links(links)
.size([width, height])
.linkDistance(60)
.charge(-300)
.on("tick", tick)
.start();
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
svg.append("defs").selectAll("marker")
.data(["pro"])
.enter().append("marker")
.attr("id", function(d) { return d; })
.attr("viewBox", "0 -5 10 10")
.attr("refX", 15)
.attr("refY", -1.5)
.attr("markerWidth", 6)
.attr("markerHeight", 6)
.attr("orient", "auto")
.append("path")
.attr("d", "M0,-5L10,0L0,5");
var path = svg.append("g").selectAll("path")
.data(force.links())
.enter().append("path")
.attr("class", function(d) { return "link " + d.type; })
.attr("marker-end", function(d) { return "url(#" + d.type + ")"; });
var circle = svg.append("g").selectAll("circle")
.data(force.nodes())
.enter().append("circle")
.attr("r", 6)
.call(force.drag);
var text = svg.append("g").selectAll("text")
.data(force.nodes())
.enter().append("text")
.attr("x", 8)
.attr("y", ".31em")
.text(function(d) { return d.name; });
function tick() {
path.attr("d", linkArc);
circle.attr("transform", transform);
text.attr("transform", transform);
}
function linkArc(d) {
var dx = d.target.x - d.source.x,
dy = d.target.y - d.source.y,
dr = Math.sqrt(dx * dx + dy * dy);
return "M" + d.source.x + "," + d.source.y + "A" + dr + "," + dr + " 0 0,1 " + d.target.x + "," + d.target.y;
}
function transform(d) {
return "translate(" + d.x + "," + d.y + ")";
}
</script>
How do I call the data from an external data.json file? I've looked at all the other related SO questions and other D3 examples, but I haven't been able to get anything to work.
I've tried (and then changing all references to links to data):
d3.json("data.json", function(error, data) {
});
Here is that full code:
<body>
<script src="//d3js.org/d3.v3.min.js"></script>
<script>
d3.json("data.json", function(error, data) {
});
var nodes = {};
data.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});
});
var width = 2000,
height = 1000;
var force = d3.layout.force()
.nodes(d3.values(nodes))
.data(data)
.size([width, height])
.linkDistance(60)
.charge(-300)
.on("tick", tick)
.start();
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
svg.append("defs").selectAll("marker")
.data(["pro"])
.enter().append("marker")
.attr("id", function(d) { return d; })
.attr("viewBox", "0 -5 10 10")
.attr("refX", 15)
.attr("refY", -1.5)
.attr("markerWidth", 6)
.attr("markerHeight", 6)
.attr("orient", "auto")
.append("path")
.attr("d", "M0,-5L10,0L0,5");
var path = svg.append("g").selectAll("path")
.data(force.data())
.enter().append("path")
.attr("class", function(d) { return "link " + d.type; })
.attr("marker-end", function(d) { return "url(#" + d.type + ")"; });
var circle = svg.append("g").selectAll("circle")
.data(force.nodes())
.enter().append("circle")
.attr("r", 6)
.call(force.drag);
var text = svg.append("g").selectAll("text")
.data(force.nodes())
.enter().append("text")
.attr("x", 8)
.attr("y", ".31em")
.text(function(d) { return d.name; });
function tick() {
path.attr("d", linkArc);
circle.attr("transform", transform);
text.attr("transform", transform);
}
function linkArc(d) {
var dx = d.target.x - d.source.x,
dy = d.target.y - d.source.y,
dr = Math.sqrt(dx * dx + dy * dy);
return "M" + d.source.x + "," + d.source.y + "A" + dr + "," + dr + " 0 0,1 " + d.target.x + "," + d.target.y;
}
function transform(d) {
return "translate(" + d.x + "," + d.y + ")";
}
</script>
This results in the error Uncaught Reference Error: "data" is not defined for this line data.forEach(function(link) {. data.json is located in the same directory as index.html
I've tried various other implementations as well that I picked up from other D3 examples on blocks.org. Any and all insight would be greatly appreciated. Please let me know if there's anything I can do to improve my question!
Here is a working example with your data. You need to do a couple of things to make it work with an external json file.
Make sure you include all your code inside your d3.json call.
Since we're using the variable links to hold all our data, you need to set that equal to data which is returned from the d3.json call.
Check working code below:
var data_url = "https://api.myjson.com/bins/10kk91";
d3.json(data_url, function(error, data){
var links = data; //set links equal to data which is returned from d3.json
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});
});
var width = 960,
height = 500;
var force = d3.layout.force()
.nodes(d3.values(nodes))
.links(links)
.size([width, height])
.linkDistance(60)
.charge(-300)
.on("tick", tick)
.start();
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
// Per-type markers, as they don't inherit styles.
svg.append("defs").selectAll("marker")
.data(["suit", "licensing", "resolved"])
.enter().append("marker")
.attr("id", function(d) { return d; })
.attr("viewBox", "0 -5 10 10")
.attr("refX", 15)
.attr("refY", -1.5)
.attr("markerWidth", 6)
.attr("markerHeight", 6)
.attr("orient", "auto")
.append("path")
.attr("d", "M0,-5L10,0L0,5");
var path = svg.append("g").selectAll("path")
.data(force.links())
.enter().append("path")
.attr("class", function(d) { return "link " + d.type; })
.attr("marker-end", function(d) { return "url(#" + d.type + ")"; });
var circle = svg.append("g").selectAll("circle")
.data(force.nodes())
.enter().append("circle")
.attr("r", 6)
.call(force.drag);
var text = svg.append("g").selectAll("text")
.data(force.nodes())
.enter().append("text")
.attr("x", 8)
.attr("y", ".31em")
.text(function(d) { return d.name; });
// Use elliptical arc path segments to doubly-encode directionality.
function tick() {
path.attr("d", linkArc);
circle.attr("transform", transform);
text.attr("transform", transform);
}
function linkArc(d) {
var dx = d.target.x - d.source.x,
dy = d.target.y - d.source.y,
dr = Math.sqrt(dx * dx + dy * dy);
return "M" + d.source.x + "," + d.source.y + "A" + dr + "," + dr + " 0 0,1 " + d.target.x + "," + d.target.y;
}
function transform(d) {
return "translate(" + d.x + "," + d.y + ")";
}
}); //end d3.json call
.link {
fill: none;
stroke: #666;
stroke-width: 1.5px;
}
#licensing {
fill: green;
}
.link.licensing {
stroke: green;
}
.link.resolved {
stroke-dasharray: 0,2 1;
}
circle {
fill: #ccc;
stroke: #333;
stroke-width: 1.5px;
}
text {
font: 10px sans-serif;
pointer-events: none;
text-shadow: 0 1px 0 #fff, 1px 0 0 #fff, 0 -1px 0 #fff, -1px 0 0 #fff;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>

Changing position of label base D3

Right, so I have this graph that works great. It has tooltips, labels, and a total value in the center:
However, when I hover over one of the arcs to activate the tooltips, the little dot that has the pointer coming out of it stays put, as seen here:
This is my mouse movement code
path.on('mouseenter', function(d){
d3.select(this)
.transition()
.duration(500)
.attr("d", arcOver);
});
path.on('mouseleave', function(d){
d3.select(this).transition()
.duration(500)
.attr("d", arc);
});
path.on('mouseover', function(d) {
var percent = Math.round(1000 * d.data.value / sum) / 10;
tooltip.style('opacity', 0);
tooltip.select('.label').html(d.data.label);
tooltip.select('.value').html(d3.format("$,.2f")(d.data.value));
tooltip.select('.percent').html(percent + '%');
tooltip.style('display', 'block');
tooltip.transition()
.duration(600)
.style("opacity",1);
});
path.on('mouseout', function() {
tooltip.transition()
.duration(600)
.style("opacity",0)
.style('pointer-events', 'none')
});
Here's the code that creates the labels:
svg.selectAll("text").data(pieData)
.enter()
.append("text")
.attr("class", "stickLabels")
.attr("text-anchor", "middle")
.attr("x", function(d) {
var a = d.startAngle + (d.endAngle - d.startAngle)/2 - Math.PI/2;
d.cx = Math.cos(a) * (radius - 37.5);
return d.x = Math.cos(a) * (radius + 40);
})
.attr("y", function(d) {
var a = d.startAngle + (d.endAngle - d.startAngle)/2 - Math.PI/2;
d.cy = Math.sin(a) * (radius - 37.5);
return d.y = Math.sin(a) * (radius + 40);
})
.text(function(d) { return d3.format("s")(d.value); })
.each(function(d) {
var bbox = this.getBBox();
d.sx = d.x - bbox.width/2 - 2;
d.ox = d.x + bbox.width/2 + 2;
d.sy = d.oy = d.y + 5;
});
svg.append("defs").append("marker")
.attr("id", "circ")
.attr("markerWidth", 6)
.attr("markerHeight", 6)
.attr("refX", 3)
.attr("refY", 3)
.append("circle")
.attr("cx", 3)
.attr("cy", 3)
.attr("r", 3);
svg.selectAll("path.pointer").data(pieData).enter()
.append("path")
.attr("class", "pointer")
.style("fill", "none")
.style("stroke", "black")
.attr("marker-end", "url(#circ)")
.attr("d", function(d) {
if(d.cx > d.ox) {
return "M" + d.sx + "," + d.sy + "L" + d.ox + "," + d.oy + " " + d.cx + "," + d.cy;
} else {
return "M" + d.ox + "," + d.oy + "L" + d.sx + "," + d.sy + " " + d.cx + "," + d.cy;
}
});
And here's a fiddle of the whole code: http://jsfiddle.net/18L6u5xf/
My question is this: How do I move the dot with the arc.
PS I know what I need to change it to, I just don't know how to do it with a mouseover. This is what it needs to end up being:
d.cx = Math.cos(a) * (radius - 17.5);
PPS It looks better with my styling than in the fiddle, just bear with me.
A slightly cleaner way of doing this is to wrap the arc, path and text in g and then just transition that on mouseenter and mouseout. This way you aren't repeating the same data binding and calculations 3 separate times:
// create a group
var gs = svg.selectAll('.slice')
.data(pieData)
.enter()
.append('g')
.attr('class', 'slice');
// add arcs to it
gs.append('path')
.attr('d', arc)
.attr('id', function(d){return d.data.id;})
.attr('class', 'arcPath')
.attr('fill', function(d, i) {
return color(d.data.label);
});
// add text to it
gs.append("text")
.attr("class", "stickLabels")
.attr("text-anchor", "middle")
.attr("x", function(d) {
var a = d.startAngle + (d.endAngle - d.startAngle)/2 - Math.PI/2;
d.cx = Math.cos(a) * (radius - 37.5);
return d.x = Math.cos(a) * (radius + 40);
})
.attr("y", function(d) {
var a = d.startAngle + (d.endAngle - d.startAngle)/2 - Math.PI/2;
d.cy = Math.sin(a) * (radius - 37.5);
return d.y = Math.sin(a) * (radius + 40);
})
.text(function(d) { return d3.format("s")(d.value); })
.each(function(d) {
var bbox = this.getBBox();
d.sx = d.x - bbox.width/2 - 2;
d.ox = d.x + bbox.width/2 + 2;
d.sy = d.oy = d.y + 5;
});
// add connection paths
gs.append("path")
.attr("class", "pointer")
.style("fill", "none")
.style("stroke", "black")
.attr("marker-end", "url(#circ)")
.attr("d", function(d) {
if(d.cx > d.ox) {
return "M" + d.sx + "," + d.sy + "L" + d.ox + "," + d.oy + " " + d.cx + "," + d.cy;
} else {
return "M" + d.ox + "," + d.oy + "L" + d.sx + "," + d.sy + " " + d.cx + "," + d.cy;
}
});
// mouseenter of group
gs.on('mouseenter', function(d){
d3.select(this)
.transition()
.duration(500)
.attr("transform",function(d){
var a = d.startAngle + (d.endAngle - d.startAngle)/2 - Math.PI/2;
var x = Math.cos(a) * 20;
var y = Math.sin(a) * 20;
return 'translate(' + x + ',' + y + ')';
});
});
// on mouse leave
gs.on('mouseleave', function(d){
d3.select(this)
.transition()
.duration(500)
.attr("transform",function(d){
return 'translate(0,0)';
});
});
Example here.

Curved line on d3 force directed tree

New to d3 and trying to develop a force directed tree into which we can plug varioss dataset. I've managed to get the basic idea up and running but would like to make the links curved so I can deal with multiple links. I've looked at http://bl.ocks.org/1153292 and I'm just not getting it. The nearest I get is it all working with no path visible. This is my code for straight lines, I'd appreciate some help if you've got the time
Thanks:
//Sets up the svg that holds the data structure and puts it in the div called mapBox
var svg = d3.select("div#mapBox.theMap").append("svg")
.attr("width", mapWidth)
.attr("height", mapHeight);
//Sets up the data structure and binds the data to it
var force = d3.layout.force()
.nodes(data.nodes)
.links(data.links)
.size([mapWidth, mapHeight])
.charge(-600)
.linkDistance(60)
.start();
//Draws the links and set up their styles
var link = svg.selectAll("link")
.data(data.links)
.enter().append("line")
.attr("class", "link")
.style("stroke", "#ccc")
//Creates nodes and attached "g" element to append other things to
var node = svg.selectAll(".node")
.data(data.nodes)
.enter().append("g")
.call(force.drag);
//Appends the cirdle to the "g" element and defines styles
node.append("circle")
.attr("r", function(d) { if (d.weight<1.1) {return 5} else {return d.weight*1.3+5 }})
.style("fill", "White")
.style("stroke-width", 3)
.style("stroke", function(d) { if (d.type==1) {return "#eda45e "} if(d.type==2) {return "#819e9a"}else {return "#c36256" }} ) // Node stroke colors
.on("mouseover", nodeMouseover)
on("mouseout", nodeMouseout)
.on("mousedown", nodeMousedown)
.call(force.drag);
//Appends text to the "g" element and defines styles
node.append("text")
.attr("class", "nodetext")
.attr("dx", 16)
.attr("dy", ".35em")
.attr("text-anchor", function(d) { if (d.type==1) {return "middle";} else {return "start";} })
.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 + ")"; });
});
Der, worked it out.
change
.enter().append("line")
to
.enter().append("path")
then change
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; });
to
link.attr("d", function(d) {
var dx = d.target.x - d.source.x,
dy = d.target.y - d.source.y,
dr = Math.sqrt(dx * dx + dy * dy);
return "M" + d.source.x + "," + d.source.y + "A" + dr + "," + dr + " 0 0,1 " + d.target.x + "," + d.target.y;
});
Hope that help anyone stuck as I was
This also worked for me.
First define a path:
var path = vis.selectAll("path")
.data(force.links());
path.enter().insert("svg:path")
.attr("class", "link")
.style("stroke", "#ccc");
Then define the curve, as Bob Haslett says and in the Mobile Patent Suits example:
path.attr("d", function(d) {
var dx = d.target.x - d.source.x,
dy = d.target.y - d.source.y,
dr = Math.sqrt(dx * dx + dy * dy);
return "M" + d.source.x + "," + d.source.y + "A" + dr + "," + dr + " 0 0,1 " + d.target.x + "," + d.target.y;
});

Unable to get click event in D3 JavaScript library

I am using D3 JavaScript library to display data as a force directed marker. It works fine. But I am unable to add click event to the circle. so when I click on the circle, I get detailed analysis of the circle and display it in a modal box.
var links = [{source: "x", target:"y", type: "paid"},......]';
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});
});
var w = 950,
h = 500;
var force = d3.layout.force()
.nodes(d3.values(nodes))
.links(links)
.size([w, h])
.linkDistance(60)
.charge(-300)
.on("tick", tick)
.start();
var svg = d3.select("#graph").append("svg:svg")
.attr("width", w)
.attr("height", h);
// Per-type markers, as they don't inherit styles.
svg.append("svg:defs").selectAll("marker")
.data(["suit", "licensing", "resolved"])
.enter().append("svg:marker")
.attr("id", String)
.attr("viewBox", "0 -5 10 10")
.attr("refX", 15)
.attr("refY", -1.5)
.attr("markerWidth", 6)
.attr("markerHeight", 6)
.attr("orient", "auto")
.append("svg:path")
.attr("d", "M0,-5L10,0L0,5");
var path = svg.append("svg:g").selectAll("path")
.data(force.links())
.enter().append("svg:path")
.attr("class", function(d) { return "link " + d.type; })
.attr("marker-end", function(d) { return "url(#" + d.type + ")"; });
var circle = svg.append("svg:g").selectAll("circle")
.data(force.nodes())
.enter().append("svg:circle")
.attr("r", 6)
.call(force.drag);
var text = svg.append("svg:g").selectAll("g")
.data(force.nodes())
.enter().append("svg:g");
// A copy of the text with a thick white stroke for legibility.
text.append("svg:text")
.attr("x", 8)
.attr("y", ".31em")
.attr("class", "shadow")
.text(function(d) { return d.name; });
text.append("svg:text")
.attr("x", 8)
.attr("y", ".31em")
.text(function(d) { return d.name; });
// Use elliptical arc path segments to doubly-encode directionality.
function tick() {
path.attr("d", function(d) {
var dx = d.target.x - d.source.x,
dy = d.target.y - d.source.y,
dr = Math.sqrt(dx * dx + dy * dy);
return "M" + d.source.x + "," + d.source.y + "A" + dr + "," + dr + " 0 0,1 " + d.target.x + "," + d.target.y;
});
circle.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
});
text.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
});
}
I tried adding .on("click", 'alert(\'Hello world\')') to var circle. It does not work as expected. It alerts on load instead on click.
I appreciate any help
Try this:
var circle = svg.append("svg:g").selectAll("circle")
.data(force.nodes())
.enter().append("svg:circle")
.attr("r", 6)
.on("click", function(d,i) { alert("Hello world"); })
.call(force.drag);
Try out this, if you want the node contained within the circle (let's say that your nodes are mapping an object with a key called anger and a value 34:
var circle = svg.append("svg:g").selectAll("circle")
.data(force.nodes())
.enter().append("svg:circle")
.attr("r", 6)
.on("click", function(d,i) { alert(d.anger); }) // this will alert 34
.call(force.drag);
Or try this for the attributes of the svg (getting the radius of the svg, for example):
var circle = svg.append("svg:g").selectAll("circle")
.data(force.nodes())
.enter().append("svg:circle")
.attr("r", 6)
.on("click", function(d,i) { alert(d3.select(this).r; }) // this will print out the radius })
.call(force.drag);
Sorry if my post is like the one above, but I thought the clarification could be useful.

Categories