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