Source for org.jfree.chart.plot.ContourPlot

   1: /* ===========================================================
   2:  * JFreeChart : a free chart library for the Java(tm) platform
   3:  * ===========================================================
   4:  *
   5:  * (C) Copyright 2000-2007, by Object Refinery Limited and Contributors.
   6:  *
   7:  * Project Info:  http://www.jfree.org/jfreechart/index.html
   8:  *
   9:  * This library is free software; you can redistribute it and/or modify it 
  10:  * under the terms of the GNU Lesser General Public License as published by 
  11:  * the Free Software Foundation; either version 2.1 of the License, or 
  12:  * (at your option) any later version.
  13:  *
  14:  * This library is distributed in the hope that it will be useful, but 
  15:  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
  16:  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 
  17:  * License for more details.
  18:  *
  19:  * You should have received a copy of the GNU Lesser General Public
  20:  * License along with this library; if not, write to the Free Software
  21:  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, 
  22:  * USA.  
  23:  *
  24:  * [Java is a trademark or registered trademark of Sun Microsystems, Inc. 
  25:  * in the United States and other countries.]
  26:  *
  27:  * ----------------
  28:  * ContourPlot.java
  29:  * ----------------
  30:  * (C) Copyright 2002-2007, 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:  * 
  66:  */
  67: 
  68: package org.jfree.chart.plot;
  69: 
  70: import java.awt.AlphaComposite;
  71: import java.awt.Composite;
  72: import java.awt.Graphics2D;
  73: import java.awt.Paint;
  74: import java.awt.RenderingHints;
  75: import java.awt.Shape;
  76: import java.awt.Stroke;
  77: import java.awt.geom.Ellipse2D;
  78: import java.awt.geom.GeneralPath;
  79: import java.awt.geom.Line2D;
  80: import java.awt.geom.Point2D;
  81: import java.awt.geom.Rectangle2D;
  82: import java.awt.geom.RectangularShape;
  83: import java.beans.PropertyChangeEvent;
  84: import java.beans.PropertyChangeListener;
  85: import java.io.Serializable;
  86: import java.util.Iterator;
  87: import java.util.List;
  88: import java.util.ResourceBundle;
  89: 
  90: import org.jfree.chart.ClipPath;
  91: import org.jfree.chart.annotations.XYAnnotation;
  92: import org.jfree.chart.axis.AxisSpace;
  93: import org.jfree.chart.axis.ColorBar;
  94: import org.jfree.chart.axis.NumberAxis;
  95: import org.jfree.chart.axis.ValueAxis;
  96: import org.jfree.chart.entity.ContourEntity;
  97: import org.jfree.chart.entity.EntityCollection;
  98: import org.jfree.chart.event.AxisChangeEvent;
  99: import org.jfree.chart.event.PlotChangeEvent;
 100: import org.jfree.chart.labels.ContourToolTipGenerator;
 101: import org.jfree.chart.labels.StandardContourToolTipGenerator;
 102: import org.jfree.chart.renderer.xy.XYBlockRenderer;
 103: import org.jfree.chart.urls.XYURLGenerator;
 104: import org.jfree.data.Range;
 105: import org.jfree.data.contour.ContourDataset;
 106: import org.jfree.data.general.DatasetChangeEvent;
 107: import org.jfree.data.general.DatasetUtilities;
 108: import org.jfree.ui.RectangleEdge;
 109: import org.jfree.ui.RectangleInsets;
 110: import org.jfree.util.ObjectUtilities;
 111: 
 112: /**
 113:  * A class for creating shaded contours.
 114:  * 
 115:  * @deprecated This plot is no longer supported, please use {@link XYPlot} with
 116:  *     an {@link XYBlockRenderer}.
 117:  */
 118: public class ContourPlot extends Plot implements ContourValuePlot,
 119:                                                  ValueAxisPlot,
 120:                                                  PropertyChangeListener,
 121:                                                  Serializable,
 122:                                                  Cloneable {
 123: 
 124:     /** For serialization. */
 125:     private static final long serialVersionUID = 7861072556590502247L;
 126:     
 127:     /** The default insets. */
 128:     protected static final RectangleInsets DEFAULT_INSETS 
 129:         = new RectangleInsets(2.0, 2.0, 100.0, 10.0);
 130: 
 131:     /** The domain axis (used for the x-values). */
 132:     private ValueAxis domainAxis;
 133: 
 134:     /** The range axis (used for the y-values). */
 135:     private ValueAxis rangeAxis;
 136: 
 137:     /** The dataset. */
 138:     private ContourDataset dataset;
 139:     
 140:     /** The colorbar axis (used for the z-values). */
 141:     private ColorBar colorBar = null;
 142: 
 143:     /** The color bar location. */
 144:     private RectangleEdge colorBarLocation;
 145:     
 146:     /** A flag that controls whether or not a domain crosshair is drawn..*/
 147:     private boolean domainCrosshairVisible;
 148: 
 149:     /** The domain crosshair value. */
 150:     private double domainCrosshairValue;
 151: 
 152:     /** The pen/brush used to draw the crosshair (if any). */
 153:     private transient Stroke domainCrosshairStroke;
 154: 
 155:     /** The color used to draw the crosshair (if any). */
 156:     private transient Paint domainCrosshairPaint;
 157: 
 158:     /** 
 159:      * A flag that controls whether or not the crosshair locks onto actual data
 160:      * points. 
 161:      */
 162:     private boolean domainCrosshairLockedOnData = true;
 163: 
 164:     /** A flag that controls whether or not a range crosshair is drawn..*/
 165:     private boolean rangeCrosshairVisible;
 166: 
 167:     /** The range crosshair value. */
 168:     private double rangeCrosshairValue;
 169: 
 170:     /** The pen/brush used to draw the crosshair (if any). */
 171:     private transient Stroke rangeCrosshairStroke;
 172: 
 173:     /** The color used to draw the crosshair (if any). */
 174:     private transient Paint rangeCrosshairPaint;
 175: 
 176:     /** 
 177:      * A flag that controls whether or not the crosshair locks onto actual data
 178:      * points. 
 179:      */
 180:     private boolean rangeCrosshairLockedOnData = true;
 181: 
 182:     /** 
 183:      * Defines dataArea rectangle as the ratio formed from dividing height by 
 184:      * width (of the dataArea).  Modifies plot area calculations.
 185:      * ratio>0 will attempt to layout the plot so that the
 186:      * dataArea.height/dataArea.width = ratio.
 187:      * ratio<0 will attempt to layout the plot so that the
 188:      * dataArea.height/dataArea.width in plot units (not java2D units as when 
 189:      * ratio>0) = -1.*ratio.
 190:      */         //dmo
 191:     private double dataAreaRatio = 0.0;  //zero when the parameter is not set
 192: 
 193:     /** A list of markers (optional) for the domain axis. */
 194:     private List domainMarkers;
 195: 
 196:     /** A list of markers (optional) for the range axis. */
 197:     private List rangeMarkers;
 198: 
 199:     /** A list of annotations (optional) for the plot. */
 200:     private List annotations;
 201: 
 202:     /** The tool tip generator. */
 203:     private ContourToolTipGenerator toolTipGenerator;
 204: 
 205:     /** The URL text generator. */
 206:     private XYURLGenerator urlGenerator;
 207: 
 208:     /** 
 209:      * Controls whether data are render as filled rectangles or rendered as 
 210:      * points 
 211:      */
 212:     private boolean renderAsPoints = false;
 213: 
 214:     /** 
 215:      * Size of points rendered when renderAsPoints = true.  Size is relative to
 216:      * dataArea 
 217:      */
 218:     private double ptSizePct = 0.05;
 219: 
 220:     /** Contains the a ClipPath to "trim" the contours. */
 221:     private transient ClipPath clipPath = null;
 222: 
 223:     /** Set to Paint to represent missing values. */
 224:     private transient Paint missingPaint = null;
 225: 
 226:     /** The resourceBundle for the localization. */
 227:     protected static ResourceBundle localizationResources = 
 228:         ResourceBundle.getBundle("org.jfree.chart.plot.LocalizationBundle");
 229: 
 230:     /**
 231:      * Creates a new plot with no dataset or axes.
 232:      */
 233:     public ContourPlot() {
 234:         this(null, null, null, null);
 235:     }
 236:     
 237:     /**
 238:      * Constructs a contour plot with the specified axes (other attributes take
 239:      * default values).
 240:      *
 241:      * @param dataset  The dataset.
 242:      * @param domainAxis  The domain axis.
 243:      * @param rangeAxis  The range axis.
 244:      * @param colorBar  The z-axis axis.
 245:     */
 246:     public ContourPlot(ContourDataset dataset,
 247:                        ValueAxis domainAxis, ValueAxis rangeAxis, 
 248:                        ColorBar colorBar) {
 249: 
 250:         super();
 251: 
 252:         this.dataset = dataset;
 253:         if (dataset != null) {
 254:             dataset.addChangeListener(this);
 255:         }
 256:         
 257:         this.domainAxis = domainAxis;
 258:         if (domainAxis != null) {
 259:             domainAxis.setPlot(this);
 260:             domainAxis.addChangeListener(this);
 261:         }
 262: 
 263:         this.rangeAxis = rangeAxis;
 264:         if (rangeAxis != null) {
 265:             rangeAxis.setPlot(this);
 266:             rangeAxis.addChangeListener(this);
 267:         }
 268: 
 269:         this.colorBar = colorBar;
 270:         if (colorBar != null) {
 271:             colorBar.getAxis().setPlot(this);
 272:             colorBar.getAxis().addChangeListener(this);
 273:             colorBar.configure(this);
 274:         }
 275:         this.colorBarLocation = RectangleEdge.LEFT;
 276: 
 277:         this.toolTipGenerator = new StandardContourToolTipGenerator();
 278: 
 279:     }
 280: 
 281:     /**
 282:      * Returns the color bar location.
 283:      * 
 284:      * @return The color bar location.
 285:      */
 286:     public RectangleEdge getColorBarLocation() {
 287:         return this.colorBarLocation;
 288:     }
 289:     
 290:     /**
 291:      * Sets the color bar location and sends a {@link PlotChangeEvent} to all 
 292:      * registered listeners.
 293:      * 
 294:      * @param edge  the location.
 295:      */
 296:     public void setColorBarLocation(RectangleEdge edge) {
 297:         this.colorBarLocation = edge;
 298:         notifyListeners(new PlotChangeEvent(this));    
 299:     }
 300:     
 301:     /**
 302:      * Returns the primary dataset for the plot.
 303:      * 
 304:      * @return The primary dataset (possibly <code>null</code>).
 305:      */
 306:     public ContourDataset getDataset() {
 307:         return this.dataset;
 308:     }
 309:     
 310:     /**
 311:      * Sets the dataset for the plot, replacing the existing dataset if there
 312:      * is one.
 313:      * 
 314:      * @param dataset  the dataset (<code>null</code> permitted).
 315:      */
 316:     public void setDataset(ContourDataset dataset) {
 317:         
 318:         // if there is an existing dataset, remove the plot from the list of 
 319:         // change listeners...
 320:         ContourDataset existing = this.dataset;
 321:         if (existing != null) {
 322:             existing.removeChangeListener(this);
 323:         }
 324: 
 325:         // set the new dataset, and register the chart as a change listener...
 326:         this.dataset = dataset;
 327:         if (dataset != null) {
 328:             setDatasetGroup(dataset.getGroup());
 329:             dataset.addChangeListener(this);
 330:         }
 331: 
 332:         // send a dataset change event to self...
 333:         DatasetChangeEvent event = new DatasetChangeEvent(this, dataset);
 334:         datasetChanged(event);
 335:         
 336:     }
 337: 
 338:     /**
 339:      * Returns the domain axis for the plot.
 340:      *
 341:      * @return The domain axis.
 342:      */
 343:     public ValueAxis getDomainAxis() {
 344: 
 345:         ValueAxis result = this.domainAxis;
 346: 
 347:         return result;
 348: 
 349:     }
 350: 
 351:     /**
 352:      * Sets the domain axis for the plot (this must be compatible with the plot
 353:      * type or an exception is thrown).
 354:      *
 355:      * @param axis The new axis.
 356:      */
 357:     public void setDomainAxis(ValueAxis axis) {
 358: 
 359:         if (isCompatibleDomainAxis(axis)) {
 360: 
 361:             if (axis != null) {
 362:                 axis.setPlot(this);
 363:                 axis.addChangeListener(this);
 364:             }
 365: 
 366:             // plot is likely registered as a listener with the existing axis...
 367:             if (this.domainAxis != null) {
 368:                 this.domainAxis.removeChangeListener(this);
 369:             }
 370: 
 371:             this.domainAxis = axis;
 372:             notifyListeners(new PlotChangeEvent(this));
 373: 
 374:         }
 375: 
 376:     }
 377: 
 378:     /**
 379:      * Returns the range axis for the plot.
 380:      *
 381:      * @return The range axis.
 382:      */
 383:     public ValueAxis getRangeAxis() {
 384: 
 385:         ValueAxis result = this.rangeAxis;
 386: 
 387:         return result;
 388: 
 389:     }
 390: 
 391:     /**
 392:      * Sets the range axis for the plot.
 393:      * <P>
 394:      * An exception is thrown if the new axis and the plot are not mutually
 395:      * compatible.
 396:      *
 397:      * @param axis The new axis (null permitted).
 398:      */
 399:     public void setRangeAxis(ValueAxis axis) {
 400: 
 401:         if (axis != null) {
 402:             axis.setPlot(this);
 403:             axis.addChangeListener(this);
 404:         }
 405: 
 406:         // plot is likely registered as a listener with the existing axis...
 407:         if (this.rangeAxis != null) {
 408:             this.rangeAxis.removeChangeListener(this);
 409:         }
 410: 
 411:         this.rangeAxis = axis;
 412:         notifyListeners(new PlotChangeEvent(this));
 413: 
 414:     }
 415: 
 416:     /**
 417:      * Sets the colorbar for the plot.
 418:      *
 419:      * @param axis The new axis (null permitted).
 420:      */
 421:     public void setColorBarAxis(ColorBar axis) {
 422: 
 423:         this.colorBar = axis;
 424:         notifyListeners(new PlotChangeEvent(this));
 425: 
 426:     }
 427: 
 428:     /**
 429:      * Returns the data area ratio.
 430:      *
 431:      * @return The ratio.
 432:      */
 433:     public double getDataAreaRatio() {
 434:         return this.dataAreaRatio;
 435:     }
 436: 
 437:     /**
 438:      * Sets the data area ratio.
 439:      *
 440:      * @param ratio  the ratio.
 441:      */
 442:     public void setDataAreaRatio(double ratio) {
 443:         this.dataAreaRatio = ratio;
 444:     }
 445: 
 446:     /**
 447:      * Adds a marker for the domain axis.
 448:      * <P>
 449:      * Typically a marker will be drawn by the renderer as a line perpendicular
 450:      * to the range axis, however this is entirely up to the renderer.
 451:      *
 452:      * @param marker the marker.
 453:      */
 454:     public void addDomainMarker(Marker marker) {
 455: 
 456:         if (this.domainMarkers == null) {
 457:             this.domainMarkers = new java.util.ArrayList();
 458:         }
 459:         this.domainMarkers.add(marker);
 460:         notifyListeners(new PlotChangeEvent(this));
 461: 
 462:     }
 463: 
 464:     /**
 465:      * Clears all the domain markers.
 466:      */
 467:     public void clearDomainMarkers() {
 468:         if (this.domainMarkers != null) {
 469:             this.domainMarkers.clear();
 470:             notifyListeners(new PlotChangeEvent(this));
 471:         }
 472:     }
 473: 
 474:     /**
 475:      * Adds a marker for the range axis.
 476:      * <P>
 477:      * Typically a marker will be drawn by the renderer as a line perpendicular
 478:      * to the range axis, however this is entirely up to the renderer.
 479:      *
 480:      * @param marker The marker.
 481:      */
 482:     public void addRangeMarker(Marker marker) {
 483: 
 484:         if (this.rangeMarkers == null) {
 485:             this.rangeMarkers = new java.util.ArrayList();
 486:         }
 487:         this.rangeMarkers.add(marker);
 488:         notifyListeners(new PlotChangeEvent(this));
 489: 
 490:     }
 491: 
 492:     /**
 493:      * Clears all the range markers.
 494:      */
 495:     public void clearRangeMarkers() {
 496:         if (this.rangeMarkers != null) {
 497:             this.rangeMarkers.clear();
 498:             notifyListeners(new PlotChangeEvent(this));
 499:         }
 500:     }
 501: 
 502:     /**
 503:      * Adds an annotation to the plot.
 504:      *
 505:      * @param annotation  the annotation.
 506:      */
 507:     public void addAnnotation(XYAnnotation annotation) {
 508: 
 509:         if (this.annotations == null) {
 510:             this.annotations = new java.util.ArrayList();
 511:         }
 512:         this.annotations.add(annotation);
 513:         notifyListeners(new PlotChangeEvent(this));
 514: 
 515:     }
 516: 
 517:     /**
 518:      * Clears all the annotations.
 519:      */
 520:     public void clearAnnotations() {
 521:         if (this.annotations != null) {
 522:             this.annotations.clear();
 523:             notifyListeners(new PlotChangeEvent(this));
 524:         }
 525:     }
 526: 
 527:     /**
 528:      * Checks the compatibility of a domain axis, returning true if the axis is
 529:      * compatible with the plot, and false otherwise.
 530:      *
 531:      * @param axis The proposed axis.
 532:      *
 533:      * @return <code>true</code> if the axis is compatible with the plot.
 534:      */
 535:     public boolean isCompatibleDomainAxis(ValueAxis axis) {
 536: 
 537:         return true;
 538: 
 539:     }
 540: 
 541:     /**
 542:      * Draws the plot on a Java 2D graphics device (such as the screen or a 
 543:      * printer).
 544:      * <P>
 545:      * The optional <code>info</code> argument collects information about the 
 546:      * rendering of the plot (dimensions, tooltip information etc).  Just pass
 547:      * in <code>null</code> if you do not need this information.
 548:      *
 549:      * @param g2  the graphics device.
 550:      * @param area  the area within which the plot (including axis labels)
 551:      *              should be drawn.
 552:      * @param anchor  the anchor point (<code>null</code> permitted).
 553:      * @param parentState  the state from the parent plot, if there is one.
 554:      * @param info  collects chart drawing information (<code>null</code> 
 555:      *              permitted).
 556:      */
 557:     public void draw(Graphics2D g2, Rectangle2D area, Point2D anchor,
 558:                      PlotState parentState,
 559:                      PlotRenderingInfo info) {
 560: 
 561:         // if the plot area is too small, just return...
 562:         boolean b1 = (area.getWidth() <= MINIMUM_WIDTH_TO_DRAW);
 563:         boolean b2 = (area.getHeight() <= MINIMUM_HEIGHT_TO_DRAW);
 564:         if (b1 || b2) {
 565:             return;
 566:         }
 567: 
 568:         // record the plot area...
 569:         if (info != null) {
 570:             info.setPlotArea(area);
 571:         }
 572: 
 573:         // adjust the drawing area for plot insets (if any)...
 574:         RectangleInsets insets = getInsets();
 575:         insets.trim(area);
 576: 
 577:         AxisSpace space = new AxisSpace();
 578:         
 579:         space = this.domainAxis.reserveSpace(g2, this, area, 
 580:                 RectangleEdge.BOTTOM, space);
 581:         space = this.rangeAxis.reserveSpace(g2, this, area, 
 582:                 RectangleEdge.LEFT, space);
 583: 
 584:         Rectangle2D estimatedDataArea = space.shrink(area, null);
 585:         
 586:         AxisSpace space2 = new AxisSpace();
 587:         space2 = this.colorBar.reserveSpace(g2, this, area, estimatedDataArea, 
 588:                 this.colorBarLocation, space2);
 589:         Rectangle2D adjustedPlotArea = space2.shrink(area, null);
 590:         
 591:         Rectangle2D dataArea = space.shrink(adjustedPlotArea, null);
 592: 
 593:         Rectangle2D colorBarArea = space2.reserved(area, this.colorBarLocation);
 594: 
 595:         // additional dataArea modifications
 596:         if (getDataAreaRatio() != 0.0) { //check whether modification is
 597:             double ratio = getDataAreaRatio();
 598:             Rectangle2D tmpDataArea = (Rectangle2D) dataArea.clone();
 599:             double h = tmpDataArea.getHeight();
 600:             double w = tmpDataArea.getWidth();
 601: 
 602:             if (ratio > 0) { // ratio represents pixels
 603:                 if (w * ratio <= h) {
 604:                     h = ratio * w;
 605:                 }
 606:                 else {
 607:                     w = h / ratio;
 608:                 }
 609:             }
 610:             else {  // ratio represents axis units
 611:                 ratio *= -1.0;
 612:                 double xLength = getDomainAxis().getRange().getLength();
 613:                 double yLength = getRangeAxis().getRange().getLength();
 614:                 double unitRatio = yLength / xLength;
 615: 
 616:                 ratio = unitRatio * ratio;
 617: 
 618:                 if (w * ratio <= h) {
 619:                     h = ratio * w;
 620:                 }
 621:                 else {
 622:                     w = h / ratio;
 623:                 }
 624:             }
 625: 
 626:             dataArea.setRect(tmpDataArea.getX() + tmpDataArea.getWidth() / 2 
 627:                     - w / 2, tmpDataArea.getY(), w, h);
 628:         }
 629: 
 630:         if (info != null) {
 631:             info.setDataArea(dataArea);
 632:         }
 633: 
 634:         CrosshairState crosshairState = new CrosshairState();
 635:         crosshairState.setCrosshairDistance(Double.POSITIVE_INFINITY);
 636: 
 637:         // draw the plot background...
 638:         drawBackground(g2, dataArea);
 639: 
 640:         double cursor = dataArea.getMaxY();
 641:         if (this.domainAxis != null) {
 642:             this.domainAxis.draw(g2, cursor, adjustedPlotArea, dataArea, 
 643:                     RectangleEdge.BOTTOM, info);
 644:         }
 645: 
 646:         if (this.rangeAxis != null) {
 647:             cursor = dataArea.getMinX();
 648:             this.rangeAxis.draw(g2, cursor, adjustedPlotArea, dataArea, 
 649:                     RectangleEdge.LEFT, info);
 650:         }
 651: 
 652:         if (this.colorBar != null) {
 653:             cursor = 0.0;
 654:             cursor = this.colorBar.draw(g2, cursor, adjustedPlotArea, dataArea,
 655:                     colorBarArea, this.colorBarLocation);
 656:         }
 657:         Shape originalClip = g2.getClip();
 658:         Composite originalComposite = g2.getComposite();
 659: 
 660:         g2.clip(dataArea);
 661:         g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 
 662:                 getForegroundAlpha()));
 663:         render(g2, dataArea, info, crosshairState);
 664: 
 665:         if (this.domainMarkers != null) {
 666:             Iterator iterator = this.domainMarkers.iterator();
 667:             while (iterator.hasNext()) {
 668:                 Marker marker = (Marker) iterator.next();
 669:                 drawDomainMarker(g2, this, getDomainAxis(), marker, dataArea);
 670:             }
 671:         }
 672: 
 673:         if (this.rangeMarkers != null) {
 674:             Iterator iterator = this.rangeMarkers.iterator();
 675:             while (iterator.hasNext()) {
 676:                 Marker marker = (Marker) iterator.next();
 677:                 drawRangeMarker(g2, this, getRangeAxis(), marker, dataArea);
 678:             }
 679:         }
 680: 
 681: // TO DO:  these annotations only work with XYPlot, see if it is possible to 
 682: // make ContourPlot a subclass of XYPlot (DG);
 683: 
 684: //        // draw the annotations...
 685: //        if (this.annotations != null) {
 686: //            Iterator iterator = this.annotations.iterator();
 687: //            while (iterator.hasNext()) {
 688: //                Annotation annotation = (Annotation) iterator.next();
 689: //                if (annotation instanceof XYAnnotation) {
 690: //                    XYAnnotation xya = (XYAnnotation) annotation;
 691: //                    // get the annotation to draw itself...
 692: //                    xya.draw(g2, this, dataArea, getDomainAxis(), 
 693: //                             getRangeAxis());
 694: //                }
 695: //            }
 696: //        }
 697: 
 698:         g2.setClip(originalClip);
 699:         g2.setComposite(originalComposite);
 700:         drawOutline(g2, dataArea);
 701: 
 702:     }
 703: 
 704:     /**
 705:      * Draws a representation of the data within the dataArea region, using the
 706:      * current renderer.
 707:      * <P>
 708:      * The <code>info</code> and <code>crosshairState</code> arguments may be 
 709:      * <code>null</code>.
 710:      *
 711:      * @param g2  the graphics device.
 712:      * @param dataArea  the region in which the data is to be drawn.
 713:      * @param info  an optional object for collection dimension information.
 714:      * @param crosshairState  an optional object for collecting crosshair info.
 715:      */
 716:     public void render(Graphics2D g2, Rectangle2D dataArea,
 717:                        PlotRenderingInfo info, CrosshairState crosshairState) {
 718: 
 719:         // now get the data and plot it (the visual representation will depend
 720:         // on the renderer that has been set)...
 721:         ContourDataset data = getDataset();
 722:         if (data != null) {
 723: 
 724:             ColorBar zAxis = getColorBar();
 725: 
 726:             if (this.clipPath != null) {
 727:                 GeneralPath clipper = getClipPath().draw(g2, dataArea, 
 728:                         this.domainAxis, this.rangeAxis);
 729:                 if (this.clipPath.isClip()) {
 730:                     g2.clip(clipper);
 731:                 }
 732:             }
 733: 
 734:             if (this.renderAsPoints) {
 735:                 pointRenderer(g2, dataArea, info, this, this.domainAxis, 
 736:                         this.rangeAxis, zAxis, data, crosshairState);
 737:             }
 738:             else {
 739:                 contourRenderer(g2, dataArea, info, this, this.domainAxis, 
 740:                         this.rangeAxis, zAxis, data, crosshairState);
 741:             }
 742: 
 743:             // draw vertical crosshair if required...
 744:             setDomainCrosshairValue(crosshairState.getCrosshairX(), false);
 745:             if (isDomainCrosshairVisible()) {
 746:                 drawVerticalLine(g2, dataArea,
 747:                                  getDomainCrosshairValue(),
 748:                                  getDomainCrosshairStroke(),
 749:                                  getDomainCrosshairPaint());
 750:             }
 751: 
 752:             // draw horizontal crosshair if required...
 753:             setRangeCrosshairValue(crosshairState.getCrosshairY(), false);
 754:             if (isRangeCrosshairVisible()) {
 755:                 drawHorizontalLine(g2, dataArea,
 756:                                    getRangeCrosshairValue(),
 757:                                    getRangeCrosshairStroke(),
 758:                                    getRangeCrosshairPaint());
 759:             }
 760: 
 761:         }
 762:         else if (this.clipPath != null) {
 763:             getClipPath().draw(g2, dataArea, this.domainAxis, this.rangeAxis);
 764:         }
 765: 
 766:     }
 767: 
 768:     /**
 769:      * Fills the plot.
 770:      *
 771:      * @param g2  the graphics device.
 772:      * @param dataArea  the area within which the data is being drawn.
 773:      * @param info  collects information about the drawing.
 774:      * @param plot  the plot (can be used to obtain standard color 
 775:      *              information etc).
 776:      * @param horizontalAxis  the domain (horizontal) axis.
 777:      * @param verticalAxis  the range (vertical) axis.
 778:      * @param colorBar  the color bar axis.
 779:      * @param data  the dataset.
 780:      * @param crosshairState  information about crosshairs on a plot.
 781:      */
 782:     public void contourRenderer(Graphics2D g2,
 783:                                 Rectangle2D dataArea,
 784:                                 PlotRenderingInfo info,
 785:                                 ContourPlot plot,
 786:                                 ValueAxis horizontalAxis,
 787:                                 ValueAxis verticalAxis,
 788:                                 ColorBar colorBar,
 789:                                 ContourDataset data,
 790:                                 CrosshairState crosshairState) {
 791: 
 792:         // setup for collecting optional entity info...
 793:         Rectangle2D.Double entityArea = null;
 794:         EntityCollection entities = null;
 795:         if (info != null) {
 796:             entities = info.getOwner().getEntityCollection();
 797:         }
 798: 
 799:         Rectangle2D.Double rect = null;
 800:         rect = new Rectangle2D.Double();
 801: 
 802:         //turn off anti-aliasing when filling rectangles
 803:         Object antiAlias = g2.getRenderingHint(RenderingHints.KEY_ANTIALIASING);
 804:         g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, 
 805:                 RenderingHints.VALUE_ANTIALIAS_OFF);
 806: 
 807:         // get the data points
 808:         Number[] xNumber = data.getXValues();
 809:         Number[] yNumber = data.getYValues();
 810:         Number[] zNumber = data.getZValues();
 811: 
 812:         double[] x = new double[xNumber.length];
 813:         double[] y = new double[yNumber.length];
 814: 
 815:         for (int i = 0; i < x.length; i++) {
 816:             x[i] = xNumber[i].doubleValue();
 817:             y[i] = yNumber[i].doubleValue();
 818:         }
 819: 
 820:         int[] xIndex = data.indexX();
 821:         int[] indexX = data.getXIndices();
 822:         boolean vertInverted = ((NumberAxis) verticalAxis).isInverted();
 823:         boolean horizInverted = false;
 824:         if (horizontalAxis instanceof NumberAxis) {
 825:             horizInverted = ((NumberAxis) horizontalAxis).isInverted();
 826:         }
 827:         double transX = 0.0;
 828:         double transXm1 = 0.0;
 829:         double transXp1 = 0.0;
 830:         double transDXm1 = 0.0;
 831:         double transDXp1 = 0.0;
 832:         double transDX = 0.0;
 833:         double transY = 0.0;
 834:         double transYm1 = 0.0;
 835:         double transYp1 = 0.0;
 836:         double transDYm1 = 0.0;
 837:         double transDYp1 = 0.0;
 838:         double transDY = 0.0;
 839:         int iMax = xIndex[xIndex.length - 1];
 840:         for (int k = 0; k < x.length; k++) {
 841:             int i = xIndex[k];
 842:             if (indexX[i] == k) { // this is a new column
 843:                 if (i == 0) {
 844:                     transX = horizontalAxis.valueToJava2D(x[k], dataArea, 
 845:                             RectangleEdge.BOTTOM);
 846:                     transXm1 = transX;
 847:                     transXp1 = horizontalAxis.valueToJava2D(
 848:                             x[indexX[i + 1]], dataArea, RectangleEdge.BOTTOM);
 849:                     transDXm1 = Math.abs(0.5 * (transX - transXm1));
 850:                     transDXp1 = Math.abs(0.5 * (transX - transXp1));
 851:                 }
 852:                 else if (i == iMax) {
 853:                     transX = horizontalAxis.valueToJava2D(x[k], dataArea, 
 854:                             RectangleEdge.BOTTOM);
 855:                     transXm1 = horizontalAxis.valueToJava2D(x[indexX[i - 1]], 
 856:                             dataArea, RectangleEdge.BOTTOM);
 857:                     transXp1 = transX;
 858:                     transDXm1 = Math.abs(0.5 * (transX - transXm1));
 859:                     transDXp1 = Math.abs(0.5 * (transX - transXp1));
 860:                 }
 861:                 else {
 862:                     transX = horizontalAxis.valueToJava2D(x[k], dataArea, 
 863:                             RectangleEdge.BOTTOM);
 864:                     transXp1 = horizontalAxis.valueToJava2D(x[indexX[i + 1]], 
 865:                             dataArea, RectangleEdge.BOTTOM);
 866:                     transDXm1 = transDXp1;
 867:                     transDXp1 = Math.abs(0.5 * (transX - transXp1));
 868:                 }
 869: 
 870:                 if (horizInverted) {
 871:                     transX -= transDXp1;
 872:                 }
 873:                 else {
 874:                     transX -= transDXm1;
 875:                 }
 876: 
 877:                 transDX = transDXm1 + transDXp1;
 878: 
 879:                 transY = verticalAxis.valueToJava2D(y[k], dataArea, 
 880:                         RectangleEdge.LEFT);
 881:                 transYm1 = transY;
 882:                 if (k + 1 == y.length) {
 883:                     continue;
 884:                 }
 885:                 transYp1 = verticalAxis.valueToJava2D(y[k + 1], dataArea, 
 886:                         RectangleEdge.LEFT);
 887:                 transDYm1 = Math.abs(0.5 * (transY - transYm1));
 888:                 transDYp1 = Math.abs(0.5 * (transY - transYp1));
 889:             }
 890:             else if ((i < indexX.length - 1 
 891:                      && indexX[i + 1] - 1 == k) || k == x.length - 1) {
 892:                 // end of column
 893:                 transY = verticalAxis.valueToJava2D(y[k], dataArea, 
 894:                         RectangleEdge.LEFT);
 895:                 transYm1 = verticalAxis.valueToJava2D(y[k - 1], dataArea, 
 896:                         RectangleEdge.LEFT);
 897:                 transYp1 = transY;
 898:                 transDYm1 = Math.abs(0.5 * (transY - transYm1));
 899:                 transDYp1 = Math.abs(0.5 * (transY - transYp1));
 900:             }
 901:             else {
 902:                 transY = verticalAxis.valueToJava2D(y[k], dataArea, 
 903:                         RectangleEdge.LEFT);
 904:                 transYp1 = verticalAxis.valueToJava2D(y[k + 1], dataArea, 
 905:                         RectangleEdge.LEFT);
 906:                 transDYm1 = transDYp1;
 907:                 transDYp1 = Math.abs(0.5 * (transY - transYp1));
 908:             }
 909:             if (vertInverted) {
 910:                 transY -= transDYm1;
 911:             }
 912:             else {
 913:                 transY -= transDYp1;
 914:             }
 915: 
 916:             transDY = transDYm1 + transDYp1;
 917: 
 918:             rect.setRect(transX, transY, transDX, transDY);
 919:             if (zNumber[k] != null) {
 920:                 g2.setPaint(colorBar.getPaint(zNumber[k].doubleValue()));
 921:                 g2.fill(rect);
 922:             }
 923:             else if (this.missingPaint != null) {
 924:                 g2.setPaint(this.missingPaint);
 925:                 g2.fill(rect);
 926:             }
 927: 
 928:             entityArea = rect;
 929: 
 930:             // add an entity for the item...
 931:             if (entities != null) {
 932:                 String tip = "";
 933:                 if (getToolTipGenerator() != null) {
 934:                     tip = this.toolTipGenerator.generateToolTip(data, k);
 935:                 }
 936: //              Shape s = g2.getClip();
 937: //              if (s.contains(rect) || s.intersects(rect)) {
 938:                 String url = null;
 939:                 // if (getURLGenerator() != null) {    //dmo: look at this later
 940:                 //      url = getURLGenerator().generateURL(data, series, item);
 941:                 // }
 942:                 // Unlike XYItemRenderer, we need to clone entityArea since it 
 943:                 // reused.
 944:                 ContourEntity entity = new ContourEntity(
 945:                         (Rectangle2D.Double) entityArea.clone(), tip, url);
 946:                 entity.setIndex(k);
 947:                 entities.add(entity);
 948: //              }
 949:             }
 950: 
 951:             // do we need to update the crosshair values?
 952:             if (plot.isDomainCrosshairLockedOnData()) {
 953:                 if (plot.isRangeCrosshairLockedOnData()) {
 954:                     // both axes
 955:                     crosshairState.updateCrosshairPoint(x[k], y[k], transX, 
 956:                             transY, PlotOrientation.VERTICAL);
 957:                 }
 958:                 else {
 959:                     // just the horizontal axis...
 960:                     crosshairState.updateCrosshairX(transX);
 961:                 }
 962:             }
 963:             else {
 964:                 if (plot.isRangeCrosshairLockedOnData()) {
 965:                     // just the vertical axis...
 966:                     crosshairState.updateCrosshairY(transY);
 967:                 }
 968:             }
 969:         }
 970: 
 971:         g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, antiAlias);
 972: 
 973:         return;
 974: 
 975:     }
 976: 
 977:     /**
 978:      * Draws the visual representation of a single data item.
 979:      *
 980:      * @param g2  the graphics device.
 981:      * @param dataArea  the area within which the data is being drawn.
 982:      * @param info  collects information about the drawing.
 983:      * @param plot  the plot (can be used to obtain standard color 
 984:      *              information etc).
 985:      * @param domainAxis  the domain (horizontal) axis.
 986:      * @param rangeAxis  the range (vertical) axis.
 987:      * @param colorBar  the color bar axis.
 988:      * @param data  the dataset.
 989:      * @param crosshairState  information about crosshairs on a plot.
 990:      */
 991:     public void pointRenderer(Graphics2D g2,
 992:                               Rectangle2D dataArea,
 993:                               PlotRenderingInfo info,
 994:                               ContourPlot plot,
 995:                               ValueAxis domainAxis,
 996:                               ValueAxis rangeAxis,
 997:                               ColorBar colorBar,
 998:                               ContourDataset data,
 999:                               CrosshairState crosshairState) {
1000: 
1001:         // setup for collecting optional entity info...
1002:         RectangularShape entityArea = null;
1003:         EntityCollection entities = null;
1004:         if (info != null) {
1005:             entities = info.getOwner().getEntityCollection();
1006:         }
1007: 
1008: //      Rectangle2D.Double rect = null;
1009: //      rect = new Rectangle2D.Double();
1010:         RectangularShape rect = new Ellipse2D.Double();
1011: 
1012: 
1013:         //turn off anti-aliasing when filling rectangles
1014:         Object antiAlias = g2.getRenderingHint(RenderingHints.KEY_ANTIALIASING);
1015:         g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, 
1016:                 RenderingHints.VALUE_ANTIALIAS_OFF);
1017: 
1018:         // if (tooltips!=null) tooltips.clearToolTips(); // reset collection
1019:         // get the data points
1020:         Number[] xNumber = data.getXValues();
1021:         Number[] yNumber = data.getYValues();
1022:         Number[] zNumber = data.getZValues();
1023: 
1024:         double[] x = new double[xNumber.length];
1025:         double[] y = new double[yNumber.length];
1026: 
1027:         for (int i = 0; i < x.length; i++) {
1028:             x[i] = xNumber[i].doubleValue();
1029:             y[i] = yNumber[i].doubleValue();
1030:         }
1031: 
1032:         double transX = 0.0;
1033:         double transDX = 0.0;
1034:         double transY = 0.0;
1035:         double transDY = 0.0;
1036:         double size = dataArea.getWidth() * this.ptSizePct;
1037:         for (int k = 0; k < x.length; k++) {
1038: 
1039:             transX = domainAxis.valueToJava2D(x[k], dataArea, 
1040:                     RectangleEdge.BOTTOM) - 0.5 * size;
1041:             transY = rangeAxis.valueToJava2D(y[k], dataArea, RectangleEdge.LEFT)
1042:                      - 0.5 * size;
1043:             transDX = size;
1044:             transDY = size;
1045: 
1046:             rect.setFrame(transX, transY, transDX, transDY);
1047: 
1048:             if (zNumber[k] != null) {
1049:                 g2.setPaint(colorBar.getPaint(zNumber[k].doubleValue()));
1050:                 g2.fill(rect);
1051:             }
1052:             else if (this.missingPaint != null) {
1053:                 g2.setPaint(this.missingPaint);
1054:                 g2.fill(rect);
1055:             }
1056: 
1057: 
1058:             entityArea = rect;
1059: 
1060:             // add an entity for the item...
1061:             if (entities != null) {
1062:                 String tip = null;
1063:                 if (getToolTipGenerator() != null) {
1064:                     tip = this.toolTipGenerator.generateToolTip(data, k);
1065:                 }
1066:                 String url = null;
1067:                 // if (getURLGenerator() != null) {   //dmo: look at this later
1068:                 //   url = getURLGenerator().generateURL(data, series, item);
1069:                 // }
1070:                 // Unlike XYItemRenderer, we need to clone entityArea since it 
1071:                 // reused.
1072:                 ContourEntity entity = new ContourEntity(
1073:                         (RectangularShape) entityArea.clone(), tip, url);
1074:                 entity.setIndex(k);
1075:                 entities.add(entity);
1076:             }
1077: 
1078:             // do we need to update the crosshair values?
1079:             if (plot.isDomainCrosshairLockedOnData()) {
1080:                 if (plot.isRangeCrosshairLockedOnData()) {
1081:                     // both axes
1082:                     crosshairState.updateCrosshairPoint(x[k], y[k], transX, 
1083:                             transY, PlotOrientation.VERTICAL);
1084:                 }
1085:                 else {
1086:                     // just the horizontal axis...
1087:                     crosshairState.updateCrosshairX(transX);
1088:                 }
1089:             }
1090:             else {
1091:                 if (plot.isRangeCrosshairLockedOnData()) {
1092:                     // just the vertical axis...
1093:                     crosshairState.updateCrosshairY(transY);
1094:                 }
1095:             }
1096:         }
1097: 
1098: 
1099:         g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, antiAlias);
1100: 
1101:         return;
1102: 
1103:     }
1104: 
1105:     /**
1106:      * Utility method for drawing a crosshair on the chart (if required).
1107:      *
1108:      * @param g2  The graphics device.
1109:      * @param dataArea  The data area.
1110:      * @param value  The coordinate, where to draw the line.
1111:      * @param stroke  The stroke to use.
1112:      * @param paint  The paint to use.
1113:      */
1114:     protected void drawVerticalLine(Graphics2D g2, Rectangle2D dataArea,
1115:                                     double value, Stroke stroke, Paint paint) {
1116: 
1117:         double xx = getDomainAxis().valueToJava2D(value, dataArea, 
1118:                 RectangleEdge.BOTTOM);
1119:         Line2D line = new Line2D.Double(xx, dataArea.getMinY(), xx, 
1120:                 dataArea.getMaxY());
1121:         g2.setStroke(stroke);
1122:         g2.setPaint(paint);
1123:         g2.draw(line);
1124: 
1125:     }
1126: 
1127:     /**
1128:      * Utility method for drawing a crosshair on the chart (if required).
1129:      *
1130:      * @param g2  The graphics device.
1131:      * @param dataArea  The data area.
1132:      * @param value  The coordinate, where to draw the line.
1133:      * @param stroke  The stroke to use.
1134:      * @param paint  The paint to use.
1135:      */
1136:     protected void drawHorizontalLine(Graphics2D g2, Rectangle2D dataArea,
1137:                                       double value, Stroke stroke, 
1138:                                       Paint paint) {
1139: 
1140:         double yy = getRangeAxis().valueToJava2D(value, dataArea, 
1141:                 RectangleEdge.LEFT);
1142:         Line2D line = new Line2D.Double(dataArea.getMinX(), yy, 
1143:                 dataArea.getMaxX(), yy);
1144:         g2.setStroke(stroke);
1145:         g2.setPaint(paint);
1146:         g2.draw(line);
1147: 
1148:     }
1149: 
1150:     /**
1151:      * Handles a 'click' on the plot by updating the anchor values...
1152:      *
1153:      * @param x  x-coordinate, where the click occured.
1154:      * @param y  y-coordinate, where the click occured.
1155:      * @param info  An object for collection dimension information.
1156:      */
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:     public void zoom(double percent) {
1188: 
1189:         if (percent > 0) {
1190:           //  double range = this.domainAxis.getRange().getLength();
1191:           //  double scaledRange = range * percent;
1192:           //  domainAxis.setAnchoredRange(scaledRange);
1193: 
1194:           //  range = this.rangeAxis.getRange().getLength();
1195:          //  scaledRange = range * percent;
1196:          //   rangeAxis.setAnchoredRange(scaledRange);
1197:         }
1198:         else {
1199:             getRangeAxis().setAutoRange(true);
1200:             getDomainAxis().setAutoRange(true);
1201:         }
1202: 
1203:     }
1204: 
1205:     /**
1206:      * Returns the plot type as a string.
1207:      *
1208:      * @return A short string describing the type of plot.
1209:      */
1210:     public String getPlotType() {
1211:         return localizationResources.getString("Contour_Plot");
1212:     }
1213: 
1214:     /**
1215:      * Returns the range for an axis.
1216:      *
1217:      * @param axis  the axis.
1218:      *
1219:      * @return The range for an axis.
1220:      */
1221:     public Range getDataRange(ValueAxis axis) {
1222: 
1223:         if (this.dataset == null) {
1224:             return null;
1225:         }
1226: 
1227:         Range result = null;
1228: 
1229:         if (axis == getDomainAxis()) {
1230:             result = DatasetUtilities.findDomainBounds(this.dataset);
1231:         }
1232:         else if (axis == getRangeAxis()) {
1233:             result = DatasetUtilities.findRangeBounds(this.dataset);
1234:         }
1235: 
1236:         return result;
1237: 
1238:     }
1239: 
1240:     /**
1241:      * Returns the range for the Contours.
1242:      *
1243:      * @return The range for the Contours (z-axis).
1244:      */
1245:     public Range getContourDataRange() {
1246: 
1247:         Range result = null;
1248: 
1249:         ContourDataset data = getDataset();
1250: 
1251:         if (data != null) {
1252:             Range h = getDomainAxis().getRange();
1253:             Range v = getRangeAxis().getRange();
1254:             result = this.visibleRange(data, h, v);
1255:         }
1256: 
1257:         return result;
1258:     }
1259: 
1260:     /**
1261:      * Notifies all registered listeners of a property change.
1262:      * <P>
1263:      * One source of property change events is the plot's renderer.
1264:      *
1265:      * @param event  Information about the property change.
1266:      */
1267:     public void propertyChange(PropertyChangeEvent event) {
1268:         notifyListeners(new PlotChangeEvent(this));
1269:     }
1270: 
1271:     /**
1272:      * Receives notification of a change to the plot's dataset.
1273:      * <P>
1274:      * The chart reacts by passing on a chart change event to all registered
1275:      * listeners.
1276:      *
1277:      * @param event  Information about the event (not used here).
1278:      */
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:      */
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:      */
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:      */
1315:     public void setDomainCrosshairVisible(boolean flag) {
1316: 
1317:         if (this.domainCrosshairVisible != flag) {
1318:             this.domainCrosshairVisible = flag;
1319:             notifyListeners(new PlotChangeEvent(this));
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:      */
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:      */
1340:     public void setDomainCrosshairLockedOnData(boolean flag) {
1341:         if (this.domainCrosshairLockedOnData != flag) {
1342:             this.domainCrosshairLockedOnData = flag;
1343:             notifyListeners(new PlotChangeEvent(this));
1344:         }
1345:     }
1346: 
1347:     /**
1348:      * Returns the domain crosshair value.
1349:      *
1350:      * @return The value.
1351:      */
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:      */
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:      */
1378:     public void setDomainCrosshairValue(double value, boolean notify) {
1379:         this.domainCrosshairValue = value;
1380:         if (isDomainCrosshairVisible() && notify) {
1381:             notifyListeners(new PlotChangeEvent(this));
1382:         }
1383:     }
1384: 
1385:     /**
1386:      * Returns the Stroke used to draw the crosshair (if visible).
1387:      *
1388:      * @return The crosshair stroke.
1389:      */
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:      */
1400:     public void setDomainCrosshairStroke(Stroke stroke) {
1401:         this.domainCrosshairStroke = stroke;
1402:         notifyListeners(new PlotChangeEvent(this));
1403:     }
1404: 
1405:     /**
1406:      * Returns the domain crosshair color.
1407:      *
1408:      * @return The crosshair color.
1409:      */
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:      */
1420:     public void setDomainCrosshairPaint(Paint paint) {
1421:         this.domainCrosshairPaint = paint;
1422:         notifyListeners(new PlotChangeEvent(this));
1423:     }
1424: 
1425:     /**
1426:      * Returns a flag indicating whether or not the range crosshair is visible.
1427:      *
1428:      * @return The flag.
1429:      */
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:      */
1439:     public void setRangeCrosshairVisible(boolean flag) {
1440:         if (this.rangeCrosshairVisible != flag) {
1441:             this.rangeCrosshairVisible = flag;
1442:             notifyListeners(new PlotChangeEvent(this));
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:      */
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:      */
1462:     public void setRangeCrosshairLockedOnData(boolean flag) {
1463:         if (this.rangeCrosshairLockedOnData != flag) {
1464:             this.rangeCrosshairLockedOnData = flag;
1465:             notifyListeners(new PlotChangeEvent(this));
1466:         }
1467:     }
1468: 
1469:     /**
1470:      * Returns the range crosshair value.
1471:      *
1472:      * @return The value.
1473:      */
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:      */
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:      */
1500:     public void setRangeCrosshairValue(double value, boolean notify) {
1501:         this.rangeCrosshairValue = value;
1502:         if (isRangeCrosshairVisible() && notify) {
1503:             notifyListeners(new PlotChangeEvent(this));
1504:         }
1505:     }
1506: 
1507:     /**
1508:      * Returns the Stroke used to draw the crosshair (if visible).
1509:      *
1510:      * @return The crosshair stroke.
1511:      */
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:      */
1522:     public void setRangeCrosshairStroke(Stroke stroke) {
1523:         this.rangeCrosshairStroke = stroke;
1524:         notifyListeners(new PlotChangeEvent(this));
1525:     }
1526: 
1527:     /**
1528:      * Returns the range crosshair color.
1529:      *
1530:      * @return The crosshair color.
1531:      */
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:      */
1542:     public void setRangeCrosshairPaint(Paint paint) {
1543:         this.rangeCrosshairPaint = paint;
1544:         notifyListeners(new PlotChangeEvent(this));
1545:     }
1546: 
1547:     /**
1548:      * Returns the tool tip generator.
1549:      *
1550:      * @return The tool tip generator (possibly null).
1551:      */
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:      */
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:      */
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:      */
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:      */
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:      */
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:      */
1661:     public ClipPath getClipPath() {
1662:         return this.clipPath;
1663:     }
1664: 
1665:     /**
1666:      * Sets the clipPath.
1667:      * @param clipPath The clipPath to set
1668:      */
1669:     public void setClipPath(ClipPath clipPath) {
1670:         this.clipPath = clipPath;
1671:     }
1672: 
1673:     /**
1674:      * Returns the ptSizePct.
1675:      * @return double
1676:      */
1677:     public double getPtSizePct() {
1678:         return this.ptSizePct;
1679:     }
1680: 
1681:     /**
1682:      * Returns the renderAsPoints.
1683:      * @return boolean
1684:      */
1685:     public boolean isRenderAsPoints() {
1686:         return this.renderAsPoints;
1687:     }
1688: 
1689:     /**
1690:      * Sets the ptSizePct.
1691:      * @param ptSizePct The ptSizePct to set
1692:      */
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:      */
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:     public void axisChanged(AxisChangeEvent event) {
1711:         Object source = event.getSource();
1712:         if (source.equals(this.rangeAxis) || source.equals(this.domainAxis)) {
1713:             ColorBar cba = this.colorBar;
1714:             if (this.colorBar.getAxis().isAutoRange()) {
1715:                 cba.getAxis().configure();
1716:             }
1717: 
1718:         }
1719:         super.axisChanged(event);
1720:     }
1721: 
1722:     /**
1723:      * Returns the visible z-range.
1724:      *
1725:      * @param data  the dataset.
1726:      * @param x  the x range.
1727:      * @param y  the y range.
1728:      *
1729:      * @return The range.
1730:      */
1731:     public Range visibleRange(ContourDataset data, Range x, Range y) {
1732:         Range range = null;
1733:         range = data.getZValueRange(x, y);
1734:         return range;
1735:     }
1736: 
1737:     /**
1738:      * Returns the missingPaint.
1739:      * @return Paint
1740:      */
1741:     public Paint getMissingPaint() {
1742:         return this.missingPaint;
1743:     }
1744: 
1745:     /**
1746:      * Sets the missingPaint.
1747:      * 
1748:      * @param paint  the missingPaint to set.
1749:      */
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:      */
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:      */
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:      */
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:      */
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:      */
1808:     public boolean isDomainZoomable() {
1809:         return false;
1810:     }
1811:     
1812:     /**
1813:      * Returns <code>false</code>.
1814:      * 
1815:      * @return A boolean.
1816:      */
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:     public Object clone() throws CloneNotSupportedException {
1826:         ContourPlot clone = (ContourPlot) super.clone();
1827:         
1828:         if (this.domainAxis != null) {
1829:             clone.domainAxis = (ValueAxis) this.domainAxis.clone();
1830:             clone.domainAxis.setPlot(clone);
1831:             clone.domainAxis.addChangeListener(clone);
1832:         }
1833:         if (this.rangeAxis != null) {
1834:             clone.rangeAxis = (ValueAxis) this.rangeAxis.clone();
1835:             clone.rangeAxis.setPlot(clone);
1836:             clone.rangeAxis.addChangeListener(clone);
1837:         }
1838: 
1839:         if (clone.dataset != null) {
1840:             clone.dataset.addChangeListener(clone); 
1841:         }
1842:     
1843:         if (this.colorBar != null) {
1844:             clone.colorBar = (ColorBar) this.colorBar.clone();
1845:         }
1846: 
1847:         clone.domainMarkers = (List) ObjectUtilities.deepClone(
1848:                 this.domainMarkers);
1849:         clone.rangeMarkers = (List) ObjectUtilities.deepClone(
1850:                 this.rangeMarkers);
1851:         clone.annotations = (List) ObjectUtilities.deepClone(this.annotations);
1852: 
1853:         if (this.clipPath != null) {
1854:             clone.clipPath = (ClipPath) this.clipPath.clone(); 
1855:         }
1856: 
1857:         return clone;
1858:     }
1859: 
1860: }