view flys-artifacts/src/main/java/de/intevation/flys/jfree/StickyAxisAnnotation.java @ 2089:0da8874bd378

Added initial state to map artifact to be able to advance and step back. The map artifact overrides describe() to have the complete UI information in the describe response document. flys-artifacts/trunk@3613 c6561f87-3c4e-4783-a992-168aeb5c3f6f
author Raimund Renkert <raimund.renkert@intevation.de>
date Fri, 06 Jan 2012 12:02:10 +0000
parents 59622ba800c8
children c68f4f227c09
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 java.awt.Color;
import java.awt.Font;
import java.awt.BasicStroke;

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;

import de.intevation.flys.utils.ThemeAccess;

/**
 * Custom Annotations class that is drawn only if no collisions with other
 * already drawn CustomAnnotations in current plot are found. A mark on
 * the axis is shown in all cases, though.
 * 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;

    /** Theme attributes. */
    protected Color textColor;
    protected Color lineColor;
    protected Font font;
    protected int lineWidth;
    protected String textOrientation = "vertical";
    protected Color textBackground;
    protected boolean showBackground;


    /**
     * 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){
            float o = 270f;
            if(textOrientation.equals("horizontal")) {
                o = 0f;
            }
            this.setRotationAngle(o * (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) {
            setOutlineStroke(new BasicStroke((float) lineWidth));
            g2.setStroke(getOutlineStroke());
            g2.setPaint(lineColor);
            g2.draw(line);
        }
    }


    /**
     * Draw the Annotation; the text only if it does not collide with other
     * already drawn Annotations- texts.
     *
     * @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 2% 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;
        }

        //Call to apply orientation.
        setStickyAxis(stickyAxis);

        // Always draw the small line at axis.
        drawAxisMark(g2, dataArea, domainAxis, rangeAxis, domainEdge,
            rangeEdge, orientation);

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

        // Draw the background.
        if (showBackground) {
            g2.setStroke(new BasicStroke ((float) 1));
            g2.setBackground(textBackground);
            g2.setPaint(textBackground);
            hotspot = TextUtilities.calculateRotatedStringBounds(
                getText(), g2, anchorX, anchorY, getTextAnchor(),
                getRotationAngle(), getRotationAnchor());
            g2.fill(hotspot);
            g2.draw(hotspot);
        }

        // Draw the text.
        g2.setPaint(textColor);
        g2.setFont(font);
        TextUtilities.drawRotatedString(getText(), g2, anchorX, anchorY,
            getTextAnchor(), getRotationAngle(), getRotationAnchor());

        // Add info that we have drawn this Annotation.
        addEntity(info, hotspot, rendererIndex, getToolTipText(), getURL());
    }


    public void applyTheme(ThemeAccess ta) {
        lineWidth       = ta.parseLineWidth();
        lineColor       = ta.parseLineColorField();
        textColor       = ta.parseTextColor();
        font            = ta.parseTextFont();
        textOrientation = ta.parseTextOrientation();
        textBackground  = ta.parseTextBackground();
        showBackground  = ta.parseShowTextBackground();
    }
}
// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :

http://dive4elements.wald.intevation.org