4b891dd3641810c73b66d890ba079966816e5d4f
[g.raphael] / g.pie.sector.js
1 /*
2  * g.Raphael 0.5 - Charting library, based on RaphaĆ«l
3  *
4  * Copyright (c) 2009 Dmitry Baranovskiy (http://g.raphaeljs.com)
5  * Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license.
6  */
7 Raphael = typeof(Raphael) != 'undefined' ? Raphael :  (imports ? imports.seed.Raphael.Raphael : {});
8 Roo = typeof(Roo) != 'undefined' ? Roo:  (imports ? imports.seed.Roo.Roo: {});
9 //chartinst = typeof(chartinst) != 'undefined' ? chartinst:  (imports ? imports.chartinst.chartinst : {});
10  
11
12
13 (function () {
14
15     /**
16      * @param {Raphael} paper to draw on
17      * @param {int} cx - centre X
18      * @param {int} cy - centre Y
19      * @param {int} r - radius
20      * @param {Array} values
21      * @param {Object} opts options
22      *   background (string) : background color
23      *   start_angle (number) : the angle of the starting position of the first pie
24      *   barwidth (number) : width of a pie
25      *   colors (array) : colors of the pies
26      *   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)
27      *   others (string) : legend label labelling the pie for the merged elements (*required if there are merged elements)
28      *   no_sort (boolean) : sort the values in descending order if it is not set
29      *   labels (array) : labels on the pie
30      *   labelfont (string) : font family of the labels
31      *   labelsize (number) : font size of the labels
32      *   labelweight (string)(number) : font weight of the labels
33      *   labelcolor (string) : font color of the labels
34      *   legend (array) : legend
35      *   legendpos (string) : position of the legend ('right' / 'bottom')
36      *   legendkeyshape (string) : shape of the legend keys ('circle' / 'rect')
37      *   legendkeysize (number) : size of the legend keys (diameter for 'circle' and width for 'rect')
38      *   legendfont (string) : font family of the legend labels
39      *   legendfontsize (number) : font size of the legend labels
40      *   legendfontcolor (string) : font color of the legend colors
41      *   lineheight (number) : distance between two legend labels
42      *   legendcolumn (number) : number of columns used to show legend (1 / 2)
43      */
44
45     function Piesectorchart(paper, width, height, cx, cy, r, values, opts) {
46         
47         opts = opts || {};
48
49         var chartinst = this,
50             chart = paper.set(),
51             len = values.length,
52             total = 0,
53             angle = opts.start_angle || 90, 
54             cut = opts.cut || 9,
55             defcut = true,
56             labels = opts.labels || false, // default no labels
57             labelFont = opts.labelfont || "'Fontin Sans', Fontin-Sans, sans-serif",
58             labelSize = opts.labelsize || 18,
59             labelWeight = opts.labelweight || 'normal',
60             labelColor = opts.labelcolor || '#FFFFFF';
61         
62         opts.barwidth = opts.barwidth || 80;
63         
64         paper.rect(0, 0, width, height).attr({ stroke: "none", fill: (opts.background || "#F0F4F7") });
65         
66         paper.customAttributes.sector = function (cx, cy, startAngle, endAngle, color, R) {
67             
68             var rad = Math.PI / 180,
69                 x1 = cx + r * Math.cos(-startAngle * rad),
70                 x2 = cx + r * Math.cos(-endAngle * rad),
71                 y1 = cy + r * Math.sin(-startAngle * rad),
72                 y2 = cy + r * Math.sin(-endAngle * rad),
73                 path;
74         
75             path = [["M", x1, y1], ["A", R, R, 0, +(Math.abs(endAngle - startAngle) > 180), 1, x2, y2]];
76
77             return {path: path, stroke: color};
78         };
79
80         for (var i = 0; i < len; i++) {
81             total += values[i] * 1;
82             values[i] = {
83                 value: values[i],
84                 origin: i,
85                 valueOf: function () { return this.value; }
86             };
87         }
88         
89         if (len == 1) {
90             paper.circle(cx, cy, r + opts.barwidth / 2).attr({ fill: opts.colors && opts.colors[0] || chartinst.colors[0] || "#3E66BC", "stroke": "#fff" });
91             paper.circle(cx, cy, r - opts.barwidth / 2).attr({ fill: opts.background || "#F0F4F7", "stroke": "#fff" });
92             
93         } else {
94             
95             if (!opts.no_sort) {
96                 values.sort(function (a, b) {
97                     return b.value - a.value;
98                 });
99             }
100             
101             for (i = 0; i < len; i++) {
102                 // minimum degree of a pie shown
103                 if (defcut && values[i] * 360 / total <= 1.5) {
104                     cut = i;
105                     defcut = false;
106                 }
107
108                 if (i > cut) {
109                     defcut = false;
110                     values[cut].value += values[i];
111                     values[cut].others = true;
112                 }
113             }
114             
115             len = Math.min(cut + 1, values.length);
116
117             var a = angle;
118             
119             for (i = 0; i < len; i++) {
120                 paper.path().attr({
121                     "stroke": "#fff", 
122                     "stroke-width": opts.barwidth
123                 }).attr({sector: [cx, cy, a, a -= 360 * values[i] / total, opts.colors && opts.colors[i] || chartinst.colors[i], r]});
124             }
125             
126         }
127
128         // labels
129         var rad = Math.PI / 180;
130
131         var a = angle;
132
133         for (i = 0; i < len; i++) {
134
135             a -= 360 * values[i] / total;
136
137             // show the label only if the values >= 5% of total
138             if(labels && values[i] / total >= 0.05) {
139                 var text = labels[i];
140
141                 if(text.indexOf('#qty#') !== -1) {
142                     text = text.replace('#qty#', Math.round(values[i]));
143                 }
144
145                 if(text.indexOf('#%#') !== -1) {
146                     text = text.replace('#%#', Math.round(values[i] / total * 100) + '%');
147                 }
148
149                 var tx = cx + r * Math.cos(-(a + 180 * values[i] / total) * rad),
150                     ty = cy + r * Math.sin(-(a + 180 * values[i] / total) * rad);
151
152                 paper.text(tx, ty, text).attr({ 
153                     "font-size": labelSize,
154                     "font-family": labelFont,
155                     "font-weight" : labelWeight,
156                     "text-anchor": "middle",
157                     fill : labelColor
158                 });
159             }
160         }
161
162         
163         legend(paper, cx, cy, r, values, opts, total, len);
164
165         chart.cx = cx;
166         chart.cy = cy;
167         chart.r = r;
168         return chart;
169     }
170     
171     // draw legend
172     function legend(paper, cx, cy, r, values, opts, total, len) 
173     {
174         var legendPos = opts.legendpos || 'right',
175             legendKeyShape = opts.legendkeyshape || 'circle',
176             legendKeySize = opts.legendkeysize || 12,
177             legendFont = opts.legendfont || "'Fontin Sans', Fontin-Sans, sans-serif",
178             legendFontSize = opts.legendfontsize || 18,
179             legendFontColor = opts.legendfontcolor || '#0C014F',
180             lineHeight = opts.lineheight || 30,
181             legendColumn = opts.legendcolumn || 1;
182
183         // default 'legendPos' is 'right'
184         // ix, iy: center position of legend key
185         var ix = cx + r + opts.barwidth / 2 + 30, // 30 pixels away from right of the chart
186             iy = cy - r - 30;
187
188         if(legendPos == 'bottom') {
189             ix = cx - r - opts.barwidth / 2 - 30; // 30 pixels away from left of the chart
190             iy = cy + r + opts.barwidth / 2 + 30; // 30 pixels away from bottom of the chart
191
192             // default 'legendColumn' is 1
193             if(legendColumn == 2) {
194                 ix = cx - r - opts.barwidth / 2 - 90; // 90 pixels away from left of the chart
195             }
196         }
197
198         for (var i = 0; i < len; i++) {
199             
200             if(legendKeyShape == 'rect') {
201                 // pass top left position for 'rect'
202                 paper.rect(ix - (legendKeySize / 2), iy - (legendKeySize / 2), legendKeySize, legendKeySize, 0).attr({ fill: opts.colors && opts.colors[i] || chartinst.colors[i] || "#3E66BC", "stroke": "#fff" });
203             }
204             else {
205                 // pass center position for 'circle'
206                 paper.circle(ix, iy, legendKeySize / 2).attr({ fill: opts.colors && opts.colors[i] || chartinst.colors[i] || "#3E66BC", "stroke": "#fff" });
207             }
208
209             var text = (values[i].others) ? opts.others : opts.legend[values[i].origin] || values[i];
210             
211             if(text.indexOf('#qty#') !== -1) {
212                 text = text.replace('#qty#', Math.round(values[i]));
213             }
214
215             if(text.indexOf('#%#') !== -1) {
216                 text = text.replace('#%#', Math.round(values[i] / total * 100) + '%');
217             }
218
219             var ty = iy - legendFontSize / 10;
220
221             if(text.indexOf("\n")> -1) {
222                 var ty = iy - legendFontSize / 10 + legendFontSize / 2;
223             }
224             
225             // 12 pixels away from the right of legend key
226             // align legend key and text horizontally
227             paper.text(ix + legendKeySize / 2 + 12, ty, text).attr({ 
228                 "font-size": legendFontSize,
229                 "font-family": legendFont,
230                 "text-anchor": "start",
231                 fill : legendFontColor
232             });
233
234             if(legendColumn == 2) {
235                 if(i % 2 == 0) {
236                     ix += r + opts.barwidth / 2 + 120;
237                     iy -= lineHeight;
238                 }
239                 else {
240                     ix -= r + opts.barwidth / 2 + 120;
241                 }
242             }
243
244             iy += lineHeight;
245         }
246     }
247
248     //inheritance
249     var F = function() {};
250     F.prototype = Raphael.g;
251     Piesectorchart.prototype = new F;
252     
253     //public
254     Raphael.fn.piesectorchart = function(width, height, cx, cy, r, values, opts) {
255         return new Piesectorchart(this, width, height, cx, cy, r, values, opts);
256     }
257     
258 })();