Source for org.jfree.chart.renderer.xy.StackedXYBarRenderer

   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:  * StackedXYBarRenderer.java
  29:  * -------------------------
  30:  * (C) Copyright 2004-2006, by Andreas Schroeder and Contributors.
  31:  *
  32:  * Original Author:  Andreas Schroeder;
  33:  * Contributor(s):   David Gilbert (for Object Refinery Limited);          
  34:  *
  35:  * $Id: StackedXYBarRenderer.java,v 1.10.2.2 2006/08/21 13:17:49 mungady Exp $
  36:  *
  37:  * Changes
  38:  * -------
  39:  * 01-Apr-2004 : Version 1 (AS);
  40:  * 15-Jul-2004 : Switched getX() with getXValue() and getY() with 
  41:  *               getYValue() (DG);
  42:  * 15-Aug-2004 : Added drawBarOutline to control draw/don't-draw bar 
  43:  *               outlines (BN);
  44:  * 10-Sep-2004 : drawBarOutline attribute is now inherited from XYBarRenderer 
  45:  *               and double primitives are retrieved from the dataset rather 
  46:  *               than Number objects (DG);
  47:  * 07-Jan-2005 : Updated for method name change in DatasetUtilities (DG);
  48:  * 25-Jan-2005 : Modified to handle negative values correctly (DG);
  49:  * 
  50:  */
  51: 
  52: package org.jfree.chart.renderer.xy;
  53: 
  54: import java.awt.Graphics2D;
  55: import java.awt.geom.Rectangle2D;
  56: import java.io.Serializable;
  57: 
  58: import org.jfree.chart.axis.ValueAxis;
  59: import org.jfree.chart.entity.EntityCollection;
  60: import org.jfree.chart.entity.XYItemEntity;
  61: import org.jfree.chart.labels.XYToolTipGenerator;
  62: import org.jfree.chart.plot.CrosshairState;
  63: import org.jfree.chart.plot.PlotOrientation;
  64: import org.jfree.chart.plot.PlotRenderingInfo;
  65: import org.jfree.chart.plot.XYPlot;
  66: import org.jfree.data.Range;
  67: import org.jfree.data.general.DatasetUtilities;
  68: import org.jfree.data.xy.IntervalXYDataset;
  69: import org.jfree.data.xy.TableXYDataset;
  70: import org.jfree.data.xy.XYDataset;
  71: import org.jfree.ui.RectangleEdge;
  72: 
  73: /**
  74:  * A bar renderer that displays the series items stacked.
  75:  * The dataset used together with this renderer must be a
  76:  * {@link org.jfree.data.xy.IntervalXYDataset} and a
  77:  * {@link org.jfree.data.xy.TableXYDataset}. For example, the
  78:  * dataset class {@link org.jfree.data.xy.CategoryTableXYDataset}
  79:  * implements both interfaces.
  80:  *
  81:  * @author andreas.schroeder
  82:  */
  83: public class StackedXYBarRenderer extends XYBarRenderer 
  84:                                   implements Serializable {
  85:   
  86:     /** For serialization. */
  87:     private static final long serialVersionUID = -7049101055533436444L;
  88:     
  89:     /**
  90:      * Creates a new renderer.
  91:      */
  92:     public StackedXYBarRenderer() {
  93:         super();
  94:     }
  95: 
  96:     /**
  97:      * Creates a new renderer.
  98:      *
  99:      * @param margin  the percentual amount of the bars that are cut away.
 100:      */
 101:     public StackedXYBarRenderer(double margin) {
 102:         super(margin);
 103:     }
 104: 
 105:     /**
 106:      * Initialises the renderer and returns a state object that should be 
 107:      * passed to all subsequent calls to the drawItem() method. Here there is 
 108:      * nothing to do.
 109:      *
 110:      * @param g2  the graphics device.
 111:      * @param dataArea  the area inside the axes.
 112:      * @param plot  the plot.
 113:      * @param data  the data.
 114:      * @param info  an optional info collection object to return data back to
 115:      *              the caller.
 116:      *
 117:      * @return A state object.
 118:      */
 119:     public XYItemRendererState initialise(Graphics2D g2,
 120:                                           Rectangle2D dataArea,
 121:                                           XYPlot plot,
 122:                                           XYDataset data,
 123:                                           PlotRenderingInfo info) {
 124:         return new XYBarRendererState(info);
 125:     }
 126: 
 127:     /**
 128:      * Returns the range of values the renderer requires to display all the 
 129:      * items from the specified dataset.
 130:      * 
 131:      * @param dataset  the dataset (<code>null</code> permitted).
 132:      * 
 133:      * @return The range (<code>null</code> if the dataset is <code>null</code>
 134:      *         or empty).
 135:      */
 136:     public Range findRangeBounds(XYDataset dataset) {
 137:         if (dataset != null) {
 138:             return DatasetUtilities.findStackedRangeBounds(
 139:                     (TableXYDataset) dataset);
 140:         }
 141:         else {
 142:             return null;
 143:         }
 144:     }
 145: 
 146:     /**
 147:      * Draws the visual representation of a single data item.
 148:      *
 149:      * @param g2  the graphics device.
 150:      * @param state  the renderer state.
 151:      * @param dataArea  the area within which the plot is being drawn.
 152:      * @param info  collects information about the drawing.
 153:      * @param plot  the plot (can be used to obtain standard color information 
 154:      *              etc).
 155:      * @param domainAxis  the domain axis.
 156:      * @param rangeAxis  the range axis.
 157:      * @param dataset  the dataset.
 158:      * @param series  the series index (zero-based).
 159:      * @param item  the item index (zero-based).
 160:      * @param crosshairState  crosshair information for the plot 
 161:      *                        (<code>null</code> permitted).
 162:      * @param pass  the pass index.
 163:      */
 164:     public void drawItem(Graphics2D g2, 
 165:                          XYItemRendererState state,
 166:                          Rectangle2D dataArea,
 167:                          PlotRenderingInfo info,
 168:                          XYPlot plot,
 169:                          ValueAxis domainAxis,
 170:                          ValueAxis rangeAxis,
 171:                          XYDataset dataset,
 172:                          int series,
 173:                          int item,
 174:                          CrosshairState crosshairState,
 175:                          int pass) {
 176:         
 177:         if (!(dataset instanceof IntervalXYDataset 
 178:                 && dataset instanceof TableXYDataset)) {
 179:             String message = "dataset (type " + dataset.getClass().getName() 
 180:                 + ") has wrong type:";
 181:             boolean and = false;
 182:             if (!IntervalXYDataset.class.isAssignableFrom(dataset.getClass())) {
 183:                 message += " it is no IntervalXYDataset";
 184:                 and = true;
 185:             }
 186:             if (!TableXYDataset.class.isAssignableFrom(dataset.getClass())) {
 187:                 if (and) {
 188:                     message += " and";
 189:                 }
 190:                 message += " it is no TableXYDataset";
 191:             }
 192: 
 193:             throw new IllegalArgumentException(message);
 194:         }
 195: 
 196:         IntervalXYDataset intervalDataset = (IntervalXYDataset) dataset;
 197:         double value = intervalDataset.getYValue(series, item);
 198:         if (Double.isNaN(value)) {
 199:             return;
 200:         }
 201: 
 202:         double positiveBase = 0.0;
 203:         double negativeBase = 0.0;
 204: 
 205:         for (int i = 0; i < series; i++) {
 206:             double v = dataset.getYValue(i, item);
 207:             if (!Double.isNaN(v)) {
 208:                 if (v > 0) {
 209:                     positiveBase = positiveBase + v;
 210:                 }
 211:                 else {
 212:                     negativeBase = negativeBase + v;
 213:                 }
 214:             }
 215:         }
 216: 
 217:         double translatedBase;
 218:         double translatedValue;
 219:         RectangleEdge edgeR = plot.getRangeAxisEdge();
 220:         if (value > 0.0) {
 221:             translatedBase = rangeAxis.valueToJava2D(positiveBase, dataArea, 
 222:                     edgeR);
 223:             translatedValue = rangeAxis.valueToJava2D(positiveBase + value, 
 224:                     dataArea, edgeR);
 225:         }
 226:         else {
 227:             translatedBase = rangeAxis.valueToJava2D(negativeBase, dataArea, 
 228:                     edgeR);
 229:             translatedValue = rangeAxis.valueToJava2D(negativeBase + value, 
 230:                     dataArea, edgeR);
 231:         }
 232: 
 233:         RectangleEdge edgeD = plot.getDomainAxisEdge();
 234:         double startX = intervalDataset.getStartXValue(series, item);
 235:         if (Double.isNaN(startX)) {
 236:             return;
 237:         }
 238:         double translatedStartX = domainAxis.valueToJava2D(startX, dataArea, 
 239:                 edgeD);
 240: 
 241:         double endX = intervalDataset.getEndXValue(series, item);
 242:         if (Double.isNaN(endX)) {
 243:             return;
 244:         }
 245:         double translatedEndX = domainAxis.valueToJava2D(endX, dataArea, edgeD);
 246: 
 247:         double translatedWidth = Math.max(1, Math.abs(translatedEndX 
 248:                 - translatedStartX));
 249:         double translatedHeight = Math.abs(translatedValue - translatedBase);
 250:         if (getMargin() > 0.0) {
 251:             double cut = translatedWidth * getMargin();
 252:             translatedWidth = translatedWidth - cut;
 253:             translatedStartX = translatedStartX + cut / 2;
 254:         }
 255: 
 256:         Rectangle2D bar = null;
 257:         PlotOrientation orientation = plot.getOrientation();
 258:         if (orientation == PlotOrientation.HORIZONTAL) {
 259:             bar = new Rectangle2D.Double(Math.min(translatedBase, 
 260:                     translatedValue), translatedEndX, translatedHeight,
 261:                     translatedWidth);
 262:         }
 263:         else if (orientation == PlotOrientation.VERTICAL) {
 264:             bar = new Rectangle2D.Double(translatedStartX,
 265:                     Math.min(translatedBase, translatedValue),
 266:                     translatedWidth, translatedHeight);
 267:         }
 268: 
 269:         g2.setPaint(getItemPaint(series, item));
 270:         g2.fill(bar);
 271:         if (isDrawBarOutline() 
 272:                 && Math.abs(translatedEndX - translatedStartX) > 3) {
 273:             g2.setStroke(getItemStroke(series, item));
 274:             g2.setPaint(getItemOutlinePaint(series, item));
 275:             g2.draw(bar);
 276:         }
 277: 
 278:         // add an entity for the item...
 279:         if (info != null) {
 280:             EntityCollection entities = info.getOwner().getEntityCollection();
 281:             if (entities != null) {
 282:                 String tip = null;
 283:                 XYToolTipGenerator generator 
 284:                     = getToolTipGenerator(series, item);
 285:                 if (generator != null) {
 286:                     tip = generator.generateToolTip(dataset, series, item);
 287:                 }
 288:                 String url = null;
 289:                 if (getURLGenerator() != null) {
 290:                     url = getURLGenerator().generateURL(dataset, series, item);
 291:                 }
 292:                 XYItemEntity entity = new XYItemEntity(bar, dataset, series, 
 293:                         item, tip, url);
 294:                 entities.add(entity);
 295:             }
 296:         }
 297:     }
 298:     
 299: }