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