001/* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-2014, 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-2014, 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 * 03-Jul-2013 : Use ParamChecks (DG); 081 * 082 */ 083 084package org.jfree.chart.renderer.xy; 085 086import java.awt.Graphics2D; 087import java.awt.Paint; 088import java.awt.Shape; 089import java.awt.Stroke; 090import java.awt.geom.GeneralPath; 091import java.awt.geom.Rectangle2D; 092import java.io.IOException; 093import java.io.ObjectInputStream; 094import java.io.ObjectOutputStream; 095 096import org.jfree.chart.LegendItem; 097import org.jfree.chart.axis.ValueAxis; 098import org.jfree.chart.entity.EntityCollection; 099import org.jfree.chart.entity.XYItemEntity; 100import org.jfree.chart.event.RendererChangeEvent; 101import org.jfree.chart.labels.XYSeriesLabelGenerator; 102import org.jfree.chart.labels.XYToolTipGenerator; 103import org.jfree.chart.plot.CrosshairState; 104import org.jfree.chart.plot.PlotOrientation; 105import org.jfree.chart.plot.PlotRenderingInfo; 106import org.jfree.chart.plot.XYPlot; 107import org.jfree.chart.urls.XYURLGenerator; 108import org.jfree.chart.util.ParamChecks; 109import org.jfree.data.xy.XYDataset; 110import org.jfree.io.SerialUtilities; 111import org.jfree.util.PublicCloneable; 112import org.jfree.util.ShapeUtilities; 113 114/** 115 * Area item renderer for an {@link XYPlot}. The example shown here is 116 * generated by the <code>XYAreaRenderer2Demo1.java</code> program included in 117 * the JFreeChart demo collection: 118 * <br><br> 119 * <img src="../../../../../images/XYAreaRenderer2Sample.png" 120 * alt="XYAreaRenderer2Sample.png"> 121 */ 122public class XYAreaRenderer2 extends AbstractXYItemRenderer 123 implements XYItemRenderer, PublicCloneable { 124 125 /** For serialization. */ 126 private static final long serialVersionUID = -7378069681579984133L; 127 128 /** A flag that controls whether or not the outline is shown. */ 129 private boolean showOutline; 130 131 /** 132 * The shape used to represent an area in each legend item (this should 133 * never be <code>null</code>). 134 */ 135 private transient Shape legendArea; 136 137 /** 138 * Constructs a new renderer. 139 */ 140 public XYAreaRenderer2() { 141 this(null, null); 142 } 143 144 /** 145 * Constructs a new renderer. 146 * 147 * @param labelGenerator the tool tip generator to use. <code>null</code> 148 * is none. 149 * @param urlGenerator the URL generator (null permitted). 150 */ 151 public XYAreaRenderer2(XYToolTipGenerator labelGenerator, 152 XYURLGenerator urlGenerator) { 153 super(); 154 this.showOutline = false; 155 setBaseToolTipGenerator(labelGenerator); 156 setURLGenerator(urlGenerator); 157 GeneralPath area = new GeneralPath(); 158 area.moveTo(0.0f, -4.0f); 159 area.lineTo(3.0f, -2.0f); 160 area.lineTo(4.0f, 4.0f); 161 area.lineTo(-4.0f, 4.0f); 162 area.lineTo(-3.0f, -2.0f); 163 area.closePath(); 164 this.legendArea = area; 165 } 166 167 /** 168 * Returns a flag that controls whether or not outlines of the areas are 169 * drawn. 170 * 171 * @return The flag. 172 * 173 * @see #setOutline(boolean) 174 */ 175 public boolean isOutline() { 176 return this.showOutline; 177 } 178 179 /** 180 * Sets a flag that controls whether or not outlines of the areas are 181 * drawn, and sends a {@link RendererChangeEvent} to all registered 182 * listeners. 183 * 184 * @param show the flag. 185 * 186 * @see #isOutline() 187 */ 188 public void setOutline(boolean show) { 189 this.showOutline = show; 190 fireChangeEvent(); 191 } 192 193 /** 194 * This method should not be used. 195 * 196 * @return <code>false</code> always. 197 * 198 * @deprecated This method was included in the API by mistake and serves 199 * no useful purpose. It has always returned <code>false</code>. 200 * 201 */ 202 public boolean getPlotLines() { 203 return false; 204 } 205 206 /** 207 * Returns the shape used to represent an area in the legend. 208 * 209 * @return The legend area (never <code>null</code>). 210 * 211 * @see #setLegendArea(Shape) 212 */ 213 public Shape getLegendArea() { 214 return this.legendArea; 215 } 216 217 /** 218 * Sets the shape used as an area in each legend item and sends a 219 * {@link RendererChangeEvent} to all registered listeners. 220 * 221 * @param area the area (<code>null</code> not permitted). 222 * 223 * @see #getLegendArea() 224 */ 225 public void setLegendArea(Shape area) { 226 ParamChecks.nullNotPermitted(area, "area"); 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 @Override 241 public LegendItem getLegendItem(int datasetIndex, int series) { 242 LegendItem result = null; 243 XYPlot xyplot = getPlot(); 244 if (xyplot != null) { 245 XYDataset dataset = xyplot.getDataset(datasetIndex); 246 if (dataset != null) { 247 XYSeriesLabelGenerator lg = getLegendItemLabelGenerator(); 248 String label = lg.generateLabel(dataset, series); 249 String description = label; 250 String toolTipText = null; 251 if (getLegendItemToolTipGenerator() != null) { 252 toolTipText = getLegendItemToolTipGenerator().generateLabel( 253 dataset, series); 254 } 255 String urlText = null; 256 if (getLegendItemURLGenerator() != null) { 257 urlText = getLegendItemURLGenerator().generateLabel( 258 dataset, series); 259 } 260 Paint paint = lookupSeriesPaint(series); 261 result = new LegendItem(label, description, toolTipText, 262 urlText, this.legendArea, paint); 263 result.setLabelFont(lookupLegendTextFont(series)); 264 Paint labelPaint = lookupLegendTextPaint(series); 265 if (labelPaint != null) { 266 result.setLabelPaint(labelPaint); 267 } 268 result.setDataset(dataset); 269 result.setDatasetIndex(datasetIndex); 270 result.setSeriesKey(dataset.getSeriesKey(series)); 271 result.setSeriesIndex(series); 272 } 273 } 274 return result; 275 } 276 277 /** 278 * Draws the visual representation of a single data item. 279 * 280 * @param g2 the graphics device. 281 * @param state the renderer state. 282 * @param dataArea the area within which the data is being drawn. 283 * @param info collects information about the drawing. 284 * @param plot the plot (can be used to obtain standard color 285 * information etc). 286 * @param domainAxis the domain axis. 287 * @param rangeAxis the range axis. 288 * @param dataset the dataset. 289 * @param series the series index (zero-based). 290 * @param item the item index (zero-based). 291 * @param crosshairState crosshair information for the plot 292 * (<code>null</code> permitted). 293 * @param pass the pass index. 294 */ 295 @Override 296 public void drawItem(Graphics2D g2, XYItemRendererState state, 297 Rectangle2D dataArea, PlotRenderingInfo info, XYPlot plot, 298 ValueAxis domainAxis, ValueAxis rangeAxis, XYDataset dataset, 299 int series, int item, CrosshairState crosshairState, int pass) { 300 301 if (!getItemVisible(series, item)) { 302 return; 303 } 304 // get the data point... 305 double x1 = dataset.getXValue(series, item); 306 double y1 = dataset.getYValue(series, item); 307 if (Double.isNaN(y1)) { 308 y1 = 0.0; 309 } 310 311 double transX1 = domainAxis.valueToJava2D(x1, dataArea, 312 plot.getDomainAxisEdge()); 313 double transY1 = rangeAxis.valueToJava2D(y1, dataArea, 314 plot.getRangeAxisEdge()); 315 316 // get the previous point and the next point so we can calculate a 317 // "hot spot" for the area (used by the chart entity)... 318 double x0 = dataset.getXValue(series, Math.max(item - 1, 0)); 319 double y0 = dataset.getYValue(series, Math.max(item - 1, 0)); 320 if (Double.isNaN(y0)) { 321 y0 = 0.0; 322 } 323 double transX0 = domainAxis.valueToJava2D(x0, dataArea, 324 plot.getDomainAxisEdge()); 325 double transY0 = rangeAxis.valueToJava2D(y0, dataArea, 326 plot.getRangeAxisEdge()); 327 328 int itemCount = dataset.getItemCount(series); 329 double x2 = dataset.getXValue(series, Math.min(item + 1, 330 itemCount - 1)); 331 double y2 = dataset.getYValue(series, Math.min(item + 1, 332 itemCount - 1)); 333 if (Double.isNaN(y2)) { 334 y2 = 0.0; 335 } 336 double transX2 = domainAxis.valueToJava2D(x2, dataArea, 337 plot.getDomainAxisEdge()); 338 double transY2 = rangeAxis.valueToJava2D(y2, dataArea, 339 plot.getRangeAxisEdge()); 340 341 double transZero = rangeAxis.valueToJava2D(0.0, dataArea, 342 plot.getRangeAxisEdge()); 343 GeneralPath hotspot = new GeneralPath(); 344 if (plot.getOrientation() == PlotOrientation.HORIZONTAL) { 345 moveTo(hotspot, transZero, ((transX0 + transX1) / 2.0)); 346 lineTo(hotspot, ((transY0 + transY1) / 2.0), 347 ((transX0 + transX1) / 2.0)); 348 lineTo(hotspot, transY1, transX1); 349 lineTo(hotspot, ((transY1 + transY2) / 2.0), 350 ((transX1 + transX2) / 2.0)); 351 lineTo(hotspot, transZero, ((transX1 + transX2) / 2.0)); 352 } 353 else { // vertical orientation 354 moveTo(hotspot, ((transX0 + transX1) / 2.0), transZero); 355 lineTo(hotspot, ((transX0 + transX1) / 2.0), 356 ((transY0 + transY1) / 2.0)); 357 lineTo(hotspot, transX1, transY1); 358 lineTo(hotspot, ((transX1 + transX2) / 2.0), 359 ((transY1 + transY2) / 2.0)); 360 lineTo(hotspot, ((transX1 + transX2) / 2.0), transZero); 361 } 362 hotspot.closePath(); 363 364 PlotOrientation orientation = plot.getOrientation(); 365 Paint paint = getItemPaint(series, item); 366 Stroke stroke = getItemStroke(series, item); 367 g2.setPaint(paint); 368 g2.setStroke(stroke); 369 370 // Check if the item is the last item for the series. 371 // and number of items > 0. We can't draw an area for a single point. 372 g2.fill(hotspot); 373 374 // draw an outline around the Area. 375 if (isOutline()) { 376 g2.setStroke(lookupSeriesOutlineStroke(series)); 377 g2.setPaint(lookupSeriesOutlinePaint(series)); 378 g2.draw(hotspot); 379 } 380 int domainAxisIndex = plot.getDomainAxisIndex(domainAxis); 381 int rangeAxisIndex = plot.getRangeAxisIndex(rangeAxis); 382 updateCrosshairValues(crosshairState, x1, y1, domainAxisIndex, 383 rangeAxisIndex, transX1, transY1, orientation); 384 385 // collect entity and tool tip information... 386 if (state.getInfo() != null) { 387 EntityCollection entities = state.getEntityCollection(); 388 if (entities != null) { 389 String tip = null; 390 XYToolTipGenerator generator = getToolTipGenerator(series, 391 item); 392 if (generator != null) { 393 tip = generator.generateToolTip(dataset, series, item); 394 } 395 String url = null; 396 if (getURLGenerator() != null) { 397 url = getURLGenerator().generateURL(dataset, series, item); 398 } 399 XYItemEntity entity = new XYItemEntity(hotspot, dataset, 400 series, item, tip, url); 401 entities.add(entity); 402 } 403 } 404 405 } 406 407 /** 408 * Tests this renderer for equality with an arbitrary object. 409 * 410 * @param obj the object (<code>null</code> not permitted). 411 * 412 * @return A boolean. 413 */ 414 @Override 415 public boolean equals(Object obj) { 416 if (obj == this) { 417 return true; 418 } 419 if (!(obj instanceof XYAreaRenderer2)) { 420 return false; 421 } 422 XYAreaRenderer2 that = (XYAreaRenderer2) obj; 423 if (this.showOutline != that.showOutline) { 424 return false; 425 } 426 if (!ShapeUtilities.equal(this.legendArea, that.legendArea)) { 427 return false; 428 } 429 return super.equals(obj); 430 } 431 432 /** 433 * Returns a clone of the renderer. 434 * 435 * @return A clone. 436 * 437 * @throws CloneNotSupportedException if the renderer cannot be cloned. 438 */ 439 @Override 440 public Object clone() throws CloneNotSupportedException { 441 XYAreaRenderer2 clone = (XYAreaRenderer2) super.clone(); 442 clone.legendArea = ShapeUtilities.clone(this.legendArea); 443 return clone; 444 } 445 446 /** 447 * Provides serialization support. 448 * 449 * @param stream the input stream. 450 * 451 * @throws IOException if there is an I/O error. 452 * @throws ClassNotFoundException if there is a classpath problem. 453 */ 454 private void readObject(ObjectInputStream stream) 455 throws IOException, ClassNotFoundException { 456 stream.defaultReadObject(); 457 this.legendArea = SerialUtilities.readShape(stream); 458 } 459 460 /** 461 * Provides serialization support. 462 * 463 * @param stream the output stream. 464 * 465 * @throws IOException if there is an I/O error. 466 */ 467 private void writeObject(ObjectOutputStream stream) throws IOException { 468 stream.defaultWriteObject(); 469 SerialUtilities.writeShape(this.legendArea, stream); 470 } 471 472} 473