view flys-artifacts/src/main/java/de/intevation/flys/jfree/StickyAxisAnnotation.java @ 1042:8873c43119ca

Fix rendering of Annotations/lines under certain zooming conditions, ChangeLog. flys-artifacts/trunk@2503 c6561f87-3c4e-4783-a992-168aeb5c3f6f
author Felix Wolfsteller <felix.wolfsteller@intevation.de>
date Thu, 18 Aug 2011 14:09:54 +0000
parents cf39205df113
children f16b66839e59
line wrap: on
line source
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.text.TextUtilities;
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.ui.RectangleEdge;


/**
 * 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;


    /**
     * 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).
     */
    public StickyAxisAnnotation(String text, float x, float y) {
        super(text, x, y);
    }


    /**
     * 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;
    }


    /**
     * 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);
        */
        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;

        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;

        // 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 = (float) domainAxis.valueToJava2D(
            getX(), dataArea, domainEdge);
        float 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