Actually I've integrated collapsible feature inside bounded force directed graph. But when I tried to put a label on each node, I got unexpected output.
I used bellow code to append labels on node:
node.enter().append("text")
.attr("class","node")
.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; })
.on("click",click)
.text(function(d){return d.name})
.call(force.drag);
And below code I've written inside a tick function:
node.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
});
What I might be doing wrong?
I need to append the g tag and then circle and text:
var nodeEnter = node.enter().append("g")
.attr("class", "node")
.on("click", click)
.call(force.drag);
nodeEnter.append("circle")
.attr("r", function(d) { return Math.sqrt(d.size) / 10 || 8.5; });
nodeEnter.append("text")
.attr("dy", ".35em")
.text(function(d) { return d.name; });
node.select("circle")
.style("fill", color);
Related
Using d3.js v3, I've made this bubble chart:
d3.json(JSON_URL, function(data) {
data = JSON.parse(data)
var nodes = pack.nodes(data);
var node = canvas.selectAll(".node")
.data(nodes)
.enter()
.append("g")
.attr("class", "node")
.attr("transform", function (d) { return "translate(" + d.x + ","+ d.y + ")" ;});
node.append("circle")
.attr("r", function(d) {return d.r; })
.attr("fill", function (d, i) {return d.children ? "#fff" : color(i)})
.attr("opacity", 0.25)
.attr("stroke-width", "2");
node.append("text")
.text(function(d) {return d.children ? "" : d.name ;})
.attr("text-anchor", "middle")
.attr("class", "nodetext")
.attr("data-classname", function(d) {return d.className;})
.attr("style", function(d) {return "font-size:" + d.r/4;})
.on("click", function(d) { window.open("https://twitter.com/hashtag/" + d.name.trim() + "\?src=hash"); })
.on("mouseover", function(d) {
d3.select(this).attr("r", 10).style("fill", "#2f4cff");
d3.select(this).style("cursor", "pointer");
})
.on("mouseout", function(d) {
d3.select(this).attr("r", 5.5).style("fill", "#000");
});
//problem here
node.append("text")
.text(function(d) {return d.children ? "" : d.value ;})
.attr("text-anchor", "middle")
.attr("class", "nodevalue")
.attr("data-classname", function(d) {return d.className;})
.attr("style", function(d) {return "font-size:" + d.r/4;});
}) ;
It's all well, expect the fact that the texts for d.value overlap the texts of d.name, while I want them to come under the d.name. The result should be something like this:
I tried to tweak the nodevalue class but it did not affect the positioning of texts.
How can I acheive this?
You can use the y attribute to position vertically your text. Your code is a bit hard to test because we don't have access to your json, so blindly the code for the text part would be :
node.append("text")
.text(function(d) {return d.children ? "" : d.name ;})
.attr("text-anchor", "middle")
.attr("class", "nodetext")
.attr("data-classname", function(d) {return d.className;})
.attr("style", function(d) {return "font-size:" + d.r/4;})
.on("click", function(d) { window.open("https://twitter.com/hashtag/" + d.name.trim() + "\?src=hash"); })
.on("mouseover", function(d) {
d3.select(this).attr("r", 10).style("fill", "#2f4cff");
d3.select(this).style("cursor", "pointer");
})
.on("mouseout", function(d) {
d3.select(this).attr("r", 5.5).style("fill", "#000");
});
node.append("text")
.text(function(d) {return d.children ? "" : d.value ;})
.attr("text-anchor", "middle")
.attr("class", "nodevalue")
.attr("y", 15) // adding y attribute to offset vertically your text
.attr("data-classname", function(d) {return d.className;})
.attr("style", function(d) {return "font-size:" + d.r/4;});
I'm trying to figure out the correct way to displays labels that will sit on top of each bar in my bar chart. I'd also like them to display a % after the number.
Here is my Plunker: https://plnkr.co/edit/FbIquWxfLjcRTg7tiX4E?p=preview
I experimented with using this code from the question found in the link below. However, I wasnt able to get it to work properly (meaning the whole chart failed to display)
Adding label on a D3 bar chart
var yTextPadding = 20;
svg.selectAll(".bartext")
.data(data)
.enter()
.append("text")
.attr("class", "bartext")
.attr("text-anchor", "top")
.attr("fill", "white")
.attr("x", function(d,i) {
return x(i)+x.rangeBand()/2;
})
.attr("y", function(d,i) {
return height-y(d)+yTextPadding;
})
.text(function(d){
return d;
});
This is the most straight forward way given your existing code:
// keep a reference to the g holding the rects
var rectG = g.append("g")
.selectAll("g")
.data(data)
.enter().append("g")
.attr("transform", function(d) { return "translate(" + x0(d.State) + ",0)"; })
.selectAll("rect")
.data(function(d) { return keys.map(function(key) { return {key: key, value: d[key]}; }); })
.enter();
// append the rects the same way as before
rectG.append("rect")
.attr("x", function(d) { return x1(d.key); })
.attr("y", function(d) { return y(d.value); })
.attr("width", x1.bandwidth())
.attr("height", function(d) { return height - y(d.value); })
.attr("fill", function(d) { return z(d.key); });
// now add the text to the g
rectG.append("text")
.text(function(d){
return d.value + '%';
})
.attr("x", function(d) { return x1(d.key) + (x1.bandwidth()/2); })
.attr("y", function(d) { return y(d.value); })
.style("text-anchor", "middle");
Updated plunker.
I am creating a sankey diagram using D3. I am trying to redraw the diagram with additional node and link and using transition to animate the previous diagram to the new diagram. I was able to add in new node and link but the old nodes and links did not change position. Since the new node and link could be added at any place within the diagram, I do not want to clear and redraw the entire svg, but use transition to get from the old diagram to the new one. The code to draw the sankey diagram is this:
function draw(data){
// Set the sankey diagram properties
var sankey = d3sankey()
.nodeWidth(17)
.nodePadding(27)
.size([width, height]);
var path = sankey.link();
var graph = data;
sankey.nodes(graph.nodes)
.links(graph.links)
.layout(32);
sankey.relayout();
// add in the links
link.selectAll(".link")
.data(graph.links)
.enter().append("path")
.attr("class", "link")
.attr("d", path)
.style("fill", "none")
.style("stroke", function(d){
return "grey";
})
.style("stroke-opacity", "0.4")
.on("mouseover", function() { d3.select(this).style("stroke-opacity", "0.7") } )
.on("mouseout", function() { d3.select(this).style("stroke-opacity", "0.4") } )
.style("stroke-width", function (d) {
return Math.max(1, d.dy);
})
.sort(function (a, b) {
return b.dy - a.dy;
});
link.transition().duration(750);
//link.exit();
// add in the nodes
var node = nodes.selectAll(".node")
.data(graph.nodes)
.enter().append("g")
.attr("class", "node")
.attr("transform", function (d) {
return "translate(" + d.x + "," + d.y + ")";
});
// add the rectangles for the nodes
node.append("rect")
.attr("height", function (d) {
return d.dy;
})
.attr("width", sankey.nodeWidth())
.style("fill", function (d) {
return d.color = color(d.name.replace(/ .*/, ""));
})
.style("fill-opacity", ".9")
.style("shape-rendering", "crispEdges")
.style("stroke", function (d) {
return d3.rgb(d.color).darker(2);
})
.append("title")
.text(function (d) {
return d.name + "\n" + format(d.value);
});
// add in the title for the nodes
node.append("text")
.attr("x", -6)
.attr("y", function (d) {
return d.dy / 2;
})
.attr("dy", ".35em")
.attr("text-anchor", "end")
.attr("text-shadow", "0 1px 0 #fff")
.attr("transform", null)
.text(function (d) {
return d.name;
})
.filter(function (d) {
return d.x < width / 2;
})
.attr("x", 6 + sankey.nodeWidth())
.attr("text-anchor", "start");
node.transition().duration(750);
}
The JSFiddle
Is it possible to use transition to add in new node and link and reposition
old nodes and links?
Thanks!
I was able to do this by using moving the nodes and links to new position. The code for that is:
var nodes = d3.selectAll(".node")
.transition().duration(750)
.attr('opacity', 1.0)
.attr("transform", function (d) {
if(d.node == 3){
console.log(d.x, d.y);
}
return "translate(" + d.x + "," + d.y + ")";
});
var nodeRects = d3.selectAll(".node rect")
.attr("height", function (d) {
if(d.node == 3){
console.log(d.dy);
}
return d.dy;
})
var links = d3.selectAll(".link")
.transition().duration(750)
.attr('d', path)
.attr('opacity', 1.0)
Updated JSFiddle
I suspect I'm barking up the wrong tree - although I've got no errors in the console to help me out.
I've got this block of code:
var path = svg.data([json]).selectAll("path")
.data(partition.nodes)
.enter()
.append("path")
.attr("display", function(d) {
return d.depth ? null : "none";
})
.attr("d", arc)
.style("stroke", "#fff")
.style("fill", function(d) {
return color((d.children ? d : d.parent).name);
})
.attr("fill-rule", "evenodd")
.style("opacity", 1)
.each(stash);
//>>this section functions ok
path.append("title")
.text(function(d) {
return d.name + ": " + d.amount;
});
//>>this section throws no errors but "foo" does not appear
path.append("text")
.text(function(d) {
return "foo";
})
.style("stroke", "#3b5998")
.style("fill", "#3b5998");
The code snippet beginning path.append("title")... works ok but the final snippet beginning path.append("text")... adds the text foo to the html but it is not visible on the webpage - why is it not visible and how do I add labels?
This is part of this visual:
http://plnkr.co/edit/q9ODd5?p=preview
text cannot be nested with a path. You'll need to add it to the svg instead. Additionally, you'll want to position the text in some way:
svg.append("text")
.text(function(d) {
return "foo";
})
.attr("x", function(){ return 0 })
.attr("y", function(){ return 0 })
.style("stroke", "#3b5998")
.style("fill", "#3b5998");
The working code ended as the following:
var path = svg.data([json]).selectAll(".theArc")
.data(partition.nodes)
.enter()
.append("path")
.attr("class", "theArc") //<<<<<<<<<<new
.attr("id", function(d,i) { return "theArc_"+i; }) //Give each slice a unique ID //<<<<<<<<<<new jq
.attr("display", function(d) {
return d.depth ? null : "none";
})
.attr("d", arc)
.style("stroke", "#fff")
.style("fill", function(d) {
return color((d.children ? d : d.parent).name);
})
.attr("fill-rule", "evenodd")
.style("opacity", 1)
.each(stash);
path
.append("title") //mouseover title showing the figures
.text(function(d) {
return d.name + ": " + d.amount;
});
svg.data([json]).selectAll(".theTxts")
.data(partition.nodes)
.enter()
.append("text")
.attr("class", "theTxts")
.attr("dx", 10) //Move the text from the start angle of the arc
.attr("dy", 18) //Move the text down
.append("textPath")
.attr("xlink:href", function(d, i) {
return "#theArc_" + i;
})
.text(function(d) {
return d.name;
})
.style("stroke", "#3b5998")
.style("fill", "#3b5998");
I want to display one <circle> and <text> for each node. My code looks like this, having added the suggested code from the answer below. Please note the different
var width = 960,
height = 500;
var color = d3.scale.category10();
var nodes = [],
links = [];
var force = d3.layout.force()
.nodes(nodes)
.links(links)
.charge(-250)
.linkDistance(25)
.size([width, height])
.on("tick", tick);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
var node = svg.selectAll(".node")
.data(force.nodes(), function(d) { return d.id;}),
link = svg.selectAll(".link");
var text=svg.selectAll("text") //simply add text to svg
.data(force.nodes())
.enter()
.append("text")
.attr("class", "nodeText")
.attr("font-family", "sans-serif")
.attr("font-size", "10px")
.attr("fill", "red");
function start() {
link = link.data(force.links(), function(d) { return d.source.id + "-" + d.target.id; });
link.enter().insert("line", ".node")
.attr("class", function(d) { return "link " + d.edgeType; })
.attr("id", function(d) { return d.source.id + "-" + d.target.id; });
link.exit().remove();
v1: <line>s exist and are displayed, no <circle>s or <text> exist
var g = node.enter().append("g");
g.append("circle")
.attr("class", function(d) { return "node " + d.id; })
.attr("id", function(d) { return d.id; })
.attr("r", 8)
.on("click", nodeClick);
g.append("text")
.text(function(d) {return d.id; });
/v1
v2: <line>s and <circle>s exist and are displayed. <text>s exist within <circle>s but aren't displayed
node = node.data(force.nodes(), function(d) { return d.id;});
node.enter()
.append("circle")
.attr("class", function(d) { return "node " + d.id; })
.attr("id", function(d) { return d.id; })
.attr("r", 8)
.on("click", nodeClick);
node.append("text")
.text(function(d) {return d.id; });
/v2
node.exit().remove();
force.start();
}
function nodeClick() {
var node_id = event.target.id;
handleClick(node_id, "node");
}
function tick() {
text.attr("dx", function(d) { return d.x+5; })
.attr("dy", function(d) { return d.y+5; })
.text(function(d){return d.id});
node.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; });
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; });
}
Simply add text element as following in your js
var text=svg.selectAll("text") //simply add text to svg
.data(Your_nodes_array)
.enter()
.append("text")
.attr("class", "nodeText")
.attr("font-family", "sans-serif")
.attr("font-size", "10px")
.attr("fill", "red");
And inside tick do this :
text.attr("dx", function(d) { return d.x+5;}) //to keep it away from node
.attr("dy", function(d) { return d.y+5; })
.text(function(d){return d.id});
This will solve circle and text problems as this time we are adding text directly now