Mercurial > hg > extraction-interface
comparison geotemco/js/FuzzyTimeline/FuzzyTimelineRangeBars.js @ 0:b12c99b7c3f0
commit for previous development
author | Zoe Hong <zhong@mpiwg-berlin.mpg.de> |
---|---|
date | Mon, 19 Jan 2015 17:13:49 +0100 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:b12c99b7c3f0 |
---|---|
1 /* | |
2 * FuzzyTimelineRangeBars.js | |
3 * | |
4 * Copyright (c) 2013, Sebastian Kruse. All rights reserved. | |
5 * | |
6 * This library is free software; you can redistribute it and/or | |
7 * modify it under the terms of the GNU Lesser General Public | |
8 * License as published by the Free Software Foundation; either | |
9 * version 3 of the License, or (at your option) any later version. | |
10 * | |
11 * This library is distributed in the hope that it will be useful, | |
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 * Lesser General Public License for more details. | |
15 * | |
16 * You should have received a copy of the GNU Lesser General Public | |
17 * License along with this library; if not, write to the Free Software | |
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, | |
19 * MA 02110-1301 USA | |
20 */ | |
21 | |
22 /** | |
23 * @class FuzzyTimelineRangeBars | |
24 * Implementation for a fuzzy time-ranges barchart | |
25 * @author Sebastian Kruse (skruse@mpiwg-berlin.mpg.de) | |
26 * | |
27 * @param {HTML object} parent div to append the FuzzyTimeline | |
28 */ | |
29 function FuzzyTimelineRangeBars(parent) { | |
30 | |
31 this.rangeBars = this; | |
32 | |
33 this.parent = parent; | |
34 this.options = parent.options; | |
35 | |
36 this.datasets; | |
37 //contains selected data | |
38 this.selected = undefined; | |
39 | |
40 this.datasetsPlot; | |
41 this.highlightedDatasetsPlot; | |
42 this.yValMin; | |
43 this.yValMax; | |
44 this.displayType; | |
45 | |
46 this.plotDiv = this.parent.gui.plotDiv; | |
47 | |
48 this.spanWidth; | |
49 this.tickSpans; | |
50 this.plot; | |
51 } | |
52 | |
53 FuzzyTimelineRangeBars.prototype = { | |
54 | |
55 initialize : function(datasets) { | |
56 var rangeBar = this; | |
57 | |
58 rangeBar.datasets = datasets; | |
59 rangeBar.selected = []; | |
60 }, | |
61 | |
62 createPlot : function(datasets) { | |
63 var rangeBar = this; | |
64 var plots = []; | |
65 var objectHashes = []; | |
66 | |
67 //-1 because last span is always empty (only there to have the ending date) | |
68 var tickCount = rangeBar.tickSpans.length-1; | |
69 | |
70 $(datasets).each(function(){ | |
71 var chartDataCounter = []; | |
72 var objectHash = new Object(); | |
73 | |
74 for (var i = 0; i < tickCount; i++){ | |
75 chartDataCounter[i]=0; | |
76 } | |
77 //check if we got "real" datasets, or just array of objects | |
78 var datasetObjects = this; | |
79 if (typeof this.objects !== "undefined") | |
80 datasetObjects = this.objects; | |
81 $(datasetObjects).each(function(){ | |
82 var ticks = rangeBar.parent.getTicks(this, rangeBar.spanWidth); | |
83 if (typeof ticks !== "undefined"){ | |
84 var exactTickCount = | |
85 ticks.firstTickPercentage+ | |
86 ticks.lastTickPercentage+ | |
87 (ticks.lastTick-ticks.firstTick-1); | |
88 for (var i = ticks.firstTick; i <= ticks.lastTick; i++){ | |
89 var weight = 0; | |
90 //calculate the weight for each span, that the object overlaps | |
91 if (rangeBar.parent.options.timelineMode == 'fuzzy'){ | |
92 //in fuzzy mode, each span gets just a fraction of the complete weight | |
93 if (i == ticks.firstTick) | |
94 weight = this.weight * ticks.firstTickPercentage/exactTickCount; | |
95 else if (i == ticks.lastTick) | |
96 weight = this.weight * ticks.lastTickPercentage/exactTickCount; | |
97 else | |
98 weight = this.weight * 1/exactTickCount; | |
99 } else if (rangeBar.parent.options.timelineMode == 'stacking'){ | |
100 //in stacking mode each span gets the same amount. | |
101 //(besides first and last..) | |
102 if (i == ticks.firstTick) | |
103 weight = this.weight * ticks.firstTickPercentage; | |
104 else if (i == ticks.lastTick) | |
105 weight = this.weight * ticks.lastTickPercentage; | |
106 else | |
107 weight = this.weight; | |
108 | |
109 weight = this.weight; | |
110 } | |
111 | |
112 chartDataCounter[i] += weight; | |
113 //add this object to the hash | |
114 if (typeof objectHash[i] === "undefined") | |
115 objectHash[i] = []; | |
116 objectHash[i].push(this); | |
117 } | |
118 } | |
119 }); | |
120 | |
121 //scale according to selected type | |
122 chartDataCounter = rangeBar.parent.scaleData(chartDataCounter); | |
123 | |
124 //transform data so it can be passed to the flot barchart | |
125 var plotData = []; | |
126 for (var i = 0; i < tickCount; i++){ | |
127 plotData[i] = []; | |
128 plotData[i][0] = i; | |
129 plotData[i][1] = chartDataCounter[i]; | |
130 } | |
131 | |
132 //delete bars with 0 values | |
133 for (var i = 0; i < tickCount; i++){ | |
134 if (plotData[i][1]==0) | |
135 delete plotData[i]; | |
136 } | |
137 | |
138 plots.push(plotData); | |
139 objectHashes.push(objectHash); | |
140 }); | |
141 | |
142 return {plots:plots, hashs:objectHashes}; | |
143 }, | |
144 | |
145 showPlot : function(){ | |
146 var rangeBar = this; | |
147 var plot = rangeBar.datasetsPlot; | |
148 var highlight_select_plot = $.merge([],plot); | |
149 | |
150 //see if there are selected/highlighted values | |
151 if (rangeBar.highlightedDatasetsPlot instanceof Array){ | |
152 //check if plot is some other - external - graph | |
153 if (plot === rangeBar.datasetsPlot) | |
154 highlight_select_plot = $.merge(highlight_select_plot,rangeBar.highlightedDatasetsPlot); | |
155 } | |
156 | |
157 var tickCount = rangeBar.tickSpans.length-1; | |
158 var ticks = []; | |
159 | |
160 var axisFormatString = "YYYY"; | |
161 if (rangeBar.spanWidth<60*1000){ | |
162 axisFormatString = "YYYY/MM/DD HH:mm:ss"; | |
163 } else if (rangeBar.spanWidth<60*60*1000) { | |
164 axisFormatString = "YYYY/MM/DD HH:mm"; | |
165 } else if (rangeBar.spanWidth<24*60*60*1000){ | |
166 axisFormatString = "YYYY/MM/DD HH"; | |
167 } else if (rangeBar.spanWidth<31*24*60*60*1000){ | |
168 axisFormatString = "YYYY/MM/DD"; | |
169 } else if (rangeBar.spanWidth<12*31*24*60*60*1000){ | |
170 axisFormatString = "YYYY/MM"; | |
171 } | |
172 //only show ~10 labels on the x-Axis (increase if zoomed) | |
173 var labelModulo = Math.ceil(tickCount/(10*rangeBar.parent.zoomFactor)); | |
174 for (var i = 0; i < tickCount; i++){ | |
175 var tickLabel = ""; | |
176 if (i%labelModulo==0){ | |
177 tickLabel = rangeBar.tickSpans[i].format(axisFormatString); | |
178 } | |
179 while ((tickLabel.length > 1) && (tickLabel.indexOf("0")==0)) | |
180 tickLabel = tickLabel.substring(1); | |
181 ticks[i] = [i,tickLabel]; | |
182 } | |
183 | |
184 var options = { | |
185 series:{ | |
186 bars:{show: true} | |
187 }, | |
188 grid: { | |
189 hoverable: true, | |
190 clickable: true, | |
191 backgroundColor: rangeBar.parent.options.backgroundColor, | |
192 borderWidth: 0, | |
193 minBorderMargin: 0, | |
194 }, | |
195 xaxis: { | |
196 ticks: ticks, | |
197 min : 0, | |
198 max : tickCount, | |
199 }, | |
200 yaxis: { | |
201 min : rangeBar.yValMin, | |
202 max : rangeBar.yValMax*1.05 | |
203 }, | |
204 tooltip: true, | |
205 tooltipOpts: { | |
206 content: function(label, xval, yval, flotItem){ | |
207 var fromLabel = rangeBar.tickSpans[xval].format(axisFormatString); | |
208 while ((fromLabel.length > 1) && (fromLabel.indexOf("0")==0)) | |
209 fromLabel = fromLabel.substring(1); | |
210 var toLabel = rangeBar.tickSpans[xval+1].clone().subtract("ms",1).format(axisFormatString); | |
211 while ((toLabel.length > 1) && (toLabel.indexOf("0")==0)) | |
212 toLabel = toLabel.substring(1); | |
213 highlightString = fromLabel + " - " + toLabel + " : "; | |
214 //(max.)2 Nachkomma-Stellen von y-Wert anzeigen | |
215 highlightString += Math.round(yval*100)/100; | |
216 | |
217 return highlightString; | |
218 } | |
219 }, | |
220 selection: { | |
221 mode: "x" | |
222 } | |
223 }; | |
224 if (!rangeBar.parent.options.showYAxis) | |
225 options.yaxis.show=false; | |
226 | |
227 var highlight_select_plot_colors = []; | |
228 var i = 0; | |
229 $(highlight_select_plot).each(function(){ | |
230 var color; | |
231 if (i < GeoTemConfig.datasets.length){ | |
232 var datasetColors = GeoTemConfig.getColor(i); | |
233 if (highlight_select_plot.length>GeoTemConfig.datasets.length) | |
234 color = "rgb("+datasetColors.r0+","+datasetColors.g0+","+datasetColors.b0+")"; | |
235 else | |
236 color = "rgb("+datasetColors.r1+","+datasetColors.g1+","+datasetColors.b1+")"; | |
237 } else { | |
238 var datasetColors = GeoTemConfig.getColor(i-GeoTemConfig.datasets.length); | |
239 color = "rgb("+datasetColors.r1+","+datasetColors.g1+","+datasetColors.b1+")"; | |
240 } | |
241 | |
242 highlight_select_plot_colors.push({ | |
243 color : color, | |
244 data : this | |
245 }); | |
246 i++; | |
247 }); | |
248 | |
249 $(rangeBar.plotDiv).unbind(); | |
250 rangeBar.plot = $.plot($(rangeBar.plotDiv), highlight_select_plot_colors, options); | |
251 rangeBar.parent.drawHandles(); | |
252 | |
253 var density = rangeBar.parent.density; | |
254 if (typeof density !== "undefined") | |
255 $(rangeBar.plotDiv).unbind("plothover", density.hoverFunction); | |
256 $(rangeBar.plotDiv).unbind("plothover", rangeBar.hoverFunction); | |
257 $(rangeBar.plotDiv).bind("plothover", $.proxy(rangeBar.hoverFunction,rangeBar)); | |
258 | |
259 //this var prevents the execution of the plotclick event after a select event | |
260 rangeBar.wasSelection = false; | |
261 $(rangeBar.plotDiv).unbind("plotclick"); | |
262 $(rangeBar.plotDiv).bind("plotclick", $.proxy(rangeBar.clickFunction,rangeBar)); | |
263 | |
264 $(rangeBar.plotDiv).unbind("plotselected"); | |
265 $(rangeBar.plotDiv).bind("plotselected", $.proxy(rangeBar.selectFunction,rangeBar)); | |
266 }, | |
267 | |
268 hoverFunction : function (event, pos, item) { | |
269 var rangeBar = this; | |
270 var hoverBar; | |
271 var spans; | |
272 if (item) { | |
273 hoverBar = item.datapoint[0]; | |
274 } | |
275 //remember last date, so that we don't redraw the current state | |
276 //that date may be undefined is on purpose | |
277 if (rangeBar.highlighted !== hoverBar){ | |
278 rangeBar.highlighted = hoverBar; | |
279 if (typeof hoverBar === "undefined") | |
280 rangeBar.triggerHighlight(); | |
281 else | |
282 rangeBar.triggerHighlight(hoverBar); | |
283 } | |
284 }, | |
285 | |
286 clickFunction : function (event, pos, item) { | |
287 var rangeBar = this; | |
288 if (rangeBar.wasSelection) | |
289 rangeBar.wasSelection = false; | |
290 else { | |
291 //remove selection handles (if there were any) | |
292 rangeBar.parent.clearHandles(); | |
293 | |
294 var clickBar; | |
295 if (item) { | |
296 //contains the x-value (date) | |
297 clickBar = item.datapoint[0]; | |
298 } | |
299 if (typeof clickBar === "undefined") | |
300 rangeBar.triggerSelection(); | |
301 else | |
302 rangeBar.triggerSelection(clickBar); | |
303 wasDataClick = true; | |
304 } | |
305 }, | |
306 | |
307 selectFunction : function(event, ranges) { | |
308 var rangeBar = this; | |
309 startBar = Math.floor(ranges.xaxis.from); | |
310 endBar = Math.floor(ranges.xaxis.to); | |
311 rangeBar.triggerSelection(startBar, endBar); | |
312 rangeBar.wasSelection = true; | |
313 | |
314 rangeBar.parent.clearHandles(); | |
315 var xaxis = rangeBar.plot.getAxes().xaxis; | |
316 var x1 = rangeBar.plot.pointOffset({x:ranges.xaxis.from,y:0}).left; | |
317 var x2 = rangeBar.plot.pointOffset({x:ranges.xaxis.to,y:0}).left; | |
318 rangeBar.parent.addHandle(x1,x2); | |
319 }, | |
320 | |
321 selectByX : function(x1, x2){ | |
322 rangeBar = this; | |
323 var xaxis = rangeBar.plot.getAxes().xaxis; | |
324 var offset = rangeBar.plot.getPlotOffset().left; | |
325 var from = Math.floor(xaxis.c2p(x1-offset)); | |
326 var to = Math.floor(xaxis.c2p(x2-offset)); | |
327 | |
328 rangeBar.triggerSelection(from, to); | |
329 }, | |
330 | |
331 drawRangeBarChart : function(datasets, spanWidth){ | |
332 var rangeBar = this; | |
333 rangeBar.spanWidth = spanWidth; | |
334 rangeBar.tickSpans = rangeBar.parent.getSpanArray(rangeBar.spanWidth); | |
335 //-1 because last span is always empty (only there to have the ending date) | |
336 var tickCount = rangeBar.tickSpans.length-1; | |
337 | |
338 if (tickCount > rangeBar.options.maxBars){ | |
339 var zoomFactor = tickCount / rangeBar.options.maxBars; | |
340 rangeBar.parent.zoomPlot(zoomFactor); | |
341 } else | |
342 rangeBar.parent.zoomPlot(1); | |
343 | |
344 rangeBar.yValMin = 0; | |
345 rangeBar.yValMax = 0; | |
346 | |
347 var plotAndHash = rangeBar.createPlot(datasets); | |
348 rangeBar.datasetsPlot = plotAndHash.plots; | |
349 rangeBar.datasetsHash = plotAndHash.hashs; | |
350 delete rangeBar.highlightedDatasetsPlot; | |
351 //redraw selected plot to fit (possible) new scale | |
352 rangeBar.selectionChanged(rangeBar.selected); | |
353 | |
354 //get min and max values | |
355 for (var i = 0; i < rangeBar.datasetsPlot.length; i++){ | |
356 for (var j = 0; j < rangeBar.datasetsPlot[i].length; j++){ | |
357 if (typeof rangeBar.datasetsPlot[i][j] !== "undefined"){ | |
358 var val = rangeBar.datasetsPlot[i][j][1]; | |
359 | |
360 if (val < rangeBar.yValMin) | |
361 rangeBar.yValMin = val; | |
362 if (val > rangeBar.yValMax) | |
363 rangeBar.yValMax = val; | |
364 } | |
365 } | |
366 } | |
367 | |
368 rangeBar.showPlot(); | |
369 }, | |
370 | |
371 highlightChanged : function(objects) { | |
372 if( !GeoTemConfig.highlightEvents ){ | |
373 return; | |
374 } | |
375 var rangeBar = this; | |
376 var emptyHighlight = true; | |
377 var selected_highlighted = objects; | |
378 if (typeof rangeBar.selected !== "undefined") | |
379 var selected_highlighted = GeoTemConfig.mergeObjects(objects,rangeBar.selected); | |
380 $(selected_highlighted).each(function(){ | |
381 if ((this instanceof Array) && (this.length > 0)){ | |
382 emptyHighlight = false; | |
383 return false; | |
384 } | |
385 }); | |
386 if (emptyHighlight && (typeof rangeBar.selected === "undefined")){ | |
387 rangeBar.highlightedDatasetsPlot = []; | |
388 } else { | |
389 rangeBar.highlightedDatasetsPlot = rangeBar.createPlot(selected_highlighted).plots; | |
390 } | |
391 rangeBar.showPlot(); | |
392 }, | |
393 | |
394 selectionChanged : function(objects) { | |
395 if( !GeoTemConfig.selectionEvents ){ | |
396 return; | |
397 } | |
398 var rangeBar = this; | |
399 rangeBar.selected = objects; | |
400 rangeBar.highlightChanged([]); | |
401 }, | |
402 | |
403 triggerHighlight : function(hoverPoint) { | |
404 var rangeBar = this; | |
405 var highlightedObjects = []; | |
406 | |
407 if (typeof hoverPoint !== "undefined"){ | |
408 $(rangeBar.datasetsHash).each(function(){ | |
409 if (typeof this[hoverPoint] !== "undefined") | |
410 highlightedObjects.push(this[hoverPoint]); | |
411 else | |
412 highlightedObjects.push([]); | |
413 }); | |
414 } else { | |
415 for (var i = 0; i < GeoTemConfig.datasets.length; i++) | |
416 highlightedObjects.push([]); | |
417 } | |
418 | |
419 this.parent.core.triggerHighlight(highlightedObjects); | |
420 }, | |
421 | |
422 triggerSelection : function(startBar, endBar) { | |
423 var rangeBar = this; | |
424 var selection; | |
425 if (typeof startBar !== "undefined") { | |
426 if (typeof endBar === "undefined") | |
427 endBar = startBar; | |
428 rangeBar.selected = []; | |
429 $(rangeBar.datasetsHash).each(function(){ | |
430 var objects = []; | |
431 for (var i = startBar; i <= endBar; i++){ | |
432 $(this[i]).each(function(){ | |
433 if ($.inArray(this, objects) == -1){ | |
434 objects.push(this); | |
435 } | |
436 }); | |
437 } | |
438 rangeBar.selected.push(objects); | |
439 }); | |
440 selection = new Selection(rangeBar.selected, rangeBar.parent); | |
441 } else { | |
442 rangeBar.selected = []; | |
443 for (var i = 0; i < GeoTemConfig.datasets.length; i++) | |
444 rangeBar.selected.push([]); | |
445 selection = new Selection(rangeBar.selected); | |
446 } | |
447 | |
448 rangeBar.parent.selectionChanged(selection); | |
449 rangeBar.parent.core.triggerSelection(selection); | |
450 }, | |
451 | |
452 deselection : function() { | |
453 }, | |
454 | |
455 filtering : function() { | |
456 }, | |
457 | |
458 inverseFiltering : function() { | |
459 }, | |
460 | |
461 triggerRefining : function() { | |
462 }, | |
463 | |
464 reset : function() { | |
465 }, | |
466 | |
467 show : function() { | |
468 }, | |
469 | |
470 hide : function() { | |
471 } | |
472 }; |