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