KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jfree > chart > renderer > xy > StackedXYAreaRenderer2


1 /* ===========================================================
2  * JFreeChart : a free chart library for the Java(tm) platform
3  * ===========================================================
4  *
5  * (C) Copyright 2000-2006, 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  * StackedXYAreaRenderer2.java
29  * ---------------------------
30  * (C) Copyright 2004-2006, by Object Refinery Limited and Contributors.
31  *
32  * Original Author: David Gilbert (for Object Refinery Limited), based on
33  * the StackedXYAreaRenderer class by Richard Atkinson;
34  * Contributor(s): -;
35  *
36  * $Id: StackedXYAreaRenderer2.java,v 1.6.2.4 2006/11/16 13:36:03 mungady Exp $
37  *
38  * Changes:
39  * --------
40  * 30-Apr-2004 : Version 1 (DG);
41  * 15-Jul-2004 : Switched getX() with getXValue() and getY() with
42  * getYValue() (DG);
43  * 10-Sep-2004 : Removed getRangeType() method (DG);
44  * 06-Jan-2004 : Renamed getRangeExtent() --> findRangeBounds (DG);
45  * 28-Mar-2005 : Use getXValue() and getYValue() from dataset (DG);
46  * 03-Oct-2005 : Add entity generation to drawItem() method (DG);
47  * ------------- JFREECHART 1.0.0 ---------------------------------------------
48  * 22-Aug-2006 : Handle null and empty datasets correctly in the
49  * findRangeBounds() method (DG);
50  * 22-Sep-2006 : Added a flag to allow rounding of x-coordinates (after
51  * translation to Java2D space) in order to avoid the striping
52  * that can result from anti-aliasing (thanks to Doug
53  * Clayton) (DG);
54  *
55  */

56
57 package org.jfree.chart.renderer.xy;
58
59 import java.awt.Graphics2D JavaDoc;
60 import java.awt.Paint JavaDoc;
61 import java.awt.Shape JavaDoc;
62 import java.awt.geom.GeneralPath JavaDoc;
63 import java.awt.geom.Rectangle2D JavaDoc;
64 import java.io.Serializable JavaDoc;
65
66 import org.jfree.chart.axis.ValueAxis;
67 import org.jfree.chart.entity.EntityCollection;
68 import org.jfree.chart.labels.XYToolTipGenerator;
69 import org.jfree.chart.plot.CrosshairState;
70 import org.jfree.chart.plot.PlotRenderingInfo;
71 import org.jfree.chart.plot.XYPlot;
72 import org.jfree.chart.urls.XYURLGenerator;
73 import org.jfree.data.Range;
74 import org.jfree.data.xy.TableXYDataset;
75 import org.jfree.data.xy.XYDataset;
76 import org.jfree.ui.RectangleEdge;
77 import org.jfree.util.PublicCloneable;
78
79 /**
80  * A stacked area renderer for the {@link XYPlot} class.
81  */

82 public class StackedXYAreaRenderer2 extends XYAreaRenderer2
83                                     implements Cloneable JavaDoc,
84                                                PublicCloneable,
85                                                Serializable JavaDoc {
86
87     /** For serialization. */
88     private static final long serialVersionUID = 7752676509764539182L;
89     
90     /**
91      * This flag controls whether or not the x-coordinates (in Java2D space)
92      * are rounded to integers. When set to true, this can avoid the vertical
93      * striping that anti-aliasing can generate. However, the rounding may not
94      * be appropriate for output in high resolution formats (for example,
95      * vector graphics formats such as SVG and PDF).
96      *
97      * @since 1.0.3
98      */

99     private boolean roundXCoordinates = false;
100     
101     /**
102      * Creates a new renderer.
103      */

104     public StackedXYAreaRenderer2() {
105         this(null, null);
106     }
107
108     /**
109      * Constructs a new renderer.
110      *
111      * @param labelGenerator the tool tip generator to use. <code>null</code>
112      * is none.
113      * @param urlGenerator the URL generator (<code>null</code> permitted).
114      */

115     public StackedXYAreaRenderer2(XYToolTipGenerator labelGenerator,
116                                   XYURLGenerator urlGenerator) {
117         super(labelGenerator, urlGenerator);
118     }
119
120     /**
121      * Returns the range of values the renderer requires to display all the
122      * items from the specified dataset.
123      *
124      * @param dataset the dataset (<code>null</code> permitted).
125      *
126      * @return The range (or <code>null</code> if the dataset is
127      * <code>null</code> or empty).
128      */

129     public Range findRangeBounds(XYDataset dataset) {
130         if (dataset == null) {
131             return null;
132         }
133         double min = Double.POSITIVE_INFINITY;
134         double max = Double.NEGATIVE_INFINITY;
135         TableXYDataset d = (TableXYDataset) dataset;
136         int itemCount = d.getItemCount();
137         for (int i = 0; i < itemCount; i++) {
138             double[] stackValues = getStackValues((TableXYDataset) dataset,
139                     d.getSeriesCount(), i);
140             min = Math.min(min, stackValues[0]);
141             max = Math.max(max, stackValues[1]);
142         }
143         if (min == Double.POSITIVE_INFINITY) {
144             return null;
145         }
146         return new Range(min, max);
147     }
148
149     /**
150      * Returns the number of passes required by the renderer.
151      *
152      * @return 1.
153      */

154     public int getPassCount() {
155         return 1;
156     }
157
158     /**
159      * Draws the visual representation of a single data item.
160      *
161      * @param g2 the graphics device.
162      * @param state the renderer state.
163      * @param dataArea the area within which the data is being drawn.
164      * @param info collects information about the drawing.
165      * @param plot the plot (can be used to obtain standard color information
166      * etc).
167      * @param domainAxis the domain axis.
168      * @param rangeAxis the range axis.
169      * @param dataset the dataset.
170      * @param series the series index (zero-based).
171      * @param item the item index (zero-based).
172      * @param crosshairState information about crosshairs on a plot.
173      * @param pass the pass index.
174      */

175     public void drawItem(Graphics2D JavaDoc g2,
176                          XYItemRendererState state,
177                          Rectangle2D JavaDoc dataArea,
178                          PlotRenderingInfo info,
179                          XYPlot plot,
180                          ValueAxis domainAxis,
181                          ValueAxis rangeAxis,
182                          XYDataset dataset,
183                          int series,
184                          int item,
185                          CrosshairState crosshairState,
186                          int pass) {
187
188         // setup for collecting optional entity info...
189
Shape JavaDoc entityArea = null;
190         EntityCollection entities = null;
191         if (info != null) {
192             entities = info.getOwner().getEntityCollection();
193         }
194
195         TableXYDataset tdataset = (TableXYDataset) dataset;
196         
197         // get the data point...
198
double x1 = dataset.getXValue(series, item);
199         double y1 = dataset.getYValue(series, item);
200         if (Double.isNaN(y1)) {
201             y1 = 0.0;
202         }
203         double[] stack1 = getStackValues(tdataset, series, item);
204         
205         // get the previous point and the next point so we can calculate a
206
// "hot spot" for the area (used by the chart entity)...
207
double x0 = dataset.getXValue(series, Math.max(item - 1, 0));
208         double y0 = dataset.getYValue(series, Math.max(item - 1, 0));
209         if (Double.isNaN(y0)) {
210             y0 = 0.0;
211         }
212         double[] stack0 = getStackValues(tdataset, series, Math.max(item - 1,
213                 0));
214         
215         int itemCount = dataset.getItemCount(series);
216         double x2 = dataset.getXValue(series, Math.min(item + 1,
217                 itemCount - 1));
218         double y2 = dataset.getYValue(series, Math.min(item + 1,
219                 itemCount - 1));
220         if (Double.isNaN(y2)) {
221             y2 = 0.0;
222         }
223         double[] stack2 = getStackValues(tdataset, series, Math.min(item + 1,
224                 itemCount - 1));
225
226         double xleft = (x0 + x1) / 2.0;
227         double xright = (x1 + x2) / 2.0;
228         double[] stackLeft = averageStackValues(stack0, stack1);
229         double[] stackRight = averageStackValues(stack1, stack2);
230         double[] adjStackLeft = adjustedStackValues(stack0, stack1);
231         double[] adjStackRight = adjustedStackValues(stack1, stack2);
232         
233         RectangleEdge edge0 = plot.getDomainAxisEdge();
234         
235         float transX1 = (float) domainAxis.valueToJava2D(x1, dataArea, edge0);
236         float transXLeft = (float) domainAxis.valueToJava2D(xleft, dataArea,
237                 edge0);
238         float transXRight = (float) domainAxis.valueToJava2D(xright, dataArea,
239                 edge0);
240         
241         if (this.roundXCoordinates) {
242             transX1 = Math.round(transX1);
243             transXLeft = Math.round(transXLeft);
244             transXRight = Math.round(transXRight);
245         }
246         float transY1;
247         
248         RectangleEdge edge1 = plot.getRangeAxisEdge();
249         
250         GeneralPath JavaDoc left = new GeneralPath JavaDoc();
251         GeneralPath JavaDoc right = new GeneralPath JavaDoc();
252         if (y1 >= 0.0) { // handle positive value
253
transY1 = (float) rangeAxis.valueToJava2D(y1 + stack1[1], dataArea,
254                     edge1);
255             float transStack1 = (float) rangeAxis.valueToJava2D(stack1[1],
256                     dataArea, edge1);
257             float transStackLeft = (float) rangeAxis.valueToJava2D(
258                     adjStackLeft[1], dataArea, edge1);
259             
260             // LEFT POLYGON
261
if (y0 >= 0.0) {
262                 double yleft = (y0 + y1) / 2.0 + stackLeft[1];
263                 float transYLeft
264                     = (float) rangeAxis.valueToJava2D(yleft, dataArea, edge1);
265                 left.moveTo(transX1, transY1);
266                 left.lineTo(transX1, transStack1);
267                 left.lineTo(transXLeft, transStackLeft);
268                 left.lineTo(transXLeft, transYLeft);
269                 left.closePath();
270             }
271             else {
272                 left.moveTo(transX1, transStack1);
273                 left.lineTo(transX1, transY1);
274                 left.lineTo(transXLeft, transStackLeft);
275                 left.closePath();
276             }
277
278             float transStackRight = (float) rangeAxis.valueToJava2D(
279                     adjStackRight[1], dataArea, edge1);
280             // RIGHT POLYGON
281
if (y2 >= 0.0) {
282                 double yright = (y1 + y2) / 2.0 + stackRight[1];
283                 float transYRight
284                     = (float) rangeAxis.valueToJava2D(yright, dataArea, edge1);
285                 right.moveTo(transX1, transStack1);
286                 right.lineTo(transX1, transY1);
287                 right.lineTo(transXRight, transYRight);
288                 right.lineTo(transXRight, transStackRight);
289                 right.closePath();
290             }
291             else {
292                 right.moveTo(transX1, transStack1);
293                 right.lineTo(transX1, transY1);
294                 right.lineTo(transXRight, transStackRight);
295                 right.closePath();
296             }
297         }
298         else { // handle negative value
299
transY1 = (float) rangeAxis.valueToJava2D(y1 + stack1[0], dataArea,
300                     edge1);
301             float transStack1 = (float) rangeAxis.valueToJava2D(stack1[0],
302                     dataArea, edge1);
303             float transStackLeft = (float) rangeAxis.valueToJava2D(
304                     adjStackLeft[0], dataArea, edge1);
305
306             // LEFT POLYGON
307
if (y0 >= 0.0) {
308                 left.moveTo(transX1, transStack1);
309                 left.lineTo(transX1, transY1);
310                 left.lineTo(transXLeft, transStackLeft);
311                 left.clone();
312             }
313             else {
314                 double yleft = (y0 + y1) / 2.0 + stackLeft[0];
315                 float transYLeft = (float) rangeAxis.valueToJava2D(yleft,
316                         dataArea, edge1);
317                 left.moveTo(transX1, transY1);
318                 left.lineTo(transX1, transStack1);
319                 left.lineTo(transXLeft, transStackLeft);
320                 left.lineTo(transXLeft, transYLeft);
321                 left.closePath();
322             }
323             float transStackRight = (float) rangeAxis.valueToJava2D(
324                     adjStackRight[0], dataArea, edge1);
325             
326             // RIGHT POLYGON
327
if (y2 >= 0.0) {
328                 right.moveTo(transX1, transStack1);
329                 right.lineTo(transX1, transY1);
330                 right.lineTo(transXRight, transStackRight);
331                 right.closePath();
332             }
333             else {
334                 double yright = (y1 + y2) / 2.0 + stackRight[0];
335                 float transYRight = (float) rangeAxis.valueToJava2D(yright,
336                         dataArea, edge1);
337                 right.moveTo(transX1, transStack1);
338                 right.lineTo(transX1, transY1);
339                 right.lineTo(transXRight, transYRight);
340                 right.lineTo(transXRight, transStackRight);
341                 right.closePath();
342             }
343         }
344
345         // Get series Paint and Stroke
346
Paint JavaDoc itemPaint = getItemPaint(series, item);
347         if (pass == 0) {
348             g2.setPaint(itemPaint);
349             g2.fill(left);
350             g2.fill(right);
351         }
352         
353         // add an entity for the item...
354
if (entities != null) {
355             GeneralPath JavaDoc gp = new GeneralPath JavaDoc(left);
356             gp.append(right, false);
357             entityArea = gp;
358             addEntity(entities, entityArea, dataset, series, item,
359                     transX1, transY1);
360         }
361
362     }
363
364     /**
365      * Calculates the stacked value of the all series up to, but not including
366      * <code>series</code> for the specified item. It returns 0.0 if
367      * <code>series</code> is the first series, i.e. 0.
368      *
369      * @param dataset the dataset.
370      * @param series the series.
371      * @param index the index.
372      *
373      * @return The cumulative value for all series' values up to but excluding
374      * <code>series</code> for <code>index</code>.
375      */

376     private double[] getStackValues(TableXYDataset dataset,
377                                     int series, int index) {
378         double[] result = new double[2];
379         for (int i = 0; i < series; i++) {
380             double v = dataset.getYValue(i, index);
381             if (!Double.isNaN(v)) {
382                 if (v >= 0.0) {
383                     result[1] += v;
384                 }
385                 else {
386                     result[0] += v;
387                 }
388             }
389         }
390         return result;
391     }
392     
393     /**
394      * Returns a pair of "stack" values calculated from the two specified pairs.
395      *
396      * @param stack1 the first stack pair.
397      * @param stack2 the second stack pair.
398      *
399      * @return A pair of average stack values.
400      */

401     private double[] averageStackValues(double[] stack1, double[] stack2) {
402         double[] result = new double[2];
403         result[0] = (stack1[0] + stack2[0]) / 2.0;
404         result[1] = (stack1[1] + stack2[1]) / 2.0;
405         return result;
406     }
407
408     /**
409      * Returns a pair of "stack" values calculated from the two specified pairs.
410      *
411      * @param stack1 the first stack pair.
412      * @param stack2 the second stack pair.
413      *
414      * @return A pair of average stack values.
415      */

416     private double[] adjustedStackValues(double[] stack1, double[] stack2) {
417         double[] result = new double[2];
418         if (stack1[0] == 0.0 || stack2[0] == 0.0) {
419             result[0] = 0.0;
420         }
421         else {
422             result[0] = (stack1[0] + stack2[0]) / 2.0;
423         }
424         if (stack1[1] == 0.0 || stack2[1] == 0.0) {
425             result[1] = 0.0;
426         }
427         else {
428             result[1] = (stack1[1] + stack2[1]) / 2.0;
429         }
430         return result;
431     }
432
433     /**
434      * Returns a clone of the renderer.
435      *
436      * @return A clone.
437      *
438      * @throws CloneNotSupportedException if the renderer cannot be cloned.
439      */

440     public Object JavaDoc clone() throws CloneNotSupportedException JavaDoc {
441         return super.clone();
442     }
443
444 }
445
Popular Tags