001 /* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-2008, by Object Refinery Limited and Contributors. 006 * 007 * Project Info: http://www.jfree.org/jfreechart/index.html 008 * 009 * This library is free software; you can redistribute it and/or modify it 010 * under the terms of the GNU Lesser General Public License as published by 011 * the Free Software Foundation; either version 2.1 of the License, or 012 * (at your option) any later version. 013 * 014 * This library is distributed in the hope that it will be useful, but 015 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 016 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 017 * License for more details. 018 * 019 * You should have received a copy of the GNU Lesser General Public 020 * License along with this library; if not, write to the Free Software 021 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 022 * USA. 023 * 024 * [Java is a trademark or registered trademark of Sun Microsystems, Inc. 025 * in the United States and other countries.] 026 * 027 * ---------------------- 028 * YIntervalRenderer.java 029 * ---------------------- 030 * (C) Copyright 2002-2008, by Object Refinery Limited. 031 * 032 * Original Author: David Gilbert (for Object Refinery Limited); 033 * Contributor(s): -; 034 * 035 * Changes 036 * ------- 037 * 05-Nov-2002 : Version 1 (DG); 038 * 25-Mar-2003 : Implemented Serializable (DG); 039 * 01-May-2003 : Modified drawItem() method signature (DG); 040 * 20-Aug-2003 : Implemented Cloneable and PublicCloneable (DG); 041 * 16-Sep-2003 : Changed ChartRenderingInfo --> PlotRenderingInfo (DG); 042 * 25-Feb-2004 : Replaced CrosshairInfo with CrosshairState (DG); 043 * 27-Sep-2004 : Access double values from dataset (DG); 044 * 11-Nov-2004 : Now uses ShapeUtilities to translate shapes (DG); 045 * 11-Apr-2008 : New override for findRangeBounds() (DG); 046 * 26-May-2008 : Added item label support (DG); 047 * 048 */ 049 050 package org.jfree.chart.renderer.xy; 051 052 import java.awt.Font; 053 import java.awt.Graphics2D; 054 import java.awt.Paint; 055 import java.awt.Shape; 056 import java.awt.Stroke; 057 import java.awt.geom.Line2D; 058 import java.awt.geom.Point2D; 059 import java.awt.geom.Rectangle2D; 060 import java.io.Serializable; 061 062 import org.jfree.chart.axis.ValueAxis; 063 import org.jfree.chart.entity.EntityCollection; 064 import org.jfree.chart.event.RendererChangeEvent; 065 import org.jfree.chart.labels.ItemLabelPosition; 066 import org.jfree.chart.labels.XYItemLabelGenerator; 067 import org.jfree.chart.plot.CrosshairState; 068 import org.jfree.chart.plot.PlotOrientation; 069 import org.jfree.chart.plot.PlotRenderingInfo; 070 import org.jfree.chart.plot.XYPlot; 071 import org.jfree.data.Range; 072 import org.jfree.data.general.DatasetUtilities; 073 import org.jfree.data.xy.IntervalXYDataset; 074 import org.jfree.data.xy.XYDataset; 075 import org.jfree.text.TextUtilities; 076 import org.jfree.ui.RectangleEdge; 077 import org.jfree.util.ObjectUtilities; 078 import org.jfree.util.PublicCloneable; 079 import org.jfree.util.ShapeUtilities; 080 081 /** 082 * A renderer that draws a line connecting the start and end Y values for an 083 * {@link XYPlot}. 084 */ 085 public class YIntervalRenderer extends AbstractXYItemRenderer 086 implements XYItemRenderer, Cloneable, PublicCloneable, Serializable { 087 088 /** For serialization. */ 089 private static final long serialVersionUID = -2951586537224143260L; 090 091 /** 092 * An additional item label generator. If this is non-null, the item 093 * label generated will be displayed near the lower y-value at the 094 * position given by getNegativeItemLabelPosition(). 095 * 096 * @since 1.0.10 097 */ 098 private XYItemLabelGenerator additionalItemLabelGenerator; 099 100 /** 101 * The default constructor. 102 */ 103 public YIntervalRenderer() { 104 super(); 105 this.additionalItemLabelGenerator = null; 106 } 107 108 /** 109 * Returns the generator for the item labels that appear near the lower 110 * y-value. 111 * 112 * @return The generator (possibly <code>null</code>). 113 * 114 * @see #setAdditionalItemLabelGenerator(XYItemLabelGenerator) 115 * 116 * @since 1.0.10 117 */ 118 public XYItemLabelGenerator getAdditionalItemLabelGenerator() { 119 return this.additionalItemLabelGenerator; 120 } 121 122 /** 123 * Sets the generator for the item labels that appear near the lower 124 * y-value and sends a {@link RendererChangeEvent} to all registered 125 * listeners. If this is set to <code>null</code>, no item labels will be 126 * drawn. 127 * 128 * @param generator the generator (<code>null</code> permitted). 129 * 130 * @see #getAdditionalItemLabelGenerator() 131 * 132 * @since 1.0.10 133 */ 134 public void setAdditionalItemLabelGenerator( 135 XYItemLabelGenerator generator) { 136 this.additionalItemLabelGenerator = generator; 137 fireChangeEvent(); 138 } 139 140 /** 141 * Returns the range of values the renderer requires to display all the 142 * items from the specified dataset. 143 * 144 * @param dataset the dataset (<code>null</code> permitted). 145 * 146 * @return The range (<code>null</code> if the dataset is <code>null</code> 147 * or empty). 148 */ 149 public Range findRangeBounds(XYDataset dataset) { 150 if (dataset != null) { 151 return DatasetUtilities.findRangeBounds(dataset, true); 152 } 153 else { 154 return null; 155 } 156 } 157 158 /** 159 * Draws the visual representation of a single data item. 160 * 161 * @param g2 the graphics device. 162 * @param state the renderer state. 163 * @param dataArea the area within which the plot is being drawn. 164 * @param info collects information about the drawing. 165 * @param plot the plot (can be used to obtain standard color 166 * information etc). 167 * @param domainAxis the domain axis. 168 * @param rangeAxis the range axis. 169 * @param dataset the dataset. 170 * @param series the series index (zero-based). 171 * @param item the item index (zero-based). 172 * @param crosshairState crosshair information for the plot 173 * (<code>null</code> permitted). 174 * @param pass the pass index (ignored here). 175 */ 176 public void drawItem(Graphics2D g2, 177 XYItemRendererState state, 178 Rectangle2D dataArea, 179 PlotRenderingInfo info, 180 XYPlot plot, 181 ValueAxis domainAxis, 182 ValueAxis rangeAxis, 183 XYDataset dataset, 184 int series, 185 int item, 186 CrosshairState crosshairState, 187 int pass) { 188 189 // setup for collecting optional entity info... 190 EntityCollection entities = null; 191 if (info != null) { 192 entities = info.getOwner().getEntityCollection(); 193 } 194 195 IntervalXYDataset intervalDataset = (IntervalXYDataset) dataset; 196 197 double x = intervalDataset.getXValue(series, item); 198 double yLow = intervalDataset.getStartYValue(series, item); 199 double yHigh = intervalDataset.getEndYValue(series, item); 200 201 RectangleEdge xAxisLocation = plot.getDomainAxisEdge(); 202 RectangleEdge yAxisLocation = plot.getRangeAxisEdge(); 203 204 double xx = domainAxis.valueToJava2D(x, dataArea, xAxisLocation); 205 double yyLow = rangeAxis.valueToJava2D(yLow, dataArea, yAxisLocation); 206 double yyHigh = rangeAxis.valueToJava2D(yHigh, dataArea, yAxisLocation); 207 208 Paint p = getItemPaint(series, item); 209 Stroke s = getItemStroke(series, item); 210 211 Line2D line = null; 212 Shape shape = getItemShape(series, item); 213 Shape top = null; 214 Shape bottom = null; 215 PlotOrientation orientation = plot.getOrientation(); 216 if (orientation == PlotOrientation.HORIZONTAL) { 217 line = new Line2D.Double(yyLow, xx, yyHigh, xx); 218 top = ShapeUtilities.createTranslatedShape(shape, yyHigh, xx); 219 bottom = ShapeUtilities.createTranslatedShape(shape, yyLow, xx); 220 } 221 else if (orientation == PlotOrientation.VERTICAL) { 222 line = new Line2D.Double(xx, yyLow, xx, yyHigh); 223 top = ShapeUtilities.createTranslatedShape(shape, xx, yyHigh); 224 bottom = ShapeUtilities.createTranslatedShape(shape, xx, yyLow); 225 } 226 g2.setPaint(p); 227 g2.setStroke(s); 228 g2.draw(line); 229 230 g2.fill(top); 231 g2.fill(bottom); 232 233 // for item labels, we have a special case because there is the 234 // possibility to draw (a) the regular item label near to just the 235 // upper y-value, or (b) the regular item label near the upper y-value 236 // PLUS an additional item label near the lower y-value. 237 if (isItemLabelVisible(series, item)) { 238 drawItemLabel(g2, orientation, dataset, series, item, xx, yyHigh, 239 false); 240 drawAdditionalItemLabel(g2, orientation, dataset, series, item, 241 xx, yyLow); 242 } 243 244 // add an entity for the item... 245 if (entities != null) { 246 addEntity(entities, line.getBounds(), dataset, series, item, 0.0, 247 0.0); 248 } 249 250 } 251 252 /** 253 * Draws an item label. 254 * 255 * @param g2 the graphics device. 256 * @param orientation the orientation. 257 * @param dataset the dataset. 258 * @param series the series index (zero-based). 259 * @param item the item index (zero-based). 260 * @param x the x coordinate (in Java2D space). 261 * @param y the y coordinate (in Java2D space). 262 * @param negative indicates a negative value (which affects the item 263 * label position). 264 */ 265 private void drawAdditionalItemLabel(Graphics2D g2, 266 PlotOrientation orientation, XYDataset dataset, int series, 267 int item, double x, double y) { 268 269 if (this.additionalItemLabelGenerator == null) { 270 return; 271 } 272 273 Font labelFont = getItemLabelFont(series, item); 274 Paint paint = getItemLabelPaint(series, item); 275 g2.setFont(labelFont); 276 g2.setPaint(paint); 277 String label = this.additionalItemLabelGenerator.generateLabel(dataset, 278 series, item); 279 280 ItemLabelPosition position = getNegativeItemLabelPosition(series, item); 281 Point2D anchorPoint = calculateLabelAnchorPoint( 282 position.getItemLabelAnchor(), x, y, orientation); 283 TextUtilities.drawRotatedString(label, g2, 284 (float) anchorPoint.getX(), (float) anchorPoint.getY(), 285 position.getTextAnchor(), position.getAngle(), 286 position.getRotationAnchor()); 287 } 288 289 /** 290 * Tests this renderer for equality with an arbitrary object. 291 * 292 * @param obj the object (<code>null</code> permitted). 293 * 294 * @return A boolean. 295 */ 296 public boolean equals(Object obj) { 297 if (obj == this) { 298 return true; 299 } 300 if (!(obj instanceof YIntervalRenderer)) { 301 return false; 302 } 303 YIntervalRenderer that = (YIntervalRenderer) obj; 304 if (!ObjectUtilities.equal(this.additionalItemLabelGenerator, 305 that.additionalItemLabelGenerator)) { 306 return false; 307 } 308 return super.equals(obj); 309 } 310 311 /** 312 * Returns a clone of the renderer. 313 * 314 * @return A clone. 315 * 316 * @throws CloneNotSupportedException if the renderer cannot be cloned. 317 */ 318 public Object clone() throws CloneNotSupportedException { 319 return super.clone(); 320 } 321 322 }