001/* ===========================================================
002 * JFreeChart : a free chart library for the Java(tm) platform
003 * ===========================================================
004 *
005 * (C) Copyright 2000-2013, 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-2013, 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 * 02-Jul-2013 : Use ParamChecks (DG);
047 *
048 */
049
050package org.jfree.chart.annotations;
051
052import java.awt.Graphics2D;
053import java.awt.Image;
054import java.awt.geom.Point2D;
055import java.awt.geom.Rectangle2D;
056import java.io.IOException;
057import java.io.ObjectInputStream;
058import java.io.ObjectOutputStream;
059import java.io.Serializable;
060
061import org.jfree.chart.axis.AxisLocation;
062import org.jfree.chart.axis.ValueAxis;
063import org.jfree.chart.plot.Plot;
064import org.jfree.chart.plot.PlotOrientation;
065import org.jfree.chart.plot.PlotRenderingInfo;
066import org.jfree.chart.plot.XYPlot;
067import org.jfree.chart.util.ParamChecks;
068import org.jfree.ui.RectangleAnchor;
069import org.jfree.ui.RectangleEdge;
070import org.jfree.util.ObjectUtilities;
071import org.jfree.util.PublicCloneable;
072
073/**
074 * An annotation that allows an image to be placed at some location on
075 * an {@link XYPlot}.
076 *
077 * TODO:  implement serialization properly (image is not serializable).
078 */
079public class XYImageAnnotation extends AbstractXYAnnotation
080        implements Cloneable, PublicCloneable, Serializable {
081
082    /** For serialization. */
083    private static final long serialVersionUID = -4364694501921559958L;
084
085    /** The x-coordinate (in data space). */
086    private double x;
087
088    /** The y-coordinate (in data space). */
089    private double y;
090
091    /** The image. */
092    private transient Image image;
093
094    /**
095     * The image anchor point.
096     *
097     * @since 1.0.4
098     */
099    private RectangleAnchor anchor;
100
101    /**
102     * Creates a new annotation to be displayed at the specified (x, y)
103     * location.
104     *
105     * @param x  the x-coordinate (in data space).
106     * @param y  the y-coordinate (in data space).
107     * @param image  the image (<code>null</code> not permitted).
108     */
109    public XYImageAnnotation(double x, double y, Image image) {
110        this(x, y, image, RectangleAnchor.CENTER);
111    }
112
113    /**
114     * Creates a new annotation to be displayed at the specified (x, y)
115     * location.
116     *
117     * @param x  the x-coordinate (in data space).
118     * @param y  the y-coordinate (in data space).
119     * @param image  the image (<code>null</code> not permitted).
120     * @param anchor  the image anchor (<code>null</code> not permitted).
121     *
122     * @since 1.0.4
123     */
124    public XYImageAnnotation(double x, double y, Image image,
125            RectangleAnchor anchor) {
126        super();
127        ParamChecks.nullNotPermitted(image, "image");
128        ParamChecks.nullNotPermitted(anchor, "anchor");
129        this.x = x;
130        this.y = y;
131        this.image = image;
132        this.anchor = anchor;
133    }
134
135    /**
136     * Returns the x-coordinate (in data space) for the annotation.
137     *
138     * @return The x-coordinate.
139     *
140     * @since 1.0.4
141     */
142    public double getX() {
143        return this.x;
144    }
145
146    /**
147     * Returns the y-coordinate (in data space) for the annotation.
148     *
149     * @return The y-coordinate.
150     *
151     * @since 1.0.4
152     */
153    public double getY() {
154        return this.y;
155    }
156
157    /**
158     * Returns the image for the annotation.
159     *
160     * @return The image.
161     *
162     * @since 1.0.4
163     */
164    public Image getImage() {
165        return this.image;
166    }
167
168    /**
169     * Returns the image anchor for the annotation.
170     *
171     * @return The image anchor.
172     *
173     * @since 1.0.4
174     */
175    public RectangleAnchor getImageAnchor() {
176        return this.anchor;
177    }
178
179    /**
180     * Draws the annotation.  This method is called by the drawing code in the
181     * {@link XYPlot} class, you don't normally need to call this method
182     * directly.
183     *
184     * @param g2  the graphics device.
185     * @param plot  the plot.
186     * @param dataArea  the data area.
187     * @param domainAxis  the domain axis.
188     * @param rangeAxis  the range axis.
189     * @param rendererIndex  the renderer index.
190     * @param info  if supplied, this info object will be populated with
191     *              entity information.
192     */
193    @Override
194    public void draw(Graphics2D g2, XYPlot plot, Rectangle2D dataArea,
195                     ValueAxis domainAxis, ValueAxis rangeAxis,
196                     int rendererIndex,
197                     PlotRenderingInfo info) {
198
199        PlotOrientation orientation = plot.getOrientation();
200        AxisLocation domainAxisLocation = plot.getDomainAxisLocation();
201        AxisLocation rangeAxisLocation = plot.getRangeAxisLocation();
202        RectangleEdge domainEdge
203            = Plot.resolveDomainAxisLocation(domainAxisLocation, orientation);
204        RectangleEdge rangeEdge
205            = Plot.resolveRangeAxisLocation(rangeAxisLocation, orientation);
206        float j2DX
207            = (float) domainAxis.valueToJava2D(this.x, dataArea, domainEdge);
208        float j2DY
209            = (float) rangeAxis.valueToJava2D(this.y, dataArea, rangeEdge);
210        float xx = 0.0f;
211        float yy = 0.0f;
212        if (orientation == PlotOrientation.HORIZONTAL) {
213            xx = j2DY;
214            yy = j2DX;
215        }
216        else if (orientation == PlotOrientation.VERTICAL) {
217            xx = j2DX;
218            yy = j2DY;
219        }
220        int w = this.image.getWidth(null);
221        int h = this.image.getHeight(null);
222
223        Rectangle2D imageRect = new Rectangle2D.Double(0, 0, w, h);
224        Point2D anchorPoint = RectangleAnchor.coordinates(imageRect,
225                this.anchor);
226        xx = xx - (float) anchorPoint.getX();
227        yy = yy - (float) anchorPoint.getY();
228        g2.drawImage(this.image, (int) xx, (int) yy, null);
229
230        String toolTip = getToolTipText();
231        String url = getURL();
232        if (toolTip != null || url != null) {
233            addEntity(info, new Rectangle2D.Float(xx, yy, w, h), rendererIndex,
234                    toolTip, url);
235        }
236    }
237
238    /**
239     * Tests this object for equality with an arbitrary object.
240     *
241     * @param obj  the object (<code>null</code> permitted).
242     *
243     * @return A boolean.
244     */
245    @Override
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    @Override
280    public int hashCode() {
281        return this.image.hashCode();
282    }
283
284    /**
285     * Returns a clone of the annotation.
286     *
287     * @return A clone.
288     *
289     * @throws CloneNotSupportedException  if the annotation can't be cloned.
290     */
291    @Override
292    public Object clone() throws CloneNotSupportedException {
293        return super.clone();
294    }
295
296    /**
297     * Provides serialization support.
298     *
299     * @param stream  the output stream.
300     *
301     * @throws IOException  if there is an I/O error.
302     */
303    private void writeObject(ObjectOutputStream stream) throws IOException {
304        stream.defaultWriteObject();
305        //SerialUtilities.writeImage(this.image, stream);
306    }
307
308    /**
309     * Provides serialization support.
310     *
311     * @param stream  the input stream.
312     *
313     * @throws IOException  if there is an I/O error.
314     * @throws ClassNotFoundException  if there is a classpath problem.
315     */
316    private void readObject(ObjectInputStream stream)
317        throws IOException, ClassNotFoundException {
318        stream.defaultReadObject();
319        //this.image = SerialUtilities.readImage(stream);
320    }
321
322
323}