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.entity.XYItemEntity; 065 import org.jfree.chart.event.RendererChangeEvent; 066 import org.jfree.chart.labels.ItemLabelPosition; 067 import org.jfree.chart.labels.XYItemLabelGenerator; 068 import org.jfree.chart.labels.XYToolTipGenerator; 069 import org.jfree.chart.plot.CrosshairState; 070 import org.jfree.chart.plot.PlotOrientation; 071 import org.jfree.chart.plot.PlotRenderingInfo; 072 import org.jfree.chart.plot.XYPlot; 073 import org.jfree.data.Range; 074 import org.jfree.data.general.DatasetUtilities; 075 import org.jfree.data.xy.IntervalXYDataset; 076 import org.jfree.data.xy.XYDataset; 077 import org.jfree.text.TextUtilities; 078 import org.jfree.ui.RectangleEdge; 079 import org.jfree.util.ObjectUtilities; 080 import org.jfree.util.PublicCloneable; 081 import org.jfree.util.ShapeUtilities; 082 083 /** 084 * A renderer that draws a line connecting the start and end Y values for an 085 * {@link XYPlot}. 086 */ 087 public class YIntervalRenderer extends AbstractXYItemRenderer 088 implements XYItemRenderer, Cloneable, PublicCloneable, Serializable { 089 090 /** For serialization. */ 091 private static final long serialVersionUID = -2951586537224143260L; 092 093 /** 094 * An additional item label generator. If this is non-null, the item 095 * label generated will be displayed near the lower y-value at the 096 * position given by getNegativeItemLabelPosition(). 097 * 098 * @since 1.0.10 099 */ 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 }