overlapping labels in flot pie chart

alexarsh picture alexarsh · Feb 29, 2012 · Viewed 9.9k times · Source

I use jquery flot for my pie charts and I have a problem with overlapping labels when the pie chart pieces are very small. Is there a good solution for that?

My pie chart:

series: { 
                pie: { 
                    show: true, 
                    radius: 1, 
                    label: { 
                        show: true, 
                        radius: 5/8, 
                        formatter: function(label, series){ 
                            return '<div style="font-size:12pt;text-  align:center;padding:2px;color:black;margin-left:-80%;margin-  top:-20%;">'+label+'<br/>'+Math.round(series.percent)+'%</div>'; 
                        }, 
                        background: { opacity: 0.5 } 
                    } 
                } 
            }, 
            legend: { 
                show: false 
            }

Thanks, Arshavski Alexander.

Answer

Nasser Al-Wohaibi picture Nasser Al-Wohaibi · Jul 1, 2012

A solution from Flot's Google code issues by Marshall Leggett(link):

I've found that it seems common for pie labels to overlap in smaller pie charts making them unreadable, particularly if several slices have small percentage values. This is with the jquery.flot.pie plugin.
Please see attached images. I've worked around this with the addition of an anti-collision routine in the label rendering code. I'm attaching a copy of the revised plugin as well. See lines 472-501, particularly the new functions getPositions() and comparePositions(). This is based in part on Šime Vidas' DOM-element collision detection code. Something like this might be a nice addition to the pie library.

pie labels overlapping pie labels overlapping fixed

long story short:

  1. In jquery.flot.pie.js and after the line 463 that contains:

    label.css('left', labelLeft);

add the following code:

// check to make sure that the label doesn't overlap one of the other labels
var label_pos = getPositions(label);
for(var j=0; j<labels.length; j++)
{
var tmpPos = getPositions(labels[j]);
var horizontalMatch = comparePositions(label_pos[0], tmpPos[0]);
var verticalMatch = comparePositions(label_pos[1], tmpPos[1]);                  
var match = horizontalMatch && verticalMatch;                           
if(match)
{
    var newTop = tmpPos[1][0] - (label.height() +1 );
    label.css('top', newTop);
    labelTop = newTop;
}       
}

function getPositions(box) {
        var $box = $(box);
        var pos = $box.position();
        var width = $box.width();
        var height = $box.height();
        return [ [ pos.left, pos.left + width ], [ pos.top, pos.top + height ] ];
}

function comparePositions(p1, p2) {
        var x1 = p1[0] < p2[0] ? p1 : p2;
        var x2 = p1[0] < p2[0] ? p2 : p1;
        return x1[1] > x2[0] || x1[0] === x2[0] ? true : false;
}
labels.push(label);
  1. Add the following to drawLabels() and you are done:

    var labels = [];