Frames | No Frames |
1: /* =========================================================== 2: * JFreeChart : a free chart library for the Java(tm) platform 3: * =========================================================== 4: * 5: * (C) Copyright 2000-2007, by Object Refinery Limited and Contributors. 6: * 7: * Project Info: http://www.jfree.org/jfreechart/index.html 8: * 9: * This library is free software; you can redistribute it and/or modify it 10: * under the terms of the GNU Lesser General Public License as published by 11: * the Free Software Foundation; either version 2.1 of the License, or 12: * (at your option) any later version. 13: * 14: * This library is distributed in the hope that it will be useful, but 15: * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 16: * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 17: * License for more details. 18: * 19: * You should have received a copy of the GNU Lesser General Public 20: * License along with this library; if not, write to the Free Software 21: * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 22: * USA. 23: * 24: * [Java is a trademark or registered trademark of Sun Microsystems, Inc. 25: * in the United States and other countries.] 26: * 27: * -------------------- 28: * XYAreaRenderer2.java 29: * -------------------- 30: * (C) Copyright 2004-2007, by Hari and Contributors. 31: * 32: * Original Author: Hari (ourhari@hotmail.com); 33: * Contributor(s): David Gilbert (for Object Refinery Limited); 34: * Richard Atkinson; 35: * Christian W. Zuckschwerdt; 36: * 37: * $Id: XYAreaRenderer2.java,v 1.12.2.7 2007/02/06 16:29:11 mungady Exp $ 38: * 39: * Changes: 40: * -------- 41: * 03-Apr-2002 : Version 1, contributed by Hari. This class is based on the 42: * StandardXYItemRenderer class (DG); 43: * 09-Apr-2002 : Removed the translated zero from the drawItem method - 44: * overridden the initialise() method to calculate it (DG); 45: * 30-May-2002 : Added tool tip generator to constructor to match super 46: * class (DG); 47: * 25-Jun-2002 : Removed unnecessary local variable (DG); 48: * 05-Aug-2002 : Small modification to drawItem method to support URLs for 49: * HTML image maps (RA); 50: * 01-Oct-2002 : Fixed errors reported by Checkstyle (DG); 51: * 07-Nov-2002 : Renamed AreaXYItemRenderer --> XYAreaRenderer (DG); 52: * 25-Mar-2003 : Implemented Serializable (DG); 53: * 01-May-2003 : Modified drawItem() method signature (DG); 54: * 27-Jul-2003 : Made line and polygon properties protected rather than 55: * private (RA); 56: * 30-Jul-2003 : Modified entity constructor (CZ); 57: * 20-Aug-2003 : Implemented Cloneable and PublicCloneable (DG); 58: * 16-Sep-2003 : Changed ChartRenderingInfo --> PlotRenderingInfo (DG); 59: * 07-Oct-2003 : Added renderer state (DG); 60: * 08-Dec-2003 : Modified hotspot for chart entity (DG); 61: * 10-Feb-2004 : Changed the drawItem() method to make cut-and-paste 62: * overriding easier. Also moved state class into this 63: * class (DG); 64: * 25-Feb-2004 : Replaced CrosshairInfo with CrosshairState. Renamed 65: * XYToolTipGenerator --> XYItemLabelGenerator (DG); 66: * 15-Jul-2004 : Switched getX() with getXValue() and getY() with 67: * getYValue() (DG); 68: * 11-Nov-2004 : Now uses ShapeUtilities to translate shapes (DG); 69: * 19-Jan-2005 : Now accesses only primitives from the dataset (DG); 70: * 21-Mar-2005 : Override getLegendItem() (DG); 71: * 20-Apr-2005 : Use generators for legend tooltips and URLs (DG); 72: * ------------- JFREECHART 1.0.x --------------------------------------------- 73: * 30-Nov-2006 : Fixed equals() and clone() implementations (DG); 74: * 06-Feb-2007 : Fixed bug 1086307, crosshairs with multiple axes (DG); 75: * 76: */ 77: 78: package org.jfree.chart.renderer.xy; 79: 80: import java.awt.Graphics2D; 81: import java.awt.Paint; 82: import java.awt.Polygon; 83: import java.awt.Shape; 84: import java.awt.Stroke; 85: import java.awt.geom.GeneralPath; 86: import java.awt.geom.Rectangle2D; 87: import java.io.IOException; 88: import java.io.ObjectInputStream; 89: import java.io.ObjectOutputStream; 90: import java.io.Serializable; 91: 92: import org.jfree.chart.LegendItem; 93: import org.jfree.chart.axis.ValueAxis; 94: import org.jfree.chart.entity.EntityCollection; 95: import org.jfree.chart.entity.XYItemEntity; 96: import org.jfree.chart.event.RendererChangeEvent; 97: import org.jfree.chart.labels.XYSeriesLabelGenerator; 98: import org.jfree.chart.labels.XYToolTipGenerator; 99: 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: