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    
049    package org.jfree.chart.annotations;
050    
051    import java.awt.Graphics2D;
052    import java.awt.Image;
053    import java.awt.geom.Point2D;
054    import java.awt.geom.Rectangle2D;
055    import java.io.IOException;
056    import java.io.ObjectInputStream;
057    import java.io.ObjectOutputStream;
058    import java.io.Serializable;
059    
060    import org.jfree.chart.axis.AxisLocation;
061    import org.jfree.chart.axis.ValueAxis;
062    import org.jfree.chart.plot.Plot;
063    import org.jfree.chart.plot.PlotOrientation;
064    import org.jfree.chart.plot.PlotRenderingInfo;
065    import org.jfree.chart.plot.XYPlot;
066    import org.jfree.ui.RectangleAnchor;
067    import org.jfree.ui.RectangleEdge;
068    import org.jfree.util.ObjectUtilities;
069    import 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     */
077    public 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    }