001 /* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-2007, 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 * XYAreaRenderer2.java 029 * -------------------- 030 * (C) Copyright 2004-2007, by Hari and Contributors. 031 * 032 * Original Author: Hari (ourhari@hotmail.com); 033 * Contributor(s): David Gilbert (for Object Refinery Limited); 034 * Richard Atkinson; 035 * Christian W. Zuckschwerdt; 036 * 037 * $Id: XYAreaRenderer2.java,v 1.12.2.7 2007/02/06 16:29:11 mungady Exp $ 038 * 039 * Changes: 040 * -------- 041 * 03-Apr-2002 : Version 1, contributed by Hari. This class is based on the 042 * StandardXYItemRenderer class (DG); 043 * 09-Apr-2002 : Removed the translated zero from the drawItem method - 044 * overridden the initialise() method to calculate it (DG); 045 * 30-May-2002 : Added tool tip generator to constructor to match super 046 * class (DG); 047 * 25-Jun-2002 : Removed unnecessary local variable (DG); 048 * 05-Aug-2002 : Small modification to drawItem method to support URLs for 049 * HTML image maps (RA); 050 * 01-Oct-2002 : Fixed errors reported by Checkstyle (DG); 051 * 07-Nov-2002 : Renamed AreaXYItemRenderer --> XYAreaRenderer (DG); 052 * 25-Mar-2003 : Implemented Serializable (DG); 053 * 01-May-2003 : Modified drawItem() method signature (DG); 054 * 27-Jul-2003 : Made line and polygon properties protected rather than 055 * private (RA); 056 * 30-Jul-2003 : Modified entity constructor (CZ); 057 * 20-Aug-2003 : Implemented Cloneable and PublicCloneable (DG); 058 * 16-Sep-2003 : Changed ChartRenderingInfo --> PlotRenderingInfo (DG); 059 * 07-Oct-2003 : Added renderer state (DG); 060 * 08-Dec-2003 : Modified hotspot for chart entity (DG); 061 * 10-Feb-2004 : Changed the drawItem() method to make cut-and-paste 062 * overriding easier. Also moved state class into this 063 * class (DG); 064 * 25-Feb-2004 : Replaced CrosshairInfo with CrosshairState. Renamed 065 * XYToolTipGenerator --> XYItemLabelGenerator (DG); 066 * 15-Jul-2004 : Switched getX() with getXValue() and getY() with 067 * getYValue() (DG); 068 * 11-Nov-2004 : Now uses ShapeUtilities to translate shapes (DG); 069 * 19-Jan-2005 : Now accesses only primitives from the dataset (DG); 070 * 21-Mar-2005 : Override getLegendItem() (DG); 071 * 20-Apr-2005 : Use generators for legend tooltips and URLs (DG); 072 * ------------- JFREECHART 1.0.x --------------------------------------------- 073 * 30-Nov-2006 : Fixed equals() and clone() implementations (DG); 074 * 06-Feb-2007 : Fixed bug 1086307, crosshairs with multiple axes (DG); 075 * 076 */ 077 078 package org.jfree.chart.renderer.xy; 079 080 import java.awt.Graphics2D; 081 import java.awt.Paint; 082 import java.awt.Polygon; 083 import java.awt.Shape; 084 import java.awt.Stroke; 085 import java.awt.geom.GeneralPath; 086 import java.awt.geom.Rectangle2D; 087 import java.io.IOException; 088 import java.io.ObjectInputStream; 089 import java.io.ObjectOutputStream; 090 import java.io.Serializable; 091 092 import org.jfree.chart.LegendItem; 093 import org.jfree.chart.axis.ValueAxis; 094 import org.jfree.chart.entity.EntityCollection; 095 import org.jfree.chart.entity.XYItemEntity; 096 import org.jfree.chart.event.RendererChangeEvent; 097 import org.jfree.chart.labels.XYSeriesLabelGenerator; 098 import org.jfree.chart.labels.XYToolTipGenerator; 099 import org.jfree.chart.plot.CrosshairState; 100 import org.jfree.chart.plot.PlotOrientation; 101 import org.jfree.chart.plot.PlotRenderingInfo; 102 import org.jfree.chart.plot.XYPlot; 103 import org.jfree.chart.urls.XYURLGenerator; 104 import org.jfree.data.xy.XYDataset; 105 import org.jfree.io.SerialUtilities; 106 import org.jfree.util.PublicCloneable; 107 import org.jfree.util.ShapeUtilities; 108 109 /** 110 * Area item renderer for an {@link XYPlot}. 111 */ 112 public class XYAreaRenderer2 extends AbstractXYItemRenderer 113 implements XYItemRenderer, 114 Cloneable, 115 PublicCloneable, 116 Serializable { 117 118 /** For serialization. */ 119 private static final long serialVersionUID = -7378069681579984133L; 120 121 /** A flag that controls whether or not the outline is shown. */ 122 private boolean showOutline; 123 124 /** 125 * The shape used to represent an area in each legend item (this should 126 * never be <code>null</code>). 127 */ 128 private transient Shape legendArea; 129 130 /** 131 * Constructs a new renderer. 132 */ 133 public XYAreaRenderer2() { 134 this(null, null); 135 } 136 137 /** 138 * Constructs a new renderer. 139 * 140 * @param labelGenerator the tool tip generator to use. <code>null</code> 141 * is none. 142 * @param urlGenerator the URL generator (null permitted). 143 */ 144 public XYAreaRenderer2(XYToolTipGenerator labelGenerator, 145 XYURLGenerator urlGenerator) { 146 super(); 147 this.showOutline = false; 148 setBaseToolTipGenerator(labelGenerator); 149 setURLGenerator(urlGenerator); 150 GeneralPath area = new GeneralPath(); 151 area.moveTo(0.0f, -4.0f); 152 area.lineTo(3.0f, -2.0f); 153 area.lineTo(4.0f, 4.0f); 154 area.lineTo(-4.0f, 4.0f); 155 area.lineTo(-3.0f, -2.0f); 156 area.closePath(); 157 this.legendArea = area; 158 } 159 160 /** 161 * Returns a flag that controls whether or not outlines of the areas are 162 * drawn. 163 * 164 * @return The flag. 165 * 166 * @see #setOutline(boolean) 167 */ 168 public boolean isOutline() { 169 return this.showOutline; 170 } 171 172 /** 173 * Sets a flag that controls whether or not outlines of the areas are 174 * drawn, and sends a {@link RendererChangeEvent} to all registered 175 * listeners. 176 * 177 * @param show the flag. 178 * 179 * @see #isOutline() 180 */ 181 public void setOutline(boolean show) { 182 this.showOutline = show; 183 notifyListeners(new RendererChangeEvent(this)); 184 } 185 186 /** 187 * This method should not be used. 188 * 189 * @return <code>false</code> always. 190 * 191 * @deprecated This method was included in the API by mistake and serves 192 * no useful purpose. It has always returned <code>false</code>. 193 * 194 */ 195 public boolean getPlotLines() { 196 return false; 197 } 198 199 /** 200 * Returns the shape used to represent an area in the legend. 201 * 202 * @return The legend area (never <code>null</code>). 203 * 204 * @see #setLegendArea(Shape) 205 */ 206 public Shape getLegendArea() { 207 return this.legendArea; 208 } 209 210 /** 211 * Sets the shape used as an area in each legend item and sends a 212 * {@link RendererChangeEvent} to all registered listeners. 213 * 214 * @param area the area (<code>null</code> not permitted). 215 * 216 * @see #getLegendArea() 217 */ 218 public void setLegendArea(Shape area) { 219 if (area == null) { 220 throw new IllegalArgumentException("Null 'area' argument."); 221 } 222 this.legendArea = area; 223 notifyListeners(new RendererChangeEvent(this)); 224 } 225 226 /** 227 * Returns a default legend item for the specified series. Subclasses 228 * should override this method to generate customised items. 229 * 230 * @param datasetIndex the dataset index (zero-based). 231 * @param series the series index (zero-based). 232 * 233 * @return A legend item for the series. 234 */ 235 public LegendItem getLegendItem(int datasetIndex, int series) { 236 LegendItem result = null; 237 XYPlot xyplot = getPlot(); 238 if (xyplot != null) { 239 XYDataset dataset = xyplot.getDataset(datasetIndex); 240 if (dataset != null) { 241 XYSeriesLabelGenerator lg = getLegendItemLabelGenerator(); 242 String label = lg.generateLabel(dataset, series); 243 String description = label; 244 String toolTipText = null; 245 if (getLegendItemToolTipGenerator() != null) { 246 toolTipText = getLegendItemToolTipGenerator().generateLabel( 247 dataset, series); 248 } 249 String urlText = null; 250 if (getLegendItemURLGenerator() != null) { 251 urlText = getLegendItemURLGenerator().generateLabel( 252 dataset, series); 253 } 254 Paint paint = getSeriesPaint(series); 255 result = new LegendItem(label, description, toolTipText, 256 urlText, this.legendArea, paint); 257 } 258 } 259 return result; 260 } 261 262 /** 263 * Draws the visual representation of a single data item. 264 * 265 * @param g2 the graphics device. 266 * @param state the renderer state. 267 * @param dataArea the area within which the data is being drawn. 268 * @param info collects information about the drawing. 269 * @param plot the plot (can be used to obtain standard color 270 * information etc). 271 * @param domainAxis the domain axis. 272 * @param rangeAxis the range axis. 273 * @param dataset the dataset. 274 * @param series the series index (zero-based). 275 * @param item the item index (zero-based). 276 * @param crosshairState crosshair information for the plot 277 * (<code>null</code> permitted). 278 * @param pass the pass index. 279 */ 280 public void drawItem(Graphics2D g2, 281 XYItemRendererState state, 282 Rectangle2D dataArea, 283 PlotRenderingInfo info, 284 XYPlot plot, 285 ValueAxis domainAxis, 286 ValueAxis rangeAxis, 287 XYDataset dataset, 288 int series, 289 int item, 290 CrosshairState crosshairState, 291 int pass) { 292 293 if (!getItemVisible(series, item)) { 294 return; 295 } 296 // get the data point... 297 double x1 = dataset.getXValue(series, item); 298 double y1 = dataset.getYValue(series, item); 299 if (Double.isNaN(y1)) { 300 y1 = 0.0; 301 } 302 303 double transX1 = domainAxis.valueToJava2D(x1, dataArea, 304 plot.getDomainAxisEdge()); 305 double transY1 = rangeAxis.valueToJava2D(y1, dataArea, 306 plot.getRangeAxisEdge()); 307 308 // get the previous point and the next point so we can calculate a 309 // "hot spot" for the area (used by the chart entity)... 310 double x0 = dataset.getXValue(series, Math.max(item - 1, 0)); 311 double y0 = dataset.getYValue(series, Math.max(item - 1, 0)); 312 if (Double.isNaN(y0)) { 313 y0 = 0.0; 314 } 315 double transX0 = domainAxis.valueToJava2D(x0, dataArea, 316 plot.getDomainAxisEdge()); 317 double transY0 = rangeAxis.valueToJava2D(y0, dataArea, 318 plot.getRangeAxisEdge()); 319 320 int itemCount = dataset.getItemCount(series); 321 double x2 = dataset.getXValue(series, Math.min(item + 1, 322 itemCount - 1)); 323 double y2 = dataset.getYValue(series, Math.min(item + 1, 324 itemCount - 1)); 325 if (Double.isNaN(y2)) { 326 y2 = 0.0; 327 } 328 double transX2 = domainAxis.valueToJava2D(x2, dataArea, 329 plot.getDomainAxisEdge()); 330 double transY2 = rangeAxis.valueToJava2D(y2, dataArea, 331 plot.getRangeAxisEdge()); 332 333 double transZero = rangeAxis.valueToJava2D(0.0, dataArea, 334 plot.getRangeAxisEdge()); 335 Polygon hotspot = null; 336 if (plot.getOrientation() == PlotOrientation.HORIZONTAL) { 337 hotspot = new Polygon(); 338 hotspot.addPoint((int) transZero, 339 (int) ((transX0 + transX1) / 2.0)); 340 hotspot.addPoint((int) ((transY0 + transY1) / 2.0), 341 (int) ((transX0 + transX1) / 2.0)); 342 hotspot.addPoint((int) transY1, (int) transX1); 343 hotspot.addPoint((int) ((transY1 + transY2) / 2.0), 344 (int) ((transX1 + transX2) / 2.0)); 345 hotspot.addPoint((int) transZero, 346 (int) ((transX1 + transX2) / 2.0)); 347 } 348 else { // vertical orientation 349 hotspot = new Polygon(); 350 hotspot.addPoint((int) ((transX0 + transX1) / 2.0), 351 (int) transZero); 352 hotspot.addPoint((int) ((transX0 + transX1) / 2.0), 353 (int) ((transY0 + transY1) / 2.0)); 354 hotspot.addPoint((int) transX1, (int) transY1); 355 hotspot.addPoint((int) ((transX1 + transX2) / 2.0), 356 (int) ((transY1 + transY2) / 2.0)); 357 hotspot.addPoint((int) ((transX1 + transX2) / 2.0), 358 (int) transZero); 359 } 360 361 PlotOrientation orientation = plot.getOrientation(); 362 Paint paint = getItemPaint(series, item); 363 Stroke stroke = getItemStroke(series, item); 364 g2.setPaint(paint); 365 g2.setStroke(stroke); 366 367 if (getPlotLines()) { 368 if (item > 0) { 369 if (plot.getOrientation() == PlotOrientation.VERTICAL) { 370 state.workingLine.setLine(transX0, transY0, transX1, 371 transY1); 372 } 373 else if (plot.getOrientation() == PlotOrientation.HORIZONTAL) { 374 state.workingLine.setLine(transY0, transX0, transY1, 375 transX1); 376 } 377 g2.draw(state.workingLine); 378 } 379 } 380 381 // Check if the item is the last item for the series. 382 // and number of items > 0. We can't draw an area for a single point. 383 g2.fill(hotspot); 384 385 // draw an outline around the Area. 386 if (isOutline()) { 387 g2.setStroke(getSeriesOutlineStroke(series)); 388 g2.setPaint(getSeriesOutlinePaint(series)); 389 g2.draw(hotspot); 390 } 391 int domainAxisIndex = plot.getDomainAxisIndex(domainAxis); 392 int rangeAxisIndex = plot.getRangeAxisIndex(rangeAxis); 393 updateCrosshairValues(crosshairState, x1, y1, domainAxisIndex, 394 rangeAxisIndex, transX1, transY1, orientation); 395 396 // collect entity and tool tip information... 397 if (state.getInfo() != null) { 398 EntityCollection entities = state.getEntityCollection(); 399 if (entities != null && hotspot != null) { 400 String tip = null; 401 XYToolTipGenerator generator = getToolTipGenerator( 402 series, item 403 ); 404 if (generator != null) { 405 tip = generator.generateToolTip(dataset, series, item); 406 } 407 String url = null; 408 if (getURLGenerator() != null) { 409 url = getURLGenerator().generateURL(dataset, series, item); 410 } 411 XYItemEntity entity = new XYItemEntity(hotspot, dataset, 412 series, item, tip, url); 413 entities.add(entity); 414 } 415 } 416 417 } 418 419 /** 420 * Tests this renderer for equality with an arbitrary object. 421 * 422 * @param obj the object (<code>null</code> not permitted). 423 * 424 * @return A boolean. 425 */ 426 public boolean equals(Object obj) { 427 if (obj == this) { 428 return true; 429 } 430 if (!(obj instanceof XYAreaRenderer2)) { 431 return false; 432 } 433 XYAreaRenderer2 that = (XYAreaRenderer2) obj; 434 if (this.showOutline != that.showOutline) { 435 return false; 436 } 437 if (!ShapeUtilities.equal(this.legendArea, that.legendArea)) { 438 return false; 439 } 440 return super.equals(obj); 441 } 442 443 /** 444 * Returns a clone of the renderer. 445 * 446 * @return A clone. 447 * 448 * @throws CloneNotSupportedException if the renderer cannot be cloned. 449 */ 450 public Object clone() throws CloneNotSupportedException { 451 XYAreaRenderer2 clone = (XYAreaRenderer2) super.clone(); 452 clone.legendArea = ShapeUtilities.clone(this.legendArea); 453 return clone; 454 } 455 456 /** 457 * Provides serialization support. 458 * 459 * @param stream the input stream. 460 * 461 * @throws IOException if there is an I/O error. 462 * @throws ClassNotFoundException if there is a classpath problem. 463 */ 464 private void readObject(ObjectInputStream stream) 465 throws IOException, ClassNotFoundException { 466 stream.defaultReadObject(); 467 this.legendArea = SerialUtilities.readShape(stream); 468 } 469 470 /** 471 * Provides serialization support. 472 * 473 * @param stream the output stream. 474 * 475 * @throws IOException if there is an I/O error. 476 */ 477 private void writeObject(ObjectOutputStream stream) throws IOException { 478 stream.defaultWriteObject(); 479 SerialUtilities.writeShape(this.legendArea, stream); 480 } 481 482 } 483