Mercurial > dive4elements > river
diff flys-artifacts/src/main/java/de/intevation/flys/jfree/StickyAxisAnnotation.java @ 1190:f514894ec2fd
merged flys-artifacts/2.5
author | Thomas Arendsen Hein <thomas@intevation.de> |
---|---|
date | Fri, 28 Sep 2012 12:14:17 +0200 |
parents | f1c9bfb07ba7 |
children | f708120cb7bc |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/jfree/StickyAxisAnnotation.java Fri Sep 28 12:14:17 2012 +0200 @@ -0,0 +1,322 @@ +package de.intevation.flys.jfree; + +import org.apache.log4j.Logger; + +import java.util.Iterator; + +import java.awt.Shape; +import java.awt.geom.Rectangle2D; +import java.awt.geom.Line2D; + +import org.jfree.chart.annotations.XYTextAnnotation; +import org.jfree.chart.axis.ValueAxis; +import org.jfree.chart.util.LineUtilities; +import org.jfree.chart.plot.PlotOrientation; +import org.jfree.chart.plot.XYPlot; +import org.jfree.chart.entity.XYAnnotationEntity; +import org.jfree.chart.plot.PlotRenderingInfo; +import org.jfree.chart.ChartRenderingInfo; +import org.jfree.chart.plot.Plot; + +import org.jfree.data.Range; + +import org.jfree.text.TextUtilities; + +import org.jfree.ui.RectangleEdge; +import org.jfree.ui.TextAnchor; + + +/** + * Custom Annotations class that is drawn only if no collisions with other + * already drawn CustomAnnotations in current plot are found. + * Draws a given text and a line to it from either axis. + */ +public class StickyAxisAnnotation extends XYTextAnnotation { + + /** Logger for this class. */ + private static Logger logger = + Logger.getLogger(StickyAxisAnnotation.class); + + /** Simplified view on axes. */ + public static enum SimpleAxis { + X_AXIS, /** Usually "horizontal". */ + Y_AXIS /** Usually "vertical". */ + } + + /** Which axis to stick to. */ + protected SimpleAxis stickyAxis = SimpleAxis.X_AXIS; + + /** The 1-dimensional position of this annotation. */ + protected float pos; + + + /** + * Trivial constructor. + * + * @param text Text to display. + * @param x X-position in dataspace (typical horizontal, in km). + * @param y Y-position in dataspace (typical vertical, in m). + * @deprecated + */ + public StickyAxisAnnotation(String text, float x, float y) { + super(text, x, y); + setStickyAxis(SimpleAxis.X_AXIS); + } + + + /** + * Constructor with implicit sticky x-axis. + * @param text the text to display. + * @param pos the position at which to draw the text and mark. + */ + public StickyAxisAnnotation(String text, float pos) { + this(text, pos, pos, SimpleAxis.X_AXIS); + } + + + /** + * Constructor with given explicit axis. + * @param text the text to display. + * @param pos the position at which to draw the text and mark. + * @param stickyAxis the axis at which to stick (and to which 'pos' is + * relative). + */ + public StickyAxisAnnotation(String text, float pos, SimpleAxis stickAxis) { + super(text, pos, pos); + setStickyAxis(stickAxis); + this.pos = pos; + } + + /** + * Legacy-Constructor. + * @deprecated + */ + public StickyAxisAnnotation(String text, float x, float y, + SimpleAxis stickAxis) { + super(text, x, y); + setStickyAxis(stickAxis); + this.pos = x; + } + + + /** + * Sets the "sticky axis" (whether to draw annotations at the + * X- or the Y-Axis. + * + * @param stickyAxis axis to stick to. + */ + public void setStickyAxis(SimpleAxis stickyAxis) { + this.stickyAxis = stickyAxis; + if (stickyAxis == SimpleAxis.X_AXIS) { + this.setRotationAngle(270f * (Math.PI / 180f)); + this.setRotationAnchor(TextAnchor.CENTER_LEFT); + this.setTextAnchor(TextAnchor.CENTER_LEFT); + } else { + this.setRotationAngle(0f * (Math.PI / 180f)); + this.setRotationAnchor(TextAnchor.CENTER_LEFT); + this.setTextAnchor(TextAnchor.CENTER_LEFT); + } + } + + + /** + * Draws a small line at axis where this annotation resides. + * + * @param g2 the graphics device. + * @param dataArea the data area. + * @param domainAxis the domain axis. + * @param rangeAxis the range axis. + * @param domainEdge the domain edge. + * @param rangeEdge the range edge. + * @param orientation the plot orientation. + */ + protected void drawAxisMark( + java.awt.Graphics2D g2, + java.awt.geom.Rectangle2D dataArea, + ValueAxis domainAxis, + ValueAxis rangeAxis, + RectangleEdge domainEdge, + RectangleEdge rangeEdge, + PlotOrientation orientation) { + float j2DX1 = 0.0f; + float j2DX2 = 0.0f; + float j2DY1 = 0.0f; + float j2DY2 = 0.0f; + float x = (float) getX(); + float y = (float) getY(); + /* When dependent on X/Y-Axis and orientation, following + can be used as a base: + if (orientation == PlotOrientation.VERTICAL) { + j2DX1 = (float) domainAxis.valueToJava2D(x, dataArea, + domainEdge); + j2DY1 = (float) rangeAxis.valueToJava2D(y, dataArea, + rangeEdge); + j2DX2 = (float) domainAxis.valueToJava2D(x, dataArea, + domainEdge); + j2DY2 = (float) rangeAxis.valueToJava2D(y, dataArea, + rangeEdge); + } + else if (orientation == PlotOrientation.HORIZONTAL) { + j2DY1 = (float) domainAxis.valueToJava2D(x, dataArea, + domainEdge); + j2DX1 = (float) rangeAxis.valueToJava2D(y, dataArea, + rangeEdge); + j2DY2 = (float) domainAxis.valueToJava2D(x, dataArea, + domainEdge); + j2DX2 = (float) rangeAxis.valueToJava2D(y, dataArea, + rangeEdge); + } + + g2.setPaint(this.paint); + g2.setStroke(this.stroke); + */ + if (this.stickyAxis == SimpleAxis.X_AXIS) { + j2DY1 = (float) RectangleEdge.coordinate(dataArea, domainEdge); + double rangeLow = rangeAxis.getRange().getLowerBound(); + // Line ends at 1.5% of full distance. + j2DY2 = (float) rangeAxis.valueToJava2D( + (1f - 0.015f) * rangeLow + 0.015f * + rangeAxis.getRange().getUpperBound(), + dataArea, rangeEdge); + j2DX1 = (float) domainAxis.valueToJava2D(x, dataArea, domainEdge); + j2DX2 = j2DX1; + } else { + j2DX1 = (float) RectangleEdge.coordinate(dataArea, rangeEdge); + Range domainRange = domainAxis.getRange(); + double rangeLow = domainRange.getLowerBound(); + // Line ends at 1.5% of full distance. + j2DX2 = (float) domainAxis.valueToJava2D( + (1f - 0.015f) * rangeLow + 0.015f * + domainRange.getUpperBound(), + dataArea, domainEdge); + j2DY1 = (float) rangeAxis.valueToJava2D(pos, dataArea, rangeEdge); + j2DY2 = j2DY1; + } + + Line2D line = new Line2D.Float(j2DX1, j2DY1, j2DX2, j2DY2); + + // line is clipped to avoid JRE bug 6574155, for more info + // see JFreeChart bug 2221495 + boolean visible = LineUtilities.clipLine(line, dataArea); + if (visible) { + g2.draw(line); + } + } + + + /** + * Draw the Annotiation if it does not collide with other already drawn + * Annotations. + * + * @param g2 the graphics device. + * @param plot the plot. + * @param dataArea the data area. + * @param domainAxis the domain axis. + * @param rangeAxis the range axis. + * @param rendererIndex the render index. + * @param info state information, escpecially collects info about + * already drawn shapes (and thus annotations), used + * for collision detection. + */ + @Override + public void draw( + java.awt.Graphics2D g2, + XYPlot plot, + java.awt.geom.Rectangle2D dataArea, + ValueAxis domainAxis, + ValueAxis rangeAxis, + int rendererIndex, + PlotRenderingInfo info) { + + if (info == null) + return; + + if (domainAxis == null || rangeAxis == null + || domainAxis.getRange() == null + || rangeAxis.getRange() == null + ) { + logger.error("Annotation cannot be drawn (missing axis)."); + return; + } + + // Calculate the bounding box. + ChartRenderingInfo chartInfo = info.getOwner(); + + PlotOrientation orientation = plot.getOrientation(); + RectangleEdge domainEdge = Plot.resolveDomainAxisLocation( + plot.getDomainAxisLocation(), orientation); + RectangleEdge rangeEdge = Plot.resolveRangeAxisLocation( + plot.getRangeAxisLocation(), orientation); + float anchorX = 0f; + float anchorY = 0.0f; + if (this.stickyAxis == SimpleAxis.X_AXIS) { + // Text starts at 1.5% of full distance. + float rangeLow = (float) rangeAxis.getRange().getLowerBound(); + float y = rangeLow + 0.02f * ((float) + rangeAxis.getRange().getUpperBound() - rangeLow); + setY(y); + + anchorX = (float) domainAxis.valueToJava2D( + getX(), dataArea, domainEdge); + anchorY = (float) rangeAxis.valueToJava2D( + getY(), dataArea, rangeEdge); + } else { + float rangeLow = (float) domainAxis.getRange().getLowerBound(); + float x = rangeLow + 0.02f * ((float) + domainAxis.getRange().getUpperBound() - rangeLow); + setX(x); + anchorX = (float) domainAxis.valueToJava2D( + getX(), dataArea, domainEdge); + anchorY = (float) rangeAxis.valueToJava2D( + getY(), dataArea, rangeEdge); + } + if (orientation == PlotOrientation.HORIZONTAL) { + float tempAnchor = anchorX; + anchorX = anchorY; + anchorY = tempAnchor; + } + + // Always draw the small line at axis. + drawAxisMark(g2, dataArea, domainAxis, rangeAxis, domainEdge, + rangeEdge, orientation); + + g2.setFont(getFont()); + Shape hotspot = TextUtilities.calculateRotatedStringBounds( + getText(), g2, anchorX, anchorY, getTextAnchor(), + getRotationAngle(), getRotationAnchor()); + Rectangle2D hotspotBox = hotspot.getBounds2D(); + // Check for collisions with other XYAnnotations. + for (Iterator i = chartInfo.getEntityCollection().iterator(); + i.hasNext(); ) { + Object next = i.next(); + // Collision with other stuff than XYAnnotations are okay. + if (next instanceof XYAnnotationEntity) { + XYAnnotationEntity drawnShape = (XYAnnotationEntity) next; + if (drawnShape.getArea().intersects(hotspotBox)) { + // Found collision, early stop. + return; + } + } + } + + // Actuall drawing. + if (getBackgroundPaint() != null) { + g2.setPaint(getBackgroundPaint()); + g2.fill(hotspot); + } + g2.setPaint(getPaint()); + TextUtilities.drawRotatedString(getText(), g2, anchorX, anchorY, + getTextAnchor(), getRotationAngle(), getRotationAnchor()); + // Draw outline. + if (false) { + g2.setStroke(getOutlineStroke()); + g2.setPaint(getOutlinePaint()); + g2.draw(hotspot); + } + + // Add info that we have drawn this Annotation. + addEntity(info, hotspot, rendererIndex, getToolTipText(), getURL()); + } +} + +