comparison gnv-artifacts/src/main/java/de/intevation/gnv/chart/TimeSeriesChart.java @ 657:af3f56758f59

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

http://dive4elements.wald.intevation.org