diff 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
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gnv-artifacts/src/main/java/de/intevation/gnv/chart/TimeSeriesChart.java	Fri Sep 28 12:13:49 2012 +0200
@@ -0,0 +1,495 @@
+package de.intevation.gnv.chart;
+
+import de.intevation.gnv.artifacts.ressource.RessourceFactory;
+
+import de.intevation.gnv.geobackend.base.Result;
+
+import de.intevation.gnv.state.describedata.KeyValueDescibeData;
+
+import de.intevation.gnv.timeseries.gap.TimeGap;
+
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Locale;
+import java.util.TimeZone;
+
+import org.apache.log4j.Logger;
+
+import org.jfree.chart.ChartFactory;
+import org.jfree.chart.ChartTheme;
+
+import org.jfree.chart.axis.Axis;
+import org.jfree.chart.axis.DateAxis;
+import org.jfree.chart.axis.DateTickUnit;
+import org.jfree.chart.axis.DateTickUnitType;
+import org.jfree.chart.axis.TickUnits;
+import org.jfree.chart.axis.TickUnitSource;
+import org.jfree.chart.axis.ValueAxis;
+
+import org.jfree.chart.plot.PlotOrientation;
+import org.jfree.chart.plot.XYPlot;
+
+import org.jfree.data.general.Series;
+
+import org.jfree.data.time.Minute;
+import org.jfree.data.time.TimeSeries;
+import org.jfree.data.time.TimeSeriesCollection;
+
+/**
+ * @author Ingo Weinzierl (ingo.weinzierl@intevation.de)
+ */
+public class TimeSeriesChart
+extends      AbstractXYLineChart
+{
+
+    private static final String DATE_FORMAT = "chart.timeseries.date.format";
+
+    public static final String DEFAULT_DATE_FORMAT = "dd-MMM-yyyy";
+
+    private static final long   NO_TIME_GAP = Long.MAX_VALUE - 1000;
+
+    private static Logger log = Logger.getLogger(TimeSeriesChart.class);
+
+
+    public TimeSeriesChart(
+        ChartLabels labels,
+        ChartTheme  theme,
+        Collection  parameters,
+        Collection  measurements,
+        Collection  dates,
+        Collection  result,
+        Collection  timeGaps,
+        Locale      locale,
+        boolean     linesVisible,
+        boolean     shapesVisible
+    ) {
+        this.labels           = labels;
+        this.theme            = theme;
+        this.parameters       = parameters;
+        this.measurements     = measurements;
+        this.dates            = dates;
+        this.resultSet        = result;
+        this.timeGaps         = timeGaps;
+        this.locale           = locale;
+        this.PLOT_ORIENTATION = PlotOrientation.VERTICAL;
+        this.linesVisible     = linesVisible;
+        this.shapesVisible    = shapesVisible;
+        this.datasets         = new HashMap();
+        this.ranges           = new HashMap();
+    }
+
+
+    protected void initChart() {
+        chart = ChartFactory.createTimeSeriesChart(
+            labels.getTitle(),
+            labels.getDomainAxisLabel(),
+            null,
+            null,
+            true,
+            false,
+            false
+        );
+
+        XYPlot plot = (XYPlot) chart.getPlot();
+        plot.setDomainAxis(0, new DateAxis(
+            labels.getDomainAxisLabel(), TimeZone.getDefault(), locale));
+    }
+
+
+    protected void initData() {
+        log.debug("init data for timeseries chart");
+
+        String  breakPoint1       = null;
+        String  breakPoint2       = null;
+        String  breakPoint3       = null;
+
+        Iterator   iter       = resultSet.iterator();
+        Result     row        = null;
+        String     seriesName = null;
+        String     parameter  = null;
+        TimeSeries series     = null;
+
+        int  idx       = 0;
+        int  startPos  = 0;
+        int  endPos    = 0;
+        Date startDate = null;
+        Date endDate   = null;
+
+        Result[] results =
+            (Result[]) resultSet.toArray(new Result[resultSet.size()]);
+
+        while (iter.hasNext()) {
+            row = (Result) iter.next();
+
+            // add current data to plot and prepare for next one
+            if (!row.getString("GROUP1").equals(breakPoint1) ||
+                !row.getString("GROUP2").equals(breakPoint2) ||
+                !row.getString("GROUP3").equals(breakPoint3)
+            ) {
+                log.debug("prepare data/plot for next dataset");
+
+                if(series != null) {
+                    // add gaps before adding series to chart
+                    startDate = results[startPos].getDate("XORDINATE");
+                    endDate   = results[endPos-1].getDate("XORDINATE");
+                    addGaps(results,series,startDate,endDate,startPos,endPos);
+                    addSeries(series, parameter, idx);
+
+                    startPos  = endPos + 1;
+                }
+
+                // prepare variables for next plot
+                breakPoint1 = row.getString("GROUP1");
+                breakPoint2 = row.getString("GROUP2");
+                breakPoint3 = row.getString("GROUP3");
+
+                seriesName = createSeriesName(
+                    breakPoint1,
+                    breakPoint2,
+                    breakPoint3
+                );
+                parameter = findParameter(seriesName);
+
+                log.debug("next dataset is '" + seriesName + "'");
+                series = new TimeSeries(seriesName, Minute.class);
+            }
+
+            addValue(row, series);
+            storeMaxRange(row.getDouble("YORDINATE"), parameter);
+            endPos++;
+        }
+
+        if (startPos < results.length && endPos-1 < results.length) {
+            // add the last dataset if existing to plot and prepare its axis
+            startDate = results[startPos].getDate("XORDINATE");
+            endDate = results[endPos-1].getDate("XORDINATE");
+            addGaps(results, series, startDate, endDate, startPos, endPos);
+            addSeries(series, parameter, idx);
+        }
+
+        addDatasets();
+    }
+
+
+    protected void addValue(Result row, Series series) {
+        ((TimeSeries) series).addOrUpdate(
+            new Minute(row.getDate("XORDINATE")),
+            row.getDouble("YORDINATE")
+        );
+    }
+
+
+    protected void addSeries(Series series, String parameter, int idx) {
+        log.debug("add series (" + parameter + ")to timeseries chart");
+
+        if (series == null) {
+            log.warn("no data to add");
+            return;
+        }
+
+        TimeSeriesCollection tsc = null;
+
+        if (datasets.containsKey(parameter))
+            tsc = (TimeSeriesCollection) datasets.get(parameter);
+        else
+            tsc = new TimeSeriesCollection();
+
+        tsc.addSeries((TimeSeries) series);
+        datasets.put(parameter, tsc);
+    }
+
+
+    protected void addDatasets() {
+        Iterator   iter = parameters.iterator();
+        XYPlot     plot = chart.getXYPlot();
+        int        idx  = 0;
+
+        TimeSeriesCollection tsc  = null;
+        KeyValueDescibeData  data = null;
+        String               key  = null;
+        while (iter.hasNext()) {
+            data = (KeyValueDescibeData) iter.next();
+            key  = data.getValue();
+
+            if (datasets.containsKey(key)) {
+                tsc  = (TimeSeriesCollection)datasets.get(key);
+                plot.setDataset(idx, tsc );
+                log.debug("Added " + key + " parameter to plot.");
+                prepareAxis(key, idx);
+                adjustRenderer(
+                    idx++,
+                    tsc.getSeriesCount(),
+                    linesVisible,
+                    shapesVisible
+                );
+            }
+        }
+    }
+
+
+    protected void localizeDomainAxis(Axis axis, Locale locale) {
+        ((ValueAxis)axis).setStandardTickUnits(createStandardDateTickUnits(
+            TimeZone.getDefault(),
+            locale));
+    }
+
+
+    public static TickUnitSource createStandardDateTickUnits(
+        TimeZone zone,
+        Locale locale)
+    {
+        /* 
+         * This method have been copied from JFreeChart's DateAxis class.
+         * DateFormat objects are hard coded in DateAxis and cannot be adjusted.
+         */
+        if (zone == null) {
+            throw new IllegalArgumentException("Null 'zone' argument.");
+        }
+        if (locale == null) {
+            throw new IllegalArgumentException("Null 'locale' argument.");
+        }
+        TickUnits units = new TickUnits();
+
+        // date formatters
+        DateFormat f1 = new SimpleDateFormat("HH:mm:ss.SSS", locale);
+        DateFormat f2 = new SimpleDateFormat("HH:mm:ss", locale);
+        DateFormat f3 = new SimpleDateFormat("HH:mm", locale);
+        DateFormat f4 = new SimpleDateFormat("d-MMM, HH:mm", locale);
+        DateFormat f5 = new SimpleDateFormat("d-MMM yyyy", locale);
+        DateFormat f6 = new SimpleDateFormat("MMM-yyyy", locale);
+        DateFormat f7 = new SimpleDateFormat("yyyy", locale);
+
+        f1.setTimeZone(zone);
+        f2.setTimeZone(zone);
+        f3.setTimeZone(zone);
+        f4.setTimeZone(zone);
+        f5.setTimeZone(zone);
+        f6.setTimeZone(zone);
+        f7.setTimeZone(zone);
+
+        // milliseconds
+        units.add(new DateTickUnit(DateTickUnitType.MILLISECOND, 1, f1));
+        units.add(new DateTickUnit(DateTickUnitType.MILLISECOND, 5,
+                DateTickUnitType.MILLISECOND, 1, f1));
+        units.add(new DateTickUnit(DateTickUnitType.MILLISECOND, 10,
+                DateTickUnitType.MILLISECOND, 1, f1));
+        units.add(new DateTickUnit(DateTickUnitType.MILLISECOND, 25,
+                DateTickUnitType.MILLISECOND, 5, f1));
+        units.add(new DateTickUnit(DateTickUnitType.MILLISECOND, 50,
+                DateTickUnitType.MILLISECOND, 10, f1));
+        units.add(new DateTickUnit(DateTickUnitType.MILLISECOND, 100,
+                DateTickUnitType.MILLISECOND, 10, f1));
+        units.add(new DateTickUnit(DateTickUnitType.MILLISECOND, 250,
+                DateTickUnitType.MILLISECOND, 10, f1));
+        units.add(new DateTickUnit(DateTickUnitType.MILLISECOND, 500,
+                DateTickUnitType.MILLISECOND, 50, f1));
+
+        // seconds
+        units.add(new DateTickUnit(DateTickUnitType.SECOND, 1,
+                DateTickUnitType.MILLISECOND, 50, f2));
+        units.add(new DateTickUnit(DateTickUnitType.SECOND, 5,
+                DateTickUnitType.SECOND, 1, f2));
+        units.add(new DateTickUnit(DateTickUnitType.SECOND, 10,
+                DateTickUnitType.SECOND, 1, f2));
+        units.add(new DateTickUnit(DateTickUnitType.SECOND, 30,
+                DateTickUnitType.SECOND, 5, f2));
+
+        // minutes
+        units.add(new DateTickUnit(DateTickUnitType.MINUTE, 1,
+                DateTickUnitType.SECOND, 5, f3));
+        units.add(new DateTickUnit(DateTickUnitType.MINUTE, 2,
+                DateTickUnitType.SECOND, 10, f3));
+        units.add(new DateTickUnit(DateTickUnitType.MINUTE, 5,
+                DateTickUnitType.MINUTE, 1, f3));
+        units.add(new DateTickUnit(DateTickUnitType.MINUTE, 10,
+                DateTickUnitType.MINUTE, 1, f3));
+        units.add(new DateTickUnit(DateTickUnitType.MINUTE, 15,
+                DateTickUnitType.MINUTE, 5, f3));
+        units.add(new DateTickUnit(DateTickUnitType.MINUTE, 20,
+                DateTickUnitType.MINUTE, 5, f3));
+        units.add(new DateTickUnit(DateTickUnitType.MINUTE, 30,
+                DateTickUnitType.MINUTE, 5, f3));
+
+        // hours
+        units.add(new DateTickUnit(DateTickUnitType.HOUR, 1,
+                DateTickUnitType.MINUTE, 5, f3));
+        units.add(new DateTickUnit(DateTickUnitType.HOUR, 2,
+                DateTickUnitType.MINUTE, 10, f3));
+        units.add(new DateTickUnit(DateTickUnitType.HOUR, 4,
+                DateTickUnitType.MINUTE, 30, f3));
+        units.add(new DateTickUnit(DateTickUnitType.HOUR, 6,
+                DateTickUnitType.HOUR, 1, f3));
+        units.add(new DateTickUnit(DateTickUnitType.HOUR, 12,
+                DateTickUnitType.HOUR, 1, f4));
+
+        // days
+        units.add(new DateTickUnit(DateTickUnitType.DAY, 1,
+                DateTickUnitType.HOUR, 1, f5));
+        units.add(new DateTickUnit(DateTickUnitType.DAY, 2,
+                DateTickUnitType.HOUR, 1, f5));
+        units.add(new DateTickUnit(DateTickUnitType.DAY, 7,
+                DateTickUnitType.DAY, 1, f5));
+        units.add(new DateTickUnit(DateTickUnitType.DAY, 15,
+                DateTickUnitType.DAY, 1, f5));
+
+        // months
+        units.add(new DateTickUnit(DateTickUnitType.MONTH, 1,
+                DateTickUnitType.DAY, 1, f6));
+        units.add(new DateTickUnit(DateTickUnitType.MONTH, 2,
+                DateTickUnitType.DAY, 1, f6));
+        units.add(new DateTickUnit(DateTickUnitType.MONTH, 3,
+                DateTickUnitType.MONTH, 1, f6));
+        units.add(new DateTickUnit(DateTickUnitType.MONTH, 4,
+                DateTickUnitType.MONTH, 1, f6));
+        units.add(new DateTickUnit(DateTickUnitType.MONTH, 6,
+                DateTickUnitType.MONTH, 1, f6));
+
+        // years
+        units.add(new DateTickUnit(DateTickUnitType.YEAR, 1,
+                DateTickUnitType.MONTH, 1, f7));
+        units.add(new DateTickUnit(DateTickUnitType.YEAR, 2,
+                DateTickUnitType.MONTH, 3, f7));
+        units.add(new DateTickUnit(DateTickUnitType.YEAR, 5,
+                DateTickUnitType.YEAR, 1, f7));
+        units.add(new DateTickUnit(DateTickUnitType.YEAR, 10,
+                DateTickUnitType.YEAR, 1, f7));
+        units.add(new DateTickUnit(DateTickUnitType.YEAR, 25,
+                DateTickUnitType.YEAR, 5, f7));
+        units.add(new DateTickUnit(DateTickUnitType.YEAR, 50,
+                DateTickUnitType.YEAR, 10, f7));
+        units.add(new DateTickUnit(DateTickUnitType.YEAR, 100,
+                DateTickUnitType.YEAR, 20, f7));
+
+        return units;
+    }
+
+
+    protected String getMessage(Locale locale, String key, String def) {
+        return RessourceFactory.getInstance().getRessource(locale, key, def);
+    }
+
+
+    protected String createSeriesName(
+        String breakPoint1,
+        String breakPoint2,
+        String breakPoint3
+    ) {
+        log.debug("create seriesname of timeseries chart");
+        return findValueTitle(parameters, breakPoint1) +
+            " " +
+            findValueTitle(measurements, breakPoint2) +
+            "m";
+    }
+
+
+    protected void addGaps(
+        Result[] results,
+        Series   series,
+        Date     startDate,
+        Date     endDate,
+        int      startPos,
+        int      endPos
+    ) {
+        int  gapID   = results[startPos].getInteger("GAPID");
+        long maxDiff = calculateGapSize(
+            startDate, endDate, startPos, endPos, gapID
+        );
+
+        Date last = startDate;
+        for (int i = startPos+1; i < endPos; i++) {
+            Result res = results[i];
+            Date   now = res.getDate("XORDINATE");
+
+            if ((now.getTime() - last.getTime()) > maxDiff) {
+                // add gap, add 1 minute to last date and add null value
+                log.info(
+                    "Gap between " +
+                    last.toString() + " and " + now.toString()
+                );
+                last.setTime(last.getTime() + 60000);
+                ((TimeSeries) series).addOrUpdate(new Minute(last), null);
+            }
+
+            last = now;
+        }
+    }
+
+
+    protected long calculateGapSize(
+        Date start,
+        Date end,
+        int  startPos,
+        int  endPos,
+        int  gapID
+    ){
+        long maxGap   = (end.getTime() - start.getTime()) / 20;
+        long interval = getTimeGapValue(start, end, startPos, endPos, gapID);
+
+        if (maxGap < interval)
+            maxGap = interval + 10;
+
+        return maxGap;
+    }
+
+
+    protected long getTimeGapValue(
+        Date dStart,
+        Date dEnd,
+        int  pStart,
+        int  pEnd,
+        int  gapID
+    ){
+        long gap = 0;
+
+        if (gapID < 0 || gapID >= 99) {
+
+            if (gapID == -1) {
+                // no gaps in meshes
+                gap = NO_TIME_GAP;
+            }
+            else if (pEnd-pStart < 60) {
+                gap = (3/(pEnd-pStart)) * (dEnd.getTime() - dStart.getTime());
+            }
+        }
+        else{
+            Iterator it = timeGaps.iterator();
+
+            while (it.hasNext()) {
+                TimeGap tempTimeGap = (TimeGap) it.next();
+
+                if (tempTimeGap.getKey() == gapID){
+                    String unit     = tempTimeGap.getUnit();
+                    int    gapValue = tempTimeGap.getValue();
+
+                    if (unit.equals(TimeGap.TIME_UNIT_MINUTE)) {
+                        gap = gapValue * TimeGap.MINUTE_IN_MILLIS;
+                    }
+                    else if (unit.equals(TimeGap.TIME_UNIT_HOUR)) {
+                        gap = gapValue * TimeGap.HOUR_IN_MILLIS;
+                    }
+                    else if (unit.equals(TimeGap.TIME_UNIT_DAY)) {
+                        gap = gapValue * TimeGap.DAY_IN_MILLIS;
+                    }
+                    else if (unit.equals(TimeGap.TIME_UNIT_WEEK)) {
+                        gap = gapValue * TimeGap.WEEK_IN_MILLIS;
+                    }
+                    else if (unit.equals(TimeGap.TIME_UNIT_MONTH)) {
+                        gap = gapValue * (TimeGap.DAY_IN_MILLIS *30);
+                    }
+                    else if (unit.equals(TimeGap.TIME_UNIT_YEAR)) {
+                        gap = gapValue * (TimeGap.DAY_IN_MILLIS *365);
+                    }
+                    break;
+                }
+            }
+        }
+
+        return gap;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :

http://dive4elements.wald.intevation.org