ingo@298: package de.intevation.gnv.chart; ingo@298: ingo@315: import java.text.DateFormat; ingo@315: import java.text.SimpleDateFormat; ingo@298: import java.util.Collection; ingo@329: import java.util.Date; ingo@334: import java.util.HashMap; ingo@298: import java.util.Iterator; ingo@298: import java.util.Locale; ingo@298: ingo@298: import org.apache.log4j.Logger; ingo@298: ingo@298: import org.jfree.chart.ChartTheme; ingo@312: import org.jfree.chart.ChartFactory; ingo@315: import org.jfree.chart.axis.Axis; ingo@315: import org.jfree.chart.axis.DateAxis; ingo@298: import org.jfree.chart.plot.XYPlot; ingo@298: import org.jfree.chart.plot.PlotOrientation; ingo@298: import org.jfree.data.general.Series; ingo@298: import org.jfree.data.time.TimeSeries; ingo@298: import org.jfree.data.time.Minute; ingo@298: import org.jfree.data.time.TimeSeriesCollection; ingo@298: ingo@350: import de.intevation.gnv.artifacts.ressource.RessourceFactory; ingo@298: import de.intevation.gnv.geobackend.base.Result; tim@335: import de.intevation.gnv.state.describedata.KeyValueDescibeData; ingo@329: import de.intevation.gnv.timeseries.gap.TimeGap; ingo@298: ingo@298: ingo@298: /** ingo@298: * @author Ingo Weinzierl ingo@298: */ ingo@298: public class TimeSeriesChart ingo@298: extends AbstractXYLineChart ingo@298: { ingo@298: ingo@350: private static final String DATE_FORMAT = "chart.timeseries.date.format"; ingo@350: ingo@350: public static final String DEFAULT_DATE_FORMAT = "dd-MMM-yyyy"; ingo@315: ingo@329: private static final long NO_TIME_GAP = Long.MAX_VALUE - 1000; ingo@329: ingo@298: private static Logger log = Logger.getLogger(TimeSeriesChart.class); ingo@298: ingo@298: ingo@298: public TimeSeriesChart( ingo@298: ChartLabels labels, ingo@298: ChartTheme theme, ingo@298: Collection parameters, ingo@298: Collection measurements, ingo@310: Collection dates, ingo@298: Collection result, ingo@310: Collection timeGaps, ingo@327: Locale locale, ingo@327: boolean linesVisible, ingo@327: boolean shapesVisible ingo@298: ) { ingo@298: this.labels = labels; ingo@298: this.theme = theme; ingo@298: this.parameters = parameters; ingo@298: this.measurements = measurements; ingo@310: this.dates = dates; ingo@298: this.resultSet = result; ingo@310: this.timeGaps = timeGaps; ingo@298: this.locale = locale; ingo@298: this.PLOT_ORIENTATION = PlotOrientation.VERTICAL; ingo@327: this.linesVisible = linesVisible; ingo@327: this.shapesVisible = shapesVisible; ingo@334: this.datasets = new HashMap(); ingo@298: } ingo@298: ingo@298: ingo@333: protected void initChart() { ingo@312: chart = ChartFactory.createTimeSeriesChart( ingo@312: labels.getTitle(), ingo@312: labels.getDomainAxisLabel(), ingo@312: null, ingo@312: null, ingo@312: true, ingo@312: false, ingo@312: false ingo@312: ); ingo@312: } ingo@312: ingo@312: ingo@298: protected void initData() { ingo@298: log.debug("init data for timeseries chart"); ingo@298: ingo@298: String breakPoint1 = null; ingo@298: String breakPoint2 = null; ingo@298: String breakPoint3 = null; ingo@298: ingo@298: Iterator iter = resultSet.iterator(); ingo@298: Result row = null; ingo@298: String seriesName = null; ingo@298: TimeSeries series = null; ingo@298: ingo@329: int idx = 0; ingo@329: int startPos = 0; ingo@329: int endPos = 0; ingo@329: Date startDate = null; ingo@329: Date endDate = null; ingo@329: ingo@329: Result[] results = ingo@329: (Result[]) resultSet.toArray(new Result[resultSet.size()]); ingo@298: ingo@298: while (iter.hasNext()) { ingo@298: row = (Result) iter.next(); ingo@298: ingo@298: // add current data to plot and prepare for next one ingo@298: if (!row.getString("GROUP1").equals(breakPoint1) || ingo@298: !row.getString("GROUP2").equals(breakPoint2) || ingo@298: !row.getString("GROUP3").equals(breakPoint3) ingo@298: ) { ingo@298: log.debug("prepare data/plot for next dataset"); ingo@298: ingo@298: if(series != null) { ingo@329: // add gaps before adding series to chart ingo@329: startDate = results[startPos].getDate("XORDINATE"); ingo@329: endDate = results[endPos-1].getDate("XORDINATE"); ingo@329: addGaps(results,series,startDate,endDate,startPos,endPos); ingo@334: addSeries(series, seriesName, idx); ingo@329: ingo@329: startPos = endPos + 1; ingo@298: } ingo@298: ingo@298: // prepare variables for next plot ingo@298: breakPoint1 = row.getString("GROUP1"); ingo@298: breakPoint2 = row.getString("GROUP2"); ingo@298: breakPoint3 = row.getString("GROUP3"); ingo@298: ingo@298: seriesName = createSeriesName( ingo@298: breakPoint1, ingo@298: breakPoint2, ingo@298: breakPoint3 ingo@298: ); ingo@298: ingo@298: log.debug("next dataset is '" + seriesName + "'"); ingo@298: series = new TimeSeries(seriesName, Minute.class); ingo@298: } ingo@298: ingo@298: addValue(row, series); ingo@329: endPos++; ingo@298: } ingo@298: ingo@298: // add the last dataset if existing to plot and prepare its axis ingo@329: startDate = results[startPos].getDate("XORDINATE"); ingo@329: endDate = results[endPos-1].getDate("XORDINATE"); ingo@329: addGaps(results, series, startDate, endDate, startPos, endPos); ingo@334: addSeries(series, seriesName, idx); ingo@310: ingo@334: addDatasets(); ingo@298: } ingo@298: ingo@298: ingo@298: protected void addValue(Result row, Series series) { ingo@298: ((TimeSeries) series).addOrUpdate( ingo@298: new Minute(row.getDate("XORDINATE")), ingo@298: row.getDouble("YORDINATE") ingo@298: ); ingo@298: } ingo@298: ingo@298: ingo@334: protected void addSeries(Series series, String label, int idx) { ingo@334: log.debug("add series (" + label + ")to timeseries chart"); ingo@298: ingo@298: if (series == null) { ingo@298: log.warn("no data to add"); ingo@298: return; ingo@298: } ingo@298: ingo@334: TimeSeriesCollection tsc = null; ingo@334: ingo@334: String parameter = findParameter(label); ingo@334: if (datasets.containsKey(parameter)) ingo@334: tsc = (TimeSeriesCollection) datasets.get(parameter); ingo@334: else ingo@334: tsc = new TimeSeriesCollection(); ingo@334: ingo@334: tsc.addSeries((TimeSeries) series); ingo@334: datasets.put(parameter, tsc); ingo@334: } ingo@334: ingo@334: ingo@334: protected void addDatasets() { ingo@334: Iterator iter = parameters.iterator(); ingo@334: XYPlot plot = chart.getXYPlot(); ingo@334: int idx = 0; ingo@334: ingo@334: TimeSeriesCollection tsc = null; ingo@334: KeyValueDescibeData data = null; ingo@334: String key = null; ingo@334: while (iter.hasNext()) { ingo@334: data = (KeyValueDescibeData) iter.next(); ingo@334: key = data.getValue(); ingo@334: ingo@334: if (datasets.containsKey(key)) { ingo@334: tsc = (TimeSeriesCollection)datasets.get(key); ingo@334: plot.setDataset(idx, tsc ); ingo@334: log.debug("Added " + key + " parameter to plot."); ingo@334: prepareAxis(key, idx); ingo@334: adjustRenderer( ingo@334: idx++, ingo@334: tsc.getSeriesCount(), ingo@334: linesVisible, ingo@334: shapesVisible ingo@334: ); ingo@334: } ingo@334: } ingo@298: } ingo@298: ingo@298: ingo@315: protected void localizeDomainAxis(Axis axis, Locale locale) { ingo@315: if (locale == null) ingo@315: return; ingo@315: ingo@315: log.debug( ingo@315: "Set language of axis [" + axis.getLabel() + "] " + ingo@315: "to " + locale.toString() ingo@315: ); ingo@315: ingo@350: String dateFormat = getMessage(locale, DATE_FORMAT,DEFAULT_DATE_FORMAT); ingo@350: DateFormat format = new SimpleDateFormat(dateFormat, locale); ingo@315: ((DateAxis) axis).setDateFormatOverride(format); ingo@315: } ingo@315: ingo@315: ingo@350: protected String getMessage(Locale locale, String key, String def) { ingo@350: return RessourceFactory.getInstance().getRessource(locale, key, def); ingo@350: } ingo@350: ingo@350: ingo@298: protected String createSeriesName( ingo@298: String breakPoint1, ingo@298: String breakPoint2, ingo@298: String breakPoint3 ingo@298: ) { ingo@298: log.debug("create seriesname of timeseries chart"); ingo@298: return findValueTitle(parameters, breakPoint1) + ingo@298: " " + ingo@298: findValueTitle(measurements, breakPoint2) + ingo@298: "m"; ingo@298: } ingo@329: ingo@329: ingo@329: protected void addGaps( ingo@329: Result[] results, ingo@329: Series series, ingo@329: Date startDate, ingo@329: Date endDate, ingo@329: int startPos, ingo@329: int endPos ingo@329: ) { ingo@329: int gapID = results[startPos].getInteger("GAPID"); ingo@329: long maxDiff = calculateGapSize( ingo@329: startDate, endDate, startPos, endPos, gapID ingo@329: ); ingo@329: ingo@329: Date last = startDate; ingo@329: for (int i = startPos+1; i < endPos; i++) { ingo@329: Result res = results[i]; ingo@329: Date now = res.getDate("XORDINATE"); ingo@329: ingo@329: if ((now.getTime() - last.getTime()) > maxDiff) { ingo@329: // add gap, add 1 minute to last date and add null value ingo@329: log.info( ingo@329: "Gap between " + ingo@329: last.toString() + " and " + now.toString() ingo@329: ); ingo@329: last.setTime(last.getTime() + 60000); ingo@329: ((TimeSeries) series).addOrUpdate(new Minute(last), null); ingo@329: } ingo@329: ingo@329: last = now; ingo@329: } ingo@329: } ingo@329: ingo@329: ingo@329: protected long calculateGapSize( ingo@329: Date start, ingo@329: Date end, ingo@329: int startPos, ingo@329: int endPos, ingo@329: int gapID ingo@329: ){ ingo@329: long maxGap = (end.getTime() - start.getTime()) / 20; ingo@329: long interval = getTimeGapValue(start, end, startPos, endPos, gapID); ingo@329: ingo@329: if (maxGap < interval) ingo@329: maxGap = interval + 10; ingo@329: ingo@329: return maxGap; ingo@329: } ingo@329: ingo@329: ingo@329: protected long getTimeGapValue( ingo@329: Date dStart, ingo@329: Date dEnd, ingo@329: int pStart, ingo@329: int pEnd, ingo@329: int gapID ingo@329: ){ ingo@329: long gap = 0; ingo@329: ingo@329: if (gapID < 0 || gapID >= 99) { ingo@329: ingo@329: if (gapID == -1) { ingo@329: // no gaps in meshes ingo@329: gap = NO_TIME_GAP; ingo@329: } ingo@329: else if (pEnd-pStart < 60) { ingo@329: gap = (3/(pEnd-pStart)) * (dEnd.getTime() - dStart.getTime()); ingo@329: } ingo@329: } ingo@329: else{ ingo@329: Iterator it = timeGaps.iterator(); ingo@329: ingo@329: while (it.hasNext()) { ingo@329: TimeGap tempTimeGap = (TimeGap) it.next(); ingo@329: ingo@329: if (tempTimeGap.getKey() == gapID){ ingo@329: String unit = tempTimeGap.getUnit(); ingo@329: int gapValue = tempTimeGap.getValue(); ingo@329: ingo@329: if (unit.equals(TimeGap.TIME_UNIT_MINUTE)) { ingo@329: gap = gapValue * TimeGap.MINUTE_IN_MILLIS; ingo@329: } ingo@329: else if (unit.equals(TimeGap.TIME_UNIT_HOUR)) { ingo@329: gap = gapValue * TimeGap.HOUR_IN_MILLIS; ingo@329: } ingo@329: else if (unit.equals(TimeGap.TIME_UNIT_DAY)) { ingo@329: gap = gapValue * TimeGap.DAY_IN_MILLIS; ingo@329: } ingo@329: else if (unit.equals(TimeGap.TIME_UNIT_WEEK)) { ingo@329: gap = gapValue * TimeGap.WEEK_IN_MILLIS; ingo@329: } ingo@329: else if (unit.equals(TimeGap.TIME_UNIT_MONTH)) { ingo@329: gap = gapValue * (TimeGap.DAY_IN_MILLIS *30); ingo@329: } ingo@329: else if (unit.equals(TimeGap.TIME_UNIT_YEAR)) { ingo@329: gap = gapValue * (TimeGap.DAY_IN_MILLIS *365); ingo@329: } ingo@329: break; ingo@329: } ingo@329: } ingo@329: } ingo@329: ingo@329: return gap; ingo@329: } ingo@298: } ingo@315: // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :