fix #8056 - more refinements to checking data
[g.raphael] / g.pie.sector.js
index 86f20cf..ded7dae 100644 (file)
@@ -19,24 +19,47 @@ Roo = typeof(Roo) != 'undefined' ? Roo:  (imports ? imports.seed.Roo.Roo: {});
      * @param {int} r - radius
      * @param {Array} values
      * @param {Object} opts options
-     *   cut : after this meany items - do not show a pie element?
-     *   
-     *   
-     * 
+     *   background (string) : background color
+     *   start_angle (number) : the angle of the starting position of the first pie
+     *   barwidth (number) : width of a pie
+     *   colors (array) : colors of the pies
+     *   cut (number) : after showing this number of elements using this number of pies, merge and show the rest of the elements using one pie (maximum 'cut' + 1 pies in total)
+     *   others (string) : legend label labelling the pie for the merged elements (*required if there are merged elements)
+     *   no_sort (boolean) : sort the values in descending order if it is not set
+     *   labels (array) : labels on the pie
+     *   labelfont (string) : font family of the labels
+     *   labelsize (number) : font size of the labels
+     *   labelweight (string)(number) : font weight of the labels
+     *   labelcolor (string) : font color of the labels
+     *   showlabel (number) : only show label if the value >= 'showlabel'% of total
+     *   legend (array) : legend
+     *   legendpos (string) : position of the legend ('right' / 'bottom')
+     *   legendkeyshape (string) : shape of the legend keys ('circle' / 'rect')
+     *   legendkeysize (number) : size of the legend keys (diameter for 'circle' and width for 'rect')
+     *   legendfont (string) : font family of the legend labels
+     *   legendfontsize (number) : font size of the legend labels
+     *   legendfontcolor (string) : font color of the legend colors
+     *   lineheight (number) : distance between two legend labels
+     *   legendcolumn (number) : number of columns used to show legend (1 / 2)
      */
 
     function Piesectorchart(paper, width, height, cx, cy, r, values, opts) {
-        
+         
         opts = opts || {};
 
         var chartinst = this,
             chart = paper.set(),
             len = values.length,
-            angle = opts.start_angle || 90,
             total = 0,
-            others = 0,
+            angle = opts.start_angle || 90, 
             cut = opts.cut || 9,
-            defcut = true;
+            defcut = true,
+            labels = opts.labels || false, // default no labels
+            labelFont = opts.labelfont || "'Fontin Sans', Fontin-Sans, sans-serif",
+            labelSize = opts.labelsize || 18,
+            labelWeight = opts.labelweight || 'normal',
+            labelColor = opts.labelcolor || '#FFFFFF',
+            showLabel = typeof(opts.showlabel) != 'undefined' ? opts.showlabel : 5; // default show label if value >= 5% of total
         
         opts.barwidth = opts.barwidth || 80;
         
@@ -55,22 +78,22 @@ Roo = typeof(Roo) != 'undefined' ? Roo:  (imports ? imports.seed.Roo.Roo: {});
 
             return {path: path, stroke: color};
         };
+
+        for (var i = 0; i < len; i++) {
+            total += values[i] * 1;
+            values[i] = {
+                value: values[i],
+                origin: i,
+                valueOf: function () { return this.value; }
+            };
+        }
         
         if (len == 1) {
-            total = values[0];
-            paper.circle(cx, cy, r + opts.barwidth / 2).attr({ fill: opts.colors && opts.colors[0] || chartinst.colors[0] || "#3E66BC" });
-            paper.circle(cx, cy, r - opts.barwidth / 2).attr({ fill: opts.background || "#F0F4F7" });
+            paper.circle(cx, cy, r + opts.barwidth / 2).attr({ fill: opts.colors && opts.colors[0] || chartinst.colors[0] || "#3E66BC", "stroke": "#fff" });
+            paper.circle(cx, cy, r - opts.barwidth / 2).attr({ fill: opts.background || "#F0F4F7", "stroke": "#fff" });
             
         } else {
             
-            for (var i = 0; i < len; i++) {
-                total += values[i] * 1;
-                values[i] = {
-                    value: values[i],
-                    valueOf: function () { return this.value; }
-                };
-            }
-            
             if (!opts.no_sort) {
                 values.sort(function (a, b) {
                     return b.value - a.value;
@@ -78,65 +101,153 @@ Roo = typeof(Roo) != 'undefined' ? Roo:  (imports ? imports.seed.Roo.Roo: {});
             }
             
             for (i = 0; i < len; i++) {
-                if (defcut && values[i] * 360 / total <= 1.5) {
-                    cut = i;
-                    defcut = false;
-                }
-
                 if (i > cut) {
                     defcut = false;
                     values[cut].value += values[i];
                     values[cut].others = true;
                 }
+
+                // minimum degree of a pie shown
+                if (defcut && values[i] * 360 / total <= 1.5) {
+                    cut = i;
+                    defcut = false;
+                }
             }
             
             len = Math.min(cut + 1, values.length);
+
+            var a = angle;
             
             for (i = 0; i < len; i++) {
-                
-                var p = paper.path().attr({
+                paper.path().attr({
                     "stroke": "#fff", 
                     "stroke-width": opts.barwidth
-                }).attr({sector: [cx, cy, angle, angle -= 360 * values[i] / total, opts.colors && opts.colors[i] || chartinst.colors[i], r]});
-            
+                }).attr({sector: [cx, cy, a, a -= 360 * values[i] / total, opts.colors && opts.colors[i] || chartinst.colors[i], r]});
             }
             
         }
+
+        // labels
+        var rad = Math.PI / 180;
+
+        var a = angle;
+
+        for (i = 0; i < len; i++) {
+
+            a -= 360 * values[i] / total;
+
+            // show the label only if the values >= 5% of total
+            if(labels && values[i] / total >= (showLabel / 100)) {
+                var text = labels[values[i].origin];
+
+                if(text.indexOf('#qty#') !== -1) {
+                    text = text.replace('#qty#', Roo.util.Format.number(Math.round(values[i]), 0));
+                }
+
+                if(text.indexOf('#%#') !== -1) {
+                    text = text.replace('#%#', Math.round(values[i] / total * 100) + '%');
+                }
+
+                var tx = cx + r * Math.cos(-(a + 180 * values[i] / total) * rad),
+                    ty = cy + r * Math.sin(-(a + 180 * values[i] / total) * rad);
+
+                paper.text(tx, ty, text).attr({ 
+                    "font-size": labelSize,
+                    "font-family": labelFont,
+                    "font-weight" : labelWeight,
+                    "text-anchor": "middle",
+                    fill : labelColor
+                });
+            }
+        }
+
         
-        var ix = cx + r + opts.barwidth / 2 + 30,
+        legend(paper, cx, cy, r, values, opts, total, len);
+
+        chart.cx = cx;
+        chart.cy = cy;
+        chart.r = r;
+        return chart;
+    }
+    
+    // draw legend
+    function legend(paper, cx, cy, r, values, opts, total, len) 
+    {
+        var chartinst = this,
+            legendPos = opts.legendpos || 'right',
+            legendKeyShape = opts.legendkeyshape || 'circle',
+            legendKeySize = opts.legendkeysize || 12,
+            legendFont = opts.legendfont || "'Fontin Sans', Fontin-Sans, sans-serif",
+            legendFontSize = opts.legendfontsize || 18,
+            legendFontColor = opts.legendfontcolor || '#0C014F',
+            lineHeight = opts.lineheight || 30,
+            legendColumn = opts.legendcolumn || 1;
+
+        // default 'legendPos' is 'right'
+        // ix, iy: center position of legend key
+        var ix = cx + r + opts.barwidth / 2 + 30, // 30 pixels away from right of the chart
             iy = cy - r - 30;
-        
+
+        if(legendPos == 'bottom') {
+            ix = cx - r - opts.barwidth / 2 - 30; // 30 pixels away from left of the chart
+            iy = cy + r + opts.barwidth / 2 + 30; // 30 pixels away from bottom of the chart
+
+            // default 'legendColumn' is 1
+            if(legendColumn == 2) {
+                ix = cx - r - opts.barwidth / 2 - 90; // 90 pixels away from left of the chart
+            }
+        }
+
         for (var i = 0; i < len; i++) {
             
-            paper.circle(ix, iy, 6).attr({ fill: opts.colors && opts.colors[i] || chartinst.colors[i] || "#3E66BC" });
-            
-            var text = (values[i].others) ? opts.others : opts.legend[i] || values[i];
+            if(legendKeyShape == 'rect') {
+                // pass top left position for 'rect'
+                paper.rect(ix - (legendKeySize / 2), iy - (legendKeySize / 2), legendKeySize, legendKeySize, 0).attr({ fill: opts.colors && opts.colors[i] || chartinst.colors[i] || "#3E66BC", "stroke": "#fff" });
+            }
+            else {
+                // pass center position for 'circle'
+                paper.circle(ix, iy, legendKeySize / 2).attr({ fill: opts.colors && opts.colors[i] || chartinst.colors[i] || "#3E66BC", "stroke": "#fff" });
+            }
+
+            var text = (values[i].others) ? opts.others : opts.legend[values[i].origin] || values[i];
             
             if(text.indexOf('#qty#') !== -1) {
-                text = text.replace('#qty#', Math.round(values[i]));
+                text = text.replace('#qty#', Roo.util.Format.number(Math.round(values[i]), 0));
             }
-            
+
             if(text.indexOf('#%#') !== -1) {
                 text = text.replace('#%#', Math.round(values[i] / total * 100) + '%');
             }
+
+            var ty = iy - legendFontSize / 10;
+
+            if(text.indexOf("\n")> -1) {
+                var ty = iy - legendFontSize / 10 + legendFontSize / 2;
+            }
             
-            paper.text(ix + 20, iy, text).attr({ 
-                "font-size": "18",
-                "font-family": "'Fontin Sans', Fontin-Sans, sans-serif",
+            // 12 pixels away from the right of legend key
+            // align legend key and text horizontally
+            paper.text(ix + legendKeySize / 2 + 12, ty, text).attr({ 
+                "font-size": legendFontSize,
+                "font-family": legendFont,
                 "text-anchor": "start",
-                fill : "#0C014F"
+                fill : legendFontColor
             });
-            
-            iy += 30;
-            
+
+            if(legendColumn == 2) {
+                if(i % 2 == 0) {
+                    ix += r + opts.barwidth / 2 + 120;
+                    iy -= lineHeight;
+                }
+                else {
+                    ix -= r + opts.barwidth / 2 + 120;
+                }
+            }
+
+            iy += lineHeight;
         }
+    }
 
-        chart.cx = cx;
-        chart.cy = cy;
-        chart.r = r;
-        return chart;
-    };
-    
     //inheritance
     var F = function() {};
     F.prototype = Raphael.g;