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 :

http://dive4elements.wald.intevation.org