changeset 2072:4cdd9c4896f6

#393 Added a new Renderer and Options in Themes that allow displaying minimum and maximum of a chart series. flys-artifacts/trunk@3581 c6561f87-3c4e-4783-a992-168aeb5c3f6f
author Ingo Weinzierl <ingo.weinzierl@intevation.de>
date Wed, 04 Jan 2012 12:24:35 +0000
parents fd95bfbb2ec2
children 27ada2e4243d
files flys-artifacts/ChangeLog flys-artifacts/doc/conf/themes.xml flys-artifacts/src/main/java/de/intevation/flys/exports/EnhancedLineAndShapeRenderer.java flys-artifacts/src/main/java/de/intevation/flys/exports/StyledXYSeries.java flys-artifacts/src/main/java/de/intevation/flys/exports/XYChartGenerator.java flys-artifacts/src/main/java/de/intevation/flys/utils/ThemeUtil.java
diffstat 6 files changed, 318 insertions(+), 35 deletions(-) [+]
line wrap: on
line diff
--- a/flys-artifacts/ChangeLog	Tue Jan 03 13:14:42 2012 +0000
+++ b/flys-artifacts/ChangeLog	Wed Jan 04 12:24:35 2012 +0000
@@ -1,3 +1,26 @@
+2012-01-04  Ingo Weinzierl <ingo@intevation.de>
+
+	flys/issue393 (Themenstileditor: Minimum anzeigen / Beschriftung anzeigen)
+
+	* src/main/java/de/intevation/flys/exports/EnhancedLineAndShapeRenderer.java:
+	  New renderer that overrides JFreeChart's XYLineAndShapeRenderer. This
+	  renderer brings the option to explicitly display the minimum and/or
+	  maximum or a series as shape. Currently, there are no options to adjust
+	  the style of those shapes.
+
+	* doc/conf/themes.xml: Added a new virtual theme 'MinMaxPoints'. All line
+	  theme inherit from that theme now.
+
+	* src/main/java/de/intevation/flys/utils/ThemeUtil.java: Added methods to
+	  parse the fields 'showminimum' and 'showmaximum' of 'MinMaxPoints' theme.
+
+	* src/main/java/de/intevation/flys/exports/StyledXYSeries.java: Added
+	  methods to apply the fields of the new Theme 'MinMaxPoints'.
+
+	* src/main/java/de/intevation/flys/exports/XYChartGenerator.java: Modified
+	  the getRenderer() method which now always returns a new instance of
+	  EnhancedLineAndShapeRenderer.
+
 2011-01-02	Felix Wolfsteller	<felix.wolfsteller@intevation.de>
 
 	Allow styling of outline of areas.
--- a/flys-artifacts/doc/conf/themes.xml	Tue Jan 03 13:14:42 2012 +0000
+++ b/flys-artifacts/doc/conf/themes.xml	Wed Jan 04 12:24:35 2012 +0000
@@ -4,6 +4,7 @@
     <theme name="DischargeCurve">
         <inherits>
             <inherit from="HiddenColorLines"/>
+            <inherit from="MinMaxPoints"/>
         </inherits>
         <fields>
             <field name="linecolor" type="Color" display="Linienfarbe" default="0, 0, 153"/>
@@ -17,6 +18,7 @@
     <theme name="LongitudinalSectionW">
         <inherits>
             <inherit from="HiddenColorLines"/>
+            <inherit from="MinMaxPoints"/>
         </inherits>
         <fields>
             <field name="linecolor" type="Color" display="Linienfarbe" default="204, 204, 204"/>
@@ -328,6 +330,7 @@
     <theme name="LongitudinalSectionQ">
         <inherits>
             <inherit from="HiddenColorLines"/>
+            <inherit from="MinMaxPoints"/>
         </inherits>
         <fields>
             <field name="linecolor" type="Color" display="Linienfarbe" default="204, 204, 204"/>
@@ -503,6 +506,7 @@
     <theme name="ComputedDischargeCurve">
         <inherits>
             <inherit from="HiddenColorLines"/>
+            <inherit from="MinMaxPoints"/>
         </inherits>
         <fields>
             <field name="linecolor" type="Color" display="Linienfarbe" default="0, 0, 153"/>
@@ -513,6 +517,7 @@
     <theme name="ComputedDischargeCurveQ">
         <inherits>
             <inherit from="HiddenColorLines"/>
+            <inherit from="MinMaxPoints"/>
             <inherit from="Text"/>
         </inherits>
         <fields>
@@ -523,6 +528,7 @@
     <theme name="ComputedDischargeCurveW">
         <inherits>
             <inherit from="HiddenColorLines"/>
+            <inherit from="MinMaxPoints"/>
             <inherit from="Text"/>
         </inherits>
         <fields>
@@ -536,6 +542,7 @@
     <theme name="CrossSection">
         <inherits>
             <inherit from="HiddenColorLines"/>
+            <inherit from="MinMaxPoints"/>
         </inherits>
         <fields>
             <field name="linecolor" type="Color" display="Linienfarbe" default="0,0,0"/>
@@ -560,6 +567,7 @@
     <theme name="DurationCurveW">
         <inherits>
             <inherit from="HiddenColorLines"/>
+            <inherit from="MinMaxPoints"/>
         </inherits>
         <fields>
             <field name="linecolor" type="Color" display="Linienfarbe" default="0,51,204"/>
@@ -570,6 +578,7 @@
     <theme name="DurationCurveQ">
         <inherits>
             <inherit from="HiddenColorLines"/>
+            <inherit from="MinMaxPoints"/>
         </inherits>
         <fields>
             <field name="linecolor" type="Color" display="Linienfarbe" default="0,204,0"/>
@@ -581,6 +590,7 @@
     <theme name="Differences">
         <inherits>
             <inherit from="HiddenColorLines"/>
+            <inherit from="MinMaxPoints"/>
         </inherits>
         <fields>
             <field name="linecolor" type="Color" display="Linienfarbe" default="204, 204, 204"/>
@@ -590,15 +600,21 @@
 
     <!-- General -->
     <theme name="WKms">
-       <inherits><inherit from="HiddenColorLines"/></inherits>
+        <inherits>
+            <inherit from="HiddenColorLines"/>
+            <inherit from="MinMaxPoints"/>
+        </inherits>
        <fields>
          <field name="linecolor" type="Color" display="Linienfarbe" default="204, 204, 204"/>
        </fields>
     </theme>
 
     <theme name="WQKms">
-       <inherits><inherit from="HiddenColorLines"/></inherits>
-       <fields>
+        <inherits>
+            <inherit from="HiddenColorLines"/>
+            <inherit from="MinMaxPoints"/>
+        </inherits>
+        <fields>
          <field name="linecolor" type="Color" display="Linienfarbe" default="204, 204, 204"/>
        </fields>
     </theme>
@@ -618,6 +634,7 @@
     <theme name="DischargeLongitudinalSectionW">
         <inherits>
             <inherit from="HiddenColorLines"/>
+            <inherit from="MinMaxPoints"/>
         </inherits>
         <fields>
             <field name="linecolor" type="Color" display="Linienfarbe" default="204, 204, 204"/>
@@ -628,6 +645,7 @@
     <theme name="DischargeLongitudinalSectionC">
         <inherits>
             <inherit from="HiddenColorLines"/>
+            <inherit from="MinMaxPoints"/>
         </inherits>
         <fields>
             <field name="linecolor" type="Color" display="Linienfarbe" default="255, 0 , 0"/>
@@ -637,6 +655,7 @@
     <theme name="DischargeLongitudinalSectionQ">
         <inherits>
             <inherit from="HiddenColorLines"/>
+            <inherit from="MinMaxPoints"/>
         </inherits>
         <fields>
             <field name="linecolor" type="Color" display="Linienfarbe" default="204, 204, 204"/>
@@ -706,6 +725,13 @@
         </fields>
     </theme>
 
+    <theme name="MinMaxPoints" type="virtual">
+        <fields>
+            <field name="showminimum" type="boolean" display="Minimum anzeigen"    default="false" hints="h"/>
+            <field name="showmaximum" type="boolean" display="Minimum anzeigen"    default="false" hints="h"/>
+        </fields>
+    </theme>
+
     <theme name="Text" type="virtual">
         <fields>
             <field name="font"      type="Font"  display="Schriftart"      default="arial"/>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/exports/EnhancedLineAndShapeRenderer.java	Wed Jan 04 12:24:35 2012 +0000
@@ -0,0 +1,213 @@
+package de.intevation.flys.exports;
+
+import java.awt.Graphics2D;
+import java.awt.Shape;
+import java.awt.geom.Rectangle2D;
+
+import org.apache.log4j.Logger;
+
+import org.jfree.chart.axis.ValueAxis;
+import org.jfree.chart.entity.EntityCollection;
+import org.jfree.chart.plot.CrosshairState;
+import org.jfree.chart.plot.PlotOrientation;
+import org.jfree.chart.plot.XYPlot;
+import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
+import org.jfree.data.xy.XYDataset;
+import org.jfree.ui.RectangleEdge;
+import org.jfree.util.BooleanList;
+import org.jfree.util.ShapeUtilities;
+
+
+public class EnhancedLineAndShapeRenderer extends XYLineAndShapeRenderer {
+
+    private static final Logger logger =
+        Logger.getLogger(EnhancedLineAndShapeRenderer.class);
+
+    protected BooleanList isMinimumShapeVisible;
+    protected BooleanList isMaximumShapeVisible;
+
+
+    public EnhancedLineAndShapeRenderer(boolean lines, boolean shapes) {
+        super(lines, shapes);
+        this.isMinimumShapeVisible = new BooleanList();
+        this.isMaximumShapeVisible = new BooleanList();
+    }
+
+
+    public boolean getItemShapeVisible(XYDataset dataset, int series, int item){
+        if (super.getItemShapeVisible(series, item)) {
+            logger.debug("Items are visible.");
+            return true;
+        }
+
+        if (isMinimumShapeVisible(series) && isMinimum(dataset, series, item)) {
+            logger.debug("Minimum is visible for series: " + series);
+            return true;
+        }
+
+        if (isMaximumShapeVisible(series) && isMaximum(dataset, series, item)) {
+            logger.debug("Maximum is visible for series: " + series);
+            return true;
+        }
+
+        logger.debug("Nothing is visible at all.");
+
+        return false;
+    }
+
+
+    /**
+     * Overrides XYLineAndShapeRenderer.drawSecondaryPass() to call an adapted
+     * method getItemShapeVisible() which now takes an XYDataset. So, 99% of
+     * code equal the code in XYLineAndShapeRenderer.
+     */
+    @Override
+    protected void drawSecondaryPass(
+        Graphics2D       g2,
+        XYPlot           plot,
+        XYDataset        dataset,
+        int              pass,
+        int              series,
+        int              item,
+        ValueAxis        domainAxis,
+        Rectangle2D      dataArea,
+        ValueAxis        rangeAxis,
+        CrosshairState   crosshairState,
+        EntityCollection entities
+    ) {
+        logger.debug("Draw secondary pass");
+        Shape entityArea = null;
+
+        // get the data point...
+        double x1 = dataset.getXValue(series, item);
+        double y1 = dataset.getYValue(series, item);
+        if (Double.isNaN(y1) || Double.isNaN(x1)) {
+            return;
+        }
+
+        PlotOrientation orientation = plot.getOrientation();
+        RectangleEdge xAxisLocation = plot.getDomainAxisEdge();
+        RectangleEdge yAxisLocation = plot.getRangeAxisEdge();
+        double transX1 = domainAxis.valueToJava2D(x1, dataArea, xAxisLocation);
+        double transY1 = rangeAxis.valueToJava2D(y1, dataArea, yAxisLocation);
+
+        if (getItemShapeVisible(dataset, series, item)) {
+            Shape shape = getItemShape(series, item);
+            if (orientation == PlotOrientation.HORIZONTAL) {
+                shape = ShapeUtilities.createTranslatedShape(shape, transY1,
+                        transX1);
+            }
+            else if (orientation == PlotOrientation.VERTICAL) {
+                shape = ShapeUtilities.createTranslatedShape(shape, transX1,
+                        transY1);
+            }
+            entityArea = shape;
+            if (shape.intersects(dataArea)) {
+                if (getItemShapeFilled(series, item)) {
+                    if (getUseFillPaint()) {
+                        g2.setPaint(getItemFillPaint(series, item));
+                    }
+                    else {
+                        g2.setPaint(getItemPaint(series, item));
+                    }
+                    g2.fill(shape);
+                }
+                if (getDrawOutlines()) {
+                    if (getUseOutlinePaint()) {
+                        g2.setPaint(getItemOutlinePaint(series, item));
+                    }
+                    else {
+                        g2.setPaint(getItemPaint(series, item));
+                    }
+                    g2.setStroke(getItemOutlineStroke(series, item));
+                    g2.draw(shape);
+                }
+            }
+        }
+
+        double xx = transX1;
+        double yy = transY1;
+        if (orientation == PlotOrientation.HORIZONTAL) {
+            xx = transY1;
+            yy = transX1;
+        }
+
+        // draw the item label if there is one...
+        if (isItemLabelVisible(series, item)) {
+            drawItemLabel(g2, orientation, dataset, series, item, xx, yy,
+                    (y1 < 0.0));
+        }
+
+        int domainAxisIndex = plot.getDomainAxisIndex(domainAxis);
+        int rangeAxisIndex = plot.getRangeAxisIndex(rangeAxis);
+        updateCrosshairValues(crosshairState, x1, y1, domainAxisIndex,
+                rangeAxisIndex, transX1, transY1, orientation);
+
+        // add an entity for the item, but only if it falls within the data
+        // area...
+        if (entities != null && isPointInRect(dataArea, xx, yy)) {
+            addEntity(entities, entityArea, dataset, series, item, xx, yy);
+        }
+    }
+
+
+    public void setIsMinimumShapeVisisble(int series, boolean isVisible) {
+        this.isMinimumShapeVisible.setBoolean(series, isVisible);
+    }
+
+
+    public boolean isMinimumShapeVisible(int series) {
+        return isMinimumShapeVisible.getBoolean(series);
+    }
+
+
+    public void setIsMaximumShapeVisible(int series, boolean isVisible) {
+        this.isMaximumShapeVisible.setBoolean(series, isVisible);
+    }
+
+
+    public boolean isMaximumShapeVisible(int series) {
+        return isMaximumShapeVisible.getBoolean(series);
+    }
+
+
+    public static boolean isMinimum(XYDataset dataset, int series, int item) {
+        return dataset.getYValue(series, item) == getMinimum(dataset, series);
+    }
+
+
+    public static double getMinimum(XYDataset dataset, int series) {
+        double min = Double.MAX_VALUE;
+
+        for (int i = 0, n = dataset.getItemCount(series); i < n; i++) {
+            double tmpValue = dataset.getYValue(series, i);
+
+            if (tmpValue < min) {
+                min = tmpValue;
+            }
+        }
+
+        return min;
+    }
+
+
+    public static boolean isMaximum(XYDataset dataset, int series, int item) {
+        return dataset.getYValue(series, item) == getMaximum(dataset, series);
+    }
+
+
+    public static double getMaximum(XYDataset dataset, int series) {
+        double max = -Double.MAX_VALUE;
+
+        for (int i = 0, n = dataset.getItemCount(series); i < n; i++) {
+            double tmpValue = dataset.getYValue(series, i);
+
+            if (tmpValue > max) {
+                max = tmpValue;
+            }
+        }
+
+        return max;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/exports/StyledXYSeries.java	Tue Jan 03 13:14:42 2012 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/exports/StyledXYSeries.java	Wed Jan 04 12:24:35 2012 +0000
@@ -53,6 +53,8 @@
         applyLineType(r, idx);
         applyShowLine(r, idx);
         applyShowPoints(r, idx);
+        applyShowMinimum(r, idx);
+        applyShowMaximum(r, idx);
 
         return r;
     }
@@ -112,5 +114,29 @@
         boolean show = ThemeUtil.parseShowLine(theme);
         r.setSeriesLinesVisible(idx, show);
     }
+
+
+    protected void applyShowMinimum(XYLineAndShapeRenderer r, int idx) {
+        if (!(r instanceof EnhancedLineAndShapeRenderer)) {
+            return;
+        }
+
+        boolean visible = ThemeUtil.parseShowMinimum(theme);
+
+        EnhancedLineAndShapeRenderer er = (EnhancedLineAndShapeRenderer) r;
+        er.setIsMinimumShapeVisisble(idx, visible);
+    }
+
+
+    protected void applyShowMaximum(XYLineAndShapeRenderer r, int idx) {
+        if (!(r instanceof EnhancedLineAndShapeRenderer)) {
+            return;
+        }
+
+        boolean visible = ThemeUtil.parseShowMaximum(theme);
+
+        EnhancedLineAndShapeRenderer er = (EnhancedLineAndShapeRenderer) r;
+        er.setIsMaximumShapeVisible(idx, visible);
+    }
 }
 // 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	Tue Jan 03 13:14:42 2012 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/exports/XYChartGenerator.java	Wed Jan 04 12:24:35 2012 +0000
@@ -1223,42 +1223,12 @@
 
 
     /**
-     * Get renderer, from plot or cloned default renderer otherwise.
+     * Returns a new instance of EnhancedLineAndShapeRenderer always.
      */
     protected XYLineAndShapeRenderer getRenderer(XYPlot plot, int idx) {
         logger.debug("getRenderer: " + idx);
 
-        XYLineAndShapeRenderer r =
-            (XYLineAndShapeRenderer) plot.getRenderer(idx);
-
-        if (r != null) {
-            return r;
-        }
-
-        // Need a new renderer.
-        if (idx == 0) {
-            logger.warn("No default renderer set!");
-            return new XYLineAndShapeRenderer();
-        }
-
-        // 'Default' (first) renderer is an area-renderer.
-        XYItemRenderer renderer = (XYItemRenderer) plot.getRenderer(0);
-        if (renderer instanceof StableXYDifferenceRenderer) {
-            return new XYLineAndShapeRenderer();
-        }
-
-        r = (XYLineAndShapeRenderer) renderer;
-
-        try {
-            return (XYLineAndShapeRenderer) r.clone();
-        }
-        catch (CloneNotSupportedException cnse) {
-            logger.warn(cnse, cnse);
-        }
-
-        logger.warn("No applicalable renderer found!");
-
-        return new XYLineAndShapeRenderer();
+        return new EnhancedLineAndShapeRenderer(true, false);
     }
 
 
--- a/flys-artifacts/src/main/java/de/intevation/flys/utils/ThemeUtil.java	Tue Jan 03 13:14:42 2012 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/utils/ThemeUtil.java	Wed Jan 04 12:24:35 2012 +0000
@@ -71,6 +71,12 @@
     public final static String XPATH_SYMBOL =
         "/theme/field[@name='symbol']/@default";
 
+    public final static String XPATH_SHOW_MINIMUM =
+        "/theme/field[@name='showminimum']/@default";
+
+    public final static String XPATH_SHOW_MAXIMUM =
+        "/theme/field[@name='showmaximum']/@default";
+
 
     /** Parse string to be boolean with default if empty or unrecognized. */
     public static boolean parseBoolean(String value, boolean defaultsTo) {
@@ -326,6 +332,16 @@
     }
 
 
+    public static String getShowMinimum(Document theme) {
+        return XMLUtils.xpathString(theme, XPATH_SHOW_MINIMUM, null);
+    }
+
+
+    public static String getShowMaximum(Document theme) {
+        return XMLUtils.xpathString(theme, XPATH_SHOW_MAXIMUM, null);
+    }
+
+
     /**
      * Gets color from color field.
      * @param theme    the theme document.
@@ -353,6 +369,15 @@
     }
 
 
+    public static boolean parseShowMinimum(Document theme) {
+        return parseBoolean(getShowMinimum(theme), false);
+    }
+
+    public static boolean parseShowMaximum(Document theme) {
+        return parseBoolean(getShowMaximum(theme), false);
+    }
+
+
     public static String createMapserverStyle(Document theme) {
         String symbol    = getSymbol(theme);
         String backcolor = getBackgroundColorString(theme);

http://dive4elements.wald.intevation.org