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: fireChangeEvent(); 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: fireChangeEvent(); 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: fireChangeEvent(); 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 and sends a 201: * {@link RendererChangeEvent} to all registered listeners. 202: * 203: * @param paint the paint (<code>null</code> permitted). 204: * 205: * @see #getErrorPaint() 206: */ 207: public void setErrorPaint(Paint paint) { 208: this.errorPaint = paint; 209: fireChangeEvent(); 210: } 211: 212: /** 213: * Returns the range required by this renderer to display all the domain 214: * values in the specified dataset. 215: * 216: * @param dataset the dataset (<code>null</code> permitted). 217: * 218: * @return The range, or <code>null</code> if the dataset is 219: * <code>null</code>. 220: */ 221: public Range findDomainBounds(XYDataset dataset) { 222: if (dataset != null) { 223: return DatasetUtilities.findDomainBounds(dataset, true); 224: } 225: else { 226: return null; 227: } 228: } 229: 230: /** 231: * Returns the range required by this renderer to display all the range 232: * values in the specified dataset. 233: * 234: * @param dataset the dataset (<code>null</code> permitted). 235: * 236: * @return The range, or <code>null</code> if the dataset is 237: * <code>null</code>. 238: */ 239: public Range findRangeBounds(XYDataset dataset) { 240: if (dataset != null) { 241: return DatasetUtilities.findRangeBounds(dataset, true); 242: } 243: else { 244: return null; 245: } 246: } 247: 248: /** 249: * Draws the visual representation for one data item. 250: * 251: * @param g2 the graphics output target. 252: * @param state the renderer state. 253: * @param dataArea the data area. 254: * @param info the plot rendering info. 255: * @param plot the plot. 256: * @param domainAxis the domain axis. 257: * @param rangeAxis the range axis. 258: * @param dataset the dataset. 259: * @param series the series index. 260: * @param item the item index. 261: * @param crosshairState the crosshair state. 262: * @param pass the pass index. 263: */ 264: public void drawItem(Graphics2D g2, XYItemRendererState state, 265: Rectangle2D dataArea, PlotRenderingInfo info, XYPlot plot, 266: ValueAxis domainAxis, ValueAxis rangeAxis, XYDataset dataset, 267: int series, int item, CrosshairState crosshairState, int pass) { 268: 269: if (pass == 0 && dataset instanceof IntervalXYDataset 270: && getItemVisible(series, item)) { 271: IntervalXYDataset ixyd = (IntervalXYDataset) dataset; 272: PlotOrientation orientation = plot.getOrientation(); 273: if (this.drawXError) { 274: // draw the error bar for the x-interval 275: double x0 = ixyd.getStartXValue(series, item); 276: double x1 = ixyd.getEndXValue(series, item); 277: double y = ixyd.getYValue(series, item); 278: RectangleEdge edge = plot.getDomainAxisEdge(); 279: double xx0 = domainAxis.valueToJava2D(x0, dataArea, edge); 280: double xx1 = domainAxis.valueToJava2D(x1, dataArea, edge); 281: double yy = rangeAxis.valueToJava2D(y, dataArea, 282: plot.getRangeAxisEdge()); 283: Line2D line; 284: Line2D cap1 = null; 285: Line2D cap2 = null; 286: double adj = this.capLength / 2.0; 287: if (orientation == PlotOrientation.VERTICAL) { 288: line = new Line2D.Double(xx0, yy, xx1, yy); 289: cap1 = new Line2D.Double(xx0, yy - adj, xx0, yy + adj); 290: cap2 = new Line2D.Double(xx1, yy - adj, xx1, yy + adj); 291: } 292: else { // PlotOrientation.HORIZONTAL 293: line = new Line2D.Double(yy, xx0, yy, xx1); 294: cap1 = new Line2D.Double(yy - adj, xx0, yy + adj, xx0); 295: cap2 = new Line2D.Double(yy - adj, xx1, yy + adj, xx1); 296: } 297: g2.setStroke(new BasicStroke(1.0f)); 298: if (this.errorPaint != null) { 299: g2.setPaint(this.errorPaint); 300: } 301: else { 302: g2.setPaint(getItemPaint(series, item)); 303: } 304: g2.draw(line); 305: g2.draw(cap1); 306: g2.draw(cap2); 307: } 308: if (this.drawYError) { 309: // draw the error bar for the y-interval 310: double y0 = ixyd.getStartYValue(series, item); 311: double y1 = ixyd.getEndYValue(series, item); 312: double x = ixyd.getXValue(series, item); 313: RectangleEdge edge = plot.getRangeAxisEdge(); 314: double yy0 = rangeAxis.valueToJava2D(y0, dataArea, edge); 315: double yy1 = rangeAxis.valueToJava2D(y1, dataArea, edge); 316: double xx = domainAxis.valueToJava2D(x, dataArea, 317: plot.getDomainAxisEdge()); 318: Line2D line; 319: Line2D cap1 = null; 320: Line2D cap2 = null; 321: double adj = this.capLength / 2.0; 322: if (orientation == PlotOrientation.VERTICAL) { 323: line = new Line2D.Double(xx, yy0, xx, yy1); 324: cap1 = new Line2D.Double(xx - adj, yy0, xx + adj, yy0); 325: cap2 = new Line2D.Double(xx - adj, yy1, xx + adj, yy1); 326: } 327: else { // PlotOrientation.HORIZONTAL 328: line = new Line2D.Double(yy0, xx, yy1, xx); 329: cap1 = new Line2D.Double(yy0, xx - adj, yy0, xx + adj); 330: cap2 = new Line2D.Double(yy1, xx - adj, yy1, xx + adj); 331: } 332: g2.setStroke(new BasicStroke(1.0f)); 333: if (this.errorPaint != null) { 334: g2.setPaint(this.errorPaint); 335: } 336: else { 337: g2.setPaint(getItemPaint(series, item)); 338: } 339: g2.draw(line); 340: g2.draw(cap1); 341: g2.draw(cap2); 342: } 343: } 344: super.drawItem(g2, state, dataArea, info, plot, domainAxis, rangeAxis, 345: dataset, series, item, crosshairState, pass); 346: } 347: 348: /** 349: * Tests this instance for equality with an arbitrary object. 350: * 351: * @param obj the object (<code>null</code> permitted). 352: * 353: * @return A boolean. 354: */ 355: public boolean equals(Object obj) { 356: if (obj == this) { 357: return true; 358: } 359: if (!(obj instanceof XYErrorRenderer)) { 360: return false; 361: } 362: XYErrorRenderer that = (XYErrorRenderer) obj; 363: if (this.drawXError != that.drawXError) { 364: return false; 365: } 366: if (this.drawYError != that.drawYError) { 367: return false; 368: } 369: if (this.capLength != that.capLength) { 370: return false; 371: } 372: if (!PaintUtilities.equal(this.errorPaint, that.errorPaint)) { 373: return false; 374: } 375: return super.equals(obj); 376: } 377: 378: /** 379: * Provides serialization support. 380: * 381: * @param stream the input stream. 382: * 383: * @throws IOException if there is an I/O error. 384: * @throws ClassNotFoundException if there is a classpath problem. 385: */ 386: private void readObject(ObjectInputStream stream) 387: throws IOException, ClassNotFoundException { 388: stream.defaultReadObject(); 389: this.errorPaint = SerialUtilities.readPaint(stream); 390: } 391: 392: /** 393: * Provides serialization support. 394: * 395: * @param stream the output stream. 396: * 397: * @throws IOException if there is an I/O error. 398: */ 399: private void writeObject(ObjectOutputStream stream) throws IOException { 400: stream.defaultWriteObject(); 401: SerialUtilities.writePaint(this.errorPaint, stream); 402: } 403: 404: }