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());
+    }
+}
+
+

http://dive4elements.wald.intevation.org