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-2007, 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    * StackedXYBarRenderer.java
   29    * -------------------------
   30    * (C) Copyright 2004-2007, by Andreas Schroeder and Contributors.
   31    *
   32    * Original Author:  Andreas Schroeder;
   33    * Contributor(s):   David Gilbert (for Object Refinery Limited);
   34    *
   35    * Changes
   36    * -------
   37    * 01-Apr-2004 : Version 1 (AS);
   38    * 15-Jul-2004 : Switched getX() with getXValue() and getY() with 
   39    *               getYValue() (DG);
   40    * 15-Aug-2004 : Added drawBarOutline to control draw/don't-draw bar 
   41    *               outlines (BN);
   42    * 10-Sep-2004 : drawBarOutline attribute is now inherited from XYBarRenderer 
   43    *               and double primitives are retrieved from the dataset rather 
   44    *               than Number objects (DG);
   45    * 07-Jan-2005 : Updated for method name change in DatasetUtilities (DG);
   46    * 25-Jan-2005 : Modified to handle negative values correctly (DG);
   47    * ------------- JFREECHART 1.0.x ---------------------------------------------
   48    * 06-Dec-2006 : Added support for GradientPaint (DG);
   49    * 15-Mar-2007 : Added renderAsPercentages option (DG);
   50    * 
   51    */
   52   
   53   package org.jfree.chart.renderer.xy;
   54   
   55   import java.awt.GradientPaint;
   56   import java.awt.Graphics2D;
   57   import java.awt.Paint;
   58   import java.awt.geom.Rectangle2D;
   59   
   60   import org.jfree.chart.axis.ValueAxis;
   61   import org.jfree.chart.entity.EntityCollection;
   62   import org.jfree.chart.event.RendererChangeEvent;
   63   import org.jfree.chart.labels.ItemLabelAnchor;
   64   import org.jfree.chart.labels.ItemLabelPosition;
   65   import org.jfree.chart.labels.XYItemLabelGenerator;
   66   import org.jfree.chart.plot.CrosshairState;
   67   import org.jfree.chart.plot.PlotOrientation;
   68   import org.jfree.chart.plot.PlotRenderingInfo;
   69   import org.jfree.chart.plot.XYPlot;
   70   import org.jfree.data.Range;
   71   import org.jfree.data.general.DatasetUtilities;
   72   import org.jfree.data.xy.IntervalXYDataset;
   73   import org.jfree.data.xy.TableXYDataset;
   74   import org.jfree.data.xy.XYDataset;
   75   import org.jfree.ui.RectangleEdge;
   76   import org.jfree.ui.TextAnchor;
   77   
   78   /**
   79    * A bar renderer that displays the series items stacked.
   80    * The dataset used together with this renderer must be a
   81    * {@link org.jfree.data.xy.IntervalXYDataset} and a
   82    * {@link org.jfree.data.xy.TableXYDataset}. For example, the
   83    * dataset class {@link org.jfree.data.xy.CategoryTableXYDataset}
   84    * implements both interfaces.
   85    */
   86   public class StackedXYBarRenderer extends XYBarRenderer {
   87     
   88       /** For serialization. */
   89       private static final long serialVersionUID = -7049101055533436444L;
   90       
   91       /** A flag that controls whether the bars display values or percentages. */
   92       private boolean renderAsPercentages;
   93   
   94       /**
   95        * Creates a new renderer.
   96        */
   97       public StackedXYBarRenderer() {
   98           this(0.0);
   99       }
  100   
  101       /**
  102        * Creates a new renderer.
  103        *
  104        * @param margin  the percentual amount of the bars that are cut away.
  105        */
  106       public StackedXYBarRenderer(double margin) {
  107           super(margin);
  108           this.renderAsPercentages = false;
  109           
  110           // set the default item label positions, which will only be used if 
  111           // the user requests visible item labels...
  112           ItemLabelPosition p = new ItemLabelPosition(ItemLabelAnchor.CENTER, 
  113                   TextAnchor.CENTER);
  114           setBasePositiveItemLabelPosition(p);
  115           setBaseNegativeItemLabelPosition(p);
  116           setPositiveItemLabelPositionFallback(null);
  117           setNegativeItemLabelPositionFallback(null);
  118       }
  119   
  120       /**
  121        * Returns <code>true</code> if the renderer displays each item value as
  122        * a percentage (so that the stacked bars add to 100%), and 
  123        * <code>false</code> otherwise.
  124        * 
  125        * @return A boolean.
  126        * 
  127        * @see #setRenderAsPercentages(boolean)
  128        * 
  129        * @since 1.0.5
  130        */
  131       public boolean getRenderAsPercentages() {
  132           return this.renderAsPercentages;   
  133       }
  134       
  135       /**
  136        * Sets the flag that controls whether the renderer displays each item
  137        * value as a percentage (so that the stacked bars add to 100%), and sends
  138        * a {@link RendererChangeEvent} to all registered listeners.
  139        * 
  140        * @param asPercentages  the flag.
  141        * 
  142        * @see #getRenderAsPercentages()
  143        * 
  144        * @since 1.0.5
  145        */
  146       public void setRenderAsPercentages(boolean asPercentages) {
  147           this.renderAsPercentages = asPercentages; 
  148           fireChangeEvent();
  149       }
  150   
  151       /**
  152        * Returns <code>2</code> to indicate that this renderer requires two 
  153        * passes for drawing (item labels are drawn in the second pass so that 
  154        * they always appear in front of all the bars).
  155        * 
  156        * @return <code>2</code>.
  157        */
  158       public int getPassCount() {
  159           return 2;
  160       }
  161   
  162       /**
  163        * Initialises the renderer and returns a state object that should be 
  164        * passed to all subsequent calls to the drawItem() method. Here there is 
  165        * nothing to do.
  166        *
  167        * @param g2  the graphics device.
  168        * @param dataArea  the area inside the axes.
  169        * @param plot  the plot.
  170        * @param data  the data.
  171        * @param info  an optional info collection object to return data back to
  172        *              the caller.
  173        *
  174        * @return A state object.
  175        */
  176       public XYItemRendererState initialise(Graphics2D g2,
  177                                             Rectangle2D dataArea,
  178                                             XYPlot plot,
  179                                             XYDataset data,
  180                                             PlotRenderingInfo info) {
  181           return new XYBarRendererState(info);
  182       }
  183   
  184       /**
  185        * Returns the range of values the renderer requires to display all the 
  186        * items from the specified dataset.
  187        * 
  188        * @param dataset  the dataset (<code>null</code> permitted).
  189        * 
  190        * @return The range (<code>null</code> if the dataset is <code>null</code>
  191        *         or empty).
  192        */
  193       public Range findRangeBounds(XYDataset dataset) {
  194           if (dataset != null) {
  195               if (this.renderAsPercentages) {
  196                   return new Range(0.0, 1.0);
  197               }
  198               else {
  199                   return DatasetUtilities.findStackedRangeBounds(
  200                           (TableXYDataset) dataset);
  201               }
  202           }
  203           else {
  204               return null;
  205           }
  206       }
  207   
  208       /**
  209        * Draws the visual representation of a single data item.
  210        *
  211        * @param g2  the graphics device.
  212        * @param state  the renderer state.
  213        * @param dataArea  the area within which the plot is being drawn.
  214        * @param info  collects information about the drawing.
  215        * @param plot  the plot (can be used to obtain standard color information 
  216        *              etc).
  217        * @param domainAxis  the domain axis.
  218        * @param rangeAxis  the range axis.
  219        * @param dataset  the dataset.
  220        * @param series  the series index (zero-based).
  221        * @param item  the item index (zero-based).
  222        * @param crosshairState  crosshair information for the plot 
  223        *                        (<code>null</code> permitted).
  224        * @param pass  the pass index.
  225        */
  226       public void drawItem(Graphics2D g2, 
  227                            XYItemRendererState state,
  228                            Rectangle2D dataArea,
  229                            PlotRenderingInfo info,
  230                            XYPlot plot,
  231                            ValueAxis domainAxis,
  232                            ValueAxis rangeAxis,
  233                            XYDataset dataset,
  234                            int series,
  235                            int item,
  236                            CrosshairState crosshairState,
  237                            int pass) {
  238           
  239           if (!(dataset instanceof IntervalXYDataset 
  240                   && dataset instanceof TableXYDataset)) {
  241               String message = "dataset (type " + dataset.getClass().getName() 
  242                   + ") has wrong type:";
  243               boolean and = false;
  244               if (!IntervalXYDataset.class.isAssignableFrom(dataset.getClass())) {
  245                   message += " it is no IntervalXYDataset";
  246                   and = true;
  247               }
  248               if (!TableXYDataset.class.isAssignableFrom(dataset.getClass())) {
  249                   if (and) {
  250                       message += " and";
  251                   }
  252                   message += " it is no TableXYDataset";
  253               }
  254   
  255               throw new IllegalArgumentException(message);
  256           }
  257   
  258           IntervalXYDataset intervalDataset = (IntervalXYDataset) dataset;
  259           double value = intervalDataset.getYValue(series, item);
  260           if (Double.isNaN(value)) {
  261               return;
  262           }
  263           
  264           // if we are rendering the values as percentages, we need to calculate
  265           // the total for the current item.  Unfortunately here we end up 
  266           // repeating the calculation more times than is strictly necessary -
  267           // hopefully I'll come back to this and find a way to add the 
  268           // total(s) to the renderer state.  The other problem is we implicitly
  269           // assume the dataset has no negative values...perhaps that can be
  270           // fixed too.
  271           double total = 0.0;  
  272           if (this.renderAsPercentages) {
  273               total = DatasetUtilities.calculateStackTotal(
  274                       (TableXYDataset) dataset, item);
  275               value = value / total;
  276           }
  277           
  278           double positiveBase = 0.0;
  279           double negativeBase = 0.0;
  280           
  281           for (int i = 0; i < series; i++) {
  282               double v = dataset.getYValue(i, item);
  283               if (!Double.isNaN(v)) {
  284                   if (this.renderAsPercentages) {
  285                       v = v / total;
  286                   }
  287                   if (v > 0) {
  288                       positiveBase = positiveBase + v;
  289                   }
  290                   else {
  291                       negativeBase = negativeBase + v;
  292                   }
  293               }
  294           }
  295   
  296           double translatedBase;
  297           double translatedValue;
  298           RectangleEdge edgeR = plot.getRangeAxisEdge();
  299           if (value > 0.0) {
  300               translatedBase = rangeAxis.valueToJava2D(positiveBase, dataArea, 
  301                       edgeR);
  302               translatedValue = rangeAxis.valueToJava2D(positiveBase + value, 
  303                       dataArea, edgeR);
  304           }
  305           else {
  306               translatedBase = rangeAxis.valueToJava2D(negativeBase, dataArea, 
  307                       edgeR);
  308               translatedValue = rangeAxis.valueToJava2D(negativeBase + value, 
  309                       dataArea, edgeR);
  310           }
  311   
  312           RectangleEdge edgeD = plot.getDomainAxisEdge();
  313           double startX = intervalDataset.getStartXValue(series, item);
  314           if (Double.isNaN(startX)) {
  315               return;
  316           }
  317           double translatedStartX = domainAxis.valueToJava2D(startX, dataArea, 
  318                   edgeD);
  319   
  320           double endX = intervalDataset.getEndXValue(series, item);
  321           if (Double.isNaN(endX)) {
  322               return;
  323           }
  324           double translatedEndX = domainAxis.valueToJava2D(endX, dataArea, edgeD);
  325   
  326           double translatedWidth = Math.max(1, Math.abs(translatedEndX 
  327                   - translatedStartX));
  328           double translatedHeight = Math.abs(translatedValue - translatedBase);
  329           if (getMargin() > 0.0) {
  330               double cut = translatedWidth * getMargin();
  331               translatedWidth = translatedWidth - cut;
  332               translatedStartX = translatedStartX + cut / 2;
  333           }
  334   
  335           Rectangle2D bar = null;
  336           PlotOrientation orientation = plot.getOrientation();
  337           if (orientation == PlotOrientation.HORIZONTAL) {
  338               bar = new Rectangle2D.Double(Math.min(translatedBase, 
  339                       translatedValue), translatedEndX, translatedHeight,
  340                       translatedWidth);
  341           }
  342           else if (orientation == PlotOrientation.VERTICAL) {
  343               bar = new Rectangle2D.Double(translatedStartX,
  344                       Math.min(translatedBase, translatedValue),
  345                       translatedWidth, translatedHeight);
  346           }
  347   
  348           if (pass == 0) {
  349               Paint itemPaint = getItemPaint(series, item);
  350               if (getGradientPaintTransformer() 
  351                       != null && itemPaint instanceof GradientPaint) {
  352                   GradientPaint gp = (GradientPaint) itemPaint;
  353                   itemPaint = getGradientPaintTransformer().transform(gp, bar);
  354               }
  355               g2.setPaint(itemPaint);
  356               g2.fill(bar);
  357               if (isDrawBarOutline() 
  358                       && Math.abs(translatedEndX - translatedStartX) > 3) {
  359                   g2.setStroke(getItemStroke(series, item));
  360                   g2.setPaint(getItemOutlinePaint(series, item));
  361                   g2.draw(bar);
  362               }
  363   
  364               // add an entity for the item...
  365               if (info != null) {
  366                   EntityCollection entities = info.getOwner()
  367                           .getEntityCollection();
  368                   if (entities != null) {
  369                       addEntity(entities, bar, dataset, series, item, 
  370                               bar.getCenterX(), bar.getCenterY());
  371                   }
  372               }
  373           }
  374           else if (pass == 1) {
  375               // handle item label drawing, now that we know all the bars have
  376               // been drawn...
  377               if (isItemLabelVisible(series, item)) {
  378                   XYItemLabelGenerator generator = getItemLabelGenerator(series, 
  379                           item);
  380                   drawItemLabel(g2, dataset, series, item, plot, generator, bar, 
  381                           value < 0.0);
  382               }
  383           }
  384   
  385       }
  386       
  387       /**
  388        * Tests this renderer for equality with an arbitrary object.
  389        * 
  390        * @param obj  the object (<code>null</code> permitted).
  391        * 
  392        * @return A boolean.
  393        */
  394       public boolean equals(Object obj) {
  395           if (obj == this) {
  396               return true;   
  397           }
  398           if (!(obj instanceof StackedXYBarRenderer)) {
  399               return false;   
  400           }
  401           StackedXYBarRenderer that = (StackedXYBarRenderer) obj;
  402           if (this.renderAsPercentages != that.renderAsPercentages) {
  403               return false;   
  404           }
  405           return super.equals(obj);
  406       }
  407       
  408       /**
  409        * Returns a hash code for this instance.
  410        * 
  411        * @return A hash code.
  412        */
  413       public int hashCode() {
  414           int result = super.hashCode();
  415           result = result * 37 + (this.renderAsPercentages ? 1 : 0);
  416           return result;
  417       }
  418       
  419   }

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