KickJava   Java API By Example, From Geeks To Geeks.

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


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  * XYStepAreaRenderer.java
29  * -----------------------
30  * (C) Copyright 2003-2006, by Matthias Rose and Contributors.
31  *
32  * Original Author: Matthias Rose (based on XYAreaRenderer.java);
33  * Contributor(s): David Gilbert (for Object Refinery Limited);
34  *
35  * $Id: XYStepAreaRenderer.java,v 1.7.2.4 2006/07/06 10:27:57 mungady Exp $
36  *
37  * Changes:
38  * --------
39  * 07-Oct-2003 : Version 1, contributed by Matthias Rose (DG);
40  * 10-Feb-2004 : Added some getter and setter methods (DG);
41  * 25-Feb-2004 : Replaced CrosshairInfo with CrosshairState. Renamed
42  * XYToolTipGenerator --> XYItemLabelGenerator (DG);
43  * 15-Jul-2004 : Switched getX() with getXValue() and getY() with
44  * getYValue() (DG);
45  * 11-Nov-2004 : Now uses ShapeUtilities to translate shapes (DG);
46  * 06-Jul-2005 : Renamed get/setPlotShapes() --> get/setShapesVisible() (DG);
47  * ------------- JFREECHART 1.0.0 ---------------------------------------------
48  * 06-Jul-2006 : Modified to call dataset methods that return double
49  * primitives only (DG);
50  *
51  */

52
53 package org.jfree.chart.renderer.xy;
54
55 import java.awt.Graphics2D JavaDoc;
56 import java.awt.Paint JavaDoc;
57 import java.awt.Polygon JavaDoc;
58 import java.awt.Shape JavaDoc;
59 import java.awt.Stroke JavaDoc;
60 import java.awt.geom.Rectangle2D JavaDoc;
61 import java.io.Serializable JavaDoc;
62
63 import org.jfree.chart.axis.ValueAxis;
64 import org.jfree.chart.entity.EntityCollection;
65 import org.jfree.chart.entity.XYItemEntity;
66 import org.jfree.chart.event.RendererChangeEvent;
67 import org.jfree.chart.labels.XYToolTipGenerator;
68 import org.jfree.chart.plot.CrosshairState;
69 import org.jfree.chart.plot.PlotOrientation;
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.xy.XYDataset;
74 import org.jfree.util.PublicCloneable;
75 import org.jfree.util.ShapeUtilities;
76
77 /**
78  * A step chart renderer that fills the area between the step and the x-axis.
79  */

80 public class XYStepAreaRenderer extends AbstractXYItemRenderer
81                                 implements XYItemRenderer,
82                                            Cloneable JavaDoc,
83                                            PublicCloneable,
84                                            Serializable JavaDoc {
85
86     /** For serialization. */
87     private static final long serialVersionUID = -7311560779702649635L;
88     
89     /** Useful constant for specifying the type of rendering (shapes only). */
90     public static final int SHAPES = 1;
91
92     /** Useful constant for specifying the type of rendering (area only). */
93     public static final int AREA = 2;
94
95     /**
96      * Useful constant for specifying the type of rendering (area and shapes).
97      */

98     public static final int AREA_AND_SHAPES = 3;
99
100     /** A flag indicating whether or not shapes are drawn at each XY point. */
101     private boolean shapesVisible;
102
103     /** A flag that controls whether or not shapes are filled for ALL series. */
104     private boolean shapesFilled;
105
106     /** A flag indicating whether or not Area are drawn at each XY point. */
107     private boolean plotArea;
108
109     /** A flag that controls whether or not the outline is shown. */
110     private boolean showOutline;
111
112     /** Area of the complete series */
113     protected transient Polygon JavaDoc pArea = null;
114
115     /**
116      * The value on the range axis which defines the 'lower' border of the
117      * area.
118      */

119     private double rangeBase;
120
121     /**
122      * Constructs a new renderer.
123      */

124     public XYStepAreaRenderer() {
125         this(AREA);
126     }
127
128     /**
129      * Constructs a new renderer.
130      *
131      * @param type the type of the renderer.
132      */

133     public XYStepAreaRenderer(int type) {
134         this(type, null, null);
135     }
136
137     /**
138      * Constructs a new renderer.
139      * <p>
140      * To specify the type of renderer, use one of the constants:
141      * AREA, SHAPES or AREA_AND_SHAPES.
142      *
143      * @param type the type of renderer.
144      * @param toolTipGenerator the tool tip generator to use
145      * (<code>null</code> permitted).
146      * @param urlGenerator the URL generator (<code>null</code> permitted).
147      */

148     public XYStepAreaRenderer(int type,
149                               XYToolTipGenerator toolTipGenerator,
150                               XYURLGenerator urlGenerator) {
151
152         super();
153         setBaseToolTipGenerator(toolTipGenerator);
154         setURLGenerator(urlGenerator);
155
156         if (type == AREA) {
157             this.plotArea = true;
158         }
159         else if (type == SHAPES) {
160             this.shapesVisible = true;
161         }
162         else if (type == AREA_AND_SHAPES) {
163             this.plotArea = true;
164             this.shapesVisible = true;
165         }
166         this.showOutline = false;
167     }
168
169     /**
170      * Returns a flag that controls whether or not outlines of the areas are
171      * drawn.
172      *
173      * @return The flag.
174      */

175     public boolean isOutline() {
176         return this.showOutline;
177     }
178
179     /**
180      * Sets a flag that controls whether or not outlines of the areas are
181      * drawn, and sends a {@link RendererChangeEvent} to all registered
182      * listeners.
183      *
184      * @param show the flag.
185      */

186     public void setOutline(boolean show) {
187         this.showOutline = show;
188         notifyListeners(new RendererChangeEvent(this));
189     }
190
191     /**
192      * Returns true if shapes are being plotted by the renderer.
193      *
194      * @return <code>true</code> if shapes are being plotted by the renderer.
195      */

196     public boolean getShapesVisible() {
197         return this.shapesVisible;
198     }
199     
200     /**
201      * Sets the flag that controls whether or not shapes are displayed for each
202      * data item, and sends a {@link RendererChangeEvent} to all registered
203      * listeners.
204      *
205      * @param flag the flag.
206      */

207     public void setShapesVisible(boolean flag) {
208         this.shapesVisible = flag;
209         notifyListeners(new RendererChangeEvent(this));
210     }
211
212     /**
213      * Returns the flag that controls whether or not the shapes are filled.
214      *
215      * @return A boolean.
216      */

217     public boolean isShapesFilled() {
218         return this.shapesFilled;
219     }
220     
221     /**
222      * Sets the 'shapes filled' for ALL series.
223      *
224      * @param filled the flag.
225      */

226     public void setShapesFilled(boolean filled) {
227         this.shapesFilled = filled;
228         notifyListeners(new RendererChangeEvent(this));
229     }
230
231     /**
232      * Returns true if Area is being plotted by the renderer.
233      *
234      * @return <code>true</code> if Area is being plotted by the renderer.
235      */

236     public boolean getPlotArea() {
237         return this.plotArea;
238     }
239
240     /**
241      * Sets a flag that controls whether or not areas are drawn for each data
242      * item.
243      *
244      * @param flag the flag.
245      */

246     public void setPlotArea(boolean flag) {
247         this.plotArea = flag;
248         notifyListeners(new RendererChangeEvent(this));
249     }
250     
251     /**
252      * Returns the value on the range axis which defines the 'lower' border of
253      * the area.
254      *
255      * @return <code>double</code> the value on the range axis which defines
256      * the 'lower' border of the area.
257      */

258     public double getRangeBase() {
259         return this.rangeBase;
260     }
261
262     /**
263      * Sets the value on the range axis which defines the default border of the
264      * area. E.g. setRangeBase(Double.NEGATIVE_INFINITY) lets areas always
265      * reach the lower border of the plotArea.
266      *
267      * @param val the value on the range axis which defines the default border
268      * of the area.
269      */

270     public void setRangeBase(double val) {
271         this.rangeBase = val;
272         notifyListeners(new RendererChangeEvent(this));
273     }
274
275     /**
276      * Initialises the renderer. Here we calculate the Java2D y-coordinate for
277      * zero, since all the bars have their bases fixed at zero.
278      *
279      * @param g2 the graphics device.
280      * @param dataArea the area inside the axes.
281      * @param plot the plot.
282      * @param data the data.
283      * @param info an optional info collection object to return data back to
284      * the caller.
285      *
286      * @return The number of passes required by the renderer.
287      */

288     public XYItemRendererState initialise(Graphics2D JavaDoc g2,
289                                           Rectangle2D JavaDoc dataArea,
290                                           XYPlot plot,
291                                           XYDataset data,
292                                           PlotRenderingInfo info) {
293
294         return super.initialise(g2, dataArea, plot, data, info);
295
296     }
297
298
299     /**
300      * Draws the visual representation of a single data item.
301      *
302      * @param g2 the graphics device.
303      * @param state the renderer state.
304      * @param dataArea the area within which the data is being drawn.
305      * @param info collects information about the drawing.
306      * @param plot the plot (can be used to obtain standard color information
307      * etc).
308      * @param domainAxis the domain axis.
309      * @param rangeAxis the range axis.
310      * @param dataset the dataset.
311      * @param series the series index (zero-based).
312      * @param item the item index (zero-based).
313      * @param crosshairState crosshair information for the plot
314      * (<code>null</code> permitted).
315      * @param pass the pass index.
316      */

317     public void drawItem(Graphics2D JavaDoc g2,
318                          XYItemRendererState state,
319                          Rectangle2D JavaDoc dataArea,
320                          PlotRenderingInfo info,
321                          XYPlot plot,
322                          ValueAxis domainAxis,
323                          ValueAxis rangeAxis,
324                          XYDataset dataset,
325                          int series,
326                          int item,
327                          CrosshairState crosshairState,
328                          int pass) {
329                              
330         PlotOrientation orientation = plot.getOrientation();
331         
332         // Get the item count for the series, so that we can know which is the
333
// end of the series.
334
int itemCount = dataset.getItemCount(series);
335
336         Paint JavaDoc paint = getItemPaint(series, item);
337         Stroke JavaDoc seriesStroke = getItemStroke(series, item);
338         g2.setPaint(paint);
339         g2.setStroke(seriesStroke);
340
341         // get the data point...
342
double x1 = dataset.getXValue(series, item);
343         double y1 = dataset.getYValue(series, item);
344         double x = x1;
345         double y = Double.isNaN(y1) ? getRangeBase() : y1;
346         double transX1 = domainAxis.valueToJava2D(x, dataArea,
347                 plot.getDomainAxisEdge());
348         double transY1 = rangeAxis.valueToJava2D(y, dataArea,
349                 plot.getRangeAxisEdge());
350                                                           
351         // avoid possible sun.dc.pr.PRException: endPath: bad path
352
transY1 = restrictValueToDataArea(transY1, plot, dataArea);
353
354         if (this.pArea == null && !Double.isNaN(y1)) {
355
356             // Create a new Area for the series
357
this.pArea = new Polygon JavaDoc();
358         
359             // start from Y = rangeBase
360
double transY2 = rangeAxis.valueToJava2D(getRangeBase(), dataArea,
361                     plot.getRangeAxisEdge());
362         
363             // avoid possible sun.dc.pr.PRException: endPath: bad path
364
transY2 = restrictValueToDataArea(transY2, plot, dataArea);
365         
366             // The first point is (x, this.baseYValue)
367
if (orientation == PlotOrientation.VERTICAL) {
368                 this.pArea.addPoint((int) transX1, (int) transY2);
369             }
370             else if (orientation == PlotOrientation.HORIZONTAL) {
371                 this.pArea.addPoint((int) transY2, (int) transX1);
372             }
373         }
374
375         double transX0 = 0;
376         double transY0 = restrictValueToDataArea(
377             getRangeBase(), plot, dataArea
378         );
379         
380         double x0;
381         double y0;
382         if (item > 0) {
383             // get the previous data point...
384
x0 = dataset.getXValue(series, item - 1);
385             y0 = Double.isNaN(y1) ? y1 : dataset.getYValue(series, item - 1);
386
387             x = x0;
388             y = Double.isNaN(y0) ? getRangeBase() : y0;
389             transX0 = domainAxis.valueToJava2D(x, dataArea,
390                     plot.getDomainAxisEdge());
391             transY0 = rangeAxis.valueToJava2D(y, dataArea,
392                     plot.getRangeAxisEdge());
393
394             // avoid possible sun.dc.pr.PRException: endPath: bad path
395
transY0 = restrictValueToDataArea(transY0, plot, dataArea);
396                         
397             if (Double.isNaN(y1)) {
398                 // NULL value -> insert point on base line
399
// instead of 'step point'
400
transX1 = transX0;
401                 transY0 = transY1;
402             }
403             if (transY0 != transY1) {
404                 // not just a horizontal bar but need to perform a 'step'.
405
if (orientation == PlotOrientation.VERTICAL) {
406                     this.pArea.addPoint((int) transX1, (int) transY0);
407                 }
408                 else if (orientation == PlotOrientation.HORIZONTAL) {
409                     this.pArea.addPoint((int) transY0, (int) transX1);
410                 }
411             }
412         }
413
414         Shape JavaDoc shape = null;
415         if (!Double.isNaN(y1)) {
416             // Add each point to Area (x, y)
417
if (orientation == PlotOrientation.VERTICAL) {
418                 this.pArea.addPoint((int) transX1, (int) transY1);
419             }
420             else if (orientation == PlotOrientation.HORIZONTAL) {
421                 this.pArea.addPoint((int) transY1, (int) transX1);
422             }
423
424             if (getShapesVisible()) {
425                 shape = getItemShape(series, item);
426                 if (orientation == PlotOrientation.VERTICAL) {
427                     shape = ShapeUtilities.createTranslatedShape(shape,
428                             transX1, transY1);
429                 }
430                 else if (orientation == PlotOrientation.HORIZONTAL) {
431                     shape = ShapeUtilities.createTranslatedShape(shape,
432                             transY1, transX1);
433                 }
434                 if (isShapesFilled()) {
435                     g2.fill(shape);
436                 }
437                 else {
438                     g2.draw(shape);
439                 }
440             }
441             else {
442                 if (orientation == PlotOrientation.VERTICAL) {
443                     shape = new Rectangle2D.Double JavaDoc(transX1 - 2, transY1 - 2,
444                             4.0, 4.0);
445                 }
446                 else if (orientation == PlotOrientation.HORIZONTAL) {
447                     shape = new Rectangle2D.Double JavaDoc(transY1 - 2, transX1 - 2,
448                             4.0, 4.0);
449                 }
450             }
451         }
452
453         // Check if the item is the last item for the series or if it
454
// is a NULL value and number of items > 0. We can't draw an area for
455
// a single point.
456
if (getPlotArea() && item > 0 && this.pArea != null
457                           && (item == (itemCount - 1) || Double.isNaN(y1))) {
458
459             double transY2 = rangeAxis.valueToJava2D(getRangeBase(), dataArea,
460                     plot.getRangeAxisEdge());
461
462             // avoid possible sun.dc.pr.PRException: endPath: bad path
463
transY2 = restrictValueToDataArea(transY2, plot, dataArea);
464
465             if (orientation == PlotOrientation.VERTICAL) {
466                 // Add the last point (x,0)
467
this.pArea.addPoint((int) transX1, (int) transY2);
468             }
469             else if (orientation == PlotOrientation.HORIZONTAL) {
470                 // Add the last point (x,0)
471
this.pArea.addPoint((int) transY2, (int) transX1);
472             }
473
474             // fill the polygon
475
g2.fill(this.pArea);
476
477             // draw an outline around the Area.
478
if (isOutline()) {
479                 g2.setStroke(plot.getOutlineStroke());
480                 g2.setPaint(plot.getOutlinePaint());
481                 g2.draw(this.pArea);
482             }
483
484             // start new area when needed (see above)
485
this.pArea = null;
486         }
487
488         // do we need to update the crosshair values?
489
if (!Double.isNaN(y1)) {
490             updateCrosshairValues(crosshairState, x1, y1, transX1, transY1,
491                     orientation);
492         }
493
494         // collect entity and tool tip information...
495
if (state.getInfo() != null) {
496             EntityCollection entities = state.getEntityCollection();
497             if (entities != null && shape != null) {
498                 String JavaDoc tip = null;
499                 XYToolTipGenerator generator
500                     = getToolTipGenerator(series, item);
501                 if (generator != null) {
502                     tip = generator.generateToolTip(dataset, series, item);
503                 }
504                 String JavaDoc url = null;
505                 if (getURLGenerator() != null) {
506                     url = getURLGenerator().generateURL(dataset, series, item);
507                 }
508                 XYItemEntity entity = new XYItemEntity(shape, dataset, series,
509                         item, tip, url);
510                 entities.add(entity);
511             }
512         }
513     }
514
515     /**
516      * Returns a clone of the renderer.
517      *
518      * @return A clone.
519      *
520      * @throws CloneNotSupportedException if the renderer cannot be cloned.
521      */

522     public Object JavaDoc clone() throws CloneNotSupportedException JavaDoc {
523         return super.clone();
524     }
525     
526     /**
527      * Helper method which returns a value if it lies
528      * inside the visible dataArea and otherwise the corresponding
529      * coordinate on the border of the dataArea. The PlotOrientation
530      * is taken into account.
531      * Useful to avoid possible sun.dc.pr.PRException: endPath: bad path
532      * which occurs when trying to draw lines/shapes which in large part
533      * lie outside of the visible dataArea.
534      *
535      * @param value the value which shall be
536      * @param dataArea the area within which the data is being drawn.
537      * @param plot the plot (can be used to obtain standard color
538      * information etc).
539      * @return <code>double</code> value inside the data area.
540      */

541     protected static double restrictValueToDataArea(double value,
542                                                     XYPlot plot,
543                                                     Rectangle2D JavaDoc dataArea) {
544         double min = 0;
545         double max = 0;
546         if (plot.getOrientation() == PlotOrientation.VERTICAL) {
547             min = dataArea.getMinY();
548             max = dataArea.getMaxY();
549         }
550         else if (plot.getOrientation() == PlotOrientation.HORIZONTAL) {
551             min = dataArea.getMinX();
552             max = dataArea.getMaxX();
553         }
554         if (value < min) {
555             value = min;
556         }
557         else if (value > max) {
558             value = max;
559         }
560         return value;
561     }
562
563 }
564
Popular Tags