KickJava   Java API By Example, From Geeks To Geeks.

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


1 /* ===========================================================
2  * JFreeChart : a free chart library for the Java(tm) platform
3  * ===========================================================
4  *
5  * (C) Copyright 2000-2005, by Object Refinery Limited and Contributors.
6  *
7  * Project Info: http://www.jfree.org/jfreechart/index.html
8  *
9  * This library is free software; you can redistribute it and/or modify it
10  * under the terms of the GNU Lesser General Public License as published by
11  * the Free Software Foundation; either version 2.1 of the License, or
12  * (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful, but
15  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
16  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
17  * License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public License
20  * along with this library; if not, write to the Free Software Foundation,
21  * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
22  *
23  * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
24  * in the United States and other countries.]
25  *
26  * --------------------
27  * ThermometerPlot.java
28  * --------------------
29  *
30  * (C) Copyright 2000-2005, by Bryan Scott and Contributors.
31  *
32  * Original Author: Bryan Scott (based on MeterPlot by Hari).
33  * Contributor(s): David Gilbert (for Object Refinery Limited).
34  * Arnaud Lelievre;
35  *
36  * Changes
37  * -------
38  * 11-Apr-2002 : Version 1, contributed by Bryan Scott;
39  * 15-Apr-2002 : Changed to implement VerticalValuePlot;
40  * 29-Apr-2002 : Added getVerticalValueAxis() method (DG);
41  * 25-Jun-2002 : Removed redundant imports (DG);
42  * 17-Sep-2002 : Reviewed with Checkstyle utility (DG);
43  * 18-Sep-2002 : Extensive changes made to API, to iron out bugs and
44  * inconsistencies (DG);
45  * 13-Oct-2002 : Corrected error datasetChanged which would generate exceptions
46  * when value set to null (BRS).
47  * 23-Jan-2003 : Removed one constructor (DG);
48  * 26-Mar-2003 : Implemented Serializable (DG);
49  * 02-Jun-2003 : Removed test for compatible range axis (DG);
50  * 01-Jul-2003 : Added additional check in draw method to ensure value not
51  * null (BRS);
52  * 08-Sep-2003 : Added internationalization via use of properties
53  * resourceBundle (RFE 690236) (AL);
54  * 16-Sep-2003 : Changed ChartRenderingInfo --> PlotRenderingInfo (DG);
55  * 29-Sep-2003 : Updated draw to set value of cursor to non-zero and allow
56  * painting of axis. An incomplete fix and needs to be set for
57  * left or right drawing (BRS);
58  * 19-Nov-2003 : Added support for value labels to be displayed left of the
59  * thermometer
60  * 19-Nov-2003 : Improved axis drawing (now default axis does not draw axis line
61  * and is closer to the bulb). Added support for the positioning
62  * of the axis to the left or right of the bulb. (BRS);
63  * 03-Dec-2003 : Directly mapped deprecated setData()/getData() method to
64  * get/setDataset() (TM);
65  * 21-Jan-2004 : Update for renamed method in ValueAxis (DG);
66  * 07-Apr-2004 : Changed string width calculation (DG);
67  * 12-Nov-2004 : Implemented the new Zoomable interface (DG);
68  * 06-Jan-2004 : Added getOrientation() method (DG);
69  * 11-Jan-2005 : Removed deprecated code in preparation for 1.0.0 release (DG);
70  * 29-Mar-2005 : Fixed equals() method (DG);
71  * 05-May-2005 : Updated draw() method parameters (DG);
72  *
73  */

74
75 package org.jfree.chart.plot;
76
77 import java.awt.BasicStroke JavaDoc;
78 import java.awt.Color JavaDoc;
79 import java.awt.Font JavaDoc;
80 import java.awt.FontMetrics JavaDoc;
81 import java.awt.Graphics2D JavaDoc;
82 import java.awt.Paint JavaDoc;
83 import java.awt.Stroke JavaDoc;
84 import java.awt.geom.Area JavaDoc;
85 import java.awt.geom.Ellipse2D JavaDoc;
86 import java.awt.geom.Line2D JavaDoc;
87 import java.awt.geom.Point2D JavaDoc;
88 import java.awt.geom.Rectangle2D JavaDoc;
89 import java.awt.geom.RoundRectangle2D JavaDoc;
90 import java.io.IOException JavaDoc;
91 import java.io.ObjectInputStream JavaDoc;
92 import java.io.ObjectOutputStream JavaDoc;
93 import java.io.Serializable JavaDoc;
94 import java.text.DecimalFormat JavaDoc;
95 import java.text.NumberFormat JavaDoc;
96 import java.util.ResourceBundle JavaDoc;
97
98 import org.jfree.chart.LegendItemCollection;
99 import org.jfree.chart.axis.NumberAxis;
100 import org.jfree.chart.axis.ValueAxis;
101 import org.jfree.chart.event.PlotChangeEvent;
102 import org.jfree.data.Range;
103 import org.jfree.data.general.DatasetChangeEvent;
104 import org.jfree.data.general.DefaultValueDataset;
105 import org.jfree.data.general.ValueDataset;
106 import org.jfree.io.SerialUtilities;
107 import org.jfree.ui.RectangleEdge;
108 import org.jfree.ui.RectangleInsets;
109 import org.jfree.util.ObjectUtilities;
110 import org.jfree.util.UnitType;
111
112 /**
113  * A plot that displays a single value (from a {@link ValueDataset}) in a
114  * thermometer type display.
115  * <p>
116  * This plot supports a number of options:
117  * <ol>
118  * <li>three sub-ranges which could be viewed as 'Normal', 'Warning'
119  * and 'Critical' ranges.</li>
120  * <li>the thermometer can be run in two modes:
121  * <ul>
122  * <li>fixed range, or</li>
123  * <li>range adjusts to current sub-range.</li>
124  * </ul>
125  * </li>
126  * <li>settable units to be displayed.</li>
127  * <li>settable display location for the value text.</li>
128  * </ol>
129  *
130  * @author Bryan Scott
131  */

132 public class ThermometerPlot extends Plot implements ValueAxisPlot,
133                                                      Zoomable,
134                                                      Cloneable JavaDoc,
135                                                      Serializable JavaDoc {
136
137     /** For serialization. */
138     private static final long serialVersionUID = 4087093313147984390L;
139     
140     /** A constant for unit type 'None'. */
141     public static final int UNITS_NONE = 0;
142
143     /** A constant for unit type 'Fahrenheit'. */
144     public static final int UNITS_FAHRENHEIT = 1;
145
146     /** A constant for unit type 'Celcius'. */
147     public static final int UNITS_CELCIUS = 2;
148
149     /** A constant for unit type 'Kelvin'. */
150     public static final int UNITS_KELVIN = 3;
151
152     /** A constant for the value label position (no label). */
153     public static final int NONE = 0;
154
155     /** A constant for the value label position (right of the thermometer). */
156     public static final int RIGHT = 1;
157
158     /** A constant for the value label position (left of the thermometer). */
159     public static final int LEFT = 2;
160
161     /** A constant for the value label position (in the thermometer bulb). */
162     public static final int BULB = 3;
163
164     /** A constant for the 'normal' range. */
165     public static final int NORMAL = 0;
166
167     /** A constant for the 'warning' range. */
168     public static final int WARNING = 1;
169
170     /** A constant for the 'critical' range. */
171     public static final int CRITICAL = 2;
172
173     /** The bulb radius. */
174     protected static final int BULB_RADIUS = 40;
175
176     /** The bulb diameter. */
177     protected static final int BULB_DIAMETER = BULB_RADIUS * 2;
178
179     /** The column radius. */
180     protected static final int COLUMN_RADIUS = 20;
181
182     /** The column diameter.*/
183     protected static final int COLUMN_DIAMETER = COLUMN_RADIUS * 2;
184
185     /** The gap radius. */
186     protected static final int GAP_RADIUS = 5;
187
188     /** The gap diameter. */
189     protected static final int GAP_DIAMETER = GAP_RADIUS * 2;
190
191     /** The axis gap. */
192     protected static final int AXIS_GAP = 10;
193
194     /** The unit strings. */
195     protected static final String JavaDoc[] UNITS
196         = {"", "\u00B0F", "\u00B0C", "\u00B0K"};
197
198     /** Index for low value in subrangeInfo matrix. */
199     protected static final int RANGE_LOW = 0;
200
201     /** Index for high value in subrangeInfo matrix. */
202     protected static final int RANGE_HIGH = 1;
203
204     /** Index for display low value in subrangeInfo matrix. */
205     protected static final int DISPLAY_LOW = 2;
206
207     /** Index for display high value in subrangeInfo matrix. */
208     protected static final int DISPLAY_HIGH = 3;
209
210     /** The default lower bound. */
211     protected static final double DEFAULT_LOWER_BOUND = 0.0;
212
213     /** The default upper bound. */
214     protected static final double DEFAULT_UPPER_BOUND = 100.0;
215
216     /** The dataset for the plot. */
217     private ValueDataset dataset;
218
219     /** The range axis. */
220     private ValueAxis rangeAxis;
221
222     /** The lower bound for the thermometer. */
223     private double lowerBound = DEFAULT_LOWER_BOUND;
224
225     /** The upper bound for the thermometer. */
226     private double upperBound = DEFAULT_UPPER_BOUND;
227
228     /**
229      * Blank space inside the plot area around the outside of the thermometer.
230      */

231     private RectangleInsets padding;
232
233     /** Stroke for drawing the thermometer */
234     private transient Stroke JavaDoc thermometerStroke = new BasicStroke JavaDoc(1.0f);
235
236     /** Paint for drawing the thermometer */
237     private transient Paint JavaDoc thermometerPaint = Color.black;
238
239     /** The display units */
240     private int units = UNITS_CELCIUS;
241
242     /** The value label position. */
243     private int valueLocation = BULB;
244
245     /** The position of the axis **/
246     private int axisLocation = LEFT;
247
248     /** The font to write the value in */
249     private Font JavaDoc valueFont = new Font JavaDoc("SansSerif", Font.BOLD, 16);
250
251     /** Colour that the value is written in */
252     private transient Paint JavaDoc valuePaint = Color.white;
253
254     /** Number format for the value */
255     private NumberFormat JavaDoc valueFormat = new DecimalFormat JavaDoc();
256
257     /** The default paint for the mercury in the thermometer. */
258     private transient Paint JavaDoc mercuryPaint = Color.lightGray;
259
260     /** A flag that controls whether value lines are drawn. */
261     private boolean showValueLines = false;
262
263     /** The display sub-range. */
264     private int subrange = -1;
265
266     /** The start and end values for the subranges. */
267     private double[][] subrangeInfo = {
268         {0.0, 50.0, 0.0, 50.0},
269         {50.0, 75.0, 50.0, 75.0},
270         {75.0, 100.0, 75.0, 100.0}
271     };
272
273     /**
274      * A flag that controls whether or not the axis range adjusts to the
275      * sub-ranges.
276      */

277     private boolean followDataInSubranges = false;
278
279     /**
280      * A flag that controls whether or not the mercury paint changes with
281      * the subranges.
282      */

283     private boolean useSubrangePaint = true;
284
285     /** Paint for each range */
286     private Paint JavaDoc[] subrangePaint = {
287         Color.green,
288         Color.orange,
289         Color.red
290     };
291
292     /** A flag that controls whether the sub-range indicators are visible. */
293     private boolean subrangeIndicatorsVisible = true;
294
295     /** The stroke for the sub-range indicators. */
296     private transient Stroke JavaDoc subrangeIndicatorStroke = new BasicStroke JavaDoc(2.0f);
297
298     /** The range indicator stroke. */
299     private transient Stroke JavaDoc rangeIndicatorStroke = new BasicStroke JavaDoc(3.0f);
300
301     /** The resourceBundle for the localization. */
302     protected static ResourceBundle JavaDoc localizationResources =
303         ResourceBundle.getBundle("org.jfree.chart.plot.LocalizationBundle");
304
305     /**
306      * Creates a new thermometer plot.
307      */

308     public ThermometerPlot() {
309         this(new DefaultValueDataset());
310     }
311
312     /**
313      * Creates a new thermometer plot, using default attributes where necessary.
314      *
315      * @param dataset the data set.
316      */

317     public ThermometerPlot(ValueDataset dataset) {
318
319         super();
320
321         this.padding = new RectangleInsets(
322             UnitType.RELATIVE, 0.05, 0.05, 0.05, 0.05
323         );
324         this.dataset = dataset;
325         if (dataset != null) {
326             dataset.addChangeListener(this);
327         }
328         NumberAxis axis = new NumberAxis(null);
329         axis.setStandardTickUnits(NumberAxis.createIntegerTickUnits());
330         axis.setAxisLineVisible(false);
331
332         setRangeAxis(axis);
333         setAxisRange();
334     }
335
336     /**
337      * Returns the primary dataset for the plot.
338      *
339      * @return The primary dataset (possibly <code>null</code>).
340      */

341     public ValueDataset getDataset() {
342         return this.dataset;
343     }
344
345     /**
346      * Sets the dataset for the plot, replacing the existing dataset if there
347      * is one.
348      *
349      * @param dataset the dataset (<code>null</code> permitted).
350      */

351     public void setDataset(ValueDataset dataset) {
352
353         // if there is an existing dataset, remove the plot from the list
354
// of change listeners...
355
ValueDataset existing = this.dataset;
356         if (existing != null) {
357             existing.removeChangeListener(this);
358         }
359
360         // set the new dataset, and register the chart as a change listener...
361
this.dataset = dataset;
362         if (dataset != null) {
363             setDatasetGroup(dataset.getGroup());
364             dataset.addChangeListener(this);
365         }
366
367         // send a dataset change event to self...
368
DatasetChangeEvent event = new DatasetChangeEvent(this, dataset);
369         datasetChanged(event);
370
371     }
372
373     /**
374      * Returns the range axis.
375      *
376      * @return The range axis.
377      */

378     public ValueAxis getRangeAxis() {
379         return this.rangeAxis;
380     }
381
382     /**
383      * Sets the range axis for the plot.
384      *
385      * @param axis the new axis.
386      */

387     public void setRangeAxis(ValueAxis axis) {
388
389         if (axis != null) {
390             axis.setPlot(this);
391             axis.addChangeListener(this);
392         }
393
394         // plot is likely registered as a listener with the existing axis...
395
if (this.rangeAxis != null) {
396             this.rangeAxis.removeChangeListener(this);
397         }
398
399         this.rangeAxis = axis;
400
401     }
402
403     /**
404      * Returns the lower bound for the thermometer. The data value can be set
405      * lower than this, but it will not be shown in the thermometer.
406      *
407      * @return The lower bound.
408      *
409      */

410     public double getLowerBound() {
411         return this.lowerBound;
412     }
413
414     /**
415      * Sets the lower bound for the thermometer.
416      *
417      * @param lower the lower bound.
418      */

419     public void setLowerBound(double lower) {
420         this.lowerBound = lower;
421         setAxisRange();
422     }
423
424     /**
425      * Returns the upper bound for the thermometer. The data value can be set
426      * higher than this, but it will not be shown in the thermometer.
427      *
428      * @return The upper bound.
429      */

430     public double getUpperBound() {
431         return this.upperBound;
432     }
433
434     /**
435      * Sets the upper bound for the thermometer.
436      *
437      * @param upper the upper bound.
438      */

439     public void setUpperBound(double upper) {
440         this.upperBound = upper;
441         setAxisRange();
442     }
443
444     /**
445      * Sets the lower and upper bounds for the thermometer.
446      *
447      * @param lower the lower bound.
448      * @param upper the upper bound.
449      */

450     public void setRange(double lower, double upper) {
451         this.lowerBound = lower;
452         this.upperBound = upper;
453         setAxisRange();
454     }
455
456     /**
457      * Returns the padding for the thermometer. This is the space inside the
458      * plot area.
459      *
460      * @return The padding.
461      */

462     public RectangleInsets getPadding() {
463         return this.padding;
464     }
465
466     /**
467      * Sets the padding for the thermometer.
468      *
469      * @param padding the padding.
470      */

471     public void setPadding(RectangleInsets padding) {
472         this.padding = padding;
473         notifyListeners(new PlotChangeEvent(this));
474     }
475
476     /**
477      * Returns the stroke used to draw the thermometer outline.
478      *
479      * @return The stroke.
480      */

481     public Stroke JavaDoc getThermometerStroke() {
482         return this.thermometerStroke;
483     }
484
485     /**
486      * Sets the stroke used to draw the thermometer outline.
487      *
488      * @param s the new stroke (null ignored).
489      */

490     public void setThermometerStroke(Stroke JavaDoc s) {
491         if (s != null) {
492             this.thermometerStroke = s;
493             notifyListeners(new PlotChangeEvent(this));
494         }
495     }
496
497     /**
498      * Returns the paint used to draw the thermometer outline.
499      *
500      * @return The paint.
501      */

502     public Paint JavaDoc getThermometerPaint() {
503         return this.thermometerPaint;
504     }
505
506     /**
507      * Sets the paint used to draw the thermometer outline.
508      *
509      * @param paint the new paint (null ignored).
510      */

511     public void setThermometerPaint(Paint JavaDoc paint) {
512         if (paint != null) {
513             this.thermometerPaint = paint;
514             notifyListeners(new PlotChangeEvent(this));
515         }
516     }
517
518     /**
519      * Returns the unit display type (none/Fahrenheit/Celcius/Kelvin).
520      *
521      * @return The units type.
522      */

523     public int getUnits() {
524         return this.units;
525     }
526
527     /**
528      * Sets the units to be displayed in the thermometer.
529      * <p>
530      * Use one of the following constants:
531      *
532      * <ul>
533      * <li>UNITS_NONE : no units displayed.</li>
534      * <li>UNITS_FAHRENHEIT : units displayed in Fahrenheit.</li>
535      * <li>UNITS_CELCIUS : units displayed in Celcius.</li>
536      * <li>UNITS_KELVIN : units displayed in Kelvin.</li>
537      * </ul>
538      *
539      * @param u the new unit type.
540      */

541     public void setUnits(int u) {
542         if ((u >= 0) && (u < UNITS.length)) {
543             if (this.units != u) {
544                 this.units = u;
545                 notifyListeners(new PlotChangeEvent(this));
546             }
547         }
548     }
549
550     /**
551      * Sets the unit type.
552      *
553      * @param u the unit type (null ignored).
554      */

555     public void setUnits(String JavaDoc u) {
556         if (u == null) {
557             return;
558         }
559
560         u = u.toUpperCase().trim();
561         for (int i = 0; i < UNITS.length; ++i) {
562             if (u.equals(UNITS[i].toUpperCase().trim())) {
563                 setUnits(i);
564                 i = UNITS.length;
565             }
566         }
567     }
568
569     /**
570      * Returns the value location.
571      *
572      * @return The location.
573      */

574     public int getValueLocation() {
575         return this.valueLocation;
576     }
577
578     /**
579      * Sets the location at which the current value is displayed.
580      * <P>
581      * The location can be one of the constants:
582      * <code>NONE</code>,
583      * <code>RIGHT</code>
584      * <code>LEFT</code> and
585      * <code>BULB</code>.
586      *
587      * @param location the location.
588      */

589     public void setValueLocation(int location) {
590         if ((location >= 0) && (location < 4)) {
591             this.valueLocation = location;
592             notifyListeners(new PlotChangeEvent(this));
593         }
594         else {
595             throw new IllegalArgumentException JavaDoc("Location not recognised.");
596         }
597     }
598
599     /**
600      * Sets the location at which the axis is displayed with reference to the
601      * bulb.
602      * <P>
603      * The location can be one of the constants:
604      * <code>NONE</code>,
605      * <code>RIGHT</code> and
606      * <code>LEFT</code>.
607      *
608      * @param location the location.
609      */

610     public void setAxisLocation(int location) {
611         if ((location >= 0) && (location < 3)) {
612             this.axisLocation = location;
613             notifyListeners(new PlotChangeEvent(this));
614         }
615         else {
616             throw new IllegalArgumentException JavaDoc("Location not recognised.");
617         }
618     }
619
620     /**
621      * Returns the axis location.
622      *
623      * @return The location.
624      */

625     public int getAxisLocation() {
626         return this.axisLocation;
627     }
628
629     /**
630      * Gets the font used to display the current value.
631      *
632      * @return The font.
633      */

634     public Font JavaDoc getValueFont() {
635         return this.valueFont;
636     }
637
638     /**
639      * Sets the font used to display the current value.
640      *
641      * @param f the new font.
642      */

643     public void setValueFont(Font JavaDoc f) {
644         if ((f != null) && (!this.valueFont.equals(f))) {
645             this.valueFont = f;
646             notifyListeners(new PlotChangeEvent(this));
647         }
648     }
649
650     /**
651      * Gets the paint used to display the current value.
652     *
653      * @return The paint.
654      */

655     public Paint JavaDoc getValuePaint() {
656         return this.valuePaint;
657     }
658
659     /**
660      * Sets the paint used to display the current value.
661      *
662      * @param p the new paint.
663      */

664     public void setValuePaint(Paint JavaDoc p) {
665         if ((p != null) && (!this.valuePaint.equals(p))) {
666             this.valuePaint = p;
667             notifyListeners(new PlotChangeEvent(this));
668         }
669     }
670
671     /**
672      * Sets the formatter for the value label.
673      *
674      * @param formatter the new formatter.
675      */

676     public void setValueFormat(NumberFormat JavaDoc formatter) {
677         if (formatter != null) {
678             this.valueFormat = formatter;
679             notifyListeners(new PlotChangeEvent(this));
680         }
681     }
682
683     /**
684      * Returns the default mercury paint.
685      *
686      * @return The paint.
687      */

688     public Paint JavaDoc getMercuryPaint() {
689         return this.mercuryPaint;
690     }
691
692     /**
693      * Sets the default mercury paint.
694      *
695      * @param paint the new paint.
696      */

697     public void setMercuryPaint(Paint JavaDoc paint) {
698         this.mercuryPaint = paint;
699         notifyListeners(new PlotChangeEvent(this));
700     }
701
702     /**
703      * Returns the flag that controls whether not value lines are displayed.
704      *
705      * @return The flag.
706      */

707     public boolean getShowValueLines() {
708         return this.showValueLines;
709     }
710
711     /**
712      * Sets the display as to whether to show value lines in the output.
713      *
714      * @param b Whether to show value lines in the thermometer
715      */

716     public void setShowValueLines(boolean b) {
717         this.showValueLines = b;
718         notifyListeners(new PlotChangeEvent(this));
719     }
720
721     /**
722      * Sets information for a particular range.
723      *
724      * @param range the range to specify information about.
725      * @param low the low value for the range
726      * @param hi the high value for the range
727      */

728     public void setSubrangeInfo(int range, double low, double hi) {
729         setSubrangeInfo(range, low, hi, low, hi);
730     }
731
732     /**
733      * Sets the subrangeInfo attribute of the ThermometerPlot object
734      *
735      * @param range the new rangeInfo value.
736      * @param rangeLow the new rangeInfo value
737      * @param rangeHigh the new rangeInfo value
738      * @param displayLow the new rangeInfo value
739      * @param displayHigh the new rangeInfo value
740      */

741     public void setSubrangeInfo(int range,
742                                 double rangeLow, double rangeHigh,
743                                 double displayLow, double displayHigh) {
744
745         if ((range >= 0) && (range < 3)) {
746             setSubrange(range, rangeLow, rangeHigh);
747             setDisplayRange(range, displayLow, displayHigh);
748             setAxisRange();
749             notifyListeners(new PlotChangeEvent(this));
750         }
751
752     }
753
754     /**
755      * Sets the range.
756      *
757      * @param range the range type.
758      * @param low the low value.
759      * @param high the high value.
760      */

761     public void setSubrange(int range, double low, double high) {
762         if ((range >= 0) && (range < 3)) {
763             this.subrangeInfo[range][RANGE_HIGH] = high;
764             this.subrangeInfo[range][RANGE_LOW] = low;
765         }
766     }
767
768     /**
769      * Sets the display range.
770      *
771      * @param range the range type.
772      * @param low the low value.
773      * @param high the high value.
774      */

775     public void setDisplayRange(int range, double low, double high) {
776
777         if ((range >= 0) && (range < this.subrangeInfo.length)
778             && isValidNumber(high) && isValidNumber(low)) {
779  
780             if (high > low) {
781                 this.subrangeInfo[range][DISPLAY_HIGH] = high;
782                 this.subrangeInfo[range][DISPLAY_LOW] = low;
783             }
784             else {
785                 this.subrangeInfo[range][DISPLAY_HIGH] = high;
786                 this.subrangeInfo[range][DISPLAY_LOW] = low;
787             }
788
789         }
790
791     }
792
793     /**
794      * Gets the paint used for a particular subrange.
795      *
796      * @param range the range.
797      *
798      * @return The paint.
799      */

800     public Paint JavaDoc getSubrangePaint(int range) {
801         if ((range >= 0) && (range < this.subrangePaint.length)) {
802             return this.subrangePaint[range];
803         }
804         else {
805             return this.mercuryPaint;
806         }
807     }
808
809     /**
810      * Sets the paint to be used for a range.
811      *
812      * @param range the range.
813      * @param paint the paint to be applied.
814      */

815     public void setSubrangePaint(int range, Paint JavaDoc paint) {
816         if ((range >= 0)
817                 && (range < this.subrangePaint.length) && (paint != null)) {
818             this.subrangePaint[range] = paint;
819             notifyListeners(new PlotChangeEvent(this));
820         }
821     }
822
823     /**
824      * Returns a flag that controls whether or not the thermometer axis zooms
825      * to display the subrange within which the data value falls.
826      *
827      * @return The flag.
828      */

829     public boolean getFollowDataInSubranges() {
830         return this.followDataInSubranges;
831     }
832
833     /**
834      * Sets the flag that controls whether or not the thermometer axis zooms
835      * to display the subrange within which the data value falls.
836      *
837      * @param flag the flag.
838      */

839     public void setFollowDataInSubranges(boolean flag) {
840         this.followDataInSubranges = flag;
841         notifyListeners(new PlotChangeEvent(this));
842     }
843
844     /**
845      * Returns a flag that controls whether or not the mercury color changes
846      * for each subrange.
847      *
848      * @return The flag.
849      */

850     public boolean getUseSubrangePaint() {
851         return this.useSubrangePaint;
852     }
853
854     /**
855      * Sets the range colour change option.
856      *
857      * @param flag The new range colour change option
858      */

859     public void setUseSubrangePaint(boolean flag) {
860         this.useSubrangePaint = flag;
861         notifyListeners(new PlotChangeEvent(this));
862     }
863
864     /**
865      * Draws the plot on a Java 2D graphics device (such as the screen or a
866      * printer).
867      *
868      * @param g2 the graphics device.
869      * @param area the area within which the plot should be drawn.
870      * @param anchor the anchor point (<code>null</code> permitted).
871      * @param parentState the state from the parent plot, if there is one.
872      * @param info collects info about the drawing.
873      */

874     public void draw(Graphics2D JavaDoc g2, Rectangle2D JavaDoc area, Point2D JavaDoc anchor,
875                      PlotState parentState,
876                      PlotRenderingInfo info) {
877
878         RoundRectangle2D JavaDoc outerStem = new RoundRectangle2D.Double JavaDoc();
879         RoundRectangle2D JavaDoc innerStem = new RoundRectangle2D.Double JavaDoc();
880         RoundRectangle2D JavaDoc mercuryStem = new RoundRectangle2D.Double JavaDoc();
881         Ellipse2D JavaDoc outerBulb = new Ellipse2D.Double JavaDoc();
882         Ellipse2D JavaDoc innerBulb = new Ellipse2D.Double JavaDoc();
883         String JavaDoc temp = null;
884         FontMetrics JavaDoc metrics = null;
885         if (info != null) {
886             info.setPlotArea(area);
887         }
888
889         // adjust for insets...
890
RectangleInsets insets = getInsets();
891         insets.trim(area);
892         drawBackground(g2, area);
893
894         // adjust for padding...
895
//this.padding.trim(plotArea);
896
int midX = (int) (area.getX() + (area.getWidth() / 2));
897         int midY = (int) (area.getY() + (area.getHeight() / 2));
898         int stemTop = (int) (area.getMinY() + BULB_RADIUS);
899         int stemBottom = (int) (area.getMaxY() - BULB_DIAMETER);
900         Rectangle2D JavaDoc dataArea = new Rectangle2D.Double JavaDoc(
901             midX - COLUMN_RADIUS, stemTop, COLUMN_RADIUS, stemBottom - stemTop
902         );
903
904         outerBulb.setFrame(
905             midX - BULB_RADIUS, stemBottom, BULB_DIAMETER, BULB_DIAMETER
906         );
907
908         outerStem.setRoundRect(
909             midX - COLUMN_RADIUS, area.getMinY(), COLUMN_DIAMETER,
910             stemBottom + BULB_DIAMETER - stemTop,
911             COLUMN_DIAMETER, COLUMN_DIAMETER
912         );
913
914         Area JavaDoc outerThermometer = new Area JavaDoc(outerBulb);
915         Area JavaDoc tempArea = new Area JavaDoc(outerStem);
916         outerThermometer.add(tempArea);
917
918         innerBulb.setFrame(
919             midX - BULB_RADIUS + GAP_RADIUS, stemBottom + GAP_RADIUS,
920             BULB_DIAMETER - GAP_DIAMETER, BULB_DIAMETER - GAP_DIAMETER
921         );
922
923         innerStem.setRoundRect(
924             midX - COLUMN_RADIUS + GAP_RADIUS, area.getMinY() + GAP_RADIUS,
925             COLUMN_DIAMETER - GAP_DIAMETER,
926             stemBottom + BULB_DIAMETER - GAP_DIAMETER - stemTop,
927             COLUMN_DIAMETER - GAP_DIAMETER, COLUMN_DIAMETER - GAP_DIAMETER
928         );
929
930         Area JavaDoc innerThermometer = new Area JavaDoc(innerBulb);
931         tempArea = new Area JavaDoc(innerStem);
932         innerThermometer.add(tempArea);
933    
934         if ((this.dataset != null) && (this.dataset.getValue() != null)) {
935             double current = this.dataset.getValue().doubleValue();
936             double ds = this.rangeAxis.va