view gnv-artifacts/src/main/java/de/intevation/gnv/chart/TimeSeriesChart.java @ 340:07a64cfafdf1

Added gap detection in horizontal and vertical profile charts. Distinguish between meshes and other data sources. gnv-artifacts/trunk@406 c6561f87-3c4e-4783-a992-168aeb5c3f6f
author Ingo Weinzierl <ingo.weinzierl@intevation.de>
date Wed, 09 Dec 2009 10:22:20 +0000
parents e964a3d8f7bc
children 2f84ac484d8c
line wrap: on
line source
package de.intevation.gnv.chart;

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 org.apache.log4j.Logger;

import org.jfree.chart.ChartTheme;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.Axis;
import org.jfree.chart.axis.DateAxis;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.data.general.Series;
import org.jfree.data.time.TimeSeries;
import org.jfree.data.time.Minute;
import org.jfree.data.time.TimeSeriesCollection;

import de.intevation.gnv.geobackend.base.Result;
import de.intevation.gnv.state.describedata.KeyValueDescibeData;
import de.intevation.gnv.timeseries.gap.TimeGap;


/**
 * @author Ingo Weinzierl <ingo.weinzierl@intevation.de>
 */
public class TimeSeriesChart
extends      AbstractXYLineChart
{

    private static final String DATE_FORMAT = "dd-MMM";

    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();
    }


    protected void initChart() {
        chart = ChartFactory.createTimeSeriesChart(
            labels.getTitle(),
            labels.getDomainAxisLabel(),
            null,
            null,
            true,
            false,
            false
        );
    }


    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;
        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, seriesName, 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
                );

                log.debug("next dataset is '" + seriesName + "'");
                series = new TimeSeries(seriesName, Minute.class);
            }

            addValue(row, series);
            endPos++;
        }

        // 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, seriesName, 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 label, int idx) {
        log.debug("add series (" + label + ")to timeseries chart");

        if (series == null) {
            log.warn("no data to add");
            return;
        }

        TimeSeriesCollection tsc = null;

        String parameter = findParameter(label);
        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) {
        if (locale == null)
            return;

        log.debug(
            "Set language of axis [" + axis.getLabel() + "] " +
            "to " + locale.toString()
        );

        DateFormat format = new SimpleDateFormat(DATE_FORMAT, locale);
        ((DateAxis) axis).setDateFormatOverride(format);
    }


    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