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