teichmann@5863: /* Copyright (C) 2011, 2012, 2013 by Bundesanstalt für Gewässerkunde teichmann@5863: * Software engineering by Intevation GmbH teichmann@5863: * teichmann@5994: * This file is Free Software under the GNU AGPL (>=v3) teichmann@5863: * and comes with ABSOLUTELY NO WARRANTY! Check out the teichmann@5994: * documentation coming with Dive4Elements River for details. teichmann@5863: */ teichmann@5863: teichmann@5831: package org.dive4elements.river.jfree; ingo@2074: ingo@3227: import java.awt.BasicStroke; ingo@2074: import java.awt.Color; gernotbelger@8910: import java.awt.Paint; ingo@2074: import java.awt.Stroke; gernotbelger@8910: import java.awt.TexturePaint; gernotbelger@8910: import java.awt.geom.Ellipse2D; gernotbelger@8910: import java.awt.geom.Rectangle2D; gernotbelger@8910: import java.awt.image.BufferedImage; ingo@2074: ingo@3227: import org.jfree.data.xy.XYSeriesCollection; ingo@2074: teichmann@6905: import org.dive4elements.river.themes.ThemeDocument; ingo@2074: ingo@2074: /** ingo@2074: * One or more dataseries to draw a polygon (either "open up/downwards", or ingo@2074: * the area between two curves), a theme-document and further display options. ingo@2074: * The theme-document will later "style" the graphical representation. sascha@3076: * The display options can be used to control the z-order and the axis of the ingo@2074: * dataset. ingo@2074: */ gernotbelger@8910: // FIXME: bad abstraction: the only purpose of this derivation is to apply specific styles. This should rather be solved similar to the XYSTyle. ingo@2074: public class StyledAreaSeriesCollection extends XYSeriesCollection { christian@3889: private static final long serialVersionUID = 5274940965666948237L; christian@3889: ingo@2074: /** Mode, how to draw/which areas to fill. */ gernotbelger@8885: public enum FILL_MODE {UNDER, ABOVE, BETWEEN} ingo@2074: ingo@2074: /** MODE in use. */ gernotbelger@8885: private FILL_MODE mode; ingo@2074: tom@8856: /** Theme-document with attributes about actual visual representation. */ gernotbelger@8885: private ThemeDocument theme; ingo@2074: ingo@2074: /** ingo@2074: * @param theme the theme-document. ingo@2074: */ teichmann@6905: public StyledAreaSeriesCollection(ThemeDocument theme) { ingo@2074: this.theme = theme; ingo@2074: this.mode = FILL_MODE.BETWEEN; ingo@2074: } ingo@2074: ingo@2074: ingo@2074: /** Gets the Fill mode. */ ingo@2074: public FILL_MODE getMode() { ingo@2074: return this.mode; ingo@2074: } ingo@2074: ingo@2074: ingo@2074: /** Sets the Fill mode. */ ingo@2074: public void setMode(FILL_MODE fMode) { ingo@2074: this.mode = fMode; ingo@2074: } ingo@2074: ingo@2074: ingo@2074: /** ingo@2074: * Applies line color, size and type attributes to renderer, also ingo@2074: * whether to draw lines and/or points. felix@3395: * @param renderer Renderer to apply theme to. felix@3395: * @return \param renderer ingo@2074: */ ingo@2074: public StableXYDifferenceRenderer applyTheme( ingo@2074: StableXYDifferenceRenderer renderer ingo@2074: ) { ingo@2074: applyFillColor(renderer); ingo@2074: applyShowShape(renderer); ingo@2074: applyOutlineColor(renderer); ingo@2074: applyOutlineStyle(renderer); gernotbelger@8885: applyShowLine(renderer); aheinecke@7119: applyShowAreaLabel(renderer); gernotbelger@8910: applyPointStyle(renderer); felix@2666: if (mode == FILL_MODE.UNDER) { tom@8856: renderer.setAreaCalculationMode( tom@8856: StableXYDifferenceRenderer.CALCULATE_NEGATIVE_AREA); felix@2666: } felix@2666: else if (mode == FILL_MODE.ABOVE) { tom@8856: renderer.setAreaCalculationMode( tom@8856: StableXYDifferenceRenderer.CALCULATE_POSITIVE_AREA); felix@2666: } felix@2666: else { tom@8856: renderer.setAreaCalculationMode( tom@8856: StableXYDifferenceRenderer.CALCULATE_ALL_AREA); felix@2666: } felix@2667: felix@2667: // Apply text style. teichmann@6906: theme.parseComplexTextStyle().apply(renderer); ingo@2074: return renderer; ingo@2074: } ingo@2074: gernotbelger@8910: private void applyFillColor(final StableXYDifferenceRenderer renderer) { gernotbelger@8910: gernotbelger@8910: final boolean showArea = theme.parseShowArea(); gernotbelger@8910: if( !showArea ) { gernotbelger@8910: renderer.setPositivePaint(null); gernotbelger@8910: renderer.setNegativePaint(null); gernotbelger@8910: return; ingo@2074: } christian@3889: gernotbelger@8910: Paint paint = parseFillPaint(); gernotbelger@8910: ingo@2074: if (paint != null && this.getMode() == FILL_MODE.ABOVE) { ingo@2074: renderer.setPositivePaint(paint); ingo@2074: renderer.setNegativePaint(new Color(0,0,0,0)); ingo@2074: } ingo@2074: else if (paint != null && this.getMode() == FILL_MODE.UNDER) { ingo@2074: renderer.setNegativePaint(paint); ingo@2074: renderer.setPositivePaint(new Color(0,0,0,0)); ingo@2074: } ingo@2074: else { christian@3889: if (paint == null) christian@3889: paint = new Color(177, 117, 102); gernotbelger@8910: ingo@2074: renderer.setPositivePaint(paint); ingo@2074: renderer.setNegativePaint(paint); ingo@2074: } ingo@2074: } ingo@2074: gernotbelger@8910: private Paint parseFillPaint() { gernotbelger@8910: final Color paint = this.theme.parseAreaBackgroundColor(); gernotbelger@8910: final int transparency = theme.parseAreaTransparency(); gernotbelger@8910: gernotbelger@8910: final Color alphaPaint = withAlpha(paint, transparency); gernotbelger@8910: gernotbelger@8910: final AreaFillPattern pattern = this.theme.parseAreaBackgroundPattern(); gernotbelger@8910: gernotbelger@8913: if( pattern == null || pattern == AreaFillPattern.patternFill ) gernotbelger@8910: return alphaPaint; gernotbelger@8910: gernotbelger@8910: final BufferedImage image = pattern.getImage(alphaPaint); gernotbelger@8910: gernotbelger@8910: final Rectangle2D anchor = new Rectangle2D.Double(0,0, image.getWidth(), image.getHeight()); gernotbelger@8910: return new TexturePaint(image, anchor); gernotbelger@8910: } gernotbelger@8910: gernotbelger@8910: private Color withAlpha(final Color color, final int transparency) { gernotbelger@8910: gernotbelger@8910: if (transparency <= 0 || color == null) gernotbelger@8910: return color; gernotbelger@8910: gernotbelger@8910: return new Color( gernotbelger@8910: color.getRed(), gernotbelger@8910: color.getGreen(), gernotbelger@8910: color.getBlue(), gernotbelger@8910: (int)((100 - transparency) * 2.55f)); gernotbelger@8910: } christian@3889: gernotbelger@8885: private void applyShowShape(StableXYDifferenceRenderer renderer) { teichmann@6905: boolean show = theme.parseAreaShowBorder(); ingo@2074: renderer.setDrawOutline(show); ingo@2074: } ingo@2074: felix@3395: gernotbelger@8885: private void applyShowLine(StableXYDifferenceRenderer renderer) { gernotbelger@8910: /* FIXME: strange: this will enable/disable showing the 'point' shapes at each vertex. */ gernotbelger@8910: /* FIXME: this will also now be overridden by the option 'showpoints' */ gernotbelger@8910: final boolean show = theme.parseShowLine(); ingo@2074: renderer.setShapesVisible(show); ingo@2074: } ingo@2074: gernotbelger@8885: private void applyOutlineColor(StableXYDifferenceRenderer renderer) { teichmann@6905: Color c = theme.parseLineColorField(); ingo@2074: renderer.setOutlinePaint(c); ingo@2074: } ingo@2074: felix@2666: /** Inform renderer whether it should draw a label. */ gernotbelger@8885: private void applyShowAreaLabel(StableXYDifferenceRenderer renderer) { aheinecke@7119: renderer.setLabelArea(theme.parseShowAreaLabel()); felix@2666: } felix@2666: gernotbelger@8885: private void applyOutlineStyle(StableXYDifferenceRenderer renderer) { teichmann@6905: float[] dashes = theme.parseLineStyle(); teichmann@6905: int size = theme.parseLineWidth(); ingo@2074: ingo@2074: Stroke stroke = null; ingo@2074: ingo@2074: if (dashes.length <= 1) { ingo@2074: stroke = new BasicStroke(Integer.valueOf(size)); ingo@2074: } ingo@2074: else { ingo@2074: stroke = new BasicStroke(Integer.valueOf(size), ingo@2074: BasicStroke.CAP_BUTT, ingo@2074: BasicStroke.JOIN_ROUND, ingo@2074: 1.0f, ingo@2074: dashes, ingo@2074: 0.0f); ingo@2074: } ingo@2074: ingo@2074: renderer.setOutlineStroke(stroke); ingo@2074: } gernotbelger@8885: gernotbelger@8910: private void applyPointStyle(final StableXYDifferenceRenderer renderer) { gernotbelger@8910: gernotbelger@8910: final boolean showPoints = this.theme.parseShowPoints(); gernotbelger@8910: renderer.setShapesVisible(showPoints); gernotbelger@8910: gernotbelger@8910: if( showPoints ) gernotbelger@8910: { gernotbelger@8910: final int size = theme.parsePointWidth(); gernotbelger@8910: final int dim = 2 * size; gernotbelger@8910: gernotbelger@8910: final Ellipse2D pointShape = new Ellipse2D.Double(-size, -size, dim, dim); gernotbelger@8910: final Color pointColor = theme.parsePointColor(); gernotbelger@8910: gernotbelger@8910: renderer.setSeriesPaint(0, pointColor); gernotbelger@8910: renderer.setSeriesPaint(1, pointColor); gernotbelger@8910: gernotbelger@8910: renderer.setSeriesShape(0, pointShape); gernotbelger@8910: renderer.setSeriesShape(1, pointShape); gernotbelger@8910: } gernotbelger@8910: } gernotbelger@8910: gernotbelger@8885: public boolean shouldCalculateRange() { gernotbelger@8885: return theme.parseCalculateRange(); gernotbelger@8885: } ingo@2074: } ingo@2074: // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :