Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

d3js Redistributing labels around a pie chart

I'm using d3.js to create a donut chart with labels on the outside. Using some trigonometry based on the centroids of each slice of the pie, I position the labels.

g.append("g")
        .attr("class", "percentage")
        .append("text")
            .attr("transform", function(d)
                { 
                    var c = arc.centroid(d);
                    var x = c[0];
                    var y = c[1];
                    var h = Math.sqrt(x*x + y*y);
                    return "translate(" + (x/h * obj.labelRadius) +  ',' + (y/h * obj.labelRadius) +  ")"; 
                }
            ) 
            .attr("dy", ".4em")
            .attr("text-anchor", function(d) 
                {
                    return (d.endAngle + d.startAngle)/2 > Math.PI ? "end" : "start";
                }
            )
            .text(function(d) { return d.data.percentage+"%"; });

What I'm ultimately trying to accomplish is to rearrange labels that are outside the edges of the pie chart, to prevent overlaps.

enter image description here

One of the ways I have tried to solve the problem is to define set "anchor points", where labels can be positioned, guaranteeing that they will no overlap. Problem is mapping the centroids to the anchors and preserving some sense of visual correspondence between the slices and the labels (Specially difficult when slices are slim).

enter image description here

Image above shows the possible location of the anchors (centroids of the slices shown). With these positions it is impossible to have an overlap.

Adding complexity to the problem is the fact that when labels (they're horizontal) are close to the top or bottom of the pie, they are more easily overlapped, than when they are on the right or left of the pie.

Any ideas on how to approach this problem?

[EDIT] Following the suggestion of meetamit, I implemented the following:

.attr("dy", function(d)
{
    var c = arc.centroid(d);
        var x = c[0];
        var y = c[1];
        var h = Math.sqrt(x*x + y*y);
        var dy = y/h * obj.labelRadius; 
    dy=dy*fontSizeParam*.14/heightParam);
    return (dy)+"em";
})

It helps a bit, and gives some room to the labels, still looking for a solution that will cover all cases though...

like image 872
Cenobyte321 Avatar asked Apr 22 '13 19:04

Cenobyte321


People also ask

How do you put labels on a pie chart?

Add Labels to the Chart To add labels, right-click on any slice in the pie, then click Add Data Labels, in the popup menu.

How will you invoke the arc path generator in d3js?

d3. arc() returns a generator that when called automatically generates and returns a string of characters that can be assigned to the d attribute of a path element to define an arc, circle, or annulus. To create an arc generator simply call d3. arc() .


2 Answers

Can't you create two arcs? one for the chart, and one for the labels?

// first arc used for drawing the pie chart
var arc = d3.svg.arc()
  .outerRadius(radius - 10)
  .innerRadius(0);

// label attached to first arc
g.append("text")
  .attr("transform", function(d) { return "translate(" + arc.centroid(d) + ")"; })
  .attr("dy", ".35em")
  .style("text-anchor", "middle")
  .text(function(d) { return d.data.age; });

// second arc for labels
var arc2 = d3.svg.arc()
  .outerRadius(radius + 20)
  .innerRadius(radius + 20);

// label attached to second arc
g.append("text")
  .attr("transform", function(d) { return "translate(" + arc2.centroid(d) + ")"; })
  .attr("dy", ".35em")
  .style("text-anchor", "middle")
  .text(function(d) { return d.data.age; });
like image 180
Kim T Avatar answered Sep 25 '22 03:09

Kim T


There is a good, pragmatic d3.js-based solution by programmer John Williams presented here:

https://www.safaribooksonline.com/blog/2014/03/11/solving-d3-label-placement-constraint-relaxing/

It should work well for cases with reasonable restrictions, e.g. a maximum of 12 labels as discussed above. There are also pointers in the article to more advanced algorithms, but this simple approach may actually, when used with sufficient label-content constraints, give results that have a more orderly visual appearance than other methods would yield.

like image 37
Otto G Avatar answered Sep 25 '22 03:09

Otto G