# HG changeset patch # User Ingo Weinzierl # Date 1325679875 0 # Node ID 4cdd9c4896f622d237c0c40353691bcf67691c3e # Parent fd95bfbb2ec238beda5b796ba5350a28adbe0c6a #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 diff -r fd95bfbb2ec2 -r 4cdd9c4896f6 flys-artifacts/ChangeLog --- 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 + + 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 Allow styling of outline of areas. diff -r fd95bfbb2ec2 -r 4cdd9c4896f6 flys-artifacts/doc/conf/themes.xml --- 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 @@ + @@ -17,6 +18,7 @@ + @@ -328,6 +330,7 @@ + @@ -503,6 +506,7 @@ + @@ -513,6 +517,7 @@ + @@ -523,6 +528,7 @@ + @@ -536,6 +542,7 @@ + @@ -560,6 +567,7 @@ + @@ -570,6 +578,7 @@ + @@ -581,6 +590,7 @@ + @@ -590,15 +600,21 @@ - + + + + - - + + + + + @@ -618,6 +634,7 @@ + @@ -628,6 +645,7 @@ + @@ -637,6 +655,7 @@ + @@ -706,6 +725,13 @@ + + + + + + + diff -r fd95bfbb2ec2 -r 4cdd9c4896f6 flys-artifacts/src/main/java/de/intevation/flys/exports/EnhancedLineAndShapeRenderer.java --- /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 : diff -r fd95bfbb2ec2 -r 4cdd9c4896f6 flys-artifacts/src/main/java/de/intevation/flys/exports/StyledXYSeries.java --- 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 : diff -r fd95bfbb2ec2 -r 4cdd9c4896f6 flys-artifacts/src/main/java/de/intevation/flys/exports/XYChartGenerator.java --- 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); } diff -r fd95bfbb2ec2 -r 4cdd9c4896f6 flys-artifacts/src/main/java/de/intevation/flys/utils/ThemeUtil.java --- 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);