~jstys-z/helioviewer.org/timeline

« back to all changes in this revision

Viewing changes to timeline/Highstock-1.3.10/js/modules/funnel.src.js

  • Committer: Jeff Stys
  • Date: 2014-04-21 12:46:26 UTC
  • Revision ID: jstys@sesda3.com-20140421124626-2332pb2dyjc33jxi
Proof-of-concept version of Data Coverage Timeline using Highchart/Highstock javascript library.  Changes to getDataCoverage API in order to feed the necessary data to the Timeline

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/**
 
2
 * @license 
 
3
 * Highcharts funnel module
 
4
 *
 
5
 * (c) 2010-2014 Torstein Honsi
 
6
 *
 
7
 * License: www.highcharts.com/license
 
8
 */
 
9
 
 
10
/*global Highcharts */
 
11
(function (Highcharts) {
 
12
        
 
13
'use strict';
 
14
 
 
15
// create shortcuts
 
16
var defaultOptions = Highcharts.getOptions(),
 
17
        defaultPlotOptions = defaultOptions.plotOptions,
 
18
        seriesTypes = Highcharts.seriesTypes,
 
19
        merge = Highcharts.merge,
 
20
        noop = function () {},
 
21
        each = Highcharts.each;
 
22
 
 
23
// set default options
 
24
defaultPlotOptions.funnel = merge(defaultPlotOptions.pie, {
 
25
        center: ['50%', '50%'],
 
26
        width: '90%',
 
27
        neckWidth: '30%',
 
28
        height: '100%',
 
29
        neckHeight: '25%',
 
30
        reversed: false,
 
31
        dataLabels: {
 
32
                //position: 'right',
 
33
                connectorWidth: 1,
 
34
                connectorColor: '#606060'
 
35
        },
 
36
        size: true, // to avoid adapting to data label size in Pie.drawDataLabels
 
37
        states: {
 
38
                select: {
 
39
                        color: '#C0C0C0',
 
40
                        borderColor: '#000000',
 
41
                        shadow: false
 
42
                }
 
43
        }       
 
44
});
 
45
 
 
46
 
 
47
seriesTypes.funnel = Highcharts.extendClass(seriesTypes.pie, {
 
48
        
 
49
        type: 'funnel',
 
50
        animate: noop,
 
51
        singularTooltips: true,
 
52
 
 
53
        /**
 
54
         * Overrides the pie translate method
 
55
         */
 
56
        translate: function () {
 
57
                
 
58
                var 
 
59
                        // Get positions - either an integer or a percentage string must be given
 
60
                        getLength = function (length, relativeTo) {
 
61
                                return (/%$/).test(length) ?
 
62
                                        relativeTo * parseInt(length, 10) / 100 :
 
63
                                        parseInt(length, 10);
 
64
                        },
 
65
                        
 
66
                        sum = 0,
 
67
                        series = this,
 
68
                        chart = series.chart,
 
69
                        options = series.options,
 
70
                        reversed = options.reversed,
 
71
                        plotWidth = chart.plotWidth,
 
72
                        plotHeight = chart.plotHeight,
 
73
                        cumulative = 0, // start at top
 
74
                        center = options.center,
 
75
                        centerX = getLength(center[0], plotWidth),
 
76
                        centerY = getLength(center[0], plotHeight),
 
77
                        width = getLength(options.width, plotWidth),
 
78
                        tempWidth,
 
79
                        getWidthAt,
 
80
                        height = getLength(options.height, plotHeight),
 
81
                        neckWidth = getLength(options.neckWidth, plotWidth),
 
82
                        neckHeight = getLength(options.neckHeight, plotHeight),
 
83
                        neckY = height - neckHeight,
 
84
                        data = series.data,
 
85
                        path,
 
86
                        fraction,
 
87
                        half = options.dataLabels.position === 'left' ? 1 : 0,
 
88
 
 
89
                        x1, 
 
90
                        y1, 
 
91
                        x2, 
 
92
                        x3, 
 
93
                        y3, 
 
94
                        x4, 
 
95
                        y5;
 
96
 
 
97
                // Return the width at a specific y coordinate
 
98
                series.getWidthAt = getWidthAt = function (y) {
 
99
                        return y > height - neckHeight || height === neckHeight ?
 
100
                                neckWidth :
 
101
                                neckWidth + (width - neckWidth) * ((height - neckHeight - y) / (height - neckHeight));
 
102
                };
 
103
                series.getX = function (y, half) {
 
104
                                        return centerX + (half ? -1 : 1) * ((getWidthAt(reversed ? plotHeight - y : y) / 2) + options.dataLabels.distance);
 
105
                };
 
106
 
 
107
                // Expose
 
108
                series.center = [centerX, centerY, height];
 
109
                series.centerX = centerX;
 
110
 
 
111
                /*
 
112
                 * Individual point coordinate naming:
 
113
                 *
 
114
                 * x1,y1 _________________ x2,y1
 
115
                 *  \                         /
 
116
                 *   \                       /
 
117
                 *    \                     /
 
118
                 *     \                   /
 
119
                 *      \                 /
 
120
                 *     x3,y3 _________ x4,y3
 
121
                 *
 
122
                 * Additional for the base of the neck:
 
123
                 *
 
124
                 *       |               |
 
125
                 *       |               |
 
126
                 *       |               |
 
127
                 *     x3,y5 _________ x4,y5
 
128
                 */
 
129
 
 
130
 
 
131
 
 
132
 
 
133
                // get the total sum
 
134
                each(data, function (point) {
 
135
                        sum += point.y;
 
136
                });
 
137
 
 
138
                each(data, function (point) {
 
139
                        // set start and end positions
 
140
                        y5 = null;
 
141
                        fraction = sum ? point.y / sum : 0;
 
142
                        y1 = centerY - height / 2 + cumulative * height;
 
143
                        y3 = y1 + fraction * height;
 
144
                        //tempWidth = neckWidth + (width - neckWidth) * ((height - neckHeight - y1) / (height - neckHeight));
 
145
                        tempWidth = getWidthAt(y1);
 
146
                        x1 = centerX - tempWidth / 2;
 
147
                        x2 = x1 + tempWidth;
 
148
                        tempWidth = getWidthAt(y3);
 
149
                        x3 = centerX - tempWidth / 2;
 
150
                        x4 = x3 + tempWidth;
 
151
 
 
152
                        // the entire point is within the neck
 
153
                        if (y1 > neckY) {
 
154
                                x1 = x3 = centerX - neckWidth / 2;
 
155
                                x2 = x4 = centerX + neckWidth / 2;
 
156
                        
 
157
                        // the base of the neck
 
158
                        } else if (y3 > neckY) {
 
159
                                y5 = y3;
 
160
 
 
161
                                tempWidth = getWidthAt(neckY);
 
162
                                x3 = centerX - tempWidth / 2;
 
163
                                x4 = x3 + tempWidth;
 
164
 
 
165
                                y3 = neckY;
 
166
                        }
 
167
 
 
168
                        if (reversed) {
 
169
                                y1 = height - y1;
 
170
                                y3 = height - y3;
 
171
                                y5 = (y5 ? height - y5 : null);
 
172
                        }
 
173
                        // save the path
 
174
                        path = [
 
175
                                'M',
 
176
                                x1, y1,
 
177
                                'L',
 
178
                                x2, y1,
 
179
                                x4, y3
 
180
                        ];
 
181
                        if (y5) {
 
182
                                path.push(x4, y5, x3, y5);
 
183
                        }
 
184
                        path.push(x3, y3, 'Z');
 
185
 
 
186
                        // prepare for using shared dr
 
187
                        point.shapeType = 'path';
 
188
                        point.shapeArgs = { d: path };
 
189
 
 
190
 
 
191
                        // for tooltips and data labels
 
192
                        point.percentage = fraction * 100;
 
193
                        point.plotX = centerX;
 
194
                        point.plotY = (y1 + (y5 || y3)) / 2;
 
195
 
 
196
                        // Placement of tooltips and data labels
 
197
                        point.tooltipPos = [
 
198
                                centerX,
 
199
                                point.plotY
 
200
                        ];
 
201
 
 
202
                        // Slice is a noop on funnel points
 
203
                        point.slice = noop;
 
204
                        
 
205
                        // Mimicking pie data label placement logic
 
206
                        point.half = half;
 
207
 
 
208
                        cumulative += fraction;
 
209
                });             
 
210
        },
 
211
        /**
 
212
         * Draw a single point (wedge)
 
213
         * @param {Object} point The point object
 
214
         * @param {Object} color The color of the point
 
215
         * @param {Number} brightness The brightness relative to the color
 
216
         */
 
217
        drawPoints: function () {
 
218
                var series = this,
 
219
                        options = series.options,
 
220
                        chart = series.chart,
 
221
                        renderer = chart.renderer;
 
222
 
 
223
                each(series.data, function (point) {
 
224
                        
 
225
                        var graphic = point.graphic,
 
226
                                shapeArgs = point.shapeArgs;
 
227
 
 
228
                        if (!graphic) { // Create the shapes
 
229
                                point.graphic = renderer.path(shapeArgs).
 
230
                                        attr({
 
231
                                                fill: point.color,
 
232
                                                stroke: options.borderColor,
 
233
                                                'stroke-width': options.borderWidth
 
234
                                        }).
 
235
                                        add(series.group);
 
236
                                        
 
237
                        } else { // Update the shapes
 
238
                                graphic.animate(shapeArgs);
 
239
                        }
 
240
                });
 
241
        },
 
242
 
 
243
        /**
 
244
         * Funnel items don't have angles (#2289)
 
245
         */
 
246
        sortByAngle: function (points) {
 
247
                points.sort(function (a, b) {
 
248
                        return a.plotY - b.plotY;
 
249
                });
 
250
        },
 
251
        
 
252
        /**
 
253
         * Extend the pie data label method
 
254
         */
 
255
        drawDataLabels: function () {
 
256
                var data = this.data,
 
257
                        labelDistance = this.options.dataLabels.distance,
 
258
                        leftSide,
 
259
                        sign,
 
260
                        point,
 
261
                        i = data.length,
 
262
                        x,
 
263
                        y;
 
264
                
 
265
                // In the original pie label anticollision logic, the slots are distributed
 
266
                // from one labelDistance above to one labelDistance below the pie. In funnels
 
267
                // we don't want this.
 
268
                this.center[2] -= 2 * labelDistance;
 
269
                
 
270
                // Set the label position array for each point.
 
271
                while (i--) {
 
272
                        point = data[i];
 
273
                        leftSide = point.half;
 
274
                        sign = leftSide ? 1 : -1;
 
275
                        y = point.plotY;
 
276
                        x = this.getX(y, leftSide);
 
277
                                
 
278
                        // set the anchor point for data labels
 
279
                        point.labelPos = [
 
280
                                0, // first break of connector
 
281
                                y, // a/a
 
282
                                x + (labelDistance - 5) * sign, // second break, right outside point shape
 
283
                                y, // a/a
 
284
                                x + labelDistance * sign, // landing point for connector
 
285
                                y, // a/a
 
286
                                leftSide ? 'right' : 'left', // alignment
 
287
                                0 // center angle
 
288
                        ];
 
289
                }
 
290
                
 
291
                seriesTypes.pie.prototype.drawDataLabels.call(this);
 
292
        }
 
293
 
 
294
});
 
295
 
 
296
/** 
 
297
 * Pyramid series type.
 
298
 * A pyramid series is a special type of funnel, without neck and reversed by default.
 
299
 */
 
300
defaultOptions.plotOptions.pyramid = Highcharts.merge(defaultOptions.plotOptions.funnel, {        
 
301
        neckWidth: '0%',
 
302
        neckHeight: '0%',
 
303
        reversed: true
 
304
});
 
305
Highcharts.seriesTypes.pyramid = Highcharts.extendClass(Highcharts.seriesTypes.funnel, {
 
306
        type: 'pyramid'
 
307
});
 
308
 
 
309
}(Highcharts));