001: /* ===========================================================
002: * JFreeChart : a free chart library for the Java(tm) platform
003: * ===========================================================
004: *
005: * (C) Copyright 2000-2007, by Object Refinery Limited and Contributors.
006: *
007: * Project Info: http://www.jfree.org/jfreechart/index.html
008: *
009: * This library is free software; you can redistribute it and/or modify it
010: * under the terms of the GNU Lesser General Public License as published by
011: * the Free Software Foundation; either version 2.1 of the License, or
012: * (at your option) any later version.
013: *
014: * This library is distributed in the hope that it will be useful, but
015: * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
016: * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
017: * License for more details.
018: *
019: * You should have received a copy of the GNU Lesser General Public
020: * License along with this library; if not, write to the Free Software
021: * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
022: * USA.
023: *
024: * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
025: * in the United States and other countries.]
026: *
027: * --------------------
028: * XYErrorRenderer.java
029: * --------------------
030: * (C) Copyright 2006, 2007, by Object Refinery Limited.
031: *
032: * Original Author: David Gilbert (for Object Refinery Limited);
033: * Contributor(s): -;
034: *
035: * $Id: XYErrorRenderer.java,v 1.1.2.4 2007/03/23 14:01:12 mungady Exp $
036: *
037: * Changes
038: * -------
039: * 25-Oct-2006 : Version 1 (DG);
040: * 23-Mar-2007 : Check item visibility before drawing error bars - see bug
041: * 1686178 (DG);
042: *
043: */
044:
045: package org.jfree.chart.renderer.xy;
046:
047: import java.awt.BasicStroke;
048: import java.awt.Graphics2D;
049: import java.awt.Paint;
050: import java.awt.geom.Line2D;
051: import java.awt.geom.Rectangle2D;
052: import java.io.IOException;
053: import java.io.ObjectInputStream;
054: import java.io.ObjectOutputStream;
055:
056: import org.jfree.chart.axis.ValueAxis;
057: import org.jfree.chart.event.RendererChangeEvent;
058: import org.jfree.chart.plot.CrosshairState;
059: import org.jfree.chart.plot.PlotOrientation;
060: import org.jfree.chart.plot.PlotRenderingInfo;
061: import org.jfree.chart.plot.XYPlot;
062: import org.jfree.data.Range;
063: import org.jfree.data.general.DatasetUtilities;
064: import org.jfree.data.xy.IntervalXYDataset;
065: import org.jfree.data.xy.XYDataset;
066: import org.jfree.io.SerialUtilities;
067: import org.jfree.ui.RectangleEdge;
068: import org.jfree.util.PaintUtilities;
069:
070: /**
071: * A line and shape renderer that can also display x and/or y-error values.
072: * This renderer expects an {@link IntervalXYDataset}, otherwise it reverts
073: * to the behaviour of the super class.
074: *
075: * @since 1.0.3
076: */
077: public class XYErrorRenderer extends XYLineAndShapeRenderer {
078:
079: /** A flag that controls whether or not the x-error bars are drawn. */
080: private boolean drawXError;
081:
082: /** A flag that controls whether or not the y-error bars are drawn. */
083: private boolean drawYError;
084:
085: /** The length of the cap at the end of the error bars. */
086: private double capLength;
087:
088: /**
089: * The paint used to draw the error bars (if <code>null</code> we use the
090: * series paint).
091: */
092: private transient Paint errorPaint;
093:
094: /**
095: * Creates a new <code>XYErrorRenderer</code> instance.
096: */
097: public XYErrorRenderer() {
098: super (false, true);
099: 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: } else {
223: return null;
224: }
225: }
226:
227: /**
228: * Returns the range required by this renderer to display all the range
229: * values in the specified dataset.
230: *
231: * @param dataset the dataset (<code>null</code> permitted).
232: *
233: * @return The range, or <code>null</code> if the dataset is
234: * <code>null</code>.
235: */
236: public Range findRangeBounds(XYDataset dataset) {
237: if (dataset != null) {
238: return DatasetUtilities.findRangeBounds(dataset, true);
239: } else {
240: return null;
241: }
242: }
243:
244: /**
245: * Draws the visual representation for one data item.
246: *
247: * @param g2 the graphics output target.
248: * @param state the renderer state.
249: * @param dataArea the data area.
250: * @param info the plot rendering info.
251: * @param plot the plot.
252: * @param domainAxis the domain axis.
253: * @param rangeAxis the range axis.
254: * @param dataset the dataset.
255: * @param series the series index.
256: * @param item the item index.
257: * @param crosshairState the crosshair state.
258: * @param pass the pass index.
259: */
260: public void drawItem(Graphics2D g2, XYItemRendererState state,
261: Rectangle2D dataArea, PlotRenderingInfo info, XYPlot plot,
262: ValueAxis domainAxis, ValueAxis rangeAxis,
263: XYDataset dataset, int series, int item,
264: CrosshairState crosshairState, int pass) {
265:
266: if (pass == 0 && dataset instanceof IntervalXYDataset
267: && getItemVisible(series, item)) {
268: IntervalXYDataset ixyd = (IntervalXYDataset) dataset;
269: PlotOrientation orientation = plot.getOrientation();
270: if (this .drawXError) {
271: // draw the error bar for the x-interval
272: double x0 = ixyd.getStartXValue(series, item);
273: double x1 = ixyd.getEndXValue(series, item);
274: double y = ixyd.getYValue(series, item);
275: RectangleEdge edge = plot.getDomainAxisEdge();
276: double xx0 = domainAxis.valueToJava2D(x0, dataArea,
277: edge);
278: double xx1 = domainAxis.valueToJava2D(x1, dataArea,
279: edge);
280: double yy = rangeAxis.valueToJava2D(y, dataArea, plot
281: .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
289: + adj);
290: cap2 = new Line2D.Double(xx1, yy - adj, xx1, yy
291: + adj);
292: } else { // PlotOrientation.HORIZONTAL
293: line = new Line2D.Double(yy, xx0, yy, xx1);
294: cap1 = new Line2D.Double(yy - adj, xx0, yy + adj,
295: xx0);
296: cap2 = new Line2D.Double(yy - adj, xx1, yy + adj,
297: xx1);
298: }
299: g2.setStroke(new BasicStroke(1.0f));
300: if (this .errorPaint != null) {
301: g2.setPaint(this .errorPaint);
302: } else {
303: g2.setPaint(getItemPaint(series, item));
304: }
305: g2.draw(line);
306: g2.draw(cap1);
307: g2.draw(cap2);
308: }
309: if (this .drawYError) {
310: // draw the error bar for the y-interval
311: double y0 = ixyd.getStartYValue(series, item);
312: double y1 = ixyd.getEndYValue(series, item);
313: double x = ixyd.getXValue(series, item);
314: RectangleEdge edge = plot.getRangeAxisEdge();
315: double yy0 = rangeAxis
316: .valueToJava2D(y0, dataArea, edge);
317: double yy1 = rangeAxis
318: .valueToJava2D(y1, dataArea, edge);
319: double xx = domainAxis.valueToJava2D(x, dataArea, plot
320: .getDomainAxisEdge());
321: Line2D line;
322: Line2D cap1 = null;
323: Line2D cap2 = null;
324: double adj = this .capLength / 2.0;
325: if (orientation == PlotOrientation.VERTICAL) {
326: line = new Line2D.Double(xx, yy0, xx, yy1);
327: cap1 = new Line2D.Double(xx - adj, yy0, xx + adj,
328: yy0);
329: cap2 = new Line2D.Double(xx - adj, yy1, xx + adj,
330: yy1);
331: } else { // PlotOrientation.HORIZONTAL
332: line = new Line2D.Double(yy0, xx, yy1, xx);
333: cap1 = new Line2D.Double(yy0, xx - adj, yy0, xx
334: + adj);
335: cap2 = new Line2D.Double(yy1, xx - adj, yy1, xx
336: + adj);
337: }
338: g2.setStroke(new BasicStroke(1.0f));
339: if (this .errorPaint != null) {
340: g2.setPaint(this .errorPaint);
341: } else {
342: g2.setPaint(getItemPaint(series, item));
343: }
344: g2.draw(line);
345: g2.draw(cap1);
346: g2.draw(cap2);
347: }
348: }
349: super .drawItem(g2, state, dataArea, info, plot, domainAxis,
350: rangeAxis, dataset, series, item, crosshairState, pass);
351: }
352:
353: /**
354: * Tests this instance for equality with an arbitrary object.
355: *
356: * @param obj the object (<code>null</code> permitted).
357: *
358: * @return A boolean.
359: */
360: public boolean equals(Object obj) {
361: if (obj == this ) {
362: return true;
363: }
364: if (!(obj instanceof XYErrorRenderer)) {
365: return false;
366: }
367: XYErrorRenderer that = (XYErrorRenderer) obj;
368: if (this .drawXError != that.drawXError) {
369: return false;
370: }
371: if (this .drawYError != that.drawYError) {
372: return false;
373: }
374: if (this .capLength != that.capLength) {
375: return false;
376: }
377: if (!PaintUtilities.equal(this .errorPaint, that.errorPaint)) {
378: return false;
379: }
380: return super .equals(obj);
381: }
382:
383: /**
384: * Provides serialization support.
385: *
386: * @param stream the input stream.
387: *
388: * @throws IOException if there is an I/O error.
389: * @throws ClassNotFoundException if there is a classpath problem.
390: */
391: private void readObject(ObjectInputStream stream)
392: throws IOException, ClassNotFoundException {
393: stream.defaultReadObject();
394: this .errorPaint = SerialUtilities.readPaint(stream);
395: }
396:
397: /**
398: * Provides serialization support.
399: *
400: * @param stream the output stream.
401: *
402: * @throws IOException if there is an I/O error.
403: */
404: private void writeObject(ObjectOutputStream stream)
405: throws IOException {
406: stream.defaultWriteObject();
407: SerialUtilities.writePaint(this.errorPaint, stream);
408: }
409:
410: }
|