Frames | No Frames |
1: /* =========================================================== 2: * JFreeChart : a free chart library for the Java(tm) platform 3: * =========================================================== 4: * 5: * (C) Copyright 2000-2005, 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, 2005, 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.4 2005/12/02 11:59:43 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: * 73: */ 74: 75: package org.jfree.chart.renderer.xy; 76: 77: import java.awt.Graphics2D; 78: import java.awt.Paint; 79: import java.awt.Polygon; 80: import java.awt.Shape; 81: import java.awt.Stroke; 82: import java.awt.geom.GeneralPath; 83: import java.awt.geom.Rectangle2D; 84: import java.io.IOException; 85: import java.io.ObjectInputStream; 86: import java.io.ObjectOutputStream; 87: import java.io.Serializable; 88: 89: import org.jfree.chart.LegendItem; 90: import org.jfree.chart.axis.ValueAxis; 91: import org.jfree.chart.entity.EntityCollection; 92: import org.jfree.chart.entity.XYItemEntity; 93: import org.jfree.chart.event.RendererChangeEvent; 94: import org.jfree.chart.labels.XYSeriesLabelGenerator; 95: import org.jfree.chart.labels.XYToolTipGenerator; 96: import org.jfree.chart.plot.CrosshairState; 97: import org.jfree.chart.plot.PlotOrientation; 98: import org.jfree.chart.plot.PlotRenderingInfo; 99: 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: }