How to add text to Cluster Force Layout bubbles - d3.js

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

Related

How can I add title text to this chart in d3?

I'm following this tutorial for a D3 Circle Pack chart. https://bl.ocks.org/denjn5/6d5ddd4226506d644bb20062fc60b53f
How can I add the text from the ID column of the CSV to each circle?
Instead of creating a circle, you can create a group and have a circle and a text inside it.
So instead of:
// Draw on screen
vSlices.attr('cx', function (d) { return d.x; })
.attr('cy', function (d) { return d.y; })
.attr('r', function (d) { return d.r; });
You would do:
var gNode = vSlices
.enter()
.append("g")
.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
});
gNode.append("circle").attr("r", function(d) {
return d.r;
});
gNode
.append("text")
.style("font", "10px sans-serif")
.attr("text-anchor", "middle")
.text(function(d) {
return d.data.id;
});
Notice that the translation is being done in the group and the attributes are different.
I hope it helps.

How to apply text transition to number in d3

I am new to d3. I created a bar chart. Append text and percentage in the bar chart with animation. When bar chart draw then the number and percentage go from bottom to top to the desired location. Here is the code
svg.selectAll(".bar")
.data(data)
.enter()
.append("g")
.attr("class", "g rect")
.append("rect")
.attr("class", "bar")
.attr("x", function(d) { return x(d.label); })
.attr("y", h)
.on("mouseover", onMouseOver) //Add listener for the mouseover event
... // attach other events
.transition()
.ease(d3.easeLinear)
.duration(2000)
.delay(function (d, i) {
return i * 50;
})
.attr("y", function(d) { return y(d.percentage.slice(0, -1)); })
.attr("width", x.bandwidth() - 15) // v4’s console.log(bands.bandwidth()) replaced v3’s console.log(bands.rangeband()).
.attr("height", function(d) { return h - y(d.percentage.slice(0, -1)); }) // use .slice to remove percentage sign at the end of number
.attr("fill", function(d) { return d.color; });
var legend = svg.append("g");
svg.selectAll(".g.rect").append("text")
.text(function(d) { return d.value })
.attr("x", function(d) { return x(d.label) + x.bandwidth() / 2 - 15; })
.attr("y", h)
.transition()
.ease(d3.easeLinear)
.duration(2000)
.attr("y", function(d) { return y(d.percentage.slice(0, -1) / 2);}) // use slice to remove percentage sign from the end of number
.attr("dy", ".35em")
.style("stroke", "papayawhip")
.style("fill", "papayawhip");
svg.selectAll(".g.rect").append("text")
.text(function(d) { return d.percentage; })
.attr("x", function(d) { return x(d.label) + x.bandwidth() / 2 - 20; })
.attr("y", h)
.transition()
.ease(d3.easeLinear)
.duration(2000)
.attr("y", function(d) { return y(d.percentage.slice(0, -1)) - 10; }) // use slice to remove percentage sign from the end of number
.attr("dy", ".35em")
.attr("fill", function(d) { return d.color; });
Now I want to apply text transition. Like instead of just printing say 90%(d.percentage). I want that it starts from 0 and goes to d.percentage gradually. How can I apply text transition in this case. I tried the following but it didn't work
svg.selectAll(".g.rect").append("text")
.text(function(d) { return d.percentage; })
.attr("x", function(d) { return x(d.label) + x.bandwidth() / 2 - 20; })
.attr("y", h)
.transition()
.ease(d3.easeLinear)
.duration(2000)
.tween("text", function(d) {
var i = d3.interpolate(0, d.percentage.slice(0, -1));
return function(t) {
d3.select(this).text(i(t));
};
})
.attr("y", function(d) { return y(d.percentage.slice(0, -1)) - 10; }) // use slice to remove percentage sign from the end of number
.attr("dy", ".35em")
.attr("fill", function(d) { return d.color; });
The problem is the this value.
Save it in the closure (that).
Use a number interpolator so you can round the result to a number of decimals.
var ptag = d3.select('body').append('p');
ptag.transition()
.ease(d3.easeLinear)
.duration(2000)
.tween("text", function(d) {
var that = this;
var i = d3.interpolate(0, 90); // Number(d.percentage.slice(0, -1))
return function(t) {
d3.select(that).text(i(t).toFixed(2));
};
})
<script src="https://d3js.org/d3.v5.min.js"></script>
Your problem is that you return function(t) { ... } and try to access this of parent function inside. The solution is to return arrow function, which does not shadow this value:
return t => d3.select(this).text(i(t));
(by the way, you may also want to round percentage before printing)
-------------Edit --------------
Here is the working code
var numberFormat = d3.format("d");
svg.selectAll(".g.rect").append("text")
.attr("x", function(d) { return x(d.label) + x.bandwidth() / 2 - 20; })
.attr("y", h)
.transition()
.ease(d3.easeLinear)
.duration(2000)
.tween("text", function(d) {
var element = d3.select(this);
var i = d3.interpolate(0, d.percentage.slice(0, -1));
return function(t) {
var percent = numberFormat(i(t));
element.text(percent + "%");
};
//return t => element.text(format(i(t)));
})
.attr("y", function(d) { return y(d.percentage.slice(0, -1)) - 10; }) // use slice to remove percentage sign from the end of number
.attr("dy", ".35em")
.attr("fill", function(d) { return d.color; });
Thanks :)

d3.js migrate sankey drag and drop funcitonality to v4

Anyone managed to make the example
Sankey diagram with horizontal and vertical node movement
work in v4
since sankey.relayout() is not available anymore
d3.drag has no .origin anymore.
My attempt do the wildest things while attempting to drag a node and since some behaviors have changed in both sankey and drag specifications I'm unable to figure how to migrate the example to v4.
var graph = { "nodes": [...], "links": [...] };
var layout = d3.sankey();
layout
.extent([[1, 1], [width - 1, height - 6]])
.nodeId(function (d) {
return d.id;
})
.nodeWidth(12)
.nodePadding(padding)
.nodeAlign(d3.sankeyRight)
layout(graph);
// Add Links
var link = svg.append("g")
.attr("fill", "none")
.selectAll(".link")
.data(graph.links)
.enter()
.append("g")
.attr("class", "link")
link.append("path")
.attr("d", d3.sankeyLinkHorizontal())
.attr("stroke", "#000")
.style("stroke-width", function (d) {
return d.width;
})
.append("title")
.text("Some text");
// Drag behavior for node elements
var drag = d3.drag()
//.origin(function (d) { return d; }) // Deprecated but maybe unnecessary
.on("drag", dragmove)
// Add nodes
var node = svg.append("g")
.selectAll(".node")
.data(graph.nodes)
.enter()
.append("g")
.attr("transform", function (d) {
return "translate(" + [d.x0, d.y0] + ")";
})
.call(drag) // The g element should be now draggable
// Add element inside g element
node.append("rect")
.attr("height", function (d) { return d.y1 - d.y0; })
.attr("width", function (d) { return d.x1 - d.x0; })
.attr("fill", ...)
node.append("text")
.attr("x", function (d) { return (d.x1 - d.x0) - 6; })
.attr("y", function (d) { return (d.y1 - d.y0) / 2; })
.attr("dy", "0.35em")
.attr("text-anchor", "end")
.text(function (d) { return d.name; })
.filter(function (d) { return d.x0 < width / 2; })
.attr("x", function (d) { return (d.x1 - d.x0) + 6 })
.attr("text-anchor", "start");
// Called by d3.drag
function dragmove(d) {
var dx = Math.round(d.x = Math.max(0, Math.min(width , evt.x)));
var dy = Math.round(d.y = Math.max(0, Math.min(height, evt.y)));
d3.select(this).attr("transform", "translate(" + [dx, dy] + ")")
// Now should redraw the links but
sankey.relayout(); // not a function anymore.
// path references sankey.link() that is also deprecated in v4 in
// favour of d3.sankeyLinkHorizontal() ? (I'm not sure)
// link references the g element containing the path elements
// classed .link
link.attr("d", path);
};

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);

Resources