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 * 068 */ 069 070 package org.jfree.chart.renderer.category; 071 072 import java.awt.Graphics2D; 073 import java.awt.Paint; 074 import java.awt.Shape; 075 import java.awt.Stroke; 076 import java.awt.geom.GeneralPath; 077 import java.awt.geom.Rectangle2D; 078 import java.io.Serializable; 079 080 import org.jfree.chart.LegendItem; 081 import org.jfree.chart.axis.CategoryAxis; 082 import org.jfree.chart.axis.ValueAxis; 083 import org.jfree.chart.entity.EntityCollection; 084 import org.jfree.chart.event.RendererChangeEvent; 085 import org.jfree.chart.plot.CategoryPlot; 086 import org.jfree.chart.plot.PlotOrientation; 087 import org.jfree.chart.renderer.AreaRendererEndType; 088 import org.jfree.data.category.CategoryDataset; 089 import org.jfree.ui.RectangleEdge; 090 import org.jfree.util.PublicCloneable; 091 092 /** 093 * A category item renderer that draws area charts. You can use this renderer 094 * with the {@link org.jfree.chart.plot.CategoryPlot} class. 095 */ 096 public class AreaRenderer extends AbstractCategoryItemRenderer 097 implements Cloneable, PublicCloneable, Serializable { 098 099 /** For serialization. */ 100 private static final long serialVersionUID = -4231878281385812757L; 101 102 /** A flag that controls how the ends of the areas are drawn. */ 103 private AreaRendererEndType endType; 104 105 /** 106 * Creates a new renderer. 107 */ 108 public AreaRenderer() { 109 super(); 110 this.endType = AreaRendererEndType.TAPER; 111 } 112 113 /** 114 * Returns a token that controls how the renderer draws the end points. 115 * The default value is {@link AreaRendererEndType#TAPER}. 116 * 117 * @return The end type (never <code>null</code>). 118 * 119 * @see #setEndType 120 */ 121 public AreaRendererEndType getEndType() { 122 return this.endType; 123 } 124 125 /** 126 * Sets a token that controls how the renderer draws the end points, and 127 * sends a {@link RendererChangeEvent} to all registered listeners. 128 * 129 * @param type the end type (<code>null</code> not permitted). 130 * 131 * @see #getEndType() 132 */ 133 public void setEndType(AreaRendererEndType type) { 134 if (type == null) { 135 throw new IllegalArgumentException("Null 'type' argument."); 136 } 137 this.endType = type; 138 fireChangeEvent(); 139 } 140 141 /** 142 * Returns a legend item for a series. 143 * 144 * @param datasetIndex the dataset index (zero-based). 145 * @param series the series index (zero-based). 146 * 147 * @return The legend item. 148 */ 149 public LegendItem getLegendItem(int datasetIndex, int series) { 150 151 // if there is no plot, there is no dataset to access... 152 CategoryPlot cp = getPlot(); 153 if (cp == null) { 154 return null; 155 } 156 157 // check that a legend item needs to be displayed... 158 if (!isSeriesVisible(series) || !isSeriesVisibleInLegend(series)) { 159 return null; 160 } 161 162 CategoryDataset dataset = cp.getDataset(datasetIndex); 163 String label = getLegendItemLabelGenerator().generateLabel(dataset, 164 series); 165 String description = label; 166 String toolTipText = null; 167 if (getLegendItemToolTipGenerator() != null) { 168 toolTipText = getLegendItemToolTipGenerator().generateLabel( 169 dataset, series); 170 } 171 String urlText = null; 172 if (getLegendItemURLGenerator() != null) { 173 urlText = getLegendItemURLGenerator().generateLabel(dataset, 174 series); 175 } 176 Shape shape = new Rectangle2D.Double(-4.0, -4.0, 8.0, 8.0); 177 Paint paint = lookupSeriesPaint(series); 178 Paint outlinePaint = lookupSeriesOutlinePaint(series); 179 Stroke outlineStroke = lookupSeriesOutlineStroke(series); 180 181 LegendItem result = new LegendItem(label, description, toolTipText, 182 urlText, shape, paint, outlineStroke, outlinePaint); 183 result.setDataset(dataset); 184 result.setDatasetIndex(datasetIndex); 185 result.setSeriesKey(dataset.getRowKey(series)); 186 result.setSeriesIndex(series); 187 return result; 188 189 } 190 191 /** 192 * Draw a single data item. 193 * 194 * @param g2 the graphics device. 195 * @param state the renderer state. 196 * @param dataArea the data plot area. 197 * @param plot the plot. 198 * @param domainAxis the domain axis. 199 * @param rangeAxis the range axis. 200 * @param dataset the dataset. 201 * @param row the row index (zero-based). 202 * @param column the column index (zero-based). 203 * @param pass the pass index. 204 */ 205 public void drawItem(Graphics2D g2, 206 CategoryItemRendererState state, 207 Rectangle2D dataArea, 208 CategoryPlot plot, 209 CategoryAxis domainAxis, 210 ValueAxis rangeAxis, 211 CategoryDataset dataset, 212 int row, 213 int column, 214 int pass) { 215 216 // do nothing if item is not visible 217 if (!getItemVisible(row, column)) { 218 return; 219 } 220 221 // plot non-null values only... 222 Number value = dataset.getValue(row, column); 223 if (value != null) { 224 PlotOrientation orientation = plot.getOrientation(); 225 RectangleEdge axisEdge = plot.getDomainAxisEdge(); 226 int count = dataset.getColumnCount(); 227 float x0 = (float) domainAxis.getCategoryStart(column, count, 228 dataArea, axisEdge); 229 float x1 = (float) domainAxis.getCategoryMiddle(column, count, 230 dataArea, axisEdge); 231 float x2 = (float) domainAxis.getCategoryEnd(column, count, 232 dataArea, axisEdge); 233 234 x0 = Math.round(x0); 235 x1 = Math.round(x1); 236 x2 = Math.round(x2); 237 238 if (this.endType == AreaRendererEndType.TRUNCATE) { 239 if (column == 0) { 240 x0 = x1; 241 } 242 else if (column == getColumnCount() - 1) { 243 x2 = x1; 244 } 245 } 246 247 double yy1 = value.doubleValue(); 248 249 double yy0 = 0.0; 250 if (column > 0) { 251 Number n0 = dataset.getValue(row, column - 1); 252 if (n0 != null) { 253 yy0 = (n0.doubleValue() + yy1) / 2.0; 254 } 255 } 256 257 double yy2 = 0.0; 258 if (column < dataset.getColumnCount() - 1) { 259 Number n2 = dataset.getValue(row, column + 1); 260 if (n2 != null) { 261 yy2 = (n2.doubleValue() + yy1) / 2.0; 262 } 263 } 264 265 RectangleEdge edge = plot.getRangeAxisEdge(); 266 float y0 = (float) rangeAxis.valueToJava2D(yy0, dataArea, edge); 267 float y1 = (float) rangeAxis.valueToJava2D(yy1, dataArea, edge); 268 float y2 = (float) rangeAxis.valueToJava2D(yy2, dataArea, edge); 269 float yz = (float) rangeAxis.valueToJava2D(0.0, dataArea, edge); 270 271 g2.setPaint(getItemPaint(row, column)); 272 g2.setStroke(getItemStroke(row, column)); 273 274 GeneralPath area = new GeneralPath(); 275 276 if (orientation == PlotOrientation.VERTICAL) { 277 area.moveTo(x0, yz); 278 area.lineTo(x0, y0); 279 area.lineTo(x1, y1); 280 area.lineTo(x2, y2); 281 area.lineTo(x2, yz); 282 } 283 else if (orientation == PlotOrientation.HORIZONTAL) { 284 area.moveTo(yz, x0); 285 area.lineTo(y0, x0); 286 area.lineTo(y1, x1); 287 area.lineTo(y2, x2); 288 area.lineTo(yz, x2); 289 } 290 area.closePath(); 291 292 g2.setPaint(getItemPaint(row, column)); 293 g2.fill(area); 294 295 // draw the item labels if there are any... 296 if (isItemLabelVisible(row, column)) { 297 drawItemLabel(g2, orientation, dataset, row, column, x1, y1, 298 (value.doubleValue() < 0.0)); 299 } 300 301 // add an item entity, if this information is being collected 302 EntityCollection entities = state.getEntityCollection(); 303 if (entities != null) { 304 addItemEntity(entities, dataset, row, column, area); 305 } 306 } 307 308 } 309 310 /** 311 * Tests this instance for equality with an arbitrary object. 312 * 313 * @param obj the object to test (<code>null</code> permitted). 314 * 315 * @return A boolean. 316 */ 317 public boolean equals(Object obj) { 318 if (obj == this) { 319 return true; 320 } 321 if (!(obj instanceof AreaRenderer)) { 322 return false; 323 } 324 AreaRenderer that = (AreaRenderer) obj; 325 if (!this.endType.equals(that.endType)) { 326 return false; 327 } 328 return super.equals(obj); 329 } 330 331 /** 332 * Returns an independent copy of the renderer. 333 * 334 * @return A clone. 335 * 336 * @throws CloneNotSupportedException should not happen. 337 */ 338 public Object clone() throws CloneNotSupportedException { 339 return super.clone(); 340 } 341 342 }