KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jfree > chart > plot > PolarPlot


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  * PolarPlot.java
29  * --------------
30  * (C) Copyright 2004-2006, by Solution Engineering, Inc. and Contributors.
31  *
32  * Original Author: Daniel Bridenbecker, Solution Engineering, Inc.;
33  * Contributor(s): David Gilbert (for Object Refinery Limited);
34  *
35  * $Id: PolarPlot.java,v 1.13.2.5 2006/09/07 16:05:51 mungady Exp $
36  *
37  * Changes
38  * -------
39  * 19-Jan-2004 : Version 1, contributed by DB with minor changes by DG (DG);
40  * 07-Apr-2004 : Changed text bounds calculation (DG);
41  * 05-May-2005 : Updated draw() method parameters (DG);
42  * 09-Jun-2005 : Fixed getDataRange() and equals() methods (DG);
43  * 25-Oct-2005 : Implemented Zoomable (DG);
44  *
45  */

46
47 package org.jfree.chart.plot;
48
49 import java.awt.AlphaComposite JavaDoc;
50 import java.awt.BasicStroke JavaDoc;
51 import java.awt.Color JavaDoc;
52 import java.awt.Composite JavaDoc;
53 import java.awt.Font JavaDoc;
54 import java.awt.FontMetrics JavaDoc;
55 import java.awt.Graphics2D JavaDoc;
56 import java.awt.Paint JavaDoc;
57 import java.awt.Point JavaDoc;
58 import java.awt.Shape JavaDoc;
59 import java.awt.Stroke JavaDoc;
60 import java.awt.geom.Point2D JavaDoc;
61 import java.awt.geom.Rectangle2D JavaDoc;
62 import java.io.IOException JavaDoc;
63 import java.io.ObjectInputStream JavaDoc;
64 import java.io.ObjectOutputStream JavaDoc;
65 import java.io.Serializable JavaDoc;
66 import java.util.ArrayList JavaDoc;
67 import java.util.Iterator JavaDoc;
68 import java.util.List JavaDoc;
69 import java.util.ResourceBundle JavaDoc;
70
71 import org.jfree.chart.LegendItem;
72 import org.jfree.chart.LegendItemCollection;
73 import org.jfree.chart.axis.AxisState;
74 import org.jfree.chart.axis.NumberTick;
75 import org.jfree.chart.axis.ValueAxis;
76 import org.jfree.chart.event.PlotChangeEvent;
77 import org.jfree.chart.event.RendererChangeEvent;
78 import org.jfree.chart.event.RendererChangeListener;
79 import org.jfree.chart.renderer.PolarItemRenderer;
80 import org.jfree.data.Range;
81 import org.jfree.data.general.DatasetChangeEvent;
82 import org.jfree.data.general.DatasetUtilities;
83 import org.jfree.data.xy.XYDataset;
84 import org.jfree.io.SerialUtilities;
85 import org.jfree.text.TextUtilities;
86 import org.jfree.ui.RectangleEdge;
87 import org.jfree.ui.RectangleInsets;
88 import org.jfree.ui.TextAnchor;
89 import org.jfree.util.ObjectUtilities;
90 import org.jfree.util.PaintUtilities;
91
92
93 /**
94  * Plots data that is in (theta, radius) pairs where
95  * theta equal to zero is due north and and increases clockwise.
96  *
97  * @author Daniel Bridenbecker, Solution Engineering, Inc.
98  */

99 public class PolarPlot extends Plot implements ValueAxisPlot,
100                                                Zoomable,
101                                                RendererChangeListener,
102                                                Cloneable JavaDoc,
103                                                Serializable JavaDoc {
104    
105     /** For serialization. */
106     private static final long serialVersionUID = 3794383185924179525L;
107     
108     /** The default margin. */
109     private static final int MARGIN = 20;
110    
111     /** The annotation margin. */
112     private static final double ANNOTATION_MARGIN = 7.0;
113    
114     /** The default grid line stroke. */
115     public static final Stroke JavaDoc DEFAULT_GRIDLINE_STROKE = new BasicStroke JavaDoc(
116             0.5f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL,
117             0.0f, new float[]{2.0f, 2.0f}, 0.0f);
118    
119     /** The default grid line paint. */
120     public static final Paint JavaDoc DEFAULT_GRIDLINE_PAINT = Color.gray;
121    
122     /** The resourceBundle for the localization. */
123     protected static ResourceBundle JavaDoc localizationResources
124         = ResourceBundle.getBundle("org.jfree.chart.plot.LocalizationBundle");
125    
126     // ------------------------
127
// --- Member Variables ---
128
// ------------------------
129
/** The angles that are marked with gridlines. */
130     private List JavaDoc angleTicks;
131     
132     /** The axis (used for the y-values). */
133     private ValueAxis axis;
134     
135     /** The dataset. */
136     private XYDataset dataset;
137    
138     /**
139      * Object responsible for drawing the visual representation of each point
140      * on the plot.
141      */

142     private PolarItemRenderer renderer;
143    
144     /** A flag that controls whether or not the angle labels are visible. */
145     private boolean angleLabelsVisible = true;
146     
147     /** The font used to display the angle labels - never null. */
148     private Font JavaDoc angleLabelFont = new Font JavaDoc("SansSerif", Font.PLAIN, 12);
149     
150     /** The paint used to display the angle labels. */
151     private Paint JavaDoc angleLabelPaint = Color.black;
152     
153     /** A flag that controls whether the angular grid-lines are visible. */
154     private boolean angleGridlinesVisible;
155    
156     /** The stroke used to draw the angular grid-lines. */
157     private transient Stroke JavaDoc angleGridlineStroke;
158    
159     /** The paint used to draw the angular grid-lines. */
160     private transient Paint JavaDoc angleGridlinePaint;
161    
162     /** A flag that controls whether the radius grid-lines are visible. */
163     private boolean radiusGridlinesVisible;
164    
165     /** The stroke used to draw the radius grid-lines. */
166     private transient Stroke JavaDoc radiusGridlineStroke;
167    
168     /** The paint used to draw the radius grid-lines. */
169     private transient Paint JavaDoc radiusGridlinePaint;
170    
171     /** The annotations for the plot. */
172     private List JavaDoc cornerTextItems = new ArrayList JavaDoc();
173    
174     // --------------------
175
// --- Constructors ---
176
// --------------------
177
/**
178      * Default constructor.
179      */

180     public PolarPlot() {
181         this(null, null, null);
182     }
183    
184    /**
185      * Creates a new plot.
186      *
187      * @param dataset the dataset (<code>null</code> permitted).
188      * @param radiusAxis the radius axis (<code>null</code> permitted).
189      * @param renderer the renderer (<code>null</code> permitted).
190      */

191     public PolarPlot(XYDataset dataset,
192                      ValueAxis radiusAxis,
193                      PolarItemRenderer renderer) {
194       
195         super();
196             
197         this.dataset = dataset;
198         if (this.dataset != null) {
199             this.dataset.addChangeListener(this);
200         }
201       
202         this.angleTicks = new java.util.ArrayList JavaDoc();
203         this.angleTicks.add(new NumberTick(new Double JavaDoc(0.0), "0",
204                 TextAnchor.CENTER, TextAnchor.CENTER, 0.0));
205         this.angleTicks.add(new NumberTick(new Double JavaDoc(45.0), "45",
206                 TextAnchor.CENTER, TextAnchor.CENTER, 0.0));
207         this.angleTicks.add(new NumberTick(new Double JavaDoc(90.0), "90",
208                 TextAnchor.CENTER, TextAnchor.CENTER, 0.0));
209         this.angleTicks.add(new NumberTick(new Double JavaDoc(135.0), "135",
210                 TextAnchor.CENTER, TextAnchor.CENTER, 0.0));
211         this.angleTicks.add(new NumberTick(new Double JavaDoc(180.0), "180",
212                 TextAnchor.CENTER, TextAnchor.CENTER, 0.0));
213         this.angleTicks.add(new NumberTick(new Double JavaDoc(225.0), "225",
214                 TextAnchor.CENTER, TextAnchor.CENTER, 0.0));
215         this.angleTicks.add(new NumberTick(new Double JavaDoc(270.0), "270",
216                 TextAnchor.CENTER, TextAnchor.CENTER, 0.0));
217         this.angleTicks.add(new NumberTick(new Double JavaDoc(315.0), "315",
218                 TextAnchor.CENTER, TextAnchor.CENTER, 0.0));
219         
220         this.axis = radiusAxis;
221         if (this.axis != null) {
222             this.axis.setPlot(this);
223             this.axis.addChangeListener(this);
224         }
225       
226         this.renderer = renderer;
227         if (this.renderer != null) {
228             this.renderer.setPlot(this);
229             this.renderer.addChangeListener(this);
230         }
231       
232         this.angleGridlinesVisible = true;
233         this.angleGridlineStroke = DEFAULT_GRIDLINE_STROKE;
234         this.angleGridlinePaint = DEFAULT_GRIDLINE_PAINT;
235       
236         this.radiusGridlinesVisible = true;
237         this.radiusGridlineStroke = DEFAULT_GRIDLINE_STROKE;
238         this.radiusGridlinePaint = DEFAULT_GRIDLINE_PAINT;
239     }
240    
241     /**
242      * Add text to be displayed in the lower right hand corner.
243      *
244      * @param text the text to display (<code>null</code> not permitted).
245      */

246     public void addCornerTextItem(String JavaDoc text) {
247         if (text == null) {
248             throw new IllegalArgumentException JavaDoc("Null 'text' argument.");
249         }
250         this.cornerTextItems.add(text);
251         this.notifyListeners(new PlotChangeEvent(this));
252     }
253    
254     /**
255      * Remove the given text from the list of corner text items.
256      *
257      * @param text the text to remove (<code>null</code> ignored).
258      */

259     public void removeCornerTextItem(String JavaDoc text) {
260         boolean removed = this.cornerTextItems.remove(text);
261         if (removed) {
262             this.notifyListeners(new PlotChangeEvent(this));
263         }
264     }
265    
266     /**
267      * Clear the list of corner text items.
268      */

269     public void clearCornerTextItems() {
270         if (this.cornerTextItems.size() > 0) {
271             this.cornerTextItems.clear();
272             this.notifyListeners(new PlotChangeEvent(this));
273         }
274     }
275    
276     /**
277      * Returns the plot type as a string.
278      *
279      * @return A short string describing the type of plot.
280      */

281     public String JavaDoc getPlotType() {
282        return PolarPlot.localizationResources.getString("Polar_Plot");
283     }
284     
285     /**
286      * Returns the axis for the plot.
287      *
288      * @return The radius axis.
289      */

290     public ValueAxis getAxis() {
291         return this.axis;
292     }
293    
294     /**
295      * Sets the axis for the plot and sends a {@link PlotChangeEvent} to all
296      * registered listeners.
297      *
298      * @param axis the new axis (<code>null</code> permitted).
299      */

300     public void setAxis(ValueAxis axis) {
301         if (axis != null) {
302             axis.setPlot(this);
303         }
304        
305         // plot is likely registered as a listener with the existing axis...
306
if (this.axis != null) {
307             this.axis.removeChangeListener(this);
308         }
309        
310         this.axis = axis;
311         if (this.axis != null) {
312             this.axis.configure();
313             this.axis.addChangeListener(this);
314         }
315         notifyListeners(new PlotChangeEvent(this));
316     }
317    
318     /**
319      * Returns the primary dataset for the plot.
320      *
321      * @return The primary dataset (possibly <code>null</code>).
322      */

323     public XYDataset getDataset() {
324         return this.dataset;
325     }
326     
327     /**
328      * Sets the dataset for the plot, replacing the existing dataset if there
329      * is one.
330      *
331      * @param dataset the dataset (<code>null</code> permitted).
332      */

333     public void setDataset(XYDataset dataset) {
334         // if there is an existing dataset, remove the plot from the list of
335
// change listeners...
336
XYDataset existing = this.dataset;
337         if (existing != null) {
338             existing.removeChangeListener(this);
339         }
340        
341         // set the new m_Dataset, and register the chart as a change listener...
342
this.dataset = dataset;
343         if (this.dataset != null) {
344             setDatasetGroup(this.dataset.getGroup());
345             this.dataset.addChangeListener(this);
346         }
347        
348         // send a m_Dataset change event to self...
349
DatasetChangeEvent event = new DatasetChangeEvent(this, this.dataset);
350         datasetChanged(event);
351     }
352    
353     /**
354      * Returns the item renderer.
355      *
356      * @return The renderer (possibly <code>null</code>).
357      */

358     public PolarItemRenderer getRenderer() {
359         return this.renderer;
360     }
361    
362     /**
363      * Sets the item renderer, and notifies all listeners of a change to the
364      * plot.
365      * <P>
366      * If the renderer is set to <code>null</code>, no chart will be drawn.
367      *
368      * @param renderer the new renderer (<code>null</code> permitted).
369      */

370     public void setRenderer(PolarItemRenderer renderer) {
371         if (this.renderer != null) {
372             this.renderer.removeChangeListener(this);
373         }
374        
375         this.renderer = renderer;
376         if (this.renderer != null) {
377             this.renderer.setPlot(this);
378         }
379        
380         notifyListeners(new PlotChangeEvent(this));
381     }
382    
383     /**
384      * Returns a flag that controls whether or not the angle labels are visible.
385      *
386      * @return A boolean.
387      */

388     public boolean isAngleLabelsVisible() {
389         return this.angleLabelsVisible;
390     }
391     
392     /**
393      * Sets the flag that controls whether or not the angle labels are visible,
394      * and sends a {@link PlotChangeEvent} to all registered listeners.
395      *
396      * @param visible the flag.
397      */

398     public void setAngleLabelsVisible(boolean visible) {
399         if (this.angleLabelsVisible != visible) {
400             this.angleLabelsVisible = visible;
401             notifyListeners(new PlotChangeEvent(this));
402         }
403     }
404     
405     /**
406      * Returns the font used to display the angle labels.
407      *
408      * @return A font (never <code>null</code>).
409      */

410     public Font JavaDoc getAngleLabelFont() {
411         return this.angleLabelFont;
412     }
413     
414     /**
415      * Sets the font used to display the angle labels and sends a
416      * {@link PlotChangeEvent} to all registered listeners.
417      *
418      * @param font the font (<code>null</code> not permitted).
419      */

420     public void setAngleLabelFont(Font JavaDoc font) {
421         if (font == null) {
422             throw new IllegalArgumentException JavaDoc("Null 'font' argument.");
423         }
424         this.angleLabelFont = font;
425         notifyListeners(new PlotChangeEvent(this));
426     }
427     
428     /**
429      * Returns the paint used to display the angle labels.
430      *
431      * @return A paint.
432      */

433     public Paint JavaDoc getAngleLabelPaint() {
434         return this.angleLabelPaint;
435     }
436     
437     /**
438      * Sets the paint used to display the angle labels and sends a
439      * {@link PlotChangeEvent} to all registered listeners.
440      *
441      * @param paint the paint.
442      */

443     public void setAngleLabelPaint(Paint JavaDoc paint) {
444         this.angleLabelPaint = paint;
445         notifyListeners(new PlotChangeEvent(this));
446     }
447     
448     /**
449      * Returns <code>true</code> if the angular gridlines are visible, and
450      * <code>false<code> otherwise.
451      *
452      * @return <code>true</code> or <code>false</code>.
453      */

454     public boolean isAngleGridlinesVisible() {
455         return this.angleGridlinesVisible;
456     }
457     
458     /**
459      * Sets the flag that controls whether or not the angular grid-lines are
460      * visible.
461      * <p>
462      * If the flag value is changed, a {@link PlotChangeEvent} is sent to all
463      * registered listeners.
464      *
465      * @param visible the new value of the flag.
466      */

467     public void setAngleGridlinesVisible(boolean visible) {
468         if (this.angleGridlinesVisible != visible) {
469             this.angleGridlinesVisible = visible;
470             notifyListeners(new PlotChangeEvent(this));
471         }
472     }
473    
474     /**
475      * Returns the stroke for the grid-lines (if any) plotted against the
476      * angular axis.
477      *
478      * @return The stroke.
479      */

480     public Stroke JavaDoc getAngleGridlineStroke() {
481         return this.angleGridlineStroke;
482     }
483     
484     /**
485      * Sets the stroke for the grid lines plotted against the angular axis.
486      * <p>
487      * If you set this to <code>null</code>, no grid lines will be drawn.
488      *
489      * @param stroke the stroke (<code>null</code> permitted).
490      */

491     public void setAngleGridlineStroke(Stroke JavaDoc stroke) {
492         this.angleGridlineStroke = stroke;
493         notifyListeners(new PlotChangeEvent(this));
494     }
495     
496     /**
497      * Returns the paint for the grid lines (if any) plotted against the
498      * angular axis.
499      *
500      * @return The paint.
501      */

502     public Paint JavaDoc getAngleGridlinePaint() {
503         return this.angleGridlinePaint;
504     }
505    
506     /**
507      * Sets the paint for the grid lines plotted against the angular axis.
508      * <p>
509      * If you set this to <code>null</code>, no grid lines will be drawn.
510      *
511      * @param paint the paint (<code>null</code> permitted).
512      */

513     public void setAngleGridlinePaint(Paint JavaDoc paint) {
514         this.angleGridlinePaint = paint;
515         notifyListeners(new PlotChangeEvent(this));
516     }
517     
518     /**
519      * Returns <code>true</code> if the radius axis grid is visible, and
520      * <code>false<code> otherwise.
521      *
522      * @return <code>true</code> or <code>false</code>.
523      */

524     public boolean isRadiusGridlinesVisible() {
525         return this.radiusGridlinesVisible;
526     }
527     
528     /**
529      * Sets the flag that controls whether or not the radius axis grid lines
530      * are visible.
531      * <p>
532      * If the flag value is changed, a {@link PlotChangeEvent} is sent to all
533      * registered listeners.
534      *
535      * @param visible the new value of the flag.
536      */

537     public void setRadiusGridlinesVisible(boolean visible) {
538         if (this.radiusGridlinesVisible != visible) {
539             this.radiusGridlinesVisible = visible;
540             notifyListeners(new PlotChangeEvent(this));
541         }
542     }
543    
544     /**
545      * Returns the stroke for the grid lines (if any) plotted against the
546      * radius axis.
547      *
548      * @return The stroke.
549      */

550     public Stroke JavaDoc getRadiusGridlineStroke() {
551         return this.radiusGridlineStroke;
552     }
553     
554     /**
555      * Sets the stroke for the grid lines plotted against the radius axis.
556      * <p>
557      * If you set this to <code>null</code>, no grid lines will be drawn.
558      *
559      * @param stroke the stroke (<code>null</code> permitted).
560      */

561     public void setRadiusGridlineStroke(Stroke JavaDoc stroke) {
562         this.radiusGridlineStroke = stroke;
563         notifyListeners(new PlotChangeEvent(this));
564     }
565     
566     /**
567      * Returns the paint for the grid lines (if any) plotted against the radius
568      * axis.
569      *
570      * @return The paint.
571      */

572     public Paint JavaDoc getRadiusGridlinePaint() {
573         return this.radiusGridlinePaint;
574     }
575     
576     /**
577      * Sets the paint for the grid lines plotted against the radius axis.
578      * <p>
579      * If you set this to <code>null</code>, no grid lines will be drawn.
580      *
581      * @param paint the paint (<code>null</code> permitted).
582      */

583     public void setRadiusGridlinePaint(Paint JavaDoc paint) {
584         this.radiusGridlinePaint = paint;
585         notifyListeners(new PlotChangeEvent(this));
586     }
587     
588     /**
589      * Draws the plot on a Java 2D graphics device (such as the screen or a
590      * printer).
591      * <P>
592      * This plot relies on an
593      * {@link org.jfree.chart.renderer.DefaultPolarItemRenderer} to draw each
594      * item in the plot. This allows the visual representation of the data to
595      * be changed easily.
596      * <P>
597      * The optional info argument collects information about the rendering of
598      * the plot (dimensions, tooltip information etc). Just pass in
599      * <code>null</code> if you do not need this information.
600      *
601      * @param g2 the graphics device.
602      * @param area the area within which the plot (including axes and
603      * labels) should be drawn.
604      * @param anchor the anchor point (<code>null</code> permitted).
605      * @param parentState ignored.
606      * @param info collects chart drawing information (<code>null</code>
607      * permitted).
608      */

609     public void draw(Graphics2D JavaDoc g2,
610                      Rectangle2D JavaDoc area,
611                      Point2D JavaDoc anchor,
612                      PlotState parentState,
613                      PlotRenderingInfo info) {
614        
615         // if the plot area is too small, just return...
616
boolean b1 = (area.getWidth() <= MINIMUM_WIDTH_TO_DRAW);
617         boolean b2 = (area.getHeight() <= MINIMUM_HEIGHT_TO_DRAW);
618         if (b1 || b2) {
619             return;
620         }
621        
622         // record the plot area...
623
if (info != null) {
624             info.setPlotArea(area);
625         }
626        
627         // adjust the drawing area for the plot insets (if any)...
628
RectangleInsets insets = getInsets();
629         insets.trim(area);
630       
631         Rectangle2D JavaDoc dataArea = area;
632         if (info != null) {
633             info.setDataArea(dataArea);
634         }
635        
636         // draw the plot background and axes...
637
drawBackground(g2, dataArea);
638         double h = Math.min(dataArea.getWidth() / 2.0,
639                 dataArea.getHeight() / 2.0) - MARGIN;
640         Rectangle2D JavaDoc quadrant = new Rectangle2D.Double JavaDoc(dataArea.getCenterX(),
641                 dataArea.getCenterY(), h, h);
642         AxisState state = drawAxis(g2, area, quadrant);
643         if (this.renderer != null) {
644             Shape JavaDoc originalClip = g2.getClip();
645             Composite JavaDoc originalComposite = g2.getComposite();
646           
647             g2.clip(dataArea);
648             g2.setComposite(AlphaComposite.getInstance(
649                     AlphaComposite.SRC_OVER, getForegroundAlpha()));
650           
651             drawGridlines(g2, dataArea, this.angleTicks, state.getTicks());
652           
653             // draw...
654
render(g2, dataArea, info);
655           
656             g2.setClip(originalClip);
657             g2.setComposite(originalComposite);
658         }
659         drawOutline(g2, dataArea);
660         drawCornerTextItems(g2, dataArea);
661     }
662    
663     /**
664      * Draws the corner text items.
665      *
666      * @param g2 the drawing surface.
667      * @param area the area.
668      */

669     protected void drawCornerTextItems(Graphics2D JavaDoc g2, Rectangle2D JavaDoc area) {
670         if (this.cornerTextItems.isEmpty()) {
671             return;
672         }
673        
674         g2.setColor(Color.black);
675         double width = 0.0;
676         double height = 0.0;
677         for (Iterator JavaDoc it = this.cornerTextItems.iterator(); it.hasNext();) {
678             String JavaDoc msg = (String JavaDoc) it.next();
679             FontMetrics JavaDoc fm = g2.getFontMetrics();
680             Rectangle2D JavaDoc bounds = TextUtilities.getTextBounds(msg, g2, fm);
681             width = Math.max(width, bounds.getWidth());
682             height += bounds.getHeight();
683         }
684         
685         double xadj = ANNOTATION_MARGIN * 2.0;
686         double yadj = ANNOTATION_MARGIN;
687         width += xadj;
688         height += yadj;
689        
690         double x = area.getMaxX() - width;
691         double y = area.getMaxY() - height;
692         g2.drawRect((int) x, (int) y, (int) width, (int) height);
693         x += ANNOTATION_MARGIN;
694         for (Iterator JavaDoc it = this.cornerTextItems.iterator(); it.hasNext();) {
695             String JavaDoc msg = (String JavaDoc) it.next();
696             Rectangle2D JavaDoc bounds = TextUtilities.getTextBounds(msg, g2,
697                     g2.getFontMetrics());
698             y += bounds.getHeight();
699             g2.drawString(msg, (int) x, (int) y);
700         }
701     }
702    
703     /**
704      * A utility method for drawing the axes.
705      *
706      * @param g2 the graphics device.
707      * @param plotArea the plot area.
708      * @param dataArea the data area.
709      *
710      * @return A map containing the axis states.
711      */

712     protected AxisState drawAxis(Graphics2D JavaDoc g2, Rectangle2D JavaDoc plotArea,
713                                  Rectangle2D JavaDoc dataArea) {
714         return this.axis.draw(g2, dataArea.getMinY(), plotArea, dataArea,
715                 RectangleEdge.TOP, null);
716     }
717    
718     /**
719      * Draws a representation of the data within the dataArea region, using the
720      * current m_Renderer.
721      *
722      * @param g2 the graphics device.
723      * @param dataArea the region in which the data is to be drawn.
724      * @param info an optional object for collection dimension
725      * information (<code>null</code> permitted).
726      */

727     protected void render(Graphics2D JavaDoc g2,
728                        Rectangle2D JavaDoc dataArea,
729                        PlotRenderingInfo info) {
730       
731         // now get the data and plot it (the visual representation will depend
732
// on the m_Renderer that has been set)...
733
if (!DatasetUtilities.isEmptyOrNull(this.dataset)) {
734             int seriesCount = this.dataset.getSeriesCount();
735             for (int series = 0; series < seriesCount; series++) {
736                 this.renderer.drawSeries(g2, dataArea, info, this,
737                         this.dataset, series);
738             }
739         }
740         else {
741             drawNoDataMessage(g2, dataArea);
742         }
743     }
744    
745     /**
746      * Draws the gridlines for the plot, if they are visible.
747      *
748      * @param g2 the graphics device.
749      * @param dataArea the data area.
750      * @param angularTicks the ticks for the angular axis.
751      * @param radialTicks the ticks for the radial axis.
752      */

753     protected void drawGridlines(Graphics2D JavaDoc g2, Rectangle2D JavaDoc dataArea,
754                                  List JavaDoc angularTicks, List JavaDoc radialTicks) {
755
756         // no renderer, no gridlines...
757
if (this.renderer == null) {
758             return;
759         }
760        
761         // draw the domain grid lines, if any...
762
if (isAngleGridlinesVisible()) {
763             Stroke JavaDoc gridStroke = getAngleGridlineStroke();
764             Paint JavaDoc gridPaint = getAngleGridlinePaint();
765             if ((gridStroke != null) && (gridPaint != null)) {
766                 this.renderer.drawAngularGridLines(g2, this, angularTicks,
767                         dataArea);
768             }
769         }
770        
771         // draw the radius grid lines, if any...
772
if (isRadiusGridlinesVisible()) {
773             Stroke JavaDoc gridStroke = getRadiusGridlineStroke();
774             Paint JavaDoc gridPaint = getRadiusGridlinePaint();
775             if ((gridStroke != null) && (gridPaint != null)) {
776                 this.renderer.drawRadialGridLines(g2, this, this.axis,
777                         radialTicks, dataArea);
778             }
779         }
780     }
781    
782     /**
783      * Zooms the axis ranges by the specified percentage about the anchor point.
784      *
785      * @param percent the amount of the zoom.
786      */

787     public void zoom(double percent) {
788         if (percent > 0.0) {
789             double radius = getMaxRadius();
790             double scaledRadius = radius * percent;
791             this.axis.setUpperBound(scaledRadius);
792             getAxis().setAutoRange(false);
793         }
794         else {
795             getAxis().setAutoRange(true);
796         }
797     }
798    
799     /**
800      * Returns the range for the specified axis.
801      *
802      * @param axis the axis.
803      *
804      * @return The range.
805      */

806     public Range getDataRange(ValueAxis axis) {
807         Range result = null;
808         if (this.dataset != null) {
809             result = Range.combine(result,
810                     DatasetUtilities.findRangeBounds(this.dataset));
811         }
812         return result;
813     }
814    
815     /**
816      * Receives notification of a change to the plot's m_Dataset.
817      * <P>
818      * The axis ranges are updated if necessary.
819      *
820      * @param event information about the event (not used here).
821      */

822     public void datasetChanged(DatasetChangeEvent event) {
823
824         if (this.axis != null) {
825             this.axis.configure();
826         }
827        
828         if (getParent() != null) {
829             getParent().datasetChanged(event);
830         }
831         else {
832             super.datasetChanged(event);
833         }
834     }
835    
836     /**
837      * Notifies all registered listeners of a property change.
838      * <P>
839      * One source of property change events is the plot's m_Renderer.
840      *
841      * @param event information about the property change.
842      */

843     public void rendererChanged(RendererChangeEvent event) {
844         notifyListeners(new PlotChangeEvent(this));
845     }
846    
847     /**
848