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 * $Id: ContourPlot.java,v 1.16.2.5 2007/01/31 15:56:18 mungady Exp $
038 *
039 * Changes
040 * -------
041 * 26-Nov-2002 : Version 1 contributed by David M. O'Donnell (DG);
042 * 14-Jan-2003 : Added crosshair attributes (DG);
043 * 23-Jan-2003 : Removed two constructors (DG);
044 * 21-Mar-2003 : Bug fix 701744 (DG);
045 * 26-Mar-2003 : Implemented Serializable (DG);
046 * 09-Jul-2003 : Changed ColorBar from extending axis classes to enclosing
047 * them (DG);
048 * 05-Aug-2003 : Applied changes in bug report 780298 (DG);
049 * 08-Sep-2003 : Added internationalization via use of properties
050 * resourceBundle (RFE 690236) (AL);
051 * 11-Sep-2003 : Cloning support (NB);
052 * 16-Sep-2003 : Changed ChartRenderingInfo --> PlotRenderingInfo (DG);
053 * 17-Jan-2004 : Removed references to DefaultContourDataset class, replaced
054 * with ContourDataset interface (with changes to the interface).
055 * See bug 741048 (DG);
056 * 21-Jan-2004 : Update for renamed method in ValueAxis (DG);
057 * 25-Feb-2004 : Replaced CrosshairInfo with CrosshairState (DG);
058 * 06-Oct-2004 : Updated for changes in DatasetUtilities class (DG);
059 * 11-Nov-2004 : Renamed zoom methods to match ValueAxisPlot interface (DG);
060 * 25-Nov-2004 : Small update to clone() implementation (DG);
061 * 11-Jan-2005 : Removed deprecated code in preparation for 1.0.0 release (DG);
062 * 05-May-2005 : Updated draw() method parameters (DG);
063 * 16-Jun-2005 : Added default constructor (DG);
064 * 01-Sep-2005 : Moved dataAreaRatio from Plot to here (DG);
065 * ------------- JFREECHART 1.0.x ---------------------------------------------
066 * 31-Jan-2007 : Deprecated (DG);
067 *
068 */
069
070 package org.jfree.chart.plot;
071
072 import java.awt.AlphaComposite;
073 import java.awt.Composite;
074 import java.awt.Graphics2D;
075 import java.awt.Paint;
076 import java.awt.RenderingHints;
077 import java.awt.Shape;
078 import java.awt.Stroke;
079 import java.awt.geom.Ellipse2D;
080 import java.awt.geom.GeneralPath;
081 import java.awt.geom.Line2D;
082 import java.awt.geom.Point2D;
083 import java.awt.geom.Rectangle2D;
084 import java.awt.geom.RectangularShape;
085 import java.beans.PropertyChangeEvent;
086 import java.beans.PropertyChangeListener;
087 import java.io.Serializable;
088 import java.util.Iterator;
089 import java.util.List;
090 import java.util.ResourceBundle;
091
092 import org.jfree.chart.ClipPath;
093 import org.jfree.chart.annotations.XYAnnotation;
094 import org.jfree.chart.axis.AxisSpace;
095 import org.jfree.chart.axis.ColorBar;
096 import org.jfree.chart.axis.NumberAxis;
097 import org.jfree.chart.axis.ValueAxis;
098 import org.jfree.chart.entity.ContourEntity;
099 import org.jfree.chart.entity.EntityCollection;
100 import org.jfree.chart.event.AxisChangeEvent;
101 import org.jfree.chart.event.PlotChangeEvent;
102 import org.jfree.chart.labels.ContourToolTipGenerator;
103 import org.jfree.chart.labels.StandardContourToolTipGenerator;
104 import org.jfree.chart.renderer.xy.XYBlockRenderer;
105 import org.jfree.chart.urls.XYURLGenerator;
106 import org.jfree.data.Range;
107 import org.jfree.data.contour.ContourDataset;
108 import org.jfree.data.general.DatasetChangeEvent;
109 import org.jfree.data.general.DatasetUtilities;
110 import org.jfree.ui.RectangleEdge;
111 import org.jfree.ui.RectangleInsets;
112 import org.jfree.util.ObjectUtilities;
113
114 /**
115 * A class for creating shaded contours.
116 *
117 * @deprecated This plot is no longer supported, please use {@link XYPlot} with
118 * an {@link XYBlockRenderer}.
119 */
120 public class ContourPlot extends Plot implements ContourValuePlot,
121 ValueAxisPlot,
122 PropertyChangeListener,
123 Serializable,
124 Cloneable {
125
126 /** For serialization. */
127 private static final long serialVersionUID = 7861072556590502247L;
128
129 /** The default insets. */
130 protected static final RectangleInsets DEFAULT_INSETS
131 = new RectangleInsets(2.0, 2.0, 100.0, 10.0);
132
133 /** The domain axis (used for the x-values). */
134 private ValueAxis domainAxis;
135
136 /** The range axis (used for the y-values). */
137 private ValueAxis rangeAxis;
138
139 /** The dataset. */
140 private ContourDataset dataset;
141
142 /** The colorbar axis (used for the z-values). */
143 private ColorBar colorBar = null;
144
145 /** The color bar location. */
146 private RectangleEdge colorBarLocation;
147
148 /** A flag that controls whether or not a domain crosshair is drawn..*/
149 private boolean domainCrosshairVisible;
150
151 /** The domain crosshair value. */
152 private double domainCrosshairValue;
153
154 /** The pen/brush used to draw the crosshair (if any). */
155 private transient Stroke domainCrosshairStroke;
156
157 /** The color used to draw the crosshair (if any). */
158 private transient Paint domainCrosshairPaint;
159
160 /**
161 * A flag that controls whether or not the crosshair locks onto actual data
162 * points.
163 */
164 private boolean domainCrosshairLockedOnData = true;
165
166 /** A flag that controls whether or not a range crosshair is drawn..*/
167 private boolean rangeCrosshairVisible;
168
169 /** The range crosshair value. */
170 private double rangeCrosshairValue;
171
172 /** The pen/brush used to draw the crosshair (if any). */
173 private transient Stroke rangeCrosshairStroke;
174
175 /** The color used to draw the crosshair (if any). */
176 private transient Paint rangeCrosshairPaint;
177
178 /**
179 * A flag that controls whether or not the crosshair locks onto actual data
180 * points.
181 */
182 private boolean rangeCrosshairLockedOnData = true;
183
184 /**
185 * Defines dataArea rectangle as the ratio formed from dividing height by
186 * width (of the dataArea). Modifies plot area calculations.
187 * ratio>0 will attempt to layout the plot so that the
188 * dataArea.height/dataArea.width = ratio.
189 * ratio<0 will attempt to layout the plot so that the
190 * dataArea.height/dataArea.width in plot units (not java2D units as when
191 * ratio>0) = -1.*ratio.
192 */ //dmo
193 private double dataAreaRatio = 0.0; //zero when the parameter is not set
194
195 /** A list of markers (optional) for the domain axis. */
196 private List domainMarkers;
197
198 /** A list of markers (optional) for the range axis. */
199 private List rangeMarkers;
200
201 /** A list of annotations (optional) for the plot. */
202 private List annotations;
203
204 /** The tool tip generator. */
205 private ContourToolTipGenerator toolTipGenerator;
206
207 /** The URL text generator. */
208 private XYURLGenerator urlGenerator;
209
210 /**
211 * Controls whether data are render as filled rectangles or rendered as
212 * points
213 */
214 private boolean renderAsPoints = false;
215
216 /**
217 * Size of points rendered when renderAsPoints = true. Size is relative to
218 * dataArea
219 */
220 private double ptSizePct = 0.05;
221
222 /** Contains the a ClipPath to "trim" the contours. */
223 private transient ClipPath clipPath = null;
224
225 /** Set to Paint to represent missing values. */
226 private transient Paint missingPaint = null;
227
228 /** The resourceBundle for the localization. */
229 protected static ResourceBundle localizationResources =
230 ResourceBundle.getBundle("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 notifyListeners(new PlotChangeEvent(this));
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 notifyListeners(new PlotChangeEvent(this));
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 notifyListeners(new PlotChangeEvent(this));
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 notifyListeners(new PlotChangeEvent(this));
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 notifyListeners(new PlotChangeEvent(this));
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 notifyListeners(new PlotChangeEvent(this));
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 notifyListeners(new PlotChangeEvent(this));
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 notifyListeners(new PlotChangeEvent(this));
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 notifyListeners(new PlotChangeEvent(this));
516
517 }
518
519 /**
520 * Clears all the annotations.
521 */
522 public void clearAnnotations() {
523 if (this.annotations != null) {
524 this.annotations.clear();
525 notifyListeners(new PlotChangeEvent(this));
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 cursor = 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 notifyListeners(new PlotChangeEvent(this));
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 notifyListeners(new PlotChangeEvent(this));
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 notifyListeners(new PlotChangeEvent(this));
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 notifyListeners(new PlotChangeEvent(this));
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 notifyListeners(new PlotChangeEvent(this));
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 notifyListeners(new PlotChangeEvent(this));
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 notifyListeners(new PlotChangeEvent(this));
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 notifyListeners(new PlotChangeEvent(this));
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 notifyListeners(new PlotChangeEvent(this));
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 notifyListeners(new PlotChangeEvent(this));
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 notifyListeners(new PlotChangeEvent(this));
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 }