KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jfree > chart > renderer > category > StackedAreaRenderer


1 /* ===========================================================
2  * JFreeChart : a free chart library for the Java(tm) platform
3  * ===========================================================
4  *
5  * (C) Copyright 2000-2005, 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  * StackedAreaRenderer.java
29  * ------------------------
30  * (C) Copyright 2002-2006, by Dan Rivett (d.rivett@ukonline.co.uk) and
31  * Contributors.
32  *
33  * Original Author: Dan Rivett (adapted from AreaCategoryItemRenderer);
34  * Contributor(s): Jon Iles;
35  * David Gilbert (for Object Refinery Limited);
36  * Christian W. Zuckschwerdt;
37  *
38  * $Id: StackedAreaRenderer.java,v 1.6.2.3 2006/10/11 16:25:50 mungady Exp $
39  *
40  * Changes:
41  * --------
42  * 20-Sep-2002 : Version 1, contributed by Dan Rivett;
43  * 24-Oct-2002 : Amendments for changes in CategoryDataset interface and
44  * CategoryToolTipGenerator interface (DG);
45  * 01-Nov-2002 : Added tooltips (DG);
46  * 06-Nov-2002 : Renamed drawCategoryItem() --> drawItem() and now using axis
47  * for category spacing. Renamed StackedAreaCategoryItemRenderer
48  * --> StackedAreaRenderer (DG);
49  * 26-Nov-2002 : Switched CategoryDataset --> TableDataset (DG);
50  * 26-Nov-2002 : Replaced isStacked() method with getRangeType() method (DG);
51  * 17-Jan-2003 : Moved plot classes to a separate package (DG);
52  * 25-Mar-2003 : Implemented Serializable (DG);
53  * 13-May-2003 : Modified to take into account the plot orientation (DG);
54  * 30-Jul-2003 : Modified entity constructor (CZ);
55  * 07-Oct-2003 : Added renderer state (DG);
56  * 29-Apr-2004 : Added getRangeExtent() override (DG);
57  * 05-Nov-2004 : Modified drawItem() signature (DG);
58  * 07-Jan-2005 : Renamed getRangeExtent() --> findRangeBounds() (DG);
59  * ------------- JFREECHART 1.0.x ---------------------------------------------
60  * 11-Oct-2006 : Added support for rendering data values as percentages,
61  * and added a second pass for drawing item labels (DG);
62  *
63  */

64
65 package org.jfree.chart.renderer.category;
66
67 import java.awt.Graphics2D JavaDoc;
68 import java.awt.Polygon JavaDoc;
69 import java.awt.Shape JavaDoc;
70 import java.awt.geom.Rectangle2D JavaDoc;
71 import java.io.Serializable JavaDoc;
72
73 import org.jfree.chart.axis.CategoryAxis;
74 import org.jfree.chart.axis.ValueAxis;
75 import org.jfree.chart.entity.EntityCollection;
76 import org.jfree.chart.event.RendererChangeEvent;
77 import org.jfree.chart.plot.CategoryPlot;
78 import org.jfree.chart.plot.PlotOrientation;
79 import org.jfree.data.DataUtilities;
80 import org.jfree.data.Range;
81 import org.jfree.data.category.CategoryDataset;
82 import org.jfree.data.general.DatasetUtilities;
83 import org.jfree.ui.RectangleEdge;
84 import org.jfree.util.PublicCloneable;
85
86 /**
87  * A renderer that draws stacked area charts for a
88  * {@link org.jfree.chart.plot.CategoryPlot}.
89  */

90 public class StackedAreaRenderer extends AreaRenderer
91                                  implements Cloneable JavaDoc, PublicCloneable,
92                                             Serializable JavaDoc {
93
94     /** For serialization. */
95     private static final long serialVersionUID = -3595635038460823663L;
96      
97     /** A flag that controls whether the areas display values or percentages. */
98     private boolean renderAsPercentages;
99     
100     /**
101      * Creates a new renderer.
102      */

103     public StackedAreaRenderer() {
104         this(false);
105     }
106     
107     /**
108      * Creates a new renderer.
109      *
110      * @param renderAsPercentages a flag that controls whether the data values
111      * are rendered as percentages.
112      */

113     public StackedAreaRenderer(boolean renderAsPercentages) {
114         super();
115         this.renderAsPercentages = renderAsPercentages;
116     }
117
118     /**
119      * Returns <code>true</code> if the renderer displays each item value as
120      * a percentage (so that the stacked areas add to 100%), and
121      * <code>false</code> otherwise.
122      *
123      * @return A boolean.
124      *
125      * @since 1.0.3
126      */

127     public boolean getRenderAsPercentages() {
128         return this.renderAsPercentages;
129     }
130     
131     /**
132      * Sets the flag that controls whether the renderer displays each item
133      * value as a percentage (so that the stacked areas add to 100%), and sends
134      * a {@link RendererChangeEvent} to all registered listeners.
135      *
136      * @param asPercentages the flag.
137      *
138      * @since 1.0.3
139      */

140     public void setRenderAsPercentages(boolean asPercentages) {
141         this.renderAsPercentages = asPercentages;
142         notifyListeners(new RendererChangeEvent(this));
143     }
144     
145     /**
146      * Returns the number of passes (<code>2</code>) required by this renderer.
147      * The first pass is used to draw the bars, the second pass is used to
148      * draw the item labels (if visible).
149      *
150      * @return The number of passes required by the renderer.
151      */

152     public int getPassCount() {
153         return 2;
154     }
155
156     /**
157      * Returns the range of values the renderer requires to display all the
158      * items from the specified dataset.
159      *
160      * @param dataset the dataset (<code>null</code> not permitted).
161      *
162      * @return The range (or <code>null</code> if the dataset is empty).
163      */

164     public Range findRangeBounds(CategoryDataset dataset) {
165         if (this.renderAsPercentages) {
166             return new Range(0.0, 1.0);
167         }
168         else {
169             return DatasetUtilities.findStackedRangeBounds(dataset);
170         }
171     }
172
173     /**
174      * Draw a single data item.
175      *
176      * @param g2 the graphics device.
177      * @param state the renderer state.
178      * @param dataArea the data plot area.
179      * @param plot the plot.
180      * @param domainAxis the domain axis.
181      * @param rangeAxis the range axis.
182      * @param dataset the data.
183      * @param row the row index (zero-based).
184      * @param column the column index (zero-based).
185      * @param pass the pass index.
186      */

187     public void drawItem(Graphics2D JavaDoc g2,
188                          CategoryItemRendererState state,
189                          Rectangle2D JavaDoc dataArea,
190                          CategoryPlot plot,
191                          CategoryAxis domainAxis,
192                          ValueAxis rangeAxis,
193                          CategoryDataset dataset,
194                          int row,
195                          int column,
196                          int pass) {
197
198         // plot non-null values...
199
Number JavaDoc dataValue = dataset.getValue(row, column);
200         if (dataValue == null) {
201             return;
202         }
203
204         double value = dataValue.doubleValue();
205         double total = 0.0; // only needed if calculating percentages
206
if (this.renderAsPercentages) {
207             total = DataUtilities.calculateColumnTotal(dataset, column);
208             value = value / total;
209         }
210
211         // leave the y values (y1, y0) untranslated as it is going to be be
212
// stacked up later by previous series values, after this it will be
213
// translated.
214
double xx1 = domainAxis.getCategoryMiddle(column, getColumnCount(),
215                 dataArea, plot.getDomainAxisEdge());
216         
217         double previousHeightx1 = getPreviousHeight(dataset, row, column);
218         double y1 = value + previousHeightx1;
219         RectangleEdge location = plot.getRangeAxisEdge();
220         double yy1 = rangeAxis.valueToJava2D(y1, dataArea, location);
221
222         g2.setPaint(getItemPaint(row, column));
223         g2.setStroke(getItemStroke(row, column));
224
225         // in column zero, the only job to do is draw any visible item labels
226
// and this is done in the second pass...
227
if (column == 0) {
228             if (pass == 1) {
229                 // draw item labels, if visible
230
if (isItemLabelVisible(row, column)) {
231                     drawItemLabel(g2, plot.getOrientation(), dataset, row, column,
232                             xx1, yy1, (y1 < 0.0));
233                 }
234             }
235         }
236         else {
237             Number JavaDoc previousValue = dataset.getValue(row, column - 1);
238             if (previousValue != null) {
239
240                 double xx0 = domainAxis.getCategoryMiddle(column - 1,
241                         getColumnCount(), dataArea, plot.getDomainAxisEdge());
242                 double y0 = previousValue.doubleValue();
243                 if (this.renderAsPercentages) {
244                     total = DataUtilities.calculateColumnTotal(dataset,
245                             column - 1);
246                     y0 = y0 / total;
247                 }
248                
249
250                 // Get the previous height, but this will be different for both
251
// y0 and y1 as the previous series values could differ.
252
double previousHeightx0 = getPreviousHeight(dataset, row,
253                         column - 1);
254
255                 // Now stack the current y values on top of the previous values.
256
y0 += previousHeightx0;
257
258                 // Now translate the previous heights
259
double previousHeightxx0 = rangeAxis.valueToJava2D(
260                         previousHeightx0, dataArea, location);
261                 double previousHeightxx1 = rangeAxis.valueToJava2D(
262                         previousHeightx1, dataArea, location);
263
264                 // Now translate the current y values.
265
double yy0 = rangeAxis.valueToJava2D(y0, dataArea, location);
266
267                 if (pass == 0) {
268                     Polygon JavaDoc p = null;
269                     PlotOrientation orientation = plot.getOrientation();
270                     if (orientation == PlotOrientation.HORIZONTAL) {
271                         p = new Polygon JavaDoc();
272                         p.addPoint((int) yy0, (int) xx0);
273                         p.addPoint((int) yy1, (int) xx1);
274                         p.addPoint((int) previousHeightxx1, (int) xx1);
275                         p.addPoint((int) previousHeightxx0, (int) xx0);
276                     }
277                     else if (orientation == PlotOrientation.VERTICAL) {
278                         p = new Polygon JavaDoc();
279                         p.addPoint((int) xx0, (int) yy0);
280                         p.addPoint((int) xx1, (int) yy1);
281                         p.addPoint((int) xx1, (int) previousHeightxx1);
282                         p.addPoint((int) xx0, (int) previousHeightxx0);
283                     }
284                     g2.setPaint(getItemPaint(row, column));
285                     g2.setStroke(getItemStroke(row, column));
286                     g2.fill(p);
287                 }
288                 else {
289                     if (isItemLabelVisible(row, column)) {
290                         drawItemLabel(g2, plot.getOrientation(), dataset, row,
291                                 column, xx1, yy1, (y1 < 0.0));
292                     }
293                 }
294             }
295             
296
297         }
298         
299
300         // add an item entity, if this information is being collected
301
EntityCollection entities = state.getEntityCollection();
302         if (entities != null) {
303             Shape JavaDoc shape = new Rectangle2D.Double JavaDoc(xx1 - 3.0, yy1 - 3.0, 6.0, 6.0);
304             addItemEntity(entities, dataset, row, column, shape);
305         }
306
307     }
308
309     /**
310      * Calculates the stacked value of the all series up to, but not including
311      * <code>series</code> for the specified category, <code>category</code>.
312      * It returns 0.0 if <code>series</code> is the first series, i.e. 0.
313      *
314      * @param dataset the dataset (<code>null</code> not permitted).
315      * @param series the series.
316      * @param category the category.
317      *
318      * @return double returns a cumulative value for all series' values up to
319      * but excluding <code>series</code> for Object
320      * <code>category</code>.
321      */

322     protected double getPreviousHeight(CategoryDataset dataset,
323                                        int series, int category) {
324
325         double result = 0.0;
326         Number JavaDoc n;
327         double total = 0.0;
328         if (this.renderAsPercentages) {
329             total = DataUtilities.calculateColumnTotal(dataset, category);
330         }
331         for (int i = 0; i < series; i++) {
332             n = dataset.getValue(i, category);
333             if (n != null) {
334                 double v = n.doubleValue();
335                 if (this.renderAsPercentages) {
336                     v = v / total;
337                 }
338                 result += v;
339             }
340         }
341         return result;
342
343     }
344
345     /**
346      * Checks this instance for equality with an arbitrary object.
347      *
348      * @param obj the object (<code>null</code> not permitted).
349      *
350      * @return A boolean.
351      */

352     public boolean equals(Object JavaDoc obj) {
353         if (obj == this) {
354             return true;
355         }
356         if (! (obj instanceof StackedAreaRenderer)) {
357             return false;
358         }
359         StackedAreaRenderer that = (StackedAreaRenderer) obj;
360         if (this.renderAsPercentages != that.renderAsPercentages) {
361             return false;
362         }
363         return super.equals(obj);
364     }
365 }
366
Popular Tags