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 }
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 }