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: * StatisticalBarRenderer.java
029: * ---------------------------
030: * (C) Copyright 2002-2007, by Pascal Collet and Contributors.
031: *
032: * Original Author: Pascal Collet;
033: * Contributor(s): David Gilbert (for Object Refinery Limited);
034: * Christian W. Zuckschwerdt;
035: *
036: * $Id: StatisticalBarRenderer.java,v 1.4.2.6 2007/02/02 15:52:07 mungady Exp $
037: *
038: * Changes
039: * -------
040: * 21-Aug-2002 : Version 1, contributed by Pascal Collet (DG);
041: * 01-Oct-2002 : Fixed errors reported by Checkstyle (DG);
042: * 24-Oct-2002 : Changes to dataset interface (DG);
043: * 05-Nov-2002 : Base dataset is now TableDataset not CategoryDataset (DG);
044: * 05-Feb-2003 : Updates for new DefaultStatisticalCategoryDataset (DG);
045: * 25-Mar-2003 : Implemented Serializable (DG);
046: * 30-Jul-2003 : Modified entity constructor (CZ);
047: * 06-Oct-2003 : Corrected typo in exception message (DG);
048: * 05-Nov-2004 : Modified drawItem() signature (DG);
049: * 15-Jun-2005 : Added errorIndicatorPaint attribute (DG);
050: * ------------- JFREECHART 1.0.x ---------------------------------------------
051: * 19-May-2006 : Added support for tooltips and URLs (DG);
052: * 12-Jul-2006 : Added support for item labels (DG);
053: * 02-Feb-2007 : Removed author tags all over JFreeChart sources (DG);
054: *
055: */
056:
057: package org.jfree.chart.renderer.category;
058:
059: import java.awt.Color;
060: import java.awt.Graphics2D;
061: import java.awt.Paint;
062: import java.awt.geom.Line2D;
063: import java.awt.geom.Rectangle2D;
064: import java.io.IOException;
065: import java.io.ObjectInputStream;
066: import java.io.ObjectOutputStream;
067: import java.io.Serializable;
068:
069: import org.jfree.chart.axis.CategoryAxis;
070: import org.jfree.chart.axis.ValueAxis;
071: import org.jfree.chart.entity.EntityCollection;
072: import org.jfree.chart.event.RendererChangeEvent;
073: import org.jfree.chart.labels.CategoryItemLabelGenerator;
074: import org.jfree.chart.plot.CategoryPlot;
075: import org.jfree.chart.plot.PlotOrientation;
076: import org.jfree.data.category.CategoryDataset;
077: import org.jfree.data.statistics.StatisticalCategoryDataset;
078: import org.jfree.io.SerialUtilities;
079: import org.jfree.ui.RectangleEdge;
080: import org.jfree.util.PaintUtilities;
081: import org.jfree.util.PublicCloneable;
082:
083: /**
084: * A renderer that handles the drawing a bar plot where
085: * each bar has a mean value and a standard deviation line.
086: */
087: public class StatisticalBarRenderer extends BarRenderer implements
088: CategoryItemRenderer, Cloneable, PublicCloneable, Serializable {
089:
090: /** For serialization. */
091: private static final long serialVersionUID = -4986038395414039117L;
092:
093: /** The paint used to show the error indicator. */
094: private transient Paint errorIndicatorPaint;
095:
096: /**
097: * Default constructor.
098: */
099: public StatisticalBarRenderer() {
100: super ();
101: this .errorIndicatorPaint = Color.gray;
102: }
103:
104: /**
105: * Returns the paint used for the error indicators.
106: *
107: * @return The paint used for the error indicators (possibly
108: * <code>null</code>).
109: */
110: public Paint getErrorIndicatorPaint() {
111: return this .errorIndicatorPaint;
112: }
113:
114: /**
115: * Sets the paint used for the error indicators (if <code>null</code>,
116: * the item outline paint is used instead)
117: *
118: * @param paint the paint (<code>null</code> permitted).
119: */
120: public void setErrorIndicatorPaint(Paint paint) {
121: this .errorIndicatorPaint = paint;
122: notifyListeners(new RendererChangeEvent(this ));
123: }
124:
125: /**
126: * Draws the bar with its standard deviation line range for a single
127: * (series, category) data item.
128: *
129: * @param g2 the graphics device.
130: * @param state the renderer state.
131: * @param dataArea the data area.
132: * @param plot the plot.
133: * @param domainAxis the domain axis.
134: * @param rangeAxis the range axis.
135: * @param data the data.
136: * @param row the row index (zero-based).
137: * @param column the column index (zero-based).
138: * @param pass the pass index.
139: */
140: public void drawItem(Graphics2D g2,
141: CategoryItemRendererState state, Rectangle2D dataArea,
142: CategoryPlot plot, CategoryAxis domainAxis,
143: ValueAxis rangeAxis, CategoryDataset data, int row,
144: int column, int pass) {
145:
146: // defensive check
147: if (!(data instanceof StatisticalCategoryDataset)) {
148: throw new IllegalArgumentException(
149: "Requires StatisticalCategoryDataset.");
150: }
151: StatisticalCategoryDataset statData = (StatisticalCategoryDataset) data;
152:
153: PlotOrientation orientation = plot.getOrientation();
154: if (orientation == PlotOrientation.HORIZONTAL) {
155: drawHorizontalItem(g2, state, dataArea, plot, domainAxis,
156: rangeAxis, statData, row, column);
157: } else if (orientation == PlotOrientation.VERTICAL) {
158: drawVerticalItem(g2, state, dataArea, plot, domainAxis,
159: rangeAxis, statData, row, column);
160: }
161: }
162:
163: /**
164: * Draws an item for a plot with a horizontal orientation.
165: *
166: * @param g2 the graphics device.
167: * @param state the renderer state.
168: * @param dataArea the data area.
169: * @param plot the plot.
170: * @param domainAxis the domain axis.
171: * @param rangeAxis the range axis.
172: * @param dataset the data.
173: * @param row the row index (zero-based).
174: * @param column the column index (zero-based).
175: */
176: protected void drawHorizontalItem(Graphics2D g2,
177: CategoryItemRendererState state, Rectangle2D dataArea,
178: CategoryPlot plot, CategoryAxis domainAxis,
179: ValueAxis rangeAxis, StatisticalCategoryDataset dataset,
180: int row, int column) {
181:
182: RectangleEdge xAxisLocation = plot.getDomainAxisEdge();
183:
184: // BAR Y
185: double rectY = domainAxis.getCategoryStart(column,
186: getColumnCount(), dataArea, xAxisLocation);
187:
188: int seriesCount = getRowCount();
189: int categoryCount = getColumnCount();
190: if (seriesCount > 1) {
191: double seriesGap = dataArea.getHeight() * getItemMargin()
192: / (categoryCount * (seriesCount - 1));
193: rectY = rectY + row * (state.getBarWidth() + seriesGap);
194: } else {
195: rectY = rectY + row * state.getBarWidth();
196: }
197:
198: // BAR X
199: Number meanValue = dataset.getMeanValue(row, column);
200:
201: double value = meanValue.doubleValue();
202: double base = 0.0;
203: double lclip = getLowerClip();
204: double uclip = getUpperClip();
205:
206: if (uclip <= 0.0) { // cases 1, 2, 3 and 4
207: if (value >= uclip) {
208: return; // bar is not visible
209: }
210: base = uclip;
211: if (value <= lclip) {
212: value = lclip;
213: }
214: } else if (lclip <= 0.0) { // cases 5, 6, 7 and 8
215: if (value >= uclip) {
216: value = uclip;
217: } else {
218: if (value <= lclip) {
219: value = lclip;
220: }
221: }
222: } else { // cases 9, 10, 11 and 12
223: if (value <= lclip) {
224: return; // bar is not visible
225: }
226: base = getLowerClip();
227: if (value >= uclip) {
228: value = uclip;
229: }
230: }
231:
232: RectangleEdge yAxisLocation = plot.getRangeAxisEdge();
233: double transY1 = rangeAxis.valueToJava2D(base, dataArea,
234: yAxisLocation);
235: double transY2 = rangeAxis.valueToJava2D(value, dataArea,
236: yAxisLocation);
237: double rectX = Math.min(transY2, transY1);
238:
239: double rectHeight = state.getBarWidth();
240: double rectWidth = Math.abs(transY2 - transY1);
241:
242: Rectangle2D bar = new Rectangle2D.Double(rectX, rectY,
243: rectWidth, rectHeight);
244: Paint seriesPaint = getItemPaint(row, column);
245: g2.setPaint(seriesPaint);
246: g2.fill(bar);
247: if (state.getBarWidth() > 3) {
248: g2.setStroke(getItemStroke(row, column));
249: g2.setPaint(getItemOutlinePaint(row, column));
250: g2.draw(bar);
251: }
252:
253: // standard deviation lines
254: double valueDelta = dataset.getStdDevValue(row, column)
255: .doubleValue();
256: double highVal = rangeAxis.valueToJava2D(meanValue
257: .doubleValue()
258: + valueDelta, dataArea, yAxisLocation);
259: double lowVal = rangeAxis.valueToJava2D(meanValue.doubleValue()
260: - valueDelta, dataArea, yAxisLocation);
261:
262: if (this .errorIndicatorPaint != null) {
263: g2.setPaint(this .errorIndicatorPaint);
264: } else {
265: g2.setPaint(getItemOutlinePaint(row, column));
266: }
267: Line2D line = null;
268: line = new Line2D.Double(lowVal, rectY + rectHeight / 2.0d,
269: highVal, rectY + rectHeight / 2.0d);
270: g2.draw(line);
271: line = new Line2D.Double(highVal, rectY + rectHeight * 0.25,
272: highVal, rectY + rectHeight * 0.75);
273: g2.draw(line);
274: line = new Line2D.Double(lowVal, rectY + rectHeight * 0.25,
275: lowVal, rectY + rectHeight * 0.75);
276: g2.draw(line);
277:
278: CategoryItemLabelGenerator generator = getItemLabelGenerator(
279: row, column);
280: if (generator != null && isItemLabelVisible(row, column)) {
281: drawItemLabel(g2, dataset, row, column, plot, generator,
282: bar, (value < 0.0));
283: }
284:
285: // add an item entity, if this information is being collected
286: EntityCollection entities = state.getEntityCollection();
287: if (entities != null) {
288: addItemEntity(entities, dataset, row, column, bar);
289: }
290:
291: }
292:
293: /**
294: * Draws an item for a plot with a vertical orientation.
295: *
296: * @param g2 the graphics device.
297: * @param state the renderer state.
298: * @param dataArea the data area.
299: * @param plot the plot.
300: * @param domainAxis the domain axis.
301: * @param rangeAxis the range axis.
302: * @param dataset the data.
303: * @param row the row index (zero-based).
304: * @param column the column index (zero-based).
305: */
306: protected void drawVerticalItem(Graphics2D g2,
307: CategoryItemRendererState state, Rectangle2D dataArea,
308: CategoryPlot plot, CategoryAxis domainAxis,
309: ValueAxis rangeAxis, StatisticalCategoryDataset dataset,
310: int row, int column) {
311:
312: RectangleEdge xAxisLocation = plot.getDomainAxisEdge();
313:
314: // BAR X
315: double rectX = domainAxis.getCategoryStart(column,
316: getColumnCount(), dataArea, xAxisLocation);
317:
318: int seriesCount = getRowCount();
319: int categoryCount = getColumnCount();
320: if (seriesCount > 1) {
321: double seriesGap = dataArea.getWidth() * getItemMargin()
322: / (categoryCount * (seriesCount - 1));
323: rectX = rectX + row * (state.getBarWidth() + seriesGap);
324: } else {
325: rectX = rectX + row * state.getBarWidth();
326: }
327:
328: // BAR Y
329: Number meanValue = dataset.getMeanValue(row, column);
330:
331: double value = meanValue.doubleValue();
332: double base = 0.0;
333: double lclip = getLowerClip();
334: double uclip = getUpperClip();
335:
336: if (uclip <= 0.0) { // cases 1, 2, 3 and 4
337: if (value >= uclip) {
338: return; // bar is not visible
339: }
340: base = uclip;
341: if (value <= lclip) {
342: value = lclip;
343: }
344: } else if (lclip <= 0.0) { // cases 5, 6, 7 and 8
345: if (value >= uclip) {
346: value = uclip;
347: } else {
348: if (value <= lclip) {
349: value = lclip;
350: }
351: }
352: } else { // cases 9, 10, 11 and 12
353: if (value <= lclip) {
354: return; // bar is not visible
355: }
356: base = getLowerClip();
357: if (value >= uclip) {
358: value = uclip;
359: }
360: }
361:
362: RectangleEdge yAxisLocation = plot.getRangeAxisEdge();
363: double transY1 = rangeAxis.valueToJava2D(base, dataArea,
364: yAxisLocation);
365: double transY2 = rangeAxis.valueToJava2D(value, dataArea,
366: yAxisLocation);
367: double rectY = Math.min(transY2, transY1);
368:
369: double rectWidth = state.getBarWidth();
370: double rectHeight = Math.abs(transY2 - transY1);
371:
372: Rectangle2D bar = new Rectangle2D.Double(rectX, rectY,
373: rectWidth, rectHeight);
374: Paint seriesPaint = getItemPaint(row, column);
375: g2.setPaint(seriesPaint);
376: g2.fill(bar);
377: if (state.getBarWidth() > 3) {
378: g2.setStroke(getItemStroke(row, column));
379: g2.setPaint(getItemOutlinePaint(row, column));
380: g2.draw(bar);
381: }
382:
383: // standard deviation lines
384: double valueDelta = dataset.getStdDevValue(row, column)
385: .doubleValue();
386: double highVal = rangeAxis.valueToJava2D(meanValue
387: .doubleValue()
388: + valueDelta, dataArea, yAxisLocation);
389: double lowVal = rangeAxis.valueToJava2D(meanValue.doubleValue()
390: - valueDelta, dataArea, yAxisLocation);
391:
392: if (this .errorIndicatorPaint != null) {
393: g2.setPaint(this .errorIndicatorPaint);
394: } else {
395: g2.setPaint(getItemOutlinePaint(row, column));
396: }
397: Line2D line = null;
398: line = new Line2D.Double(rectX + rectWidth / 2.0d, lowVal,
399: rectX + rectWidth / 2.0d, highVal);
400: g2.draw(line);
401: line = new Line2D.Double(rectX + rectWidth / 2.0d - 5.0d,
402: highVal, rectX + rectWidth / 2.0d + 5.0d, highVal);
403: g2.draw(line);
404: line = new Line2D.Double(rectX + rectWidth / 2.0d - 5.0d,
405: lowVal, rectX + rectWidth / 2.0d + 5.0d, lowVal);
406: g2.draw(line);
407:
408: CategoryItemLabelGenerator generator = getItemLabelGenerator(
409: row, column);
410: if (generator != null && isItemLabelVisible(row, column)) {
411: drawItemLabel(g2, dataset, row, column, plot, generator,
412: bar, (value < 0.0));
413: }
414:
415: // add an item entity, if this information is being collected
416: EntityCollection entities = state.getEntityCollection();
417: if (entities != null) {
418: addItemEntity(entities, dataset, row, column, bar);
419: }
420: }
421:
422: /**
423: * Tests this renderer for equality with an arbitrary object.
424: *
425: * @param obj the object (<code>null</code> permitted).
426: *
427: * @return A boolean.
428: */
429: public boolean equals(Object obj) {
430: if (obj == this ) {
431: return true;
432: }
433: if (!(obj instanceof StatisticalBarRenderer)) {
434: return false;
435: }
436: if (!super .equals(obj)) {
437: return false;
438: }
439: StatisticalBarRenderer that = (StatisticalBarRenderer) obj;
440: if (!PaintUtilities.equal(this .errorIndicatorPaint,
441: that.errorIndicatorPaint)) {
442: return false;
443: }
444: return true;
445: }
446:
447: /**
448: * Provides serialization support.
449: *
450: * @param stream the output stream.
451: *
452: * @throws IOException if there is an I/O error.
453: */
454: private void writeObject(ObjectOutputStream stream)
455: throws IOException {
456: stream.defaultWriteObject();
457: SerialUtilities.writePaint(this .errorIndicatorPaint, stream);
458: }
459:
460: /**
461: * Provides serialization support.
462: *
463: * @param stream the input stream.
464: *
465: * @throws IOException if there is an I/O error.
466: * @throws ClassNotFoundException if there is a classpath problem.
467: */
468: private void readObject(ObjectInputStream stream)
469: throws IOException, ClassNotFoundException {
470: stream.defaultReadObject();
471: this.errorIndicatorPaint = SerialUtilities.readPaint(stream);
472: }
473:
474: }
|