view flys-artifacts/src/main/java/de/intevation/flys/exports/TimeseriesChartGenerator.java @ 2587:bece6f604899

Removed references to Range and replaced those with references to Bounds in ChartGenerators. flys-artifacts/trunk@4143 c6561f87-3c4e-4783-a992-168aeb5c3f6f
author Ingo Weinzierl <ingo.weinzierl@intevation.de>
date Thu, 15 Mar 2012 10:30:03 +0000
parents 8cd6358eb7f8
children d75b427da50a
line wrap: on
line source
package de.intevation.flys.exports;

import java.awt.Color;
import java.awt.Font;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.log4j.Logger;

import org.jfree.chart.ChartFactory;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.ValueAxis;
import org.jfree.chart.plot.XYPlot;

import org.jfree.data.Range;
import org.jfree.data.time.TimeSeriesCollection;
import org.jfree.data.general.Series;
import org.jfree.data.xy.XYDataset;

import de.intevation.flys.jfree.Bounds;
import de.intevation.flys.jfree.DoubleBounds;
import de.intevation.flys.jfree.TimeBounds;


/**
 * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
 */
public abstract class TimeseriesChartGenerator extends ChartGenerator {


    /**
     * Inner class TimeseriesAxisDataset stores TimeSeriesCollection.
     */
    public class TimeseriesAxisDataset implements AxisDataset {

        protected int axisSymbol;

        protected List<TimeSeriesCollection> datasets;

        protected Range range;

        protected int plotAxisIndex;


        public TimeseriesAxisDataset(int axisSymbol) {
            this.axisSymbol = axisSymbol;
            this.datasets   = new ArrayList<TimeSeriesCollection>();
        }


        @Override
        public void addDataset(XYDataset dataset) {
            if (!(dataset instanceof TimeSeriesCollection)) {
                logger.warn("Skip non TimeSeriesCollection dataset.");
                return;
            }

            TimeSeriesCollection tsc = (TimeSeriesCollection) dataset;

            datasets.add(tsc);
            mergeRanges(tsc);
        }


        @Override
        public XYDataset[] getDatasets() {
            return (XYDataset[])
                datasets.toArray(new XYDataset[datasets.size()]);
        }


        @Override
        public boolean isEmpty() {
            return datasets.isEmpty();
        }


        @Override
        public void setRange(Range range) {
            this.range = range;
        }


        @Override
        public Range getRange() {
            return range;
        }


        @Override
        public void setPlotAxisIndex(int plotAxisIndex) {
            this.plotAxisIndex = plotAxisIndex;
        }


        @Override
        public int getPlotAxisIndex() {
            return plotAxisIndex;
        }


        @Override
        public boolean isArea(XYDataset dataset) {
            logger.warn("This AxisDataset doesn't support Areas yet!");
            return false;
        }


        protected void mergeRanges(TimeSeriesCollection dataset) {
            logger.debug("Range after merging: " + range);

            Bounds[] xyRanges = ChartHelper.getBounds(dataset);

            // TODO COMBINE BOUNDS!

            logger.debug("Range after merging: " + range);
        }

    } // end of TimeseriesAxisDataset class



    private static final Logger logger =
        Logger.getLogger(TimeseriesChartGenerator.class);


    public static final int AXIS_SPACE = 5;


    protected Map<Integer, Bounds> xBounds;

    protected Map<Integer, Bounds> yBounds;



    /**
     * The default constructor that initializes internal datastructures.
     */
    public TimeseriesChartGenerator() {
        super();

        xBounds = new HashMap<Integer, Bounds>();
        yBounds = new HashMap<Integer, Bounds>();
    }



    @Override
    public JFreeChart generateChart() {
        logger.info("Generate Timeseries Chart.");

        JFreeChart chart = ChartFactory.createTimeSeriesChart(
            getChartTitle(),
            getXAxisLabel(),
            getYAxisLabel(0),
            null,
            isLegendVisible(),
            false,
            false);

        XYPlot plot = (XYPlot) chart.getPlot();

        chart.setBackgroundPaint(Color.WHITE);
        plot.setBackgroundPaint(Color.WHITE);

        addSubtitles(chart);
        adjustPlot(plot);
        addDatasets(plot);
        adjustAxes(plot);

        adaptZoom(plot);

        return chart;
    }


    @Override
    protected Series getSeriesOf(XYDataset dataset, int idx) {
        return ((TimeSeriesCollection) dataset).getSeries(idx);
    }


    /**
     * This method creates new instances of TimeseriesAxisDataset.
     *
     * @param idx The symbol for the new TimeseriesAxisDataset.
     */
    @Override
    protected AxisDataset createAxisDataset(int idx) {
        logger.debug("Create a new AxisDataset for index: " + idx);
        return new TimeseriesAxisDataset(idx);
    }


    @Override
    protected void combineXBounds(Bounds bounds, int index) {
        if (bounds != null) {
            Bounds old = getXBounds(index);

            if (old != null) {
                bounds = bounds.combine(old);
            }

            setXBounds(index, bounds);
        }
    }


    @Override
    protected void combineYBounds(Bounds bounds, int index) {
        if (bounds != null) {
            Bounds old = getYBounds(index);

            if (old != null) {
                bounds = bounds.combine(old);
            }

            setYBounds(index, bounds);
        }
    }


    // TODO REPLACE THIS METHOD WITH getBoundsForAxis(index)
    @Override
    public Range[] getRangesForAxis(int index) {
        // TODO
        Bounds[] bounds = getBoundsForAxis(index);

        return new Range[] {
            new Range(
                bounds[0].getLower().doubleValue(),
                bounds[0].getUpper().doubleValue()),
            new Range(
                bounds[1].getLower().doubleValue(),
                bounds[1].getUpper().doubleValue())
        };
    }


    @Override
    public Bounds getXBounds(int axis) {
        return xBounds.get(axis);
    }


    @Override
    protected void setXBounds(int axis, Bounds bounds) {
        xBounds.put(axis, bounds);
    }


    @Override
    public Bounds getYBounds(int axis) {
        return yBounds.get(axis);
    }


    @Override
    protected void setYBounds(int axis, Bounds bounds) {
        if (bounds != null) {
            yBounds.put(axis, bounds);
        }
    }


    public Bounds[] getBoundsForAxis(int index) {
        logger.debug("Return x and y bounds for axis at: " + index);

        Bounds rx = getXBounds(Integer.valueOf(index));
        Bounds ry = getYBounds(Integer.valueOf(index));

        if (rx == null) {
            logger.warn("Range for x axis not set." +
                        " Using default values: 0 - 1.");
            rx = new TimeBounds(0l, 1l);
        }

        if (ry == null) {
            logger.warn("Range for y axis not set." +
                        " Using default values: 0 - 1.");
            ry = new DoubleBounds(0l, 1l);
        }

        logger.debug("X Bounds at index " + index + " is: " + rx);
        logger.debug("Y Bounds at index " + index + " is: " + ry);

        return new Bounds[] {rx, ry};
    }


    public Bounds getDomainAxisRange() {
        String[] ranges = getDomainAxisRangeFromRequest();

        if (ranges == null || ranges.length < 2) {
            logger.debug("No zoom range for domain axis specified.");
            return null;
        }

        if (ranges[0] == null || ranges[1] == null) {
            logger.warn("Invalid ranges for domain axis specified!");
            return null;
        }

        try {
            double lower = Double.parseDouble(ranges[0]);
            double upper = Double.parseDouble(ranges[1]);

            return new DoubleBounds(lower, upper);
        }
        catch (NumberFormatException nfe) {
            logger.warn("Invalid ranges for domain axis specified: " + nfe);
        }

        return null;
    }


    public Bounds getValueAxisRange() {
        String[] ranges = getValueAxisRangeFromRequest();

        if (ranges == null || ranges.length < 2) {
            logger.debug("No zoom range for domain axis specified.");
            return null;
        }

        if (ranges[0] == null || ranges[1] == null) {
            logger.warn("Invalid ranges for domain axis specified!");
            return null;
        }

        try {
            double lower = Double.parseDouble(ranges[0]);
            double upper = Double.parseDouble(ranges[1]);

            return new DoubleBounds(lower, upper);
        }
        catch (NumberFormatException nfe) {
            logger.warn("Invalid ranges for domain axis specified: " + nfe);
        }

        return null;
    }


    protected void adaptZoom(XYPlot plot) {
        logger.debug("Adapt zoom of Timeseries chart.");

        zoomX(plot, plot.getDomainAxis(), getXBounds(0), getDomainAxisRange());

        Bounds valueAxisBounds = getValueAxisRange();

        for (int j = 0, n = plot.getRangeAxisCount(); j < n; j++) {
            zoomY(
                plot,
                plot.getRangeAxis(j),
                getYBounds(j),
                valueAxisBounds);
        }
    }


    protected void zoomX(
        XYPlot    plot,
        ValueAxis axis,
        Bounds    total,
        Bounds    user
    ) {
        if (logger.isDebugEnabled()) {
            logger.debug("== Zoom X axis ==");
            logger.debug("    Total axis range  : " + total);
            logger.debug("    User defined range: " + user);
        }

        if (user != null) {
            long min  = total.getLower().longValue();
            long max  = total.getUpper().longValue();
            long diff = max > min ? max - min : min - max;

            long newMin = (long) Math.round(min + user.getLower().doubleValue() * diff);
            long newMax = (long) Math.round(min + user.getUpper().doubleValue() * diff);

            TimeBounds newBounds = new TimeBounds(newMin, newMax);

            logger.debug("    Zoom axis to: " + newBounds);

            newBounds.applyBounds(axis, AXIS_SPACE);
        }
        else {
            logger.debug("No user specified zoom values found!");
            total.applyBounds(axis, AXIS_SPACE);
        }
    }


    protected void zoomY(
        XYPlot    plot,
        ValueAxis axis,
        Bounds    total,
        Bounds    user
    ) {
        if (logger.isDebugEnabled()) {
            logger.debug("== Zoom Y axis ==");
            logger.debug("    Total axis range  : " + total);
            logger.debug("    User defined range: " + user);
        }

        if (user != null) {
            double min  = total.getLower().doubleValue();
            double max  = total.getUpper().doubleValue();
            double diff = max > min ? max - min : min - max;

            double newMin = min + user.getLower().doubleValue() * diff;
            double newMax = min + user.getUpper().doubleValue() * diff;

            DoubleBounds newBounds = new DoubleBounds(newMin, newMax);

            logger.debug("    Zoom axis to: " + newBounds);

            newBounds.applyBounds(axis, AXIS_SPACE);
        }
        else {
            logger.debug("No user specified zoom values found!");
            total.applyBounds(axis, AXIS_SPACE);
        }
    }


    /**
     * Adjusts the axes of a plot. This method sets the <i>labelFont</i> of the
     * X axis.
     *
     * @param plot The XYPlot of the chart.
     */
    protected void adjustAxes(XYPlot plot) {
        ValueAxis xaxis = plot.getDomainAxis();

        ChartSettings chartSettings = getChartSettings();
        if (chartSettings == null) {
            return;
        }

        Font labelFont = new Font(
            DEFAULT_FONT_NAME,
            Font.BOLD,
            getXAxisLabelFontSize());

        xaxis.setLabelFont(labelFont);
    }
}
// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :

http://dive4elements.wald.intevation.org