    001/* ===========================================================
    002 * JFreeChart : a free chart library for the Java(tm) platform
    003 * ===========================================================
    004 *
    005 * (C) Copyright 2000-2011, by Object Refinery Limited and Contributors.
    006 *
    007 * Project Info:  http://www.jfree.org/jfreechart/index.html
    008 *
    009 * This library is free software; you can redistribute it and/or modify it
    010 * under the terms of the GNU Lesser General Public License as published by
    011 * the Free Software Foundation; either version 2.1 of the License, or
    012 * (at your option) any later version.
    013 *
    014 * This library is distributed in the hope that it will be useful, but
    015 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
    016 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
    017 * License for more details.
    018 *
    019 * You should have received a copy of the GNU Lesser General Public
    020 * License along with this library; if not, write to the Free Software
    021 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
    022 * USA.
    023 *
    024 * [Oracle and Java are registered trademarks of Oracle and/or its affiliates.
    025 * Other names may be trademarks of their respective owners.]
    026 *
    027 * ----------------------
    028 * XYImageAnnotation.java
    029 * ----------------------
    030 * (C) Copyright 2003-2009, by Object Refinery Limited and Contributors.
    031 *
    032 * Original Author:  David Gilbert (for Object Refinery Limited);
    033 * Contributor(s):   Mike Harris;
    034 *                   Peter Kolb (patch 2809117);
    035 *
    036 * Changes:
    037 * --------
    038 * 01-Dec-2003 : Version 1 (DG);
    039 * 21-Jan-2004 : Update for renamed method in ValueAxis (DG);
    040 * 18-May-2004 : Fixed bug with plot orientation (DG);
    041 * 29-Sep-2004 : Now extends AbstractXYAnnotation, with modified draw()
    042 *               method signature and updated equals() method (DG);
    043 * ------------- JFREECHART 1.0.x ---------------------------------------------
    044 * 01-Dec-2006 : Added anchor attribute (see patch 1584860 from
    045 *               Mike Harris) (DG);
    046 *
    047 */
    048
    049package org.jfree.chart.annotations;
    050
    051import java.awt.Graphics2D;
    052import java.awt.Image;
    053import java.awt.geom.Point2D;
    054import java.awt.geom.Rectangle2D;
    055import java.io.IOException;
    056import java.io.ObjectInputStream;
    057import java.io.ObjectOutputStream;
    058import java.io.Serializable;
    059
    060import org.jfree.chart.axis.AxisLocation;
    061import org.jfree.chart.axis.ValueAxis;
    062import org.jfree.chart.plot.Plot;
    063import org.jfree.chart.plot.PlotOrientation;
    064import org.jfree.chart.plot.PlotRenderingInfo;
    065import org.jfree.chart.plot.XYPlot;
    066import org.jfree.ui.RectangleAnchor;
    067import org.jfree.ui.RectangleEdge;
    068import org.jfree.util.ObjectUtilities;
    069import org.jfree.util.PublicCloneable;
    070
    071/**
    072 * An annotation that allows an image to be placed at some location on
    073 * an {@link XYPlot}.
    074 *
    075 * TODO:  implement serialization properly (image is not serializable).
    076 */
    077public class XYImageAnnotation extends AbstractXYAnnotation
    078        implements Cloneable, PublicCloneable, Serializable {
    079
    080    /** For serialization. */
    081    private static final long serialVersionUID = -4364694501921559958L;
    082
    083    /** The x-coordinate (in data space). */
    084    private double x;
    085
    086    /** The y-coordinate (in data space). */
    087    private double y;
    088
    089    /** The image. */
    090    private transient Image image;
    091
    092    /**
    093     * The image anchor point.
    094     *
    095     * @since 1.0.4
    096     */
    097    private RectangleAnchor anchor;
    098
    099    /**
    100     * Creates a new annotation to be displayed at the specified (x, y)
    101     * location.
    102     *
    103     * @param x  the x-coordinate (in data space).
    104     * @param y  the y-coordinate (in data space).
    105     * @param image  the image (<code>null</code> not permitted).
    106     */
    107    public XYImageAnnotation(double x, double y, Image image) {
    108        this(x, y, image, RectangleAnchor.CENTER);
    109    }
    110
    111    /**
    112     * Creates a new annotation to be displayed at the specified (x, y)
    113     * location.
    114     *
    115     * @param x  the x-coordinate (in data space).
    116     * @param y  the y-coordinate (in data space).
    117     * @param image  the image (<code>null</code> not permitted).
    118     * @param anchor  the image anchor (<code>null</code> not permitted).
    119     *
    120     * @since 1.0.4
    121     */
    122    public XYImageAnnotation(double x, double y, Image image,
    123            RectangleAnchor anchor) {
    124        super();
    125        if (image == null) {
    126            throw new IllegalArgumentException("Null 'image' argument.");
    127        }
    128        if (anchor == null) {
    129            throw new IllegalArgumentException("Null 'anchor' argument.");
    130        }
    131        this.x = x;
    132        this.y = y;
    133        this.image = image;
    134        this.anchor = anchor;
    135    }
    136
    137    /**
    138     * Returns the x-coordinate (in data space) for the annotation.
    139     *
    140     * @return The x-coordinate.
    141     *
    142     * @since 1.0.4
    143     */
    144    public double getX() {
    145        return this.x;
    146    }
    147
    148    /**
    149     * Returns the y-coordinate (in data space) for the annotation.
    150     *
    151     * @return The y-coordinate.
    152     *
    153     * @since 1.0.4
    154     */
    155    public double getY() {
    156        return this.y;
    157    }
    158
    159    /**
    160     * Returns the image for the annotation.
    161     *
    162     * @return The image.
    163     *
    164     * @since 1.0.4
    165     */
    166    public Image getImage() {
    167        return this.image;
    168    }
    169
    170    /**
    171     * Returns the image anchor for the annotation.
    172     *
    173     * @return The image anchor.
    174     *
    175     * @since 1.0.4
    176     */
    177    public RectangleAnchor getImageAnchor() {
    178        return this.anchor;
    179    }
    180
    181    /**
    182     * Draws the annotation.  This method is called by the drawing code in the
    183     * {@link XYPlot} class, you don't normally need to call this method
    184     * directly.
    185     *
    186     * @param g2  the graphics device.
    187     * @param plot  the plot.
    188     * @param dataArea  the data area.
    189     * @param domainAxis  the domain axis.
    190     * @param rangeAxis  the range axis.
    191     * @param rendererIndex  the renderer index.
    192     * @param info  if supplied, this info object will be populated with
    193     *              entity information.
    194     */
    195    public void draw(Graphics2D g2, XYPlot plot, Rectangle2D dataArea,
    196                     ValueAxis domainAxis, ValueAxis rangeAxis,
    197                     int rendererIndex,
    198                     PlotRenderingInfo info) {
    199
    200        PlotOrientation orientation = plot.getOrientation();
    201        AxisLocation domainAxisLocation = plot.getDomainAxisLocation();
    202        AxisLocation rangeAxisLocation = plot.getRangeAxisLocation();
    203        RectangleEdge domainEdge
    204            = Plot.resolveDomainAxisLocation(domainAxisLocation, orientation);
    205        RectangleEdge rangeEdge
    206            = Plot.resolveRangeAxisLocation(rangeAxisLocation, orientation);
    207        float j2DX
    208            = (float) domainAxis.valueToJava2D(this.x, dataArea, domainEdge);
    209        float j2DY
    210            = (float) rangeAxis.valueToJava2D(this.y, dataArea, rangeEdge);
    211        float xx = 0.0f;
    212        float yy = 0.0f;
    213        if (orientation == PlotOrientation.HORIZONTAL) {
    214            xx = j2DY;
    215            yy = j2DX;
    216        }
    217        else if (orientation == PlotOrientation.VERTICAL) {
    218            xx = j2DX;
    219            yy = j2DY;
    220        }
    221        int w = this.image.getWidth(null);
    222        int h = this.image.getHeight(null);
    223
    224        Rectangle2D imageRect = new Rectangle2D.Double(0, 0, w, h);
    225        Point2D anchorPoint = RectangleAnchor.coordinates(imageRect,
    226                this.anchor);
    227        xx = xx - (float) anchorPoint.getX();
    228        yy = yy - (float) anchorPoint.getY();
    229        g2.drawImage(this.image, (int) xx, (int) yy, null);
    230
    231        String toolTip = getToolTipText();
    232        String url = getURL();
    233        if (toolTip != null || url != null) {
    234            addEntity(info, new Rectangle2D.Float(xx, yy, w, h), rendererIndex,
    235                    toolTip, url);
    236        }
    237    }
    238
    239    /**
    240     * Tests this object for equality with an arbitrary object.
    241     *
    242     * @param obj  the object (<code>null</code> permitted).
    243     *
    244     * @return A boolean.
    245     */
    246    public boolean equals(Object obj) {
    247        if (obj == this) {
    248            return true;
    249        }
    250        // now try to reject equality...
    251        if (!super.equals(obj)) {
    252            return false;
    253        }
    254        if (!(obj instanceof XYImageAnnotation)) {
    255            return false;
    256        }
    257        XYImageAnnotation that = (XYImageAnnotation) obj;
    258        if (this.x != that.x) {
    259            return false;
    260        }
    261        if (this.y != that.y) {
    262            return false;
    263        }
    264        if (!ObjectUtilities.equal(this.image, that.image)) {
    265            return false;
    266        }
    267        if (!this.anchor.equals(that.anchor)) {
    268            return false;
    269        }
    270        // seems to be the same...
    271        return true;
    272    }
    273
    274    /**
    275     * Returns a hash code for this object.
    276     *
    277     * @return A hash code.
    278     */
    279    public int hashCode() {
    280        return this.image.hashCode();
    281    }
    282
    283    /**
    284     * Returns a clone of the annotation.
    285     *
    286     * @return A clone.
    287     *
    288     * @throws CloneNotSupportedException  if the annotation can't be cloned.
    289     */
    290    public Object clone() throws CloneNotSupportedException {
    291        return super.clone();
    292    }
    293
    294    /**
    295     * Provides serialization support.
    296     *
    297     * @param stream  the output stream.
    298     *
    299     * @throws IOException  if there is an I/O error.
    300     */
    301    private void writeObject(ObjectOutputStream stream) throws IOException {
    302        stream.defaultWriteObject();
    303        //SerialUtilities.writeImage(this.image, stream);
    304    }
    305
    306    /**
    307     * Provides serialization support.
    308     *
    309     * @param stream  the input stream.
    310     *
    311     * @throws IOException  if there is an I/O error.
    312     * @throws ClassNotFoundException  if there is a classpath problem.
    313     */
    314    private void readObject(ObjectInputStream stream)
    315        throws IOException, ClassNotFoundException {
    316        stream.defaultReadObject();
    317        //this.image = SerialUtilities.readImage(stream);
    318    }
    319
    320
    321}