KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jfree > chart > renderer > category > BoxAndWhiskerRenderer


1 /* ===========================================================
2  * JFreeChart : a free chart library for the Java(tm) platform
3  * ===========================================================
4  *
5  * (C) Copyright 2000-2006, 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  * BoxAndWhiskerRenderer.java
29  * --------------------------
30  * (C) Copyright 2003-2006, by David Browning and Contributors.
31  *
32  * Original Author: David Browning (for the Australian Institute of Marine
33  * Science);
34  * Contributor(s): David Gilbert (for Object Refinery Limited);
35  * Tim Bardzil;
36  *
37  * $Id: BoxAndWhiskerRenderer.java,v 1.8.2.8 2006/10/23 09:17:27 mungady Exp $
38  *
39  * Changes
40  * -------
41  * 21-Aug-2003 : Version 1, contributed by David Browning (for the Australian
42  * Institute of Marine Science);
43  * 01-Sep-2003 : Incorporated outlier and farout symbols for low values
44  * also (DG);
45  * 08-Sep-2003 : Changed ValueAxis API (DG);
46  * 16-Sep-2003 : Changed ChartRenderingInfo --> PlotRenderingInfo (DG);
47  * 07-Oct-2003 : Added renderer state (DG);
48  * 12-Nov-2003 : Fixed casting bug reported by Tim Bardzil (DG);
49  * 13-Nov-2003 : Added drawHorizontalItem() method contributed by Tim
50  * Bardzil (DG);
51  * 25-Apr-2004 : Added fillBox attribute, equals() method and added
52  * serialization code (DG);
53  * 29-Apr-2004 : Changed drawing of upper and lower shadows - see bug report
54  * 944011 (DG);
55  * 05-Nov-2004 : Modified drawItem() signature (DG);
56  * 09-Mar-2005 : Override getLegendItem() method so that legend item shapes
57  * are shown as blocks (DG);
58  * 20-Apr-2005 : Generate legend labels, tooltips and URLs (DG);
59  * 09-Jun-2005 : Updated equals() to handle GradientPaint (DG);
60  * ------------- JFREECHART 1.0.x ---------------------------------------------
61  * 12-Oct-2006 : Source reformatting and API doc updates (DG);
62  * 12-Oct-2006 : Fixed bug 1572478, potential NullPointerException (DG);
63  *
64  */

65
66 package org.jfree.chart.renderer.category;
67
68 import java.awt.Color JavaDoc;
69 import java.awt.Graphics2D JavaDoc;
70 import java.awt.Paint JavaDoc;
71 import java.awt.Shape JavaDoc;
72 import java.awt.Stroke JavaDoc;
73 import java.awt.geom.Ellipse2D JavaDoc;
74 import java.awt.geom.Line2D JavaDoc;
75 import java.awt.geom.Point2D JavaDoc;
76 import java.awt.geom.Rectangle2D JavaDoc;
77 import java.io.IOException JavaDoc;
78 import java.io.ObjectInputStream JavaDoc;
79 import java.io.ObjectOutputStream JavaDoc;
80 import java.io.Serializable JavaDoc;
81 import java.util.ArrayList JavaDoc;
82 import java.util.Collections JavaDoc;
83 import java.util.Iterator JavaDoc;
84 import java.util.List JavaDoc;
85
86 import org.jfree.chart.LegendItem;
87 import org.jfree.chart.axis.CategoryAxis;
88 import org.jfree.chart.axis.ValueAxis;
89 import org.jfree.chart.entity.CategoryItemEntity;
90 import org.jfree.chart.entity.EntityCollection;
91 import org.jfree.chart.event.RendererChangeEvent;
92 import org.jfree.chart.labels.CategoryToolTipGenerator;
93 import org.jfree.chart.plot.CategoryPlot;
94 import org.jfree.chart.plot.PlotOrientation;
95 import org.jfree.chart.plot.PlotRenderingInfo;
96 import org.jfree.chart.renderer.Outlier;
97 import org.jfree.chart.renderer.OutlierList;
98 import org.jfree.chart.renderer.OutlierListCollection;
99 import org.jfree.data.category.CategoryDataset;
100 import org.jfree.data.statistics.BoxAndWhiskerCategoryDataset;
101 import org.jfree.io.SerialUtilities;
102 import org.jfree.ui.RectangleEdge;
103 import org.jfree.util.PaintUtilities;
104 import org.jfree.util.PublicCloneable;
105
106 /**
107  * A box-and-whisker renderer. This renderer requires a
108  * {@link BoxAndWhiskerCategoryDataset} and is for use with the
109  * {@link CategoryPlot} class.
110  */

111 public class BoxAndWhiskerRenderer extends AbstractCategoryItemRenderer
112                                    implements Cloneable JavaDoc, PublicCloneable,
113                                               Serializable JavaDoc {
114
115     /** For serialization. */
116     private static final long serialVersionUID = 632027470694481177L;
117     
118     /** The color used to paint the median line and average marker. */
119     private transient Paint JavaDoc artifactPaint;
120
121     /** A flag that controls whether or not the box is filled. */
122     private boolean fillBox;
123     
124     /** The margin between items (boxes) within a category. */
125     private double itemMargin;
126
127     /**
128      * Default constructor.
129      */

130     public BoxAndWhiskerRenderer() {
131         this.artifactPaint = Color.black;
132         this.fillBox = true;
133         this.itemMargin = 0.20;
134     }
135
136     /**
137      * Returns the paint used to color the median and average markers.
138      *
139      * @return A paint.
140      *
141      * @see #setArtifactPaint(Paint)
142      */

143     public Paint JavaDoc getArtifactPaint() {
144         return this.artifactPaint;
145     }
146
147     /**
148      * Sets the paint used to color the median and average markers.
149      *
150      * @param paint the paint.
151      *
152      * @see #getArtifactPaint()
153      */

154     public void setArtifactPaint(Paint JavaDoc paint) {
155         this.artifactPaint = paint;
156     }
157
158     /**
159      * Returns the flag that controls whether or not the box is filled.
160      *
161      * @return A boolean.
162      *
163      * @see #setFillBox(boolean)
164      */

165     public boolean getFillBox() {
166         return this.fillBox;
167     }
168     
169     /**
170      * Sets the flag that controls whether or not the box is filled and sends a
171      * {@link RendererChangeEvent} to all registered listeners.
172      *
173      * @param flag the flag.
174      *
175      * @see #getFillBox()
176      */

177     public void setFillBox(boolean flag) {
178         this.fillBox = flag;
179         notifyListeners(new RendererChangeEvent(this));
180     }
181
182     /**
183      * Returns the item margin. This is a percentage of the available space
184      * that is allocated to the space between items in the chart.
185      *
186      * @return The margin.
187      *
188      * @see #setItemMargin(double)
189      */

190     public double getItemMargin() {
191         return this.itemMargin;
192     }
193
194     /**
195      * Sets the item margin.
196      *
197      * @param margin the margin.
198      *
199      * @see #getItemMargin()
200      */

201     public void setItemMargin(double margin) {
202         this.itemMargin = margin;
203     }
204
205     /**
206      * Returns a legend item for a series.
207      *
208      * @param datasetIndex the dataset index (zero-based).
209      * @param series the series index (zero-based).
210      *
211      * @return The legend item.
212      */

213     public LegendItem getLegendItem(int datasetIndex, int series) {
214
215         CategoryPlot cp = getPlot();
216         if (cp == null) {
217             return null;
218         }
219
220         CategoryDataset dataset;
221         dataset = cp.getDataset(datasetIndex);
222         String JavaDoc label = getLegendItemLabelGenerator().generateLabel(dataset,
223                 series);
224         String JavaDoc description = label;
225         String JavaDoc toolTipText = null;
226         if (getLegendItemToolTipGenerator() != null) {
227             toolTipText = getLegendItemToolTipGenerator().generateLabel(
228                     dataset, series);
229         }
230         String JavaDoc urlText = null;
231         if (getLegendItemURLGenerator() != null) {
232             urlText = getLegendItemURLGenerator().generateLabel(dataset,
233                     series);
234         }
235         Shape JavaDoc shape = new Rectangle2D.Double JavaDoc(-4.0, -4.0, 8.0, 8.0);
236         Paint JavaDoc paint = getSeriesPaint(series);
237         Paint JavaDoc outlinePaint = getSeriesOutlinePaint(series);
238         Stroke JavaDoc outlineStroke = getSeriesOutlineStroke(series);
239
240         return new LegendItem(label, description, toolTipText, urlText,
241                 shape, paint, outlineStroke, outlinePaint);
242
243     }
244
245     /**
246      * Initialises the renderer. This method gets called once at the start of
247      * the process of drawing a chart.
248      *
249      * @param g2 the graphics device.
250      * @param dataArea the area in which the data is to be plotted.
251      * @param plot the plot.
252      * @param rendererIndex the renderer index.
253      * @param info collects chart rendering information for return to caller.
254      *
255      * @return The renderer state.
256      */

257     public CategoryItemRendererState initialise(Graphics2D JavaDoc g2,
258                                                 Rectangle2D JavaDoc dataArea,
259                                                 CategoryPlot plot,
260                                                 int rendererIndex,
261                                                 PlotRenderingInfo info) {
262
263         CategoryItemRendererState state = super.initialise(g2, dataArea, plot,
264                 rendererIndex, info);
265
266         // calculate the box width
267
CategoryAxis domainAxis = getDomainAxis(plot, rendererIndex);
268         CategoryDataset dataset = plot.getDataset(rendererIndex);
269         if (dataset != null) {
270             int columns = dataset.getColumnCount();
271             int rows = dataset.getRowCount();
272             double space = 0.0;
273             PlotOrientation orientation = plot.getOrientation();
274             if (orientation == PlotOrientation.HORIZONTAL) {
275                 space = dataArea.getHeight();
276             }
277             else if (orientation == PlotOrientation.VERTICAL) {
278                 space = dataArea.getWidth();
279             }
280             double categoryMargin = 0.0;
281             double currentItemMargin = 0.0;
282             if (columns > 1) {
283                 categoryMargin = domainAxis.getCategoryMargin();
284             }
285             if (rows > 1) {
286                 currentItemMargin = getItemMargin();
287             }
288             double used = space * (1 - domainAxis.getLowerMargin()
289                                      - domainAxis.getUpperMargin()
290                                      - categoryMargin - currentItemMargin);
291             if ((rows * columns) > 0) {
292                 state.setBarWidth(used / (dataset.getColumnCount()
293                         * dataset.getRowCount()));
294             }
295             else {
296                 state.setBarWidth(used);
297             }
298         }
299         
300         return state;
301
302     }
303
304     /**
305      * Draw a single data item.
306      *
307      * @param g2 the graphics device.
308      * @param state the renderer state.
309      * @param dataArea the area in which the data is drawn.
310      * @param plot the plot.
311      * @param domainAxis the domain axis.
312      * @param rangeAxis the range axis.
313      * @param dataset the data.
314      * @param row the row index (zero-based).
315      * @param column the column index (zero-based).
316      * @param pass the pass index.
317      */

318     public void drawItem(Graphics2D JavaDoc g2,
319                          CategoryItemRendererState state,
320                          Rectangle2D JavaDoc dataArea,
321                          CategoryPlot plot,
322                          CategoryAxis domainAxis,
323                          ValueAxis rangeAxis,
324                          CategoryDataset dataset,
325                          int row,
326                          int column,
327                          int pass) {
328                              
329         if (!(dataset instanceof BoxAndWhiskerCategoryDataset)) {
330             throw new IllegalArgumentException JavaDoc(
331                     "BoxAndWhiskerRenderer.drawItem() : the data should be "
332                     + "of type BoxAndWhiskerCategoryDataset only."
333             );
334         }
335
336         PlotOrientation orientation = plot.getOrientation();
337
338         if (orientation == PlotOrientation.HORIZONTAL) {
339             drawHorizontalItem(g2, state, dataArea, plot, domainAxis,
340                     rangeAxis, dataset, row, column);
341         }
342         else if (orientation == PlotOrientation.VERTICAL) {
343             drawVerticalItem(g2, state, dataArea, plot, domainAxis,
344                     rangeAxis, dataset, row, column);
345         }
346         
347     }
348
349     /**
350      * Draws the visual representation of a single data item when the plot has
351      * a horizontal orientation.
352      *
353      * @param g2 the graphics device.
354      * @param state the renderer state.
355      * @param dataArea the area within which the plot is being drawn.
356      * @param plot the plot (can be used to obtain standard color
357      * information etc).
358      * @param domainAxis the domain axis.
359      * @param rangeAxis the range axis.
360      * @param dataset the dataset.
361      * @param row the row index (zero-based).
362      * @param column the column index (zero-based).
363      */

364     public void drawHorizontalItem(Graphics2D JavaDoc g2,
365                                    CategoryItemRendererState state,
366                                    Rectangle2D JavaDoc dataArea,
367                                    CategoryPlot plot,
368                                    CategoryAxis domainAxis,
369                                    ValueAxis rangeAxis,
370                                    CategoryDataset dataset,
371                                    int row,
372                                    int column) {
373
374         BoxAndWhiskerCategoryDataset bawDataset
375                 = (BoxAndWhiskerCategoryDataset) dataset;
376
377         double categoryEnd = domainAxis.getCategoryEnd(column,
378                 getColumnCount(), dataArea, plot.getDomainAxisEdge());
379         double categoryStart = domainAxis.getCategoryStart(column,
380                 getColumnCount(), dataArea, plot.getDomainAxisEdge());
381         double categoryWidth = Math.abs(categoryEnd - categoryStart);
382
383         double yy = categoryStart;
384         int seriesCount = getRowCount();
385         int categoryCount = getColumnCount();
386
387         if (seriesCount > 1) {
388             double seriesGap = dataArea.getWidth() * getItemMargin()
389                                / (categoryCount * (seriesCount - 1));
390             double usedWidth = (state.getBarWidth() * seriesCount)
391                                + (seriesGap * (seriesCount - 1));
392             // offset the start of the boxes if the total width used is smaller
393
// than the category width
394
double offset = (categoryWidth - usedWidth) / 2;
395             yy = yy + offset + (row * (state.getBarWidth() + seriesGap));
396         }
397         else {
398             // offset the start of the box if the box width is smaller than
399
// the category width
400
double offset = (categoryWidth - state.getBarWidth()) / 2;
401             yy = yy + offset;
402         }
403
404         Paint JavaDoc p = getItemPaint(row, column);
405         if (p != null) {
406             g2.setPaint(p);
407         }
408         Stroke JavaDoc s = getItemStroke(row, column);
409         g2.setStroke(s);
410
411         RectangleEdge location = plot.getRangeAxisEdge();
412
413         Number JavaDoc xQ1 = bawDataset.getQ1Value(row, column);
414         Number JavaDoc xQ3 = bawDataset.getQ3Value(row, column);
415         Number JavaDoc xMax = bawDataset.getMaxRegularValue(row, column);
416         Number JavaDoc xMin = bawDataset.getMinRegularValue(row, column);
417
418         Shape JavaDoc box = null;
419         if (xQ1 != null && xQ3 != null && xMax != null && xMin != null) {
420
421             double xxQ1 = rangeAxis.valueToJava2D(xQ1.doubleValue(), dataArea,
422                     location);
423             double xxQ3 = rangeAxis.valueToJava2D(xQ3.doubleValue(), dataArea,
424                     location);
425             double xxMax = rangeAxis.valueToJava2D(xMax.doubleValue(), dataArea,
426                     location);
427             double xxMin = rangeAxis.valueToJava2D(xMin.doubleValue(), dataArea,
428                     location);
429             double yymid = yy + state.getBarWidth() / 2.0;
430             
431             // draw the upper shadow...
432
g2.draw(new Line2D.Double JavaDoc(xxMax, yymid, xxQ3, yymid));
433             g2.draw(new Line2D.Double JavaDoc(xxMax, yy, xxMax,
434                     yy + state.getBarWidth()));
435
436             // draw the lower shadow...
437
g2.draw(new Line2D.Double JavaDoc(xxMin, yymid, xxQ1, yymid));
438             g2.draw(new Line2D.Double JavaDoc(xxMin, yy, xxMin,
439                     yy + state.getBarWidth()));
440
441             // draw the box...
442
box = new Rectangle2D.Double JavaDoc(Math.min(xxQ1, xxQ3), yy,
443                     Math.abs(xxQ1 - xxQ3), state.getBarWidth());
444             if (this.fillBox) {
445                 g2.fill(box);
446             }
447             g2.draw(box);
448
449         }
450
451         g2.setPaint(this.artifactPaint);
452         double aRadius = 0; // average radius
453

454         // draw mean - SPECIAL AIMS REQUIREMENT...
455
Number JavaDoc xMean = bawDataset.getMeanValue(row, column);
456         if (xMean != null) {
457             double xxMean = rangeAxis.valueToJava2D(xMean.doubleValue(),
458                     dataArea, location);
459             aRadius = state.getBarWidth() / 4;
460             Ellipse2D.Double JavaDoc avgEllipse = new Ellipse2D.Double JavaDoc(xxMean
461                     - aRadius, yy + aRadius, aRadius * 2, aRadius * 2);
462             g2.fill(avgEllipse);
463             g2.draw(avgEllipse);
464         }
465
466         // draw median...
467
Number JavaDoc xMedian = bawDataset.getMedianValue(row, column);
468         if (xMedian != null) {
469             double xxMedian = rangeAxis.valueToJava2D(xMedian.doubleValue(),
470                     dataArea, location);
471             g2.draw(new Line2D.Double JavaDoc(xxMedian, yy, xxMedian,
472                     yy + state.getBarWidth()));
473         }
474         
475         // collect entity and tool tip information...
476
if (state.getInfo() != null && box != null) {
477             EntityCollection entities = state.getEntityCollection();
478             if (entities != null) {
479                 String JavaDoc tip = null;
480                 CategoryToolTipGenerator tipster
481                         = getToolTipGenerator(row, column);
482                 if (tipster != null) {
483                     tip = tipster.generateToolTip(dataset, row, column);
484                 }
485                 String JavaDoc url = null;
486                 if (getItemURLGenerator(row, column) != null) {
487                     url = getItemURLGenerator(row, column).generateURL(
488                             dataset, row, column);
489                 }
490                 CategoryItemEntity entity = new CategoryItemEntity(box, tip,
491                         url, dataset, row, dataset.getColumnKey(column),
492                         column);
493                 entities.add(entity);
494             }
495         }
496
497     }
498         
499     /**
500      * Draws the visual representation of a single data item when the plot has
501      * a vertical orientation.
502      *
503      * @param g2 the graphics device.
504      * @param state the renderer state.
505      * @param dataArea the area within which the plot is being drawn.
506      * @param plot the plot (can be used to obtain standard color information
507      * etc).
508      * @param domainAxis the domain axis.
509      * @param rangeAxis the range axis.
510      * @param dataset the dataset.
511      * @param row the row index (zero-based).
512      * @param column the column index (zero-based).
513      */

514     public void drawVerticalItem(Graphics2D JavaDoc g2,
515                                  CategoryItemRendererState state,
516                                  Rectangle2D JavaDoc dataArea,
517                                  CategoryPlot plot,
518                                  CategoryAxis domainAxis,
519                                  ValueAxis rangeAxis,
520                                  CategoryDataset dataset,
521                                  int row,
522                                  int column) {
523
524         BoxAndWhiskerCategoryDataset bawDataset
525                 = (BoxAndWhiskerCategoryDataset) dataset;
526         
527         double categoryEnd = domainAxis.getCategoryEnd(column,
528                 getColumnCount(), dataArea, plot.getDomainAxisEdge());
529         double categoryStart = domainAxis.getCategoryStart(column,
530                 getColumnCount(), dataArea, plot.getDomainAxisEdge());
531         double categoryWidth = categoryEnd - categoryStart;
532
533         double xx = categoryStart;
534         int seriesCount = getRowCount();
535         int categoryCount = getColumnCount();
536
537         if (seriesCount > 1) {
538             double seriesGap = dataArea.getWidth() * getItemMargin()
539                                / (categoryCount * (seriesCount - 1));
540             double usedWidth = (state.getBarWidth() * seriesCount)
541                                + (seriesGap * (seriesCount - 1));
542             // offset the start of the boxes if the total width used is smaller
543
// than the category width
544
double offset = (categoryWidth - usedWidth) / 2;
545             xx = xx + offset + (row * (state.getBarWidth() + seriesGap));
546         }
547         else {
548             // offset the start of the box if the box width is smaller than the
549
// category width
550
double offset = (categoryWidth - state.getBarWidth()) / 2;
551             xx = xx + offset;
552         }
553         
554         double yyAverage = 0.0;
555         double yyOutlier;
556
557         Paint JavaDoc p = getItemPaint(row, column);
558         if (p != null) {
559             g2.setPaint(p);
560         }
561         Stroke JavaDoc s = getItemStroke(row, column);
562         g2.setStroke(s);
563
564         double aRadius = 0; // average radius
565

566         RectangleEdge location = plot.getRangeAxisEdge();
567
568         Number JavaDoc yQ1 = bawDataset.getQ1Value(row, column);
569         Number JavaDoc yQ3 = bawDataset.getQ3Value(row, column);
570         Number JavaDoc yMax = bawDataset.getMaxRegularValue(row, column);
571         Number JavaDoc yMin = bawDataset.getMinRegularValue(row, column);
572         Shape JavaDoc box = null;
573         if (yQ1 != null && yQ3 != null && yMax != null && yMin != null) {
574
575             double yyQ1 = rangeAxis.valueToJava2D(yQ1.doubleValue(), dataArea,
576                     location);
577             double yyQ3 = rangeAxis.valueToJava2D(yQ3.doubleValue(), dataArea,
578                     location);
579             double yyMax = rangeAxis.valueToJava2D(yMax.doubleValue(),
580                     dataArea, location);
581             double yyMin = rangeAxis.valueToJava2D(yMin.doubleValue(),
582                     dataArea, location);
583             double xxmid = xx + state.getBarWidth() / 2.0;
584             
585             // draw the upper shadow...
586
g2.draw(new Line2D.Double JavaDoc(xxmid, yyMax, xxmid, yyQ3));
587             g2.draw(new Line2D.Double JavaDoc(xx, yyMax, xx + state.getBarWidth(),
588                     yyMax));
589
590             // draw the lower shadow...
591
g2.draw(new Line2D.Double JavaDoc(xxmid, yyMin, xxmid, yyQ1));
592             g2.draw(new Line2D.Double JavaDoc(xx, yyMin, xx + state.getBarWidth(),
593                     yyMin));
594
595             // draw the body...
596
box = new Rectangle2D.Double JavaDoc(xx, Math.min(yyQ1, yyQ3),
597                     state.getBarWidth(), Math.abs(yyQ1 - yyQ3));
598             if (this.fillBox) {
599                 g2.fill(box);
600             }
601             g2.draw(box);
602   
603         }
604         
605         g2.setPaint(this.artifactPaint);
606
607         // draw mean - SPECIAL AIMS REQUIREMENT...
608
Number JavaDoc yMean = bawDataset.getMeanValue(row, column);
609         if (yMean != null) {
610             yyAverage = rangeAxis.valueToJava2D(yMean.doubleValue(),
611                     dataArea, location);
612             aRadius = state.getBarWidth() / 4;
613             Ellipse2D.Double JavaDoc avgEllipse = new Ellipse2D.Double JavaDoc(xx + aRadius,
614                     yyAverage - aRadius, aRadius * 2, aRadius * 2);
615             g2.fill(avgEllipse);
616             g2.draw(avgEllipse);
617         }
618
619         // draw median...
620
Number JavaDoc yMedian = bawDataset.getMedianValue(row, column);
621         if (yMedian != null) {
622             double yyMedian = rangeAxis.valueToJava2D(yMedian.doubleValue(),
623                     dataArea, location);
624             g2.draw(new Line2D.Double JavaDoc(xx, yyMedian, xx + state.getBarWidth(),
625                     yyMedian));
626         }
627         
628         // draw yOutliers...
629
double maxAxisValue = rangeAxis.valueToJava2D(
630                 rangeAxis.getUpperBound(), dataArea, location) + aRadius;
631         double minAxisValue = rangeAxis.valueToJava2D(
632                 rangeAxis.getLowerBound(), dataArea, location) - aRadius;
633
634         g2.setPaint(p);
635
636         // draw outliers
637
double oRadius = state.getBarWidth() / 3; // outlier radius
638
List JavaDoc outliers = new ArrayList JavaDoc();
639         OutlierListCollection outlierListCollection
640                 = new OutlierListCollection();
641
642         // From outlier array sort out which are outliers and put these into a
643
// list If there are any farouts, set the flag on the
644
// OutlierListCollection
645
List JavaDoc yOutliers = bawDataset.getOutliers(row, column);
646         if (yOutliers != null) {
647             for (int i = 0; i < yOutliers.size(); i++) {
648                 double outlier = ((Number JavaDoc) yOutliers.get(i)).doubleValue();
649                 Number JavaDoc minOutlier = bawDataset.getMinOutlier(row, column);
650                 Number JavaDoc maxOutlier = bawDataset.getMaxOutlier(row, column);
651                 Number JavaDoc minRegular = bawDataset.getMinRegularValue(row, column);
652                 Number JavaDoc maxRegular = bawDataset.getMaxRegularValue(row, column);
653                 if (outlier > maxOutlier.doubleValue()) {
654                     outlierListCollection.setHighFarOut(true);
655                 }
656                 else if (outlier < minOutlier.doubleValue()) {
657                     outlierListCollection.setLowFarOut(true);
658                 }
659                 else if (outlier > maxRegular.doubleValue()) {
660                     yyOutlier = rangeAxis.valueToJava2D(outlier, dataArea,
661                             location);
662                     outliers.add(new Outlier(xx + state.getBarWidth() / 2.0,
663                             yyOutlier, oRadius));
664                 }
665                 else if (outlier < minRegular.doubleValue()) {
666                     yyOutlier = rangeAxis.valueToJava2D(outlier, dataArea,
667                             location);
668                     outliers.add(new Outlier(xx + state.getBarWidth() / 2.0,
669                             yyOutlier, oRadius));
670                 }
671                 Collections.sort(outliers);
672             }
673
674             // Process outliers. Each outlier is either added to the
675
// appropriate outlier list or a new outlier list is made
676
for (Iterator JavaDoc iterator = outliers.iterator(); iterator.hasNext();) {
677                 Outlier outlier = (Outlier) iterator.next();
678                 outlierListCollection.add(outlier);
679             }
680
681             for (Iterator JavaDoc iterator = outlierListCollection.iterator();
682                      iterator.hasNext();) {
683                 OutlierList list = (OutlierList) iterator.next();
684                 Outlier outlier = list.getAveragedOutlier();
685                 Point2D JavaDoc point = outlier.getPoint();
686
687                 if (list.isMultiple()) {
688                     drawMultipleEllipse(point, state.getBarWidth(), oRadius,
689                             g2);
690                 }
691                 else {
692                     drawEllipse(point, oRadius, g2);
693                 }
694             }
695
696             // draw farout indicators
697
if (outlierListCollection.isHighFarOut()) {
698                 drawHighFarOut(aRadius / 2.0, g2,
699                         xx + state.getBarWidth() / 2.0, maxAxisValue);
700             }
701         
702             if (outlierListCollection.isLowFarOut()) {
703                 drawLowFarOut(aRadius / 2.0, g2,
704                         xx + state.getBarWidth() / 2.0, minAxisValue);
705             }
706         }
707         // collect entity and tool tip information...
708
if (state.getInfo() != null && box != null) {
709             EntityCollection entities = state.getEntityCollection();
710             if (entities != null) {
711                 String JavaDoc tip = null;
712                 CategoryToolTipGenerator tipster
713                         = getToolTipGenerator(row, column);
714                 if (tipster != null) {
715                     tip = tipster.generateToolTip(dataset, row, column);
716                 }
717                 String JavaDoc url = null;
718                 if (getItemURLGenerator(row, column) != null) {
719                     url = getItemURLGenerator(row, column).generateURL(dataset,
720                             row, column);
721                 }
722                 CategoryItemEntity entity = new CategoryItemEntity(box, tip,
723                         url, dataset, row, dataset.getColumnKey(column),
724                         column);
725                 entities.add(entity);
726             }
727         }
728
729     }
730
731     /**
732      * Draws a dot to represent an outlier.
733      *
734      * @param point the location.
735      * @param oRadius the radius.
736      * @param g2 the graphics device.
737      */

738     private void drawEllipse(Point2D JavaDoc point, double oRadius, Graphics2D JavaDoc g2) {
739         Ellipse2D JavaDoc dot = new Ellipse2D.Double JavaDoc(point.getX() + oRadius / 2,
740                 point.getY(), oRadius, oRadius);
741         g2.draw(dot);
742     }
743
744     /**
745      * Draws two dots to represent the average value of more than one outlier.
746      *
747      * @param point the location
748      * @param boxWidth the box width.
749      * @param oRadius the radius.
750      * @param g2 the graphics device.
751      */

752     private void drawMultipleEllipse(Point2D JavaDoc point, double boxWidth,
753                                      double oRadius, Graphics2D JavaDoc g2) {
754                                          
755         Ellipse2D JavaDoc dot1 = new Ellipse2D.Double JavaDoc(point.getX() - (boxWidth / 2)
756                 + oRadius, point.getY(), oRadius, oRadius);
757         Ellipse2D JavaDoc dot2 = new Ellipse2D.Double JavaDoc(point.getX() + (boxWidth / 2),
758                 point.getY(), oRadius, oRadius);
759         g2.draw(dot1);
760         g2.draw(dot2);
761     }
762
763     /**
764      * Draws a triangle to indicate the presence of far-out values.
765      *
766      * @param aRadius the radius.
767      * @param g2 the graphics device.
768      * @param xx the x coordinate.
769      * @param m the y coordinate.
770      */

771     private void drawHighFarOut(double aRadius, Graphics2D JavaDoc g2, double xx,
772                                 double m) {
773         double side = aRadius * 2;
774         g2.draw(new Line2D.Double JavaDoc(xx - side, m + side, xx + side, m + side));
775         g2.draw(new Line2D.Double JavaDoc(xx - side, m + side, xx, m));
776         g2.draw(new Line2D.Double JavaDoc(xx + side, m + side, xx, m));
777     }
778
779     /**
780      * Draws a triangle to indicate the presence of far-out values.
781      *
782      * @param aRadius the radius.
783      * @param g2 the graphics device.
784      * @param xx the x coordinate.
785      * @param m the y coordinate.
786      */

787     private void drawLowFarOut(double aRadius, Graphics2D JavaDoc g2, double xx,
788                                double m) {
789         double side = aRadius * 2;
790         g2.draw(new Line2D.Double JavaDoc(xx - side, m - side, xx + side, m - side));
791         g2.draw(new Line2D.Double JavaDoc(xx - side, m - side, xx, m));
792         g2.draw(new Line2D.Double JavaDoc(xx + side, m - side, xx, m));
793     }
794     
795     /**
796      * Tests this renderer for equality with an arbitrary object.
797      *
798      * @param obj the object (<code>null</code> permitted).
799      *
800      * @return <code>true</code> or <code>false</code>.
801      */

802     public boolean equals(Object JavaDoc obj) {
803         if (obj == this) {
804             return true;
805         }
806         if (!(obj instanceof BoxAndWhiskerRenderer)) {
807             return false;
808         }
809         if (!super.equals(obj)) {
810             return false;
811         }
812         BoxAndWhiskerRenderer that = (BoxAndWhiskerRenderer) obj;
813         if (!PaintUtilities.equal(this.artifactPaint, that.artifactPaint)) {
814             return false;
815         }
816         if (!(this.fillBox == that.fillBox)) {
817             return false;
818         }
819         if (!(this.itemMargin == that.itemMargin)) {
820             return false;
821         }
822         return true;
823     }
824     
825     /**
826      * Provides serialization support.
827      *
828      * @param stream the output stream.
829      *
830      * @throws IOException if there is an I/O error.
831      */

832     private void writeObject(ObjectOutputStream JavaDoc stream) throws IOException JavaDoc {
833         stream.defaultWriteObject();
834         SerialUtilities.writePaint(this.artifactPaint, stream);
835     }
836
837     /**
838      * Provides serialization support.
839      *
840      * @param stream the input stream.
841      *
842      * @throws IOException if there is an I/O error.
843      * @throws ClassNotFoundException if there is a classpath problem.
844      */

845     private void readObject(ObjectInputStream JavaDoc stream)
846             throws IOException JavaDoc, ClassNotFoundException JavaDoc {
847         stream.defaultReadObject();
848         this.artifactPaint = SerialUtilities.readPaint(stream);
849     }
850    
851 }
852
Popular Tags