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 chart constuctor
20 * @param object canvas
23 RGraph.Rscatter = function (id, data)
26 this.canvas = document.getElementById(id);
27 this.context = this.canvas.getContext('2d');
29 this.canvas.__object__ = this;
30 this.type = 'rscatter';
31 this.hasTooltips = false;
36 * Compatibility with older browsers
38 RGraph.OldBrowserCompat(this.context);
47 'chart.colors': [], // This is used internally for the key
48 'chart.colors.default': 'black',
51 'chart.title.background': null,
52 'chart.title.hpos': null,
53 'chart.title.vpos': null,
55 'chart.labels.position': 'center',
56 'chart.labels.axes': 'nsew',
57 'chart.text.color': 'black',
58 'chart.text.font': 'Verdana',
59 'chart.text.size': 10,
61 'chart.key.background': 'white',
62 'chart.key.position': 'graph',
63 'chart.key.shadow': false,
64 'chart.key.shadow.color': '#666',
65 'chart.key.shadow.blur': 3,
66 'chart.key.shadow.offsetx': 2,
67 'chart.key.shadow.offsety': 2,
68 'chart.key.position.gutter.boxed': true,
69 'chart.key.position.x': null,
70 'chart.key.position.y': null,
71 'chart.key.color.shape': 'square',
72 'chart.key.rounded': true,
73 'chart.contextmenu': null,
74 'chart.tooltips.effect': 'fade',
75 'chart.tooltips.css.class': 'RGraph_tooltip',
76 'chart.tooltips.highlight': true,
77 'chart.tooltips.hotspot': 3,
78 'chart.annotatable': false,
79 'chart.annotate.color': 'black',
80 'chart.zoom.factor': 1.5,
81 'chart.zoom.fade.in': true,
82 'chart.zoom.fade.out': true,
83 'chart.zoom.hdir': 'right',
84 'chart.zoom.vdir': 'down',
85 'chart.zoom.frames': 10,
86 'chart.zoom.delay': 50,
87 'chart.zoom.shadow': true,
88 'chart.zoom.mode': 'canvas',
89 'chart.zoom.thumbnail.width': 75,
90 'chart.zoom.thumbnail.height': 75,
91 'chart.zoom.background': true,
92 'chart.zoom.action': 'zoom',
93 'chart.resizable': false,
94 'chart.adjustable': false,
97 'chart.tickmarks': 'cross',
99 'chart.scale.decimals': null,
100 'chart.scale.round': false
103 // Check the common library has been included
104 if (typeof(RGraph) == 'undefined') {
105 alert('[RSCATTER] Fatal error: The common library does not appear to have been included');
113 * @param string name The name of the property to set
114 * @param string value The value of the property
116 RGraph.Rscatter.prototype.Set = function (name, value)
118 this.properties[name.toLowerCase()] = value;
125 * @param string name The name of the property to get
127 RGraph.Rscatter.prototype.Get = function (name)
129 return this.properties[name.toLowerCase()];
134 * This method draws the rose chart
136 RGraph.Rscatter.prototype.Draw = function ()
139 * Fire the onbeforedraw event
141 RGraph.FireCustomEvent(this, 'onbeforedraw');
144 * Clear all of this canvases event handlers (the ones installed by RGraph)
146 RGraph.ClearEventListeners(this.id);
148 // Calculate the radius
149 this.radius = (Math.min(this.canvas.width, this.canvas.height) / 2) - this.Get('chart.gutter');
150 this.centerx = this.canvas.width / 2;
151 this.centery = this.canvas.height / 2;
157 var max = this.Get('chart.ymax');
158 var min = this.Get('chart.ymin');
160 if (typeof(max) == 'number') {
162 this.scale = [((max - min) * 0.2) + min,((max - min) * 0.4) + min,((max - min) * 0.6) + min,((max - min) * 0.8) + min,((max - min) * 1.0) + min,];
165 for (var i=0; i<this.data.length; ++i) {
166 this.max = Math.max(this.max, this.data[i][1]);
168 this.scale = RGraph.getScale(this.max, this);
169 this.max = this.scale[4];
172 if (String(this.scale[0]).indexOf('e') == -1) {
173 this.scale[0] = Number(this.scale[0]).toFixed(this.Get('chart.scale.decimals'));
174 this.scale[1] = Number(this.scale[1]).toFixed(this.Get('chart.scale.decimals'));
175 this.scale[2] = Number(this.scale[2]).toFixed(this.Get('chart.scale.decimals'));
176 this.scale[3] = Number(this.scale[3]).toFixed(this.Get('chart.scale.decimals'));
177 this.scale[4] = Number(this.scale[4]).toFixed(this.Get('chart.scale.decimals'));
182 * Change the centerx marginally if the key is defined
184 if (this.Get('chart.key') && this.Get('chart.key').length > 0 && this.Get('chart.key').length >= 3) {
185 this.centerx = this.centerx - this.Get('chart.gutter') + 5;
189 * Populate the colors array for the purposes of generating the key
191 if (typeof(this.Get('chart.key')) == 'object' && RGraph.is_array(this.Get('chart.key')) && this.Get('chart.key')[0]) {
192 for (var i=0; i<this.data.length; ++i) {
193 if (this.data[i][2] && typeof(this.data[i][2]) == 'string') {
194 this.Get('chart.colors').push(this.data[i][2]);
199 this.DrawBackground();
204 * Setup the context menu if required
206 if (this.Get('chart.contextmenu')) {
207 RGraph.ShowContext(this);
213 if (this.hasTooltips) {
216 * Register this object for redrawing
218 RGraph.Register(this);
221 * The onmousemove event
223 var canvas_onmousemove_func = function (event)
225 event = RGraph.FixEventObject(event);
227 var mouseCoords = RGraph.getMouseXY(event);
228 var mouseX = mouseCoords[0];
229 var mouseY = mouseCoords[1];
230 var obj = event.target.__object__;
231 var canvas = obj.canvas;
232 var context = obj.context;
233 var overHotspot = false;
234 var offset = obj.Get('chart.tooltips.hotspot'); // This is how far the hotspot extends
236 for (var i=0; i<obj.coords.length; ++i) {
238 var xCoord = obj.coords[i][0];
239 var yCoord = obj.coords[i][1];
240 var tooltip = obj.coords[i][3];
243 mouseX < (xCoord + offset) &&
244 mouseX > (xCoord - offset) &&
245 mouseY < (yCoord + offset) &&
246 mouseY > (yCoord - offset) &&
247 typeof(tooltip) == 'string' &&
252 canvas.style.cursor = 'pointer';
254 if (!RGraph.Registry.Get('chart.tooltip') || RGraph.Registry.Get('chart.tooltip').__text__ != tooltip) {
256 if (obj.Get('chart.tooltips.highlight')) {
261 * Get the tooltip text
263 if (typeof(tooltip) == 'function') {
264 var text = String(tooltip(i));
267 var text = String(tooltip);
270 RGraph.Tooltip(canvas, text, event.pageX + 5, event.pageY - 5, i);
273 * Highlight the tickmark
275 if (obj.Get('chart.tooltips.highlight')) {
277 context.fillStyle = 'rgba(255,255,255,0.5)';
278 context.arc(xCoord, yCoord, 3, 0, 6.2830, 0);
286 canvas.style.cursor = 'default';
289 this.canvas.addEventListener('mousemove', canvas_onmousemove_func, false);
290 RGraph.AddEventListener(this.id, 'mousemove', canvas_onmousemove_func);
293 // Draw the title if any has been set
294 if (this.Get('chart.title')) {
295 RGraph.DrawTitle(this.canvas, this.Get('chart.title'), this.Get('chart.gutter'), this.centerx, this.Get('chart.text.size') + 2);
299 * If the canvas is annotatable, do install the event handlers
301 if (this.Get('chart.annotatable')) {
302 RGraph.Annotate(this);
306 * This bit shows the mini zoom window if requested
308 if (this.Get('chart.zoom.mode') == 'thumbnail' || this.Get('chart.zoom.mode') == 'area') {
309 RGraph.ShowZoomWindow(this);
314 * This function enables resizing
316 if (this.Get('chart.resizable')) {
317 RGraph.AllowResizing(this);
321 * Fire the RGraph ondraw event
323 RGraph.FireCustomEvent(this, 'ondraw');
327 * This method draws the rose charts background
329 RGraph.Rscatter.prototype.DrawBackground = function ()
331 this.context.lineWidth = 1;
333 // Draw the background grey circles
334 this.context.strokeStyle = '#ccc';
335 for (var i=15; i<this.radius - (document.all ? 5 : 0); i+=15) {// Radius must be greater than 0 for Opera to work
336 //this.context.moveTo(this.centerx + i, this.centery);
338 // Radius must be greater than 0 for Opera to work
339 this.context.arc(this.centerx, this.centery, i, 0, (2 * Math.PI), 0);
341 this.context.stroke();
343 // Draw the background lines that go from the center outwards
344 this.context.beginPath();
345 for (var i=15; i<360; i+=15) {
347 // Radius must be greater than 0 for Opera to work
348 this.context.arc(this.centerx, this.centery, this.radius, i / 57.3, (i + 0.01) / 57.3, 0);
350 this.context.lineTo(this.centerx, this.centery);
352 this.context.stroke();
354 this.context.beginPath();
355 this.context.strokeStyle = 'black';
358 this.context.moveTo(this.centerx - this.radius, this.centery);
359 this.context.lineTo(this.centerx + this.radius, this.centery);
362 this.context.moveTo(this.centerx - this.radius, this.centery - 5);
363 this.context.lineTo(this.centerx - this.radius, this.centery + 5);
364 this.context.moveTo(this.centerx + this.radius, this.centery - 5);
365 this.context.lineTo(this.centerx + this.radius, this.centery + 5);
367 // Draw the X check marks
368 for (var i=(this.centerx - this.radius); i<(this.centerx + this.radius); i+=20) {
369 this.context.moveTo(i, this.centery - 3);
370 this.context.lineTo(i, this.centery + 3);
373 // Draw the Y check marks
374 for (var i=(this.centery - this.radius); i<(this.centery + this.radius); i+=20) {
375 this.context.moveTo(this.centerx - 3, i);
376 this.context.lineTo(this.centerx + 3, i);
380 this.context.moveTo(this.centerx, this.centery - this.radius);
381 this.context.lineTo(this.centerx, this.centery + this.radius);
384 this.context.moveTo(this.centerx - 5, this.centery - this.radius);
385 this.context.lineTo(this.centerx + 5, this.centery - this.radius);
387 this.context.moveTo(this.centerx - 5, this.centery + this.radius);
388 this.context.lineTo(this.centerx + 5, this.centery + this.radius);
391 this.context.closePath();
392 this.context.stroke();
397 * This method draws a set of data on the graph
399 RGraph.Rscatter.prototype.DrawRscatter = function ()
401 var data = this.data;
403 for (var i=0; i<data.length; ++i) {
407 var a = d1 / (180 / Math.PI); // RADIANS
408 var r = ( (d2 - this.Get('chart.ymin')) / (this.max - this.Get('chart.ymin')) ) * this.radius;
409 var x = Math.sin(a) * r;
410 var y = Math.cos(a) * r;
411 var color = data[i][2] ? data[i][2] : this.Get('chart.colors.default');
412 var tooltip = data[i][3] ? data[i][3] : null;
414 if (tooltip && tooltip.length) {
415 this.hasTooltips = true;
419 * Account for the correct quadrant
421 x = x + this.centerx;
422 y = this.centery - y;
425 this.DrawTick(x, y, color);
427 // Populate the coords array with the coordinates and the tooltip
428 this.coords.push([x, y, color, tooltip]);
434 * Unsuprisingly, draws the labels
436 RGraph.Rscatter.prototype.DrawLabels = function ()
438 this.context.lineWidth = 1;
439 var key = this.Get('chart.key');
441 // Set the color to black
442 this.context.fillStyle = 'black';
443 this.context.strokeStyle = 'black';
446 var color = this.Get('chart.text.color');
447 var font_face = this.Get('chart.text.font');
448 var font_size = this.Get('chart.text.size');
449 var context = this.context;
450 var axes = this.Get('chart.labels.axes').toLowerCase();
452 this.context.fillStyle = this.Get('chart.text.color');
455 if (typeof(this.Get('chart.labels')) == 'object' && this.Get('chart.labels')) {
456 this.DrawCircularLabels(context, this.Get('chart.labels'), font_face, font_size, r);
460 var color = 'rgba(255,255,255,0.8)';
462 // The "North" axis labels
463 if (axes.indexOf('n') > -1) {
464 RGraph.Text(context,font_face,font_size,this.centerx,this.centery - ((r) * 0.2),String(this.scale[0]),'center','center',true,false,color);
465 RGraph.Text(context, font_face, font_size, this.centerx, this.centery - ((r) * 0.4), String(this.scale[1]), 'center', 'center', true, false, color);
466 RGraph.Text(context, font_face, font_size, this.centerx, this.centery - ((r) * 0.6), String(this.scale[2]), 'center', 'center', true, false, color);
467 RGraph.Text(context, font_face, font_size, this.centerx, this.centery - ((r) * 0.8), String(this.scale[3]), 'center', 'center', true, false, color);
468 RGraph.Text(context, font_face, font_size, this.centerx, this.centery - r, String(this.scale[4]), 'center', 'center', true, false, color);
471 // The "South" axis labels
472 if (axes.indexOf('s') > -1) {
473 RGraph.Text(context, font_face, font_size, this.centerx, this.centery + ((r) * 0.2), String(this.scale[0]), 'center', 'center', true, false, color);
474 RGraph.Text(context, font_face, font_size, this.centerx, this.centery + ((r) * 0.4), String(this.scale[1]), 'center', 'center', true, false, color);
475 RGraph.Text(context, font_face, font_size, this.centerx, this.centery + ((r) * 0.6), String(this.scale[2]), 'center', 'center', true, false, color);
476 RGraph.Text(context, font_face, font_size, this.centerx, this.centery + ((r) * 0.8), String(this.scale[3]), 'center', 'center', true, false, color);
477 RGraph.Text(context, font_face, font_size, this.centerx, this.centery + r, String(this.scale[4]), 'center', 'center', true, false, color);
480 // The "East" axis labels
481 if (axes.indexOf('e') > -1) {
482 RGraph.Text(context, font_face, font_size, this.centerx + ((r) * 0.2), this.centery, String(this.scale[0]), 'center', 'center', true, false, color);
483 RGraph.Text(context, font_face, font_size, this.centerx + ((r) * 0.4), this.centery, String(this.scale[1]), 'center', 'center', true, false, color);
484 RGraph.Text(context, font_face, font_size, this.centerx + ((r) * 0.6), this.centery, String(this.scale[2]), 'center', 'center', true, false, color);
485 RGraph.Text(context, font_face, font_size, this.centerx + ((r) * 0.8), this.centery, String(this.scale[3]), 'center', 'center', true, false, color);
486 RGraph.Text(context, font_face, font_size, this.centerx + r, this.centery, String(this.scale[4]), 'center', 'center', true, false, color);
489 // The "West" axis labels
490 if (axes.indexOf('w') > -1) {
491 RGraph.Text(context, font_face, font_size, this.centerx - ((r) * 0.2), this.centery, String(this.scale[0]), 'center', 'center', true, false, color);
492 RGraph.Text(context, font_face, font_size, this.centerx - ((r) * 0.4), this.centery, String(this.scale[1]), 'center', 'center', true, false, color);
493 RGraph.Text(context, font_face, font_size, this.centerx - ((r) * 0.6), this.centery, String(this.scale[2]), 'center', 'center', true, false, color);
494 RGraph.Text(context, font_face, font_size, this.centerx - ((r) * 0.8), this.centery, String(this.scale[3]), 'center', 'center', true, false, color);
495 RGraph.Text(context, font_face, font_size, this.centerx - r, this.centery, String(this.scale[4]), 'center', 'center', true, false, color);
498 // Draw the center minimum value (but only if there's at least one axes labels stipulated)
499 if (this.Get('chart.labels.axes').length > 0) {
500 RGraph.Text(context, font_face, font_size, this.centerx, this.centery, this.Get('chart.ymin') > 0 ? String(this.Get('chart.ymin').toFixed(this.Get('chart.scale.decimals'))) : '0', 'center', 'center', true, false, color);
506 if (key && key.length) {
507 RGraph.DrawKey(this, key, this.Get('chart.colors'));
513 * Draws the circular labels that go around the charts
515 * @param labels array The labels that go around the chart
517 RGraph.Rscatter.prototype.DrawCircularLabels = function (context, labels, font_face, font_size, r)
519 var position = this.Get('chart.labels.position');
522 for (var i=0; i<labels.length; ++i) {
525 var a = (360 / labels.length) * (i + 1) - (360 / (labels.length * 2));
526 var a = a - 90 + (this.Get('chart.labels.position') == 'edge' ? ((360 / labels.length) / 2) : 0);
528 var x = Math.cos(a / 57.29577866666) * (r + 10);
529 var y = Math.sin(a / 57.29577866666) * (r + 10);
531 RGraph.Text(context, font_face, font_size, this.centerx + x, this.centery + y, String(labels[i]), 'center', 'center');
537 * Draws a single tickmark
539 RGraph.Rscatter.prototype.DrawTick = function (x, y, color)
541 var tickmarks = this.Get('chart.tickmarks');
542 var ticksize = this.Get('chart.ticksize');
544 this.context.strokeStyle = color;
545 this.context.fillStyle = color;
548 if (tickmarks == 'cross') {
550 this.context.beginPath();
551 this.context.moveTo(x + ticksize, y + ticksize);
552 this.context.lineTo(x - ticksize, y - ticksize);
553 this.context.stroke();
555 this.context.beginPath();
556 this.context.moveTo(x - ticksize, y + ticksize);
557 this.context.lineTo(x + ticksize, y - ticksize);
558 this.context.stroke();
561 } else if (tickmarks == 'circle') {
563 this.context.beginPath();
564 this.context.arc(x, y, ticksize, 0, 6.2830, false);
568 } else if (tickmarks == 'square') {
570 this.context.beginPath();
571 this.context.fillRect(x - ticksize, y - ticksize, 2 * ticksize, 2 * ticksize);
574 // Diamond shape tickmarks
575 } else if (tickmarks == 'diamond') {
577 this.context.beginPath();
578 this.context.moveTo(x, y - ticksize);
579 this.context.lineTo(x + ticksize, y);
580 this.context.lineTo(x, y + ticksize);
581 this.context.lineTo(x - ticksize, y);
582 this.context.closePath();
585 // Plus style tickmarks
586 } else if (tickmarks == 'plus') {
588 this.context.lineWidth = 1;
590 this.context.beginPath();
591 this.context.moveTo(x, y - ticksize);
592 this.context.lineTo(x, y + ticksize);
593 this.context.moveTo(x - ticksize, y);
594 this.context.lineTo(x + ticksize, y);
595 this.context.stroke();