KickJava   Java API By Example, From Geeks To Geeks.

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


1 /* ===========================================================
2  * JFreeChart : a free chart library for the Java(tm) platform
3  * ===========================================================
4  *
5  * (C) Copyright 2000-2006, 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
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
22  * USA.
23  *
24  * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
25  * in the United States and other countries.]
26  *
27  * --------------------
28  * MultiplePiePlot.java
29  * --------------------
30  * (C) Copyright 2004-2006, by Object Refinery Limited.
31  *
32  * Original Author: David Gilbert (for Object Refinery Limited);
33  * Contributor(s): -;
34  *
35  * $Id: MultiplePiePlot.java,v 1.12.2.7 2006/09/27 17:06:59 mungady Exp $
36  *
37  * Changes (from 21-Jun-2001)
38  * --------------------------
39  * 29-Jan-2004 : Version 1 (DG);
40  * 31-Mar-2004 : Added setPieIndex() call during drawing (DG);
41  * 20-Apr-2005 : Small change for update to LegendItem constructors (DG);
42  * 05-May-2005 : Updated draw() method parameters (DG);
43  * 16-Jun-2005 : Added get/setDataset() and equals() methods (DG);
44  * ------------- JFREECHART 1.0.0 ---------------------------------------------
45  * 06-Apr-2006 : Fixed bug 1190647 - legend and section colors not consistent
46  * when aggregation limit is specified (DG);
47  * 27-Sep-2006 : Updated draw() method for deprecated code (DG);
48  *
49  */

50
51 package org.jfree.chart.plot;
52
53 import java.awt.Color JavaDoc;
54 import java.awt.Font JavaDoc;
55 import java.awt.Graphics2D JavaDoc;
56 import java.awt.Paint JavaDoc;
57 import java.awt.Rectangle JavaDoc;
58 import java.awt.geom.Point2D JavaDoc;
59 import java.awt.geom.Rectangle2D JavaDoc;
60 import java.io.IOException JavaDoc;
61 import java.io.ObjectInputStream JavaDoc;
62 import java.io.ObjectOutputStream JavaDoc;
63 import java.io.Serializable JavaDoc;
64 import java.util.HashMap JavaDoc;
65 import java.util.Iterator JavaDoc;
66 import java.util.List JavaDoc;
67 import java.util.Map JavaDoc;
68
69 import org.jfree.chart.ChartRenderingInfo;
70 import org.jfree.chart.JFreeChart;
71 import org.jfree.chart.LegendItem;
72 import org.jfree.chart.LegendItemCollection;
73 import org.jfree.chart.event.PlotChangeEvent;
74 import org.jfree.chart.title.TextTitle;
75 import org.jfree.data.category.CategoryDataset;
76 import org.jfree.data.category.CategoryToPieDataset;
77 import org.jfree.data.general.DatasetChangeEvent;
78 import org.jfree.data.general.DatasetUtilities;
79 import org.jfree.data.general.PieDataset;
80 import org.jfree.io.SerialUtilities;
81 import org.jfree.ui.RectangleEdge;
82 import org.jfree.ui.RectangleInsets;
83 import org.jfree.util.ObjectUtilities;
84 import org.jfree.util.PaintUtilities;
85 import org.jfree.util.TableOrder;
86
87 /**
88  * A plot that displays multiple pie plots using data from a
89  * {@link CategoryDataset}.
90  */

91 public class MultiplePiePlot extends Plot implements Cloneable JavaDoc, Serializable JavaDoc {
92     
93     /** For serialization. */
94     private static final long serialVersionUID = -355377800470807389L;
95     
96     /** The chart object that draws the individual pie charts. */
97     private JFreeChart pieChart;
98     
99     /** The dataset. */
100     private CategoryDataset dataset;
101     
102     /** The data extract order (by row or by column). */
103     private TableOrder dataExtractOrder;
104     
105     /** The pie section limit percentage. */
106     private double limit = 0.0;
107     
108     /**
109      * The key for the aggregated items.
110      * @since 1.0.2
111      */

112     private Comparable JavaDoc aggregatedItemsKey;
113     
114     /**
115      * The paint for the aggregated items.
116      * @since 1.0.2
117      */

118     private transient Paint JavaDoc aggregatedItemsPaint;
119     
120     /**
121      * The colors to use for each section.
122      * @since 1.0.2
123      */

124     private transient Map JavaDoc sectionPaints;
125     
126     /**
127      * Creates a new plot with no data.
128      */

129     public MultiplePiePlot() {
130         this(null);
131     }
132     
133     /**
134      * Creates a new plot.
135      *
136      * @param dataset the dataset (<code>null</code> permitted).
137      */

138     public MultiplePiePlot(CategoryDataset dataset) {
139         super();
140         this.dataset = dataset;
141         PiePlot piePlot = new PiePlot(null);
142         this.pieChart = new JFreeChart(piePlot);
143         this.pieChart.removeLegend();
144         this.dataExtractOrder = TableOrder.BY_COLUMN;
145         this.pieChart.setBackgroundPaint(null);
146         TextTitle seriesTitle = new TextTitle("Series Title",
147                 new Font JavaDoc("SansSerif", Font.BOLD, 12));
148         seriesTitle.setPosition(RectangleEdge.BOTTOM);
149         this.pieChart.setTitle(seriesTitle);
150         this.aggregatedItemsKey = "Other";
151         this.aggregatedItemsPaint = Color.lightGray;
152         this.sectionPaints = new HashMap JavaDoc();
153     }
154     
155     /**
156      * Returns the dataset used by the plot.
157      *
158      * @return The dataset (possibly <code>null</code>).
159      */

160     public CategoryDataset getDataset() {
161         return this.dataset;
162     }
163     
164     /**
165      * Sets the dataset used by the plot and sends a {@link PlotChangeEvent}
166      * to all registered listeners.
167      *
168      * @param dataset the dataset (<code>null</code> permitted).
169      */

170     public void setDataset(CategoryDataset dataset) {
171         // if there is an existing dataset, remove the plot from the list of
172
// change listeners...
173
if (this.dataset != null) {
174             this.dataset.removeChangeListener(this);
175         }
176
177         // set the new dataset, and register the chart as a change listener...
178
this.dataset = dataset;
179         if (dataset != null) {
180             setDatasetGroup(dataset.getGroup());
181             dataset.addChangeListener(this);
182         }
183
184         // send a dataset change event to self to trigger plot change event
185
datasetChanged(new DatasetChangeEvent(this, dataset));
186     }
187     
188     /**
189      * Returns the pie chart that is used to draw the individual pie plots.
190      *
191      * @return The pie chart.
192      */

193     public JFreeChart getPieChart() {
194         return this.pieChart;
195     }
196     
197     /**
198      * Sets the chart that is used to draw the individual pie plots.
199      *
200      * @param pieChart the pie chart.
201      */

202     public void setPieChart(JFreeChart pieChart) {
203         this.pieChart = pieChart;
204         notifyListeners(new PlotChangeEvent(this));
205     }
206     
207     /**
208      * Returns the data extract order (by row or by column).
209      *
210      * @return The data extract order (never <code>null</code>).
211      */

212     public TableOrder getDataExtractOrder() {
213         return this.dataExtractOrder;
214     }
215     
216     /**
217      * Sets the data extract order (by row or by column) and sends a
218      * {@link PlotChangeEvent} to all registered listeners.
219      *
220      * @param order the order (<code>null</code> not permitted).
221      */

222     public void setDataExtractOrder(TableOrder order) {
223         if (order == null) {
224             throw new IllegalArgumentException JavaDoc("Null 'order' argument");
225         }
226         this.dataExtractOrder = order;
227         notifyListeners(new PlotChangeEvent(this));
228     }
229     
230     /**
231      * Returns the limit (as a percentage) below which small pie sections are
232      * aggregated.
233      *
234      * @return The limit percentage.
235      */

236     public double getLimit() {
237         return this.limit;
238     }
239     
240     /**
241      * Sets the limit below which pie sections are aggregated.
242      * Set this to 0.0 if you don't want any aggregation to occur.
243      *
244      * @param limit the limit percent.
245      */

246     public void setLimit(double limit) {
247         this.limit = limit;
248         notifyListeners(new PlotChangeEvent(this));
249     }
250     
251     /**
252      * Returns the key for aggregated items in the pie plots, if there are any.
253      * The default value is "Other".
254      *
255      * @return The aggregated items key.
256      *
257      * @since 1.0.2
258      */

259     public Comparable JavaDoc getAggregatedItemsKey() {
260         return this.aggregatedItemsKey;
261     }
262     
263     /**
264      * Sets the key for aggregated items in the pie plots. You must ensure
265      * that this doesn't clash with any keys in the dataset.
266      *
267      * @param key the key (<code>null</code> not permitted).
268      *
269      * @since 1.0.2
270      */

271     public void setAggregatedItemsKey(Comparable JavaDoc key) {
272         if (key == null) {
273             throw new IllegalArgumentException JavaDoc("Null 'key' argument.");
274         }
275         this.aggregatedItemsKey = key;
276         notifyListeners(new PlotChangeEvent(this));
277     }
278     
279     /**
280      * Returns the paint used to draw the pie section representing the
281      * aggregated items. The default value is <code>Color.lightGray</code>.
282      *
283      * @return The paint.
284      *
285      * @since 1.0.2
286      */

287     public Paint JavaDoc getAggregatedItemsPaint() {
288         return this.aggregatedItemsPaint;
289     }
290     
291     /**
292      * Sets the paint used to draw the pie section representing the aggregated
293      * items and sends a {@link PlotChangeEvent} to all registered listeners.
294      *
295      * @param paint the paint (<code>null</code> not permitted).
296      *
297      * @since 1.0.2
298      */

299     public void setAggregatedItemsPaint(Paint JavaDoc paint) {
300         if (paint == null) {
301             throw new IllegalArgumentException JavaDoc("Null 'paint' argument.");
302         }
303         this.aggregatedItemsPaint = paint;
304         notifyListeners(new PlotChangeEvent(this));
305     }
306     
307     /**
308      * Returns a short string describing the type of plot.
309      *
310      * @return The plot type.
311      */

312     public String JavaDoc getPlotType() {
313         return "Multiple Pie Plot";
314          // TODO: need to fetch this from localised resources
315
}
316
317     /**
318      * Draws the plot on a Java 2D graphics device (such as the screen or a
319      * printer).
320      *
321      * @param g2 the graphics device.
322      * @param area the area within which the plot should be drawn.
323      * @param anchor the anchor point (<code>null</code> permitted).
324      * @param parentState the state from the parent plot, if there is one.
325      * @param info collects info about the drawing.
326      */

327     public void draw(Graphics2D JavaDoc g2,
328                      Rectangle2D JavaDoc area,
329                      Point2D JavaDoc anchor,
330                      PlotState parentState,
331                      PlotRenderingInfo info) {
332         
333        
334         // adjust the drawing area for the plot insets (if any)...
335
RectangleInsets insets = getInsets();
336         insets.trim(area);
337         drawBackground(g2, area);
338         drawOutline(g2, area);
339         
340         // check that there is some data to display...
341
if (DatasetUtilities.isEmptyOrNull(this.dataset)) {
342             drawNoDataMessage(g2, area);
343             return;
344         }
345
346         int pieCount = 0;
347         if (this.dataExtractOrder == TableOrder.BY_ROW) {
348             pieCount = this.dataset.getRowCount();
349         }
350         else {
351             pieCount = this.dataset.getColumnCount();
352         }
353
354         // the columns variable is always >= rows
355
int displayCols = (int) Math.ceil(Math.sqrt(pieCount));
356         int displayRows
357             = (int) Math.ceil((double) pieCount / (double) displayCols);
358
359         // swap rows and columns to match plotArea shape
360
if (displayCols > displayRows && area.getWidth() < area.getHeight()) {
361             int temp = displayCols;
362             displayCols = displayRows;
363             displayRows = temp;
364         }
365
366         prefetchSectionPaints();
367         
368         int x = (int) area.getX();
369         int y = (int) area.getY();
370         int width = ((int) area.getWidth()) / displayCols;
371         int height = ((int) area.getHeight()) / displayRows;
372         int row = 0;
373         int column = 0;
374         int diff = (displayRows * displayCols) - pieCount;
375         int xoffset = 0;
376         Rectangle JavaDoc rect = new Rectangle JavaDoc();
377
378         for (int pieIndex = 0; pieIndex < pieCount; pieIndex++) {
379             rect.setBounds(x + xoffset + (width * column), y + (height * row),
380                     width, height);
381
382             String JavaDoc title = null;
383             if (this.dataExtractOrder == TableOrder.BY_ROW) {
384                 title = this.dataset.getRowKey(pieIndex).toString();
385             }
386             else {
387                 title = this.dataset.getColumnKey(pieIndex).toString();
388             }
389             this.pieChart.setTitle(title);
390             
391             PieDataset piedataset = null;
392             PieDataset dd = new CategoryToPieDataset(this.dataset,
393                     this.dataExtractOrder, pieIndex);
394             if (this.limit > 0.0) {
395                 piedataset = DatasetUtilities.createConsolidatedPieDataset(
396                         dd, this.aggregatedItemsKey, this.limit);
397             }
398             else {
399                 piedataset = dd;
400             }
401             PiePlot piePlot = (PiePlot) this.pieChart.getPlot();
402             piePlot.setDataset(piedataset);
403             piePlot.setPieIndex(pieIndex);
404             
405             // update the section colors to match the global colors...
406
for (int i = 0; i < piedataset.getItemCount(); i++) {
407                 Comparable JavaDoc key = piedataset.getKey(i);
408                 Paint JavaDoc p;
409                 if (key.equals(this.aggregatedItemsKey)) {
410                     p = this.aggregatedItemsPaint;
411                 }
412                 else {
413                     p = (Paint JavaDoc) this.sectionPaints.get(key);
414                 }
415                 piePlot.setSectionPaint(key, p);
416             }
417             
418             ChartRenderingInfo subinfo = null;
419             if (info != null) {
420                 subinfo = new ChartRenderingInfo();
421             }
422             this.pieChart.draw(g2, rect, subinfo);
423             if (info != null) {
424                 info.getOwner().getEntityCollection().addAll(
425                         subinfo.getEntityCollection());
426                 info.addSubplotInfo(subinfo.getPlotInfo());
427             }
428             
429             ++column;
430             if (column == displayCols) {
431                 column = 0;
432                 ++row;
433
434                 if (row == displayRows - 1 && diff != 0) {
435                     xoffset = (diff * width) / 2;
436                 }
437             }
438         }
439
440     }
441     
442     /**
443      * For each key in the dataset, check the <code>sectionPaints</code>
444      * cache to see if a paint is associated with that key and, if not,
445      * fetch one from the drawing supplier. These colors are cached so that
446      * the legend and all the subplots use consistent colors.
447      */

448     private void prefetchSectionPaints() {
449         
450         // pre-fetch the colors for each key...this is because the subplots
451
// may not display every key, but we need the coloring to be
452
// consistent...
453
if (this.dataExtractOrder == TableOrder.BY_ROW) {
454             // column keys provide potential keys for individual pies
455
for (int c = 0; c < this.dataset.getColumnCount(); c++) {
456                 Comparable JavaDoc key = this.dataset.getColumnKey(c);
457                 Paint JavaDoc p = (Paint JavaDoc) this.sectionPaints.get(key);
458                 if (p == null) {
459                     this.sectionPaints.put(key,
460                             this.getDrawingSupplier().getNextPaint());
461                 }
462             }
463         }
464         else {
465             // row keys provide potential keys for individual pies
466
for (int r = 0; r < this.dataset.getRowCount(); r++) {
467                 Comparable JavaDoc key = this.dataset.getRowKey(r);
468                 Paint JavaDoc p = (Paint JavaDoc) this.sectionPaints.get(key);
469                 if (p == null) {
470                     this.sectionPaints.put(key,
471                             this.getDrawingSupplier().getNextPaint());
472                 }
473             }
474         }
475         
476     }
477     
478     /**
479      * Returns a collection of legend items for the pie chart.
480      *
481      * @return The legend items.
482      */

483     public LegendItemCollection getLegendItems() {
484
485         LegendItemCollection result = new LegendItemCollection();
486         
487         if (this.dataset != null) {
488             List JavaDoc keys = null;
489       
490             prefetchSectionPaints();
491             if (this.dataExtractOrder == TableOrder.BY_ROW) {
492                 keys = this.dataset.getColumnKeys();
493             }
494             else if (this.dataExtractOrder == TableOrder.BY_COLUMN) {
495                 keys = this.dataset.getRowKeys();
496             }
497
498             if (keys != null) {
499                 int section = 0;
500                 Iterator JavaDoc iterator = keys.iterator();
501                 while (iterator.hasNext()) {
502                     Comparable JavaDoc key = (Comparable JavaDoc) iterator.next();
503                     String JavaDoc label = key.toString();
504                     String JavaDoc description = label;
505                     Paint JavaDoc paint = (Paint JavaDoc) this.sectionPaints.get(key);
506                     LegendItem item = new LegendItem(label, description,
507                             null, null, Plot.DEFAULT_LEGEND_ITEM_CIRCLE,
508                             paint, Plot.DEFAULT_OUTLINE_STROKE, paint);
509
510                     result.add(item);
511                     section++;
512                 }
513             }
514             if (this.limit > 0.0) {
515                 result.add(new LegendItem(this.aggregatedItemsKey.toString(),
516                         this.aggregatedItemsKey.toString(), null, null,
517                         Plot.DEFAULT_LEGEND_ITEM_CIRCLE,
518                         this.aggregatedItemsPaint,
519                         Plot.DEFAULT_OUTLINE_STROKE,
520                         this.aggregatedItemsPaint));
521             }
522         }
523         return result;
524     }
525     
526     /**
527      * Tests this plot for equality with an arbitrary object. Note that the
528      * plot's dataset is not considered in the equality test.
529      *
530      * @param obj the object (<code>null</code> permitted).
531      *
532      * @return <code>true</code> if this plot is equal to <code>obj</code>, and
533      * <code>false</code> otherwise.
534      */

535     public boolean equals(Object JavaDoc obj) {
536         if (obj == this) {
537             return true;
538         }
539         if (!(obj instanceof MultiplePiePlot)) {
540             return false;
541         }
542         MultiplePiePlot that = (MultiplePiePlot) obj;
543         if (this.dataExtractOrder != that.dataExtractOrder) {
544             return false;
545         }
546         if (this.limit != that.limit) {
547             return false;
548         }
549         if (!this.aggregatedItemsKey.equals(that.aggregatedItemsKey)) {
550             return false;
551         }
552         if (!PaintUtilities.equal(this.aggregatedItemsPaint,
553                 that.aggregatedItemsPaint)) {
554             return false;
555         }
556         if (!ObjectUtilities.equal(this.pieChart, that.pieChart)) {
557             return false;
558         }
559         if (!super.equals(obj)) {
560             return false;
561         }
562         return true;
563     }
564     
565     /**
566      * Provides serialization support.
567      *
568      * @param stream the output stream.
569      *
570      * @throws IOException if there is an I/O error.
571      */

572     private void writeObject(ObjectOutputStream JavaDoc stream) throws IOException JavaDoc {
573         stream.defaultWriteObject();
574         SerialUtilities.writePaint(this.aggregatedItemsPaint, stream);
575     }
576
577     /**
578      * Provides serialization support.
579      *
580      * @param stream the input stream.
581      *
582      * @throws IOException if there is an I/O error.
583      * @throws ClassNotFoundException if there is a classpath problem.
584      */

585     private void readObject(ObjectInputStream JavaDoc stream)
586         throws IOException JavaDoc, ClassNotFoundException JavaDoc {
587         stream.defaultReadObject();
588         this.aggregatedItemsPaint = SerialUtilities.readPaint(stream);
589         this.sectionPaints = new HashMap JavaDoc();
590     }
591
592     
593 }
594
Popular Tags