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