Mercurial > dive4elements > gnv-client
comparison gnv-artifacts/src/main/java/de/intevation/gnv/chart/TimeSeriesChart.java @ 540:80630520e25a
merged gnv-artifacts/0.4
author | Thomas Arendsen Hein <thomas@intevation.de> |
---|---|
date | Fri, 28 Sep 2012 12:13:49 +0200 |
parents | 4cbcc130cffc |
children | 83f8f98a4f11 |
comparison
equal
deleted
inserted
replaced
415:9f4a0b990d27 | 540:80630520e25a |
---|---|
1 package de.intevation.gnv.chart; | |
2 | |
3 import de.intevation.gnv.artifacts.ressource.RessourceFactory; | |
4 | |
5 import de.intevation.gnv.geobackend.base.Result; | |
6 | |
7 import de.intevation.gnv.state.describedata.KeyValueDescibeData; | |
8 | |
9 import de.intevation.gnv.timeseries.gap.TimeGap; | |
10 | |
11 import java.text.DateFormat; | |
12 import java.text.SimpleDateFormat; | |
13 | |
14 import java.util.Collection; | |
15 import java.util.Date; | |
16 import java.util.HashMap; | |
17 import java.util.Iterator; | |
18 import java.util.Locale; | |
19 import java.util.TimeZone; | |
20 | |
21 import org.apache.log4j.Logger; | |
22 | |
23 import org.jfree.chart.ChartFactory; | |
24 import org.jfree.chart.ChartTheme; | |
25 | |
26 import org.jfree.chart.axis.Axis; | |
27 import org.jfree.chart.axis.DateAxis; | |
28 import org.jfree.chart.axis.DateTickUnit; | |
29 import org.jfree.chart.axis.DateTickUnitType; | |
30 import org.jfree.chart.axis.TickUnits; | |
31 import org.jfree.chart.axis.TickUnitSource; | |
32 import org.jfree.chart.axis.ValueAxis; | |
33 | |
34 import org.jfree.chart.plot.PlotOrientation; | |
35 import org.jfree.chart.plot.XYPlot; | |
36 | |
37 import org.jfree.data.general.Series; | |
38 | |
39 import org.jfree.data.time.Minute; | |
40 import org.jfree.data.time.TimeSeries; | |
41 import org.jfree.data.time.TimeSeriesCollection; | |
42 | |
43 /** | |
44 * @author Ingo Weinzierl (ingo.weinzierl@intevation.de) | |
45 */ | |
46 public class TimeSeriesChart | |
47 extends AbstractXYLineChart | |
48 { | |
49 | |
50 private static final String DATE_FORMAT = "chart.timeseries.date.format"; | |
51 | |
52 public static final String DEFAULT_DATE_FORMAT = "dd-MMM-yyyy"; | |
53 | |
54 private static final long NO_TIME_GAP = Long.MAX_VALUE - 1000; | |
55 | |
56 private static Logger log = Logger.getLogger(TimeSeriesChart.class); | |
57 | |
58 | |
59 public TimeSeriesChart( | |
60 ChartLabels labels, | |
61 ChartTheme theme, | |
62 Collection parameters, | |
63 Collection measurements, | |
64 Collection dates, | |
65 Collection result, | |
66 Collection timeGaps, | |
67 Locale locale, | |
68 boolean linesVisible, | |
69 boolean shapesVisible | |
70 ) { | |
71 this.labels = labels; | |
72 this.theme = theme; | |
73 this.parameters = parameters; | |
74 this.measurements = measurements; | |
75 this.dates = dates; | |
76 this.resultSet = result; | |
77 this.timeGaps = timeGaps; | |
78 this.locale = locale; | |
79 this.PLOT_ORIENTATION = PlotOrientation.VERTICAL; | |
80 this.linesVisible = linesVisible; | |
81 this.shapesVisible = shapesVisible; | |
82 this.datasets = new HashMap(); | |
83 this.ranges = new HashMap(); | |
84 } | |
85 | |
86 | |
87 protected void initChart() { | |
88 chart = ChartFactory.createTimeSeriesChart( | |
89 labels.getTitle(), | |
90 labels.getDomainAxisLabel(), | |
91 null, | |
92 null, | |
93 true, | |
94 false, | |
95 false | |
96 ); | |
97 | |
98 XYPlot plot = (XYPlot) chart.getPlot(); | |
99 plot.setDomainAxis(0, new DateAxis( | |
100 labels.getDomainAxisLabel(), TimeZone.getDefault(), locale)); | |
101 } | |
102 | |
103 | |
104 protected void initData() { | |
105 log.debug("init data for timeseries chart"); | |
106 | |
107 String breakPoint1 = null; | |
108 String breakPoint2 = null; | |
109 String breakPoint3 = null; | |
110 | |
111 Iterator iter = resultSet.iterator(); | |
112 Result row = null; | |
113 String seriesName = null; | |
114 String parameter = null; | |
115 TimeSeries series = null; | |
116 | |
117 int idx = 0; | |
118 int startPos = 0; | |
119 int endPos = 0; | |
120 Date startDate = null; | |
121 Date endDate = null; | |
122 | |
123 Result[] results = | |
124 (Result[]) resultSet.toArray(new Result[resultSet.size()]); | |
125 | |
126 while (iter.hasNext()) { | |
127 row = (Result) iter.next(); | |
128 | |
129 // add current data to plot and prepare for next one | |
130 if (!row.getString("GROUP1").equals(breakPoint1) || | |
131 !row.getString("GROUP2").equals(breakPoint2) || | |
132 !row.getString("GROUP3").equals(breakPoint3) | |
133 ) { | |
134 log.debug("prepare data/plot for next dataset"); | |
135 | |
136 if(series != null) { | |
137 // add gaps before adding series to chart | |
138 startDate = results[startPos].getDate("XORDINATE"); | |
139 endDate = results[endPos-1].getDate("XORDINATE"); | |
140 addGaps(results,series,startDate,endDate,startPos,endPos); | |
141 addSeries(series, parameter, idx); | |
142 | |
143 startPos = endPos + 1; | |
144 } | |
145 | |
146 // prepare variables for next plot | |
147 breakPoint1 = row.getString("GROUP1"); | |
148 breakPoint2 = row.getString("GROUP2"); | |
149 breakPoint3 = row.getString("GROUP3"); | |
150 | |
151 seriesName = createSeriesName( | |
152 breakPoint1, | |
153 breakPoint2, | |
154 breakPoint3 | |
155 ); | |
156 parameter = findParameter(seriesName); | |
157 | |
158 log.debug("next dataset is '" + seriesName + "'"); | |
159 series = new TimeSeries(seriesName, Minute.class); | |
160 } | |
161 | |
162 addValue(row, series); | |
163 storeMaxRange(row.getDouble("YORDINATE"), parameter); | |
164 endPos++; | |
165 } | |
166 | |
167 if (startPos < results.length && endPos-1 < results.length) { | |
168 // add the last dataset if existing to plot and prepare its axis | |
169 startDate = results[startPos].getDate("XORDINATE"); | |
170 endDate = results[endPos-1].getDate("XORDINATE"); | |
171 addGaps(results, series, startDate, endDate, startPos, endPos); | |
172 addSeries(series, parameter, idx); | |
173 } | |
174 | |
175 addDatasets(); | |
176 } | |
177 | |
178 | |
179 protected void addValue(Result row, Series series) { | |
180 ((TimeSeries) series).addOrUpdate( | |
181 new Minute(row.getDate("XORDINATE")), | |
182 row.getDouble("YORDINATE") | |
183 ); | |
184 } | |
185 | |
186 | |
187 protected void addSeries(Series series, String parameter, int idx) { | |
188 log.debug("add series (" + parameter + ")to timeseries chart"); | |
189 | |
190 if (series == null) { | |
191 log.warn("no data to add"); | |
192 return; | |
193 } | |
194 | |
195 TimeSeriesCollection tsc = null; | |
196 | |
197 if (datasets.containsKey(parameter)) | |
198 tsc = (TimeSeriesCollection) datasets.get(parameter); | |
199 else | |
200 tsc = new TimeSeriesCollection(); | |
201 | |
202 tsc.addSeries((TimeSeries) series); | |
203 datasets.put(parameter, tsc); | |
204 } | |
205 | |
206 | |
207 protected void addDatasets() { | |
208 Iterator iter = parameters.iterator(); | |
209 XYPlot plot = chart.getXYPlot(); | |
210 int idx = 0; | |
211 | |
212 TimeSeriesCollection tsc = null; | |
213 KeyValueDescibeData data = null; | |
214 String key = null; | |
215 while (iter.hasNext()) { | |
216 data = (KeyValueDescibeData) iter.next(); | |
217 key = data.getValue(); | |
218 | |
219 if (datasets.containsKey(key)) { | |
220 tsc = (TimeSeriesCollection)datasets.get(key); | |
221 plot.setDataset(idx, tsc ); | |
222 log.debug("Added " + key + " parameter to plot."); | |
223 prepareAxis(key, idx); | |
224 adjustRenderer( | |
225 idx++, | |
226 tsc.getSeriesCount(), | |
227 linesVisible, | |
228 shapesVisible | |
229 ); | |
230 } | |
231 } | |
232 } | |
233 | |
234 | |
235 protected void localizeDomainAxis(Axis axis, Locale locale) { | |
236 ((ValueAxis)axis).setStandardTickUnits(createStandardDateTickUnits( | |
237 TimeZone.getDefault(), | |
238 locale)); | |
239 } | |
240 | |
241 | |
242 public static TickUnitSource createStandardDateTickUnits( | |
243 TimeZone zone, | |
244 Locale locale) | |
245 { | |
246 /* | |
247 * This method have been copied from JFreeChart's DateAxis class. | |
248 * DateFormat objects are hard coded in DateAxis and cannot be adjusted. | |
249 */ | |
250 if (zone == null) { | |
251 throw new IllegalArgumentException("Null 'zone' argument."); | |
252 } | |
253 if (locale == null) { | |
254 throw new IllegalArgumentException("Null 'locale' argument."); | |
255 } | |
256 TickUnits units = new TickUnits(); | |
257 | |
258 // date formatters | |
259 DateFormat f1 = new SimpleDateFormat("HH:mm:ss.SSS", locale); | |
260 DateFormat f2 = new SimpleDateFormat("HH:mm:ss", locale); | |
261 DateFormat f3 = new SimpleDateFormat("HH:mm", locale); | |
262 DateFormat f4 = new SimpleDateFormat("d-MMM, HH:mm", locale); | |
263 DateFormat f5 = new SimpleDateFormat("d-MMM yyyy", locale); | |
264 DateFormat f6 = new SimpleDateFormat("MMM-yyyy", locale); | |
265 DateFormat f7 = new SimpleDateFormat("yyyy", locale); | |
266 | |
267 f1.setTimeZone(zone); | |
268 f2.setTimeZone(zone); | |
269 f3.setTimeZone(zone); | |
270 f4.setTimeZone(zone); | |
271 f5.setTimeZone(zone); | |
272 f6.setTimeZone(zone); | |
273 f7.setTimeZone(zone); | |
274 | |
275 // milliseconds | |
276 units.add(new DateTickUnit(DateTickUnitType.MILLISECOND, 1, f1)); | |
277 units.add(new DateTickUnit(DateTickUnitType.MILLISECOND, 5, | |
278 DateTickUnitType.MILLISECOND, 1, f1)); | |
279 units.add(new DateTickUnit(DateTickUnitType.MILLISECOND, 10, | |
280 DateTickUnitType.MILLISECOND, 1, f1)); | |
281 units.add(new DateTickUnit(DateTickUnitType.MILLISECOND, 25, | |
282 DateTickUnitType.MILLISECOND, 5, f1)); | |
283 units.add(new DateTickUnit(DateTickUnitType.MILLISECOND, 50, | |
284 DateTickUnitType.MILLISECOND, 10, f1)); | |
285 units.add(new DateTickUnit(DateTickUnitType.MILLISECOND, 100, | |
286 DateTickUnitType.MILLISECOND, 10, f1)); | |
287 units.add(new DateTickUnit(DateTickUnitType.MILLISECOND, 250, | |
288 DateTickUnitType.MILLISECOND, 10, f1)); | |
289 units.add(new DateTickUnit(DateTickUnitType.MILLISECOND, 500, | |
290 DateTickUnitType.MILLISECOND, 50, f1)); | |
291 | |
292 // seconds | |
293 units.add(new DateTickUnit(DateTickUnitType.SECOND, 1, | |
294 DateTickUnitType.MILLISECOND, 50, f2)); | |
295 units.add(new DateTickUnit(DateTickUnitType.SECOND, 5, | |
296 DateTickUnitType.SECOND, 1, f2)); | |
297 units.add(new DateTickUnit(DateTickUnitType.SECOND, 10, | |
298 DateTickUnitType.SECOND, 1, f2)); | |
299 units.add(new DateTickUnit(DateTickUnitType.SECOND, 30, | |
300 DateTickUnitType.SECOND, 5, f2)); | |
301 | |
302 // minutes | |
303 units.add(new DateTickUnit(DateTickUnitType.MINUTE, 1, | |
304 DateTickUnitType.SECOND, 5, f3)); | |
305 units.add(new DateTickUnit(DateTickUnitType.MINUTE, 2, | |
306 DateTickUnitType.SECOND, 10, f3)); | |
307 units.add(new DateTickUnit(DateTickUnitType.MINUTE, 5, | |
308 DateTickUnitType.MINUTE, 1, f3)); | |
309 units.add(new DateTickUnit(DateTickUnitType.MINUTE, 10, | |
310 DateTickUnitType.MINUTE, 1, f3)); | |
311 units.add(new DateTickUnit(DateTickUnitType.MINUTE, 15, | |
312 DateTickUnitType.MINUTE, 5, f3)); | |
313 units.add(new DateTickUnit(DateTickUnitType.MINUTE, 20, | |
314 DateTickUnitType.MINUTE, 5, f3)); | |
315 units.add(new DateTickUnit(DateTickUnitType.MINUTE, 30, | |
316 DateTickUnitType.MINUTE, 5, f3)); | |
317 | |
318 // hours | |
319 units.add(new DateTickUnit(DateTickUnitType.HOUR, 1, | |
320 DateTickUnitType.MINUTE, 5, f3)); | |
321 units.add(new DateTickUnit(DateTickUnitType.HOUR, 2, | |
322 DateTickUnitType.MINUTE, 10, f3)); | |
323 units.add(new DateTickUnit(DateTickUnitType.HOUR, 4, | |
324 DateTickUnitType.MINUTE, 30, f3)); | |
325 units.add(new DateTickUnit(DateTickUnitType.HOUR, 6, | |
326 DateTickUnitType.HOUR, 1, f3)); | |
327 units.add(new DateTickUnit(DateTickUnitType.HOUR, 12, | |
328 DateTickUnitType.HOUR, 1, f4)); | |
329 | |
330 // days | |
331 units.add(new DateTickUnit(DateTickUnitType.DAY, 1, | |
332 DateTickUnitType.HOUR, 1, f5)); | |
333 units.add(new DateTickUnit(DateTickUnitType.DAY, 2, | |
334 DateTickUnitType.HOUR, 1, f5)); | |
335 units.add(new DateTickUnit(DateTickUnitType.DAY, 7, | |
336 DateTickUnitType.DAY, 1, f5)); | |
337 units.add(new DateTickUnit(DateTickUnitType.DAY, 15, | |
338 DateTickUnitType.DAY, 1, f5)); | |
339 | |
340 // months | |
341 units.add(new DateTickUnit(DateTickUnitType.MONTH, 1, | |
342 DateTickUnitType.DAY, 1, f6)); | |
343 units.add(new DateTickUnit(DateTickUnitType.MONTH, 2, | |
344 DateTickUnitType.DAY, 1, f6)); | |
345 units.add(new DateTickUnit(DateTickUnitType.MONTH, 3, | |
346 DateTickUnitType.MONTH, 1, f6)); | |
347 units.add(new DateTickUnit(DateTickUnitType.MONTH, 4, | |
348 DateTickUnitType.MONTH, 1, f6)); | |
349 units.add(new DateTickUnit(DateTickUnitType.MONTH, 6, | |
350 DateTickUnitType.MONTH, 1, f6)); | |
351 | |
352 // years | |
353 units.add(new DateTickUnit(DateTickUnitType.YEAR, 1, | |
354 DateTickUnitType.MONTH, 1, f7)); | |
355 units.add(new DateTickUnit(DateTickUnitType.YEAR, 2, | |
356 DateTickUnitType.MONTH, 3, f7)); | |
357 units.add(new DateTickUnit(DateTickUnitType.YEAR, 5, | |
358 DateTickUnitType.YEAR, 1, f7)); | |
359 units.add(new DateTickUnit(DateTickUnitType.YEAR, 10, | |
360 DateTickUnitType.YEAR, 1, f7)); | |
361 units.add(new DateTickUnit(DateTickUnitType.YEAR, 25, | |
362 DateTickUnitType.YEAR, 5, f7)); | |
363 units.add(new DateTickUnit(DateTickUnitType.YEAR, 50, | |
364 DateTickUnitType.YEAR, 10, f7)); | |
365 units.add(new DateTickUnit(DateTickUnitType.YEAR, 100, | |
366 DateTickUnitType.YEAR, 20, f7)); | |
367 | |
368 return units; | |
369 } | |
370 | |
371 | |
372 protected String getMessage(Locale locale, String key, String def) { | |
373 return RessourceFactory.getInstance().getRessource(locale, key, def); | |
374 } | |
375 | |
376 | |
377 protected String createSeriesName( | |
378 String breakPoint1, | |
379 String breakPoint2, | |
380 String breakPoint3 | |
381 ) { | |
382 log.debug("create seriesname of timeseries chart"); | |
383 return findValueTitle(parameters, breakPoint1) + | |
384 " " + | |
385 findValueTitle(measurements, breakPoint2) + | |
386 "m"; | |
387 } | |
388 | |
389 | |
390 protected void addGaps( | |
391 Result[] results, | |
392 Series series, | |
393 Date startDate, | |
394 Date endDate, | |
395 int startPos, | |
396 int endPos | |
397 ) { | |
398 int gapID = results[startPos].getInteger("GAPID"); | |
399 long maxDiff = calculateGapSize( | |
400 startDate, endDate, startPos, endPos, gapID | |
401 ); | |
402 | |
403 Date last = startDate; | |
404 for (int i = startPos+1; i < endPos; i++) { | |
405 Result res = results[i]; | |
406 Date now = res.getDate("XORDINATE"); | |
407 | |
408 if ((now.getTime() - last.getTime()) > maxDiff) { | |
409 // add gap, add 1 minute to last date and add null value | |
410 log.info( | |
411 "Gap between " + | |
412 last.toString() + " and " + now.toString() | |
413 ); | |
414 last.setTime(last.getTime() + 60000); | |
415 ((TimeSeries) series).addOrUpdate(new Minute(last), null); | |
416 } | |
417 | |
418 last = now; | |
419 } | |
420 } | |
421 | |
422 | |
423 protected long calculateGapSize( | |
424 Date start, | |
425 Date end, | |
426 int startPos, | |
427 int endPos, | |
428 int gapID | |
429 ){ | |
430 long maxGap = (end.getTime() - start.getTime()) / 20; | |
431 long interval = getTimeGapValue(start, end, startPos, endPos, gapID); | |
432 | |
433 if (maxGap < interval) | |
434 maxGap = interval + 10; | |
435 | |
436 return maxGap; | |
437 } | |
438 | |
439 | |
440 protected long getTimeGapValue( | |
441 Date dStart, | |
442 Date dEnd, | |
443 int pStart, | |
444 int pEnd, | |
445 int gapID | |
446 ){ | |
447 long gap = 0; | |
448 | |
449 if (gapID < 0 || gapID >= 99) { | |
450 | |
451 if (gapID == -1) { | |
452 // no gaps in meshes | |
453 gap = NO_TIME_GAP; | |
454 } | |
455 else if (pEnd-pStart < 60) { | |
456 gap = (3/(pEnd-pStart)) * (dEnd.getTime() - dStart.getTime()); | |
457 } | |
458 } | |
459 else{ | |
460 Iterator it = timeGaps.iterator(); | |
461 | |
462 while (it.hasNext()) { | |
463 TimeGap tempTimeGap = (TimeGap) it.next(); | |
464 | |
465 if (tempTimeGap.getKey() == gapID){ | |
466 String unit = tempTimeGap.getUnit(); | |
467 int gapValue = tempTimeGap.getValue(); | |
468 | |
469 if (unit.equals(TimeGap.TIME_UNIT_MINUTE)) { | |
470 gap = gapValue * TimeGap.MINUTE_IN_MILLIS; | |
471 } | |
472 else if (unit.equals(TimeGap.TIME_UNIT_HOUR)) { | |
473 gap = gapValue * TimeGap.HOUR_IN_MILLIS; | |
474 } | |
475 else if (unit.equals(TimeGap.TIME_UNIT_DAY)) { | |
476 gap = gapValue * TimeGap.DAY_IN_MILLIS; | |
477 } | |
478 else if (unit.equals(TimeGap.TIME_UNIT_WEEK)) { | |
479 gap = gapValue * TimeGap.WEEK_IN_MILLIS; | |
480 } | |
481 else if (unit.equals(TimeGap.TIME_UNIT_MONTH)) { | |
482 gap = gapValue * (TimeGap.DAY_IN_MILLIS *30); | |
483 } | |
484 else if (unit.equals(TimeGap.TIME_UNIT_YEAR)) { | |
485 gap = gapValue * (TimeGap.DAY_IN_MILLIS *365); | |
486 } | |
487 break; | |
488 } | |
489 } | |
490 } | |
491 | |
492 return gap; | |
493 } | |
494 } | |
495 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 : |