Mercurial > dive4elements > gnv-client
diff gnv-artifacts/src/main/java/de/intevation/gnv/jfreechart/PolygonRenderer.java @ 1119:7c4f81f74c47
merged gnv-artifacts
author | Thomas Arendsen Hein <thomas@intevation.de> |
---|---|
date | Fri, 28 Sep 2012 12:14:00 +0200 |
parents | f953c9a559d8 |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gnv-artifacts/src/main/java/de/intevation/gnv/jfreechart/PolygonRenderer.java Fri Sep 28 12:14:00 2012 +0200 @@ -0,0 +1,312 @@ +/* + * Copyright (c) 2010 by Intevation GmbH + * + * This program is free software under the LGPL (>=v2.1) + * Read the file LGPL.txt coming with the software for details + * or visit http://www.gnu.org/licenses/ if it does not exist. + */ + +package de.intevation.gnv.jfreechart; + +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.Graphics2D; +import java.awt.Paint; +import java.awt.Shape; + +import java.awt.geom.GeneralPath; + +import java.awt.geom.Rectangle2D.Double; + +import java.awt.geom.Rectangle2D; + +import java.util.ArrayList; + +import org.apache.log4j.Logger; + +import org.jfree.chart.axis.ValueAxis; + +import org.jfree.data.Range; + +import org.jfree.text.TextUtilities; + +import org.jfree.ui.RectangleEdge; + +/** + * This renderer is used to draw polygons into a Graphics object. + * + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + * @author <a href="mailto:sascha.teichmann@intevation.de">Sascha L. Teichmann</a> + */ +public class PolygonRenderer +{ + private static Logger log = Logger.getLogger( + PolygonRenderer.class); + + /** + * This interfaces describes a single method to retrieve a Paint object + * for a given index. + */ + public interface PaintLookup { + + /** + * + * @param index Index. + * @return Paint + */ + Paint getPaint(int index); + + } // interface PaintLookup + + /** + * This class is used to generate labels for a given series. + */ + public static class DefaultLabelGenerator + implements PolygonSeriesLabelGenerator + { + /** + * Construts an empty DefaultLabelGenerator. + */ + public DefaultLabelGenerator() { + } + + /** + * + * @param series A PolygonSeries. + * @return The label of series. + */ + public String generateLabel(PolygonSeries series) { + Object label = series.getAttribute("label"); + return label != null + ? toString(label) + : null; + } + + /** + * + * @param label Object + * @return String representaton of label. + */ + protected String toString(Object label) { + return label.toString(); + } + } // class DefaultLabelGenerator + + /** + * Constructor. + */ + public static final PolygonSeriesLabelGenerator + DEFAULT_LABEL_GENERATOR_INSTANCE = new DefaultLabelGenerator(); + + protected PaintLookup lookup; + + protected PolygonSeriesLabelGenerator labelGenerator; + + + public PolygonRenderer(PaintLookup lookup) { + this(lookup, null); + } + + + public PolygonRenderer( + PaintLookup lookup, + PolygonSeriesLabelGenerator labelGenerator + ) { + this.lookup = lookup; + this.labelGenerator = labelGenerator; + } + + /** + * This method draws polygons of each series in <code>dataset</code> into + * the given graphics object. If a polygon has no attribute 'fill', we + * expect that it is a line, otherwise the polygon is filled. + * + */ + public void drawPolygons( + Graphics2D graphics, + PolygonPlot plot, + Rectangle2D area, + PolygonDataset dataset + ) { + int seriesCount = dataset.getSeriesCount(); + for (int i = 0; i < seriesCount; i++) { + PolygonSeries series = dataset.getSeries(i); + Integer colorIdx = (Integer)series.getAttribute("fill"); + + if (colorIdx != null) { + Paint paint = lookup.getPaint(colorIdx.intValue()); + graphics.setPaint(paint != null ? paint : Color.black); + graphics.fill(constructShape(plot, area, series, true)); + } + else { + Number lineWidth = (Number)series.getAttribute("line.width"); + BasicStroke stroke = new BasicStroke( + lineWidth != null ? lineWidth.floatValue() : 1f); + graphics.setStroke(stroke); + graphics.setPaint(Color.black); + graphics.draw(constructShape(plot, area, series, false)); + } + } + } + + /** + * Draw labels at each item of a series in the given dataset. If the series + * has no label attritue, no label is drawn. + * + */ + public void drawLabels( + final Graphics2D graphics, + final PolygonPlot plot, + final Rectangle2D area, + PolygonDataset dataset + ) { + if (labelGenerator == null) { + return; + } + + final ArrayList<Rectangle2D> bboxes = new ArrayList<Rectangle2D>(); + + Font font = graphics.getFont(); + font = font.deriveFont(Font.PLAIN, Math.max(8, font.getSize()-3)); + graphics.setFont(font); + FontMetrics metrics = graphics.getFontMetrics(font); + + for (int i = dataset.getSeriesCount()-1; i >= 0; --i) { + PolygonSeries series = dataset.getSeries(i); + + String label = labelGenerator.generateLabel(series); + if (label == null) { + continue; + } + + final Rectangle2D box = TextUtilities.getTextBounds( + label, graphics, metrics); + + for (int j = series.getItemCount()-1; j >= 0; --j) { + final CompactXYItems ring = series.getItem(j); + LevelOrderIndices loi = new LevelOrderIndices(ring.size()-1); + Rectangle2D r = (Rectangle2D)loi.visit( + new LevelOrderIndices.Visitor() + { + ValueAxis da = plot.getDomainAxis(); + ValueAxis ra = plot.getRangeAxis(); + RectangleEdge de = plot.getDomainAxisEdge(); + RectangleEdge re = plot.getRangeAxisEdge(); + Rectangle2D.Double r = new Rectangle2D.Double( + 0d, 0d, box.getWidth(), box.getHeight()); + + public Object visit(int index) { + r.x = da.valueToJava2D(ring.getX(index), area, de) + - 0.5*box.getWidth(); + r.y = ra.valueToJava2D(ring.getY(index), area, re) + + 0.5*box.getHeight(); + + for (Rectangle2D b: bboxes) { + if (b.intersects(r)) { + return null; + } + } + return r; + } + }); + + if (r != null) { + bboxes.add(r); + graphics.drawString( + label, (float)r.getX(), (float)r.getY()); + } + } // for all items in series + } // for all series + } + + /** + * Creates a shape made up of the CompactXYItems object stored in the given + * series. + * + * @param plot The plot. + * @param area The boundary. + * @param series The series storing the items. + * @param close Specifies if the polygon should be closed or not. + * @return the constructed shape. + */ + protected Shape constructShape( + PolygonPlot plot, + Rectangle2D area, + PolygonSeries series, + boolean close + ) { + ValueAxis da = plot.getDomainAxis(); + ValueAxis ra = plot.getRangeAxis(); + RectangleEdge de = plot.getDomainAxisEdge(); + RectangleEdge re = plot.getRangeAxisEdge(); + + CompactXYItems [] rings = series.getRings(); + GeneralPath path = new GeneralPath(); + + for (int i = 0; i < rings.length; ++i) { + + CompactXYItems ring = rings[i]; + + double [] data = ring.getData(); + + if (data.length >= 2) { + path.moveTo( + (float)da.valueToJava2D(data[0], area, de), + (float)ra.valueToJava2D(data[1], area, re)); + } + for (int j = 2; j < data.length;) { + path.lineTo( + (float)da.valueToJava2D(data[j++], area, de), + (float)ra.valueToJava2D(data[j++], area, re)); + } + if (close) { + path.closePath(); + } + } + return path; + } + + /** + * Retrieves the bounding box of a dataset. + */ + public Rectangle2D getBoundingBox(PolygonDataset dataset) { + Rectangle2D bbox = null; + + for (int i = 0, N = dataset.getSeriesCount(); i < N; i++) { + Range domain = dataset.getSeries(i).getDomainBounds(); + Range range = dataset.getSeries(i).getRangeBounds(); + + double x = domain.getLowerBound(); + double y = range.getLowerBound(); + double w = Math.abs(domain.getUpperBound() - x); + double h = Math.abs(range.getUpperBound() - y); + + if (bbox == null) { + bbox = new Rectangle2D.Double(x, y, w, h); + } + else { + bbox.add(new Rectangle2D.Double(x, y, w, h)); + } + } + + return bbox; + } + + /** + * + * @return the bounds of a series. + */ + public Rectangle2D getBounds(PolygonSeries series) { + + Range domain = series.getDomainBounds(); + Range range = series.getRangeBounds(); + + return new Rectangle2D.Double( + domain.getLowerBound(), range.getLowerBound(), + domain.getUpperBound(), range.getUpperBound() + ); + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :