changeset 2238:23c7c51df772

Some more refactoring in XYChartGenerator and ChartGenerator; implemented necessary stuff in TimeseriesChartGenerator and return new empty instances of timeseries charts. flys-artifacts/trunk@3885 c6561f87-3c4e-4783-a992-168aeb5c3f6f
author Ingo Weinzierl <ingo.weinzierl@intevation.de>
date Thu, 02 Feb 2012 15:44:13 +0000
parents 60615235e951
children e7843533f086
files flys-artifacts/ChangeLog flys-artifacts/src/main/java/de/intevation/flys/exports/ChartGenerator.java flys-artifacts/src/main/java/de/intevation/flys/exports/TimeseriesChartGenerator.java flys-artifacts/src/main/java/de/intevation/flys/exports/XYChartGenerator.java
diffstat 4 files changed, 297 insertions(+), 57 deletions(-) [+]
line wrap: on
line diff
--- a/flys-artifacts/ChangeLog	Thu Feb 02 15:39:28 2012 +0000
+++ b/flys-artifacts/ChangeLog	Thu Feb 02 15:44:13 2012 +0000
@@ -1,3 +1,15 @@
+2012-02-02  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/exports/XYChartGenerator.java,
+	  src/main/java/de/intevation/flys/exports/ChartGenerator.java: More
+	  refactoring: the AxisDataset is defined by an interface in ChartGenerator
+	  now. Each subclass of ChartGenerator should implement its own AxisDataset.
+	  This allows us to provide multiple XYDataset types in different charts.
+
+	* src/main/java/de/intevation/flys/exports/TimeseriesChartGenerator.java:
+	  Implemented some necessary methods (abstract definitions of parent class)
+	  and create new empty timeseries charts.
+
 2012-02-02  Ingo Weinzierl <ingo@intevation.de>
 
 	* src/main/java/de/intevation/flys/exports/ChartHelper.java: New helper
--- a/flys-artifacts/src/main/java/de/intevation/flys/exports/ChartGenerator.java	Thu Feb 02 15:39:28 2012 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/exports/ChartGenerator.java	Thu Feb 02 15:44:13 2012 +0000
@@ -9,6 +9,8 @@
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Locale;
+import java.util.TreeMap;
+import java.util.SortedMap;
 
 import javax.xml.xpath.XPathConstants;
 
@@ -21,6 +23,7 @@
 import org.jfree.chart.LegendItem;
 import org.jfree.chart.axis.NumberAxis;
 import org.jfree.data.Range;
+import org.jfree.data.xy.XYDataset;
 
 import de.intevation.artifacts.Artifact;
 import de.intevation.artifacts.CallContext;
@@ -88,17 +91,43 @@
     /** The settings that should be used during output creation.*/
     protected Settings settings;
 
+    /** Map of datasets ("index"). */
+    protected SortedMap<Integer, AxisDataset> datasets;
+
+
 
     /**
      * A mini interface that allows to walk over the YAXIS enums defined in
      * subclasses.
      */
     public interface YAxisWalker {
+
         int length();
+
         String getId(int idx);
+    } // end of YAxisWalker interface
+
+
+
+    public interface AxisDataset {
+
+        void addDataset(XYDataset dataset);
+
+        boolean isEmpty();
+
+    } // end of AxisDataset interface
+
+
+
+    /**
+     * Default constructor that initializes internal data structures.
+     */
+    public ChartGenerator() {
+        datasets = new TreeMap<Integer, AxisDataset>();
     }
 
 
+
     /**
      * This method needs to be implemented by concrete subclasses to create new
      * instances of JFreeChart.
@@ -145,6 +174,25 @@
 
 
     /**
+     * This method is used to create new AxisDataset instances which may differ
+     * in concrete subclasses.
+     *
+     * @param idx The index of an axis.
+     */
+    protected abstract AxisDataset createAxisDataset(int idx);
+
+
+    /**
+     * Combines the ranges of the X axis at index <i>idx</i>.
+     *
+     * @param range A new range.
+     * @param idx The index of the X axis that should be comined with
+     * <i>range</i>.
+     */
+    protected abstract void combineXRanges(Range range, int idx);
+
+
+    /**
      * This method should be used by concrete subclasses to add subtitle to
      * <i>chart</i>. <b>The method in this implementation is empty</b>.
      *
@@ -731,6 +779,59 @@
 
 
     /**
+     * Adds a new AxisDataset which contains <i>dataset</i> at index <i>idx</i>.
+     *
+     * @param dataset An XYDataset.
+     * @param idx The axis index.
+     * @param visible Determines, if the dataset should be visible or not.
+     */
+    public void addAxisDataset(XYDataset dataset, int idx, boolean visible) {
+        if (dataset == null || idx < 0) {
+            return;
+        }
+
+        AxisDataset axisDataset = getAxisDataset(idx);
+
+        Range[] xyRanges = ChartHelper.getRanges(dataset);
+
+        if (visible) {
+            logger.debug("Add new AxisDataset at index: " + idx);
+            axisDataset.addDataset(dataset);
+            combineXRanges(xyRanges[0], 0);
+        }
+        else {
+            combineXRanges(xyRanges[0], 0);
+
+            // TODO
+            // Expand y range provided by 'timeseries' to have a proper range
+            // set which includes all data.
+            // iw: I am not sure if we still need this
+        }
+    }
+
+
+    /**
+     * This method grants access to the AxisDatasets stored in <i>datasets</i>.
+     * If no AxisDataset exists for index <i>idx</i>, a new AxisDataset is
+     * created using <i>createAxisDataset()</i>.
+     *
+     * @param idx The index of the desired AxisDataset.
+     *
+     * @return an existing or new AxisDataset.
+     */
+    public AxisDataset getAxisDataset(int idx) {
+        AxisDataset axisDataset = datasets.get(idx);
+
+        if (axisDataset == null) {
+            axisDataset = createAxisDataset(idx);
+            datasets.put(idx, axisDataset);
+        }
+
+        return axisDataset;
+    }
+
+
+    /**
      * This helper mehtod is used to extract the current locale from instance
      * vairable <i>context</i>.
      *
@@ -947,6 +1048,29 @@
 
 
     /**
+     * Create Y (range) axis for given index.
+     * Shall be overriden by subclasses.
+     */
+    protected NumberAxis createYAxis(int index) {
+        YAxisWalker walker = getYAxisWalker();
+
+        Font labelFont = new Font(
+            DEFAULT_FONT_NAME,
+            Font.BOLD,
+            getYAxisFontSize(index));
+
+        IdentifiableNumberAxis axis = new IdentifiableNumberAxis(
+            walker.getId(index),
+            getYAxisLabel(index));
+
+        axis.setAutoRangeIncludesZero(false);
+        axis.setLabelFont(labelFont);
+
+        return axis;
+    }
+
+
+    /**
      * Creates a new LegendItem with <i>name</i> and font provided by
      * <i>createLegendLabelFont()</i>.
      *
--- a/flys-artifacts/src/main/java/de/intevation/flys/exports/TimeseriesChartGenerator.java	Thu Feb 02 15:39:28 2012 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/exports/TimeseriesChartGenerator.java	Thu Feb 02 15:44:13 2012 +0000
@@ -1,10 +1,22 @@
 package de.intevation.flys.exports;
 
+import java.awt.Color;
+
+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.NumberAxis;
+import org.jfree.chart.plot.XYPlot;
+
+import org.jfree.data.Range;
+import org.jfree.data.time.TimeSeriesCollection;
+import org.jfree.data.xy.XYDataset;
 
 
 /**
@@ -12,20 +24,139 @@
  */
 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 boolean isEmpty() {
+            return datasets.isEmpty();
+        }
+
+
+        protected void mergeRanges(TimeSeriesCollection dataset) {
+            logger.debug("Range after merging: " + range);
+
+            Range[] xyRanges = ChartHelper.getRanges(dataset);
+            range = Range.combine(range, xyRanges[1]);
+
+            logger.debug("Range after merging: " + range);
+        }
+
+    } // end of TimeseriesAxisDataset class
+
+
+
     private static final Logger logger =
         Logger.getLogger(TimeseriesChartGenerator.class);
 
 
-    protected abstract NumberAxis createYAxis(int index);
+    protected Map<Integer, Range> xRanges;
+
+
+
+    /**
+     * The default constructor that initializes internal datastructures.
+     */
+    public TimeseriesChartGenerator() {
+        super();
+
+        xRanges = new HashMap<Integer, Range>();
+    }
+
 
 
     @Override
     public JFreeChart generateChart() {
         logger.info("Generate Timeseries Chart.");
 
+        JFreeChart chart = ChartFactory.createTimeSeriesChart(
+            getChartTitle(),
+            getXAxisLabel(),
+            getYAxisLabel(0),
+            null,
+            true,
+            false,
+            false);
+
         logger.warn("TODO: IMPLEMENT ME!");
 
-        return null;
+        XYPlot plot = (XYPlot) chart.getPlot();
+
+        chart.setBackgroundPaint(Color.WHITE);
+        plot.setBackgroundPaint(Color.WHITE);
+
+        addSubtitles(chart);
+        addDatasets(plot);
+
+        return chart;
+    }
+
+
+    /**
+     * 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);
+    }
+
+
+    /**
+     * Effect: extend range of x axis to include given limits.
+     * @param range the given ("minimal") range.
+     * @param index index of axis to be merged.
+     */
+    @Override
+    protected void combineXRanges(Range range, int index) {
+        if (range != null) {
+            Range old = xRanges.get(index);
+
+            if (old != null) {
+                range = Range.combine(old, range);
+            }
+
+            xRanges.put(index, range);
+        }
+    }
+
+
+    protected void addDatasets(XYPlot plot) {
+        logger.warn("TODO: IMPLEMENT ME!");
     }
 }
 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/exports/XYChartGenerator.java	Thu Feb 02 15:39:28 2012 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/exports/XYChartGenerator.java	Thu Feb 02 15:44:13 2012 +0000
@@ -83,7 +83,7 @@
 public abstract class XYChartGenerator extends ChartGenerator {
 
     // TODO Consider storing the renderer here.
-    private class AxisDataset {
+    private class XYAxisDataset implements AxisDataset {
         /** Symbolic integer, but also coding the priority (0 goes first). */
         protected int axisSymbol;
         /** List of assigned datasets (in order). */
@@ -94,7 +94,7 @@
         protected int plotAxisIndex;
 
         /** Create AxisDataset. */
-        public AxisDataset(int symb) {
+        public XYAxisDataset(int symb) {
             this.axisSymbol = symb;
             datasets        = new ArrayList<XYDataset>();
         }
@@ -114,10 +114,16 @@
             range = Range.combine(range, subRange);
         }
 
+
+        @Override
+        public void addDataset(XYDataset dataset) {
+            datasets.add(dataset);
+            includeYRange(((XYSeriesCollection) dataset).getSeries(0));
+        }
+
         /** Add a dataset, include its range. */
-        public void addDataset(XYSeries dataset) {
-            this.datasets.add(new XYSeriesCollection(dataset));
-            includeYRange(dataset);
+        public void addDataset(XYSeries series) {
+            addDataset(new XYSeriesCollection(series));
         }
 
         public void addArea(StyledAreaSeriesCollection series) {
@@ -172,9 +178,6 @@
     /** The logger that is used in this generator. */
     private static Logger logger = Logger.getLogger(XYChartGenerator.class);
 
-    /** Map of datasets ("index"). */
-    protected SortedMap<Integer, AxisDataset> datasets;
-
     /** List of annotations to insert in plot. */
     protected List<FLYSAnnotation> annotations;
 
@@ -186,9 +189,10 @@
 
 
     public XYChartGenerator() {
+        super();
+
         xRanges  = new HashMap<Integer, Range>();
         yRanges  = new HashMap<Integer, Range>();
-        datasets = new TreeMap<Integer, AxisDataset>();
     }
 
 
@@ -236,6 +240,13 @@
     }
 
 
+    @Override
+    protected AxisDataset createAxisDataset(int idx) {
+        logger.debug("Create new XYAxisDataset for index: " + idx);
+        return new XYAxisDataset(idx);
+    }
+
+
     /**
      * Put debug output about datasets.
      */
@@ -288,7 +299,7 @@
         for (Map.Entry<Integer, AxisDataset> entry: datasets.entrySet()) {
             if (!entry.getValue().isEmpty()) {
                 // Add axis and range information.
-                AxisDataset axisDataset = entry.getValue();
+                XYAxisDataset axisDataset = (XYAxisDataset) entry.getValue();
                 NumberAxis axis = createYAxis(entry.getKey());
 
                 plot.setRangeAxis(axisIndex, axis);
@@ -324,12 +335,8 @@
             logger.warn("Cannot yet render above/under curve.");
             return;
         }
-        AxisDataset axisDataset = datasets.get(index);
 
-        if (axisDataset == null) {
-            axisDataset = new AxisDataset(index);
-            datasets.put(index, axisDataset);
-        }
+        XYAxisDataset axisDataset = (XYAxisDataset) getAxisDataset(index);
 
         if (visible) {
             axisDataset.addArea(area);
@@ -349,30 +356,19 @@
      * @param visible whether or not the data should be plotted.
      */
     public void addAxisSeries(XYSeries series, int index, boolean visible) {
+        addAxisDataset(new XYSeriesCollection(series), index, visible);
+
         if (series == null) {
             return;
         }
 
-        AxisDataset axisDataset = datasets.get(index);
-
-        if (axisDataset == null) {
-            axisDataset = new AxisDataset(index);
-            datasets.put(index, axisDataset);
-        }
+        XYAxisDataset axisDataset = (XYAxisDataset) getAxisDataset(index);
 
-        logger.debug("addAxisSeries: extent X " + series.getMinX() + " : " + series.getMaxX()
-            + " extent y " + series.getMinY() + " : " + series.getMaxY());
-
-        if (visible) {
-            axisDataset.addDataset(series);
-        }
-        else {
+        if (!visible) {
             // Do this also when not visible to have axis scaled by default such
             // that every data-point could be seen (except for annotations).
             axisDataset.includeYRange(series);
         }
-
-        combineXRanges(new Range(series.getMinX(), series.getMaxX()), 0);
     }
 
 
@@ -381,7 +377,7 @@
      * @param range the given ("minimal") range.
      * @param index index of axis to be merged.
      */
-    private void combineXRanges(Range range, int index) {
+    protected void combineXRanges(Range range, int index) {
 
         if (range == null
             || Double.isNaN(range.getLowerBound())
@@ -416,29 +412,6 @@
 
 
     /**
-     * Create Y (range) axis for given index.
-     * Shall be overriden by subclasses.
-     */
-    protected NumberAxis createYAxis(int index) {
-        YAxisWalker walker = getYAxisWalker();
-
-        Font labelFont = new Font(
-            DEFAULT_FONT_NAME,
-            Font.BOLD,
-            getYAxisFontSize(index));
-
-        IdentifiableNumberAxis axis = new IdentifiableNumberAxis(
-            walker.getId(index),
-            getYAxisLabel(index));
-
-        axis.setAutoRangeIncludesZero(false);
-        axis.setLabelFont(labelFont);
-
-        return axis;
-    }
-
-
-    /**
      * If no data is visible, draw at least empty axis.
      */
     private void recoverEmptyPlot(XYPlot plot) {
@@ -687,7 +660,7 @@
             // Do the more complicated case where we stick to the Y-Axis.
             // There is one nasty case (duration curves, where annotations
             // might stick to the second y-axis).
-            AxisDataset dataset = this.datasets.get(
+            XYAxisDataset dataset = (XYAxisDataset) getAxisDataset(
                 new Integer(annotation.getAxisSymbol()));
             if (dataset == null) {
                 logger.warn("Annotation should stick to unfindable y-axis: "

http://dive4elements.wald.intevation.org