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