Mercurial > hg > mpiwg_geobrowser
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 }; |