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: * XYErrorRenderer.java 29: * -------------------- 30: * (C) Copyright 2006, 2007, by Object Refinery Limited. 31: * 32: * Original Author: David Gilbert (for Object Refinery Limited); 33: * Contributor(s): -; 34: * 35: * $Id: XYErrorRenderer.java,v 1.1.2.4 2007/03/23 14:01:12 mungady Exp $ 36: * 37: * Changes 38: * ------- 39: * 25-Oct-2006 : Version 1 (DG); 40: * 23-Mar-2007 : Check item visibility before drawing error bars - see bug 41: * 1686178 (DG); 42: * 43: */ 44: 45: package org.jfree.chart.renderer.xy; 46: 47: import java.awt.BasicStroke; 48: import java.awt.Graphics2D; 49: import java.awt.Paint; 50: import java.awt.geom.Line2D; 51: import java.awt.geom.Rectangle2D; 52: import java.io.IOException; 53: import java.io.ObjectInputStream; 54: import java.io.ObjectOutputStream; 55: 56: import org.jfree.chart.axis.ValueAxis; 57: import org.jfree.chart.event.RendererChangeEvent; 58: import org.jfree.chart.plot.CrosshairState; 59: import org.jfree.chart.plot.PlotOrientation; 60: import org.jfree.chart.plot.PlotRenderingInfo; 61: import org.jfree.chart.plot.XYPlot; 62: import org.jfree.data.Range; 63: import org.jfree.data.general.DatasetUtilities; 64: import org.jfree.data.xy.IntervalXYDataset; 65: import org.jfree.data.xy.XYDataset; 66: import org.jfree.io.SerialUtilities; 67: import org.jfree.ui.RectangleEdge; 68: import org.jfree.util.PaintUtilities; 69: 70: /** 71: * A line and shape renderer that can also display x and/or y-error values. 72: * This renderer expects an {@link IntervalXYDataset}, otherwise it reverts 73: * to the behaviour of the super class. 74: * 75: * @since 1.0.3 76: */ 77: public class XYErrorRenderer extends XYLineAndShapeRenderer { 78: 79: /** A flag that controls whether or not the x-error bars are drawn. */ 80: private boolean drawXError; 81: 82: /** A flag that controls whether or not the y-error bars are drawn. */ 83: private boolean drawYError; 84: 85: /** The length of the cap at the end of the error bars. */ 86: private double capLength; 87: 88: /** 89: * The paint used to draw the error bars (if <code>null</code> we use the 90: * series paint). 91: */ 92: private transient Paint errorPaint; 93: 94: /** 95: * Creates a new <code>XYErrorRenderer</code> instance. 96: */ 97: public XYErrorRenderer() { 98: super(false, true); 99: this.drawXError = true; 100: this.drawYError = true; 101: this.errorPaint = null; 102: this.capLength = 4.0; 103: } 104: 105: /** 106: * Returns the flag that controls whether or not the renderer draws error 107: * bars for the x-values. 108: * 109: * @return A boolean. 110: * 111: * @see #setDrawXError(boolean) 112: */ 113: public boolean getDrawXError() { 114: return this.drawXError; 115: } 116: 117: /** 118: * Sets the flag that controls whether or not the renderer draws error 119: * bars for the x-values and, if the flag changes, sends a 120: * {@link RendererChangeEvent} to all registered listeners. 121: * 122: * @param draw the flag value. 123: * 124: * @see #getDrawXError() 125: */ 126: public void setDrawXError(boolean draw) { 127: if (this.drawXError != draw) { 128: this.drawXError = draw; 129: this.notifyListeners(new RendererChangeEvent(this)); 130: } 131: } 132: 133: /** 134: * Returns the flag that controls whether or not the renderer draws error 135: * bars for the y-values. 136: * 137: * @return A boolean. 138: * 139: * @see #setDrawYError(boolean) 140: */ 141: public boolean getDrawYError() { 142: return this.drawYError; 143: } 144: 145: /** 146: * Sets the flag that controls whether or not the renderer draws error 147: * bars for the y-values and, if the flag changes, sends a 148: * {@link RendererChangeEvent} to all registered listeners. 149: * 150: * @param draw the flag value. 151: * 152: * @see #getDrawYError() 153: */ 154: public void setDrawYError(boolean draw) { 155: if (this.drawYError != draw) { 156: this.drawYError = draw; 157: notifyListeners(new RendererChangeEvent(this)); 158: } 159: } 160: 161: /** 162: * Returns the length (in Java2D units) of the cap at the end of the error 163: * bars. 164: * 165: * @return The cap length. 166: * 167: * @see #setCapLength(double) 168: */ 169: public double getCapLength() { 170: return this.capLength; 171: } 172: 173: /** 174: * Sets the length of the cap at the end of the error bars, and sends a 175: * {@link RendererChangeEvent} to all registered listeners. 176: * 177: * @param length the length (in Java2D units). 178: * 179: * @see #getCapLength() 180: */ 181: public void setCapLength(double length) { 182: this.capLength = length; 183: notifyListeners(new RendererChangeEvent(this)); 184: } 185: 186: /** 187: * Returns the paint used to draw the error bars. If this is 188: * <code>null</code> (the default), the item paint is used instead. 189: * 190: * @return The paint (possibly <code>null</code>). 191: * 192: * @see #setErrorPaint(Paint) 193: */ 194: public Paint getErrorPaint() { 195: return this.errorPaint; 196: } 197: 198: /** 199: * Sets the paint used to draw the error bars. 200: * 201: * @param paint the paint (<code>null</code> permitted). 202: * 203: * @see #getErrorPaint() 204: */ 205: public void setErrorPaint(Paint paint) { 206: this.errorPaint = paint; 207: notifyListeners(new RendererChangeEvent(this)); 208: } 209: 210: /** 211: * Returns the range required by this renderer to display all the domain 212: * values in the specified dataset. 213: * 214: * @param dataset the dataset (<code>null</code> permitted). 215: * 216: * @return The range, or <code>null</code> if the dataset is 217: * <code>null</code>. 218: */ 219: public Range findDomainBounds(XYDataset dataset) { 220: if (dataset != null) { 221: return DatasetUtilities.findDomainBounds(dataset, true); 222: } 223: else { 224: return null; 225: } 226: } 227: 228: /** 229: * Returns the range required by this renderer to display all the range 230: * values in the specified dataset. 231: * 232: * @param dataset the dataset (<code>null</code> permitted). 233: * 234: * @return The range, or <code>null</code> if the dataset is 235: * <code>null</code>. 236: */ 237: public Range findRangeBounds(XYDataset dataset) { 238: if (dataset != null) { 239: return DatasetUtilities.findRangeBounds(dataset, true); 240: } 241: else { 242: return null; 243: } 244: } 245: 246: /** 247: * Draws the visual representation for one data item. 248: * 249: * @param g2 the graphics output target. 250: * @param state the renderer state. 251: * @param dataArea the data area. 252: * @param info the plot rendering info. 253: * @param plot the plot. 254: * @param domainAxis the domain axis. 255: * @param rangeAxis the range axis. 256: * @param dataset the dataset. 257: * @param series the series index. 258: * @param item the item index. 259: * @param crosshairState the crosshair state. 260: * @param pass the pass index. 261: */ 262: public void drawItem(Graphics2D g2, XYItemRendererState state, 263: Rectangle2D dataArea, PlotRenderingInfo info, XYPlot plot, 264: ValueAxis domainAxis, ValueAxis rangeAxis, XYDataset dataset, 265: int series, int item, CrosshairState crosshairState, int pass) { 266: 267: if (pass == 0 && dataset instanceof IntervalXYDataset 268: && getItemVisible(series, item)) { 269: IntervalXYDataset ixyd = (IntervalXYDataset) dataset; 270: PlotOrientation orientation = plot.getOrientation(); 271: if (this.drawXError) { 272: // draw the error bar for the x-interval 273: double x0 = ixyd.getStartXValue(series, item); 274: double x1 = ixyd.getEndXValue(series, item); 275: double y = ixyd.getYValue(series, item); 276: RectangleEdge edge = plot.getDomainAxisEdge(); 277: double xx0 = domainAxis.valueToJava2D(x0, dataArea, edge); 278: double xx1 = domainAxis.valueToJava2D(x1, dataArea, edge); 279: double yy = rangeAxis.valueToJava2D(y, dataArea, 280: plot.getRangeAxisEdge()); 281: Line2D line; 282: Line2D cap1 = null; 283: Line2D cap2 = null; 284: double adj = this.capLength / 2.0; 285: if (orientation == PlotOrientation.VERTICAL) { 286: line = new Line2D.Double(xx0, yy, xx1, yy); 287: cap1 = new Line2D.Double(xx0, yy - adj, xx0, yy + adj); 288: cap2 = new Line2D.Double(xx1, yy - adj, xx1, yy + adj); 289: } 290: else { // PlotOrientation.HORIZONTAL 291: line = new Line2D.Double(yy, xx0, yy, xx1); 292: cap1 = new Line2D.Double(yy - adj, xx0, yy + adj, xx0); 293: cap2 = new Line2D.Double(yy - adj, xx1, yy + adj, xx1); 294: } 295: g2.setStroke(new BasicStroke(1.0f)); 296: if (this.errorPaint != null) { 297: g2.setPaint(this.errorPaint); 298: } 299: else { 300: g2.setPaint(getItemPaint(series, item)); 301: } 302: g2.draw(line); 303: g2.draw(cap1); 304: g2.draw(cap2); 305: } 306: if (this.drawYError) { 307: // draw the error bar for the y-interval 308: double y0 = ixyd.getStartYValue(series, item); 309: double y1 = ixyd.getEndYValue(series, item); 310: double x = ixyd.getXValue(series, item); 311: RectangleEdge edge = plot.getRangeAxisEdge(); 312: double yy0 = rangeAxis.valueToJava2D(y0, dataArea, edge); 313: double yy1 = rangeAxis.valueToJava2D(y1, dataArea, edge); 314: double xx = domainAxis.valueToJava2D(x, dataArea, 315: plot.getDomainAxisEdge()); 316: Line2D line; 317: Line2D cap1 = null; 318: Line2D cap2 = null; 319: double adj = this.capLength / 2.0; 320: if (orientation == PlotOrientation.VERTICAL) { 321: line = new Line2D.Double(xx, yy0, xx, yy1); 322: cap1 = new Line2D.Double(xx - adj, yy0, xx + adj, yy0); 323: cap2 = new Line2D.Double(xx - adj, yy1, xx + adj, yy1); 324: } 325: else { // PlotOrientation.HORIZONTAL 326: line = new Line2D.Double(yy0, xx, yy1, xx); 327: cap1 = new Line2D.Double(yy0, xx - adj, yy0, xx + adj); 328: cap2 = new Line2D.Double(yy1, xx - adj, yy1, xx + adj); 329: } 330: g2.setStroke(new BasicStroke(1.0f)); 331: if (this.errorPaint != null) { 332: g2.setPaint(this.errorPaint); 333: } 334: else { 335: g2.setPaint(getItemPaint(series, item)); 336: } 337: g2.draw(line); 338: g2.draw(cap1); 339: g2.draw(cap2); 340: } 341: } 342: super.drawItem(g2, state, dataArea, info, plot, domainAxis, rangeAxis, 343: dataset, series, item, crosshairState, pass); 344: } 345: 346: /** 347: * Tests this instance for equality with an arbitrary object. 348: * 349: * @param obj the object (<code>null</code> permitted). 350: * 351: * @return A boolean. 352: */ 353: public boolean equals(Object obj) { 354: if (obj == this) { 355: return true; 356: } 357: if (!(obj instanceof XYErrorRenderer)) { 358: return false; 359: } 360: XYErrorRenderer that = (XYErrorRenderer) obj; 361: if (this.drawXError != that.drawXError) { 362: return false; 363: } 364: if (this.drawYError != that.drawYError) { 365: return false; 366: } 367: if (this.capLength != that.capLength) { 368: return false; 369: } 370: if (!PaintUtilities.equal(this.errorPaint, that.errorPaint)) { 371: return false; 372: } 373: return super.equals(obj); 374: } 375: 376: /** 377: * Provides serialization support. 378: * 379: * @param stream the input stream. 380: * 381: * @throws IOException if there is an I/O error. 382: * @throws ClassNotFoundException if there is a classpath problem. 383: */ 384: private void readObject(ObjectInputStream stream) 385: throws IOException, ClassNotFoundException { 386: stream.defaultReadObject(); 387: this.errorPaint = SerialUtilities.readPaint(stream); 388: } 389: 390: /** 391: * Provides serialization support. 392: * 393: * @param stream the output stream. 394: * 395: * @throws IOException if there is an I/O error. 396: */ 397: private void writeObject(ObjectOutputStream stream) throws IOException { 398: stream.defaultWriteObject(); 399: SerialUtilities.writePaint(this.errorPaint, stream); 400: } 401: 402: }