Best solution to label nodes in SVG dynamic tree (using D3.js) - javascript

I am trying to modify this D3.js example (Dynamic Node-Link Tree) by adding a specific label (SVG text) to each node, but unsucessfully.
If I understand correctly, after a brief look at SVG specs and D3 documentation, the best way would be to create SVG groups and move them around.
Unfortunately, this is not working, as the transitions have no effect on the groups.
Is there a simple(r) way I am not aware of?
Many thanks.

If you're looking for an effect where you switch the circles for text labels, you can do the following:
// Enter any new nodes at the parent's previous position.
node.enter().append("svg:text")
.attr("class", "node")
.attr("x", function(d) { return d.parent.data.x0; })
.attr("y", function(d) { return d.parent.data.y0; })
.attr("text-anchor", "middle")
.text(function(d) { return "Node "+(nodeCount++); })
.transition()
.duration(duration)
.attr("x", x)
.attr("y", y);
See the fiddle here: http://jsfiddle.net/mccannf/pcwMa/4/
Edit
However, if you're looking to add labels alongside the circles, I would not recommend using svg:g in this case, because then you would have to use transforms to move the groups around. Instead, just double up on the circle nodes and text nodes like so in the update function:
// Update the nodes…
var cnode = vis.selectAll("circle.node")
.data(nodes, nodeId);
cnode.enter().append("svg:circle")
.attr("class", "node")
.attr("r", 3.5)
.attr("cx", function(d) { return d.parent.data.x0; })
.attr("cy", function(d) { return d.parent.data.y0; })
.transition()
.duration(duration)
.attr("cx", x)
.attr("cy", y);
var tnode = vis.selectAll("text.node")
.data(nodes, nodeId);
tnode.enter().append("svg:text")
.attr("class", "node")
.text(function(d) { return "Node "+(nodeCount++); })
.attr("x", function(d) { return d.parent.data.x0; })
.attr("y", function(d) { return d.parent.data.y0; })
.transition()
.duration(duration)
.attr("x", x)
.attr("y", y);
// Transition nodes to their new position.
cnode.transition()
.duration(duration)
.attr("cx", x)
.attr("cy", y);
tnode.transition()
.duration(duration)
.attr("x", x)
.attr("y", y)
.attr("dx", 4)
.attr("dy", 4); //padding-left and padding-top
A fiddle that demonstrates this can be found here: http://jsfiddle.net/mccannf/8ny7w/19/

Related

I can not move my nodes, the force diagram does not apply in 3.js

In a previous question a user helped me with a problem that consisted in not knowing how to put images where the circles are. this time my problem is that I can not drag the nodes. This gif illustrates my problem (first I show how the nodes should move normally, then I illustrate my problem.).
Why does this happen?
var link = g.append("g")
.attr("class", "links")
.selectAll("line")
.data(graph.links)
.enter().append("line")
.attr("stroke", function(d) {
console.log(d);
return colorLink(d.group);
})
.attr("marker-end", "url(#arrow)");
var node = g.selectAll("g.node")
.data(graph.nodes)
//.filter(function(d){ return d.type=="circle"; })
var NodeCircleEnter= node.enter().append("g")
.attr("class","node")
.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; })
NodeCircleEnter
.append("circle")
.attr("r", 20)
.attr("fill", function(d) {
return colorNode(d.group);
})
.style("stroke", function(d) {
return colorNode(d.group);
})
// Append images
var images = NodeCircleEnter.append("svg:image")
.attr("xlink:href", function(d) {console.log(d); return d.image;})
.attr("x", function(d) { return -25;})
.attr("y", function(d) { return -25;})
.attr("height", 50)
.attr("width", 50);
This is my full code:
https://plnkr.co/edit/9uH13N3or3G9VTgQlMm9?p=preview
Your event handlers need to be applied to the elements themselves rather than the canvas as a whole.
In this code (only existing in your Plunker project, but not in your question):
var drag_handler = d3.drag()
.on("start", drag_start)
.on("drag", drag_drag)
.on("end", drag_end);
drag_handler(node);
Your variable node is the drawing. You actually want a collection of elements to affect which you get from functions like .enter(). Your variable NodeCircleEnter contains that collection so the particular line should be:
drag_handler(NodeCircleEnter);
This still leaves an issue where dragging on the labels themselves doesn't work. This is because the labels aren't children of the elements you set the handlers on.

how to move circles(data points) in d3.js

I have 2 lines in the form of waves plotted in x-y axis based on randomly generated data and i am showing circles on the waves denoting the data points on it.
Based on setInterval of 200 ms, I am updating the original data and the lines(waves) are moving to the left, but the issue is that the only circles which are there in the initial interval are moving and for 2nd interval onward the circles are not showing up on the waves.
see the jsfiddle for the running code : https://jsfiddle.net/rajatmehta/tm5166e1/10/
here is the code :
chartBody.append("path") // Add the valueline path
.datum(globalData)
.attr("id", "path1")
.attr("class", "line")
.attr("d", valueline);
chartBody.selectAll(null)
.data(globalData)
.enter()
.append("circle")
.attr("class", "dot1")
.attr("r", 3)
.attr("cx", function(d) {
console.log(d);
return x(d.timestamp);
})
.attr("cy", function(d) {
return y(d.value);
});
chartBody.selectAll(null)
.data(globalDataNew)
.enter()
.append("circle")
.attr("class", "dot2")
.attr("r", 3)
.attr("cx", function(d) {
return x(d.timestamp);
})
.attr("cy", function(d) {
return y(d.value);
});
chartBody.append("path") // Add the valueline path
.datum(globalDataNew)
.attr("id", "path2")
.attr("class", "line")
.attr("d", valueline2);
any idea how to do that ?
You need to create new circles based on the updated data. Currently, you are only updating the data to selection, but not appending circles, and then moving existing circles to the left.
For example, you could to this:
chartBody.selectAll(".dot1")
.data(globalData, function(d){ return d.timestamp; })
.enter()
.append("circle")
.attr("class", "dot1")
.attr("r", 3)
.attr("cx", function(d) {
return x(d.timestamp);
})
.attr("cy", function(d) {
return y(d.value);
});
chartBody.selectAll(".dot2")
.data(globalDataNew, function(d){ return d.timestamp; })
.enter()
.append("circle")
.attr("class", "dot2")
.attr("r", 3)
.attr("cx", function(d) {
return x(d.timestamp);
})
.attr("cy", function(d) {
return y(d.value);
});
d3.selectAll(".dot1")
//.data(globalData)
.transition()
.duration(duration)
.ease("linear")
.attr("transform", "translate(" + String(dx) + ")");
d3.selectAll(".dot2")
//.data(globalDataNew)
.transition()
.ease("linear")
.duration(duration)
.attr("transform", "translate(" + String(dx) + ")");
See here: https://jsfiddle.net/tm5166e1/11/
This appends the data, using the timestamp as a key so you only create new circles for newly added datums.
(There is an issue when they are first added which is beyond the scope of this question, but it will be worth checking out these examples: https://bl.ocks.org/tomshanley/15a2b09a95ccaf338650e50fd207fcbf and https://bl.ocks.org/mbostock/1642874)

Image enlarge centering for Node D3 Force layout

I am trying to do the following:
Click a node
Enlarge the image of that node according to its current width and height
The idea is to determine x,y position based on its own width and height so it does not run wild.
My code for force tick
function render(){
force.on("tick", function() {
node.attr("x", function(d) {return d.x-d.width/2})
.attr("y", function(d) {return d.y-d.height/2});
Declaration of node
var node = container.append("g").selectAll(".node")
.data(graph.nodes)
.enter()
.append("image")
.attr("xlink:href",function(d){return d.type+ ".svg" ;})
.attr("class", "node")
.attr("x", function(d) { return d.x; })
.attr("y", function(d) { return d.y; })
.attr("width", "24px")
.attr("height", "24px")
.attr("isChosen", "no")
.on("click",click);
Sample of data
"nodes":[
{"id":"0", "name":"ETCO I","type":"Company","trust":50},
{"id": "1","name":"PINKERTON Eidel ","type":"human","trust":50}
],
"links":[
{"source":1,"target":0,"linktype":"Secretary"},
{"source":1,"target":0,"linktype":"Director"},
Click handle
function click(node)
d3.select(this)
.attr("isChosen", "yes")
.transition()
.duration(750)
.attr("x", function(d) {return d.x-24})
.attr("y", function(d) {return d.y-24})
.attr("width", "35px")
.attr("height", "35px")
but it does not work, the image is all on left conner. Where does my logic go wrong?

D3 Get Variable Value

This is a very basic question, but how do I access the value of attributes in d3?
I just started learning today, so I haven't figured this out yet
Suppose I have this as part of my code here
http://jsfiddle.net/matthewpiatetsky/nCNyE/9/
var node = svg.selectAll("circle.node")
.data(nodes)
.enter().append("circle")
.attr("class", "node")
.attr("r", function (d) {
if (width < height){
return d.count * width/100;
} else {
return d.count * height/100;
}
})
.on("mouseover", animateFirstStep)
.on("mouseout",animateSecondStep)
.style("fill", function(d,i){return color(i);})
.call(force.drag);
For my animation the circle gets bigger when you mouse over it, and I want the circle to return to its normal size when you move the mouse away. However, i'm not sure how to get the value of the radius.
i set the value here
.attr("r", function (d) {
if (width < height){
return d.count * width/100;
} else {
return d.count * height/100;
}
I tried to do node.r and things like that, but i'm not sure what the correct syntax is
Thanks!
You can access an attribute of a selection with:
var node = svg.selectAll("circle.node")
.data(nodes)
.enter().append("circle")
.attr("class", "node")
.attr("r", function (d) { return rScale(d.count); })
.on("mouseover", function(d) {
d3.select(this)
.transition()
.duration(1000)
.attr('r', 1.8 * rScale(d.count));
})
.on("mouseout", function(d) {
d3.select(this)
.transition()
.duration(1000)
.attr('r', rScale(d.count));
})
.style("fill", function (d, i) {
return color(i);
})
.call(force.drag);
in this context, this points to the DOM element binded with d. Normally, the area of a circle must be proportional to the quantities that you are showing, take a look at the documentation of Quantitative Scales. A fork of your fiddle is here.

D3.js identifying links between nodes to change link thickness

I have been setting up graphs to dynamically scale in reference to the width and height of the svg or bounding box, and with nodes and node borders this is relatively simple. By using d.depth and assigning the desired ratio to assign a relative radius to a node at a certain depth, this is giving me no problems and nodes at lower depths become smaller to avoid overlap. Then, for the node border I assign a ratio between the node radius and the thickness of the border.
This has proven to be an ideal solution to a problem that arises when adding a zoom feature by scaling the container. This is the problem:
The text, the nodes, and the node borders no longer bunch up, but the links remain the same weight.
I would also like to scale the thickness of the edges between nodes but I do not know how to identify edges so that I can either assign a thickness in relation to the radius of the parent or child node
d3.json("aviation.json", function(root) {
var nodes = balloon.nodes(root),
links = balloon.links(nodes);
var link= svg.selectAll("path")
.data(links)
.enter().append("path")
.attr("d",diagonal)
.attr("stroke", "black")
.attr("shape-rendering", "auto")
.attr("fill", "none")
.data(nodes)
.attr("stroke-width", function(d) {return (1/5000*diameter);});
var node = svg.selectAll("g.node")
.data(nodes)
.enter()
.append("g")
.attr("class", "node");
node.append("circle")
.attr("r", function(d) { return d.r;})
.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; })
.attr("fill", "white")
.attr("opacity", "0.05")
.attr("stroke-width", function(d) {return d.r/600;}) //here I am referecing d.r
//to find the stroke width.
.attr("stroke-opacity", ".5")
.on("Click", function(d) { return zoomClick});
var text = svg.selectAll("g.text")
.data(nodes)
.enter()
.append("text")
.attr("dx", function(d) { return d.x })
.attr("dy", function(d) { return d.y })
.attr("font-family", "Helvetica")
.attr("font-size", function(d) {return d.children ? d.r/7 : d.r/4;})
.attr("stroke-width", function(d) {return d.r/400;})
.attr("stroke-opacity", "1")
.attr("fill", "white")
.attr("pointer-events", "all")
.style("text-anchor", "middle")
.text(function(d) { return d.name; })

Categories

Resources