001 /* ===========================================================
002 * JFreeChart : a free chart library for the Java(tm) platform
003 * ===========================================================
004 *
005 * (C) Copyright 2000-2009, 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 * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
025 * in the United States and other countries.]
026 *
027 * --------------------------
028 * XYDataImageAnnotation.java
029 * --------------------------
030 * (C) Copyright 2008, 2009, by Object Refinery Limited and Contributors.
031 *
032 * Original Author: David Gilbert (for Object Refinery Limited);
033 * Contributor(s): -;
034 *
035 * Changes:
036 * --------
037 * 17-Sep-2008 : Version 1, based on XYImageAnnotation (DG);
038 * 10-Mar-2009 : Implemented XYAnnotationBoundsInfo (DG);
039 *
040 */
041
042 package org.jfree.chart.annotations;
043
044 import java.awt.Graphics2D;
045 import java.awt.Image;
046 import java.awt.geom.Rectangle2D;
047 import java.io.IOException;
048 import java.io.ObjectInputStream;
049 import java.io.ObjectOutputStream;
050
051 import org.jfree.chart.axis.AxisLocation;
052 import org.jfree.chart.axis.ValueAxis;
053 import org.jfree.chart.plot.Plot;
054 import org.jfree.chart.plot.PlotOrientation;
055 import org.jfree.chart.plot.PlotRenderingInfo;
056 import org.jfree.chart.plot.XYPlot;
057 import org.jfree.data.Range;
058 import org.jfree.ui.RectangleEdge;
059 import org.jfree.util.ObjectUtilities;
060 import org.jfree.util.PublicCloneable;
061
062 /**
063 * An annotation that allows an image to be placed within a rectangle specified
064 * in data coordinates on an {@link XYPlot}. Note that this annotation
065 * is not currently serializable, so don't use it if you plan on serializing
066 * your chart(s).
067 *
068 * @since 1.0.11
069 */
070 public class XYDataImageAnnotation extends AbstractXYAnnotation
071 implements Cloneable, PublicCloneable, XYAnnotationBoundsInfo {
072
073 /** The image. */
074 private transient Image image;
075
076 /**
077 * The x-coordinate (in data space).
078 */
079 private double x;
080
081 /**
082 * The y-coordinate (in data space).
083 */
084 private double y;
085
086 /**
087 * The image display area width in data coordinates.
088 */
089 private double w;
090
091 /**
092 * The image display area height in data coordinates.
093 */
094 private double h;
095
096 /**
097 * A flag indicating whether or not the annotation should contribute to
098 * the data range for a plot/renderer.
099 *
100 * @since 1.0.13
101 */
102 private boolean includeInDataBounds;
103
104 /**
105 * Creates a new annotation to be displayed within the specified rectangle.
106 *
107 * @param image the image (<code>null</code> not permitted).
108 * @param x the x-coordinate (in data space).
109 * @param y the y-coordinate (in data space).
110 * @param w the image display area width.
111 * @param h the image display area height.
112 */
113 public XYDataImageAnnotation(Image image, double x, double y, double w,
114 double h) {
115 this(image, x, y, w, h, false);
116 }
117
118 /**
119 * Creates a new annotation to be displayed within the specified rectangle.
120 *
121 * @param image the image (<code>null</code> not permitted).
122 * @param x the x-coordinate (in data space).
123 * @param y the y-coordinate (in data space).
124 * @param w the image display area width.
125 * @param h the image display area height.
126 * @param includeInDataBounds a flag that controls whether or not the
127 * annotation is included in the data bounds for the axis autoRange.
128 *
129 * @since 1.0.13
130 */
131 public XYDataImageAnnotation(Image image, double x, double y, double w,
132 double h, boolean includeInDataBounds) {
133
134 if (image == null) {
135 throw new IllegalArgumentException("Null 'image' argument.");
136 }
137 this.image = image;
138 this.x = x;
139 this.y = y;
140 this.w = w;
141 this.h = h;
142 this.includeInDataBounds = includeInDataBounds;
143 }
144
145 /**
146 * Returns the image for the annotation.
147 *
148 * @return The image.
149 */
150 public Image getImage() {
151 return this.image;
152 }
153
154 /**
155 * Returns the x-coordinate (in data space) for the annotation.
156 *
157 * @return The x-coordinate.
158 */
159 public double getX() {
160 return this.x;
161 }
162
163 /**
164 * Returns the y-coordinate (in data space) for the annotation.
165 *
166 * @return The y-coordinate.
167 */
168 public double getY() {
169 return this.y;
170 }
171
172 /**
173 * Returns the width (in data space) of the data rectangle into which the
174 * image will be drawn.
175 *
176 * @return The width.
177 */
178 public double getWidth() {
179 return this.w;
180 }
181
182 /**
183 * Returns the height (in data space) of the data rectangle into which the
184 * image will be drawn.
185 *
186 * @return The height.
187 */
188 public double getHeight() {
189 return this.h;
190 }
191
192 /**
193 * Returns the flag that controls whether or not the annotation should
194 * contribute to the autoRange for the axis it is plotted against.
195 *
196 * @return A boolean.
197 *
198 * @since 1.0.13
199 */
200 public boolean getIncludeInDataBounds() {
201 return this.includeInDataBounds;
202 }
203
204 /**
205 * Returns the x-range for the annotation.
206 *
207 * @return The range.
208 *
209 * @since 1.0.13
210 */
211 public Range getXRange() {
212 return new Range(this.x, this.x + this.w);
213 }
214
215 /**
216 * Returns the y-range for the annotation.
217 *
218 * @return The range.
219 *
220 * @since 1.0.13
221 */
222 public Range getYRange() {
223 return new Range(this.y, this.y + this.h);
224 }
225
226 /**
227 * Draws the annotation. This method is called by the drawing code in the
228 * {@link XYPlot} class, you don't normally need to call this method
229 * directly.
230 *
231 * @param g2 the graphics device.
232 * @param plot the plot.
233 * @param dataArea the data area.
234 * @param domainAxis the domain axis.
235 * @param rangeAxis the range axis.
236 * @param rendererIndex the renderer index.
237 * @param info if supplied, this info object will be populated with
238 * entity information.
239 */
240 public void draw(Graphics2D g2, XYPlot plot, Rectangle2D dataArea,
241 ValueAxis domainAxis, ValueAxis rangeAxis,
242 int rendererIndex,
243 PlotRenderingInfo info) {
244
245 PlotOrientation orientation = plot.getOrientation();
246 AxisLocation xAxisLocation = plot.getDomainAxisLocation();
247 AxisLocation yAxisLocation = plot.getRangeAxisLocation();
248 RectangleEdge xEdge = Plot.resolveDomainAxisLocation(xAxisLocation,
249 orientation);
250 RectangleEdge yEdge = Plot.resolveRangeAxisLocation(yAxisLocation,
251 orientation);
252 float j2DX0 = (float) domainAxis.valueToJava2D(this.x, dataArea, xEdge);
253 float j2DY0 = (float) rangeAxis.valueToJava2D(this.y, dataArea, yEdge);
254 float j2DX1 = (float) domainAxis.valueToJava2D(this.x + this.w,
255 dataArea, xEdge);
256 float j2DY1 = (float) rangeAxis.valueToJava2D(this.y + this.h,
257 dataArea, yEdge);
258 float xx0 = 0.0f;
259 float yy0 = 0.0f;
260 float xx1 = 0.0f;
261 float yy1 = 0.0f;
262 if (orientation == PlotOrientation.HORIZONTAL) {
263 xx0 = j2DY0;
264 xx1 = j2DY1;
265 yy0 = j2DX0;
266 yy1 = j2DX1;
267 }
268 else if (orientation == PlotOrientation.VERTICAL) {
269 xx0 = j2DX0;
270 xx1 = j2DX1;
271 yy0 = j2DY0;
272 yy1 = j2DY1;
273 }
274 // TODO: rotate the image when drawn with horizontal orientation?
275 g2.drawImage(this.image, (int) xx0, (int) Math.min(yy0, yy1),
276 (int) (xx1 - xx0), (int) Math.abs(yy1 - yy0), null);
277 String toolTip = getToolTipText();
278 String url = getURL();
279 if (toolTip != null || url != null) {
280 addEntity(info, new Rectangle2D.Float(xx0, yy0, (xx1 - xx0),
281 (yy1 - yy0)), rendererIndex, toolTip, url);
282 }
283 }
284
285 /**
286 * Tests this object for equality with an arbitrary object.
287 *
288 * @param obj the object (<code>null</code> permitted).
289 *
290 * @return A boolean.
291 */
292 public boolean equals(Object obj) {
293 if (obj == this) {
294 return true;
295 }
296 // now try to reject equality...
297 if (!super.equals(obj)) {
298 return false;
299 }
300 if (!(obj instanceof XYDataImageAnnotation)) {
301 return false;
302 }
303 XYDataImageAnnotation that = (XYDataImageAnnotation) obj;
304 if (this.x != that.x) {
305 return false;
306 }
307 if (this.y != that.y) {
308 return false;
309 }
310 if (this.w != that.w) {
311 return false;
312 }
313 if (this.h != that.h) {
314 return false;
315 }
316 if (this.includeInDataBounds != that.includeInDataBounds) {
317 return false;
318 }
319 if (!ObjectUtilities.equal(this.image, that.image)) {
320 return false;
321 }
322 // seems to be the same...
323 return true;
324 }
325
326 /**
327 * Returns a hash code for this object.
328 *
329 * @return A hash code.
330 */
331 public int hashCode() {
332 return this.image.hashCode();
333 }
334
335 /**
336 * Returns a clone of the annotation.
337 *
338 * @return A clone.
339 *
340 * @throws CloneNotSupportedException if the annotation can't be cloned.
341 */
342 public Object clone() throws CloneNotSupportedException {
343 return super.clone();
344 }
345
346 /**
347 * Provides serialization support.
348 *
349 * @param stream the output stream.
350 *
351 * @throws IOException if there is an I/O error.
352 */
353 private void writeObject(ObjectOutputStream stream) throws IOException {
354 stream.defaultWriteObject();
355 // FIXME
356 //SerialUtilities.writeImage(this.image, stream);
357 }
358
359 /**
360 * Provides serialization support.
361 *
362 * @param stream the input stream.
363 *
364 * @throws IOException if there is an I/O error.
365 * @throws ClassNotFoundException if there is a classpath problem.
366 */
367 private void readObject(ObjectInputStream stream)
368 throws IOException, ClassNotFoundException {
369 stream.defaultReadObject();
370 // FIXME
371 //this.image = SerialUtilities.readImage(stream);
372 }
373
374 }