Frames | No Frames |
1: /* =========================================================== 2: * JFreeChart : a free chart library for the Java(tm) platform 3: * =========================================================== 4: * 5: * (C) Copyright 2000-2007, by Object Refinery Limited and Contributors. 6: * 7: * Project Info: http://www.jfree.org/jfreechart/index.html 8: * 9: * This library is free software; you can redistribute it and/or modify it 10: * under the terms of the GNU Lesser General Public License as published by 11: * the Free Software Foundation; either version 2.1 of the License, or 12: * (at your option) any later version. 13: * 14: * This library is distributed in the hope that it will be useful, but 15: * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 16: * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 17: * License for more details. 18: * 19: * You should have received a copy of the GNU Lesser General Public 20: * License along with this library; if not, write to the Free Software 21: * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 22: * USA. 23: * 24: * [Java is a trademark or registered trademark of Sun Microsystems, Inc. 25: * in the United States and other countries.] 26: * 27: * ----------------- 28: * AreaRenderer.java 29: * ----------------- 30: * (C) Copyright 2002-2007, by Jon Iles and Contributors. 31: * 32: * Original Author: Jon Iles; 33: * Contributor(s): David Gilbert (for Object Refinery Limited); 34: * Christian W. Zuckschwerdt; 35: * 36: * Changes: 37: * -------- 38: * 21-May-2002 : Version 1, contributed by John Iles (DG); 39: * 29-May-2002 : Now extends AbstractCategoryItemRenderer (DG); 40: * 11-Jun-2002 : Updated Javadoc comments (DG); 41: * 25-Jun-2002 : Removed unnecessary imports (DG); 42: * 01-Oct-2002 : Fixed errors reported by Checkstyle (DG); 43: * 10-Oct-2002 : Added constructors and basic entity support (DG); 44: * 24-Oct-2002 : Amendments for changes in CategoryDataset interface and 45: * CategoryToolTipGenerator interface (DG); 46: * 05-Nov-2002 : Replaced references to CategoryDataset with TableDataset (DG); 47: * 06-Nov-2002 : Renamed drawCategoryItem() --> drawItem() and now using axis 48: * for category spacing. Renamed AreaCategoryItemRenderer 49: * --> AreaRenderer (DG); 50: * 17-Jan-2003 : Moved plot classes into a separate package (DG); 51: * 25-Mar-2003 : Implemented Serializable (DG); 52: * 10-Apr-2003 : Changed CategoryDataset to KeyedValues2DDataset in 53: * drawItem() method (DG); 54: * 12-May-2003 : Modified to take into account the plot orientation (DG); 55: * 30-Jul-2003 : Modified entity constructor (CZ); 56: * 13-Aug-2003 : Implemented Cloneable (DG); 57: * 07-Oct-2003 : Added renderer state (DG); 58: * 05-Nov-2004 : Modified drawItem() signature (DG); 59: * 20-Apr-2005 : Apply tooltips and URLs to legend items (DG); 60: * 09-Jun-2005 : Use addItemEntity() method from superclass (DG); 61: * ------------- JFREECHART 1.0.x --------------------------------------------- 62: * 11-Oct-2006 : Fixed bug in equals() method (DG); 63: * 30-Nov-2006 : Added checks for series visibility (DG); 64: * 20-Apr-2007 : Updated getLegendItem() for renderer change (DG); 65: * 17-May-2007 : Set datasetIndex and seriesIndex in getLegendItem() (DG); 66: * 18-May-2007 : Set dataset and seriesKey for LegendItem (DG); 67: * 68: */ 69: 70: package org.jfree.chart.renderer.category; 71: 72: import java.awt.Graphics2D; 73: import java.awt.Paint; 74: import java.awt.Shape; 75: import java.awt.Stroke; 76: import java.awt.geom.GeneralPath; 77: import java.awt.geom.Rectangle2D; 78: import java.io.Serializable; 79: 80: import org.jfree.chart.LegendItem; 81: import org.jfree.chart.axis.CategoryAxis; 82: import org.jfree.chart.axis.ValueAxis; 83: import org.jfree.chart.entity.EntityCollection; 84: import org.jfree.chart.event.RendererChangeEvent; 85: import org.jfree.chart.plot.CategoryPlot; 86: import org.jfree.chart.plot.PlotOrientation; 87: import org.jfree.chart.renderer.AreaRendererEndType; 88: import org.jfree.data.category.CategoryDataset; 89: import org.jfree.ui.RectangleEdge; 90: import org.jfree.util.PublicCloneable; 91: 92: /** 93: * A category item renderer that draws area charts. You can use this renderer 94: * with the {@link org.jfree.chart.plot.CategoryPlot} class. 95: */ 96: public class AreaRenderer extends AbstractCategoryItemRenderer 97: implements Cloneable, PublicCloneable, Serializable { 98: 99: /** 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: notifyListeners(new RendererChangeEvent(this)); 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: }