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