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