Adding labels to groups in d3 force layout - d3.js

Is there a way to add labels to the groups for the d3-cola force layout example here
I have added tooltips:
var group = svg.selectAll(".group")
.data(groups)
.enter().append("rect")
.classed("group", true)
.attr("rx",20)
.attr("ry",20)
.style("fill", function (d) { return color(d.id); })
.call(cola.drag);
group.append("title")
.text(function (d) { return d.id; });
I tried adding this part:
var groupText = svg.selectAll(".group")
.append("text")
.attr("class", "link-label")
.attr("font-family", "Arial, Helvetica, sans-serif")
.attr("fill", "Black")
.style("font", "normal 9px Arial")
.attr("dy", ".35em")
.attr("text-anchor", "middle")
.text(function(d) {
return (d.id);
});
And then in the tick function:
groupText
.attr("x", function(d) {
return ((d.bounds.x + d.bounds.width())/2);
})
.attr("y", function(d) {
return ((d.bounds.y + d.bounds.height())/2);
});
But it doesn't show up.

Related

Replace space with new line charcter in texts d3js

I have a script in which I make txts in the following way:
texts = svg.selectAll(null)
.data(data)
.enter()
.append('text')
.attr("text-anchor", "middle")
.text(function(d) {
return d.abbreviation;
})
.attr("pointer-events", "none")
.attr("font-family", "sans-serif")
.attr("font-size", "10px")
.attr("fill", "black");
texts.each(function(d) {
console.log(this.getComputedTextLength());
d.size = this.getComputedTextLength() / 2 ;
});
simulation.nodes(data).on("tick", function() {
circles.attr("cx", function(d) {
return d.x = Math.max(20, Math.min(width - 20, d.x));
})
.attr("cy", function(d) {
return d.y = Math.max(20, Math.min(height - 20, d.y));
})
texts.attr("x", function(d) {
return d.x;
})
.attr("y", function(d) {
return d.y;
});
});
I am using property abbreviation in data to put text over the bubbles but I want to replace all spaces in input with new line character.
I tried some soutions like given at this link: How to linebreak an svg text in javascript?
but all texts go to left most corner or if I remove the .attr("x", 0) property from this, then the alignment of texts in not right. See picture below:
"State" should come directly below "Iowa"
Updated script:
texts = svg.selectAll(null)
.data(data)
.enter()
.append('text')
.attr("text-anchor", "middle")
.each(function (d) {
var arr = d.abbreviation.split(" ");
for (i = 0; i < arr.length; i++) {
d3.select(this).append("tspan")
.text(arr[i])
.attr("dy", i ? "1.2em" : 0)
.attr("text-anchor", "middle")
.attr("class", "tspan" + i);
}
})
.attr("pointer-events", "none")
.attr("font-family", "sans-serif")
.attr("font-size", "10px")
.attr("fill", "black");
What should I do to make the alignment right or is there any other way to do this?
There are different ways to fix this. An easy one is appending the <tspan> to a <g> element, setting all their x properties to 0 and text-anchor to middle.
Have a look at the demo:
var svg = d3.select("svg");
var data = [{
text: "some text"
}, {
text: "a longer text here"
}, {
text: "an even longer text here"
}, {
text: "short text"
}, {
text: "a long text"
}];
var texts = svg.selectAll(null)
.data(data)
.enter()
.append("g");
texts.append("text")
.attr("text-anchor", "middle")
.each(function(d) {
var arr = d.text.split(" ");
d3.select(this).selectAll(null)
.data(arr)
.enter()
.append("tspan")
.attr("text-anchor", "middle")
.attr("x", 0)
.attr("dy", function(d, i) {
return "1.2em"
})
.text(String)
})
var simulation = d3.forceSimulation(data)
.force("center", d3.forceCenter(200, 100))
.force("collide", d3.forceCollide(40))
.on("tick", tick);
function tick() {
texts.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")"
})
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<svg width="400" height="250"></svg>
PS: Don't use that for loop inside the each. That's not idiomatic D3. Instead of that, just use an enter selection (refer to the demo to see how to do it).

How to fit text inside nodes at bottom in pack layout in d3?

I created d3 pack layout.Text is added in nodes. Requirement is text should be fit in circular nodes in such way that Text should be at bottom, proportional to node size and should be fit inside node.but text is either overlapping or hides behind another node or not proportional to nodes as in JSFiddle examples
http://jsfiddle.net/oo66o0q0/11/,
http://jsfiddle.net/oo66o0q0/12/,
http://jsfiddle.net/oo66o0q0/13/
.So how to resolve all issue with one code?
function treeLayout(nodes){
var node = diagramLayout.selectAll(".node");
node = node.data(nodes.descendants(), function(d) {
return d.uid;
});
var nodeEnter = node.enter().append("g")
.attr("class", "node")
.attr("height", nodeHeight)
.attr("width", nodeWidth)
nodeEnter.append("circle")
.attr("id", function(d) { return "node-" + d.data.name; })
.attr("r", function(d) { return d.r; })
.style("fill", function(d) { return color(d.depth); });
nodeEnter.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });
var nodeText = nodeEnter.append("text")
.text(function(d) { return d.data.name; })
.attr("clip-path", function(d) { return "url(#clip-" + d.data.name + ")"; })
.style("text-anchor", "middle")
.style("stroke", "#000")
.style("stroke-width", "0.1px")
.attr("y", function(d) {
return d.r-(d.r/d.data.name.length);
})
.style("font-family", "sans-serif")
.style("font-size", function(d) {
return d.r/(d.data.name.length)+ "px";
})
}
}

Node names disappears when user mouse hover on the node name for d3 sankey layout on edge browser windows 10

I created sankey layout in d3. tooltips added on links and nodes as shown in given Jfiddle http://jsfiddle.net/n7f2dkt0/39/
Issue is Node names disappears when user mouse hover on the node name for sankey layout on edge browser.how to resolve this issue?
screenshot for error
var link = svg.append("g").selectAll(".link")
.data(energy.links)
.enter().append("path")
.attr("class", "link")
.attr("d", path)
.style("stroke-width", function(d) { return Math.max(1, d.dy); })
.sort(function(a, b) { return b.dy - a.dy; });
link.on("mouseover", mouseoverLink)
link.on("mouseout", mouseoutLink);
var node = svg.append("g").selectAll(".node")
.data(energy.nodes)
.enter().append("g")
.attr("class", "node")
.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; })
.call(drag);
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("stroke", function(d) { return d3.rgb(d.color).darker(2); })
node.append("text")
.attr("x", -6)
.attr("y", function(d) { return d.dy / 2; })
.attr("dy", ".35em")
.attr("text-anchor", "end")
.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.on("mouseover", mouseover);
node.on("mouseout", mouseout);

How to add text to Cluster Force Layout bubbles

I am trying to add some text from the name field in my JSON file to each bubble in a cluster.
https://plnkr.co/edit/hwxxG34Z2wYZ0bc51Hgu?p=preview
I have added what I thought was correct attributes to the nodes with
node.append("text")
.text(function (d) {
return d.name;
})
.attr("dx", -10)
.attr("dy", "5em")
.text(function (d) {
return d.name
})
.style("stroke", "white");
function tick(e) {
node.each(cluster(10 * e.alpha * e.alpha))
.each(collide(.5))
.attr("transform", function (d) {
var k = "translate(" + d.x + "," + d.y + ")";
return k;
})
}
Everything works fine except the labels.
What am I missing here?
Thanks
Kev
For that you will need to make a group like this:
var node = svg.selectAll("g")
.data(nodes)
.enter().append("g").call(force.drag);//add drag to the group
To the group add circle.
var circles = node.append("circle")
.style("fill", function(d) {
return color(d.cluster);
})
To the group add text:
node.append("text")
.text(function(d) {
return d.name;
})
.attr("dx", -10)
.text(function(d) {
return d.name
})
.style("stroke", "white");
Add tween to the circle in group like this:
node.selectAll("circle").transition()
.duration(750)
.delay(function(d, i) {
return i * 5;
})
.attrTween("r", function(d) {
var i = d3.interpolate(0, d.radius);
return function(t) {
return d.radius = i(t);
};
});
Now the tick method will translate the group and with the group the circle and text will take its position.
working code here
The problem: a text SVG element cannot be child of a circle SVG element.
The solution is creating another selection for the texts:
var nodeText = svg.selectAll(".nodeText")
.data(nodes)
.enter().append("text")
.text(function (d) {
return d.name;
})
.attr("text-anchor", "middle")
.style("stroke", "white")
.call(force.drag);
Here is the plunker: https://plnkr.co/edit/qnx7CQox0ge89zBL9jxc?p=preview

How to add a background color to d3 text elements?

I'm trying to add a rect element behind text with d3 to simulate background-color which doesn't exist for d3 text elements. I would like the rect to have the exact same size as the text itself.
node.append("text")
.attr("class", "text")
.attr("text-anchor", "middle")
.attr("dx", 0)
.attr("dy", ".35em")
.text(function(d) {
var bbox = this.getBBox();
node.insert("rect",":first-child")
.attr("x", bbox.x)
.attr("y", bbox.y)
.attr("width", bbox.width)
.attr("height", bbox.height)
.style("fill", "yellow");
return d.name;
});
this.getBBox() returns 0 for both x and y.
The following code display the box, but it size doesn't very with text size, and the box is drawn even when text is not (when an image exists).
node.filter(function(d) {return (!d.image)}).append("text")
.attr("class", function(d) { return "text "+d.type; })
.attr("text-anchor", "middle")
.attr("dx", 0)
.attr("dy", ".35em")
//.text(function(d) { if (!d.imgB64) { return d.label; }
.text(function(d) {
return d.name;
})
.each(function(d) {
var bbox = this.getBBox();
node.insert("rect", "text")
.style("fill", "#FFE6F0")
.attr("x", bbox.x)
.attr("y", bbox.y)
.attr("width", bbox.width)
.attr("height", bbox.height);
});
SOLUTION
Thanks to Cool Blue, the following code now properly works: display a rect behind the text so that it is readable when larger than the node circle. In the futur it could be improved with a sphere arc rather than the rect to only hide the circle frame behind the text...
// only display node labels if node has no image
node.filter(function(d) {return (!d.image)}).append("text")
.attr("class", function(d) { return "text "+d.type; })
.attr("text-anchor", "middle")
.attr("dx", 0)
.attr("dy", ".35em")
.text(function(d) {
return d.name;
})
.call(getTextBox);
// only display a rect behind labels if node has no image
node.filter(function(d) {return (!d.image)}).insert("rect","text")
.attr("x", function(d){return d.bbox.x})
.attr("y", function(d){return d.bbox.y})
.attr("width", function(d){return d.bbox.width})
.attr("height", function(d){return d.bbox.height})
.style("fill", "#FFE6F0");
function getTextBox(selection) {
selection.each(function(d) { d.bbox = this.getBBox(); })
}
As mentioned in the comments, use this pattern and add whatever details you need...
var textNode = node.filter(function(d) {return (!d.image)})
textNode.append("text")
.attr("class", "text")
.attr("text-anchor", "middle")
.attr("dx", 0)
.attr("dy", ".35em")
.text(function(d) {
return d.name;
}).call(getBB);
textNode.insert("rect","text")
.attr("width", function(d){return d.bbox.width})
.attr("height", function(d){return d.bbox.height})
.style("fill", "yellow");
function getBB(selection) {
selection.each(function(d){d.bbox = this.getBBox();})
}

Resources