001 /* ===========================================================
002 * JFreeChart : a free chart library for the Java(tm) platform
003 * ===========================================================
004 *
005 * (C) Copyright 2000-2007, 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 * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
025 * in the United States and other countries.]
026 *
027 * ----------------
028 * ContourPlot.java
029 * ----------------
030 * (C) Copyright 2002-2007, 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 *
066 */
067
068 package org.jfree.chart.plot;
069
070 import java.awt.AlphaComposite;
071 import java.awt.Composite;
072 import java.awt.Graphics2D;
073 import java.awt.Paint;
074 import java.awt.RenderingHints;
075 import java.awt.Shape;
076 import java.awt.Stroke;
077 import java.awt.geom.Ellipse2D;
078 import java.awt.geom.GeneralPath;
079 import java.awt.geom.Line2D;
080 import java.awt.geom.Point2D;
081 import java.awt.geom.Rectangle2D;
082 import java.awt.geom.RectangularShape;
083 import java.beans.PropertyChangeEvent;
084 import java.beans.PropertyChangeListener;
085 import java.io.Serializable;
086 import java.util.Iterator;
087 import java.util.List;
088 import java.util.ResourceBundle;
089
090 import org.jfree.chart.ClipPath;
091 import org.jfree.chart.annotations.XYAnnotation;
092 import org.jfree.chart.axis.AxisSpace;
093 import org.jfree.chart.axis.ColorBar;
094 import org.jfree.chart.axis.NumberAxis;
095 import org.jfree.chart.axis.ValueAxis;
096 import org.jfree.chart.entity.ContourEntity;
097 import org.jfree.chart.entity.EntityCollection;
098 import org.jfree.chart.event.AxisChangeEvent;
099 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 }