| 1 74 75 package org.jfree.chart.plot; 76 77 import java.awt.BasicStroke ; 78 import java.awt.Color ; 79 import java.awt.Font ; 80 import java.awt.FontMetrics ; 81 import java.awt.Graphics2D ; 82 import java.awt.Paint ; 83 import java.awt.Stroke ; 84 import java.awt.geom.Area ; 85 import java.awt.geom.Ellipse2D ; 86 import java.awt.geom.Line2D ; 87 import java.awt.geom.Point2D ; 88 import java.awt.geom.Rectangle2D ; 89 import java.awt.geom.RoundRectangle2D ; 90 import java.io.IOException ; 91 import java.io.ObjectInputStream ; 92 import java.io.ObjectOutputStream ; 93 import java.io.Serializable ; 94 import java.text.DecimalFormat ; 95 import java.text.NumberFormat ; 96 import java.util.ResourceBundle ; 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 132 public class ThermometerPlot extends Plot implements ValueAxisPlot, 133 Zoomable, 134 Cloneable , 135 Serializable { 136 137 138 private static final long serialVersionUID = 4087093313147984390L; 139 140 141 public static final int UNITS_NONE = 0; 142 143 144 public static final int UNITS_FAHRENHEIT = 1; 145 146 147 public static final int UNITS_CELCIUS = 2; 148 149 150 public static final int UNITS_KELVIN = 3; 151 152 153 public static final int NONE = 0; 154 155 156 public static final int RIGHT = 1; 157 158 159 public static final int LEFT = 2; 160 161 162 public static final int BULB = 3; 163 164 165 public static final int NORMAL = 0; 166 167 168 public static final int WARNING = 1; 169 170 171 public static final int CRITICAL = 2; 172 173 174 protected static final int BULB_RADIUS = 40; 175 176 177 protected static final int BULB_DIAMETER = BULB_RADIUS * 2; 178 179 180 protected static final int COLUMN_RADIUS = 20; 181 182 183 protected static final int COLUMN_DIAMETER = COLUMN_RADIUS * 2; 184 185 186 protected static final int GAP_RADIUS = 5; 187 188 189 protected static final int GAP_DIAMETER = GAP_RADIUS * 2; 190 191 192 protected static final int AXIS_GAP = 10; 193 194 195 protected static final String [] UNITS 196 = {"", "\u00B0F", "\u00B0C", "\u00B0K"}; 197 198 199 protected static final int RANGE_LOW = 0; 200 201 202 protected static final int RANGE_HIGH = 1; 203 204 205 protected static final int DISPLAY_LOW = 2; 206 207 208 protected static final int DISPLAY_HIGH = 3; 209 210 211 protected static final double DEFAULT_LOWER_BOUND = 0.0; 212 213 214 protected static final double DEFAULT_UPPER_BOUND = 100.0; 215 216 217 private ValueDataset dataset; 218 219 220 private ValueAxis rangeAxis; 221 222 223 private double lowerBound = DEFAULT_LOWER_BOUND; 224 225 226 private double upperBound = DEFAULT_UPPER_BOUND; 227 228 231 private RectangleInsets padding; 232 233 234 private transient Stroke thermometerStroke = new BasicStroke (1.0f); 235 236 237 private transient Paint thermometerPaint = Color.black; 238 239 240 private int units = UNITS_CELCIUS; 241 242 243 private int valueLocation = BULB; 244 245 246 private int axisLocation = LEFT; 247 248 249 private Font valueFont = new Font ("SansSerif", Font.BOLD, 16); 250 251 252 private transient Paint valuePaint = Color.white; 253 254 255 private NumberFormat valueFormat = new DecimalFormat (); 256 257 258 private transient Paint mercuryPaint = Color.lightGray; 259 260 261 private boolean showValueLines = false; 262 263 264 private int subrange = -1; 265 266 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 277 private boolean followDataInSubranges = false; 278 279 283 private boolean useSubrangePaint = true; 284 285 286 private Paint [] subrangePaint = { 287 Color.green, 288 Color.orange, 289 Color.red 290 }; 291 292 293 private boolean subrangeIndicatorsVisible = true; 294 295 296 private transient Stroke subrangeIndicatorStroke = new BasicStroke (2.0f); 297 298 299 private transient Stroke rangeIndicatorStroke = new BasicStroke (3.0f); 300 301 302 protected static ResourceBundle localizationResources = 303 ResourceBundle.getBundle("org.jfree.chart.plot.LocalizationBundle"); 304 305 308 public ThermometerPlot() { 309 this(new DefaultValueDataset()); 310 } 311 312 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 341 public ValueDataset getDataset() { 342 return this.dataset; 343 } 344 345 351 public void setDataset(ValueDataset dataset) { 352 353 ValueDataset existing = this.dataset; 356 if (existing != null) { 357 existing.removeChangeListener(this); 358 } 359 360 this.dataset = dataset; 362 if (dataset != null) { 363 setDatasetGroup(dataset.getGroup()); 364 dataset.addChangeListener(this); 365 } 366 367 DatasetChangeEvent event = new DatasetChangeEvent(this, dataset); 369 datasetChanged(event); 370 371 } 372 373 378 public ValueAxis getRangeAxis() { 379 return this.rangeAxis; 380 } 381 382 387 public void setRangeAxis(ValueAxis axis) { 388 389 if (axis != null) { 390 axis.setPlot(this); 391 axis.addChangeListener(this); 392 } 393 394 if (this.rangeAxis != null) { 396 this.rangeAxis.removeChangeListener(this); 397 } 398 399 this.rangeAxis = axis; 400 401 } 402 403 410 public double getLowerBound() { 411 return this.lowerBound; 412 } 413 414 419 public void setLowerBound(double lower) { 420 this.lowerBound = lower; 421 setAxisRange(); 422 } 423 424 430 public double getUpperBound() { 431 return this.upperBound; 432 } 433 434 439 public void setUpperBound(double upper) { 440 this.upperBound = upper; 441 setAxisRange(); 442 } 443 444 450 public void setRange(double lower, double upper) { 451 this.lowerBound = lower; 452 this.upperBound = upper; 453 setAxisRange(); 454 } 455 456 462 public RectangleInsets getPadding() { 463 return this.padding; 464 } 465 466 471 public void setPadding(RectangleInsets padding) { 472 this.padding = padding; 473 notifyListeners(new PlotChangeEvent(this)); 474 } 475 476 481 public Stroke getThermometerStroke() { 482 return this.thermometerStroke; 483 } 484 485 490 public void setThermometerStroke(Stroke s) { 491 if (s != null) { 492 this.thermometerStroke = s; 493 notifyListeners(new PlotChangeEvent(this)); 494 } 495 } 496 497 502 public Paint getThermometerPaint() { 503 return this.thermometerPaint; 504 } 505 506 511 public void setThermometerPaint(Paint paint) { 512 if (paint != null) { 513 this.thermometerPaint = paint; 514 notifyListeners(new PlotChangeEvent(this)); 515 } 516 } 517 518 523 public int getUnits() { 524 return this.units; 525 } 526 527 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 555 public void setUnits(String 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 574 public int getValueLocation() { 575 return this.valueLocation; 576 } 577 578 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 ("Location not recognised."); 596 } 597 } 598 599 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 ("Location not recognised."); 617 } 618 } 619 620 625 public int getAxisLocation() { 626 return this.axisLocation; 627 } 628 629 634 public Font getValueFont() { 635 return this.valueFont; 636 } 637 638 643 public void setValueFont(Font f) { 644 if ((f != null) && (!this.valueFont.equals(f))) { 645 this.valueFont = f; 646 notifyListeners(new PlotChangeEvent(this)); 647 } 648 } 649 650 655 public Paint getValuePaint() { 656 return this.valuePaint; 657 } 658 659 664 public void setValuePaint(Paint p) { 665 if ((p != null) && (!this.valuePaint.equals(p))) { 666 this.valuePaint = p; 667 notifyListeners(new PlotChangeEvent(this)); 668 } 669 } 670 671 676 public void setValueFormat(NumberFormat formatter) { 677 if (formatter != null) { 678 this.valueFormat = formatter; 679 notifyListeners(new PlotChangeEvent(this)); 680 } 681 } 682 683 688 public Paint getMercuryPaint() { 689 return this.mercuryPaint; 690 } 691 692 697 public void setMercuryPaint(Paint paint) { 698 this.mercuryPaint = paint; 699 notifyListeners(new PlotChangeEvent(this)); 700 } 701 702 707 public boolean getShowValueLines() { 708 return this.showValueLines; 709 } 710 711 716 public void setShowValueLines(boolean b) { 717 this.showValueLines = b; 718 notifyListeners(new PlotChangeEvent(this)); 719 } 720 721 728 public void setSubrangeInfo(int range, double low, double hi) { 729 setSubrangeInfo(range, low, hi, low, hi); 730 } 731 732 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 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 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 800 public Paint 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 815 public void setSubrangePaint(int range, Paint 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 829 public boolean getFollowDataInSubranges() { 830 return this.followDataInSubranges; 831 } 832 833 839 public void setFollowDataInSubranges(boolean flag) { 840 this.followDataInSubranges = flag; 841 notifyListeners(new PlotChangeEvent(this)); 842 } 843 844 850 public boolean getUseSubrangePaint() { 851 return this.useSubrangePaint; 852 } 853 854 859 public void setUseSubrangePaint(boolean flag) { 860 this.useSubrangePaint = flag; 861 notifyListeners(new PlotChangeEvent(this)); 862 } 863 864 874 public void draw(Graphics2D g2, Rectangle2D area, Point2D anchor, 875 PlotState parentState, 876 PlotRenderingInfo info) { 877 878 RoundRectangle2D outerStem = new RoundRectangle2D.Double (); 879 RoundRectangle2D innerStem = new RoundRectangle2D.Double (); 880 RoundRectangle2D mercuryStem = new RoundRectangle2D.Double (); 881 Ellipse2D outerBulb = new Ellipse2D.Double (); 882 Ellipse2D innerBulb = new Ellipse2D.Double (); 883 String temp = null; 884 FontMetrics metrics = null; 885 if (info != null) { 886 info.setPlotArea(area); 887 } 888 889 RectangleInsets insets = getInsets(); 891 insets.trim(area); 892 drawBackground(g2, area); 893 894 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 dataArea = new Rectangle2D.Double ( 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 outerThermometer = new Area (outerBulb); 915 Area tempArea = new Area (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 innerThermometer = new Area (innerBulb); 931 tempArea = new Area (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
|