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