KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jfree > chart > plot > CombinedDomainXYPlot


1 /* ===========================================================
2  * JFreeChart : a free chart library for the Java(tm) platform
3  * ===========================================================
4  *
5  * (C) Copyright 2000-2005, by Object Refinery Limited and Contributors.
6  *
7  * Project Info: http://www.jfree.org/jfreechart/index.html
8  *
9  * This library is free software; you can redistribute it and/or modify it
10  * under the terms of the GNU Lesser General Public License as published by
11  * the Free Software Foundation; either version 2.1 of the License, or
12  * (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful, but
15  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
16  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
17  * License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public License
20  * along with this library; if not, write to the Free Software Foundation,
21  * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
22  *
23  * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
24  * in the United States and other countries.]
25  *
26  * -------------------------
27  * CombinedDomainXYPlot.java
28  * -------------------------
29  * (C) Copyright 2001-2005, by Bill Kelemen and Contributors.
30  *
31  * Original Author: Bill Kelemen;
32  * Contributor(s): David Gilbert (for Object Refinery Limited);
33  * Anthony Boulestreau;
34  * David Basten;
35  * Kevin Frechette (for ISTI);
36  * Nicolas Brodu;
37  *
38  * $Id: CombinedDomainXYPlot.java,v 1.9 2005/05/19 14:03:41 mungady Exp $
39  *
40  * Changes:
41  * --------
42  * 06-Dec-2001 : Version 1 (BK);
43  * 12-Dec-2001 : Removed unnecessary 'throws' clause from constructor (DG);
44  * 18-Dec-2001 : Added plotArea attribute and get/set methods (BK);
45  * 22-Dec-2001 : Fixed bug in chartChanged with multiple combinations of
46  * CombinedPlots (BK);
47  * 08-Jan-2002 : Moved to new package com.jrefinery.chart.combination (DG);
48  * 25-Feb-2002 : Updated import statements (DG);
49  * 28-Feb-2002 : Readded "this.plotArea = plotArea" that was deleted from
50  * draw() method (BK);
51  * 26-Mar-2002 : Added an empty zoom method (this method needs to be written so
52  * that combined plots will support zooming (DG);
53  * 29-Mar-2002 : Changed the method createCombinedAxis adding the creation of
54  * OverlaidSymbolicAxis and CombinedSymbolicAxis(AB);
55  * 23-Apr-2002 : Renamed CombinedPlot-->MultiXYPlot, and simplified the
56  * structure (DG);
57  * 23-May-2002 : Renamed (again) MultiXYPlot-->CombinedXYPlot (DG);
58  * 19-Jun-2002 : Added get/setGap() methods suggested by David Basten (DG);
59  * 25-Jun-2002 : Removed redundant imports (DG);
60  * 16-Jul-2002 : Draws shared axis after subplots (to fix missing gridlines),
61  * added overrides of 'setSeriesPaint()' and 'setXYItemRenderer()'
62  * that pass changes down to subplots (KF);
63  * 09-Oct-2002 : Added add(XYPlot) method (DG);
64  * 26-Mar-2003 : Implemented Serializable (DG);
65  * 16-May-2003 : Renamed CombinedXYPlot --> CombinedDomainXYPlot (DG);
66  * 04-Aug-2003 : Removed leftover code that was causing domain axis drawing
67  * problem (DG);
68  * 08-Aug-2003 : Adjusted totalWeight in remove() method (DG);
69  * 21-Aug-2003 : Implemented Cloneable (DG);
70  * 11-Sep-2003 : Fix cloning support (subplots) (NB);
71  * 15-Sep-2003 : Fixed error in cloning (DG);
72  * 16-Sep-2003 : Changed ChartRenderingInfo --> PlotRenderingInfo (DG);
73  * 17-Sep-2003 : Updated handling of 'clicks' (DG);
74  * 12-Nov-2004 : Implemented the new Zoomable interface (DG);
75  * 25-Nov-2004 : Small update to clone() implementation (DG);
76  * 21-Feb-2005 : The getLegendItems() method now returns the fixed legend
77  * items if set (DG);
78  * 05-May-2005 : Removed unused draw() method (DG);
79  *
80  */

81
82 package org.jfree.chart.plot;
83
84 import java.awt.Graphics2D JavaDoc;
85 import java.awt.geom.Point2D JavaDoc;
86 import java.awt.geom.Rectangle2D JavaDoc;
87 import java.io.Serializable JavaDoc;
88 import java.util.Collections JavaDoc;
89 import java.util.Iterator JavaDoc;
90 import java.util.List JavaDoc;
91
92 import org.jfree.chart.LegendItemCollection;
93 import org.jfree.chart.axis.AxisSpace;
94 import org.jfree.chart.axis.AxisState;
95 import org.jfree.chart.axis.NumberAxis;
96 import org.jfree.chart.axis.ValueAxis;
97 import org.jfree.chart.event.PlotChangeEvent;
98 import org.jfree.chart.event.PlotChangeListener;
99 import org.jfree.chart.renderer.xy.XYItemRenderer;
100 import org.jfree.data.Range;
101 import org.jfree.ui.RectangleEdge;
102 import org.jfree.ui.RectangleInsets;
103 import org.jfree.util.ObjectUtilities;
104 import org.jfree.util.PublicCloneable;
105
106 /**
107  * An extension of {@link XYPlot} that contains multiple subplots that share a
108  * common domain axis.
109  */

110 public class CombinedDomainXYPlot extends XYPlot
111                                   implements Cloneable JavaDoc, PublicCloneable,
112                                              Serializable JavaDoc,
113                                              PlotChangeListener {
114
115     /** For serialization. */
116     private static final long serialVersionUID = -7765545541261907383L;
117     
118     /** Storage for the subplot references. */
119     private List JavaDoc subplots;
120
121     /** Total weight of all charts. */
122     private int totalWeight = 0;
123
124     /** The gap between subplots. */
125     private double gap = 5.0;
126
127     /** Temporary storage for the subplot areas. */
128     private transient Rectangle2D JavaDoc[] subplotAreas;
129     // TODO: the subplot areas needs to be moved out of the plot into the plot
130
// state
131

132     /**
133      * Default constructor.
134      */

135     public CombinedDomainXYPlot() {
136         this(new NumberAxis());
137     }
138     
139     /**
140      * Creates a new combined plot that shares a domain axis among multiple
141      * subplots.
142      *
143      * @param domainAxis the shared axis.
144      */

145     public CombinedDomainXYPlot(ValueAxis domainAxis) {
146
147         super(
148             null, // no data in the parent plot
149
domainAxis,
150             null, // no range axis
151
null // no rendereer
152
);
153
154         this.subplots = new java.util.ArrayList JavaDoc();
155
156     }
157
158     /**
159      * Returns a string describing the type of plot.
160      *
161      * @return The type of plot.
162      */

163     public String JavaDoc getPlotType() {
164         return "Combined_Domain_XYPlot";
165     }
166
167     /**
168      * Sets the orientation for the plot (also changes the orientation for all
169      * the subplots to match).
170      *
171      * @param orientation the orientation (<code>null</code> not allowed).
172      */

173     public void setOrientation(PlotOrientation orientation) {
174
175         super.setOrientation(orientation);
176         Iterator JavaDoc iterator = this.subplots.iterator();
177         while (iterator.hasNext()) {
178             XYPlot plot = (XYPlot) iterator.next();
179             plot.setOrientation(orientation);
180         }
181
182     }
183
184     /**
185      * Returns the range for the specified axis. This is the combined range
186      * of all the subplots.
187      *
188      * @param axis the axis.
189      *
190      * @return The range (possibly <code>null</code>).
191      */

192     public Range getDataRange(ValueAxis axis) {
193
194         Range result = null;
195         if (this.subplots != null) {
196             Iterator JavaDoc iterator = this.subplots.iterator();
197             while (iterator.hasNext()) {
198                 XYPlot subplot = (XYPlot) iterator.next();
199                 result = Range.combine(result, subplot.getDataRange(axis));
200             }
201         }
202         return result;
203
204     }
205
206     /**
207      * Returns the gap between subplots, measured in Java2D units.
208      *
209      * @return The gap (in Java2D units).
210      */

211     public double getGap() {
212         return this.gap;
213     }
214
215     /**
216      * Sets the amount of space between subplots and sends a
217      * {@link PlotChangeEvent} to all registered listeners.
218      *
219      * @param gap the gap between subplots (in Java2D units).
220      */

221     public void setGap(double gap) {
222         this.gap = gap;
223         notifyListeners(new PlotChangeEvent(this));
224     }
225
226     /**
227      * Adds a subplot (with a default 'weight' of 1) and sends a
228      * {@link PlotChangeEvent} to all registered listeners.
229      * <P>
230      * The domain axis for the subplot will be set to <code>null</code>.
231      *
232      * @param subplot the subplot (<code>null</code> not permitted).
233      */

234     public void add(XYPlot subplot) {
235         // defer argument checking
236
add(subplot, 1);
237     }
238
239     /**
240      * Adds a subplot with the specified weight and sends a
241      * {@link PlotChangeEvent} to all registered listeners. The weight
242      * determines how much space is allocated to the subplot relative to all
243      * the other subplots.
244      * <P>
245      * The domain axis for the subplot will be set to <code>null</code>.
246      *
247      * @param subplot the subplot (<code>null</code> not permitted).
248      * @param weight the weight (must be >= 1).
249      */

250     public void add(XYPlot subplot, int weight) {
251
252         if (subplot == null) {
253             throw new IllegalArgumentException JavaDoc("Null 'subplot' argument.");
254         }
255         if (weight <= 0) {
256             throw new IllegalArgumentException JavaDoc("Require weight >= 1.");
257         }
258
259         // store the plot and its weight
260
subplot.setParent(this);
261         subplot.setWeight(weight);
262         subplot.setInsets(new RectangleInsets(0.0, 0.0, 0.0, 0.0), false);
263         subplot.setDomainAxis(null);
264         subplot.addChangeListener(this);
265         this.subplots.add(subplot);
266
267         // keep track of total weights
268
this.totalWeight += weight;
269
270         ValueAxis axis = getDomainAxis();
271         if (axis != null) {
272             axis.configure();
273         }
274         
275         notifyListeners(new PlotChangeEvent(this));
276
277     }
278
279     /**
280      * Removes a subplot from the combined chart and sends a
281      * {@link PlotChangeEvent} to all registered listeners.
282      *
283      * @param subplot the subplot (<code>null</code> not permitted).
284      */

285     public void remove(XYPlot subplot) {
286         if (subplot == null) {
287             throw new IllegalArgumentException JavaDoc(" Null 'subplot' argument.");
288         }
289         int position = -1;
290         int size = this.subplots.size();
291         int i = 0;
292         while (position == -1 && i < size) {
293             if (this.subplots.get(i) == subplot) {
294                 position = i;
295             }
296             i++;
297         }
298         if (position != -1) {
299             this.subplots.remove(position);
300             subplot.setParent(null);
301             subplot.removeChangeListener(this);
302             this.totalWeight -= subplot.getWeight();
303
304             ValueAxis domain = getDomainAxis();
305             if (domain != null) {
306                 domain.configure();
307             }
308             notifyListeners(new PlotChangeEvent(this));
309         }
310     }
311
312     /**
313      * Returns the list of subplots.
314      *
315      * @return An unmodifiable list of subplots.
316      */

317     public List JavaDoc getSubplots() {
318         return Collections.unmodifiableList(this.subplots);
319     }
320
321     /**
322      * Calculates the axis space required.
323      *
324      * @param g2 the graphics device.
325      * @param plotArea the plot area.
326      *
327      * @return The space.
328      */

329     protected AxisSpace calculateAxisSpace(Graphics2D JavaDoc g2,
330                                            Rectangle2D JavaDoc plotArea) {
331         
332         AxisSpace space = new AxisSpace();
333         PlotOrientation orientation = getOrientation();
334         
335         // work out the space required by the domain axis...
336
AxisSpace fixed = getFixedDomainAxisSpace();
337         if (fixed != null) {
338             if (orientation == PlotOrientation.HORIZONTAL) {
339                 space.setLeft(fixed.getLeft());
340                 space.setRight(fixed.getRight());
341             }
342             else if (orientation == PlotOrientation.VERTICAL) {
343                 space.setTop(fixed.getTop());
344                 space.setBottom(fixed.getBottom());
345             }
346         }
347         else {
348             ValueAxis xAxis = getDomainAxis();
349             RectangleEdge xEdge = Plot.resolveDomainAxisLocation(
350                 getDomainAxisLocation(), orientation
351             );
352             if (xAxis != null) {
353                 space = xAxis.reserveSpace(g2, this, plotArea, xEdge, space);
354             }
355         }
356         
357         Rectangle2D JavaDoc adjustedPlotArea = space.shrink(plotArea, null);
358         
359         // work out the maximum height or width of the non-shared axes...
360
int n = this.subplots.size();
361         this.subplotAreas = new Rectangle2D JavaDoc[n];
362         double x = adjustedPlotArea.getX();
363         double y = adjustedPlotArea.getY();
364         double usableSize = 0.0;
365         if (orientation == PlotOrientation.HORIZONTAL) {
366             usableSize = adjustedPlotArea.getWidth() - this.gap * (n - 1);
367         }
368         else if (orientation == PlotOrientation.VERTICAL) {
369             usableSize = adjustedPlotArea.getHeight() - this.gap * (n - 1);
370         }
371
372         for (int i = 0; i < n; i++) {
373             XYPlot plot = (XYPlot) this.subplots.get(i);
374
375             // calculate sub-plot area
376
if (orientation == PlotOrientation.HORIZONTAL) {
377                 double w = usableSize * plot.getWeight() / this.totalWeight;
378                 this.subplotAreas[i] = new Rectangle2D.Double JavaDoc(
379                     x, y, w, adjustedPlotArea.getHeight()
380                 );
381                 x = x + w + this.gap;
382             }
383             else if (orientation == PlotOrientation.VERTICAL) {
384                 double h = usableSize * plot.getWeight() / this.totalWeight;
385                 this.subplotAreas[i] = new Rectangle2D.Double JavaDoc(
386                     x, y, adjustedPlotArea.getWidth(), h
387                 );
388                 y = y + h + this.gap;
389             }
390
391             AxisSpace subSpace = plot.calculateRangeAxisSpace(
392                 g2, this.subplotAreas[i], null
393             );
394             space.ensureAtLeast(subSpace);
395
396         }
397
398         return space;
399     }
400     
401     /**
402      * Draws the plot within the specified area on a graphics device.
403      *
404      * @param g2 the graphics device.
405      * @param area the plot area (in Java2D space).
406      * @param anchor an anchor point in Java2D space (<code>null</code>
407      * permitted).
408      * @param parentState the state from the parent plot, if there is one
409      * (<code>null</code> permitted).
410      * @param info collects chart drawing information (<code>null</code>
411      * permitted).
412      */

413     public void draw(Graphics2D JavaDoc g2,
414                      Rectangle2D JavaDoc area,
415                      Point2D JavaDoc anchor,
416                      PlotState parentState,
417                      PlotRenderingInfo info) {
418         
419         // set up info collection...
420
if (info != null) {
421             info.setPlotArea(area);
422         }
423
424         // adjust the drawing area for plot insets (if any)...
425
RectangleInsets insets = getInsets();
426         insets.trim(area);
427
428         AxisSpace space = calculateAxisSpace(g2, area);
429         Rectangle2D JavaDoc dataArea = space.shrink(area, null);
430
431         // set the width and height of non-shared axis of all sub-plots
432
setFixedRangeAxisSpaceForSubplots(space);
433
434         // draw the shared axis
435
ValueAxis axis = getDomainAxis();
436         RectangleEdge edge = getDomainAxisEdge();
437         double cursor = RectangleEdge.coordinate(dataArea, edge);
438         AxisState axisState = axis.draw(g2, cursor, area, dataArea, edge, info);
439         if (parentState == null) {
440             parentState = new PlotState();
441         }
442         parentState.getSharedAxisStates().put(axis, axisState);
443         
444         // draw all the subplots
445
for (int i = 0; i < this.subplots.size(); i++) {
446             XYPlot plot = (XYPlot) this.subplots.get(i);
447             PlotRenderingInfo subplotInfo = null;
448             if (info != null) {
449                 subplotInfo = new PlotRenderingInfo(info.getOwner());
450                 info.addSubplotInfo(subplotInfo);
451             }
452             plot.draw(
453                 g2, this.subplotAreas[i], anchor, parentState, subplotInfo
454             );
455         }
456
457         if (info != null) {
458             info.setDataArea(dataArea);
459         }
460         
461     }
462
463     /**
464      * Returns a collection of legend items for the plot.
465      *
466      * @return The legend items.
467      */

468     public LegendItemCollection getLegendItems() {
469         LegendItemCollection result = getFixedLegendItems();
470         if (result == null) {
471             result = new LegendItemCollection();
472             if (this.subplots != null) {
473                 Iterator JavaDoc iterator = this.subplots.iterator();
474                 while (iterator.hasNext()) {
475                     XYPlot plot = (XYPlot) iterator.next();
476                     LegendItemCollection more = plot.getLegendItems();
477                     result.addAll(more);
478                 }
479             }
480         }
481         return result;
482     }
483
484     /**
485      * Multiplies the range on the range axis/axes by the specified factor.
486      *
487      * @param factor the zoom factor.
488      * @param info the plot rendering info.
489      * @param source the source point.
490      */

491     public void zoomRangeAxes(double factor, PlotRenderingInfo info,
492                               Point2D JavaDoc source) {
493         XYPlot subplot = findSubplot(info, source);
494         if (subplot != null) {
495             subplot.zoomRangeAxes(factor, info, source);
496         }
497     }
498
499     /**
500      * Zooms in on the range axes.
501      *
502      * @param lowerPercent the lower bound.
503      * @param upperPercent the upper bound.
504      * @param info the plot rendering info.
505      * @param source the source point.
506      */

507     public void zoomRangeAxes(double lowerPercent, double upperPercent,
508                               PlotRenderingInfo info, Point2D JavaDoc source) {
509         XYPlot subplot = findSubplot(info, source);
510         if (subplot != null) {
511             subplot.zoomRangeAxes(lowerPercent, upperPercent, info, source);
512         }
513     }
514
515     /**
516      * Returns the subplot (if any) that contains the (x, y) point (specified
517      * in Java2D space).
518      *
519      * @param info the chart rendering info.
520      * @param source the source point.
521      *
522      * @return A subplot (possibly <code>null</code>).
523      */

524     public XYPlot findSubplot(PlotRenderingInfo info, Point2D JavaDoc source) {
525         XYPlot result = null;
526         int subplotIndex = info.getSubplotIndex(source);
527         if (subplotIndex >= 0) {
528             result = (XYPlot) this.subplots.get(subplotIndex);
529         }
530         return result;
531     }
532     
533     /**
534      * Sets the item renderer FOR ALL SUBPLOTS. Registered listeners are
535      * notified that the plot has been modified.
536      * <P>
537      * Note: usually you will want to set the renderer independently for each
538      * subplot, which is NOT what this method does.
539      *
540      * @param renderer the new renderer.
541      */

542     public void setRenderer(XYItemRenderer renderer) {
543
544         super.setRenderer(renderer); // not strictly necessary, since the
545
// renderer set for the
546
// parent plot is not used
547

548         Iterator JavaDoc iterator = this.subplots.iterator();
549         while (iterator.hasNext()) {
550             XYPlot plot = (XYPlot) iterator.next();
551             plot.setRenderer(renderer);
552         }
553
554     }
555
556     /**
557      * Sets the size (width or height, depending on the orientation of the
558      * plot) for the domain axis of each subplot.
559      *
560      * @param space the space.
561      */

562     protected void setFixedRangeAxisSpaceForSubplots(AxisSpace space) {
563
564         Iterator JavaDoc iterator = this.subplots.iterator();
565         while (iterator.hasNext()) {
566             XYPlot plot = (XYPlot) iterator.next();
567             plot.setFixedRangeAxisSpace(space);
568         }
569
570     }
571
572     /**
573      * Handles a 'click' on the plot by updating the anchor values.
574      *
575      * @param x x-coordinate, where the click occured.
576      * @param y y-coordinate, where the click occured.
577      * @param info object containing information about the plot dimensions.
578      */

579     public void handleClick(int x, int y, PlotRenderingInfo info) {
580         Rectangle2D JavaDoc dataArea = info.getDataArea();
581         if (dataArea.contains(x, y)) {
582             for (int i = 0; i < this.subplots.size(); i++) {
583                 XYPlot subplot = (XYPlot) this.subplots.get(i);
584                 PlotRenderingInfo subplotInfo = info.getSubplotInfo(i);
585                 subplot.handleClick(x, y, subplotInfo);
586             }
587         }
588     }
589     
590     /**
591      * Receives a {@link PlotChangeEvent} and responds by notifying all
592      * listeners.
593      *
594      * @param event the event.
595      */

596     public void plotChanged(PlotChangeEvent event) {
597         notifyListeners(event);
598     }
599
600     /**
601      * Tests this plot for equality with another object.
602      *
603      * @param obj the other object.
604      *
605      * @return <code>true</code> or <code>false</code>.
606      */

607     public boolean equals(Object JavaDoc obj) {
608
609         if (obj == null) {
610             return false;
611         }
612
613         if (obj == this) {
614             return true;
615         }
616
617         if (!(obj instanceof CombinedDomainXYPlot)) {
618             return false;
619         }
620         if (!super.equals(obj)) {
621             return false;
622         }
623
624         CombinedDomainXYPlot p = (CombinedDomainXYPlot) obj;
625         if (this.totalWeight != p.totalWeight) {
626             return false;
627         }
628         if (this.gap != p.gap) {
629             return false;
630         }
631         if (!ObjectUtilities.equal(this.subplots, p.subplots)) {
632             return false;
633         }
634
635         return true;
636     }
637     
638     /**
639      * Returns a clone of the annotation.
640      *
641      * @return A clone.
642      *
643      * @throws CloneNotSupportedException this class will not throw this
644      * exception, but subclasses (if any) might.
645      */

646     public Object JavaDoc clone() throws CloneNotSupportedException JavaDoc {
647         
648         CombinedDomainXYPlot result = (CombinedDomainXYPlot) super.clone();
649         result.subplots = (List JavaDoc) ObjectUtilities.deepClone(this.subplots);
650         for (Iterator JavaDoc it = result.subplots.iterator(); it.hasNext();) {
651             Plot child = (Plot) it.next();
652             child.setParent(result);
653         }
654         
655         // after setting up all the subplots, the shared domain axis may need
656
// reconfiguring
657
ValueAxis domainAxis = result.getDomainAxis();
658         if (domainAxis != null) {
659             domainAxis.configure();
660         }
661         
662         return result;
663         
664     }
665     
666 }
667
Popular Tags