0

I'm working on a donut chart using D3.js. I have it doing everything fine except that the labels for each section are being drawn on top of each other as the pieces get smaller. You can see this in the screenshot below.

Screenshot of the issue

I can't figure out how to get more spacing or flip the label to the other side.

Here is the relevant code.

var slice = chart.select(".slices").selectAll("path.slice").data(pie(data), key);

                    slice.enter()
                        .insert("path")
                        .style("fill", function(d,i) {return color(i);})
                        .attr("class", "slice")
                        .attr("data-label", function(d){ return d.data.label();})
                        .attr("data-value", function(d){ return d.data.value();});

                    slice.transition().ease("exp").duration(animationTime * 2)
                            .attrTween("d", function(d){                                        
                                    var interpolater = d3.interpolate({startAngle: 2*Math.PI, endAngle: 2*Math.PI}, d);
                                    return function(t) {
                                        return arc(interpolater(t));
                                    }
                            })
                    slice.exit().remove();


                    var text = chart.select(".labels").selectAll("text")
                        .data(pie(data), key);

                    text.enter()
                        .append("text")
                        .attr("dy", ".35em")
                        .style("font-size", "11pt")
                        .style("fill", "#8FA1A9")
                        .text(function(d) {
                            if(d.data.value() != 0){
                                return d.data.label();
                            }
                            else{
                                return "";
                            }
                        });

                    function midAngle(d){
                        return d.startAngle + (d.endAngle - d.startAngle)/2;
                    }

                    text.transition().duration(500)
                        .attrTween("transform", function(d) {
                            this._current = this._current || d;
                            var interpolate = d3.interpolate(this._current, d);
                            this._current = interpolate(0);
                            return function(t) {
                                var d2 = interpolate(t);
                                var pos = outerArc.centroid(d2);
                                pos[0] = radius * 0.75 * (midAngle(d2) < Math.PI ? 1 : -1);
                                return "translate("+ pos +")";
                            };
                        })
                        .styleTween("text-anchor", function(d){
                            this._current = this._current || d;
                            var interpolate = d3.interpolate(this._current, d);
                            this._current = interpolate(0);
                            return function(t) {
                                var d2 = interpolate(t);
                                return midAngle(d2) < Math.PI ? "start":"end";
                            };
                        });

                    text.exit().remove();


                    var polyline = chart.select(".lines").selectAll("polyline")
                        .data(pie(data), key);

                    polyline.enter()
                        .append("polyline")
                        .style("fill", "none")
                        .style("stroke-width", "2px")
                        .style("stroke", "#8FA1A9")
                        .style("opacity", "0.4");

                    polyline.transition().duration(500)
                        .attrTween("points", function(d){
                            this._current = this._current || d;
                            var interpolate = d3.interpolate(this._current, d);
                            this._current = interpolate(0);
                            return function(t) {
                                var d2 = interpolate(t);
                                var pos = outerArc.centroid(d2);
                                pos[0] = radius * 0.75 * (midAngle(d2) < Math.PI ? 1 : -1);
                                return [arc.centroid(d2), outerArc.centroid(d2), pos];
                            };          
                        });

                    polyline.exit().remove();
                };

I will be honest and say I don't entirely understand all the code in there for drawing the lines and labels, so any insight or help is appreciated.

HarvP
  • 190
  • 3
  • 13
  • You could use the force layout to position the labels as I've done in [this example](http://www.larsko.org/v/igdp/index-alt.html). Note that this is non-trivial though. – Lars Kotthoff Jul 29 '14 at 20:28
  • Its an option, but that would be quite a bit of code to fix an issue like this. – HarvP Jul 29 '14 at 20:54
  • If you're looking for quick and dirty, I can think of two options. One is to filter out small data for the labels, so small values are simply unlabeled. The other is to stagger your ordering, large slice, small slice, large slice etc. Neither is a perfect solution, but otherwise you're looking at a lot of legwork – jshanley Jul 29 '14 at 21:02
  • you really need an algorithm to do this properly. but an if then to calculate the distance between the labels would fix this case. – Union find Jul 30 '14 at 00:38
  • [This question](http://stackoverflow.com/questions/17425268/d3js-automatic-labels-placement-to-avoid-overlaps-force-repulsion) is also relevant. – Lars Kotthoff Jul 30 '14 at 09:00

0 Answers0