source: Dev/trunk/RGraph/libraries/RGraph.rscatter.js @ 77

Last change on this file since 77 was 77, checked in by fpvanagthoven, 14 years ago

RGraph

File size: 24.5 KB
Line 
1    /**
2    * o------------------------------------------------------------------------------o
3    * | This file is part of the RGraph package - you can learn more at:             |
4    * |                                                                              |
5    * |                          http://www.rgraph.net                               |
6    * |                                                                              |
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:  |
10    * |                                                                              |
11    * |                      http://www.rgraph.net/LICENSE.txt                       |
12    * o------------------------------------------------------------------------------o
13    */
14   
15    if (typeof(RGraph) == 'undefined') RGraph = {};
16   
17    /**
18    * The chart constuctor
19    *
20    * @param object canvas
21    * @param array data
22    */
23    RGraph.Rscatter = function (id, data)
24    {
25        this.id                = id;
26        this.canvas            = document.getElementById(id);
27        this.context           = this.canvas.getContext('2d');
28        this.data              = data;
29        this.canvas.__object__ = this;
30        this.type              = 'rscatter';
31        this.hasTooltips       = false;
32        this.isRGraph          = true;
33
34
35        /**
36        * Compatibility with older browsers
37        */
38        RGraph.OldBrowserCompat(this.context);
39
40
41        this.centerx = 0;
42        this.centery = 0;
43        this.radius  = 0;
44        this.max     = 0;
45       
46        this.properties = {
47            'chart.radius':                 null,
48            'chart.colors':                 [], // This is used internally for the key
49            'chart.colors.default':         'black',
50            'chart.gutter.left':            25,
51            'chart.gutter.right':           25,
52            'chart.gutter.top':             25,
53            'chart.gutter.bottom':          25,
54            'chart.title':                  '',
55            'chart.title.background':       null,
56            'chart.title.hpos':             null,
57            'chart.title.vpos':             null,
58            'chart.labels':                 null,
59            'chart.labels.position':       'center',
60            'chart.labels.axes':            'nsew',
61            'chart.text.color':             'black',
62            'chart.text.font':              'Verdana',
63            'chart.text.size':              10,
64            'chart.key':                    null,
65            'chart.key.background':         'white',
66            'chart.key.position':           'graph',
67            'chart.key.halign':             'right',
68            'chart.key.shadow':             false,
69            'chart.key.shadow.color':       '#666',
70            'chart.key.shadow.blur':        3,
71            'chart.key.shadow.offsetx':     2,
72            'chart.key.shadow.offsety':     2,
73            'chart.key.position.gutter.boxed': true,
74            'chart.key.position.x':         null,
75            'chart.key.position.y':         null,
76            'chart.key.color.shape':        'square',
77            'chart.key.rounded':            true,
78            'chart.key.linewidth':          1,
79            'chart.contextmenu':            null,
80            'chart.tooltips.effect':        'fade',
81            'chart.tooltips.css.class':     'RGraph_tooltip',
82            'chart.tooltips.highlight':     true,
83            'chart.tooltips.hotspot':       3,
84            'chart.annotatable':            false,
85            'chart.annotate.color':         'black',
86            'chart.zoom.factor':            1.5,
87            'chart.zoom.fade.in':           true,
88            'chart.zoom.fade.out':          true,
89            'chart.zoom.hdir':              'right',
90            'chart.zoom.vdir':              'down',
91            'chart.zoom.frames':            10,
92            'chart.zoom.delay':             50,
93            'chart.zoom.shadow':            true,
94            'chart.zoom.mode':              'canvas',
95            'chart.zoom.thumbnail.width':   75,
96            'chart.zoom.thumbnail.height':  75,
97            'chart.zoom.background':        true,
98            'chart.zoom.action':            'zoom',
99            'chart.resizable':              false,
100            'chart.resize.handle.adjust':   [0,0],
101            'chart.resize.handle.background': null,
102            'chart.adjustable':             false,
103            'chart.ymax':                   null,
104            'chart.ymin':                   0,
105            'chart.tickmarks':              'cross',
106            'chart.ticksize':               3,
107            'chart.scale.decimals':         null,
108            'chart.scale.round':            false
109        }
110    }
111
112
113    /**
114    * A simple setter
115    *
116    * @param string name  The name of the property to set
117    * @param string value The value of the property
118    */
119    RGraph.Rscatter.prototype.Set = function (name, value)
120    {
121        this.properties[name.toLowerCase()] = value;
122    }
123   
124   
125    /**
126    * A simple getter
127    *
128    * @param string name The name of the property to get
129    */
130    RGraph.Rscatter.prototype.Get = function (name)
131    {
132        return this.properties[name.toLowerCase()];
133    }
134
135   
136    /**
137    * This method draws the rose chart
138    */
139    RGraph.Rscatter.prototype.Draw = function ()
140    {
141        /**
142        * Fire the onbeforedraw event
143        */
144        RGraph.FireCustomEvent(this, 'onbeforedraw');
145
146        /**
147        * Clear all of this canvases event handlers (the ones installed by RGraph)
148        */
149        RGraph.ClearEventListeners(this.id);
150
151       
152        /**
153        * This doesn't affect the chart, but is used for compatibility
154        */
155        this.gutterLeft   = this.Get('chart.gutter.left');
156        this.gutterRight  = this.Get('chart.gutter.right');
157        this.gutterTop    = this.Get('chart.gutter.top');
158        this.gutterBottom = this.Get('chart.gutter.bottom');
159
160        // Calculate the radius
161        this.radius  = (Math.min(RGraph.GetWidth(this) - this.gutterLeft - this.gutterRight, RGraph.GetHeight(this) - this.gutterTop - this.gutterBottom) / 2);
162        this.centerx = RGraph.GetWidth(this) / 2;
163        this.centery = RGraph.GetHeight(this) / 2;
164        this.coords  = [];
165       
166        /**
167        * If there's a user specified radius, use that
168        */
169        if (typeof(this.Get('chart.radius')) == 'number') {
170            this.radius = this.Get('chart.radius');
171        }
172       
173        /**
174        * Work out the scale
175        */
176        var max = this.Get('chart.ymax');
177        var min = this.Get('chart.ymin');
178       
179        if (typeof(max) == 'number') {
180            this.max   = max;
181            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,];
182           
183        } else {
184            for (var i=0; i<this.data.length; ++i) {
185                this.max = Math.max(this.max, this.data[i][1]);
186            }
187            this.scale = RGraph.getScale(this.max, this);
188            this.max   = this.scale[4];
189
190            // Hmmmmmmmm
191            if (String(this.scale[0]).indexOf('e') == -1) {
192                this.scale[0] = Number(this.scale[0]).toFixed(this.Get('chart.scale.decimals'));
193                this.scale[1] = Number(this.scale[1]).toFixed(this.Get('chart.scale.decimals'));
194                this.scale[2] = Number(this.scale[2]).toFixed(this.Get('chart.scale.decimals'));
195                this.scale[3] = Number(this.scale[3]).toFixed(this.Get('chart.scale.decimals'));
196                this.scale[4] = Number(this.scale[4]).toFixed(this.Get('chart.scale.decimals'));
197            }
198        }
199
200        /**
201        * Change the centerx marginally if the key is defined
202        */
203        if (this.Get('chart.key') && this.Get('chart.key').length > 0 && this.Get('chart.key').length >= 3) {
204            this.centerx = this.centerx - this.Get('chart.gutter.right') + 5;
205        }
206       
207        /**
208        * Populate the colors array for the purposes of generating the key
209        */
210        if (typeof(this.Get('chart.key')) == 'object' && RGraph.is_array(this.Get('chart.key')) && this.Get('chart.key')[0]) {
211            for (var i=0; i<this.data.length; ++i) {
212                if (this.data[i][2] && typeof(this.data[i][2]) == 'string') {
213                    this.Get('chart.colors').push(this.data[i][2]);
214                }
215            }
216        }
217
218        this.DrawBackground();
219        this.DrawRscatter();
220        this.DrawLabels();
221
222        /**
223        * Setup the context menu if required
224        */
225        if (this.Get('chart.contextmenu')) {
226            RGraph.ShowContext(this);
227        }
228
229        /**
230        * Tooltips
231        */
232        if (this.hasTooltips) {
233
234            /**
235            * Register this object for redrawing
236            */
237            RGraph.Register(this);
238           
239            /**
240            * The onmousemove event
241            */
242            var canvas_onmousemove_func = function (event)
243            {
244                event = RGraph.FixEventObject(event);
245
246                var mouseCoords = RGraph.getMouseXY(event);
247                var mouseX      = mouseCoords[0];
248                var mouseY      = mouseCoords[1];
249                var obj         = event.target.__object__;
250                var canvas      = obj.canvas;
251                var context     = obj.context;
252                var overHotspot = false;
253                var offset      = obj.Get('chart.tooltips.hotspot'); // This is how far the hotspot extends
254
255                for (var i=0; i<obj.coords.length; ++i) {
256               
257                    var xCoord  = obj.coords[i][0];
258                    var yCoord  = obj.coords[i][1];
259                    var tooltip = obj.coords[i][3];
260
261                    if (
262                        mouseX < (xCoord + offset) &&
263                        mouseX > (xCoord - offset) &&
264                        mouseY < (yCoord + offset) &&
265                        mouseY > (yCoord - offset) &&
266                        typeof(tooltip) == 'string' &&
267                        tooltip.length > 0
268                       ) {
269
270                        overHotspot = true;
271                        canvas.style.cursor = 'pointer';
272
273                        if (!RGraph.Registry.Get('chart.tooltip') || RGraph.Registry.Get('chart.tooltip').__text__ != tooltip) {
274   
275                            if (obj.Get('chart.tooltips.highlight')) {
276                                RGraph.Redraw();
277                            }
278   
279                            /**
280                            * Get the tooltip text
281                            */
282                            if (typeof(tooltip) == 'function') {
283                                var text = String(tooltip(i));
284   
285                            } else {
286                                var text = String(tooltip);
287                            }
288   
289                            RGraph.Tooltip(canvas, text, event.pageX + 5, event.pageY - 5, i);
290   
291                            /**
292                            * Highlight the tickmark
293                            */
294                            if (obj.Get('chart.tooltips.highlight')) {
295                                context.beginPath();
296                                context.fillStyle = 'rgba(255,255,255,0.5)';
297                                context.arc(xCoord, yCoord, 3, 0, 6.2830, 0);
298                                context.fill();
299                            }
300                        }
301                    }
302                }
303               
304                if (!overHotspot) {
305                    canvas.style.cursor = 'default';
306                }
307            }
308            this.canvas.addEventListener('mousemove', canvas_onmousemove_func, false);
309            RGraph.AddEventListener(this.id, 'mousemove', canvas_onmousemove_func);
310        }
311
312        // Draw the title if any has been set
313        if (this.Get('chart.title')) {
314            RGraph.DrawTitle(this.canvas,
315                             this.Get('chart.title'),
316                             (this.canvas.height / 2) - this.radius - 5,
317                             this.centerx,
318                             this.Get('chart.text.size') + 2);
319        }
320       
321        /**
322        * If the canvas is annotatable, do install the event handlers
323        */
324        if (this.Get('chart.annotatable')) {
325            RGraph.Annotate(this);
326        }
327       
328        /**
329        * This bit shows the mini zoom window if requested
330        */
331        if (this.Get('chart.zoom.mode') == 'thumbnail' || this.Get('chart.zoom.mode') == 'area') {
332            RGraph.ShowZoomWindow(this);
333        }
334
335       
336        /**
337        * This function enables resizing
338        */
339        if (this.Get('chart.resizable')) {
340            RGraph.AllowResizing(this);
341        }
342       
343        /**
344        * Fire the RGraph ondraw event
345        */
346        RGraph.FireCustomEvent(this, 'ondraw');
347    }
348
349    /**
350    * This method draws the rose charts background
351    */
352    RGraph.Rscatter.prototype.DrawBackground = function ()
353    {
354        this.context.lineWidth = 1;
355   
356        // Draw the background grey circles
357        this.context.strokeStyle = '#ccc';
358        for (var i=15; i<this.radius - (document.all ? 5 : 0); i+=15) {// Radius must be greater than 0 for Opera to work
359            //this.context.moveTo(this.centerx + i, this.centery);
360   
361            // Radius must be greater than 0 for Opera to work
362            this.context.arc(this.centerx, this.centery, i, 0, (2 * Math.PI), 0);
363        }
364        this.context.stroke();
365
366        // Draw the background lines that go from the center outwards
367        this.context.beginPath();
368        for (var i=15; i<360; i+=15) {
369       
370            // Radius must be greater than 0 for Opera to work
371            this.context.arc(this.centerx, this.centery, this.radius, i / 57.3, (i + 0.01) / 57.3, 0);
372       
373            this.context.lineTo(this.centerx, this.centery);
374        }
375        this.context.stroke();
376       
377        this.context.beginPath();
378        this.context.strokeStyle = 'black';
379   
380        // Draw the X axis
381        this.context.moveTo(this.centerx - this.radius, this.centery);
382        this.context.lineTo(this.centerx + this.radius, this.centery);
383   
384        // Draw the X ends
385        this.context.moveTo(this.centerx - this.radius, this.centery - 5);
386        this.context.lineTo(this.centerx - this.radius, this.centery + 5);
387        this.context.moveTo(this.centerx + this.radius, this.centery - 5);
388        this.context.lineTo(this.centerx + this.radius, this.centery + 5);
389       
390        // Draw the X check marks
391        for (var i=(this.centerx - this.radius); i<(this.centerx + this.radius); i+=20) {
392            this.context.moveTo(i,  this.centery - 3);
393            this.context.lineTo(i,  this.centery + 3);
394        }
395       
396        // Draw the Y check marks
397        for (var i=(this.centery - this.radius); i<(this.centery + this.radius); i+=20) {
398            this.context.moveTo(this.centerx - 3, i);
399            this.context.lineTo(this.centerx + 3, i);
400        }
401   
402        // Draw the Y axis
403        this.context.moveTo(this.centerx, this.centery - this.radius);
404        this.context.lineTo(this.centerx, this.centery + this.radius);
405   
406        // Draw the Y ends
407        this.context.moveTo(this.centerx - 5, this.centery - this.radius);
408        this.context.lineTo(this.centerx + 5, this.centery - this.radius);
409   
410        this.context.moveTo(this.centerx - 5, this.centery + this.radius);
411        this.context.lineTo(this.centerx + 5, this.centery + this.radius);
412       
413        // Stroke it
414        this.context.closePath();
415        this.context.stroke();
416    }
417
418
419    /**
420    * This method draws a set of data on the graph
421    */
422    RGraph.Rscatter.prototype.DrawRscatter = function ()
423    {
424        var data = this.data;
425
426        for (var i=0; i<data.length; ++i) {
427
428            var d1 = data[i][0];
429            var d2 = data[i][1];
430            var a   = d1 / (180 / Math.PI); // RADIANS
431            var r   = ( (d2 - this.Get('chart.ymin')) / (this.max - this.Get('chart.ymin')) ) * this.radius;
432            var x   = Math.sin(a) * r;
433            var y   = Math.cos(a) * r;
434            var color = data[i][2] ? data[i][2] : this.Get('chart.colors.default');
435            var tooltip = data[i][3] ? data[i][3] : null;
436           
437            if (tooltip && tooltip.length) {
438                this.hasTooltips = true;
439            }
440
441            /**
442            * Account for the correct quadrant
443            */
444            x = x + this.centerx;
445            y = this.centery - y;
446
447
448            this.DrawTick(x, y, color);
449           
450            // Populate the coords array with the coordinates and the tooltip
451            this.coords.push([x, y, color, tooltip]);
452        }
453    }
454
455
456    /**
457    * Unsuprisingly, draws the labels
458    */
459    RGraph.Rscatter.prototype.DrawLabels = function ()
460    {
461        this.context.lineWidth = 1;
462        var key = this.Get('chart.key');
463       
464        // Set the color to black
465        this.context.fillStyle = 'black';
466        this.context.strokeStyle = 'black';
467       
468        var r         = this.radius;
469        var color     = this.Get('chart.text.color');
470        var font_face = this.Get('chart.text.font');
471        var font_size = this.Get('chart.text.size');
472        var context   = this.context;
473        var axes      = this.Get('chart.labels.axes').toLowerCase();
474       
475        this.context.fillStyle = this.Get('chart.text.color');
476
477        // Draw any labels
478        if (typeof(this.Get('chart.labels')) == 'object' && this.Get('chart.labels')) {
479            this.DrawCircularLabels(context, this.Get('chart.labels'), font_face, font_size, r);
480        }
481
482
483        var color = 'rgba(255,255,255,0.8)';
484
485        // The "North" axis labels
486        if (axes.indexOf('n') > -1) {
487            RGraph.Text(context,font_face,font_size,this.centerx,this.centery - ((r) * 0.2),String(this.scale[0]),'center','center',true,false,color);
488            RGraph.Text(context, font_face, font_size, this.centerx, this.centery - ((r) * 0.4), String(this.scale[1]), 'center', 'center', true, false, color);
489            RGraph.Text(context, font_face, font_size, this.centerx, this.centery - ((r) * 0.6), String(this.scale[2]), 'center', 'center', true, false, color);
490            RGraph.Text(context, font_face, font_size, this.centerx, this.centery - ((r) * 0.8), String(this.scale[3]), 'center', 'center', true, false, color);
491            RGraph.Text(context, font_face, font_size, this.centerx, this.centery - r, String(this.scale[4]), 'center', 'center', true, false, color);
492        }
493
494        // The "South" axis labels
495        if (axes.indexOf('s') > -1) {
496            RGraph.Text(context, font_face, font_size, this.centerx, this.centery + ((r) * 0.2), String(this.scale[0]), 'center', 'center', true, false, color);
497            RGraph.Text(context, font_face, font_size, this.centerx, this.centery + ((r) * 0.4), String(this.scale[1]), 'center', 'center', true, false, color);
498            RGraph.Text(context, font_face, font_size, this.centerx, this.centery + ((r) * 0.6), String(this.scale[2]), 'center', 'center', true, false, color);
499            RGraph.Text(context, font_face, font_size, this.centerx, this.centery + ((r) * 0.8), String(this.scale[3]), 'center', 'center', true, false, color);
500            RGraph.Text(context, font_face, font_size, this.centerx, this.centery + r, String(this.scale[4]), 'center', 'center', true, false, color);
501        }
502       
503        // The "East" axis labels
504        if (axes.indexOf('e') > -1) {
505            RGraph.Text(context, font_face, font_size, this.centerx + ((r) * 0.2), this.centery, String(this.scale[0]), 'center', 'center', true, false, color);
506            RGraph.Text(context, font_face, font_size, this.centerx + ((r) * 0.4), this.centery, String(this.scale[1]), 'center', 'center', true, false, color);
507            RGraph.Text(context, font_face, font_size, this.centerx + ((r) * 0.6), this.centery, String(this.scale[2]), 'center', 'center', true, false, color);
508            RGraph.Text(context, font_face, font_size, this.centerx + ((r) * 0.8), this.centery, String(this.scale[3]), 'center', 'center', true, false, color);
509            RGraph.Text(context, font_face, font_size, this.centerx + r, this.centery, String(this.scale[4]), 'center', 'center', true, false, color);
510        }
511
512        // The "West" axis labels
513        if (axes.indexOf('w') > -1) {
514            RGraph.Text(context, font_face, font_size, this.centerx - ((r) * 0.2), this.centery, String(this.scale[0]), 'center', 'center', true, false, color);
515            RGraph.Text(context, font_face, font_size, this.centerx - ((r) * 0.4), this.centery, String(this.scale[1]), 'center', 'center', true, false, color);
516            RGraph.Text(context, font_face, font_size, this.centerx - ((r) * 0.6), this.centery, String(this.scale[2]), 'center', 'center', true, false, color);
517            RGraph.Text(context, font_face, font_size, this.centerx - ((r) * 0.8), this.centery, String(this.scale[3]), 'center', 'center', true, false, color);
518            RGraph.Text(context, font_face, font_size, this.centerx - r, this.centery, String(this.scale[4]), 'center', 'center', true, false, color);
519        }
520       
521        // Draw the center minimum value (but only if there's at least one axes labels stipulated)
522        if (this.Get('chart.labels.axes').length > 0) {
523            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);
524        }
525
526        /**
527        * Draw the key
528        */
529        if (key && key.length) {
530            RGraph.DrawKey(this, key, this.Get('chart.colors'));
531        }
532    }
533
534
535    /**
536    * Draws the circular labels that go around the charts
537    *
538    * @param labels array The labels that go around the chart
539    */
540    RGraph.Rscatter.prototype.DrawCircularLabels = function (context, labels, font_face, font_size, r)
541    {
542        var position = this.Get('chart.labels.position');
543        var r        = r + 10;
544
545        for (var i=0; i<labels.length; ++i) {
546
547
548            var a = (360 / labels.length) * (i + 1) - (360 / (labels.length * 2));
549            var a = a - 90 + (this.Get('chart.labels.position') == 'edge' ? ((360 / labels.length) / 2) : 0);
550
551            var x = Math.cos(a / 57.29577866666) * (r + 10);
552            var y = Math.sin(a / 57.29577866666) * (r + 10);
553
554            RGraph.Text(context, font_face, font_size, this.centerx + x, this.centery + y, String(labels[i]), 'center', 'center');
555        }
556    }
557
558
559    /**
560    * Draws a single tickmark
561    */
562    RGraph.Rscatter.prototype.DrawTick = function (x, y, color)
563    {
564        var tickmarks    = this.Get('chart.tickmarks');
565        var ticksize     = this.Get('chart.ticksize');
566
567        this.context.strokeStyle = color;
568        this.context.fillStyle   = color;
569
570        // Cross
571        if (tickmarks == 'cross') {
572
573            this.context.beginPath();
574            this.context.moveTo(x + ticksize, y + ticksize);
575            this.context.lineTo(x - ticksize, y - ticksize);
576            this.context.stroke();
577   
578            this.context.beginPath();
579            this.context.moveTo(x - ticksize, y + ticksize);
580            this.context.lineTo(x + ticksize, y - ticksize);
581            this.context.stroke();
582       
583        // Circle
584        } else if (tickmarks == 'circle') {
585
586            this.context.beginPath();
587            this.context.arc(x, y, ticksize, 0, 6.2830, false);
588            this.context.fill();
589
590        // Square
591        } else if (tickmarks == 'square') {
592
593            this.context.beginPath();
594            this.context.fillRect(x - ticksize, y - ticksize, 2 * ticksize, 2 * ticksize);
595            this.context.fill();
596       
597        // Diamond shape tickmarks
598         } else if (tickmarks == 'diamond') {
599
600            this.context.beginPath();
601                this.context.moveTo(x, y - ticksize);
602                this.context.lineTo(x + ticksize, y);
603                this.context.lineTo(x, y + ticksize);
604                this.context.lineTo(x - ticksize, y);
605            this.context.closePath();
606            this.context.fill();
607
608        // Plus style tickmarks
609        } else if (tickmarks == 'plus') {
610       
611            this.context.lineWidth = 1;
612
613            this.context.beginPath();
614                this.context.moveTo(x, y - ticksize);
615                this.context.lineTo(x, y + ticksize);
616                this.context.moveTo(x - ticksize, y);
617                this.context.lineTo(x + ticksize, y);
618            this.context.stroke();
619        }
620    }
Note: See TracBrowser for help on using the repository browser.