Save This Page
Home » jcommon-1.0.13 » org.jfree » chart » renderer » xy » [javadoc | source]
    1   /* ===========================================================
    2    * JFreeChart : a free chart library for the Java(tm) platform
    3    * ===========================================================
    4    *
    5    * (C) Copyright 2000-2008, 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-2008, 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    * Changes
   37    * -------
   38    * 05-Aug-2003 : Version 1, contributed by David Browning.  Based on code in the
   39    *               CandlestickRenderer class.  Additional modifications by David 
   40    *               Gilbert to make the code work with 0.9.10 changes (DG);
   41    * 08-Aug-2003 : Updated some of the Javadoc
   42    *               Allowed BoxAndwhiskerDataset Average value to be null - the 
   43    *               average value is an AIMS requirement
   44    *               Allow the outlier and farout coefficients to be set - though 
   45    *               at the moment this only affects the calculation of farouts.
   46    *               Added artifactPaint variable and setter/getter
   47    * 12-Aug-2003   Rewrote code to sort out and process outliers to take 
   48    *               advantage of changes in DefaultBoxAndWhiskerDataset
   49    *               Added a limit of 10% for width of box should no width be 
   50    *               specified...maybe this should be setable???
   51    * 20-Aug-2003 : Implemented Cloneable and PublicCloneable (DG);
   52    * 08-Sep-2003 : Changed ValueAxis API (DG);
   53    * 16-Sep-2003 : Changed ChartRenderingInfo --> PlotRenderingInfo (DG);
   54    * 25-Feb-2004 : Replaced CrosshairInfo with CrosshairState (DG);
   55    * 23-Apr-2004 : Added fillBox attribute, extended equals() method and fixed 
   56    *               serialization issue (DG);
   57    * 29-Apr-2004 : Fixed problem with drawing upper and lower shadows - bug id 
   58    *               944011 (DG);
   59    * 15-Jul-2004 : Switched getX() with getXValue() and getY() with 
   60    *               getYValue() (DG);
   61    * 01-Oct-2004 : Renamed 'paint' --> 'boxPaint' to avoid conflict with 
   62    *               inherited attribute (DG);
   63    * 10-Jun-2005 : Updated equals() to handle GradientPaint (DG);
   64    * 06-Oct-2005 : Removed setPaint() call in drawItem(), it is causing a 
   65    *               loop (DG);
   66    * ------------- JFREECHART 1.0.x ---------------------------------------------
   67    * 02-Feb-2007 : Removed author tags from all over JFreeChart sources (DG);
   68    * 05-Feb-2007 : Added event notifications and fixed drawing for horizontal 
   69    *               plot orientation (DG);
   70    * 13-Jun-2007 : Replaced deprecated method call (DG);
   71    * 03-Jan-2008 : Check visibility of average marker before drawing it (DG);
   72    * 27-Mar-2008 : If boxPaint is null, revert to itemPaint (DG);
   73    *
   74    */
   75   
   76   package org.jfree.chart.renderer.xy;
   77   
   78   import java.awt.Color;
   79   import java.awt.Graphics2D;
   80   import java.awt.Paint;
   81   import java.awt.Shape;
   82   import java.awt.Stroke;
   83   import java.awt.geom.Ellipse2D;
   84   import java.awt.geom.Line2D;
   85   import java.awt.geom.Point2D;
   86   import java.awt.geom.Rectangle2D;
   87   import java.io.IOException;
   88   import java.io.ObjectInputStream;
   89   import java.io.ObjectOutputStream;
   90   import java.io.Serializable;
   91   import java.util.ArrayList;
   92   import java.util.Collections;
   93   import java.util.Iterator;
   94   import java.util.List;
   95   
   96   import org.jfree.chart.axis.ValueAxis;
   97   import org.jfree.chart.entity.EntityCollection;
   98   import org.jfree.chart.event.RendererChangeEvent;
   99   import org.jfree.chart.labels.BoxAndWhiskerXYToolTipGenerator;
  100   import org.jfree.chart.plot.CrosshairState;
  101   import org.jfree.chart.plot.PlotOrientation;
  102   import org.jfree.chart.plot.PlotRenderingInfo;
  103   import org.jfree.chart.plot.XYPlot;
  104   import org.jfree.chart.renderer.Outlier;
  105   import org.jfree.chart.renderer.OutlierList;
  106   import org.jfree.chart.renderer.OutlierListCollection;
  107   import org.jfree.data.statistics.BoxAndWhiskerXYDataset;
  108   import org.jfree.data.xy.XYDataset;
  109   import org.jfree.io.SerialUtilities;
  110   import org.jfree.ui.RectangleEdge;
  111   import org.jfree.util.PaintUtilities;
  112   import org.jfree.util.PublicCloneable;
  113   
  114   /**
  115    * A renderer that draws box-and-whisker items on an {@link XYPlot}.  This 
  116    * renderer requires a {@link BoxAndWhiskerXYDataset}).
  117    * <P>
  118    * This renderer does not include any code to calculate the crosshair point.
  119    */
  120   public class XYBoxAndWhiskerRenderer extends AbstractXYItemRenderer 
  121                                        implements XYItemRenderer, 
  122                                                   Cloneable,
  123                                                   PublicCloneable,
  124                                                   Serializable {
  125   
  126       /** For serialization. */
  127       private static final long serialVersionUID = -8020170108532232324L;
  128       
  129       /** The box width. */
  130       private double boxWidth;
  131   
  132       /** The paint used to fill the box. */
  133       private transient Paint boxPaint;
  134   
  135       /** A flag that controls whether or not the box is filled. */
  136       private boolean fillBox;
  137       
  138       /** 
  139        * The paint used to draw various artifacts such as outliers, farout 
  140        * symbol, average ellipse and median line. 
  141        */
  142       private transient Paint artifactPaint = Color.black;
  143   
  144       /**
  145        * Creates a new renderer for box and whisker charts.
  146        */
  147       public XYBoxAndWhiskerRenderer() {
  148           this(-1.0);
  149       }
  150   
  151       /**
  152        * Creates a new renderer for box and whisker charts.
  153        * <P>
  154        * Use -1 for the box width if you prefer the width to be calculated 
  155        * automatically.
  156        *
  157        * @param boxWidth  the box width.
  158        */
  159       public XYBoxAndWhiskerRenderer(double boxWidth) {
  160           super();
  161           this.boxWidth = boxWidth;
  162           this.boxPaint = Color.green;
  163           this.fillBox = true;
  164           setBaseToolTipGenerator(new BoxAndWhiskerXYToolTipGenerator());
  165       }
  166   
  167       /**
  168        * Returns the width of each box.
  169        *
  170        * @return The box width.
  171        * 
  172        * @see #setBoxWidth(double)
  173        */
  174       public double getBoxWidth() {
  175           return this.boxWidth;
  176       }
  177   
  178       /**
  179        * Sets the box width and sends a {@link RendererChangeEvent} to all 
  180        * registered listeners.
  181        * <P>
  182        * If you set the width to a negative value, the renderer will calculate
  183        * the box width automatically based on the space available on the chart.
  184        *
  185        * @param width  the width.
  186        * 
  187        * @see #getBoxWidth()
  188        */
  189       public void setBoxWidth(double width) {
  190           if (width != this.boxWidth) {
  191               this.boxWidth = width;
  192               fireChangeEvent();
  193           }
  194       }
  195   
  196       /**
  197        * Returns the paint used to fill boxes.
  198        *
  199        * @return The paint (possibly <code>null</code>).
  200        * 
  201        * @see #setBoxPaint(Paint)
  202        */
  203       public Paint getBoxPaint() {
  204           return this.boxPaint;
  205       }
  206   
  207       /**
  208        * Sets the paint used to fill boxes and sends a {@link RendererChangeEvent}
  209        * to all registered listeners.
  210        *
  211        * @param paint  the paint (<code>null</code> permitted).
  212        * 
  213        * @see #getBoxPaint()
  214        */
  215       public void setBoxPaint(Paint paint) {
  216           this.boxPaint = paint;
  217           fireChangeEvent();
  218       }
  219       
  220       /**
  221        * Returns the flag that controls whether or not the box is filled.
  222        * 
  223        * @return A boolean.
  224        * 
  225        * @see #setFillBox(boolean)
  226        */
  227       public boolean getFillBox() {
  228           return this.fillBox;   
  229       }
  230       
  231       /**
  232        * Sets the flag that controls whether or not the box is filled and sends a 
  233        * {@link RendererChangeEvent} to all registered listeners.
  234        * 
  235        * @param flag  the flag.
  236        * 
  237        * @see #setFillBox(boolean)
  238        */
  239       public void setFillBox(boolean flag) {
  240           this.fillBox = flag;
  241           fireChangeEvent();
  242       }
  243   
  244       /**
  245        * Returns the paint used to paint the various artifacts such as outliers, 
  246        * farout symbol, median line and the averages ellipse.
  247        *
  248        * @return The paint (never <code>null</code>).
  249        * 
  250        * @see #setArtifactPaint(Paint)
  251        */
  252       public Paint getArtifactPaint() {
  253           return this.artifactPaint;
  254       }
  255   
  256       /**
  257        * Sets the paint used to paint the various artifacts such as outliers, 
  258        * farout symbol, median line and the averages ellipse, and sends a 
  259        * {@link RendererChangeEvent} to all registered listeners.
  260        * 
  261        * @param paint  the paint (<code>null</code> not permitted).
  262        * 
  263        * @see #getArtifactPaint()
  264        */
  265       public void setArtifactPaint(Paint paint) {
  266           if (paint == null) {
  267               throw new IllegalArgumentException("Null 'paint' argument.");
  268           }
  269           this.artifactPaint = paint;
  270           fireChangeEvent();
  271       }
  272       
  273       /**
  274        * Returns the box paint or, if this is <code>null</code>, the item
  275        * paint.
  276        * 
  277        * @param series  the series index.
  278        * @param item  the item index.
  279        * 
  280        * @return The paint used to fill the box for the specified item (never 
  281        *         <code>null</code>).
  282        *
  283        * @since 1.0.10
  284        */
  285       protected Paint lookupBoxPaint(int series, int item) {
  286           Paint p = getBoxPaint();
  287           if (p != null) {
  288               return p;
  289           }
  290           else {
  291               // TODO: could change this to itemFillPaint().  For backwards
  292               // compatibility, it might require a useFillPaint flag.
  293               return getItemPaint(series, item);
  294           }
  295       }
  296   
  297       /**
  298        * Draws the visual representation of a single data item.
  299        *
  300        * @param g2  the graphics device.
  301        * @param state  the renderer state.
  302        * @param dataArea  the area within which the plot is being drawn.
  303        * @param info  collects info about the drawing.
  304        * @param plot  the plot (can be used to obtain standard color 
  305        *              information etc).
  306        * @param domainAxis  the domain axis.
  307        * @param rangeAxis  the range axis.
  308        * @param dataset  the dataset (must be an instance of 
  309        *                 {@link BoxAndWhiskerXYDataset}).
  310        * @param series  the series index (zero-based).
  311        * @param item  the item index (zero-based).
  312        * @param crosshairState  crosshair information for the plot 
  313        *                        (<code>null</code> permitted).
  314        * @param pass  the pass index.
  315        */
  316       public void drawItem(Graphics2D g2, 
  317                            XYItemRendererState state,
  318                            Rectangle2D 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           PlotOrientation orientation = plot.getOrientation();
  330   
  331           if (orientation == PlotOrientation.HORIZONTAL) {
  332               drawHorizontalItem(g2, dataArea, info, plot, domainAxis, rangeAxis,
  333                       dataset, series, item, crosshairState, pass);
  334           }
  335           else if (orientation == PlotOrientation.VERTICAL) {
  336               drawVerticalItem(g2, dataArea, info, plot, domainAxis, rangeAxis,
  337                       dataset, series, item, crosshairState, pass);
  338           }
  339   
  340       }
  341   
  342       /**
  343        * Draws the visual representation of a single data item.
  344        *
  345        * @param g2  the graphics device.
  346        * @param dataArea  the area within which the plot is being drawn.
  347        * @param info  collects info about the drawing.
  348        * @param plot  the plot (can be used to obtain standard color 
  349        *              information etc).
  350        * @param domainAxis  the domain axis.
  351        * @param rangeAxis  the range axis.
  352        * @param dataset  the dataset (must be an instance of 
  353        *                 {@link BoxAndWhiskerXYDataset}).
  354        * @param series  the series index (zero-based).
  355        * @param item  the item index (zero-based).
  356        * @param crosshairState  crosshair information for the plot 
  357        *                        (<code>null</code> permitted).
  358        * @param pass  the pass index.
  359        */
  360       public void drawHorizontalItem(Graphics2D g2, 
  361                                      Rectangle2D dataArea,
  362                                      PlotRenderingInfo info,
  363                                      XYPlot plot, 
  364                                      ValueAxis domainAxis, 
  365                                      ValueAxis rangeAxis,
  366                                      XYDataset dataset, 
  367                                      int series, 
  368                                      int item,
  369                                      CrosshairState crosshairState,
  370                                      int pass) {
  371   
  372           // setup for collecting optional entity info...
  373           EntityCollection entities = null;
  374           if (info != null) {
  375               entities = info.getOwner().getEntityCollection();
  376           }
  377   
  378           BoxAndWhiskerXYDataset boxAndWhiskerData 
  379                   = (BoxAndWhiskerXYDataset) dataset;
  380   
  381           Number x = boxAndWhiskerData.getX(series, item);
  382           Number yMax = boxAndWhiskerData.getMaxRegularValue(series, item);
  383           Number yMin = boxAndWhiskerData.getMinRegularValue(series, item);
  384           Number yMedian = boxAndWhiskerData.getMedianValue(series, item);
  385           Number yAverage = boxAndWhiskerData.getMeanValue(series, item);
  386           Number yQ1Median = boxAndWhiskerData.getQ1Value(series, item);
  387           Number yQ3Median = boxAndWhiskerData.getQ3Value(series, item);
  388           
  389           double xx = domainAxis.valueToJava2D(x.doubleValue(), dataArea, 
  390                   plot.getDomainAxisEdge());
  391   
  392           RectangleEdge location = plot.getRangeAxisEdge();
  393           double yyMax = rangeAxis.valueToJava2D(yMax.doubleValue(), dataArea, 
  394                   location);
  395           double yyMin = rangeAxis.valueToJava2D(yMin.doubleValue(), dataArea, 
  396                   location);
  397           double yyMedian = rangeAxis.valueToJava2D(yMedian.doubleValue(), 
  398                   dataArea, location);
  399           double yyAverage = 0.0;
  400           if (yAverage != null) {
  401               yyAverage = rangeAxis.valueToJava2D(yAverage.doubleValue(), 
  402                       dataArea, location);
  403           }
  404           double yyQ1Median = rangeAxis.valueToJava2D(yQ1Median.doubleValue(), 
  405                   dataArea, location);
  406           double yyQ3Median = rangeAxis.valueToJava2D(yQ3Median.doubleValue(), 
  407                   dataArea, location);
  408           
  409           double exactBoxWidth = getBoxWidth();
  410           double width = exactBoxWidth;
  411           double dataAreaX = dataArea.getHeight();
  412           double maxBoxPercent = 0.1;
  413           double maxBoxWidth = dataAreaX * maxBoxPercent;
  414           if (exactBoxWidth <= 0.0) {
  415               int itemCount = boxAndWhiskerData.getItemCount(series);
  416               exactBoxWidth = dataAreaX / itemCount * 4.5 / 7;
  417               if (exactBoxWidth < 3) {
  418                   width = 3;
  419               }
  420               else if (exactBoxWidth > maxBoxWidth) {
  421                   width = maxBoxWidth;
  422               }
  423               else {
  424                   width = exactBoxWidth;
  425               }
  426           }
  427   
  428           g2.setPaint(getItemPaint(series, item));
  429           Stroke s = getItemStroke(series, item);
  430           g2.setStroke(s);
  431   
  432           // draw the upper shadow
  433           g2.draw(new Line2D.Double(yyMax, xx, yyQ3Median, xx));
  434           g2.draw(new Line2D.Double(yyMax, xx - width / 2, yyMax, 
  435                   xx + width / 2));
  436   
  437           // draw the lower shadow
  438           g2.draw(new Line2D.Double(yyMin, xx, yyQ1Median, xx));
  439           g2.draw(new Line2D.Double(yyMin, xx - width / 2, yyMin, 
  440                   xx + width / 2));
  441   
  442           // draw the body
  443           Shape box = null;
  444           if (yyQ1Median < yyQ3Median) {
  445               box = new Rectangle2D.Double(yyQ1Median, xx - width / 2, 
  446                       yyQ3Median - yyQ1Median, width);
  447           }
  448           else {
  449               box = new Rectangle2D.Double(yyQ3Median, xx - width / 2, 
  450                       yyQ1Median - yyQ3Median, width);
  451           }
  452           if (this.fillBox) {
  453               g2.setPaint(lookupBoxPaint(series, item));
  454               g2.fill(box);   
  455           }
  456           g2.setStroke(getItemOutlineStroke(series, item));
  457           g2.setPaint(getItemOutlinePaint(series, item));
  458           g2.draw(box);
  459   
  460           // draw median
  461           g2.setPaint(getArtifactPaint());
  462           g2.draw(new Line2D.Double(yyMedian, 
  463                   xx - width / 2, yyMedian, xx + width / 2));
  464           
  465           // draw average - SPECIAL AIMS REQUIREMENT
  466           if (yAverage != null) {
  467               double aRadius = width / 4;
  468               // here we check that the average marker will in fact be visible
  469               // before drawing it...
  470               if ((yyAverage > (dataArea.getMinX() - aRadius)) 
  471                       && (yyAverage < (dataArea.getMaxX() + aRadius))) {
  472                   Ellipse2D.Double avgEllipse = new Ellipse2D.Double(
  473                           yyAverage - aRadius, xx - aRadius, aRadius * 2, 
  474                           aRadius * 2);
  475                   g2.fill(avgEllipse);
  476                   g2.draw(avgEllipse);
  477               }
  478           }
  479           
  480           // FIXME: draw outliers
  481           
  482           // add an entity for the item...
  483           if (entities != null && box.intersects(dataArea)) {
  484               addEntity(entities, box, dataset, series, item, yyAverage, xx);
  485           }
  486   
  487       }
  488   
  489       /**
  490        * Draws the visual representation of a single data item.
  491        *
  492        * @param g2  the graphics device.
  493        * @param dataArea  the area within which the plot is being drawn.
  494        * @param info  collects info about the drawing.
  495        * @param plot  the plot (can be used to obtain standard color 
  496        *              information etc).
  497        * @param domainAxis  the domain axis.
  498        * @param rangeAxis  the range axis.
  499        * @param dataset  the dataset (must be an instance of 
  500        *                 {@link BoxAndWhiskerXYDataset}).
  501        * @param series  the series index (zero-based).
  502        * @param item  the item index (zero-based).
  503        * @param crosshairState  crosshair information for the plot 
  504        *                        (<code>null</code> permitted).
  505        * @param pass  the pass index.
  506        */
  507       public void drawVerticalItem(Graphics2D g2, 
  508                                    Rectangle2D dataArea,
  509                                    PlotRenderingInfo info,
  510                                    XYPlot plot, 
  511                                    ValueAxis domainAxis, 
  512                                    ValueAxis rangeAxis,
  513                                    XYDataset dataset, 
  514                                    int series, 
  515                                    int item,
  516                                    CrosshairState crosshairState,
  517                                    int pass) {
  518   
  519           // setup for collecting optional entity info...
  520           EntityCollection entities = null;
  521           if (info != null) {
  522               entities = info.getOwner().getEntityCollection();
  523           }
  524   
  525           BoxAndWhiskerXYDataset boxAndWhiskerData 
  526               = (BoxAndWhiskerXYDataset) dataset;
  527   
  528           Number x = boxAndWhiskerData.getX(series, item);
  529           Number yMax = boxAndWhiskerData.getMaxRegularValue(series, item);
  530           Number yMin = boxAndWhiskerData.getMinRegularValue(series, item);
  531           Number yMedian = boxAndWhiskerData.getMedianValue(series, item);
  532           Number yAverage = boxAndWhiskerData.getMeanValue(series, item);
  533           Number yQ1Median = boxAndWhiskerData.getQ1Value(series, item);
  534           Number yQ3Median = boxAndWhiskerData.getQ3Value(series, item);
  535           List yOutliers = boxAndWhiskerData.getOutliers(series, item);
  536   
  537           double xx = domainAxis.valueToJava2D(x.doubleValue(), dataArea, 
  538                   plot.getDomainAxisEdge());
  539   
  540           RectangleEdge location = plot.getRangeAxisEdge();
  541           double yyMax = rangeAxis.valueToJava2D(yMax.doubleValue(), dataArea, 
  542                   location);
  543           double yyMin = rangeAxis.valueToJava2D(yMin.doubleValue(), dataArea, 
  544                   location);
  545           double yyMedian = rangeAxis.valueToJava2D(yMedian.doubleValue(), 
  546                   dataArea, location);
  547           double yyAverage = 0.0;
  548           if (yAverage != null) {
  549               yyAverage = rangeAxis.valueToJava2D(yAverage.doubleValue(), 
  550                       dataArea, location);
  551           }
  552           double yyQ1Median = rangeAxis.valueToJava2D(yQ1Median.doubleValue(), 
  553                   dataArea, location);
  554           double yyQ3Median = rangeAxis.valueToJava2D(yQ3Median.doubleValue(), 
  555                   dataArea, location);
  556           double yyOutlier;
  557   
  558   
  559           double exactBoxWidth = getBoxWidth();
  560           double width = exactBoxWidth;
  561           double dataAreaX = dataArea.getMaxX() - dataArea.getMinX();
  562           double maxBoxPercent = 0.1;
  563           double maxBoxWidth = dataAreaX * maxBoxPercent;
  564           if (exactBoxWidth <= 0.0) {
  565               int itemCount = boxAndWhiskerData.getItemCount(series);
  566               exactBoxWidth = dataAreaX / itemCount * 4.5 / 7;
  567               if (exactBoxWidth < 3) {
  568                   width = 3;
  569               } 
  570               else if (exactBoxWidth > maxBoxWidth) {
  571                   width = maxBoxWidth;
  572               } 
  573               else {
  574                   width = exactBoxWidth;
  575               }
  576           }
  577   
  578           g2.setPaint(getItemPaint(series, item));
  579           Stroke s = getItemStroke(series, item);
  580           g2.setStroke(s);
  581   
  582           // draw the upper shadow
  583           g2.draw(new Line2D.Double(xx, yyMax, xx, yyQ3Median));
  584           g2.draw(new Line2D.Double(xx - width / 2, yyMax, xx + width / 2, 
  585                   yyMax));
  586   
  587           // draw the lower shadow
  588           g2.draw(new Line2D.Double(xx, yyMin, xx, yyQ1Median));
  589           g2.draw(new Line2D.Double(xx - width / 2, yyMin, xx + width / 2, 
  590                   yyMin));
  591           
  592           // draw the body
  593           Shape box = null;
  594           if (yyQ1Median > yyQ3Median) {
  595               box = new Rectangle2D.Double(xx - width / 2, yyQ3Median, width, 
  596                       yyQ1Median - yyQ3Median);
  597           }
  598           else {
  599               box = new Rectangle2D.Double(xx - width / 2, yyQ1Median, width, 
  600                       yyQ3Median - yyQ1Median);
  601           }
  602           if (this.fillBox) {
  603               g2.setPaint(lookupBoxPaint(series, item));
  604               g2.fill(box);   
  605           }
  606           g2.setStroke(getItemOutlineStroke(series, item));
  607           g2.setPaint(getItemOutlinePaint(series, item));
  608           g2.draw(box);
  609   
  610           // draw median
  611           g2.setPaint(getArtifactPaint());
  612           g2.draw(new Line2D.Double(xx - width / 2, yyMedian, xx + width / 2, 
  613                   yyMedian));
  614   
  615           double aRadius = 0;                 // average radius
  616           double oRadius = width / 3;    // outlier radius
  617   
  618           // draw average - SPECIAL AIMS REQUIREMENT
  619           if (yAverage != null) {
  620               aRadius = width / 4;
  621               // here we check that the average marker will in fact be visible
  622               // before drawing it...
  623               if ((yyAverage > (dataArea.getMinY() - aRadius)) 
  624                       && (yyAverage < (dataArea.getMaxY() + aRadius))) {
  625                   Ellipse2D.Double avgEllipse = new Ellipse2D.Double(xx - aRadius, 
  626                           yyAverage - aRadius, aRadius * 2, aRadius * 2);
  627                   g2.fill(avgEllipse);
  628                   g2.draw(avgEllipse);
  629               }
  630           }
  631   
  632           List outliers = new ArrayList();
  633           OutlierListCollection outlierListCollection 
  634                   = new OutlierListCollection();
  635   
  636           /* From outlier array sort out which are outliers and put these into 
  637            * an arraylist. If there are any farouts, set the flag on the 
  638            * OutlierListCollection
  639            */
  640   
  641           for (int i = 0; i < yOutliers.size(); i++) {
  642               double outlier = ((Number) yOutliers.get(i)).doubleValue();
  643               if (outlier > boxAndWhiskerData.getMaxOutlier(series, 
  644                       item).doubleValue()) {
  645                   outlierListCollection.setHighFarOut(true);
  646               } 
  647               else if (outlier < boxAndWhiskerData.getMinOutlier(series, 
  648                       item).doubleValue()) {
  649                   outlierListCollection.setLowFarOut(true);
  650               } 
  651               else if (outlier > boxAndWhiskerData.getMaxRegularValue(series, 
  652                       item).doubleValue()) {
  653                   yyOutlier = rangeAxis.valueToJava2D(outlier, dataArea, 
  654                           location);
  655                   outliers.add(new Outlier(xx, yyOutlier, oRadius));
  656               }
  657               else if (outlier < boxAndWhiskerData.getMinRegularValue(series, 
  658                       item).doubleValue()) {
  659                   yyOutlier = rangeAxis.valueToJava2D(outlier, dataArea, 
  660                           location);
  661                   outliers.add(new Outlier(xx, yyOutlier, oRadius));
  662               }
  663               Collections.sort(outliers);
  664           }
  665   
  666           // Process outliers. Each outlier is either added to the appropriate 
  667           // outlier list or a new outlier list is made
  668           for (Iterator iterator = outliers.iterator(); iterator.hasNext();) {
  669               Outlier outlier = (Outlier) iterator.next();
  670               outlierListCollection.add(outlier);
  671           }
  672   
  673           // draw yOutliers
  674           double maxAxisValue = rangeAxis.valueToJava2D(rangeAxis.getUpperBound(),
  675                   dataArea, location) + aRadius;
  676           double minAxisValue = rangeAxis.valueToJava2D(rangeAxis.getLowerBound(),
  677                   dataArea, location) - aRadius;
  678   
  679           // draw outliers
  680           for (Iterator iterator = outlierListCollection.iterator(); 
  681                   iterator.hasNext();) {
  682               OutlierList list = (OutlierList) iterator.next();
  683               Outlier outlier = list.getAveragedOutlier();
  684               Point2D point = outlier.getPoint();
  685   
  686               if (list.isMultiple()) {
  687                   drawMultipleEllipse(point, width, oRadius, g2);
  688               } 
  689               else {
  690                   drawEllipse(point, oRadius, g2);
  691               }
  692           }
  693   
  694           // draw farout
  695           if (outlierListCollection.isHighFarOut()) {
  696               drawHighFarOut(aRadius, g2, xx, maxAxisValue);
  697           }
  698   
  699           if (outlierListCollection.isLowFarOut()) {
  700               drawLowFarOut(aRadius, g2, xx, minAxisValue);
  701           }
  702           
  703           // add an entity for the item...
  704           if (entities != null && box.intersects(dataArea)) {
  705               addEntity(entities, box, dataset, series, item, xx, yyAverage);
  706           }
  707   
  708       }
  709   
  710       /**
  711        * Draws an ellipse to represent an outlier.
  712        * 
  713        * @param point  the location.
  714        * @param oRadius  the radius.
  715        * @param g2  the graphics device.
  716        */
  717       protected void drawEllipse(Point2D point, double oRadius, Graphics2D g2) {
  718           Ellipse2D.Double dot = new Ellipse2D.Double(point.getX() + oRadius / 2,
  719                   point.getY(), oRadius, oRadius);
  720           g2.draw(dot);
  721       }
  722   
  723       /**
  724        * Draws two ellipses to represent overlapping outliers.
  725        * 
  726        * @param point  the location.
  727        * @param boxWidth  the box width.
  728        * @param oRadius  the radius.
  729        * @param g2  the graphics device.
  730        */
  731       protected void drawMultipleEllipse(Point2D point, double boxWidth, 
  732                                          double oRadius, Graphics2D g2) {
  733                                            
  734           Ellipse2D.Double dot1 = new Ellipse2D.Double(point.getX() 
  735                   - (boxWidth / 2) + oRadius, point.getY(), oRadius, oRadius);
  736           Ellipse2D.Double dot2 = new Ellipse2D.Double(point.getX() 
  737                   + (boxWidth / 2), point.getY(), oRadius, oRadius);
  738           g2.draw(dot1);
  739           g2.draw(dot2);
  740           
  741       }
  742   
  743       /**
  744        * Draws a triangle to indicate the presence of far out values.
  745        * 
  746        * @param aRadius  the radius.
  747        * @param g2  the graphics device.
  748        * @param xx  the x value.
  749        * @param m  the max y value.
  750        */
  751       protected void drawHighFarOut(double aRadius, Graphics2D g2, double xx, 
  752               double m) {
  753           double side = aRadius * 2;
  754           g2.draw(new Line2D.Double(xx - side, m + side, xx + side, m + side));
  755           g2.draw(new Line2D.Double(xx - side, m + side, xx, m));
  756           g2.draw(new Line2D.Double(xx + side, m + side, xx, m));
  757       }
  758   
  759       /**
  760        * Draws a triangle to indicate the presence of far out values.
  761        * 
  762        * @param aRadius  the radius.
  763        * @param g2  the graphics device.
  764        * @param xx  the x value.
  765        * @param m  the min y value.
  766        */
  767       protected void drawLowFarOut(double aRadius, Graphics2D g2, double xx, 
  768               double m) {
  769           double side = aRadius * 2;
  770           g2.draw(new Line2D.Double(xx - side, m - side, xx + side, m - side));
  771           g2.draw(new Line2D.Double(xx - side, m - side, xx, m));
  772           g2.draw(new Line2D.Double(xx + side, m - side, xx, m));
  773       }
  774   
  775       /**
  776        * Tests this renderer for equality with another object.
  777        *
  778        * @param obj  the object (<code>null</code> permitted).
  779        *
  780        * @return <code>true</code> or <code>false</code>.
  781        */
  782       public boolean equals(Object obj) {
  783           if (obj == this) {
  784               return true;
  785           }
  786           if (!(obj instanceof XYBoxAndWhiskerRenderer)) {
  787               return false;
  788           }
  789           if (!super.equals(obj)) {
  790               return false;
  791           }
  792           XYBoxAndWhiskerRenderer that = (XYBoxAndWhiskerRenderer) obj;
  793           if (this.boxWidth != that.getBoxWidth()) {
  794               return false;
  795           }
  796           if (!PaintUtilities.equal(this.boxPaint, that.boxPaint)) {
  797               return false;
  798           }
  799           if (!PaintUtilities.equal(this.artifactPaint, that.artifactPaint)) {
  800               return false;
  801           }
  802           if (this.fillBox != that.fillBox) {
  803               return false;
  804           }
  805           return true;
  806   
  807       }
  808   
  809       /**
  810        * Provides serialization support.
  811        *
  812        * @param stream  the output stream.
  813        *
  814        * @throws IOException  if there is an I/O error.
  815        */
  816       private void writeObject(ObjectOutputStream stream) throws IOException {
  817           stream.defaultWriteObject();
  818           SerialUtilities.writePaint(this.boxPaint, stream);
  819           SerialUtilities.writePaint(this.artifactPaint, stream);
  820       }
  821   
  822       /**
  823        * Provides serialization support.
  824        *
  825        * @param stream  the input stream.
  826        *
  827        * @throws IOException  if there is an I/O error.
  828        * @throws ClassNotFoundException  if there is a classpath problem.
  829        */
  830       private void readObject(ObjectInputStream stream) 
  831           throws IOException, ClassNotFoundException {
  832   
  833           stream.defaultReadObject();
  834           this.boxPaint = SerialUtilities.readPaint(stream);
  835           this.artifactPaint = SerialUtilities.readPaint(stream);
  836       }
  837   
  838       /**
  839        * Returns a clone of the renderer.
  840        * 
  841        * @return A clone.
  842        * 
  843        * @throws CloneNotSupportedException  if the renderer cannot be cloned.
  844        */
  845       public Object clone() throws CloneNotSupportedException {
  846           return super.clone();
  847       }
  848   
  849   }

Save This Page
Home » jcommon-1.0.13 » org.jfree » chart » renderer » xy » [javadoc | source]