2 * o------------------------------------------------------------------------------o
3 * | This file is part of the RGraph package - you can learn more at: |
5 * | http://www.rgraph.net |
7 * | This package is licensed under the RGraph license. For all kinds of business |
8 * | purposes there is a small one-time licensing fee to pay and for non |
9 * | commercial purposes it is free to use. You can read the full license here: |
11 * | http://www.rgraph.net/LICENSE.txt |
12 * o------------------------------------------------------------------------------o
15 if (typeof(RGraph) == 'undefined') RGraph = {};
18 * The rose chart constuctor
20 * @param object canvas
23 RGraph.Rose = function (id, data)
26 this.canvas = document.getElementById(id);
27 this.context = this.canvas.getContext('2d');
29 this.canvas.__object__ = this;
35 * Compatibility with older browsers
37 RGraph.OldBrowserCompat(this.context);
47 'chart.colors': ['rgb(255,0,0)', 'rgb(0,255,255)', 'rgb(0,255,0)', 'rgb(127,127,127)', 'rgb(0,0,255)', 'rgb(255,128,255)'],
48 'chart.colors.alpha': null,
49 'chart.strokestyle': 'black',
52 'chart.title.background': null,
53 'chart.title.hpos': null,
54 'chart.title.vpos': null,
56 'chart.labels.position': 'center',
57 'chart.labels.axes': 'nsew',
58 'chart.text.color': 'black',
59 'chart.text.font': 'Verdana',
60 'chart.text.size': 10,
62 'chart.key.background': 'white',
63 'chart.key.position': 'graph',
64 'chart.key.shadow': false,
65 'chart.key.shadow.color': '#666',
66 'chart.key.shadow.blur': 3,
67 'chart.key.shadow.offsetx': 2,
68 'chart.key.shadow.offsety': 2,
69 'chart.key.position.gutter.boxed': true,
70 'chart.key.position.x': null,
71 'chart.key.position.y': null,
72 'chart.key.color.shape': 'square',
73 'chart.key.rounded': true,
74 'chart.contextmenu': null,
75 'chart.tooltips': null,
76 'chart.tooltips.effect': 'fade',
77 'chart.tooltips.css.class': 'RGraph_tooltip',
78 'chart.tooltips.highlight': true,
79 'chart.annotatable': false,
80 'chart.annotate.color': 'black',
81 'chart.zoom.factor': 1.5,
82 'chart.zoom.fade.in': true,
83 'chart.zoom.fade.out': true,
84 'chart.zoom.hdir': 'right',
85 'chart.zoom.vdir': 'down',
86 'chart.zoom.frames': 10,
87 'chart.zoom.delay': 50,
88 'chart.zoom.shadow': true,
89 'chart.zoom.mode': 'canvas',
90 'chart.zoom.thumbnail.width': 75,
91 'chart.zoom.thumbnail.height': 75,
92 'chart.zoom.background': true,
93 'chart.zoom.action': 'zoom',
94 'chart.resizable': false,
95 'chart.adjustable': false,
98 'chart.scale.decimals': null
101 // Check the common library has been included
102 if (typeof(RGraph) == 'undefined') {
103 alert('[ROSE] Fatal error: The common library does not appear to have been included');
111 * @param string name The name of the property to set
112 * @param string value The value of the property
114 RGraph.Rose.prototype.Set = function (name, value)
116 this.properties[name.toLowerCase()] = value;
123 * @param string name The name of the property to get
125 RGraph.Rose.prototype.Get = function (name)
127 return this.properties[name.toLowerCase()];
132 * This method draws the rose chart
134 RGraph.Rose.prototype.Draw = function ()
137 * Fire the onbeforedraw event
139 RGraph.FireCustomEvent(this, 'onbeforedraw');
142 * Clear all of this canvases event handlers (the ones installed by RGraph)
144 RGraph.ClearEventListeners(this.id);
146 // Calculate the radius
147 this.radius = (Math.min(this.canvas.width, this.canvas.height) / 2);
148 this.centerx = this.canvas.width / 2;
149 this.centery = this.canvas.height / 2;
152 this.startRadians = 0;
155 * Change the centerx marginally if the key is defined
157 if (this.Get('chart.key') && this.Get('chart.key').length > 0 && this.Get('chart.key').length >= 3) {
158 this.centerx = this.centerx - this.Get('chart.gutter') + 5;
161 this.DrawBackground();
166 * Setup the context menu if required
168 if (this.Get('chart.contextmenu')) {
169 RGraph.ShowContext(this);
175 if (this.Get('chart.tooltips')) {
178 * Register this object for redrawing
180 RGraph.Register(this);
185 var canvas_onclick_func = function (e)
187 var obj = e.target.__object__;
188 var canvas = e.target;
189 var context = canvas.getContext('2d');
191 e = RGraph.FixEventObject(e);
195 var segment = RGraph.getSegment(e);
196 if (segment && obj.Get('chart.tooltips')[segment[5]]) {
198 context.strokeStyle = 'black';
199 context.fillStyle = 'rgba(255,255,255,0.5)';
200 context.arc(segment[0], segment[1], segment[2], segment[3] / 57.3, segment[4] / 57.3, false);
201 context.lineTo(obj.centerx, obj.centery);
206 context.strokeStyle = 'rgba(0,0,0,0)';
212 RGraph.Tooltip(canvas, obj.Get('chart.tooltips')[segment[5]], e.pageX, e.pageY, segment[5]);
219 this.canvas.addEventListener('click', canvas_onclick_func, false);
220 RGraph.AddEventListener(this.id, 'click', canvas_onclick_func);
224 * The onmousemove event
226 var canvas_onmousemove_func = function (e)
228 var obj = e.target.__object__;
229 var canvas = e.target;
230 var context = canvas.getContext('2d');
232 e = RGraph.FixEventObject(e);
234 var segment = RGraph.getSegment(e);
236 if (segment && obj.Get('chart.tooltips')[segment[5]]) {
237 canvas.style.cursor = 'pointer';
241 canvas.style.cursor = 'default';
243 this.canvas.addEventListener('mousemove', canvas_onmousemove_func, false);
244 RGraph.AddEventListener(this.id, 'mousemove', canvas_onmousemove_func);
248 * If the canvas is annotatable, do install the event handlers
250 if (this.Get('chart.annotatable')) {
251 RGraph.Annotate(this);
255 * This bit shows the mini zoom window if requested
257 if (this.Get('chart.zoom.mode') == 'thumbnail' || this.Get('chart.zoom.mode') == 'area') {
258 RGraph.ShowZoomWindow(this);
263 * This function enables resizing
265 if (this.Get('chart.resizable')) {
266 RGraph.AllowResizing(this);
271 * This function enables adjusting
273 if (this.Get('chart.adjustable')) {
274 RGraph.AllowAdjusting(this);
278 * Fire the RGraph ondraw event
280 RGraph.FireCustomEvent(this, 'ondraw');
284 * This method draws the rose charts background
286 RGraph.Rose.prototype.DrawBackground = function ()
288 this.context.lineWidth = 1;
290 // Draw the background grey circles
291 this.context.strokeStyle = '#ccc';
292 for (var i=15; i<this.radius - this.Get('chart.gutter') - (document.all ? 5 : 0); i+=15) {// Radius must be greater than 0 for Opera to work
293 //this.context.moveTo(this.centerx + i, this.centery);
295 // Radius must be greater than 0 for Opera to work
296 this.context.arc(this.centerx, this.centery, i, 0, (2 * Math.PI), 0);
298 this.context.stroke();
300 // Draw the background lines that go from the center outwards
301 this.context.beginPath();
302 for (var i=15; i<360; i+=15) {
304 // Radius must be greater than 0 for Opera to work
305 this.context.arc(this.centerx, this.centery, this.radius - this.Get('chart.gutter'), i / 57.3, (i + 0.1) / 57.3, 0); // The 0.01 avoids a bug in Chrome 6
307 this.context.lineTo(this.centerx, this.centery);
309 this.context.stroke();
311 this.context.beginPath();
312 this.context.strokeStyle = 'black';
315 this.context.moveTo(this.centerx - this.radius + this.Get('chart.gutter'), this.centery);
316 this.context.lineTo(this.centerx + this.radius - this.Get('chart.gutter'), this.centery);
319 this.context.moveTo(this.centerx - this.radius + this.Get('chart.gutter'), this.centery - 5);
320 this.context.lineTo(this.centerx - this.radius + this.Get('chart.gutter'), this.centery + 5);
321 this.context.moveTo(this.centerx + this.radius - this.Get('chart.gutter'), this.centery - 5);
322 this.context.lineTo(this.centerx + this.radius - this.Get('chart.gutter'), this.centery + 5);
324 // Draw the X check marks
325 for (var i=(this.centerx - this.radius + this.Get('chart.gutter')); i<(this.centerx + this.radius - this.Get('chart.gutter')); i+=20) {
326 this.context.moveTo(i, this.centery - 3);
327 this.context.lineTo(i, this.centery + 3);
330 // Draw the Y check marks
331 for (var i=(this.centery - this.radius + this.Get('chart.gutter')); i<(this.centery + this.radius - this.Get('chart.gutter')); i+=20) {
332 this.context.moveTo(this.centerx - 3, i);
333 this.context.lineTo(this.centerx + 3, i);
337 this.context.moveTo(this.centerx, this.centery - this.radius + this.Get('chart.gutter'));
338 this.context.lineTo(this.centerx, this.centery + this.radius - this.Get('chart.gutter'));
341 this.context.moveTo(this.centerx - 5, this.centery - this.radius + this.Get('chart.gutter'));
342 this.context.lineTo(this.centerx + 5, this.centery - this.radius + this.Get('chart.gutter'));
344 this.context.moveTo(this.centerx - 5, this.centery + this.radius - this.Get('chart.gutter'));
345 this.context.lineTo(this.centerx + 5, this.centery + this.radius - this.Get('chart.gutter'));
348 this.context.closePath();
349 this.context.stroke();
354 * This method draws a set of data on the graph
356 RGraph.Rose.prototype.DrawRose = function ()
358 var data = this.data;
360 // Must be at least two data points
361 if (data.length < 2) {
362 alert('[ROSE] Must be at least two data points! [' + data + ']');
366 // Work out the maximum value and the sum
367 if (!this.Get('chart.ymax')) {
368 this.scale = RGraph.getScale(RGraph.array_max(data), this);
369 this.max = this.scale[4];
371 var ymax = this.Get('chart.ymax');
372 var ymin = this.Get('chart.ymin');
375 ((ymax - ymin) * 0.2) + ymin,
376 ((ymax - ymin) * 0.4) + ymin,
377 ((ymax - ymin) * 0.6) + ymin,
378 ((ymax - ymin) * 0.8) + ymin,
379 ((ymax - ymin) * 1 + ymin)
381 this.max = this.scale[4];
384 this.sum = RGraph.array_sum(data);
386 // Move to the centre
387 this.context.moveTo(this.centerx, this.centery);
389 this.context.stroke(); // Stroke the background so it stays grey
392 if (this.Get('chart.colors.alpha')) {
393 this.context.globalAlpha = this.Get('chart.colors.alpha');
396 // Draw the Rose segments
397 for (var i=0; i<this.data.length; ++i) {
399 this.context.strokeStyle = this.Get('chart.strokestyle');
401 if (this.Get('chart.colors')[i]) {
402 this.context.fillStyle = this.Get('chart.colors')[i];
405 var segmentRadians = (1 / this.data.length) * (2 * Math.PI);
407 this.context.beginPath(); // Begin the segment
409 var radius = ((this.data[i] - this.Get('chart.ymin')) / (this.max - this.Get('chart.ymin'))) * (this.radius - this.Get('chart.gutter') - 10);
411 this.context.arc(this.centerx, this.centery, radius, this.startRadians - (Math.PI / 2), this.startRadians + segmentRadians - (Math.PI / 2), 0);
412 this.context.lineTo(this.centerx, this.centery);
414 this.context.closePath(); // End the segment
416 // Store the start and end angles
418 ((this.startRadians - (Math.PI / 2)) * 57.3) + 90,
419 (((this.startRadians + segmentRadians) - (Math.PI / 2)) * 57.3) + 90,
423 this.startRadians += segmentRadians;
424 this.context.stroke();
427 // Turn off the transparency
428 if (this.Get('chart.colors.alpha')) {
429 this.context.globalAlpha = 1;
432 // Draw the title if any has been set
433 if (this.Get('chart.title')) {
434 RGraph.DrawTitle(this.canvas, this.Get('chart.title'), this.Get('chart.gutter'), this.centerx, this.Get('chart.text.size') + 2);
440 * Unsuprisingly, draws the labels
442 RGraph.Rose.prototype.DrawLabels = function ()
444 this.context.lineWidth = 1;
445 var key = this.Get('chart.key');
447 if (key && key.length) {
448 RGraph.DrawKey(this, key, this.Get('chart.colors'));
451 // Set the color to black
452 this.context.fillStyle = 'black';
453 this.context.strokeStyle = 'black';
455 var r = this.radius - 10;
456 var font_face = this.Get('chart.text.font');
457 var font_size = this.Get('chart.text.size');
458 var context = this.context;
459 var axes = this.Get('chart.labels.axes').toLowerCase();
463 if (typeof(this.Get('chart.labels')) == 'object' && this.Get('chart.labels')) {
464 this.DrawCircularLabels(context, this.Get('chart.labels'), font_face, font_size, r + 10);
468 var color = 'rgba(255,255,255,0.8)';
470 // The "North" axis labels
471 if (axes.indexOf('n') > -1) {
472 RGraph.Text(context, font_face, font_size, this.centerx, this.centery - ((r - this.Get('chart.gutter')) * 0.2), String(Number(this.scale[0]).toFixed(this.Get('chart.scale.decimals'))), 'center', 'center', true, false, color);
473 RGraph.Text(context, font_face, font_size, this.centerx, this.centery - ((r - this.Get('chart.gutter')) * 0.4), String(Number(this.scale[1]).toFixed(this.Get('chart.scale.decimals'))), 'center', 'center', true, false, color);
474 RGraph.Text(context, font_face, font_size, this.centerx, this.centery - ((r - this.Get('chart.gutter')) * 0.6), String(Number(this.scale[2]).toFixed(this.Get('chart.scale.decimals'))), 'center', 'center', true, false, color);
475 RGraph.Text(context, font_face, font_size, this.centerx, this.centery - ((r - this.Get('chart.gutter')) * 0.8), String(Number(this.scale[3]).toFixed(this.Get('chart.scale.decimals'))), 'center', 'center', true, false, color);
476 RGraph.Text(context, font_face, font_size, this.centerx, this.centery - r + this.Get('chart.gutter'), String(Number(this.scale[4]).toFixed(this.Get('chart.scale.decimals'))), 'center', 'center', true, false, color);
479 // The "South" axis labels
480 if (axes.indexOf('s') > -1) {
481 RGraph.Text(context, font_face, font_size, this.centerx, this.centery + ((r - this.Get('chart.gutter')) * 0.2), String(Number(this.scale[0]).toFixed(this.Get('chart.scale.decimals'))), 'center', 'center', true, false, color);
482 RGraph.Text(context, font_face, font_size, this.centerx, this.centery + ((r - this.Get('chart.gutter')) * 0.4), String(Number(this.scale[1]).toFixed(this.Get('chart.scale.decimals'))), 'center', 'center', true, false, color);
483 RGraph.Text(context, font_face, font_size, this.centerx, this.centery + ((r - this.Get('chart.gutter')) * 0.6), String(Number(this.scale[2]).toFixed(this.Get('chart.scale.decimals'))), 'center', 'center', true, false, color);
484 RGraph.Text(context, font_face, font_size, this.centerx, this.centery + ((r - this.Get('chart.gutter')) * 0.8), String(Number(this.scale[3]).toFixed(this.Get('chart.scale.decimals'))), 'center', 'center', true, false, color);
485 RGraph.Text(context, font_face, font_size, this.centerx, this.centery + r - this.Get('chart.gutter'), String(Number(this.scale[4]).toFixed(this.Get('chart.scale.decimals'))), 'center', 'center', true, false, color);
488 // The "East" axis labels
489 if (axes.indexOf('e') > -1) {
490 RGraph.Text(context, font_face, font_size, this.centerx + ((r - this.Get('chart.gutter')) * 0.2), this.centery, String(Number(this.scale[0]).toFixed(this.Get('chart.scale.decimals'))), 'center', 'center', true, false, color);
491 RGraph.Text(context, font_face, font_size, this.centerx + ((r - this.Get('chart.gutter')) * 0.4), this.centery, String(Number(this.scale[1]).toFixed(this.Get('chart.scale.decimals'))), 'center', 'center', true, false, color);
492 RGraph.Text(context, font_face, font_size, this.centerx + ((r - this.Get('chart.gutter')) * 0.6), this.centery, String(Number(this.scale[2]).toFixed(this.Get('chart.scale.decimals'))), 'center', 'center', true, false, color);
493 RGraph.Text(context, font_face, font_size, this.centerx + ((r - this.Get('chart.gutter')) * 0.8), this.centery, String(Number(this.scale[3]).toFixed(this.Get('chart.scale.decimals'))), 'center', 'center', true, false, color);
494 RGraph.Text(context, font_face, font_size, this.centerx + r - this.Get('chart.gutter'), this.centery, String(Number(this.scale[4]).toFixed(this.Get('chart.scale.decimals'))), 'center', 'center', true, false, color);
497 // The "West" axis labels
498 if (axes.indexOf('w') > -1) {
499 RGraph.Text(context, font_face, font_size, this.centerx - ((r - this.Get('chart.gutter')) * 0.2), this.centery, String(Number(this.scale[0]).toFixed(this.Get('chart.scale.decimals'))), 'center', 'center', true, false, color);
500 RGraph.Text(context, font_face, font_size, this.centerx - ((r - this.Get('chart.gutter')) * 0.4), this.centery, String(Number(this.scale[1]).toFixed(this.Get('chart.scale.decimals'))), 'center', 'center', true, false, color);
501 RGraph.Text(context, font_face, font_size, this.centerx - ((r - this.Get('chart.gutter')) * 0.6), this.centery, String(Number(this.scale[2]).toFixed(this.Get('chart.scale.decimals'))), 'center', 'center', true, false, color);
502 RGraph.Text(context, font_face, font_size, this.centerx - ((r - this.Get('chart.gutter')) * 0.8), this.centery, String(Number(this.scale[3]).toFixed(this.Get('chart.scale.decimals'))), 'center', 'center', true, false, color);
503 RGraph.Text(context, font_face, font_size, this.centerx - r + this.Get('chart.gutter'), this.centery, String(Number(this.scale[4]).toFixed(this.Get('chart.scale.decimals'))), 'center', 'center', true, false, color);
506 RGraph.Text(context, font_face, font_size, this.centerx, this.centery, typeof(this.Get('chart.ymin')) == 'number' ? String(Number(this.Get('chart.ymin')).toFixed(this.Get('chart.scale.decimals'))) : '0', 'center', 'center', true, false, color);
511 * Draws the circular labels that go around the charts
513 * @param labels array The labels that go around the chart
515 RGraph.Rose.prototype.DrawCircularLabels = function (context, labels, font_face, font_size, r)
517 var position = this.Get('chart.labels.position');
518 var r = r - this.Get('chart.gutter') + 10;
520 for (var i=0; i<labels.length; ++i) {
523 var a = (360 / labels.length) * (i + 1) - (360 / (labels.length * 2));
524 var a = a - 90 + (this.Get('chart.labels.position') == 'edge' ? ((360 / labels.length) / 2) : 0);
526 var x = Math.cos(a / 57.29577866666) * (r + 10);
527 var y = Math.sin(a / 57.29577866666) * (r + 10);
529 RGraph.Text(context, font_face, font_size, this.centerx + x, this.centery + y, String(labels[i]), 'center', 'center');