1 /* =========================================================== 2 * JFreeChart : a free chart library for the Java(tm) platform 3 * =========================================================== 4 * 5 * (C) Copyright 2000-2013, 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 * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. 25 * Other names may be trademarks of their respective owners.] 26 * 27 * ---------------- 28 * ContourPlot.java 29 * ---------------- 30 * (C) Copyright 2002-2013, by David M. O'Donnell and Contributors. 31 * 32 * Original Author: David M. O'Donnell; 33 * Contributor(s): David Gilbert (for Object Refinery Limited); 34 * Arnaud Lelievre; 35 * Nicolas Brodu; 36 * 37 * Changes 38 * ------- 39 * 26-Nov-2002 : Version 1 contributed by David M. O'Donnell (DG); 40 * 14-Jan-2003 : Added crosshair attributes (DG); 41 * 23-Jan-2003 : Removed two constructors (DG); 42 * 21-Mar-2003 : Bug fix 701744 (DG); 43 * 26-Mar-2003 : Implemented Serializable (DG); 44 * 09-Jul-2003 : Changed ColorBar from extending axis classes to enclosing 45 * them (DG); 46 * 05-Aug-2003 : Applied changes in bug report 780298 (DG); 47 * 08-Sep-2003 : Added internationalization via use of properties 48 * resourceBundle (RFE 690236) (AL); 49 * 11-Sep-2003 : Cloning support (NB); 50 * 16-Sep-2003 : Changed ChartRenderingInfo --> PlotRenderingInfo (DG); 51 * 17-Jan-2004 : Removed references to DefaultContourDataset class, replaced 52 * with ContourDataset interface (with changes to the interface). 53 * See bug 741048 (DG); 54 * 21-Jan-2004 : Update for renamed method in ValueAxis (DG); 55 * 25-Feb-2004 : Replaced CrosshairInfo with CrosshairState (DG); 56 * 06-Oct-2004 : Updated for changes in DatasetUtilities class (DG); 57 * 11-Nov-2004 : Renamed zoom methods to match ValueAxisPlot interface (DG); 58 * 25-Nov-2004 : Small update to clone() implementation (DG); 59 * 11-Jan-2005 : Removed deprecated code in preparation for 1.0.0 release (DG); 60 * 05-May-2005 : Updated draw() method parameters (DG); 61 * 16-Jun-2005 : Added default constructor (DG); 62 * 01-Sep-2005 : Moved dataAreaRatio from Plot to here (DG); 63 * ------------- JFREECHART 1.0.x --------------------------------------------- 64 * 31-Jan-2007 : Deprecated (DG); 65 * 18-Dec-2008 : Use ResourceBundleWrapper - see patch 1607918 by 66 * Jess Thrysoee (DG); 67 * 19-May-2009 : Fixed FindBugs warnings, patch by Michal Wozniak (DG); 68 * 02-Jul-2013 : Fix NB warnings (DG); 69 * 70 */ 71 72 package org.jfree.chart.plot; 73 74 import java.awt.AlphaComposite; 75 import java.awt.Composite; 76 import java.awt.Graphics2D; 77 import java.awt.Paint; 78 import java.awt.RenderingHints; 79 import java.awt.Shape; 80 import java.awt.Stroke; 81 import java.awt.geom.Ellipse2D; 82 import java.awt.geom.GeneralPath; 83 import java.awt.geom.Line2D; 84 import java.awt.geom.Point2D; 85 import java.awt.geom.Rectangle2D; 86 import java.awt.geom.RectangularShape; 87 import java.beans.PropertyChangeEvent; 88 import java.beans.PropertyChangeListener; 89 import java.io.Serializable; 90 import java.util.Iterator; 91 import java.util.List; 92 import java.util.ResourceBundle; 93 94 import org.jfree.chart.ClipPath; 95 import org.jfree.chart.annotations.XYAnnotation; 96 import org.jfree.chart.axis.AxisSpace; 97 import org.jfree.chart.axis.ColorBar; 98 import org.jfree.chart.axis.NumberAxis; 99 import org.jfree.chart.axis.ValueAxis; 100 import org.jfree.chart.entity.ContourEntity; 101 import org.jfree.chart.entity.EntityCollection; 102 import org.jfree.chart.event.AxisChangeEvent; 103 import org.jfree.chart.event.PlotChangeEvent; 104 import org.jfree.chart.labels.ContourToolTipGenerator; 105 import org.jfree.chart.labels.StandardContourToolTipGenerator; 106 import org.jfree.chart.renderer.xy.XYBlockRenderer; 107 import org.jfree.chart.urls.XYURLGenerator; 108 import org.jfree.chart.util.ResourceBundleWrapper; 109 import org.jfree.data.Range; 110 import org.jfree.data.contour.ContourDataset; 111 import org.jfree.data.general.DatasetChangeEvent; 112 import org.jfree.data.general.DatasetUtilities; 113 import org.jfree.ui.RectangleEdge; 114 import org.jfree.ui.RectangleInsets; 115 import org.jfree.util.ObjectUtilities; 116 117 /** 118 * A class for creating shaded contours. 119 * 120 * @deprecated This plot is no longer supported, please use {@link XYPlot} with 121 * an {@link XYBlockRenderer}. 122 */ 123 public class ContourPlot extends Plot implements ContourValuePlot, 124 ValueAxisPlot, PropertyChangeListener, Serializable, Cloneable { 125 126 /** For serialization. */ 127 private static final long serialVersionUID = 7861072556590502247L; 128 129 /** The default insets. */ 130 protected static final RectangleInsets DEFAULT_INSETS 131 = new RectangleInsets(2.0, 2.0, 100.0, 10.0); 132 133 /** The domain axis (used for the x-values). */ 134 private ValueAxis domainAxis; 135 136 /** The range axis (used for the y-values). */ 137 private ValueAxis rangeAxis; 138 139 /** The dataset. */ 140 private ContourDataset dataset; 141 142 /** The colorbar axis (used for the z-values). */ 143 private ColorBar colorBar = null; 144 145 /** The color bar location. */ 146 private RectangleEdge colorBarLocation; 147 148 /** A flag that controls whether or not a domain crosshair is drawn..*/ 149 private boolean domainCrosshairVisible; 150 151 /** The domain crosshair value. */ 152 private double domainCrosshairValue; 153 154 /** The pen/brush used to draw the crosshair (if any). */ 155 private transient Stroke domainCrosshairStroke; 156 157 /** The color used to draw the crosshair (if any). */ 158 private transient Paint domainCrosshairPaint; 159 160 /** 161 * A flag that controls whether or not the crosshair locks onto actual data 162 * points. 163 */ 164 private boolean domainCrosshairLockedOnData = true; 165 166 /** A flag that controls whether or not a range crosshair is drawn..*/ 167 private boolean rangeCrosshairVisible; 168 169 /** The range crosshair value. */ 170 private double rangeCrosshairValue; 171 172 /** The pen/brush used to draw the crosshair (if any). */ 173 private transient Stroke rangeCrosshairStroke; 174 175 /** The color used to draw the crosshair (if any). */ 176 private transient Paint rangeCrosshairPaint; 177 178 /** 179 * A flag that controls whether or not the crosshair locks onto actual data 180 * points. 181 */ 182 private boolean rangeCrosshairLockedOnData = true; 183 184 /** 185 * Defines dataArea rectangle as the ratio formed from dividing height by 186 * width (of the dataArea). Modifies plot area calculations. 187 * ratio>0 will attempt to layout the plot so that the 188 * dataArea.height/dataArea.width = ratio. 189 * ratio<0 will attempt to layout the plot so that the 190 * dataArea.height/dataArea.width in plot units (not java2D units as when 191 * ratio>0) = -1.*ratio. 192 */ //dmo 193 private double dataAreaRatio = 0.0; //zero when the parameter is not set 194 195 /** A list of markers (optional) for the domain axis. */ 196 private List domainMarkers; 197 198 /** A list of markers (optional) for the range axis. */ 199 private List rangeMarkers; 200 201 /** A list of annotations (optional) for the plot. */ 202 private List annotations; 203 204 /** The tool tip generator. */ 205 private ContourToolTipGenerator toolTipGenerator; 206 207 /** The URL text generator. */ 208 private XYURLGenerator urlGenerator; 209 210 /** 211 * Controls whether data are render as filled rectangles or rendered as 212 * points 213 */ 214 private boolean renderAsPoints = false; 215 216 /** 217 * Size of points rendered when renderAsPoints = true. Size is relative to 218 * dataArea 219 */ 220 private double ptSizePct = 0.05; 221 222 /** Contains the a ClipPath to "trim" the contours. */ 223 private transient ClipPath clipPath = null; 224 225 /** Set to Paint to represent missing values. */ 226 private transient Paint missingPaint = null; 227 228 /** The resourceBundle for the localization. */ 229 protected static ResourceBundle localizationResources 230 = ResourceBundleWrapper.getBundle( 231 "org.jfree.chart.plot.LocalizationBundle"); 232 233 /** 234 * Creates a new plot with no dataset or axes. 235 */ ContourPlot()236 public ContourPlot() { 237 this(null, null, null, null); 238 } 239 240 /** 241 * Constructs a contour plot with the specified axes (other attributes take 242 * default values). 243 * 244 * @param dataset The dataset. 245 * @param domainAxis The domain axis. 246 * @param rangeAxis The range axis. 247 * @param colorBar The z-axis axis. 248 */ ContourPlot(ContourDataset dataset, ValueAxis domainAxis, ValueAxis rangeAxis, ColorBar colorBar)249 public ContourPlot(ContourDataset dataset, 250 ValueAxis domainAxis, ValueAxis rangeAxis, 251 ColorBar colorBar) { 252 253 super(); 254 255 this.dataset = dataset; 256 if (dataset != null) { 257 dataset.addChangeListener(this); 258 } 259 260 this.domainAxis = domainAxis; 261 if (domainAxis != null) { 262 domainAxis.setPlot(this); 263 domainAxis.addChangeListener(this); 264 } 265 266 this.rangeAxis = rangeAxis; 267 if (rangeAxis != null) { 268 rangeAxis.setPlot(this); 269 rangeAxis.addChangeListener(this); 270 } 271 272 this.colorBar = colorBar; 273 if (colorBar != null) { 274 colorBar.getAxis().setPlot(this); 275 colorBar.getAxis().addChangeListener(this); 276 colorBar.configure(this); 277 } 278 this.colorBarLocation = RectangleEdge.LEFT; 279 280 this.toolTipGenerator = new StandardContourToolTipGenerator(); 281 282 } 283 284 /** 285 * Returns the color bar location. 286 * 287 * @return The color bar location. 288 */ getColorBarLocation()289 public RectangleEdge getColorBarLocation() { 290 return this.colorBarLocation; 291 } 292 293 /** 294 * Sets the color bar location and sends a {@link PlotChangeEvent} to all 295 * registered listeners. 296 * 297 * @param edge the location. 298 */ setColorBarLocation(RectangleEdge edge)299 public void setColorBarLocation(RectangleEdge edge) { 300 this.colorBarLocation = edge; 301 fireChangeEvent(); 302 } 303 304 /** 305 * Returns the primary dataset for the plot. 306 * 307 * @return The primary dataset (possibly <code>null</code>). 308 */ getDataset()309 public ContourDataset getDataset() { 310 return this.dataset; 311 } 312 313 /** 314 * Sets the dataset for the plot, replacing the existing dataset if there 315 * is one. 316 * 317 * @param dataset the dataset (<code>null</code> permitted). 318 */ setDataset(ContourDataset dataset)319 public void setDataset(ContourDataset dataset) { 320 321 // if there is an existing dataset, remove the plot from the list of 322 // change listeners... 323 ContourDataset existing = this.dataset; 324 if (existing != null) { 325 existing.removeChangeListener(this); 326 } 327 328 // set the new dataset, and register the chart as a change listener... 329 this.dataset = dataset; 330 if (dataset != null) { 331 setDatasetGroup(dataset.getGroup()); 332 dataset.addChangeListener(this); 333 } 334 335 // send a dataset change event to self... 336 DatasetChangeEvent event = new DatasetChangeEvent(this, dataset); 337 datasetChanged(event); 338 339 } 340 341 /** 342 * Returns the domain axis for the plot. 343 * 344 * @return The domain axis. 345 */ getDomainAxis()346 public ValueAxis getDomainAxis() { 347 348 ValueAxis result = this.domainAxis; 349 350 return result; 351 352 } 353 354 /** 355 * Sets the domain axis for the plot (this must be compatible with the plot 356 * type or an exception is thrown). 357 * 358 * @param axis The new axis. 359 */ setDomainAxis(ValueAxis axis)360 public void setDomainAxis(ValueAxis axis) { 361 362 if (isCompatibleDomainAxis(axis)) { 363 364 if (axis != null) { 365 axis.setPlot(this); 366 axis.addChangeListener(this); 367 } 368 369 // plot is likely registered as a listener with the existing axis... 370 if (this.domainAxis != null) { 371 this.domainAxis.removeChangeListener(this); 372 } 373 374 this.domainAxis = axis; 375 fireChangeEvent(); 376 377 } 378 379 } 380 381 /** 382 * Returns the range axis for the plot. 383 * 384 * @return The range axis. 385 */ getRangeAxis()386 public ValueAxis getRangeAxis() { 387 388 ValueAxis result = this.rangeAxis; 389 390 return result; 391 392 } 393 394 /** 395 * Sets the range axis for the plot. 396 * <P> 397 * An exception is thrown if the new axis and the plot are not mutually 398 * compatible. 399 * 400 * @param axis The new axis (null permitted). 401 */ setRangeAxis(ValueAxis axis)402 public void setRangeAxis(ValueAxis axis) { 403 404 if (axis != null) { 405 axis.setPlot(this); 406 axis.addChangeListener(this); 407 } 408 409 // plot is likely registered as a listener with the existing axis... 410 if (this.rangeAxis != null) { 411 this.rangeAxis.removeChangeListener(this); 412 } 413 414 this.rangeAxis = axis; 415 fireChangeEvent(); 416 417 } 418 419 /** 420 * Sets the colorbar for the plot. 421 * 422 * @param axis The new axis (null permitted). 423 */ setColorBarAxis(ColorBar axis)424 public void setColorBarAxis(ColorBar axis) { 425 426 this.colorBar = axis; 427 fireChangeEvent(); 428 429 } 430 431 /** 432 * Returns the data area ratio. 433 * 434 * @return The ratio. 435 */ getDataAreaRatio()436 public double getDataAreaRatio() { 437 return this.dataAreaRatio; 438 } 439 440 /** 441 * Sets the data area ratio. 442 * 443 * @param ratio the ratio. 444 */ setDataAreaRatio(double ratio)445 public void setDataAreaRatio(double ratio) { 446 this.dataAreaRatio = ratio; 447 } 448 449 /** 450 * Adds a marker for the domain axis. 451 * <P> 452 * Typically a marker will be drawn by the renderer as a line perpendicular 453 * to the range axis, however this is entirely up to the renderer. 454 * 455 * @param marker the marker. 456 */ addDomainMarker(Marker marker)457 public void addDomainMarker(Marker marker) { 458 459 if (this.domainMarkers == null) { 460 this.domainMarkers = new java.util.ArrayList(); 461 } 462 this.domainMarkers.add(marker); 463 fireChangeEvent(); 464 465 } 466 467 /** 468 * Clears all the domain markers. 469 */ clearDomainMarkers()470 public void clearDomainMarkers() { 471 if (this.domainMarkers != null) { 472 this.domainMarkers.clear(); 473 fireChangeEvent(); 474 } 475 } 476 477 /** 478 * Adds a marker for the range axis. 479 * <P> 480 * Typically a marker will be drawn by the renderer as a line perpendicular 481 * to the range axis, however this is entirely up to the renderer. 482 * 483 * @param marker The marker. 484 */ addRangeMarker(Marker marker)485 public void addRangeMarker(Marker marker) { 486 487 if (this.rangeMarkers == null) { 488 this.rangeMarkers = new java.util.ArrayList(); 489 } 490 this.rangeMarkers.add(marker); 491 fireChangeEvent(); 492 493 } 494 495 /** 496 * Clears all the range markers. 497 */ clearRangeMarkers()498 public void clearRangeMarkers() { 499 if (this.rangeMarkers != null) { 500 this.rangeMarkers.clear(); 501 fireChangeEvent(); 502 } 503 } 504 505 /** 506 * Adds an annotation to the plot. 507 * 508 * @param annotation the annotation. 509 */ addAnnotation(XYAnnotation annotation)510 public void addAnnotation(XYAnnotation annotation) { 511 512 if (this.annotations == null) { 513 this.annotations = new java.util.ArrayList(); 514 } 515 this.annotations.add(annotation); 516 fireChangeEvent(); 517 518 } 519 520 /** 521 * Clears all the annotations. 522 */ clearAnnotations()523 public void clearAnnotations() { 524 if (this.annotations != null) { 525 this.annotations.clear(); 526 fireChangeEvent(); 527 } 528 } 529 530 /** 531 * Checks the compatibility of a domain axis, returning true if the axis is 532 * compatible with the plot, and false otherwise. 533 * 534 * @param axis The proposed axis. 535 * 536 * @return <code>true</code> if the axis is compatible with the plot. 537 */ isCompatibleDomainAxis(ValueAxis axis)538 public boolean isCompatibleDomainAxis(ValueAxis axis) { 539 540 return true; 541 542 } 543 544 /** 545 * Draws the plot on a Java 2D graphics device (such as the screen or a 546 * printer). 547 * <P> 548 * The optional <code>info</code> argument collects information about the 549 * rendering of the plot (dimensions, tooltip information etc). Just pass 550 * in <code>null</code> if you do not need this information. 551 * 552 * @param g2 the graphics device. 553 * @param area the area within which the plot (including axis labels) 554 * should be drawn. 555 * @param anchor the anchor point (<code>null</code> permitted). 556 * @param parentState the state from the parent plot, if there is one. 557 * @param info collects chart drawing information (<code>null</code> 558 * permitted). 559 */ 560 @Override draw(Graphics2D g2, Rectangle2D area, Point2D anchor, PlotState parentState, PlotRenderingInfo info)561 public void draw(Graphics2D g2, Rectangle2D area, Point2D anchor, 562 PlotState parentState, PlotRenderingInfo info) { 563 564 // if the plot area is too small, just return... 565 boolean b1 = (area.getWidth() <= MINIMUM_WIDTH_TO_DRAW); 566 boolean b2 = (area.getHeight() <= MINIMUM_HEIGHT_TO_DRAW); 567 if (b1 || b2) { 568 return; 569 } 570 571 // record the plot area... 572 if (info != null) { 573 info.setPlotArea(area); 574 } 575 576 // adjust the drawing area for plot insets (if any)... 577 RectangleInsets insets = getInsets(); 578 insets.trim(area); 579 580 AxisSpace space = new AxisSpace(); 581 582 space = this.domainAxis.reserveSpace(g2, this, area, 583 RectangleEdge.BOTTOM, space); 584 space = this.rangeAxis.reserveSpace(g2, this, area, 585 RectangleEdge.LEFT, space); 586 587 Rectangle2D estimatedDataArea = space.shrink(area, null); 588 589 AxisSpace space2 = new AxisSpace(); 590 space2 = this.colorBar.reserveSpace(g2, this, area, estimatedDataArea, 591 this.colorBarLocation, space2); 592 Rectangle2D adjustedPlotArea = space2.shrink(area, null); 593 594 Rectangle2D dataArea = space.shrink(adjustedPlotArea, null); 595 596 Rectangle2D colorBarArea = space2.reserved(area, this.colorBarLocation); 597 598 // additional dataArea modifications 599 if (getDataAreaRatio() != 0.0) { //check whether modification is 600 double ratio = getDataAreaRatio(); 601 Rectangle2D tmpDataArea = (Rectangle2D) dataArea.clone(); 602 double h = tmpDataArea.getHeight(); 603 double w = tmpDataArea.getWidth(); 604 605 if (ratio > 0) { // ratio represents pixels 606 if (w * ratio <= h) { 607 h = ratio * w; 608 } 609 else { 610 w = h / ratio; 611 } 612 } 613 else { // ratio represents axis units 614 ratio *= -1.0; 615 double xLength = getDomainAxis().getRange().getLength(); 616 double yLength = getRangeAxis().getRange().getLength(); 617 double unitRatio = yLength / xLength; 618 619 ratio = unitRatio * ratio; 620 621 if (w * ratio <= h) { 622 h = ratio * w; 623 } 624 else { 625 w = h / ratio; 626 } 627 } 628 629 dataArea.setRect(tmpDataArea.getX() + tmpDataArea.getWidth() / 2 630 - w / 2, tmpDataArea.getY(), w, h); 631 } 632 633 if (info != null) { 634 info.setDataArea(dataArea); 635 } 636 637 CrosshairState crosshairState = new CrosshairState(); 638 crosshairState.setCrosshairDistance(Double.POSITIVE_INFINITY); 639 640 // draw the plot background... 641 drawBackground(g2, dataArea); 642 643 double cursor = dataArea.getMaxY(); 644 if (this.domainAxis != null) { 645 this.domainAxis.draw(g2, cursor, adjustedPlotArea, dataArea, 646 RectangleEdge.BOTTOM, info); 647 } 648 649 if (this.rangeAxis != null) { 650 cursor = dataArea.getMinX(); 651 this.rangeAxis.draw(g2, cursor, adjustedPlotArea, dataArea, 652 RectangleEdge.LEFT, info); 653 } 654 655 if (this.colorBar != null) { 656 cursor = 0.0; 657 this.colorBar.draw(g2, cursor, adjustedPlotArea, dataArea, 658 colorBarArea, this.colorBarLocation); 659 } 660 Shape originalClip = g2.getClip(); 661 Composite originalComposite = g2.getComposite(); 662 663 g2.clip(dataArea); 664 g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 665 getForegroundAlpha())); 666 render(g2, dataArea, info, crosshairState); 667 668 if (this.domainMarkers != null) { 669 Iterator iterator = this.domainMarkers.iterator(); 670 while (iterator.hasNext()) { 671 Marker marker = (Marker) iterator.next(); 672 drawDomainMarker(g2, this, getDomainAxis(), marker, dataArea); 673 } 674 } 675 676 if (this.rangeMarkers != null) { 677 Iterator iterator = this.rangeMarkers.iterator(); 678 while (iterator.hasNext()) { 679 Marker marker = (Marker) iterator.next(); 680 drawRangeMarker(g2, this, getRangeAxis(), marker, dataArea); 681 } 682 } 683 684 // TO DO: these annotations only work with XYPlot, see if it is possible to 685 // make ContourPlot a subclass of XYPlot (DG); 686 687 // // draw the annotations... 688 // if (this.annotations != null) { 689 // Iterator iterator = this.annotations.iterator(); 690 // while (iterator.hasNext()) { 691 // Annotation annotation = (Annotation) iterator.next(); 692 // if (annotation instanceof XYAnnotation) { 693 // XYAnnotation xya = (XYAnnotation) annotation; 694 // // get the annotation to draw itself... 695 // xya.draw(g2, this, dataArea, getDomainAxis(), 696 // getRangeAxis()); 697 // } 698 // } 699 // } 700 701 g2.setClip(originalClip); 702 g2.setComposite(originalComposite); 703 drawOutline(g2, dataArea); 704 705 } 706 707 /** 708 * Draws a representation of the data within the dataArea region, using the 709 * current renderer. 710 * <P> 711 * The <code>info</code> and <code>crosshairState</code> arguments may be 712 * <code>null</code>. 713 * 714 * @param g2 the graphics device. 715 * @param dataArea the region in which the data is to be drawn. 716 * @param info an optional object for collection dimension information. 717 * @param crosshairState an optional object for collecting crosshair info. 718 */ render(Graphics2D g2, Rectangle2D dataArea, PlotRenderingInfo info, CrosshairState crosshairState)719 public void render(Graphics2D g2, Rectangle2D dataArea, 720 PlotRenderingInfo info, CrosshairState crosshairState) { 721 722 // now get the data and plot it (the visual representation will depend 723 // on the renderer that has been set)... 724 ContourDataset data = getDataset(); 725 if (data != null) { 726 727 ColorBar zAxis = getColorBar(); 728 729 if (this.clipPath != null) { 730 GeneralPath clipper = getClipPath().draw(g2, dataArea, 731 this.domainAxis, this.rangeAxis); 732 if (this.clipPath.isClip()) { 733 g2.clip(clipper); 734 } 735 } 736 737 if (this.renderAsPoints) { 738 pointRenderer(g2, dataArea, info, this, this.domainAxis, 739 this.rangeAxis, zAxis, data, crosshairState); 740 } 741 else { 742 contourRenderer(g2, dataArea, info, this, this.domainAxis, 743 this.rangeAxis, zAxis, data, crosshairState); 744 } 745 746 // draw vertical crosshair if required... 747 setDomainCrosshairValue(crosshairState.getCrosshairX(), false); 748 if (isDomainCrosshairVisible()) { 749 drawVerticalLine(g2, dataArea, 750 getDomainCrosshairValue(), 751 getDomainCrosshairStroke(), 752 getDomainCrosshairPaint()); 753 } 754 755 // draw horizontal crosshair if required... 756 setRangeCrosshairValue(crosshairState.getCrosshairY(), false); 757 if (isRangeCrosshairVisible()) { 758 drawHorizontalLine(g2, dataArea, 759 getRangeCrosshairValue(), 760 getRangeCrosshairStroke(), 761 getRangeCrosshairPaint()); 762 } 763 764 } 765 else if (this.clipPath != null) { 766 getClipPath().draw(g2, dataArea, this.domainAxis, this.rangeAxis); 767 } 768 769 } 770 771 /** 772 * Fills the plot. 773 * 774 * @param g2 the graphics device. 775 * @param dataArea the area within which the data is being drawn. 776 * @param info collects information about the drawing. 777 * @param plot the plot (can be used to obtain standard color 778 * information etc). 779 * @param horizontalAxis the domain (horizontal) axis. 780 * @param verticalAxis the range (vertical) axis. 781 * @param colorBar the color bar axis. 782 * @param data the dataset. 783 * @param crosshairState information about crosshairs on a plot. 784 */ contourRenderer(Graphics2D g2, Rectangle2D dataArea, PlotRenderingInfo info, ContourPlot plot, ValueAxis horizontalAxis, ValueAxis verticalAxis, ColorBar colorBar, ContourDataset data, CrosshairState crosshairState)785 public void contourRenderer(Graphics2D g2, 786 Rectangle2D dataArea, 787 PlotRenderingInfo info, 788 ContourPlot plot, 789 ValueAxis horizontalAxis, 790 ValueAxis verticalAxis, 791 ColorBar colorBar, 792 ContourDataset data, 793 CrosshairState crosshairState) { 794 795 // setup for collecting optional entity info... 796 Rectangle2D.Double entityArea; 797 EntityCollection entities = null; 798 if (info != null) { 799 entities = info.getOwner().getEntityCollection(); 800 } 801 802 Rectangle2D.Double rect; 803 rect = new Rectangle2D.Double(); 804 805 //turn off anti-aliasing when filling rectangles 806 Object antiAlias = g2.getRenderingHint(RenderingHints.KEY_ANTIALIASING); 807 g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, 808 RenderingHints.VALUE_ANTIALIAS_OFF); 809 810 // get the data points 811 Number[] xNumber = data.getXValues(); 812 Number[] yNumber = data.getYValues(); 813 Number[] zNumber = data.getZValues(); 814 815 double[] x = new double[xNumber.length]; 816 double[] y = new double[yNumber.length]; 817 818 for (int i = 0; i < x.length; i++) { 819 x[i] = xNumber[i].doubleValue(); 820 y[i] = yNumber[i].doubleValue(); 821 } 822 823 int[] xIndex = data.indexX(); 824 int[] indexX = data.getXIndices(); 825 boolean vertInverted = ((NumberAxis) verticalAxis).isInverted(); 826 boolean horizInverted = false; 827 if (horizontalAxis instanceof NumberAxis) { 828 horizInverted = ((NumberAxis) horizontalAxis).isInverted(); 829 } 830 double transX = 0.0; 831 double transXm1; 832 double transXp1; 833 double transDXm1; 834 double transDXp1 = 0.0; 835 double transDX = 0.0; 836 double transY; 837 double transYm1; 838 double transYp1; 839 double transDYm1; 840 double transDYp1 = 0.0; 841 double transDY; 842 int iMax = xIndex[xIndex.length - 1]; 843 for (int k = 0; k < x.length; k++) { 844 int i = xIndex[k]; 845 if (indexX[i] == k) { // this is a new column 846 if (i == 0) { 847 transX = horizontalAxis.valueToJava2D(x[k], dataArea, 848 RectangleEdge.BOTTOM); 849 transXm1 = transX; 850 transXp1 = horizontalAxis.valueToJava2D( 851 x[indexX[i + 1]], dataArea, RectangleEdge.BOTTOM); 852 transDXm1 = Math.abs(0.5 * (transX - transXm1)); 853 transDXp1 = Math.abs(0.5 * (transX - transXp1)); 854 } 855 else if (i == iMax) { 856 transX = horizontalAxis.valueToJava2D(x[k], dataArea, 857 RectangleEdge.BOTTOM); 858 transXm1 = horizontalAxis.valueToJava2D(x[indexX[i - 1]], 859 dataArea, RectangleEdge.BOTTOM); 860 transXp1 = transX; 861 transDXm1 = Math.abs(0.5 * (transX - transXm1)); 862 transDXp1 = Math.abs(0.5 * (transX - transXp1)); 863 } 864 else { 865 transX = horizontalAxis.valueToJava2D(x[k], dataArea, 866 RectangleEdge.BOTTOM); 867 transXp1 = horizontalAxis.valueToJava2D(x[indexX[i + 1]], 868 dataArea, RectangleEdge.BOTTOM); 869 transDXm1 = transDXp1; 870 transDXp1 = Math.abs(0.5 * (transX - transXp1)); 871 } 872 873 if (horizInverted) { 874 transX -= transDXp1; 875 } 876 else { 877 transX -= transDXm1; 878 } 879 880 transDX = transDXm1 + transDXp1; 881 882 transY = verticalAxis.valueToJava2D(y[k], dataArea, 883 RectangleEdge.LEFT); 884 transYm1 = transY; 885 if (k + 1 == y.length) { 886 continue; 887 } 888 transYp1 = verticalAxis.valueToJava2D(y[k + 1], dataArea, 889 RectangleEdge.LEFT); 890 transDYm1 = Math.abs(0.5 * (transY - transYm1)); 891 transDYp1 = Math.abs(0.5 * (transY - transYp1)); 892 } 893 else if ((i < indexX.length - 1 894 && indexX[i + 1] - 1 == k) || k == x.length - 1) { 895 // end of column 896 transY = verticalAxis.valueToJava2D(y[k], dataArea, 897 RectangleEdge.LEFT); 898 transYm1 = verticalAxis.valueToJava2D(y[k - 1], dataArea, 899 RectangleEdge.LEFT); 900 transYp1 = transY; 901 transDYm1 = Math.abs(0.5 * (transY - transYm1)); 902 transDYp1 = Math.abs(0.5 * (transY - transYp1)); 903 } 904 else { 905 transY = verticalAxis.valueToJava2D(y[k], dataArea, 906 RectangleEdge.LEFT); 907 transYp1 = verticalAxis.valueToJava2D(y[k + 1], dataArea, 908 RectangleEdge.LEFT); 909 transDYm1 = transDYp1; 910 transDYp1 = Math.abs(0.5 * (transY - transYp1)); 911 } 912 if (vertInverted) { 913 transY -= transDYm1; 914 } 915 else { 916 transY -= transDYp1; 917 } 918 919 transDY = transDYm1 + transDYp1; 920 921 rect.setRect(transX, transY, transDX, transDY); 922 if (zNumber[k] != null) { 923 g2.setPaint(colorBar.getPaint(zNumber[k].doubleValue())); 924 g2.fill(rect); 925 } 926 else if (this.missingPaint != null) { 927 g2.setPaint(this.missingPaint); 928 g2.fill(rect); 929 } 930 931 entityArea = rect; 932 933 // add an entity for the item... 934 if (entities != null) { 935 String tip = ""; 936 if (getToolTipGenerator() != null) { 937 tip = this.toolTipGenerator.generateToolTip(data, k); 938 } 939 // Shape s = g2.getClip(); 940 // if (s.contains(rect) || s.intersects(rect)) { 941 String url = null; 942 // if (getURLGenerator() != null) { //dmo: look at this later 943 // url = getURLGenerator().generateURL(data, series, item); 944 // } 945 // Unlike XYItemRenderer, we need to clone entityArea since it 946 // reused. 947 ContourEntity entity = new ContourEntity( 948 (Rectangle2D.Double) entityArea.clone(), tip, url); 949 entity.setIndex(k); 950 entities.add(entity); 951 // } 952 } 953 954 // do we need to update the crosshair values? 955 if (plot.isDomainCrosshairLockedOnData()) { 956 if (plot.isRangeCrosshairLockedOnData()) { 957 // both axes 958 crosshairState.updateCrosshairPoint(x[k], y[k], transX, 959 transY, PlotOrientation.VERTICAL); 960 } 961 else { 962 // just the horizontal axis... 963 crosshairState.updateCrosshairX(transX); 964 } 965 } 966 else { 967 if (plot.isRangeCrosshairLockedOnData()) { 968 // just the vertical axis... 969 crosshairState.updateCrosshairY(transY); 970 } 971 } 972 } 973 974 g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, antiAlias); 975 976 } 977 978 /** 979 * Draws the visual representation of a single data item. 980 * 981 * @param g2 the graphics device. 982 * @param dataArea the area within which the data is being drawn. 983 * @param info collects information about the drawing. 984 * @param plot the plot (can be used to obtain standard color 985 * information etc). 986 * @param domainAxis the domain (horizontal) axis. 987 * @param rangeAxis the range (vertical) axis. 988 * @param colorBar the color bar axis. 989 * @param data the dataset. 990 * @param crosshairState information about crosshairs on a plot. 991 */ pointRenderer(Graphics2D g2, Rectangle2D dataArea, PlotRenderingInfo info, ContourPlot plot, ValueAxis domainAxis, ValueAxis rangeAxis, ColorBar colorBar, ContourDataset data, CrosshairState crosshairState)992 public void pointRenderer(Graphics2D g2, 993 Rectangle2D dataArea, 994 PlotRenderingInfo info, 995 ContourPlot plot, 996 ValueAxis domainAxis, 997 ValueAxis rangeAxis, 998 ColorBar colorBar, 999 ContourDataset data, 1000 CrosshairState crosshairState) { 1001 1002 // setup for collecting optional entity info... 1003 RectangularShape entityArea; 1004 EntityCollection entities = null; 1005 if (info != null) { 1006 entities = info.getOwner().getEntityCollection(); 1007 } 1008 1009 // Rectangle2D.Double rect = null; 1010 // rect = new Rectangle2D.Double(); 1011 RectangularShape rect = new Ellipse2D.Double(); 1012 1013 1014 //turn off anti-aliasing when filling rectangles 1015 Object antiAlias = g2.getRenderingHint(RenderingHints.KEY_ANTIALIASING); 1016 g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, 1017 RenderingHints.VALUE_ANTIALIAS_OFF); 1018 1019 // if (tooltips!=null) tooltips.clearToolTips(); // reset collection 1020 // get the data points 1021 Number[] xNumber = data.getXValues(); 1022 Number[] yNumber = data.getYValues(); 1023 Number[] zNumber = data.getZValues(); 1024 1025 double[] x = new double[xNumber.length]; 1026 double[] y = new double[yNumber.length]; 1027 1028 for (int i = 0; i < x.length; i++) { 1029 x[i] = xNumber[i].doubleValue(); 1030 y[i] = yNumber[i].doubleValue(); 1031 } 1032 1033 double transX; 1034 double transDX; 1035 double transY; 1036 double transDY; 1037 double size = dataArea.getWidth() * this.ptSizePct; 1038 for (int k = 0; k < x.length; k++) { 1039 1040 transX = domainAxis.valueToJava2D(x[k], dataArea, 1041 RectangleEdge.BOTTOM) - 0.5 * size; 1042 transY = rangeAxis.valueToJava2D(y[k], dataArea, RectangleEdge.LEFT) 1043 - 0.5 * size; 1044 transDX = size; 1045 transDY = size; 1046 1047 rect.setFrame(transX, transY, transDX, transDY); 1048 1049 if (zNumber[k] != null) { 1050 g2.setPaint(colorBar.getPaint(zNumber[k].doubleValue())); 1051 g2.fill(rect); 1052 } 1053 else if (this.missingPaint != null) { 1054 g2.setPaint(this.missingPaint); 1055 g2.fill(rect); 1056 } 1057 1058 1059 entityArea = rect; 1060 1061 // add an entity for the item... 1062 if (entities != null) { 1063 String tip = null; 1064 if (getToolTipGenerator() != null) { 1065 tip = this.toolTipGenerator.generateToolTip(data, k); 1066 } 1067 String url = null; 1068 // if (getURLGenerator() != null) { //dmo: look at this later 1069 // url = getURLGenerator().generateURL(data, series, item); 1070 // } 1071 // Unlike XYItemRenderer, we need to clone entityArea since it 1072 // reused. 1073 ContourEntity entity = new ContourEntity( 1074 (RectangularShape) entityArea.clone(), tip, url); 1075 entity.setIndex(k); 1076 entities.add(entity); 1077 } 1078 1079 // do we need to update the crosshair values? 1080 if (plot.isDomainCrosshairLockedOnData()) { 1081 if (plot.isRangeCrosshairLockedOnData()) { 1082 // both axes 1083 crosshairState.updateCrosshairPoint(x[k], y[k], transX, 1084 transY, PlotOrientation.VERTICAL); 1085 } 1086 else { 1087 // just the horizontal axis... 1088 crosshairState.updateCrosshairX(transX); 1089 } 1090 } 1091 else { 1092 if (plot.isRangeCrosshairLockedOnData()) { 1093 // just the vertical axis... 1094 crosshairState.updateCrosshairY(transY); 1095 } 1096 } 1097 } 1098 1099 1100 g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, antiAlias); 1101 1102 } 1103 1104 /** 1105 * Utility method for drawing a crosshair on the chart (if required). 1106 * 1107 * @param g2 The graphics device. 1108 * @param dataArea The data area. 1109 * @param value The coordinate, where to draw the line. 1110 * @param stroke The stroke to use. 1111 * @param paint The paint to use. 1112 */ drawVerticalLine(Graphics2D g2, Rectangle2D dataArea, double value, Stroke stroke, Paint paint)1113 protected void drawVerticalLine(Graphics2D g2, Rectangle2D dataArea, 1114 double value, Stroke stroke, Paint paint) { 1115 1116 double xx = getDomainAxis().valueToJava2D(value, dataArea, 1117 RectangleEdge.BOTTOM); 1118 Line2D line = new Line2D.Double(xx, dataArea.getMinY(), xx, 1119 dataArea.getMaxY()); 1120 g2.setStroke(stroke); 1121 g2.setPaint(paint); 1122 g2.draw(line); 1123 1124 } 1125 1126 /** 1127 * Utility method for drawing a crosshair on the chart (if required). 1128 * 1129 * @param g2 The graphics device. 1130 * @param dataArea The data area. 1131 * @param value The coordinate, where to draw the line. 1132 * @param stroke The stroke to use. 1133 * @param paint The paint to use. 1134 */ drawHorizontalLine(Graphics2D g2, Rectangle2D dataArea, double value, Stroke stroke, Paint paint)1135 protected void drawHorizontalLine(Graphics2D g2, Rectangle2D dataArea, 1136 double value, Stroke stroke, 1137 Paint paint) { 1138 1139 double yy = getRangeAxis().valueToJava2D(value, dataArea, 1140 RectangleEdge.LEFT); 1141 Line2D line = new Line2D.Double(dataArea.getMinX(), yy, 1142 dataArea.getMaxX(), yy); 1143 g2.setStroke(stroke); 1144 g2.setPaint(paint); 1145 g2.draw(line); 1146 1147 } 1148 1149 /** 1150 * Handles a 'click' on the plot by updating the anchor values... 1151 * 1152 * @param x x-coordinate, where the click occured. 1153 * @param y y-coordinate, where the click occured. 1154 * @param info An object for collection dimension information. 1155 */ 1156 @Override handleClick(int x, int y, PlotRenderingInfo info)1157 public void handleClick(int x, int y, PlotRenderingInfo info) { 1158 1159 /* // set the anchor value for the horizontal axis... 1160 ValueAxis hva = getDomainAxis(); 1161 if (hva != null) { 1162 double hvalue = hva.translateJava2DtoValue( 1163 (float) x, info.getDataArea() 1164 ); 1165 1166 hva.setAnchorValue(hvalue); 1167 setDomainCrosshairValue(hvalue); 1168 } 1169 1170 // set the anchor value for the vertical axis... 1171 ValueAxis vva = getRangeAxis(); 1172 if (vva != null) { 1173 double vvalue = vva.translateJava2DtoValue( 1174 (float) y, info.getDataArea() 1175 ); 1176 vva.setAnchorValue(vvalue); 1177 setRangeCrosshairValue(vvalue); 1178 } 1179 */ 1180 } 1181 1182 /** 1183 * Zooms the axis ranges by the specified percentage about the anchor point. 1184 * 1185 * @param percent The amount of the zoom. 1186 */ 1187 @Override zoom(double percent)1188 public void zoom(double percent) { 1189 1190 if (percent > 0) { 1191 // double range = this.domainAxis.getRange().getLength(); 1192 // double scaledRange = range * percent; 1193 // domainAxis.setAnchoredRange(scaledRange); 1194 1195 // range = this.rangeAxis.getRange().getLength(); 1196 // scaledRange = range * percent; 1197 // rangeAxis.setAnchoredRange(scaledRange); 1198 } 1199 else { 1200 getRangeAxis().setAutoRange(true); 1201 getDomainAxis().setAutoRange(true); 1202 } 1203 1204 } 1205 1206 /** 1207 * Returns the plot type as a string. 1208 * 1209 * @return A short string describing the type of plot. 1210 */ 1211 @Override getPlotType()1212 public String getPlotType() { 1213 return localizationResources.getString("Contour_Plot"); 1214 } 1215 1216 /** 1217 * Returns the range for an axis. 1218 * 1219 * @param axis the axis. 1220 * 1221 * @return The range for an axis. 1222 */ 1223 @Override getDataRange(ValueAxis axis)1224 public Range getDataRange(ValueAxis axis) { 1225 1226 if (this.dataset == null) { 1227 return null; 1228 } 1229 1230 Range result = null; 1231 1232 if (axis == getDomainAxis()) { 1233 result = DatasetUtilities.findDomainBounds(this.dataset); 1234 } 1235 else if (axis == getRangeAxis()) { 1236 result = DatasetUtilities.findRangeBounds(this.dataset); 1237 } 1238 return result; 1239 } 1240 1241 /** 1242 * Returns the range for the Contours. 1243 * 1244 * @return The range for the Contours (z-axis). 1245 */ 1246 @Override getContourDataRange()1247 public Range getContourDataRange() { 1248 Range result = null; 1249 ContourDataset data = getDataset(); 1250 if (data != null) { 1251 Range h = getDomainAxis().getRange(); 1252 Range v = getRangeAxis().getRange(); 1253 result = this.visibleRange(data, h, v); 1254 } 1255 return result; 1256 } 1257 1258 /** 1259 * Notifies all registered listeners of a property change. 1260 * <P> 1261 * One source of property change events is the plot's renderer. 1262 * 1263 * @param event Information about the property change. 1264 */ 1265 @Override propertyChange(PropertyChangeEvent event)1266 public void propertyChange(PropertyChangeEvent event) { 1267 fireChangeEvent(); 1268 } 1269 1270 /** 1271 * Receives notification of a change to the plot's dataset. 1272 * <P> 1273 * The chart reacts by passing on a chart change event to all registered 1274 * listeners. 1275 * 1276 * @param event Information about the event (not used here). 1277 */ 1278 @Override datasetChanged(DatasetChangeEvent event)1279 public void datasetChanged(DatasetChangeEvent event) { 1280 if (this.domainAxis != null) { 1281 this.domainAxis.configure(); 1282 } 1283 if (this.rangeAxis != null) { 1284 this.rangeAxis.configure(); 1285 } 1286 if (this.colorBar != null) { 1287 this.colorBar.configure(this); 1288 } 1289 super.datasetChanged(event); 1290 } 1291 1292 /** 1293 * Returns the colorbar. 1294 * 1295 * @return The colorbar. 1296 */ getColorBar()1297 public ColorBar getColorBar() { 1298 return this.colorBar; 1299 } 1300 1301 /** 1302 * Returns a flag indicating whether or not the domain crosshair is visible. 1303 * 1304 * @return The flag. 1305 */ isDomainCrosshairVisible()1306 public boolean isDomainCrosshairVisible() { 1307 return this.domainCrosshairVisible; 1308 } 1309 1310 /** 1311 * Sets the flag indicating whether or not the domain crosshair is visible. 1312 * 1313 * @param flag the new value of the flag. 1314 */ setDomainCrosshairVisible(boolean flag)1315 public void setDomainCrosshairVisible(boolean flag) { 1316 1317 if (this.domainCrosshairVisible != flag) { 1318 this.domainCrosshairVisible = flag; 1319 fireChangeEvent(); 1320 } 1321 1322 } 1323 1324 /** 1325 * Returns a flag indicating whether or not the crosshair should "lock-on" 1326 * to actual data values. 1327 * 1328 * @return The flag. 1329 */ isDomainCrosshairLockedOnData()1330 public boolean isDomainCrosshairLockedOnData() { 1331 return this.domainCrosshairLockedOnData; 1332 } 1333 1334 /** 1335 * Sets the flag indicating whether or not the domain crosshair should 1336 * "lock-on" to actual data values. 1337 * 1338 * @param flag the flag. 1339 */ setDomainCrosshairLockedOnData(boolean flag)1340 public void setDomainCrosshairLockedOnData(boolean flag) { 1341 if (this.domainCrosshairLockedOnData != flag) { 1342 this.domainCrosshairLockedOnData = flag; 1343 fireChangeEvent(); 1344 } 1345 } 1346 1347 /** 1348 * Returns the domain crosshair value. 1349 * 1350 * @return The value. 1351 */ getDomainCrosshairValue()1352 public double getDomainCrosshairValue() { 1353 return this.domainCrosshairValue; 1354 } 1355 1356 /** 1357 * Sets the domain crosshair value. 1358 * <P> 1359 * Registered listeners are notified that the plot has been modified, but 1360 * only if the crosshair is visible. 1361 * 1362 * @param value the new value. 1363 */ setDomainCrosshairValue(double value)1364 public void setDomainCrosshairValue(double value) { 1365 setDomainCrosshairValue(value, true); 1366 } 1367 1368 /** 1369 * Sets the domain crosshair value. 1370 * <P> 1371 * Registered listeners are notified that the axis has been modified, but 1372 * only if the crosshair is visible. 1373 * 1374 * @param value the new value. 1375 * @param notify a flag that controls whether or not listeners are 1376 * notified. 1377 */ setDomainCrosshairValue(double value, boolean notify)1378 public void setDomainCrosshairValue(double value, boolean notify) { 1379 this.domainCrosshairValue = value; 1380 if (isDomainCrosshairVisible() && notify) { 1381 fireChangeEvent(); 1382 } 1383 } 1384 1385 /** 1386 * Returns the Stroke used to draw the crosshair (if visible). 1387 * 1388 * @return The crosshair stroke. 1389 */ getDomainCrosshairStroke()1390 public Stroke getDomainCrosshairStroke() { 1391 return this.domainCrosshairStroke; 1392 } 1393 1394 /** 1395 * Sets the Stroke used to draw the crosshairs (if visible) and notifies 1396 * registered listeners that the axis has been modified. 1397 * 1398 * @param stroke the new crosshair stroke. 1399 */ setDomainCrosshairStroke(Stroke stroke)1400 public void setDomainCrosshairStroke(Stroke stroke) { 1401 this.domainCrosshairStroke = stroke; 1402 fireChangeEvent(); 1403 } 1404 1405 /** 1406 * Returns the domain crosshair color. 1407 * 1408 * @return The crosshair color. 1409 */ getDomainCrosshairPaint()1410 public Paint getDomainCrosshairPaint() { 1411 return this.domainCrosshairPaint; 1412 } 1413 1414 /** 1415 * Sets the Paint used to color the crosshairs (if visible) and notifies 1416 * registered listeners that the axis has been modified. 1417 * 1418 * @param paint the new crosshair paint. 1419 */ setDomainCrosshairPaint(Paint paint)1420 public void setDomainCrosshairPaint(Paint paint) { 1421 this.domainCrosshairPaint = paint; 1422 fireChangeEvent(); 1423 } 1424 1425 /** 1426 * Returns a flag indicating whether or not the range crosshair is visible. 1427 * 1428 * @return The flag. 1429 */ isRangeCrosshairVisible()1430 public boolean isRangeCrosshairVisible() { 1431 return this.rangeCrosshairVisible; 1432 } 1433 1434 /** 1435 * Sets the flag indicating whether or not the range crosshair is visible. 1436 * 1437 * @param flag the new value of the flag. 1438 */ setRangeCrosshairVisible(boolean flag)1439 public void setRangeCrosshairVisible(boolean flag) { 1440 if (this.rangeCrosshairVisible != flag) { 1441 this.rangeCrosshairVisible = flag; 1442 fireChangeEvent(); 1443 } 1444 } 1445 1446 /** 1447 * Returns a flag indicating whether or not the crosshair should "lock-on" 1448 * to actual data values. 1449 * 1450 * @return The flag. 1451 */ isRangeCrosshairLockedOnData()1452 public boolean isRangeCrosshairLockedOnData() { 1453 return this.rangeCrosshairLockedOnData; 1454 } 1455 1456 /** 1457 * Sets the flag indicating whether or not the range crosshair should 1458 * "lock-on" to actual data values. 1459 * 1460 * @param flag the flag. 1461 */ setRangeCrosshairLockedOnData(boolean flag)1462 public void setRangeCrosshairLockedOnData(boolean flag) { 1463 if (this.rangeCrosshairLockedOnData != flag) { 1464 this.rangeCrosshairLockedOnData = flag; 1465 fireChangeEvent(); 1466 } 1467 } 1468 1469 /** 1470 * Returns the range crosshair value. 1471 * 1472 * @return The value. 1473 */ getRangeCrosshairValue()1474 public double getRangeCrosshairValue() { 1475 return this.rangeCrosshairValue; 1476 } 1477 1478 /** 1479 * Sets the domain crosshair value. 1480 * <P> 1481 * Registered listeners are notified that the plot has been modified, but 1482 * only if the crosshair is visible. 1483 * 1484 * @param value the new value. 1485 */ setRangeCrosshairValue(double value)1486 public void setRangeCrosshairValue(double value) { 1487 setRangeCrosshairValue(value, true); 1488 } 1489 1490 /** 1491 * Sets the range crosshair value. 1492 * <P> 1493 * Registered listeners are notified that the axis has been modified, but 1494 * only if the crosshair is visible. 1495 * 1496 * @param value the new value. 1497 * @param notify a flag that controls whether or not listeners are 1498 * notified. 1499 */ setRangeCrosshairValue(double value, boolean notify)1500 public void setRangeCrosshairValue(double value, boolean notify) { 1501 this.rangeCrosshairValue = value; 1502 if (isRangeCrosshairVisible() && notify) { 1503 fireChangeEvent(); 1504 } 1505 } 1506 1507 /** 1508 * Returns the Stroke used to draw the crosshair (if visible). 1509 * 1510 * @return The crosshair stroke. 1511 */ getRangeCrosshairStroke()1512 public Stroke getRangeCrosshairStroke() { 1513 return this.rangeCrosshairStroke; 1514 } 1515 1516 /** 1517 * Sets the Stroke used to draw the crosshairs (if visible) and notifies 1518 * registered listeners that the axis has been modified. 1519 * 1520 * @param stroke the new crosshair stroke. 1521 */ setRangeCrosshairStroke(Stroke stroke)1522 public void setRangeCrosshairStroke(Stroke stroke) { 1523 this.rangeCrosshairStroke = stroke; 1524 fireChangeEvent(); 1525 } 1526 1527 /** 1528 * Returns the range crosshair color. 1529 * 1530 * @return The crosshair color. 1531 */ getRangeCrosshairPaint()1532 public Paint getRangeCrosshairPaint() { 1533 return this.rangeCrosshairPaint; 1534 } 1535 1536 /** 1537 * Sets the Paint used to color the crosshairs (if visible) and notifies 1538 * registered listeners that the axis has been modified. 1539 * 1540 * @param paint the new crosshair paint. 1541 */ setRangeCrosshairPaint(Paint paint)1542 public void setRangeCrosshairPaint(Paint paint) { 1543 this.rangeCrosshairPaint = paint; 1544 fireChangeEvent(); 1545 } 1546 1547 /** 1548 * Returns the tool tip generator. 1549 * 1550 * @return The tool tip generator (possibly null). 1551 */ getToolTipGenerator()1552 public ContourToolTipGenerator getToolTipGenerator() { 1553 return this.toolTipGenerator; 1554 } 1555 1556 /** 1557 * Sets the tool tip generator. 1558 * 1559 * @param generator the tool tip generator (null permitted). 1560 */ setToolTipGenerator(ContourToolTipGenerator generator)1561 public void setToolTipGenerator(ContourToolTipGenerator generator) { 1562 //Object oldValue = this.toolTipGenerator; 1563 this.toolTipGenerator = generator; 1564 } 1565 1566 /** 1567 * Returns the URL generator for HTML image maps. 1568 * 1569 * @return The URL generator (possibly null). 1570 */ getURLGenerator()1571 public XYURLGenerator getURLGenerator() { 1572 return this.urlGenerator; 1573 } 1574 1575 /** 1576 * Sets the URL generator for HTML image maps. 1577 * 1578 * @param urlGenerator the URL generator (null permitted). 1579 */ setURLGenerator(XYURLGenerator urlGenerator)1580 public void setURLGenerator(XYURLGenerator urlGenerator) { 1581 //Object oldValue = this.urlGenerator; 1582 this.urlGenerator = urlGenerator; 1583 } 1584 1585 /** 1586 * Draws a vertical line on the chart to represent a 'range marker'. 1587 * 1588 * @param g2 the graphics device. 1589 * @param plot the plot. 1590 * @param domainAxis the domain axis. 1591 * @param marker the marker line. 1592 * @param dataArea the axis data area. 1593 */ drawDomainMarker(Graphics2D g2, ContourPlot plot, ValueAxis domainAxis, Marker marker, Rectangle2D dataArea)1594 public void drawDomainMarker(Graphics2D g2, 1595 ContourPlot plot, 1596 ValueAxis domainAxis, 1597 Marker marker, 1598 Rectangle2D dataArea) { 1599 1600 if (marker instanceof ValueMarker) { 1601 ValueMarker vm = (ValueMarker) marker; 1602 double value = vm.getValue(); 1603 Range range = domainAxis.getRange(); 1604 if (!range.contains(value)) { 1605 return; 1606 } 1607 1608 double x = domainAxis.valueToJava2D(value, dataArea, 1609 RectangleEdge.BOTTOM); 1610 Line2D line = new Line2D.Double(x, dataArea.getMinY(), x, 1611 dataArea.getMaxY()); 1612 Paint paint = marker.getOutlinePaint(); 1613 Stroke stroke = marker.getOutlineStroke(); 1614 g2.setPaint(paint != null ? paint : Plot.DEFAULT_OUTLINE_PAINT); 1615 g2.setStroke(stroke != null ? stroke : Plot.DEFAULT_OUTLINE_STROKE); 1616 g2.draw(line); 1617 } 1618 1619 } 1620 1621 /** 1622 * Draws a horizontal line across the chart to represent a 'range marker'. 1623 * 1624 * @param g2 the graphics device. 1625 * @param plot the plot. 1626 * @param rangeAxis the range axis. 1627 * @param marker the marker line. 1628 * @param dataArea the axis data area. 1629 */ drawRangeMarker(Graphics2D g2, ContourPlot plot, ValueAxis rangeAxis, Marker marker, Rectangle2D dataArea)1630 public void drawRangeMarker(Graphics2D g2, 1631 ContourPlot plot, 1632 ValueAxis rangeAxis, 1633 Marker marker, 1634 Rectangle2D dataArea) { 1635 1636 if (marker instanceof ValueMarker) { 1637 ValueMarker vm = (ValueMarker) marker; 1638 double value = vm.getValue(); 1639 Range range = rangeAxis.getRange(); 1640 if (!range.contains(value)) { 1641 return; 1642 } 1643 1644 double y = rangeAxis.valueToJava2D(value, dataArea, 1645 RectangleEdge.LEFT); 1646 Line2D line = new Line2D.Double(dataArea.getMinX(), y, 1647 dataArea.getMaxX(), y); 1648 Paint paint = marker.getOutlinePaint(); 1649 Stroke stroke = marker.getOutlineStroke(); 1650 g2.setPaint(paint != null ? paint : Plot.DEFAULT_OUTLINE_PAINT); 1651 g2.setStroke(stroke != null ? stroke : Plot.DEFAULT_OUTLINE_STROKE); 1652 g2.draw(line); 1653 } 1654 1655 } 1656 1657 /** 1658 * Returns the clipPath. 1659 * @return ClipPath 1660 */ getClipPath()1661 public ClipPath getClipPath() { 1662 return this.clipPath; 1663 } 1664 1665 /** 1666 * Sets the clipPath. 1667 * @param clipPath The clipPath to set 1668 */ setClipPath(ClipPath clipPath)1669 public void setClipPath(ClipPath clipPath) { 1670 this.clipPath = clipPath; 1671 } 1672 1673 /** 1674 * Returns the ptSizePct. 1675 * @return double 1676 */ getPtSizePct()1677 public double getPtSizePct() { 1678 return this.ptSizePct; 1679 } 1680 1681 /** 1682 * Returns the renderAsPoints. 1683 * @return boolean 1684 */ isRenderAsPoints()1685 public boolean isRenderAsPoints() { 1686 return this.renderAsPoints; 1687 } 1688 1689 /** 1690 * Sets the ptSizePct. 1691 * @param ptSizePct The ptSizePct to set 1692 */ setPtSizePct(double ptSizePct)1693 public void setPtSizePct(double ptSizePct) { 1694 this.ptSizePct = ptSizePct; 1695 } 1696 1697 /** 1698 * Sets the renderAsPoints. 1699 * @param renderAsPoints The renderAsPoints to set 1700 */ setRenderAsPoints(boolean renderAsPoints)1701 public void setRenderAsPoints(boolean renderAsPoints) { 1702 this.renderAsPoints = renderAsPoints; 1703 } 1704 1705 /** 1706 * Receives notification of a change to one of the plot's axes. 1707 * 1708 * @param event information about the event. 1709 */ 1710 @Override axisChanged(AxisChangeEvent event)1711 public void axisChanged(AxisChangeEvent event) { 1712 Object source = event.getSource(); 1713 if (source.equals(this.rangeAxis) || source.equals(this.domainAxis)) { 1714 ColorBar cba = this.colorBar; 1715 if (this.colorBar.getAxis().isAutoRange()) { 1716 cba.getAxis().configure(); 1717 } 1718 1719 } 1720 super.axisChanged(event); 1721 } 1722 1723 /** 1724 * Returns the visible z-range. 1725 * 1726 * @param data the dataset. 1727 * @param x the x range. 1728 * @param y the y range. 1729 * 1730 * @return The range. 1731 */ visibleRange(ContourDataset data, Range x, Range y)1732 public Range visibleRange(ContourDataset data, Range x, Range y) { 1733 Range range = data.getZValueRange(x, y); 1734 return range; 1735 } 1736 1737 /** 1738 * Returns the missingPaint. 1739 * @return Paint 1740 */ getMissingPaint()1741 public Paint getMissingPaint() { 1742 return this.missingPaint; 1743 } 1744 1745 /** 1746 * Sets the missingPaint. 1747 * 1748 * @param paint the missingPaint to set. 1749 */ setMissingPaint(Paint paint)1750 public void setMissingPaint(Paint paint) { 1751 this.missingPaint = paint; 1752 } 1753 1754 /** 1755 * Multiplies the range on the domain axis/axes by the specified factor 1756 * (to be implemented). 1757 * 1758 * @param x the x-coordinate (in Java2D space). 1759 * @param y the y-coordinate (in Java2D space). 1760 * @param factor the zoom factor. 1761 */ zoomDomainAxes(double x, double y, double factor)1762 public void zoomDomainAxes(double x, double y, double factor) { 1763 // TODO: to be implemented 1764 } 1765 1766 /** 1767 * Zooms the domain axes (not yet implemented). 1768 * 1769 * @param x the x-coordinate (in Java2D space). 1770 * @param y the y-coordinate (in Java2D space). 1771 * @param lowerPercent the new lower bound. 1772 * @param upperPercent the new upper bound. 1773 */ zoomDomainAxes(double x, double y, double lowerPercent, double upperPercent)1774 public void zoomDomainAxes(double x, double y, double lowerPercent, 1775 double upperPercent) { 1776 // TODO: to be implemented 1777 } 1778 1779 /** 1780 * Multiplies the range on the range axis/axes by the specified factor. 1781 * 1782 * @param x the x-coordinate (in Java2D space). 1783 * @param y the y-coordinate (in Java2D space). 1784 * @param factor the zoom factor. 1785 */ zoomRangeAxes(double x, double y, double factor)1786 public void zoomRangeAxes(double x, double y, double factor) { 1787 // TODO: to be implemented 1788 } 1789 1790 /** 1791 * Zooms the range axes (not yet implemented). 1792 * 1793 * @param x the x-coordinate (in Java2D space). 1794 * @param y the y-coordinate (in Java2D space). 1795 * @param lowerPercent the new lower bound. 1796 * @param upperPercent the new upper bound. 1797 */ zoomRangeAxes(double x, double y, double lowerPercent, double upperPercent)1798 public void zoomRangeAxes(double x, double y, double lowerPercent, 1799 double upperPercent) { 1800 // TODO: to be implemented 1801 } 1802 1803 /** 1804 * Returns <code>false</code>. 1805 * 1806 * @return A boolean. 1807 */ isDomainZoomable()1808 public boolean isDomainZoomable() { 1809 return false; 1810 } 1811 1812 /** 1813 * Returns <code>false</code>. 1814 * 1815 * @return A boolean. 1816 */ isRangeZoomable()1817 public boolean isRangeZoomable() { 1818 return false; 1819 } 1820 1821 /** 1822 * Extends plot cloning to this plot type 1823 * @see org.jfree.chart.plot.Plot#clone() 1824 */ 1825 @Override clone()1826 public Object clone() throws CloneNotSupportedException { 1827 ContourPlot clone = (ContourPlot) super.clone(); 1828 1829 if (this.domainAxis != null) { 1830 clone.domainAxis = (ValueAxis) this.domainAxis.clone(); 1831 clone.domainAxis.setPlot(clone); 1832 clone.domainAxis.addChangeListener(clone); 1833 } 1834 if (this.rangeAxis != null) { 1835 clone.rangeAxis = (ValueAxis) this.rangeAxis.clone(); 1836 clone.rangeAxis.setPlot(clone); 1837 clone.rangeAxis.addChangeListener(clone); 1838 } 1839 1840 if (clone.dataset != null) { 1841 clone.dataset.addChangeListener(clone); 1842 } 1843 1844 if (this.colorBar != null) { 1845 clone.colorBar = (ColorBar) this.colorBar.clone(); 1846 } 1847 1848 clone.domainMarkers = (List) ObjectUtilities.deepClone( 1849 this.domainMarkers); 1850 clone.rangeMarkers = (List) ObjectUtilities.deepClone( 1851 this.rangeMarkers); 1852 clone.annotations = (List) ObjectUtilities.deepClone(this.annotations); 1853 1854 if (this.clipPath != null) { 1855 clone.clipPath = (ClipPath) this.clipPath.clone(); 1856 } 1857 1858 return clone; 1859 } 1860 1861 } 1862