KickJava   Java API By Example, From Geeks To Geeks.

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


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  * CombinedRangeXYPlot.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  * Arnaud Lelievre;
37  * Nicolas Brodu;
38  *
39  * $Id: CombinedRangeXYPlot.java,v 1.10 2005/05/19 14:03:40 mungady Exp $
40  *
41  * Changes:
42  * --------
43  * 06-Dec-2001 : Version 1 (BK);
44  * 12-Dec-2001 : Removed unnecessary 'throws' clause from constructor (DG);
45  * 18-Dec-2001 : Added plotArea attribute and get/set methods (BK);
46  * 22-Dec-2001 : Fixed bug in chartChanged with multiple combinations of
47  * CombinedPlots (BK);
48  * 08-Jan-2002 : Moved to new package com.jrefinery.chart.combination (DG);
49  * 25-Feb-2002 : Updated import statements (DG);
50  * 28-Feb-2002 : Readded "this.plotArea = plotArea" that was deleted from
51  * draw() method (BK);
52  * 26-Mar-2002 : Added an empty zoom method (this method needs to be written
53  * so that combined plots will support zooming (DG);
54  * 29-Mar-2002 : Changed the method createCombinedAxis adding the creation of
55  * OverlaidSymbolicAxis and CombinedSymbolicAxis(AB);
56  * 23-Apr-2002 : Renamed CombinedPlot-->MultiXYPlot, and simplified the
57  * structure (DG);
58  * 23-May-2002 : Renamed (again) MultiXYPlot-->CombinedXYPlot (DG);
59  * 19-Jun-2002 : Added get/setGap() methods suggested by David Basten (DG);
60  * 25-Jun-2002 : Removed redundant imports (DG);
61  * 16-Jul-2002 : Draws shared axis after subplots (to fix missing gridlines),
62  * added overrides of 'setSeriesPaint()' and 'setXYItemRenderer()'
63  * that pass changes down to subplots (KF);
64  * 09-Oct-2002 : Added add(XYPlot) method (DG);
65  * 26-Mar-2003 : Implemented Serializable (DG);
66  * 16-May-2003 : Renamed CombinedXYPlot --> CombinedRangeXYPlot (DG);
67  * 26-Jun-2003 : Fixed bug 755547 (DG);
68  * 16-Jul-2003 : Removed getSubPlots() method (duplicate of getSubplots()) (DG);
69  * 08-Aug-2003 : Adjusted totalWeight in remove() method (DG);
70  * 21-Aug-2003 : Implemented Cloneable (DG);
71  * 08-Sep-2003 : Added internationalization via use of properties
72  * resourceBundle (RFE 690236) (AL);
73  * 11-Sep-2003 : Fix cloning support (subplots) (NB);
74  * 15-Sep-2003 : Fixed error in cloning (DG);
75  * 16-Sep-2003 : Changed ChartRenderingInfo --> PlotRenderingInfo (DG);
76  * 17-Sep-2003 : Updated handling of 'clicks' (DG);
77  * 12-Nov-2004 : Implements the new Zoomable interface (DG);
78  * 25-Nov-2004 : Small update to clone() implementation (DG);
79  * 21-Feb-2005 : The getLegendItems() method now returns the fixed legend
80  * items if set (DG);
81  * 05-May-2005 : Removed unused draw() method (DG);
82  *
83  */

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

113 public class CombinedRangeXYPlot extends XYPlot
114                                  implements Zoomable,
115                                             Cloneable JavaDoc, PublicCloneable,
116                                             Serializable JavaDoc,
117                                             PlotChangeListener {
118
119     /** For serialization. */
120     private static final long serialVersionUID = -5177814085082031168L;
121     
122     /** Storage for the subplot references. */
123     private List JavaDoc subplots;
124
125     /** Total weight of all charts. */
126     private int totalWeight = 0;
127
128     /** The gap between subplots. */
129     private double gap = 5.0;
130
131     /** Temporary storage for the subplot areas. */
132     private transient Rectangle2D JavaDoc[] subplotAreas;
133
134     /**
135      * Default constructor.
136      */

137     public CombinedRangeXYPlot() {
138         this(new NumberAxis());
139     }
140     
141     /**
142      * Creates a new plot.
143      *
144      * @param rangeAxis the shared axis.
145      */

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

162     public String JavaDoc getPlotType() {
163         return localizationResources.getString("Combined_Range_XYPlot");
164     }
165
166     /**
167      * Returns the space between subplots.
168      *
169      * @return The gap
170      */

171     public double getGap() {
172         return this.gap;
173     }
174
175     /**
176      * Sets the amount of space between subplots.
177      *
178      * @param gap the gap between subplots
179      */

180     public void setGap(double gap) {
181         this.gap = gap;
182     }
183
184     /**
185      * Adds a subplot, with a default 'weight' of 1.
186      *
187      * @param subplot the subplot.
188      */

189     public void add(XYPlot subplot) {
190         add(subplot, 1);
191     }
192
193     /**
194      * Adds a subplot with a particular weight (greater than or equal to one).
195      * The weight determines how much space is allocated to the subplot
196      * relative to all the other subplots.
197      *
198      * @param subplot the subplot.
199      * @param weight the weight (must be 1 or greater).
200      */

201     public void add(XYPlot subplot, int weight) {
202
203         // verify valid weight
204
if (weight <= 0) {
205             String JavaDoc msg = "The 'weight' must be positive.";
206             throw new IllegalArgumentException JavaDoc(msg);
207         }
208
209         // store the plot and its weight
210
subplot.setParent(this);
211         subplot.setWeight(weight);
212         subplot.setInsets(new RectangleInsets(0.0, 0.0, 0.0, 0.0));
213         subplot.setRangeAxis(null);
214         subplot.addChangeListener(this);
215         this.subplots.add(subplot);
216
217         // keep track of total weights
218
this.totalWeight += weight;
219         configureRangeAxes();
220         notifyListeners(new PlotChangeEvent(this));
221
222     }
223
224     /**
225      * Removes a subplot from the combined chart.
226      *
227      * @param subplot the subplot (<code>null</code> not permitted).
228      */

229     public void remove(XYPlot subplot) {
230         if (subplot == null) {
231             throw new IllegalArgumentException JavaDoc(" Null 'subplot' argument.");
232         }
233         int position = -1;
234         int size = this.subplots.size();
235         int i = 0;
236         while (position == -1 && i < size) {
237             if (this.subplots.get(i) == subplot) {
238                 position = i;
239             }
240             i++;
241         }
242         if (position != -1) {
243             subplot.setParent(null);
244             subplot.removeChangeListener(this);
245             this.totalWeight -= subplot.getWeight();
246             configureRangeAxes();
247             notifyListeners(new PlotChangeEvent(this));
248         }
249     }
250
251     /**
252      * Returns a list of the subplots.
253      *
254      * @return The list (unmodifiable).
255      */

256     public List JavaDoc getSubplots() {
257         return Collections.unmodifiableList(this.subplots);
258     }
259
260     /**
261      * Calculates the space required for the axes.
262      *
263      * @param g2 the graphics device.
264      * @param plotArea the plot area.
265      *
266      * @return The space required for the axes.
267      */

268     protected AxisSpace calculateAxisSpace(Graphics2D JavaDoc g2,
269                                            Rectangle2D JavaDoc plotArea) {
270         
271         AxisSpace space = new AxisSpace();
272         PlotOrientation orientation = getOrientation();
273         
274         // work out the space required by the domain axis...
275
AxisSpace fixed = getFixedRangeAxisSpace();
276         if (fixed != null) {
277             if (orientation == PlotOrientation.VERTICAL) {
278                 space.setLeft(fixed.getLeft());
279                 space.setRight(fixed.getRight());
280             }
281             else if (orientation == PlotOrientation.HORIZONTAL) {
282                 space.setTop(fixed.getTop());
283                 space.setBottom(fixed.getBottom());
284             }
285         }
286         else {
287             ValueAxis valueAxis = getRangeAxis();
288             RectangleEdge valueEdge = Plot.resolveRangeAxisLocation(
289                 getRangeAxisLocation(), orientation
290             );
291             if (valueAxis != null) {
292                 space = valueAxis.reserveSpace(
293                     g2, this, plotArea, valueEdge, space
294                 );
295             }
296         }
297         
298         Rectangle2D JavaDoc adjustedPlotArea = space.shrink(plotArea, null);
299         // work out the maximum height or width of the non-shared axes...
300
int n = this.subplots.size();
301
302         // calculate plotAreas of all sub-plots, maximum vertical/horizontal
303
// axis width/height
304
this.subplotAreas = new Rectangle2D JavaDoc[n];
305         double x = adjustedPlotArea.getX();
306         double y = adjustedPlotArea.getY();
307         double usableSize = 0.0;
308         if (orientation == PlotOrientation.VERTICAL) {
309             usableSize = adjustedPlotArea.getWidth() - this.gap * (n - 1);
310         }
311         else if (orientation == PlotOrientation.HORIZONTAL) {
312             usableSize = adjustedPlotArea.getHeight() - this.gap * (n - 1);
313         }
314
315         for (int i = 0; i < n; i++) {
316             XYPlot plot = (XYPlot) this.subplots.get(i);
317
318             // calculate sub-plot area
319
if (orientation == PlotOrientation.VERTICAL) {
320                 double w = usableSize * plot.getWeight() / this.totalWeight;
321                 this.subplotAreas[i] = new Rectangle2D.Double JavaDoc(
322                     x, y, w, adjustedPlotArea.getHeight()
323                 );
324                 x = x + w + this.gap;
325             }
326             else if (orientation == PlotOrientation.HORIZONTAL) {
327                 double h = usableSize * plot.getWeight() / this.totalWeight;
328                 this.subplotAreas[i] = new Rectangle2D.Double JavaDoc(
329                     x, y, adjustedPlotArea.getWidth(), h
330                 );
331                 y = y + h + this.gap;
332             }
333
334             AxisSpace subSpace = plot.calculateDomainAxisSpace(
335                 g2, this.subplotAreas[i], null
336             );
337             space.ensureAtLeast(subSpace);
338
339         }
340
341         return space;
342     }
343     
344     /**
345      * Draws the plot within the specified area on a graphics device.
346      *
347      * @param g2 the graphics device.
348      * @param area the plot area (in Java2D space).
349      * @param anchor an anchor point in Java2D space (<code>null</code>
350      * permitted).
351      * @param parentState the state from the parent plot, if there is one
352      * (<code>null</code> permitted).
353      * @param info collects chart drawing information (<code>null</code>
354      * permitted).
355      */

356     public void draw(Graphics2D JavaDoc g2,
357                      Rectangle2D JavaDoc area,
358                      Point2D JavaDoc anchor,
359                      PlotState parentState,
360                      PlotRenderingInfo info) {
361         
362         // set up info collection...
363
if (info != null) {
364             info.setPlotArea(area);
365         }
366
367         // adjust the drawing area for plot insets (if any)...
368
RectangleInsets insets = getInsets();
369         insets.trim(area);
370
371         AxisSpace space = calculateAxisSpace(g2, area);
372         Rectangle2D JavaDoc dataArea = space.shrink(area, null);
373         //this.axisOffset.trim(dataArea);
374

375         // set the width and height of non-shared axis of all sub-plots
376
setFixedDomainAxisSpaceForSubplots(space);
377
378         // draw the shared axis
379
ValueAxis axis = getRangeAxis();
380         RectangleEdge edge = getRangeAxisEdge();
381         double cursor = RectangleEdge.coordinate(dataArea, edge);
382         AxisState axisState = axis.draw(g2, cursor, area, dataArea, edge, info);
383
384         if (parentState == null) {
385             parentState = new PlotState();
386         }
387         parentState.getSharedAxisStates().put(axis, axisState);
388         
389         // draw all the charts
390
for (int i = 0; i < this.subplots.size(); i++) {
391             XYPlot plot = (XYPlot) this.subplots.get(i);
392             PlotRenderingInfo subplotInfo = null;
393             if (info != null) {
394                 subplotInfo = new PlotRenderingInfo(info.getOwner());
395                 info.addSubplotInfo(subplotInfo);
396             }
397             plot.draw(
398                 g2, this.subplotAreas[i], anchor, parentState, subplotInfo
399             );
400         }
401
402         if (info != null) {
403             info.setDataArea(dataArea);
404         }
405
406     }
407
408     /**
409      * Returns a collection of legend items for the plot.
410      *
411      * @return The legend items.
412      */

413     public LegendItemCollection getLegendItems() {
414         LegendItemCollection result = getFixedLegendItems();
415         if (result == null) {
416             result = new LegendItemCollection();
417         
418             if (this.subplots != null) {
419                 Iterator JavaDoc iterator = this.subplots.iterator();
420                 while (iterator.hasNext()) {
421                     XYPlot plot = (XYPlot) iterator.next();
422                     LegendItemCollection more = plot.getLegendItems();
423                     result.addAll(more);
424                 }
425             }
426         }
427         return result;
428     }
429
430     /**
431      * Multiplies the range on the domain axis/axes by the specified factor.
432      *
433      * @param factor the zoom factor.
434      * @param info the plot rendering info.
435      * @param source the source point.
436      */

437     public void zoomDomainAxes(double factor, PlotRenderingInfo info,
438                                Point2D JavaDoc source) {
439         XYPlot subplot = findSubplot(info, source);
440         if (subplot != null) {
441             subplot.zoomDomainAxes(factor, info, source);
442         }
443     }
444
445     /**
446      * Zooms in on the domain axes.
447      *
448      * @param lowerPercent the lower bound.
449      * @param upperPercent the upper bound.
450      * @param info the plot rendering info.
451      * @param source the source point.
452      */

453     public void zoomDomainAxes(double lowerPercent, double upperPercent,
454                                PlotRenderingInfo info, Point2D JavaDoc source) {
455         XYPlot subplot = findSubplot(info, source);
456         if (subplot != null) {
457             subplot.zoomDomainAxes(lowerPercent, upperPercent, info, source);
458         }
459     }
460
461     /**
462      * Returns the subplot (if any) that contains the (x, y) point (specified
463      * in Java2D space).
464      *
465      * @param info the chart rendering info.
466      * @param source the source point.
467      *
468      * @return A subplot (possibly <code>null</code>).
469      */

470     public XYPlot findSubplot(PlotRenderingInfo info, Point2D JavaDoc source) {
471         XYPlot result = null;
472         int subplotIndex = info.getSubplotIndex(source);
473         if (subplotIndex >= 0) {
474             result = (XYPlot) this.subplots.get(subplotIndex);
475         }
476         return result;
477     }
478
479     /**
480      * Sets the item renderer FOR ALL SUBPLOTS. Registered listeners are
481      * notified that the plot has been modified.
482      * <P>
483      * Note: usually you will want to set the renderer independently for each
484      * subplot, which is NOT what this method does.
485      *
486      * @param renderer the new renderer.
487      */

488     public void setRenderer(XYItemRenderer renderer) {
489
490         super.setRenderer(renderer); // not strictly necessary, since the
491
// renderer set for the
492
// parent plot is not used
493

494         Iterator JavaDoc iterator = this.subplots.iterator();
495         while (iterator.hasNext()) {
496             XYPlot plot = (XYPlot) iterator.next();
497             plot.setRenderer(renderer);
498         }
499
500     }
501
502     /**
503      * Sets the orientation for the plot (and all its subplots).
504      *
505      * @param orientation the orientation.
506      */

507     public void setOrientation(PlotOrientation orientation) {
508
509         super.setOrientation(orientation);
510
511         Iterator JavaDoc iterator = this.subplots.iterator();
512         while (iterator.hasNext()) {
513             XYPlot plot = (XYPlot) iterator.next();
514             plot.setOrientation(orientation);
515         }
516
517     }
518
519     /**
520      * Returns the range for the axis. This is the combined range of all the
521      * subplots.
522      *
523      * @param axis the axis.
524      *
525      * @return The range.
526      */

527     public Range getDataRange(ValueAxis axis) {
528
529         Range result = null;
530         if (this.subplots != null) {
531             Iterator JavaDoc iterator = this.subplots.iterator();
532             while (iterator.hasNext()) {
533                 XYPlot subplot = (XYPlot) iterator.next();
534                 result = Range.combine(result, subplot.getDataRange(axis));
535             }
536         }
537         return result;
538
539     }
540
541     /**
542      * Sets the space (width or height, depending on the orientation of the
543      * plot) for the domain axis of each subplot.
544      *
545      * @param space the space.
546      */

547     protected void setFixedDomainAxisSpaceForSubplots(AxisSpace space) {
548
549         Iterator JavaDoc iterator = this.subplots.iterator();
550         while (iterator.hasNext()) {
551             XYPlot plot = (XYPlot) iterator.next();
552             plot.setFixedDomainAxisSpace(space);
553         }
554
555     }
556
557     /**
558      * Handles a 'click' on the plot by updating the anchor values...
559      *
560      * @param x x-coordinate, where the click occured.
561      * @param y y-coordinate, where the click occured.
562      * @param info object containing information about the plot dimensions.
563      */

564     public void handleClick(int x, int y, PlotRenderingInfo info) {
565
566         Rectangle2D JavaDoc dataArea = info.getDataArea();
567         if (dataArea.contains(x, y)) {
568             for (int i = 0; i < this.subplots.size(); i++) {
569                 XYPlot subplot = (XYPlot) this.subplots.get(i);
570                 PlotRenderingInfo subplotInfo = info.getSubplotInfo(i);
571                 subplot.handleClick(x, y, subplotInfo);
572             }
573         }
574
575     }
576
577     /**
578      * Receives a {@link PlotChangeEvent} and responds by notifying all
579      * listeners.
580      *
581      * @param event the event.
582      */

583     public void plotChanged(PlotChangeEvent event) {
584         notifyListeners(event);
585     }
586
587     /**
588      * Tests this plot for equality with another object.
589      *
590      * @param obj the other object.
591      *
592      * @return <code>true</code> or <code>false</code>.
593      */

594     public boolean equals(Object JavaDoc obj) {
595
596         if (obj == this) {
597             return true;
598         }
599
600         if (!(obj instanceof CombinedRangeXYPlot)) {
601             return false;
602         }
603         if (!super.equals(obj)) {
604             return false;
605         }
606         CombinedRangeXYPlot that = (CombinedRangeXYPlot) obj;
607         if (!ObjectUtilities.equal(this.subplots, that.subplots)) {
608             return false;
609         }
610         if (this.totalWeight != that.totalWeight) {
611             return false;
612         }
613         if (this.gap != that.gap) {
614             return false;
615         }
616         return true;
617     }
618     
619     /**
620      * Returns a clone of the plot.
621      *
622      * @return A clone.
623      *
624      * @throws CloneNotSupportedException this class will not throw this
625      * exception, but subclasses (if any) might.
626      */

627     public Object JavaDoc clone() throws CloneNotSupportedException JavaDoc {
628         
629         CombinedRangeXYPlot result = (CombinedRangeXYPlot) super.clone();
630         result.subplots = (List JavaDoc) ObjectUtilities.deepClone(this.subplots);
631         for (Iterator JavaDoc it = result.subplots.iterator(); it.hasNext();) {
632             Plot child = (Plot) it.next();
633             child.setParent(result);
634         }
635         
636         // after setting up all the subplots, the shared range axis may need
637
// reconfiguring
638
ValueAxis rangeAxis = result.getRangeAxis();
639         if (rangeAxis != null) {
640             rangeAxis.configure();
641         }
642         
643         return result;
644     }
645
646 }
647
Popular Tags