KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jfree > experimental > chart > plot > dial > DialPlot


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  * DialPlot.java
29  * -------------
30  * (C) Copyright 2006, by Object Refinery Limited.
31  *
32  * Original Author: David Gilbert (for Object Refinery Limited);
33  * Contributor(s): -;
34  *
35  * $Id: DialPlot.java,v 1.1.2.4 2006/11/17 11:06:44 mungady Exp $
36  *
37  * Changes
38  * -------
39  * 03-Nov-2006 : Version 1 (DG);
40  *
41  */

42
43 package org.jfree.experimental.chart.plot.dial;
44
45 import java.awt.Graphics2D JavaDoc;
46 import java.awt.Shape JavaDoc;
47 import java.awt.geom.Point2D JavaDoc;
48 import java.awt.geom.Rectangle2D JavaDoc;
49 import java.io.IOException JavaDoc;
50 import java.io.ObjectInputStream JavaDoc;
51 import java.io.ObjectOutputStream JavaDoc;
52 import java.util.Iterator JavaDoc;
53 import java.util.List JavaDoc;
54
55 import org.jfree.chart.JFreeChart;
56 import org.jfree.chart.event.PlotChangeEvent;
57 import org.jfree.chart.plot.Plot;
58 import org.jfree.chart.plot.PlotRenderingInfo;
59 import org.jfree.chart.plot.PlotState;
60 import org.jfree.data.general.DatasetChangeEvent;
61 import org.jfree.data.general.ValueDataset;
62 import org.jfree.util.ObjectList;
63 import org.jfree.util.ObjectUtilities;
64
65 /**
66  * A dial plot.
67  */

68 public class DialPlot extends Plot implements DialLayerChangeListener {
69
70     /**
71      * The background layer (optional).
72      */

73     private DialLayer background;
74     
75     /**
76      * The needle cap (optional).
77      */

78     private DialLayer cap;
79     
80     /**
81      * The dial frame.
82      */

83     private DialFrame dialFrame;
84     
85     /**
86      * The dataset(s) for the dial plot.
87      */

88     private ObjectList datasets;
89     
90     /**
91      * The scale(s) for the dial plot.
92      */

93     private ObjectList scales;
94     
95     /** Storage for keys that map datasets to scales. */
96     private ObjectList datasetToScaleMap;
97
98     /**
99      * The drawing layers for the dial plot.
100      */

101     private List JavaDoc layers;
102     
103     /**
104      * The x-coordinate for the view window.
105      */

106     private double viewX;
107     
108     /**
109      * The y-coordinate for the view window.
110      */

111     private double viewY;
112     
113     /**
114      * The width of the view window, expressed as a percentage.
115      */

116     private double viewW;
117     
118     /**
119      * The height of the view window, expressed as a percentage.
120      */

121     private double viewH;
122     
123     /**
124      * Creates a new instance of <code>DialPlot</code>.
125      */

126     public DialPlot() {
127         this.background = null;
128         this.cap = null;
129         this.dialFrame = new StandardDialFrame();
130         this.datasets = new ObjectList();
131         this.scales = new ObjectList();
132         this.datasetToScaleMap = new ObjectList();
133         this.layers = new java.util.ArrayList JavaDoc();
134         this.viewX = 0.0;
135         this.viewY = 0.0;
136         this.viewW = 1.0;
137         this.viewH = 1.0;
138     }
139
140     /**
141      * Returns the background.
142      *
143      * @return The background (possibly <code>null</code>).
144      *
145      * @see #setBackground(DialLayer)
146      */

147     public DialLayer getBackground() {
148         return this.background;
149     }
150     
151     /**
152      * Sets the background layer.
153      *
154      * @param background the background layer (<code>null</code> permitted).
155      *
156      * @see #getBackground()
157      */

158     public void setBackground(DialLayer background) {
159         this.background = background;
160         notifyListeners(new PlotChangeEvent(this));
161     }
162     
163     /**
164      * Returns the cap.
165      *
166      * @return The cap (possibly <code>null</code>).
167      *
168      * @see #setCap(DialLayer)
169      */

170     public DialLayer getCap() {
171         return this.cap;
172     }
173     
174     /**
175      * Sets the cap.
176      *
177      * @param cap the cap (<code>null</code> permitted).
178      *
179      * @see #getCap()
180      */

181     public void setCap(DialLayer cap) {
182         this.cap = cap;
183         notifyListeners(new PlotChangeEvent(this));
184     }
185
186     /**
187      * Returns the dial's frame.
188      *
189      * @return The dial's frame (never <code>null</code>).
190      *
191      * @see #setDialFrame(DialFrame)
192      */

193     public DialFrame getDialFrame() {
194         return this.dialFrame;
195     }
196     
197     /**
198      * Sets the dial's frame.
199      *
200      * @param frame the frame (<code>null</code> not permitted).
201      *
202      * @see #getDialFrame()
203      */

204     public void setDialFrame(DialFrame frame) {
205         if (frame == null) {
206             throw new IllegalArgumentException JavaDoc("Null 'frame' argument.");
207         }
208         this.dialFrame = frame;
209         notifyListeners(new PlotChangeEvent(this));
210     }
211
212     /**
213      * Returns the x-coordinate of the viewing rectangle. This is specified
214      * in the range 0.0 to 1.0, relative to the dial's framing rectangle.
215      *
216      * @return The x-coordinate of the viewing rectangle.
217      *
218      * @see #setView(double, double, double, double)
219      */

220     public double getViewX() {
221         return this.viewX;
222     }
223     
224     /**
225      * Returns the y-coordinate of the viewing rectangle. This is specified
226      * in the range 0.0 to 1.0, relative to the dial's framing rectangle.
227      *
228      * @return The y-coordinate of the viewing rectangle.
229      *
230      * @see #setView(double, double, double, double)
231      */

232     public double getViewY() {
233         return this.viewY;
234     }
235     
236     /**
237      * Returns the width of the viewing rectangle. This is specified
238      * in the range 0.0 to 1.0, relative to the dial's framing rectangle.
239      *
240      * @return The width of the viewing rectangle.
241      *
242      * @see #setView(double, double, double, double)
243      */

244     public double getViewWidth() {
245         return this.viewW;
246     }
247     
248     /**
249      * Returns the height of the viewing rectangle. This is specified
250      * in the range 0.0 to 1.0, relative to the dial's framing rectangle.
251      *
252      * @return The height of the viewing rectangle.
253      *
254      * @see #setView(double, double, double, double)
255      */

256     public double getViewHeight() {
257         return this.viewH;
258     }
259     
260     /**
261      * Sets the viewing rectangle, relative to the dial's framing rectangle.
262      *
263      * @param x the x-coordinate (in the range 0.0 to 1.0).
264      * @param y the y-coordinate (in the range 0.0 to 1.0).
265      * @param w the width (in the range 0.0 to 1.0).
266      * @param h the height (in the range 0.0 to 1.0).
267      *
268      * @see #getViewX()
269      * @see #getViewY()
270      * @see #getViewWidth()
271      * @see #getViewHeight()
272      */

273     public void setView(double x, double y, double w, double h) {
274         this.viewX = x;
275         this.viewY = y;
276         this.viewW = w;
277         this.viewH = h;
278         notifyListeners(new PlotChangeEvent(this));
279     }
280
281     /**
282      * Adds a layer to the plot.
283      *
284      * @param layer the layer (<code>null</code> not permitted).
285      */

286     public void addLayer(DialLayer layer) {
287         if (layer == null) {
288             throw new IllegalArgumentException JavaDoc("Null 'layer' argument.");
289         }
290         this.layers.add(layer);
291         notifyListeners(new PlotChangeEvent(this));
292     }
293     
294     /**
295      * Returns the primary dataset for the plot.
296      *
297      * @return The primary dataset (possibly <code>null</code>).
298      */

299     public ValueDataset getDataset() {
300         return getDataset(0);
301     }
302
303     /**
304      * Returns the dataset at the given index.
305      *
306      * @param index the dataset index.
307      *
308      * @return The dataset (possibly <code>null</code>).
309      */

310     public ValueDataset getDataset(int index) {
311         ValueDataset result = null;
312         if (this.datasets.size() > index) {
313             result = (ValueDataset) this.datasets.get(index);
314         }
315         return result;
316     }
317
318     /**
319      * Sets the dataset for the plot, replacing the existing dataset, if there
320      * is one, and sends a {@link PlotChangeEvent} to all registered
321      * listeners.
322      *
323      * @param dataset the dataset (<code>null</code> permitted).
324      */

325     public void setDataset(ValueDataset dataset) {
326         setDataset(0, dataset);
327     }
328
329     /**
330      * Sets a dataset for the plot.
331      *
332      * @param index the dataset index.
333      * @param dataset the dataset (<code>null</code> permitted).
334      */

335     public void setDataset(int index, ValueDataset dataset) {
336         
337         ValueDataset existing = (ValueDataset) this.datasets.get(index);
338         if (existing != null) {
339             existing.removeChangeListener(this);
340         }
341         this.datasets.set(index, dataset);
342         if (dataset != null) {
343             dataset.addChangeListener(this);
344         }
345         
346         // send a dataset change event to self...
347
DatasetChangeEvent event = new DatasetChangeEvent(this, dataset);
348         datasetChanged(event);
349         
350     }
351
352     /**
353      * Returns the number of datasets.
354      *
355      * @return The number of datasets.
356      */

357     public int getDatasetCount() {
358         return this.datasets.size();
359     }
360     
361     /**
362      * Draws the plot. This method is usually called by the {@link JFreeChart}
363      * instance that manages the plot.
364      *
365      * @param g2 the graphics target.
366      * @param area the area in which the plot should be drawn.
367      * @param anchor the anchor point (typically the last point that the
368      * mouse clicked on, <code>null</code> is permitted).
369      * @param parentState the state for the parent plot (if any).
370      * @param info used to collect plot rendering info (<code>null</code>
371      * permitted).
372      */

373     public void draw(Graphics2D JavaDoc g2, Rectangle2D JavaDoc area, Point2D JavaDoc anchor,
374             PlotState parentState, PlotRenderingInfo info) {
375         
376         // first, expand the viewing area into a drawing frame
377
Rectangle2D JavaDoc frame = viewToFrame(area);
378         
379         // draw the background if there is one...
380
if (this.background != null && this.background.isVisible()) {
381             if (this.background.isClippedToWindow()) {
382                 Shape JavaDoc savedClip = g2.getClip();
383                 g2.setClip(this.dialFrame.getWindow(frame));
384                 this.background.draw(g2, this, frame, area);
385                 g2.setClip(savedClip);
386             }
387             else {
388                 this.background.draw(g2, this, frame, area);
389             }
390         }
391         
392         Iterator JavaDoc iterator = this.layers.iterator();
393         while (iterator.hasNext()) {
394             DialLayer current = (DialLayer) iterator.next();
395             if (current.isVisible()) {
396                 if (current.isClippedToWindow()) {
397                     Shape JavaDoc savedClip = g2.getClip();
398                     g2.setClip(this.dialFrame.getWindow(frame));
399                     current.draw(g2, this, frame, area);
400                     g2.setClip(savedClip);
401                 }
402                 else {
403                     current.draw(g2, this, frame, area);
404                 }
405             }
406         }
407
408         // draw the cap if there is one...
409
if (this.cap != null && this.cap.isVisible()) {
410             if (this.cap.isClippedToWindow()) {
411                 Shape JavaDoc savedClip = g2.getClip();
412                 g2.setClip(this.dialFrame.getWindow(frame));
413                 this.cap.draw(g2, this, frame, area);
414                 g2.setClip(savedClip);
415             }
416             else {
417                 this.cap.draw(g2, this, frame, area);
418             }
419         }
420         
421         if (this.dialFrame.isVisible()) {
422             this.dialFrame.draw(g2, this, frame, area);
423         }
424         
425     }
426     
427     /**
428      * Returns the frame surrounding the specified view rectangle.
429      *
430      * @param view the view rectangle (<code>null</code> not permitted).
431      */

432     private Rectangle2D JavaDoc viewToFrame(Rectangle2D JavaDoc view) {
433         double width = view.getWidth() / this.viewW;
434         double height = view.getHeight() / this.viewH;
435         double x = view.getX() - (width * this.viewX);
436         double y = view.getY() - (height * this.viewY);
437         return new Rectangle2D.Double JavaDoc(x, y, width, height);
438     }
439     
440     /**
441      * Returns the value from the specified dataset.
442      *
443      * @param datasetIndex the dataset index.
444      *
445      * @return The data value.
446      */

447     public double getValue(int datasetIndex) {
448         double result = Double.NaN;
449         ValueDataset dataset = getDataset(datasetIndex);
450         if (dataset != null) {
451             Number JavaDoc n = dataset.getValue();
452             if (n != null) {
453                 result = n.doubleValue();
454             }
455         }
456         return result;
457     }
458     
459     /**
460      * Adds a dial scale to the plot.
461      *
462      * @param index the scale index.
463      * @param scale the scale.
464      */

465     public void addScale(int index, DialScale scale) {
466         this.layers.add(scale);
467         this.scales.set(index, scale);
468     }
469     
470     /**
471      * Returns the scale at the given index.
472      *
473      * @param index the scale index.
474      *
475      * @return The scale (possibly <code>null</code>).
476      */

477     public DialScale getScale(int index) {
478         DialScale result = null;
479         if (this.scales.size() > index) {
480             result = (DialScale) this.scales.get(index);
481         }
482         return result;
483     }
484
485     /**
486      * Maps a dataset to a particular scale.
487      *
488      * @param index the dataset index (zero-based).
489      * @param scaleIndex the scale index (zero-based).
490      */

491     public void mapDatasetToScale(int index, int scaleIndex) {
492         this.datasetToScaleMap.set(index, new Integer JavaDoc(scaleIndex));
493         notifyListeners(new PlotChangeEvent(this));
494     }
495     
496     /**
497      * Returns the dial scale for a specific dataset.
498      *
499      * @param datasetIndex the dataset index.
500      *
501      * @return The dial scale.
502      */

503     public DialScale getScaleForDataset(int datasetIndex) {
504         DialScale result = (DialScale) this.scales.get(0);
505         Integer JavaDoc scaleIndex = (Integer JavaDoc) this.datasetToScaleMap.get(datasetIndex);
506         if (scaleIndex != null) {
507             result = getScale(scaleIndex.intValue());
508         }
509         return result;
510     }
511     
512     /**
513      * A utility method that computes a rectangle using relative radius values.
514      *
515      * @param rect the reference rectangle.
516      * @param radiusW the width radius (must be > 0.0)
517      * @param radiusH the height radius.
518      *
519      * @return A new rectangle.
520      */

521     public static Rectangle2D JavaDoc rectangleByRadius(Rectangle2D JavaDoc rect,
522             double radiusW, double radiusH) {
523         double x = rect.getCenterX();
524         double y = rect.getCenterY();
525         double w = rect.getWidth() * radiusW;
526         double h = rect.getHeight() * radiusH;
527         return new Rectangle2D.Double JavaDoc(x - w / 2.0, y - h / 2.0, w, h);
528     }
529     
530     /**
531      * Receives notification when a layer has changed.
532      *
533      * @param event the event.
534      */

535     public void dialLayerChanged(DialLayerChangeEvent event) {
536         this.notifyListeners(new PlotChangeEvent(this));
537     }
538
539     /**
540      * Tests this <code>DialPlot</code> instance for equality with an
541      * arbitrary object. The plot's dataset(s) is (are) not included in
542      * the test.
543      *
544      * @param obj the object (<code>null</code> permitted).
545      *
546      * @return A boolean.
547      */

548     public boolean equals(Object JavaDoc obj) {
549         if (obj == this) {
550             return true;
551         }
552         if (!(obj instanceof DialPlot)) {
553             return false;
554         }
555         DialPlot that = (DialPlot) obj;
556         if (!ObjectUtilities.equal(this.background, that.background)) {
557             return false;
558         }
559         if (!ObjectUtilities.equal(this.cap, that.cap)) {
560             return false;
561         }
562         if (!this.dialFrame.equals(that.dialFrame)) {
563             return false;
564         }
565         if (this.viewX != that.viewX) {
566             return false;
567         }
568         if (this.viewY != that.viewY) {
569             return false;
570         }
571         if (this.viewW != that.viewW) {
572             return false;
573         }
574         if (this.viewH != that.viewH) {
575             return false;
576         }
577         if (!this.layers.equals(that.layers)) {
578             return false;
579         }
580         return super.equals(obj);
581     }
582
583     /**
584      * Returns a hash code for this instance.
585      *
586      * @return The hash code.
587      */

588     public int hashCode() {
589         int result = 193;
590         result = 37 * result + ObjectUtilities.hashCode(this.background);
591         result = 37 * result + ObjectUtilities.hashCode(this.cap);
592         result = 37 * result + this.dialFrame.hashCode();
593         long temp = Double.doubleToLongBits(this.viewX);
594         result = (int) (temp ^ (temp >>> 32));
595         temp = Double.doubleToLongBits(this.viewY);
596         result = (int) (temp ^ (temp >>> 32));
597         temp = Double.doubleToLongBits(this.viewW);
598         result = (int) (temp ^ (temp >>> 32));
599         temp = Double.doubleToLongBits(this.viewH);
600         result = (int) (temp ^ (temp >>> 32));
601         return result;
602     }
603     
604     /**
605      * Returns the plot type.
606      *
607      * @return <code>"DialPlot"</code>
608      */

609     public String JavaDoc getPlotType() {
610         return "DialPlot";
611     }
612     
613     /**
614      * Provides serialization support.
615      *
616      * @param stream the output stream.
617      *
618      * @throws IOException if there is an I/O error.
619      */

620     private void writeObject(ObjectOutputStream JavaDoc stream) throws IOException JavaDoc {
621         stream.defaultWriteObject();
622     }
623
624     /**
625      * Provides serialization support.
626      *
627      * @param stream the input stream.
628      *
629      * @throws IOException if there is an I/O error.
630      * @throws ClassNotFoundException if there is a classpath problem.
631      */

632     private void readObject(ObjectInputStream JavaDoc stream)
633             throws IOException JavaDoc, ClassNotFoundException JavaDoc {
634         stream.defaultReadObject();
635     }
636
637     
638 }
639
Popular Tags