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 * AreaRenderer.java 029 * ----------------- 030 * (C) Copyright 2002-2008, by Jon Iles and Contributors. 031 * 032 * Original Author: Jon Iles; 033 * Contributor(s): David Gilbert (for Object Refinery Limited); 034 * Christian W. Zuckschwerdt; 035 * 036 * Changes: 037 * -------- 038 * 21-May-2002 : Version 1, contributed by John Iles (DG); 039 * 29-May-2002 : Now extends AbstractCategoryItemRenderer (DG); 040 * 11-Jun-2002 : Updated Javadoc comments (DG); 041 * 25-Jun-2002 : Removed unnecessary imports (DG); 042 * 01-Oct-2002 : Fixed errors reported by Checkstyle (DG); 043 * 10-Oct-2002 : Added constructors and basic entity support (DG); 044 * 24-Oct-2002 : Amendments for changes in CategoryDataset interface and 045 * CategoryToolTipGenerator interface (DG); 046 * 05-Nov-2002 : Replaced references to CategoryDataset with TableDataset (DG); 047 * 06-Nov-2002 : Renamed drawCategoryItem() --> drawItem() and now using axis 048 * for category spacing. Renamed AreaCategoryItemRenderer 049 * --> AreaRenderer (DG); 050 * 17-Jan-2003 : Moved plot classes into a separate package (DG); 051 * 25-Mar-2003 : Implemented Serializable (DG); 052 * 10-Apr-2003 : Changed CategoryDataset to KeyedValues2DDataset in 053 * drawItem() method (DG); 054 * 12-May-2003 : Modified to take into account the plot orientation (DG); 055 * 30-Jul-2003 : Modified entity constructor (CZ); 056 * 13-Aug-2003 : Implemented Cloneable (DG); 057 * 07-Oct-2003 : Added renderer state (DG); 058 * 05-Nov-2004 : Modified drawItem() signature (DG); 059 * 20-Apr-2005 : Apply tooltips and URLs to legend items (DG); 060 * 09-Jun-2005 : Use addItemEntity() method from superclass (DG); 061 * ------------- JFREECHART 1.0.x --------------------------------------------- 062 * 11-Oct-2006 : Fixed bug in equals() method (DG); 063 * 30-Nov-2006 : Added checks for series visibility (DG); 064 * 20-Apr-2007 : Updated getLegendItem() for renderer change (DG); 065 * 17-May-2007 : Set datasetIndex and seriesIndex in getLegendItem() (DG); 066 * 18-May-2007 : Set dataset and seriesKey for LegendItem (DG); 067 * 17-Jun-2008 : Apply legend shape, font and paint attributes (DG); 068 * 26-Jun-2008 : Added crosshair support (DG); 069 * 070 */ 071 072 package org.jfree.chart.renderer.category; 073 074 import java.awt.Graphics2D; 075 import java.awt.Paint; 076 import java.awt.Shape; 077 import java.awt.Stroke; 078 import java.awt.geom.GeneralPath; 079 import java.awt.geom.Rectangle2D; 080 import java.io.Serializable; 081 082 import org.jfree.chart.LegendItem; 083 import org.jfree.chart.axis.CategoryAxis; 084 import org.jfree.chart.axis.ValueAxis; 085 import org.jfree.chart.entity.EntityCollection; 086 import org.jfree.chart.event.RendererChangeEvent; 087 import org.jfree.chart.plot.CategoryPlot; 088 import org.jfree.chart.plot.PlotOrientation; 089 import org.jfree.chart.renderer.AreaRendererEndType; 090 import org.jfree.data.category.CategoryDataset; 091 import org.jfree.ui.RectangleEdge; 092 import org.jfree.util.PublicCloneable; 093 094 /** 095 * A category item renderer that draws area charts. You can use this renderer 096 * with the {@link org.jfree.chart.plot.CategoryPlot} class. 097 */ 098 public class AreaRenderer extends AbstractCategoryItemRenderer 099 implements Cloneable, PublicCloneable, Serializable { 100 101 /** For serialization. */ 102 private static final long serialVersionUID = -4231878281385812757L; 103 104 /** A flag that controls how the ends of the areas are drawn. */ 105 private AreaRendererEndType endType; 106 107 /** 108 * Creates a new renderer. 109 */ 110 public AreaRenderer() { 111 super(); 112 this.endType = AreaRendererEndType.TAPER; 113 setBaseLegendShape(new Rectangle2D.Double(-4.0, -4.0, 8.0, 8.0)); 114 } 115 116 /** 117 * Returns a token that controls how the renderer draws the end points. 118 * The default value is {@link AreaRendererEndType#TAPER}. 119 * 120 * @return The end type (never <code>null</code>). 121 * 122 * @see #setEndType 123 */ 124 public AreaRendererEndType getEndType() { 125 return this.endType; 126 } 127 128 /** 129 * Sets a token that controls how the renderer draws the end points, and 130 * sends a {@link RendererChangeEvent} to all registered listeners. 131 * 132 * @param type the end type (<code>null</code> not permitted). 133 * 134 * @see #getEndType() 135 */ 136 public void setEndType(AreaRendererEndType type) { 137 if (type == null) { 138 throw new IllegalArgumentException("Null 'type' argument."); 139 } 140 this.endType = type; 141 fireChangeEvent(); 142 } 143 144 /** 145 * Returns a legend item for a series. 146 * 147 * @param datasetIndex the dataset index (zero-based). 148 * @param series the series index (zero-based). 149 * 150 * @return The legend item. 151 */ 152 public LegendItem getLegendItem(int datasetIndex, int series) { 153 154 // if there is no plot, there is no dataset to access... 155 CategoryPlot cp = getPlot(); 156 if (cp == null) { 157 return null; 158 } 159 160 // check that a legend item needs to be displayed... 161 if (!isSeriesVisible(series) || !isSeriesVisibleInLegend(series)) { 162 return null; 163 } 164 165 CategoryDataset dataset = cp.getDataset(datasetIndex); 166 String label = getLegendItemLabelGenerator().generateLabel(dataset, 167 series); 168 String description = label; 169 String toolTipText = null; 170 if (getLegendItemToolTipGenerator() != null) { 171 toolTipText = getLegendItemToolTipGenerator().generateLabel( 172 dataset, series); 173 } 174 String urlText = null; 175 if (getLegendItemURLGenerator() != null) { 176 urlText = getLegendItemURLGenerator().generateLabel(dataset, 177 series); 178 } 179 Shape shape = lookupLegendShape(series); 180 Paint paint = lookupSeriesPaint(series); 181 Paint outlinePaint = lookupSeriesOutlinePaint(series); 182 Stroke outlineStroke = lookupSeriesOutlineStroke(series); 183 184 LegendItem result = new LegendItem(label, description, toolTipText, 185 urlText, shape, paint, outlineStroke, outlinePaint); 186 result.setLabelFont(lookupLegendTextFont(series)); 187 Paint labelPaint = lookupLegendTextPaint(series); 188 if (labelPaint != null) { 189 result.setLabelPaint(labelPaint); 190 } 191 result.setDataset(dataset); 192 result.setDatasetIndex(datasetIndex); 193 result.setSeriesKey(dataset.getRowKey(series)); 194 result.setSeriesIndex(series); 195 return result; 196 197 } 198 199 /** 200 * Draw a single data item. 201 * 202 * @param g2 the graphics device. 203 * @param state the renderer state. 204 * @param dataArea the data plot area. 205 * @param plot the plot. 206 * @param domainAxis the domain axis. 207 * @param rangeAxis the range axis. 208 * @param dataset the dataset. 209 * @param row the row index (zero-based). 210 * @param column the column index (zero-based). 211 * @param pass the pass index. 212 */ 213 public void drawItem(Graphics2D g2, 214 CategoryItemRendererState state, 215 Rectangle2D dataArea, 216 CategoryPlot plot, 217 CategoryAxis domainAxis, 218 ValueAxis rangeAxis, 219 CategoryDataset dataset, 220 int row, 221 int column, 222 int pass) { 223 224 // do nothing if item is not visible 225 if (!getItemVisible(row, column)) { 226 return; 227 } 228 229 // plot non-null values only... 230 Number value = dataset.getValue(row, column); 231 if (value != null) { 232 PlotOrientation orientation = plot.getOrientation(); 233 RectangleEdge axisEdge = plot.getDomainAxisEdge(); 234 int count = dataset.getColumnCount(); 235 float x0 = (float) domainAxis.getCategoryStart(column, count, 236 dataArea, axisEdge); 237 float x1 = (float) domainAxis.getCategoryMiddle(column, count, 238 dataArea, axisEdge); 239 float x2 = (float) domainAxis.getCategoryEnd(column, count, 240 dataArea, axisEdge); 241 242 x0 = Math.round(x0); 243 x1 = Math.round(x1); 244 x2 = Math.round(x2); 245 246 if (this.endType == AreaRendererEndType.TRUNCATE) { 247 if (column == 0) { 248 x0 = x1; 249 } 250 else if (column == getColumnCount() - 1) { 251 x2 = x1; 252 } 253 } 254 255 double yy1 = value.doubleValue(); 256 257 double yy0 = 0.0; 258 if (column > 0) { 259 Number n0 = dataset.getValue(row, column - 1); 260 if (n0 != null) { 261 yy0 = (n0.doubleValue() + yy1) / 2.0; 262 } 263 } 264 265 double yy2 = 0.0; 266 if (column < dataset.getColumnCount() - 1) { 267 Number n2 = dataset.getValue(row, column + 1); 268 if (n2 != null) { 269 yy2 = (n2.doubleValue() + yy1) / 2.0; 270 } 271 } 272 273 RectangleEdge edge = plot.getRangeAxisEdge(); 274 float y0 = (float) rangeAxis.valueToJava2D(yy0, dataArea, edge); 275 float y1 = (float) rangeAxis.valueToJava2D(yy1, dataArea, edge); 276 float y2 = (float) rangeAxis.valueToJava2D(yy2, dataArea, edge); 277 float yz = (float) rangeAxis.valueToJava2D(0.0, dataArea, edge); 278 279 g2.setPaint(getItemPaint(row, column)); 280 g2.setStroke(getItemStroke(row, column)); 281 282 GeneralPath area = new GeneralPath(); 283 284 if (orientation == PlotOrientation.VERTICAL) { 285 area.moveTo(x0, yz); 286 area.lineTo(x0, y0); 287 area.lineTo(x1, y1); 288 area.lineTo(x2, y2); 289 area.lineTo(x2, yz); 290 } 291 else if (orientation == PlotOrientation.HORIZONTAL) { 292 area.moveTo(yz, x0); 293 area.lineTo(y0, x0); 294 area.lineTo(y1, x1); 295 area.lineTo(y2, x2); 296 area.lineTo(yz, x2); 297 } 298 area.closePath(); 299 300 g2.setPaint(getItemPaint(row, column)); 301 g2.fill(area); 302 303 // draw the item labels if there are any... 304 if (isItemLabelVisible(row, column)) { 305 drawItemLabel(g2, orientation, dataset, row, column, x1, y1, 306 (value.doubleValue() < 0.0)); 307 } 308 309 // submit the current data point as a crosshair candidate 310 int datasetIndex = plot.indexOf(dataset); 311 updateCrosshairValues(state.getCrosshairState(), 312 dataset.getRowKey(row), dataset.getColumnKey(column), 313 yy1, datasetIndex, x1, y1, orientation); 314 315 // add an item entity, if this information is being collected 316 EntityCollection entities = state.getEntityCollection(); 317 if (entities != null) { 318 addItemEntity(entities, dataset, row, column, area); 319 } 320 } 321 322 } 323 324 /** 325 * Tests this instance for equality with an arbitrary object. 326 * 327 * @param obj the object to test (<code>null</code> permitted). 328 * 329 * @return A boolean. 330 */ 331 public boolean equals(Object obj) { 332 if (obj == this) { 333 return true; 334 } 335 if (!(obj instanceof AreaRenderer)) { 336 return false; 337 } 338 AreaRenderer that = (AreaRenderer) obj; 339 if (!this.endType.equals(that.endType)) { 340 return false; 341 } 342 return super.equals(obj); 343 } 344 345 /** 346 * Returns an independent copy of the renderer. 347 * 348 * @return A clone. 349 * 350 * @throws CloneNotSupportedException should not happen. 351 */ 352 public Object clone() throws CloneNotSupportedException { 353 return super.clone(); 354 } 355 356 }