view gnv-artifacts/src/main/java/de/intevation/gnv/chart/ChartFactory.java @ 327:22a6493e8460

New options in chart template: visibility of lines and points in charts and point's size. gnv-artifacts/trunk@393 c6561f87-3c4e-4783-a992-168aeb5c3f6f
author Ingo Weinzierl <ingo.weinzierl@intevation.de>
date Wed, 02 Dec 2009 10:50:12 +0000
parents 219e9666f590
children e964a3d8f7bc
line wrap: on
line source
/**
 * Title:           ChartFactory, $Header: /share/gdi/SDI-Suite/Repository/projekte/BSH-GDI/genericViewer/src/main/java/de/conterra/bsh/gdi/gnviewer/output/chart/ChartFactory.java,v 1.8 2007/12/21 12:31:15 blume Exp $
 * Source:          $Source: /share/gdi/SDI-Suite/Repository/projekte/BSH-GDI/genericViewer/src/main/java/de/conterra/bsh/gdi/gnviewer/output/chart/ChartFactory.java,v $
 * created by:      Stefan Blume (blume)
 * erstellt am:     06.12.2007
 * Copyright:       con terra GmbH, 2005
 *
 * modified by:     $Author: blume $
 * modified on:     $Date: 2007/12/21 12:31:15 $
 * Version:         $Revision: 1.8 $
 * TAG:             $Name:  $
 * locked from:     $Locker:  $
 * CVS State:       $State: Exp $
 * Project:         $ProjectName$
 */
package de.intevation.gnv.chart;

import java.awt.Color;
import java.awt.Font;
import java.awt.Transparency;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Collection;
import java.util.Date;
import java.util.Iterator;

import org.apache.log4j.Logger;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.AxisLocation;
import org.jfree.chart.axis.DateAxis;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.axis.NumberTickUnit;
import org.jfree.chart.encoders.KeypointPNGEncoderAdapter;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.StandardXYItemRenderer;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.data.time.Minute;
import org.jfree.data.time.TimeSeries;
import org.jfree.data.time.TimeSeriesCollection;
import org.jfree.data.xy.XYDataset;
import org.jfree.ui.RectangleInsets;

import de.intevation.gnv.chart.exception.TechnicalChartException;
import de.intevation.gnv.geobackend.base.Result;
import de.intevation.gnv.timeseries.gap.TimeGap;
import de.intevation.gnv.transition.describedata.KeyValueDescibeData;

/**
 * The class <code>ChartFactory</code> fulfills the following purposes:
 * <ol>
 * <li></li>
 * </ol>
 * 
 * @author blume
 * @version 1.0
 * @serial 1.0
 * @see
 * @since 06.12.2007 17:25:59
 */
public class ChartFactory {

    /**
     * Default Logging instance
     */
    private static Logger sLogger = Logger.getLogger(ChartFactory.class);
    private static boolean sDebug = sLogger.isDebugEnabled();
    
    private final static long NOTIMEGAP = Long.MAX_VALUE - 1000; 
    // Minus 1000 damit es bei Additionen keinen �berlauf gibt

    public synchronized void createSimpleTimeSeriesChart(
                                                         ChartLabels pLabels,
                                                         ChartStyle pStyle,
                                                         Collection<KeyValueDescibeData> parameters,
                                                         Collection<KeyValueDescibeData> measurements,
                                                         OutputStream outputStream,
                                                         Collection<Result> resultSet,
                                                         Collection<TimeGap> timeGaps)
                                                                                      throws IOException,
                                                                                      TechnicalChartException {
        if (sDebug)
            sLogger.debug("createSimpleTimeSeriesChart()");
        int lLowerLevel = Integer.MIN_VALUE;
        int lUpperLevel = Integer.MAX_VALUE;
        if (pStyle.isUseUpperDataLevel()
            && pStyle.getUpperLevel() < Integer.MAX_VALUE) {
            lUpperLevel = pStyle.getUpperLevel();
        }
        if (pStyle.isUseLowerDataLevel()
            && pStyle.getLowerLevel() > Integer.MIN_VALUE) {
            lLowerLevel = pStyle.getLowerLevel();
        }
        if (sDebug)
            sLogger.debug("  vor createDataset()");
        XYDataset lSet = this.createDataset(resultSet, lUpperLevel,
                lLowerLevel, parameters, measurements,timeGaps);
        if (sDebug)
            sLogger.debug("  nach createDataset()");
        final Color[] color = { Color.black, Color.red, Color.green, Color.blue };
        DateAxis domain = new DateAxis(pLabels.getDomainAxisLabel());
        NumberAxis axis;
        StandardXYItemRenderer renderer = new StandardXYItemRenderer();
        XYPlot plot = new XYPlot();
        // Global settings

        plot.setOrientation(PlotOrientation.VERTICAL);
        plot.setBackgroundPaint(Color.lightGray);
        plot.setDomainGridlinePaint(Color.white);
        plot.setRangeGridlinePaint(Color.white);
        plot.setAxisOffset(new RectangleInsets(5.0, 5.0, 5.0, 5.0));
        // plot.getRangeAxis().setFixedDimension(10.0);
        plot.setDomainAxis(domain);
        plot.setDomainAxisLocation(AxisLocation.BOTTOM_OR_LEFT);
        if (parameters.size() == 1) {
            KeyValueDescibeData parameter = parameters.iterator().next();
            axis = new NumberAxis(parameter.getValue());
            if (parameter.getValue().contains("richtung")) {
                NumberAxis axis1 = new NumberAxis((parameter
                        .getValue()));// ,new Range(0.0,360.0));
                axis1.setTickUnit(new NumberTickUnit(30.0));
                axis1.setUpperBound(360.0);
                axis1.setLowerBound(0.0);
                // axis1.setDisplayRange(0.0,360.0);
                plot.setRangeAxis(axis1);
            } else {
                axis.setFixedDimension(10.0);
                axis.setAutoRangeIncludesZero(false);
                plot.setRangeAxis(axis);
            }
            axis.configure();
            plot.setRangeAxisLocation(AxisLocation.BOTTOM_OR_LEFT);
            plot.setRenderer(renderer);
            plot.setDataset(lSet);
        } else {
            // Individual settings for different parameters
            for (int i = 0; i < lSet.getSeriesCount(); i++) {

                plot.setDataset(i, getDataset((TimeSeriesCollection) lSet, i));
                Color mColor = color[i % color.length]; // zyklische Farbvergabe
                mColor = color[0];
                // if ( pParameterId.length==1){

                if (((String) lSet.getSeriesKey(i)).contains("richtung")) {
                    NumberAxis axis1 = new NumberAxis(((String) lSet
                            .getSeriesKey(i)));// ,new Range(0.0,360.0));
                    axis1.setTickUnit(new NumberTickUnit(30.0));
                    // axis1.setDisplayRange(0.0,360.0);
                    axis1.setLabelPaint(mColor);
                    axis1.setTickLabelPaint(mColor);
                    axis1.setUpperBound(360.0);
                    axis1.setLowerBound(0.0);
                    plot.setRangeAxis(i, axis1);

                } else {
                    axis = new NumberAxis((String) lSet.getSeriesKey(i));
                    axis.setFixedDimension(10.0);
                    axis.setAutoRangeIncludesZero(false);
                    axis.setLabelPaint(mColor);
                    axis.setTickLabelPaint(mColor);
                    plot.setRangeAxis(i, axis);
                    axis.configure();
                }
                if (i % 2 != 0)
                    plot.setRangeAxisLocation(i, AxisLocation.BOTTOM_OR_RIGHT);
                else
                    plot.setRangeAxisLocation(i, AxisLocation.BOTTOM_OR_LEFT);
                plot.mapDatasetToRangeAxis(i, i);
                // }
                renderer = new StandardXYItemRenderer();
                renderer.setSeriesPaint(i, mColor);
                // renderer.setSeriesStroke(i,stroke[j]);
                plot.setRenderer(i, renderer);
            }
        }
        JFreeChart chart = new JFreeChart(pLabels.getTitle(), new Font(
                "SansSerif", Font.BOLD, 24), plot, true);

        setStyle(chart, pStyle);
        configureRenderingOptions(chart);
        if (sDebug)
            sLogger.debug("  vor encodeChart()");

        encodeChart(chart, pStyle, outputStream);
    }

    private static XYDataset getDataset(TimeSeriesCollection T, int pIndex) { // throws
                                                                              // TechnicalChartException{
        // if (T.getSeriesCount() < pIndex) throw TechnicalChartException();
        TimeSeriesCollection TSC = new TimeSeriesCollection();
        TSC.addSeries(T.getSeries(pIndex));
        return TSC;
    }

    private void configureRenderingOptions(JFreeChart pJfreechart) {
        org.jfree.chart.renderer.xy.XYItemRenderer xyitemrenderer = ((XYPlot) pJfreechart
                .getPlot()).getRenderer();
        if (xyitemrenderer instanceof XYLineAndShapeRenderer) {
            XYLineAndShapeRenderer xylineandshaperenderer = (XYLineAndShapeRenderer) xyitemrenderer;
            xylineandshaperenderer.setBaseShapesVisible(true);
            xylineandshaperenderer.setBaseShapesFilled(true);
        }
    }

    private void setStyle(JFreeChart pJfreechart, ChartStyle pStyle) {
        if (sDebug)
            sLogger.debug("setStyle()");
        pJfreechart.setBackgroundPaint(pStyle.getCanvasColor());
        XYPlot xyplot = (XYPlot) pJfreechart.getPlot();
        xyplot.setBackgroundPaint(pStyle.getPlotBackgroundColor());
        xyplot.setDomainGridlinePaint(pStyle.getDomainGridlineColor());
        xyplot.setRangeGridlinePaint(pStyle.getRangeGridlineColor());

        Insets lOffsets = pStyle.getAxisOffset();
        RectangleInsets lRectangleInsets = new RectangleInsets(lOffsets.mUpper,
                lOffsets.mLeft, lOffsets.mLower, lOffsets.mRight);
        xyplot.setAxisOffset(lRectangleInsets);
        xyplot.setDomainCrosshairVisible(pStyle.isDomainCrosshairVisible());
        xyplot.setRangeCrosshairVisible(pStyle.isRangeCrosshairVisible());

    }

    private TimeSeries createTimeSeries(String pTimeSeriesName,
                                        Collection<Result> resultSet,
                                        int lUpperCut, int lLowerCut,
                                        int pStart, int pEnd, long maxGap)
                                                  throws TechnicalChartException {
        if (sDebug)
            sLogger.debug("createTimeSeries()");
        Result lRow0, lRow1;

        Date lDate = null, lDate0 = null;

        TimeSeries lTimeseries = new TimeSeries(pTimeSeriesName,
                org.jfree.data.time.Minute.class);
        try {
            long lDateDiff = 0;
            double lValue = 0;
            
            int i = 0;
            Iterator<Result> resultIterator = resultSet.iterator();
            while (resultIterator.hasNext()) {
                Result lRow = resultIterator.next();
                if (i >= pStart + 1 && i <= pEnd) {
                    if (i == pStart + 1) {
                        lRow0 = lRow;//
                        lDate = lRow0.getDate("XORDINATE");
                        lDate0 = lDate;
                        lValue = lRow0.getDouble("YORDINATE");
                        if (lValue > lLowerCut && lValue < lUpperCut) {
                            // lTimeseries.addOrUpdate(new Minute(lDate),
                            // lValue);
                            lTimeseries.add(new Minute(lDate), lValue);
                        }
                    }
                    // for (int i = pStart+1; i <= pEnd; i++) {
                    lRow1 = lRow;
                    lDate = lRow1.getDate("XORDINATE");
                    lValue = lRow1.getDouble("YORDINATE");
                    lDateDiff = lDate.getTime() - lDate0.getTime();
                    if (lDateDiff > maxGap) {
                        // add 1 minute in millisecs to left hand side Date
                        // and insert Dummy to break line
                        lDate0.setTime((lDate0.getTime() + 60000));
                        lTimeseries.addOrUpdate(new Minute(lDate0), null);
                        lTimeseries.addOrUpdate(new Minute(lDate), lValue);
                        // lTimeseries.add(new Minute(lDate0), null);
                    } else if (lDateDiff == 0) {
                        if (sDebug)
                            sLogger.debug("Datediff: " + lDateDiff
                                          + " bei index : " + i + " Datum : "
                                          + lDate + " " + lDate0);
                    } else if (lValue > lLowerCut && lValue < lUpperCut) {
                        lTimeseries.addOrUpdate(new Minute(lDate), lValue);
                        // lTimeseries.add(new Minute(lDate), lValue);
                    }
                    lRow0 = lRow1;
                    lDate0 = lDate;
                } else if (i > pEnd) {
                    return lTimeseries;
                }
                i++;
            }

        } catch (OutOfMemoryError e) {
            sLogger.error(e.getMessage(), e);
            return lTimeseries;

        } catch (Exception e) { // TechnicalChartException
            sLogger.error(e.getMessage(), e);
        } finally {
        }

        return lTimeseries;
    }

    private XYDataset createDataset(Collection<Result> resultSet,
                                    int lUpperCut, int lLowerCut,
                                    Collection<KeyValueDescibeData> parameters,
                                    Collection<KeyValueDescibeData> measurements,
                                    Collection<TimeGap> timeGaps)
                                                                                 throws TechnicalChartException {

        TimeSeriesCollection lTimeSeriesCollection = new TimeSeriesCollection();
        try {
            Date dStart = null, dEnd = null;
            String break1, break2, break3;
            int mStart = 0;
            int mEnd = 0;

            Iterator<Result> resultIterator = resultSet.iterator();
            if (resultIterator.hasNext()) {
                Result row = resultIterator.next();

                break1 = row.getString("GROUP1"); // 2
                break2 = row.getString("GROUP2"); // 3
                break3 = row.getString("GROUP3"); // 4
                dStart = row.getDate("XORDINATE");
                int i = 1;
                Integer gapID = -1;
                while (resultIterator.hasNext()) {
                    row = resultIterator.next();
                    if (!break1.equals(row.getString("GROUP1"))
                        || !break2.equals(row.getString("GROUP2"))
                        || !break3.equals(row.getString("GROUP3"))) {
                        String mTimeSeriesName = findValueTitle(parameters,
                                break1)
                                                 + " "
                                                 + findValueTitle(measurements,
                                                         break2) + "m";
                        long maxGap = this.calculateMaxGap(dStart, dEnd,mStart,
                                                           mEnd, gapID.intValue(),
                                                           timeGaps);
                        
                        
                        lTimeSeriesCollection.addSeries(createTimeSeries(
                                mTimeSeriesName, resultSet, lUpperCut,
                                lLowerCut, mStart, mEnd,maxGap));
                        mStart = i;
                        dStart = row.getDate("XORDINATE");
                        break1 = row.getString("GROUP1");
                        break2 = row.getString("GROUP2"); // 3
                        break3 = row.getString("GROUP3"); // 4

                    }
                    mEnd = i;
                    dEnd = row.getDate("XORDINATE");
                    gapID = row.getInteger("GAPID");
                    i = i + 1;
                }

                String mTimeSeriesName = findValueTitle(parameters, break1)
                                         + " "
                                         + findValueTitle(measurements, break2)
                                         + "m";
                long maxGap = this.calculateMaxGap(dStart, dEnd,mStart, 
                                                   mEnd,gapID.intValue(),
                                                   timeGaps);
                lTimeSeriesCollection.addSeries(createTimeSeries(
                        mTimeSeriesName, resultSet, lUpperCut, lLowerCut,
                        mStart, mEnd, maxGap));
            }else{
                // Es sind keine Daten vorhanden: Es wird eine Zeitserie eingef�gt.
                lTimeSeriesCollection.addSeries(createTimeSeries(
                        "", resultSet, lUpperCut, lLowerCut,
                        mStart, mEnd, 999999));
            }
        } catch (Exception e) {
            sLogger.error(e.getMessage(), e);
        } finally {
        }
        return lTimeSeriesCollection;
    }

    /**
     * @param dStart
     * @param dEnd
     */
    private long calculateMaxGap(Date dStart, Date dEnd, int pStart ,
                                 int pEnd, int gapID, 
                                 Collection<TimeGap> timeGaps) {
        
           // umgesetzt nach issue 45
            // Handle Gaps > 0.5% timeserieslength, 
            // i.e do not draw here
            long maxGap = (dEnd.getTime() - dStart.getTime()) / 200; 
            // 0,5 prozent der L�nge
            long timeInterval = this.getTimeGapValue(dStart, dEnd, pStart, 
                                                     pEnd, gapID, timeGaps);
            
            if (maxGap < timeInterval){
                maxGap = timeInterval + 10;
            }
//            if (maxGap < (dEnd.getTime() - dStart.getTime())
//                         / (pEnd - pStart))
//                maxGap = (dEnd.getTime() - dStart.getTime())
//                         / (pEnd - pStart) + 1000;
            
            return maxGap;
    }
    
    private long getTimeGapValue(Date dStart, Date dEnd, 
                                 int pStart ,int pEnd, 
                                 int gapID, Collection<TimeGap> timeGaps){
        long gap = 0;
        
        if (gapID < 0 || gapID >= 99){
            
            if (gapID == -1){ // Mesh
                gap = NOTIMEGAP; // Es gibt keine L�cken in Netzen.
            }else if (pEnd-pStart < 60){
                gap = (3/(pEnd-pStart)) * (dEnd.getTime() - dStart.getTime());
            }
        }else{
            
            Iterator<TimeGap> it = timeGaps.iterator();
           
            while (it.hasNext()){
                TimeGap tempTimeGap = it.next();
                if (tempTimeGap.getKey() == gapID){
                    int gapValue = tempTimeGap.getValue();
                    String unit = tempTimeGap.getUnit();
                    
                    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)){
                        // TODO wie soll das laufen
                        gap = gapValue * (TimeGap.DAY_IN_MILLIS *30);
                    }else if (unit.equals(TimeGap.TIME_UNIT_YEAR)){
                        // TODO wie soll das laufen f�r schaltjahre
                        gap = gapValue * (TimeGap.DAY_IN_MILLIS *365);
                    }
                    break;
                }
            }
        }
        
        return gap;
    }

    private void encodeChart(JFreeChart pChart, ChartStyle pStyle,
                             OutputStream outputStream) throws IOException {
        if (sDebug)
            sLogger.debug("encodeChart()");
        KeypointPNGEncoderAdapter lEncoder = new KeypointPNGEncoderAdapter();
        lEncoder.setEncodingAlpha(true);

        int lWidth = (int) pStyle.getChartSize().getWidth();
        int lHeight = (int) pStyle.getChartSize().getHeight();

        BufferedImage lImage = pChart.createBufferedImage(lWidth, lHeight,
                Transparency.BITMASK, null);

        lEncoder.encode(lImage, outputStream);

    }

    private String findValueTitle(Collection<KeyValueDescibeData> values,
                                  String pMmtId) {
        int id = 0;
        try {
            id = Integer.parseInt(pMmtId);
        } catch (NumberFormatException e) {
            sLogger.warn(e, e);
            return pMmtId;
        }

        Iterator<KeyValueDescibeData> it = values.iterator();
        while (it.hasNext()) {
            KeyValueDescibeData data = it.next();
            if (id == Integer.parseInt(data.getKey())) {
                return data.getValue();
            }
        }
        return "";
    }
}

http://dive4elements.wald.intevation.org