001 /* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-2005, 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 * StatisticalLineAndShapeRenderer.java 029 * ------------------------------------ 030 * (C) Copyright 2005, by Object Refinery Limited and Contributors. 031 * 032 * Original Author: Mofeed Shahin; 033 * Contributor(s): David Gilbert (for Object Refinery Limited); 034 * 035 * $Id: StatisticalLineAndShapeRenderer.java,v 1.4.2.5 2005/12/02 10:40:17 mungady Exp $ 036 * 037 * Changes 038 * ------- 039 * 01-Feb-2005 : Version 1, contributed by Mofeed Shahin (DG); 040 * 16-Jun-2005 : Added errorIndicatorPaint to be consistent with 041 * StatisticalBarRenderer (DG); 042 * 043 */ 044 045 package org.jfree.chart.renderer.category; 046 047 import java.awt.Graphics2D; 048 import java.awt.Paint; 049 import java.awt.Shape; 050 import java.awt.geom.Line2D; 051 import java.awt.geom.Rectangle2D; 052 import java.io.IOException; 053 import java.io.ObjectInputStream; 054 import java.io.ObjectOutputStream; 055 import java.io.Serializable; 056 057 import org.jfree.chart.axis.CategoryAxis; 058 import org.jfree.chart.axis.ValueAxis; 059 import org.jfree.chart.entity.CategoryItemEntity; 060 import org.jfree.chart.entity.EntityCollection; 061 import org.jfree.chart.event.RendererChangeEvent; 062 import org.jfree.chart.labels.CategoryToolTipGenerator; 063 import org.jfree.chart.plot.CategoryPlot; 064 import org.jfree.chart.plot.PlotOrientation; 065 import org.jfree.data.category.CategoryDataset; 066 import org.jfree.data.statistics.StatisticalCategoryDataset; 067 import org.jfree.io.SerialUtilities; 068 import org.jfree.ui.RectangleEdge; 069 import org.jfree.util.PaintUtilities; 070 import org.jfree.util.PublicCloneable; 071 import org.jfree.util.ShapeUtilities; 072 073 /** 074 * A renderer that draws shapes for each data item, and lines between data 075 * items. Each point has a mean value and a standard deviation line. For use 076 * with the {@link CategoryPlot} class. 077 */ 078 public class StatisticalLineAndShapeRenderer extends LineAndShapeRenderer 079 implements Cloneable, PublicCloneable, Serializable { 080 081 /** For serialization. */ 082 private static final long serialVersionUID = -3557517173697777579L; 083 084 /** The paint used to show the error indicator. */ 085 private transient Paint errorIndicatorPaint; 086 087 /** 088 * Constructs a default renderer (draws shapes and lines). 089 */ 090 public StatisticalLineAndShapeRenderer() { 091 this(true, true); 092 } 093 094 /** 095 * Constructs a new renderer. 096 * 097 * @param linesVisible draw lines? 098 * @param shapesVisible draw shapes? 099 */ 100 public StatisticalLineAndShapeRenderer(boolean linesVisible, 101 boolean shapesVisible) { 102 super(true, true); 103 this.errorIndicatorPaint = null; 104 } 105 106 /** 107 * Returns the paint used for the error indicators. 108 * 109 * @return The paint used for the error indicators (possibly 110 * <code>null</code>). 111 */ 112 public Paint getErrorIndicatorPaint() { 113 return this.errorIndicatorPaint; 114 } 115 116 /** 117 * Sets the paint used for the error indicators (if <code>null</code>, 118 * the item outline paint is used instead) 119 * 120 * @param paint the paint (<code>null</code> permitted). 121 */ 122 public void setErrorIndicatorPaint(Paint paint) { 123 this.errorIndicatorPaint = paint; 124 notifyListeners(new RendererChangeEvent(this)); 125 } 126 127 /** 128 * Draw a single data item. 129 * 130 * @param g2 the graphics device. 131 * @param state the renderer state. 132 * @param dataArea the area in which the data is drawn. 133 * @param plot the plot. 134 * @param domainAxis the domain axis. 135 * @param rangeAxis the range axis. 136 * @param dataset the dataset. 137 * @param row the row index (zero-based). 138 * @param column the column index (zero-based). 139 * @param pass the pass. 140 */ 141 public void drawItem(Graphics2D g2, 142 CategoryItemRendererState state, 143 Rectangle2D dataArea, 144 CategoryPlot plot, 145 CategoryAxis domainAxis, 146 ValueAxis rangeAxis, 147 CategoryDataset dataset, 148 int row, 149 int column, 150 int pass) { 151 152 // nothing is drawn for null... 153 Number v = dataset.getValue(row, column); 154 if (v == null) { 155 return; 156 } 157 158 StatisticalCategoryDataset statData 159 = (StatisticalCategoryDataset) dataset; 160 161 Number meanValue = statData.getMeanValue(row, column); 162 163 PlotOrientation orientation = plot.getOrientation(); 164 165 // current data point... 166 double x1 = domainAxis.getCategoryMiddle(column, getColumnCount(), 167 dataArea, plot.getDomainAxisEdge()); 168 169 double y1 = rangeAxis.valueToJava2D(meanValue.doubleValue(), dataArea, 170 plot.getRangeAxisEdge()); 171 172 Shape shape = getItemShape(row, column); 173 if (orientation == PlotOrientation.HORIZONTAL) { 174 shape = ShapeUtilities.createTranslatedShape(shape, y1, x1); 175 } 176 else if (orientation == PlotOrientation.VERTICAL) { 177 shape = ShapeUtilities.createTranslatedShape(shape, x1, y1); 178 } 179 if (getItemShapeVisible(row, column)) { 180 181 if (getItemShapeFilled(row, column)) { 182 g2.setPaint(getItemPaint(row, column)); 183 g2.fill(shape); 184 } 185 else { 186 if (getUseOutlinePaint()) { 187 g2.setPaint(getItemOutlinePaint(row, column)); 188 } 189 else { 190 g2.setPaint(getItemPaint(row, column)); 191 } 192 g2.setStroke(getItemOutlineStroke(row, column)); 193 g2.draw(shape); 194 } 195 } 196 197 if (getItemLineVisible(row, column)) { 198 if (column != 0) { 199 200 Number previousValue = statData.getValue(row, column - 1); 201 if (previousValue != null) { 202 203 // previous data point... 204 double previous = previousValue.doubleValue(); 205 double x0 = domainAxis.getCategoryMiddle(column - 1, 206 getColumnCount(), dataArea, 207 plot.getDomainAxisEdge()); 208 double y0 = rangeAxis.valueToJava2D(previous, dataArea, 209 plot.getRangeAxisEdge()); 210 211 Line2D line = null; 212 if (orientation == PlotOrientation.HORIZONTAL) { 213 line = new Line2D.Double(y0, x0, y1, x1); 214 } 215 else if (orientation == PlotOrientation.VERTICAL) { 216 line = new Line2D.Double(x0, y0, x1, y1); 217 } 218 g2.setPaint(getItemPaint(row, column)); 219 g2.setStroke(getItemStroke(row, column)); 220 g2.draw(line); 221 } 222 } 223 } 224 225 RectangleEdge yAxisLocation = plot.getRangeAxisEdge(); 226 RectangleEdge xAxisLocation = plot.getDomainAxisEdge(); 227 double rectX = domainAxis.getCategoryStart(column, getColumnCount(), 228 dataArea, xAxisLocation); 229 230 rectX = rectX + row * state.getBarWidth(); 231 232 g2.setPaint(getItemPaint(row, column)); 233 //standard deviation lines 234 double valueDelta = statData.getStdDevValue(row, column).doubleValue(); 235 236 double highVal, lowVal; 237 if ((meanValue.doubleValue() + valueDelta) 238 > rangeAxis.getRange().getUpperBound()) { 239 highVal = rangeAxis.valueToJava2D( 240 rangeAxis.getRange().getUpperBound(), dataArea, 241 yAxisLocation); 242 } 243 else { 244 highVal = rangeAxis.valueToJava2D(meanValue.doubleValue() 245 + valueDelta, dataArea, yAxisLocation); 246 } 247 248 if ((meanValue.doubleValue() + valueDelta) 249 < rangeAxis.getRange().getLowerBound()) { 250 lowVal = rangeAxis.valueToJava2D( 251 rangeAxis.getRange().getLowerBound(), dataArea, 252 yAxisLocation); 253 } 254 else { 255 lowVal = rangeAxis.valueToJava2D(meanValue.doubleValue() 256 - valueDelta, dataArea, yAxisLocation); 257 } 258 259 if (this.errorIndicatorPaint != null) { 260 g2.setPaint(this.errorIndicatorPaint); 261 } 262 else { 263 g2.setPaint(getItemPaint(row, column)); 264 } 265 Line2D line = null; 266 line = new Line2D.Double(x1, lowVal, x1, highVal); 267 g2.draw(line); 268 line = new Line2D.Double(x1 - 5.0d, highVal, x1 + 5.0d, highVal); 269 g2.draw(line); 270 line = new Line2D.Double(x1 - 5.0d, lowVal, x1 + 5.0d, lowVal); 271 g2.draw(line); 272 273 // draw the item label if there is one... 274 if (isItemLabelVisible(row, column)) { 275 if (orientation == PlotOrientation.HORIZONTAL) { 276 drawItemLabel(g2, orientation, dataset, row, column, 277 y1, x1, (meanValue.doubleValue() < 0.0)); 278 } 279 else if (orientation == PlotOrientation.VERTICAL) { 280 drawItemLabel(g2, orientation, dataset, row, column, 281 x1, y1, (meanValue.doubleValue() < 0.0)); 282 } 283 } 284 285 // collect entity and tool tip information... 286 if (state.getInfo() != null) { 287 EntityCollection entities = state.getEntityCollection(); 288 if (entities != null && shape != null) { 289 String tip = null; 290 CategoryToolTipGenerator tipster = getToolTipGenerator(row, 291 column); 292 if (tipster != null) { 293 tip = tipster.generateToolTip(dataset, row, column); 294 } 295 String url = null; 296 if (getItemURLGenerator(row, column) != null) { 297 url = getItemURLGenerator(row, column).generateURL( 298 dataset, row, column); 299 } 300 CategoryItemEntity entity = new CategoryItemEntity(shape, tip, 301 url, dataset, row, dataset.getColumnKey(column), 302 column); 303 entities.add(entity); 304 305 } 306 307 } 308 309 } 310 311 /** 312 * Tests this renderer for equality with an arbitrary object. 313 * 314 * @param obj the object (<code>null</code> permitted). 315 * 316 * @return A boolean. 317 */ 318 public boolean equals(Object obj) { 319 if (obj == this) { 320 return true; 321 } 322 if (!(obj instanceof StatisticalLineAndShapeRenderer)) { 323 return false; 324 } 325 if (!super.equals(obj)) { 326 return false; 327 } 328 StatisticalLineAndShapeRenderer that 329 = (StatisticalLineAndShapeRenderer) obj; 330 if (!PaintUtilities.equal(this.errorIndicatorPaint, 331 that.errorIndicatorPaint)) { 332 return false; 333 } 334 return true; 335 } 336 337 /** 338 * Provides serialization support. 339 * 340 * @param stream the output stream. 341 * 342 * @throws IOException if there is an I/O error. 343 */ 344 private void writeObject(ObjectOutputStream stream) throws IOException { 345 stream.defaultWriteObject(); 346 SerialUtilities.writePaint(this.errorIndicatorPaint, stream); 347 } 348 349 /** 350 * Provides serialization support. 351 * 352 * @param stream the input stream. 353 * 354 * @throws IOException if there is an I/O error. 355 * @throws ClassNotFoundException if there is a classpath problem. 356 */ 357 private void readObject(ObjectInputStream stream) 358 throws IOException, ClassNotFoundException { 359 stream.defaultReadObject(); 360 this.errorIndicatorPaint = SerialUtilities.readPaint(stream); 361 } 362 363 }