comparison lib/GeoTemCo/js/Time/TimeDataSource.js @ 0:b57c7821382f

initial
author Dirk Wintergruen <dwinter@mpiwg-berlin.mpg.de>
date Thu, 28 May 2015 10:28:12 +0200
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:b57c7821382f
1 /*
2 * TimeDataSource.js
3 *
4 * Copyright (c) 2012, Stefan Jänicke. 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 TimeDataSource, TimeSlice, TimeStack
24 * implementation for aggregation of time items
25 * @author Stefan Jänicke (stjaenicke@informatik.uni-leipzig.de)
26 * @release 1.0
27 * @release date: 2012-07-27
28 * @version date: 2012-07-27
29 *
30 * @param {JSON} options time configuration
31 */
32 function TimeDataSource(options) {
33
34 this.options = options;
35 this.timeSlices = [];
36 this.unit
37 this.minDate
38 this.maxDate
39 this.eventSources
40 this.events
41 this.leftSlice
42 this.rightSlice
43
44 this.hashMapping
45
46 };
47
48 TimeDataSource.prototype = {
49
50 findTimeUnits : function(granularity, timeUnit, pixels) {
51
52 var time = SimileAjax.DateTime;
53 this.availableUnits = [];
54 var givenUnits = SimileAjax.DateTime.gregorianUnitLengths;
55 for (var i = 0; i < givenUnits.length; i++) {
56 if (granularity > i) {
57 continue;
58 }
59 var slices = 0;
60 var t = new Date(this.minDate.getTime());
61 do {
62 time.roundDownToInterval(t, i, undefined, 1, 0);
63 slices++;
64 time.incrementByInterval(t, i, undefined);
65 } while( t.getTime() <= this.maxDate.getTime() && slices < pixels+2 );
66 if (slices > 0 && slices <= pixels) {
67 this.availableUnits.push({
68 unit : i,
69 slices : slices,
70 label : SimileAjax.DateTime.Strings[GeoTemConfig.language][i]
71 });
72 }
73 }
74 var unitDiff200 = pixels + 1;
75 for (var i = 0; i < this.availableUnits.length; i++) {
76 var diff = Math.abs(this.availableUnits[i].slices - 200);
77 if (diff < unitDiff200) {
78 unitDiff200 = diff;
79 this.unit = this.availableUnits[i].unit;
80 }
81 }
82
83 },
84
85 getUnitIndex : function() {
86 for (var i = 0; i < this.availableUnits.length; i++) {
87 if (this.unit == this.availableUnits[i].unit) {
88 return i;
89 }
90 }
91 return 0;
92 },
93
94 setTimeUnit : function(unit) {
95 this.unit = unit;
96 this.initializeSlices();
97 },
98
99 /**
100 * initializes the TimeDataSource
101 * @param {Timeplot.ColumnSource[]} dataSources the column sources corresponding to the data sets
102 * @param {Timeplot.DefaultEventSource[]} eventSources the event sources corresponding to the column sources
103 * @param {TimeObject[][]} timeObjects an array of time objects of different sets
104 * @param {SimileAjax.DateTime} granularity the time granularity of the given data
105 */
106 initialize : function(dataSources, eventSources, timeObjects, granularity, timeUnit, pixels) {
107
108 this.dataSources = dataSources;
109 this.eventSources = eventSources;
110 this.timeObjects = timeObjects;
111
112 this.minDate = undefined;
113 this.maxDate = undefined;
114 this.hashMapping = [];
115 this.projHashMapping = [];
116
117 for (var i = 0; i < timeObjects.length; i++) {
118 this.hashMapping.push([]);
119 this.projHashMapping.push([]);
120 for (var j = 0; j < timeObjects[i].length; j++) {
121 var o = timeObjects[i][j];
122 if (o.isTemporal) {
123 var g = o.dates[this.options.timeIndex].granularity;
124 //o.getTimeGranularity(this.options.timeIndex);
125 if (g == null) {
126 continue;
127 }
128 var time = o.dates[this.options.timeIndex].date;
129 //o.getDate(this.options.timeIndex);
130 if (this.minDate == undefined || time.getTime() < this.minDate.getTime()) {
131 this.minDate = time;
132 }
133 if (this.maxDate == undefined || time.getTime() > this.maxDate.getTime()) {
134 this.maxDate = time;
135 }
136 }
137 }
138 }
139
140 if (this.minDate == undefined) {
141 this.minDate = this.options.defaultMinDate;
142 this.maxDate = this.options.defaultMaxDate;
143 }
144
145 this.findTimeUnits(granularity, timeUnit, pixels);
146 this.initializeSlices();
147
148 },
149
150 initializeSlices : function() {
151 for (var i = 0; i < this.dataSources.length; i++) {
152 this.dataSources[i]._range = {
153 earliestDate : null,
154 latestDate : null,
155 min : 0,
156 max : 0
157 };
158 }
159 this.timeSlices = [];
160 var time = SimileAjax.DateTime;
161 var t = new Date(this.minDate.getTime() - 0.9 * time.gregorianUnitLengths[this.unit]);
162 do {
163 time.roundDownToInterval(t, this.unit, undefined, 1, 0);
164 var slice = new TimeSlice(SimileAjax.NativeDateUnit.cloneValue(t), this.timeObjects.length, this.dataSources.length);
165 this.timeSlices.push(slice);
166 time.incrementByInterval(t, this.unit, undefined);
167 } while (t.getTime() <= this.maxDate.getTime() + 1.1 * time.gregorianUnitLengths[this.unit]);
168
169 for (var i = 0; i < this.timeObjects.length; i++) {
170 var projId = i;
171 if( this.dataSources.length == 1 ){
172 projId = 0;
173 }
174 for (var j = 0; j < this.timeObjects[i].length; j++) {
175 var o = this.timeObjects[i][j];
176 if (o.isTemporal) {
177 var date = o.dates[this.options.timeIndex].date;
178 //o.getDate(this.options.timeIndex);
179 for (var k = 0; k < this.timeSlices.length - 1; k++) {
180 var t1 = this.timeSlices[k].date.getTime();
181 var t2 = this.timeSlices[k + 1].date.getTime();
182 var stack = null, projStack = null;
183 if (date >= t1 && date < t2) {
184 stack = this.timeSlices[k].getStack(i);
185 projStack = this.timeSlices[k].getProjStack(projId);
186 }
187 if (k == this.timeSlices.length - 2 && date >= t2) {
188 stack = this.timeSlices[k + 1].getStack(i);
189 projStack = this.timeSlices[k + 1].getProjStack(projId);
190 }
191 if (stack != null) {
192 stack.addObject(o);
193 projStack.addObject(o);
194 this.hashMapping[i][o.index] = stack;
195 this.projHashMapping[i][o.index] = projStack;
196 break;
197 }
198 }
199 }
200 }
201 }
202
203 this.events = [];
204 for (var i = 0; i < this.eventSources.length; i++) {
205 var eventSet = [];
206 for (var j = 0; j < this.timeSlices.length; j++) {
207 var value = new Array("" + this.timeSlices[j].projStacks[i].value);
208 eventSet.push({
209 date : this.timeSlices[j].date,
210 value : value
211 });
212 }
213 this.eventSources[i].loadData(eventSet);
214 this.events.push(eventSet);
215 }
216
217 this.leftSlice = 0;
218 this.rightSlice = this.timeSlices.length - 1;
219
220 },
221
222 getSliceNumber : function() {
223 return this.timeSlices.length;
224 },
225
226 /**
227 * computes the slice index corresponding to a given time
228 * @param {Date} time the given time
229 * @return the corresponding slice index
230 */
231 getSliceIndex : function(time) {
232 for (var i = 0; i < this.timeSlices.length; i++) {
233 if (time == this.timeSlices[i].date) {
234 return i;
235 }
236 }
237 },
238
239 /**
240 * returns the time of a specific time slice
241 * @param {int} time the given slice index
242 * @return the corresponding slice date
243 */
244 getSliceTime : function(index) {
245 return this.timeSlices[index].date;
246 },
247
248 /**
249 * shifts the actual zoomed range
250 * @param {int} delta the value to shift (negative for left shift, positive for right shift)
251 * @return boolean value, if the range could be shifted
252 */
253 setShift : function(delta) {
254 if (delta == 1 && this.leftSlice != 0) {
255 this.leftSlice--;
256 this.rightSlice--;
257 return true;
258 } else if (delta == -1 && this.rightSlice != this.timeSlices.length - 1) {
259 this.leftSlice++;
260 this.rightSlice++;
261 return true;
262 } else {
263 return false;
264 }
265 },
266
267 /**
268 * zooms the actual range
269 * @param {int} delta the value to zoom (negative for zoom out, positive for zoom in)
270 * @param {Date} time the corresponding time of the actual mouse position on the plot
271 * @param {Date} leftTime the time of the left border of a selected timerange or null
272 * @param {Date} rightTime the time of the right border of a selected timerange or null
273 * @return boolean value, if the range could be zoomed
274 */
275 setZoom : function(delta, time, leftTime, rightTime) {
276 var n1 = 0;
277 var n2 = 0;
278 var m = -1;
279 if (delta > 0) {
280 m = 1;
281 if (leftTime != null) {
282 n1 = this.getSliceIndex(leftTime) - this.leftSlice;
283 n2 = this.rightSlice - this.getSliceIndex(rightTime);
284 } else {
285 slice = this.getSliceIndex(time);
286 if (slice == this.leftSlice || slice == this.rightSlice) {
287 return;
288 }
289 n1 = slice - 1 - this.leftSlice;
290 n2 = this.rightSlice - slice - 1;
291 }
292 } else if (delta < 0) {
293
294 n1 = this.leftSlice;
295 n2 = this.timeSlices.length - 1 - this.rightSlice;
296 }
297
298 var zoomSlices = 2 * delta;
299 if (Math.abs(n1 + n2) < Math.abs(zoomSlices)) {
300 zoomSlices = n1 + n2;
301 }
302
303 if (n1 + n2 == 0) {
304 return false;
305 }
306
307 var m1 = Math.round(n1 / (n1 + n2) * zoomSlices);
308 var m2 = zoomSlices - m1;
309
310 this.leftSlice += m1;
311 this.rightSlice -= m2;
312
313 return true;
314 },
315
316 /**
317 * resets the plots by loading data of actual zoomed range
318 */
319 reset : function(timeGeometry) {
320 for (var i = 0; i < this.eventSources.length; i++) {
321 this.eventSources[i].loadData(this.events[i].slice(this.leftSlice, this.rightSlice + 1));
322 if (i + 1 < this.eventSources.length) {
323 timeGeometry._earliestDate = null;
324 timeGeometry._latestDate = null;
325 }
326
327 }
328 },
329
330 /**
331 * Getter for actual zoom
332 * @return actual zoom value
333 */
334 getZoom : function() {
335 if (this.timeSlices == undefined) {
336 return 0;
337 }
338 return Math.round((this.timeSlices.length - 3) / 2) - Math.round((this.rightSlice - this.leftSlice - 2) / 2);
339 },
340
341 /**
342 * Getter for date of the first timeslice
343 * @return date of the first timeslice
344 */
345 earliest : function() {
346 return this.timeSlices[0].date;
347 },
348
349 /**
350 * Getter for date of the last timeslice
351 * @return date of the last timeslice
352 */
353 latest : function() {
354 return this.timeSlices[this.timeSlices.length - 1].date;
355 },
356
357 setOverlay : function(timeObjects) {
358 for (var i = 0; i < this.timeSlices.length; i++) {
359 this.timeSlices[i].reset();
360 }
361 for (var j in timeObjects ) {
362 for (var k in timeObjects[j] ) {
363 var o = timeObjects[j][k];
364 if (o.isTemporal) {
365 if (o.getTimeGranularity(this.options.timeIndex) == null) {
366 continue;
367 }
368 this.hashMapping[j][o.index].overlay += o.weight;
369 this.projHashMapping[j][o.index].overlay += o.weight;
370 }
371 }
372 }
373 },
374
375 size : function() {
376 if (this.timeSlices.length == 0) {
377 return 0;
378 }
379 return this.timeSlices[0].stacks.length;
380 }
381 };
382
383 /**
384 * small class that represents a time slice of the actual timeplot.
385 * it has a specific date and contains its corrsponding data objects as well
386 */
387 function TimeSlice(date, rows, projRows) {
388
389 this.date = date;
390 this.selected = false;
391
392 this.stacks = [];
393 this.projStacks = [];
394 for (var i = 0; i < rows; i++) {
395 this.stacks.push(new TimeStack());
396 }
397 for (var i = 0; i < projRows; i++) {
398 this.projStacks.push(new TimeStack());
399 }
400
401 this.getStack = function(row) {
402 return this.stacks[row];
403 };
404
405 this.getProjStack = function(row) {
406 return this.projStacks[row];
407 };
408
409 this.reset = function() {
410 for (var i in this.projStacks ) {
411 this.stacks[i].overlay = 0;
412 this.projStacks[i].overlay = 0;
413 }
414 };
415
416 this.overlay = function() {
417 var value = 0;
418 for (var i in this.projStacks ) {
419 if (this.projStacks[i].overlay > value) {
420 value = this.projStacks[i].overlay;
421 }
422 }
423 return value;
424 };
425
426 };
427
428 /**
429 * small class that represents a stack for a time slice which
430 * holds items for different datasets for the specific time range
431 */
432 function TimeStack() {
433
434 this.overlay = 0;
435 this.value = 0;
436 this.elements = [];
437
438 this.addObject = function(object) {
439 this.elements.push(object);
440 this.value += object.weight;
441 };
442
443 };