changeset 2400:f55984858952

Enabled zooming for timeseries charts. flys-artifacts/trunk@4026 c6561f87-3c4e-4783-a992-168aeb5c3f6f
author Ingo Weinzierl <ingo.weinzierl@intevation.de>
date Fri, 10 Feb 2012 10:45:32 +0000
parents 73b15736cb80
children b56e8613ce0d
files flys-artifacts/ChangeLog flys-artifacts/src/main/java/de/intevation/flys/exports/InfoGeneratorHelper.java flys-artifacts/src/main/java/de/intevation/flys/exports/TimeseriesChartGenerator.java
diffstat 3 files changed, 302 insertions(+), 30 deletions(-) [+]
line wrap: on
line diff
--- a/flys-artifacts/ChangeLog	Fri Feb 10 10:43:29 2012 +0000
+++ b/flys-artifacts/ChangeLog	Fri Feb 10 10:45:32 2012 +0000
@@ -1,3 +1,11 @@
+2012-02-10  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/exports/InfoGeneratorHelper.java: Write
+	  correct min and max values for date axes into the info document.
+
+	* src/main/java/de/intevation/flys/exports/TimeseriesChartGenerator.java:
+	  Enabled zooming for timeseries charts.
+
 2012-02-10  Ingo Weinzierl <ingo@intevation.de>
 
 	* src/main/java/de/intevation/flys/jfree/Bounds.java,
--- a/flys-artifacts/src/main/java/de/intevation/flys/exports/InfoGeneratorHelper.java	Fri Feb 10 10:43:29 2012 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/exports/InfoGeneratorHelper.java	Fri Feb 10 10:45:32 2012 +0000
@@ -24,6 +24,8 @@
 import de.intevation.artifacts.common.utils.XMLUtils;
 import de.intevation.artifacts.common.utils.XMLUtils.ElementCreator;
 
+import de.intevation.flys.jfree.Bounds;
+
 
 /**
  * This class helps generating chart info documents.
@@ -204,13 +206,20 @@
         Date from = axis.getMinimumDate();
         Date to   = axis.getMaximumDate();
 
+        Bounds bounds = null;
+        if (type.equals("range")) {
+            bounds = generator.getYBounds(pos);
+        }
+        else {
+            bounds = generator.getXBounds(pos);
+        }
+
         cr.addAttr(e, "axistype", "date", true);
         cr.addAttr(e, "from", String.valueOf(from.getTime()), true);
         cr.addAttr(e, "to", String.valueOf(to.getTime()), true);
 
-        // TODO Get correct min/max
-        cr.addAttr(e, "min", String.valueOf(from.getTime()), true);
-        cr.addAttr(e, "max", String.valueOf(to.getTime()), true);
+        cr.addAttr(e, "min", bounds.getLower().toString(), true);
+        cr.addAttr(e, "max", bounds.getUpper().toString(), true);
 
         return e;
     }
--- a/flys-artifacts/src/main/java/de/intevation/flys/exports/TimeseriesChartGenerator.java	Fri Feb 10 10:43:29 2012 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/exports/TimeseriesChartGenerator.java	Fri Feb 10 10:45:32 2012 +0000
@@ -11,6 +11,7 @@
 
 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;
@@ -18,6 +19,10 @@
 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>
@@ -106,8 +111,9 @@
         protected void mergeRanges(TimeSeriesCollection dataset) {
             logger.debug("Range after merging: " + range);
 
-            Range[] xyRanges = ChartHelper.getRanges(dataset);
-            range = Range.combine(range, xyRanges[1]);
+            Bounds[] xyRanges = ChartHelper.getBounds(dataset);
+
+            // TODO COMBINE BOUNDS!
 
             logger.debug("Range after merging: " + range);
         }
@@ -120,9 +126,12 @@
         Logger.getLogger(TimeseriesChartGenerator.class);
 
 
-    protected Map<Integer, Range> xRanges;
+    public static final int AXIS_SPACE = 5;
 
-    protected Map<Integer, Range> yRanges;
+
+    protected Map<Integer, Bounds> xRanges;
+
+    protected Map<Integer, Bounds> yRanges;
 
 
 
@@ -132,8 +141,8 @@
     public TimeseriesChartGenerator() {
         super();
 
-        xRanges = new HashMap<Integer, Range>();
-        yRanges = new HashMap<Integer, Range>();
+        xRanges = new HashMap<Integer, Bounds>();
+        yRanges = new HashMap<Integer, Bounds>();
     }
 
 
@@ -161,6 +170,8 @@
         addSubtitles(chart);
         addDatasets(plot);
 
+        adaptZoom(plot);
+
         return chart;
     }
 
@@ -171,15 +182,31 @@
     }
 
 
-    @Override
-    protected void setXRange(int axis, Range range) {
-        xRanges.put(Integer.valueOf(axis), range);
+    // TODO DECLARE IN UPPER CLASS AND ADD OVERRIDE ANNOTATION
+    protected Bounds getXRange(int axis) {
+        return xRanges.get(Integer.valueOf(axis));
     }
 
 
     @Override
+    // TODO setXRange should always await a Bounds instance!
+    // TODO SHOULD BE REMOVED WHEN DEFINED IN UPPER CLASS
+    protected void setXRange(int axis, Range range) {
+        // do nothing here, we will use setXRange(int, Bounds) now
+    }
+
+
+    @Override
+    // TODO setYRange should always await a Bounds instance!
     protected void setYRange(int axis, Range range) {
-        yRanges.put(Integer.valueOf(axis), range);
+        if (range == null) {
+            logger.warn("Range is null!");
+            return;
+        }
+
+        setYBounds(Integer.valueOf(axis), new DoubleBounds(
+            range.getLowerBound(),
+            range.getUpperBound()));
     }
 
 
@@ -195,6 +222,37 @@
     }
 
 
+    // TODO THIS SHOULD BE DONE IN AN UPPER CLASS!
+    @Override
+    public void addAxisDataset(XYDataset dataset, int idx, boolean visible) {
+        if (dataset == null || idx < 0) {
+            return;
+        }
+
+        AxisDataset axisDataset = getAxisDataset(idx);
+
+        Bounds[] bounds = ChartHelper.getBounds((TimeSeriesCollection)dataset);
+
+        if (bounds == null) {
+            logger.warn("Skip XYDataset for Axis (invalid ranges): " + idx);
+            return;
+        }
+
+        if (visible) {
+            if (logger.isDebugEnabled()) {
+                logger.debug("Add new AxisDataset at index: " + idx);
+                logger.debug("X extent: " + bounds[0]);
+                logger.debug("Y extent: " + bounds[1]);
+            }
+
+            axisDataset.addDataset(dataset);
+        }
+
+        combineXRanges(bounds[0], 0);
+        combineYRanges(bounds[1], idx);
+    }
+
+
     /**
      * Effect: extend range of x axis to include given limits.
      * @param range the given ("minimal") range.
@@ -202,40 +260,237 @@
      */
     @Override
     protected void combineXRanges(Range range, int index) {
-        if (range != null) {
-            Range old = xRanges.get(index);
+        throw new RuntimeException(
+            "TimeseriesChartGenerator.combineXRanges is not implemented!");
+    }
+
+
+    protected void combineXRanges(Bounds bounds, int index) {
+        if (bounds != null) {
+            Bounds old = getXRange(index);
 
             if (old != null) {
-                range = Range.combine(old, range);
+                bounds = bounds.combine(old);
             }
 
-            xRanges.put(index, range);
+            setXBounds(index, bounds);
         }
     }
 
 
+    protected void combineYRanges(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 Range[] getRangesForAxis(int index) {
-        logger.debug("Return ranges for axis at: " + index);
+    public Bounds getXBounds(int axis) {
+        return xRanges.get(axis);
+    }
 
-        Range rx = xRanges.get(Integer.valueOf(0));
-        Range ry = yRanges.get(Integer.valueOf(index));
+
+    @Override
+    protected void setXBounds(int axis, Bounds bounds) {
+        xRanges.put(axis, bounds);
+    }
+
+
+    @Override
+    public Bounds getYBounds(int axis) {
+        return yRanges.get(axis);
+    }
+
+
+    @Override
+    protected void setYBounds(int axis, Bounds bounds) {
+        yRanges.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 Range(0, 1);
-        }
-        if (ry == null) {
-            logger.warn("Range for y" + index +
-                        " axis not set. Using default values: 0 - 1.");
-            ry = new Range(0, 1);
+            rx = new TimeBounds(0l, 1l);
         }
 
-        logger.debug("X Range is: " + rx);
-        logger.debug("Y Range is: " + ry);
+        if (ry == null) {
+            logger.warn("Range for y axis not set." +
+                        " Using default values: 0 - 1.");
+            ry = new DoubleBounds(0l, 1l);
+        }
 
-        return new Range[] {rx, ry};
+        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(), getXRange(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);
+        }
     }
 }
 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :

http://dive4elements.wald.intevation.org