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

   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:  * YIntervalRenderer.java
  29:  * ----------------------
  30:  * (C) Copyright 2002-2008, by Object Refinery Limited.
  31:  *
  32:  * Original Author:  David Gilbert (for Object Refinery Limited);
  33:  * Contributor(s):   -;
  34:  *
  35:  * Changes
  36:  * -------
  37:  * 05-Nov-2002 : Version 1 (DG);
  38:  * 25-Mar-2003 : Implemented Serializable (DG);
  39:  * 01-May-2003 : Modified drawItem() method signature (DG);
  40:  * 20-Aug-2003 : Implemented Cloneable and PublicCloneable (DG);
  41:  * 16-Sep-2003 : Changed ChartRenderingInfo --> PlotRenderingInfo (DG);
  42:  * 25-Feb-2004 : Replaced CrosshairInfo with CrosshairState (DG);
  43:  * 27-Sep-2004 : Access double values from dataset (DG);
  44:  * 11-Nov-2004 : Now uses ShapeUtilities to translate shapes (DG);
  45:  * 11-Apr-2008 : New override for findRangeBounds() (DG);
  46:  * 26-May-2008 : Added item label support (DG);
  47:  *
  48:  */
  49: 
  50: package org.jfree.chart.renderer.xy;
  51: 
  52: import java.awt.Font;
  53: import java.awt.Graphics2D;
  54: import java.awt.Paint;
  55: import java.awt.Shape;
  56: import java.awt.Stroke;
  57: import java.awt.geom.Line2D;
  58: import java.awt.geom.Point2D;
  59: import java.awt.geom.Rectangle2D;
  60: import java.io.Serializable;
  61: 
  62: import org.jfree.chart.axis.ValueAxis;
  63: import org.jfree.chart.entity.EntityCollection;
  64: import org.jfree.chart.entity.XYItemEntity;
  65: import org.jfree.chart.event.RendererChangeEvent;
  66: import org.jfree.chart.labels.ItemLabelPosition;
  67: import org.jfree.chart.labels.XYItemLabelGenerator;
  68: import org.jfree.chart.labels.XYToolTipGenerator;
  69: import org.jfree.chart.plot.CrosshairState;
  70: import org.jfree.chart.plot.PlotOrientation;
  71: import org.jfree.chart.plot.PlotRenderingInfo;
  72: import org.jfree.chart.plot.XYPlot;
  73: import org.jfree.data.Range;
  74: import org.jfree.data.general.DatasetUtilities;
  75: import org.jfree.data.xy.IntervalXYDataset;
  76: import org.jfree.data.xy.XYDataset;
  77: import org.jfree.text.TextUtilities;
  78: import org.jfree.ui.RectangleEdge;
  79: import org.jfree.util.ObjectUtilities;
  80: import org.jfree.util.PublicCloneable;
  81: import org.jfree.util.ShapeUtilities;
  82: 
  83: /**
  84:  * A renderer that draws a line connecting the start and end Y values for an
  85:  * {@link XYPlot}.
  86:  */
  87: public class YIntervalRenderer extends AbstractXYItemRenderer
  88:         implements XYItemRenderer, Cloneable, PublicCloneable, Serializable {
  89: 
  90:     /** For serialization. */
  91:     private static final long serialVersionUID = -2951586537224143260L;
  92: 
  93:     /**
  94:      * An additional item label generator.  If this is non-null, the item
  95:      * label generated will be displayed near the lower y-value at the
  96:      * position given by getNegativeItemLabelPosition().
  97:      *
  98:      * @since 1.0.10
  99:      */
 100:     private XYItemLabelGenerator additionalItemLabelGenerator;
 101: 
 102:     /**
 103:      * The default constructor.
 104:      */
 105:     public YIntervalRenderer() {
 106:         super();
 107:         this.additionalItemLabelGenerator = null;
 108:     }
 109: 
 110:     /**
 111:      * Returns the generator for the item labels that appear near the lower
 112:      * y-value.
 113:      *
 114:      * @return The generator (possibly <code>null</code>).
 115:      *
 116:      * @see #setAdditionalItemLabelGenerator(XYItemLabelGenerator)
 117:      *
 118:      * @since 1.0.10
 119:      */
 120:     public XYItemLabelGenerator getAdditionalItemLabelGenerator() {
 121:         return this.additionalItemLabelGenerator;
 122:     }
 123: 
 124:     /**
 125:      * Sets the generator for the item labels that appear near the lower
 126:      * y-value and sends a {@link RendererChangeEvent} to all registered
 127:      * listeners.  If this is set to <code>null</code>, no item labels will be
 128:      * drawn.
 129:      *
 130:      * @param generator  the generator (<code>null</code> permitted).
 131:      *
 132:      * @see #getAdditionalItemLabelGenerator()
 133:      *
 134:      * @since 1.0.10
 135:      */
 136:     public void setAdditionalItemLabelGenerator(
 137:             XYItemLabelGenerator generator) {
 138:         this.additionalItemLabelGenerator = generator;
 139:         fireChangeEvent();
 140:     }
 141: 
 142:     /**
 143:      * Returns the range of values the renderer requires to display all the
 144:      * items from the specified dataset.
 145:      *
 146:      * @param dataset  the dataset (<code>null</code> permitted).
 147:      *
 148:      * @return The range (<code>null</code> if the dataset is <code>null</code>
 149:      *         or empty).
 150:      */
 151:     public Range findRangeBounds(XYDataset dataset) {
 152:         if (dataset != null) {
 153:             return DatasetUtilities.findRangeBounds(dataset, true);
 154:         }
 155:         else {
 156:             return null;
 157:         }
 158:     }
 159: 
 160:     /**
 161:      * Draws the visual representation of a single data item.
 162:      *
 163:      * @param g2  the graphics device.
 164:      * @param state  the renderer state.
 165:      * @param dataArea  the area within which the plot is being drawn.
 166:      * @param info  collects information about the drawing.
 167:      * @param plot  the plot (can be used to obtain standard color
 168:      *              information etc).
 169:      * @param domainAxis  the domain axis.
 170:      * @param rangeAxis  the range axis.
 171:      * @param dataset  the dataset.
 172:      * @param series  the series index (zero-based).
 173:      * @param item  the item index (zero-based).
 174:      * @param crosshairState  crosshair information for the plot
 175:      *                        (<code>null</code> permitted).
 176:      * @param pass  the pass index (ignored here).
 177:      */
 178:     public void drawItem(Graphics2D g2,
 179:                          XYItemRendererState state,
 180:                          Rectangle2D dataArea,
 181:                          PlotRenderingInfo info,
 182:                          XYPlot plot,
 183:                          ValueAxis domainAxis,
 184:                          ValueAxis rangeAxis,
 185:                          XYDataset dataset,
 186:                          int series,
 187:                          int item,
 188:                          CrosshairState crosshairState,
 189:                          int pass) {
 190: 
 191:         // setup for collecting optional entity info...
 192:         Shape entityArea = null;
 193:         EntityCollection entities = null;
 194:         if (info != null) {
 195:             entities = info.getOwner().getEntityCollection();
 196:         }
 197: 
 198:         IntervalXYDataset intervalDataset = (IntervalXYDataset) dataset;
 199: 
 200:         double x = intervalDataset.getXValue(series, item);
 201:         double yLow   = intervalDataset.getStartYValue(series, item);
 202:         double yHigh  = intervalDataset.getEndYValue(series, item);
 203: 
 204:         RectangleEdge xAxisLocation = plot.getDomainAxisEdge();
 205:         RectangleEdge yAxisLocation = plot.getRangeAxisEdge();
 206: 
 207:         double xx = domainAxis.valueToJava2D(x, dataArea, xAxisLocation);
 208:         double yyLow = rangeAxis.valueToJava2D(yLow, dataArea, yAxisLocation);
 209:         double yyHigh = rangeAxis.valueToJava2D(yHigh, dataArea, yAxisLocation);
 210: 
 211:         Paint p = getItemPaint(series, item);
 212:         Stroke s = getItemStroke(series, item);
 213: 
 214:         Line2D line = null;
 215:         Shape shape = getItemShape(series, item);
 216:         Shape top = null;
 217:         Shape bottom = null;
 218:         PlotOrientation orientation = plot.getOrientation();
 219:         if (orientation == PlotOrientation.HORIZONTAL) {
 220:             line = new Line2D.Double(yyLow, xx, yyHigh, xx);
 221:             top = ShapeUtilities.createTranslatedShape(shape, yyHigh, xx);
 222:             bottom = ShapeUtilities.createTranslatedShape(shape, yyLow, xx);
 223:         }
 224:         else if (orientation == PlotOrientation.VERTICAL) {
 225:             line = new Line2D.Double(xx, yyLow, xx, yyHigh);
 226:             top = ShapeUtilities.createTranslatedShape(shape, xx, yyHigh);
 227:             bottom = ShapeUtilities.createTranslatedShape(shape, xx, yyLow);
 228:         }
 229:         g2.setPaint(p);
 230:         g2.setStroke(s);
 231:         g2.draw(line);
 232: 
 233:         g2.fill(top);
 234:         g2.fill(bottom);
 235: 
 236:         // for item labels, we have a special case because there is the
 237:         // possibility to draw (a) the regular item label near to just the
 238:         // upper y-value, or (b) the regular item label near the upper y-value
 239:         // PLUS an additional item label near the lower y-value.
 240:         if (isItemLabelVisible(series, item)) {
 241:             drawItemLabel(g2, orientation, dataset, series, item, xx, yyHigh,
 242:                     false);
 243:             drawAdditionalItemLabel(g2, orientation, dataset, series, item,
 244:                     xx, yyLow);
 245:         }
 246: 
 247:         // add an entity for the item...
 248:         if (entities != null) {
 249:             if (entityArea == null) {
 250:                 entityArea = line.getBounds();
 251:             }
 252:             String tip = null;
 253:             XYToolTipGenerator generator = getToolTipGenerator(series, item);
 254:             if (generator != null) {
 255:                 tip = generator.generateToolTip(dataset, series, item);
 256:             }
 257:             String url = null;
 258:             if (getURLGenerator() != null) {
 259:                 url = getURLGenerator().generateURL(dataset, series, item);
 260:             }
 261:             XYItemEntity entity = new XYItemEntity(entityArea, dataset, series,
 262:                     item, tip, url);
 263:             entities.add(entity);
 264:         }
 265: 
 266:     }
 267: 
 268:     /**
 269:      * Draws an item label.
 270:      *
 271:      * @param g2  the graphics device.
 272:      * @param orientation  the orientation.
 273:      * @param dataset  the dataset.
 274:      * @param series  the series index (zero-based).
 275:      * @param item  the item index (zero-based).
 276:      * @param x  the x coordinate (in Java2D space).
 277:      * @param y  the y coordinate (in Java2D space).
 278:      * @param negative  indicates a negative value (which affects the item
 279:      *                  label position).
 280:      */
 281:     private void drawAdditionalItemLabel(Graphics2D g2,
 282:             PlotOrientation orientation, XYDataset dataset, int series,
 283:             int item, double x, double y) {
 284: 
 285:         if (this.additionalItemLabelGenerator == null) {
 286:             return;
 287:         }
 288: 
 289:         Font labelFont = getItemLabelFont(series, item);
 290:         Paint paint = getItemLabelPaint(series, item);
 291:         g2.setFont(labelFont);
 292:         g2.setPaint(paint);
 293:         String label = this.additionalItemLabelGenerator.generateLabel(dataset,
 294:                 series, item);
 295: 
 296:         ItemLabelPosition position = getNegativeItemLabelPosition(series, item);
 297:         Point2D anchorPoint = calculateLabelAnchorPoint(
 298:                 position.getItemLabelAnchor(), x, y, orientation);
 299:         TextUtilities.drawRotatedString(label, g2,
 300:                 (float) anchorPoint.getX(), (float) anchorPoint.getY(),
 301:                 position.getTextAnchor(), position.getAngle(),
 302:                 position.getRotationAnchor());
 303:     }
 304: 
 305:     /**
 306:      * Tests this renderer for equality with an arbitrary object.
 307:      *
 308:      * @param obj  the object (<code>null</code> permitted).
 309:      *
 310:      * @return A boolean.
 311:      */
 312:     public boolean equals(Object obj) {
 313:         if (obj == this) {
 314:             return true;
 315:         }
 316:         if (!(obj instanceof YIntervalRenderer)) {
 317:             return false;
 318:         }
 319:         YIntervalRenderer that = (YIntervalRenderer) obj;
 320:         if (!ObjectUtilities.equal(this.additionalItemLabelGenerator,
 321:                 that.additionalItemLabelGenerator)) {
 322:             return false;
 323:         }
 324:         return super.equals(obj);
 325:     }
 326: 
 327:     /**
 328:      * Returns a clone of the renderer.
 329:      *
 330:      * @return A clone.
 331:      *
 332:      * @throws CloneNotSupportedException  if the renderer cannot be cloned.
 333:      */
 334:     public Object clone() throws CloneNotSupportedException {
 335:         return super.clone();
 336:     }
 337: 
 338: }