KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jfree > chart > renderer > xy > XYBoxAndWhiskerRenderer


1 /* ===========================================================
2  * JFreeChart : a free chart library for the Java(tm) platform
3  * ===========================================================
4  *
5  * (C) Copyright 2000-2005, 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  * XYBoxAndWhiskerRenderer.java
29  * ----------------------------
30  * (C) Copyright 2003, 2004, by David Browning and Contributors.
31  *
32  * Original Author: David Browning (for Australian Institute of Marine
33  * Science);
34  * Contributor(s): David Gilbert (for Object Refinery Limited);
35  *
36  * $Id: XYBoxAndWhiskerRenderer.java,v 1.6.2.3 2005/10/25 20:56:21 mungady Exp $
37  *
38  * Changes
39  * -------
40  * 05-Aug-2003 : Version 1, contributed by David Browning. Based on code in the
41  * CandlestickRenderer class. Additional modifications by David
42  * Gilbert to make the code work with 0.9.10 changes (DG);
43  * 08-Aug-2003 : Updated some of the Javadoc
44  * Allowed BoxAndwhiskerDataset Average value to be null - the
45  * average value is an AIMS requirement
46  * Allow the outlier and farout coefficients to be set - though
47  * at the moment this only affects the calculation of farouts.
48  * Added artifactPaint variable and setter/getter
49  * 12-Aug-2003 Rewrote code to sort out and process outliers to take
50  * advantage of changes in DefaultBoxAndWhiskerDataset
51  * Added a limit of 10% for width of box should no width be
52  * specified...maybe this should be setable???
53  * 20-Aug-2003 : Implemented Cloneable and PublicCloneable (DG);
54  * 08-Sep-2003 : Changed ValueAxis API (DG);
55  * 16-Sep-2003 : Changed ChartRenderingInfo --> PlotRenderingInfo (DG);
56  * 25-Feb-2004 : Replaced CrosshairInfo with CrosshairState (DG);
57  * 23-Apr-2004 : Added fillBox attribute, extended equals() method and fixed
58  * serialization issue (DG);
59  * 29-Apr-2004 : Fixed problem with drawing upper and lower shadows - bug id
60  * 944011 (DG);
61  * 15-Jul-2004 : Switched getX() with getXValue() and getY() with
62  * getYValue() (DG);
63  * 01-Oct-2004 : Renamed 'paint' --> 'boxPaint' to avoid conflict with
64  * inherited attribute (DG);
65  * 10-Jun-2005 : Updated equals() to handle GradientPaint (DG);
66  * 06-Oct-2005 : Removed setPaint() call in drawItem(), it is causing a
67  * loop (DG);
68  *
69  * DO NOT USE drawHorizontalItem() - IT IS INCOMPLETE
70  * TO EXPERIMENT, USE drawVerticalItem()
71  */

72
73 package org.jfree.chart.renderer.xy;
74
75 import java.awt.Color JavaDoc;
76 import java.awt.Graphics2D JavaDoc;
77 import java.awt.Paint JavaDoc;
78 import java.awt.Shape JavaDoc;
79 import java.awt.Stroke JavaDoc;
80 import java.awt.geom.Ellipse2D JavaDoc;
81 import java.awt.geom.Line2D JavaDoc;
82 import java.awt.geom.Point2D JavaDoc;
83 import java.awt.geom.Rectangle2D JavaDoc;
84 import java.io.IOException JavaDoc;
85 import java.io.ObjectInputStream JavaDoc;
86 import java.io.ObjectOutputStream JavaDoc;
87 import java.io.Serializable JavaDoc;
88 import java.util.ArrayList JavaDoc;
89 import java.util.Collections JavaDoc;
90 import java.util.Iterator JavaDoc;
91 import java.util.List JavaDoc;
92
93 import org.jfree.chart.axis.ValueAxis;
94 import org.jfree.chart.entity.EntityCollection;
95 import org.jfree.chart.entity.XYItemEntity;
96 import org.jfree.chart.event.RendererChangeEvent;
97 import org.jfree.chart.labels.BoxAndWhiskerXYToolTipGenerator;
98 import org.jfree.chart.labels.XYToolTipGenerator;
99 import org.jfree.chart.plot.CrosshairState;
100 import org.jfree.chart.plot.PlotOrientation;
101 import org.jfree.chart.plot.PlotRenderingInfo;
102 import org.jfree.chart.plot.XYPlot;
103 import org.jfree.chart.renderer.Outlier;
104 import org.jfree.chart.renderer.OutlierList;
105 import org.jfree.chart.renderer.OutlierListCollection;
106 import org.jfree.data.statistics.BoxAndWhiskerXYDataset;
107 import org.jfree.data.xy.XYDataset;
108 import org.jfree.io.SerialUtilities;
109 import org.jfree.ui.RectangleEdge;
110 import org.jfree.util.PaintUtilities;
111 import org.jfree.util.PublicCloneable;
112
113 /**
114  * A renderer that draws box-and-whisker items on an {@link XYPlot}. This
115  * renderer requires a {@link BoxAndWhiskerXYDataset}).
116  * <P>
117  * This renderer does not include any code to calculate the crosshair point.
118  *
119  * @author David Browning
120  */

121 public class XYBoxAndWhiskerRenderer extends AbstractXYItemRenderer
122                                      implements XYItemRenderer,
123                                                 Cloneable JavaDoc,
124                                                 PublicCloneable,
125                                                 Serializable JavaDoc {
126
127     /** For serialization. */
128     private static final long serialVersionUID = -8020170108532232324L;
129     
130     /** The box width. */
131     private double boxWidth;
132
133     /** The paint used to fill the box. */
134     private transient Paint JavaDoc boxPaint;
135
136     /** A flag that controls whether or not the box is filled. */
137     private boolean fillBox;
138     
139     /**
140      * The paint used to draw various artifacts such as outliers, farout
141      * symbol, average ellipse and median line.
142      */

143     private transient Paint JavaDoc artifactPaint = Color.black;
144
145     /**
146      * Creates a new renderer for box and whisker charts.
147      */

148     public XYBoxAndWhiskerRenderer() {
149         this(-1.0);
150     }
151
152     /**
153      * Creates a new renderer for box and whisker charts.
154      * <P>
155      * Use -1 for the box width if you prefer the width to be calculated
156      * automatically.
157      *
158      * @param boxWidth the box width.
159      */

160     public XYBoxAndWhiskerRenderer(double boxWidth) {
161         super();
162         this.boxWidth = boxWidth;
163         this.boxPaint = Color.green;
164         this.fillBox = true;
165         setToolTipGenerator(new BoxAndWhiskerXYToolTipGenerator());
166     }
167
168     /**
169      * Returns the width of each box.
170      *
171      * @return The box width.
172      */

173     public double getBoxWidth() {
174         return this.boxWidth;
175     }
176
177     /**
178      * Sets the box width.
179      * <P>
180      * If you set the width to a negative value, the renderer will calculate
181      * the box width automatically based on the space available on the chart.
182      *
183      * @param width the width.
184      */

185     public void setBoxWidth(double width) {
186         if (width != this.boxWidth) {
187             this.boxWidth = width;
188             notifyListeners(new RendererChangeEvent(this));
189         }
190     }
191
192     /**
193      * Returns the paint used to fill boxes.
194      *
195      * @return The paint (possibly <code>null</code>).
196      */

197     public Paint JavaDoc getBoxPaint() {
198         return this.boxPaint;
199     }
200
201     /**
202      * Sets the paint used to fill boxes and sends a {@link RendererChangeEvent}
203      * to all registered listeners.
204      *
205      * @param paint the paint (<code>null</code> permitted).
206      */

207     public void setBoxPaint(Paint JavaDoc paint) {
208         this.boxPaint = paint;
209         notifyListeners(new RendererChangeEvent(this));
210     }
211     
212     /**
213      * Returns the flag that controls whether or not the box is filled.
214      *
215      * @return A boolean.
216      */

217     public boolean getFillBox() {
218         return this.fillBox;
219     }
220     
221     /**
222      * Sets the flag that controls whether or not the box is filled and sends a
223      * {@link RendererChangeEvent} to all registered listeners.
224      *
225      * @param flag the flag.
226      */

227     public void setFillBox(boolean flag) {
228         this.fillBox = flag;
229         notifyListeners(new RendererChangeEvent(this));
230     }
231
232     /**
233      * Returns the paint used to paint the various artifacts such as outliers,
234      * farout symbol, median line and the averages ellipse.
235      *
236      * @return The paint.
237      */

238     public Paint JavaDoc getArtifactPaint() {
239         return this.artifactPaint;
240     }
241
242     /**
243      * Sets the paint used to paint the various artifacts such as outliers,
244      * farout symbol, median line and the averages ellipse.
245      *
246      * @param artifactPaint the paint.
247      */

248     public void setArtifactPaint(Paint JavaDoc artifactPaint) {
249         this.artifactPaint = artifactPaint;
250     }
251
252     /**
253      * Draws the visual representation of a single data item.
254      *
255      * @param g2 the graphics device.
256      * @param state the renderer state.
257      * @param dataArea the area within which the plot is being drawn.
258      * @param info collects info about the drawing.
259      * @param plot the plot (can be used to obtain standard color
260      * information etc).
261      * @param domainAxis the domain axis.
262      * @param rangeAxis the range axis.
263      * @param dataset the dataset.
264      * @param series the series index (zero-based).
265      * @param item the item index (zero-based).
266      * @param crosshairState crosshair information for the plot
267      * (<code>null</code> permitted).
268      * @param pass the pass index.
269      */

270     public void drawItem(Graphics2D JavaDoc g2,
271                          XYItemRendererState state,
272                          Rectangle2D JavaDoc dataArea,
273                          PlotRenderingInfo info,
274                          XYPlot plot,
275                          ValueAxis domainAxis,
276                          ValueAxis rangeAxis,
277                          XYDataset dataset,
278                          int series,
279                          int item,
280                          CrosshairState crosshairState,
281                          int pass) {
282
283         PlotOrientation orientation = plot.getOrientation();
284
285         if (orientation == PlotOrientation.HORIZONTAL) {
286             drawHorizontalItem(
287                 g2, dataArea, info, plot, domainAxis, rangeAxis,
288                 dataset, series, item, crosshairState, pass
289             );
290         }
291         else if (orientation == PlotOrientation.VERTICAL) {
292             drawVerticalItem(
293                 g2, dataArea, info, plot, domainAxis, rangeAxis,
294                 dataset, series, item, crosshairState, pass
295             );
296         }
297
298     }
299
300     /**
301      * Draws the visual representation of a single data item.
302      *
303      * @param g2 the graphics device.
304      * @param dataArea the area within which the plot is being drawn.
305      * @param info collects info about the drawing.
306      * @param plot the plot (can be used to obtain standard color
307      * information etc).
308      * @param domainAxis the domain axis.
309      * @param rangeAxis the range axis.
310      * @param dataset the dataset.
311      * @param series the series index (zero-based).
312      * @param item the item index (zero-based).
313      * @param crosshairState crosshair information for the plot
314      * (<code>null</code> permitted).
315      * @param pass the pass index.
316      */

317     public void drawHorizontalItem(Graphics2D JavaDoc g2,
318                                    Rectangle2D JavaDoc dataArea,
319                                    PlotRenderingInfo info,
320                                    XYPlot plot,
321                                    ValueAxis domainAxis,
322                                    ValueAxis rangeAxis,
323                                    XYDataset dataset,
324                                    int series,
325                                    int item,
326                                    CrosshairState crosshairState,
327                                    int pass) {
328
329         // setup for collecting optional entity info...
330
EntityCollection entities = null;
331         if (info != null) {
332             entities = info.getOwner().getEntityCollection();
333         }
334
335         BoxAndWhiskerXYDataset boxAndWhiskerData
336             = (BoxAndWhiskerXYDataset) dataset;
337
338         Number JavaDoc x = boxAndWhiskerData.getX(series, item);
339         Number JavaDoc yMax = boxAndWhiskerData.getMaxRegularValue(series, item);
340         Number JavaDoc yMin = boxAndWhiskerData.getMinRegularValue(series, item);
341         Number JavaDoc yQ1Median = boxAndWhiskerData.getQ1Value(series, item);
342         Number JavaDoc yQ3Median = boxAndWhiskerData.getQ3Value(series, item);
343
344         double xx = domainAxis.valueToJava2D(
345             x.doubleValue(), dataArea, plot.getDomainAxisEdge()
346         );
347
348         RectangleEdge location = plot.getRangeAxisEdge();
349         double yyMax = rangeAxis.valueToJava2D(
350             yMax.doubleValue(), dataArea, location
351         );
352         double yyMin = rangeAxis.valueToJava2D(
353             yMin.doubleValue(), dataArea, location
354         );
355
356         double yyQ1Median = rangeAxis.valueToJava2D(
357             yQ1Median.doubleValue(), dataArea, location
358         );
359         double yyQ3Median = rangeAxis.valueToJava2D(
360             yQ3Median.doubleValue(), dataArea, location
361         );
362
363         double exactCandleWidth = getBoxWidth();
364         double thisCandleWidth = exactCandleWidth;
365         if (exactCandleWidth <= 0.0) {
366             int itemCount = boxAndWhiskerData.getItemCount(series);
367             exactCandleWidth = (dataArea.getHeight()) / itemCount * 4.5 / 7;
368             if (exactCandleWidth < 1) {
369                 exactCandleWidth = 1;
370             }
371             thisCandleWidth = exactCandleWidth;
372             if (thisCandleWidth < 3) {
373                 thisCandleWidth = 3;
374             }
375         }
376
377         Stroke JavaDoc s = getItemStroke(series, item);
378
379         g2.setStroke(s);
380
381         // draw the upper shadow
382
if ((yyMax > yyQ1Median) && (yyMax > yyQ3Median)) {
383             g2.draw(
384                 new Line2D.Double JavaDoc(yyMax, xx, Math.max(yyQ1Median, yyQ3Median),
385                         xx)
386             );
387         }
388
389         // draw the lower shadow
390
if ((yyMin < yyQ1Median) && (yyMin < yyQ3Median)) {
391             g2.draw(
392                 new Line2D.Double JavaDoc(yyMin, xx, Math.min(yyQ1Median, yyQ3Median),
393                         xx)
394             );
395         }
396
397
398         // draw the body
399
Shape JavaDoc box = null;
400         if (yyQ1Median < yyQ3Median) {
401             box = new Rectangle2D.Double JavaDoc(
402                 yyQ1Median, xx - thisCandleWidth / 2, yyQ3Median - yyQ1Median,
403                 thisCandleWidth
404             );
405         }
406         else {
407             box = new Rectangle2D.Double JavaDoc(
408                 yyQ3Median, xx - thisCandleWidth / 2, yyQ1Median - yyQ3Median,
409                 thisCandleWidth
410             );
411             if (getBoxPaint() != null) {
412                 g2.setPaint(getBoxPaint());
413             }
414             if (this.fillBox) {
415                 g2.fill(box);
416             }
417             g2.draw(box);
418         }
419
420         // add an entity for the item...
421
if (entities != null) {
422             String JavaDoc tip = null;
423             XYToolTipGenerator generator = getToolTipGenerator(series, item);
424             if (generator != null) {
425                 tip = generator.generateToolTip(dataset, series, item);
426             }
427             String JavaDoc url = null;
428             if (getURLGenerator() != null) {
429                 url = getURLGenerator().generateURL(dataset, series, item);
430             }
431             XYItemEntity entity = new XYItemEntity(box, dataset, series, item,
432                     tip, url);
433             entities.add(entity);
434         }
435
436     }
437
438     /**
439      * Draws the visual representation of a single data item.
440      *
441      * @param g2 the graphics device.
442      * @param dataArea the area within which the plot is being drawn.
443      * @param info collects info about the drawing.
444      * @param plot the plot (can be used to obtain standard color
445      * information etc).
446      * @param domainAxis the domain axis.
447      * @param rangeAxis the range axis.
448      * @param dataset the dataset.
449      * @param series the series index (zero-based).
450      * @param item the item index (zero-based).
451      * @param crosshairState crosshair information for the plot
452      * (<code>null</code> permitted).
453      * @param pass the pass index.
454      */

455     public void drawVerticalItem(Graphics2D JavaDoc g2,
456                                  Rectangle2D JavaDoc dataArea,
457                                  PlotRenderingInfo info,
458                                  XYPlot plot,
459                                  ValueAxis domainAxis,
460                                  ValueAxis rangeAxis,
461                                  XYDataset dataset,
462                                  int series,
463                                  int item,
464                                  CrosshairState crosshairState,
465                                  int pass) {
466
467         // setup for collecting optional entity info...
468
EntityCollection entities = null;
469         if (info != null) {
470             entities = info.getOwner().getEntityCollection();
471         }
472
473         BoxAndWhiskerXYDataset boxAndWhiskerData
474             = (BoxAndWhiskerXYDataset) dataset;
475
476         Number JavaDoc x = boxAndWhiskerData.getX(series, item);
477         Number JavaDoc yMax = boxAndWhiskerData.getMaxRegularValue(series, item);
478         Number JavaDoc yMin = boxAndWhiskerData.getMinRegularValue(series, item);
479         Number JavaDoc yMedian = boxAndWhiskerData.getMedianValue(series, item);
480         Number JavaDoc yAverage = boxAndWhiskerData.getMeanValue(series, item);
481         Number JavaDoc yQ1Median = boxAndWhiskerData.getQ1Value(series, item);
482         Number JavaDoc yQ3Median = boxAndWhiskerData.getQ3Value(series, item);
483         List JavaDoc yOutliers = boxAndWhiskerData.getOutliers(series, item);
484
485         double xx = domainAxis.valueToJava2D(x.doubleValue(), dataArea,
486                 plot.getDomainAxisEdge());
487
488         RectangleEdge location = plot.getRangeAxisEdge();
489         double yyMax = rangeAxis.valueToJava2D(yMax.doubleValue(), dataArea,
490                 location);
491         double yyMin = rangeAxis.valueToJava2D(yMin.doubleValue(), dataArea,
492                 location);
493         double yyMedian = rangeAxis.valueToJava2D(yMedian.doubleValue(),
494                 dataArea, location);
495         double yyAverage = 0.0;
496         if (yAverage != null) {
497             yyAverage = rangeAxis.valueToJava2D(yAverage.doubleValue(),
498                     dataArea, location);
499         }
500         double yyQ1Median = rangeAxis.valueToJava2D(yQ1Median.doubleValue(),
501                 dataArea, location);
502         double yyQ3Median = rangeAxis.valueToJava2D(yQ3Median.doubleValue(),
503                 dataArea, location);
504         double yyOutlier;
505
506
507         double exactBoxWidth = getBoxWidth();
508         double width = exactBoxWidth;
509         double dataAreaX = dataArea.getMaxX() - dataArea.getMinX();
510         double maxBoxPercent = 0.1;
511         double maxBoxWidth = dataAreaX * maxBoxPercent;
512         if (exactBoxWidth <= 0.0) {
513             int itemCount = boxAndWhiskerData.getItemCount(series);
514             exactBoxWidth = dataAreaX / itemCount * 4.5 / 7;
515             if (exactBoxWidth < 3) {
516                 width = 3;
517             }
518             else if (exactBoxWidth > maxBoxWidth) {
519                 width = maxBoxWidth;
520             }
521             else {
522                 width = exactBoxWidth;
523             }
524         }
525
526         Paint JavaDoc p = getBoxPaint();
527         if (p != null) {
528             g2.setPaint(p);
529         }
530         Stroke JavaDoc s = getItemStroke(series, item);
531
532         g2.setStroke(s);
533
534         // draw the upper shadow
535
g2.draw(new Line2D.Double JavaDoc(xx, yyMax, xx, yyQ3Median));
536         g2.draw(new Line2D.Double JavaDoc(xx - width / 2, yyMax, xx + width / 2,
537                 yyMax));
538
539         // draw the lower shadow
540
g2.draw(new Line2D.Double JavaDoc(xx, yyMin, xx, yyQ1Median));
541         g2.draw(new Line2D.Double JavaDoc(xx - width / 2, yyMin, xx + width / 2,
542                 yyMin));
543         
544         // draw the body
545
Shape JavaDoc box = null;
546         if (yyQ1Median > yyQ3Median) {
547             box = new Rectangle2D.Double JavaDoc(
548                 xx - width / 2, yyQ3Median, width, yyQ1Median - yyQ3Median
549             );
550         }
551         else {
552             box = new Rectangle2D.Double JavaDoc(
553                 xx - width / 2, yyQ1Median, width, yyQ3Median - yyQ1Median
554             );
555         }
556         if (this.fillBox) {
557             g2.fill(box);
558         }
559         g2.draw(box);
560
561         // draw median
562
g2.setPaint(getArtifactPaint());
563         g2.draw(new Line2D.Double JavaDoc(xx - width / 2, yyMedian, xx + width / 2,
564                 yyMedian));
565
566         double aRadius = 0; // average radius
567
double oRadius = width / 3; // outlier radius
568

569         // draw average - SPECIAL AIMS REQUIREMENT
570
if (yAverage != null) {
571             aRadius = width / 4;
572             Ellipse2D.Double JavaDoc avgEllipse = new Ellipse2D.Double JavaDoc(
573                 xx - aRadius, yyAverage - aRadius, aRadius * 2, aRadius * 2
574             );
575             g2.fill(avgEllipse);
576             g2.draw(avgEllipse);
577         }
578
579         List JavaDoc outliers = new ArrayList JavaDoc();
580         OutlierListCollection outlierListCollection
581             = new OutlierListCollection();
582
583         /* From outlier array sort out which are outliers and put these into
584          * an arraylist. If there are any farouts, set the flag on the
585          * OutlierListCollection
586          */

587
588         for (int i = 0; i < yOutliers.size(); i++) {
589             double outlier = ((Number JavaDoc) yOutliers.get(i)).doubleValue();
590             if (outlier > boxAndWhiskerData.getMaxOutlier(series,
591                     item).doubleValue()) {
592                 outlierListCollection.setHighFarOut(true);
593             }
594             else if (outlier < boxAndWhiskerData.getMinOutlier(series,
595                     item).doubleValue()) {
596                 outlierListCollection.setLowFarOut(true);
597             }
598             else if (outlier > boxAndWhiskerData.getMaxRegularValue(series,
599                     item).doubleValue()) {
600                 yyOutlier = rangeAxis.valueToJava2D(outlier, dataArea,
601                         location);
602                 outliers.add(new Outlier(xx, yyOutlier, oRadius));
603             }
604             else if (outlier < boxAndWhiskerData.getMinRegularValue(series,
605                     item).doubleValue()) {
606                 yyOutlier = rangeAxis.valueToJava2D(outlier, dataArea,
607                         location);
608                 outliers.add(new Outlier(xx, yyOutlier, oRadius));
609             }
610             Collections.sort(outliers);
611         }
612
613         // Process outliers. Each outlier is either added to the appropriate
614
// outlier list or a new outlier list is made
615
for (Iterator JavaDoc iterator = outliers.iterator(); iterator.hasNext();) {
616             Outlier outlier = (Outlier) iterator.next();
617             outlierListCollection.add(outlier);
618         }
619
620         // draw yOutliers
621
double maxAxisValue = rangeAxis.valueToJava2D(
622             rangeAxis.getUpperBound(), dataArea, location
623         ) + aRadius;
624         double minAxisValue = rangeAxis.valueToJava2D(
625             rangeAxis.getLowerBound(), dataArea, location
626         ) - aRadius;
627
628         // draw outliers
629
for (Iterator JavaDoc iterator = outlierListCollection.iterator();
630                 iterator.hasNext();) {
631             OutlierList list = (OutlierList) iterator.next();
632             Outlier outlier = list.getAveragedOutlier();
633             Point2D JavaDoc point = outlier.getPoint();
634
635             if (list.isMultiple()) {
636                 drawMultipleEllipse(point, width, oRadius, g2);
637             }
638             else {
639                 drawEllipse(point, oRadius, g2);
640             }
641         }
642
643         // draw farout
644
if (outlierListCollection.isHighFarOut()) {
645             drawHighFarOut(aRadius, g2, xx, maxAxisValue);
646         }
647
648         if (outlierListCollection.isLowFarOut()) {
649             drawLowFarOut(aRadius, g2, xx, minAxisValue);
650         }
651         
652         // add an entity for the item...
653
if (entities != null) {
654             String JavaDoc tip = null;
655             XYToolTipGenerator generator = getToolTipGenerator(series, item);
656             if (generator != null) {
657                 tip = generator.generateToolTip(dataset, series, item);
658             }
659             String JavaDoc url = null;
660             if (getURLGenerator() != null) {
661                 url = getURLGenerator().generateURL(dataset, series, item);
662             }
663             XYItemEntity entity = new XYItemEntity(box, dataset, series, item,
664                     tip, url);
665             entities.add(entity);
666         }
667
668     }
669
670     /**
671      * Draws an ellipse to represent an outlier.
672      *
673      * @param point the location.
674      * @param oRadius the radius.
675      * @param g2 the graphics device.
676      */

677     protected void drawEllipse(Point2D JavaDoc point, double oRadius, Graphics2D JavaDoc g2) {
678         Ellipse2D.Double JavaDoc dot = new Ellipse2D.Double JavaDoc(
679             point.getX() + oRadius / 2, point.getY(), oRadius, oRadius
680         );
681         g2.draw(dot);
682     }
683
684     /**
685      * Draws two ellipses to represent overlapping outliers.
686      *
687      * @param point the location.
688      * @param boxWidth the box width.
689      * @param oRadius the radius.
690      * @param g2 the graphics device.
691      */

692     protected void drawMultipleEllipse(Point2D JavaDoc point, double boxWidth,
693                                        double oRadius, Graphics2D JavaDoc g2) {
694                                          
695         Ellipse2D.Double JavaDoc dot1 = new Ellipse2D.Double JavaDoc(
696             point.getX() - (boxWidth / 2) + oRadius, point.getY(), oRadius,
697             oRadius
698         );
699         Ellipse2D.Double JavaDoc dot2 = new Ellipse2D.Double JavaDoc(
700             point.getX() + (boxWidth / 2), point.getY(), oRadius, oRadius
701         );
702         g2.draw(dot1);
703         g2.draw(dot2);
704         
705     }
706
707     /**
708      * Draws a triangle to indicate the presence of far out values.
709      *
710      * @param aRadius the radius.
711      * @param g2 the graphics device.
712      * @param xx the x value.
713      * @param m the max y value.
714      */

715     protected void drawHighFarOut(double aRadius, Graphics2D JavaDoc g2, double xx,
716             double m) {
717         double side = aRadius * 2;
718         g2.draw(new Line2D.Double JavaDoc(xx - side, m + side, xx + side, m + side));
719         g2.draw(new Line2D.Double JavaDoc(xx - side, m + side, xx, m));
720         g2.draw(new Line2D.Double JavaDoc(xx + side, m + side, xx, m));
721     }
722
723     /**
724      * Draws a triangle to indicate the presence of far out values.
725      *
726      * @param aRadius the radius.
727      * @param g2 the graphics device.
728      * @param xx the x value.
729      * @param m the min y value.
730      */

731     protected void drawLowFarOut(double aRadius, Graphics2D JavaDoc g2, double xx,
732             double m) {
733         double side = aRadius * 2;
734         g2.draw(new Line2D.Double JavaDoc(xx - side, m - side, xx + side, m - side));
735         g2.draw(new Line2D.Double JavaDoc(xx - side, m - side, xx, m));
736         g2.draw(new Line2D.Double JavaDoc(xx + side, m - side, xx, m));
737     }
738
739     /**
740      * Tests this renderer for equality with another object.
741      *
742      * @param obj the object (<code>null</code> permitted).
743      *
744      * @return <code>true</code> or <code>false</code>.
745      */

746     public boolean equals(Object JavaDoc obj) {
747         if (obj == this) {
748             return true;
749         }
750         if (!(obj instanceof XYBoxAndWhiskerRenderer)) {
751             return false;
752         }
753         if (!super.equals(obj)) {
754             return false;
755         }
756         XYBoxAndWhiskerRenderer that = (XYBoxAndWhiskerRenderer) obj;
757         if (this.boxWidth != that.getBoxWidth()) {
758             return false;
759         }
760         if (!PaintUtilities.equal(this.boxPaint, that.boxPaint)) {
761             return false;
762         }
763         if (!PaintUtilities.equal(this.artifactPaint, that.artifactPaint)) {
764             return false;
765         }
766         if (this.fillBox != that.fillBox) {
767             return false;
768         }
769         return true;
770
771     }
772
773     /**
774      * Provides serialization support.
775      *
776      * @param stream the output stream.
777      *
778      * @throws IOException if there is an I/O error.
779      */

780     private void writeObject(ObjectOutputStream JavaDoc stream) throws IOException JavaDoc {
781
782         stream.defaultWriteObject();
783         SerialUtilities.writePaint(this.boxPaint, stream);
784         SerialUtilities.writePaint(this.artifactPaint, stream);
785
786     }
787
788     /**
789      * Provides serialization support.
790      *
791      * @param stream the input stream.
792      *
793      * @throws IOException if there is an I/O error.
794      * @throws ClassNotFoundException if there is a classpath problem.
795      */

796     private void readObject(ObjectInputStream JavaDoc stream)
797         throws IOException JavaDoc, ClassNotFoundException JavaDoc {
798
799         stream.defaultReadObject();
800         this.boxPaint = SerialUtilities.readPaint(stream);
801         this.artifactPaint = SerialUtilities.readPaint(stream);
802
803     }
804
805     /**
806      * Returns a clone of the renderer.
807      *
808      * @return A clone.
809      *
810      * @throws CloneNotSupportedException if the renderer cannot be cloned.
811      */

812     public Object JavaDoc clone() throws CloneNotSupportedException JavaDoc {
813         return super.clone();
814     }
815
816 }
817
Popular Tags