sim.util.media.chart.PieChartGenerator Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of mason Show documentation
Show all versions of mason Show documentation
MASON is a fast discrete-event multiagent simulation library core in Java, designed to be the foundation for large custom-purpose Java simulations, and also to provide more than enough functionality for many lightweight simulation needs. MASON contains both a model library and an optional suite of visualization tools in 2D and 3D.
The newest version!
/*
Copyright 2013 by Sean Luke and George Mason University
Licensed under the Academic Free License version 3.0
See the file "LICENSE" for more information
*/
package sim.util.media.chart;
import java.awt.*;
import java.util.*;
import java.awt.event.*;
import javax.swing.*;
import sim.util.gui.*;
// From JFreeChart
import org.jfree.chart.*;
import org.jfree.chart.plot.*;
import org.jfree.data.general.*;
import org.jfree.data.statistics.*;
import org.jfree.chart.renderer.xy.*;
import org.jfree.data.category.*;
import org.jfree.chart.title.*;
import org.jfree.ui.*;
// from iText (www.lowagie.com/iText/)
import com.lowagie.text.*;
import com.lowagie.text.pdf.*;
/* // looks like we'll have to move to these soon
import com.itextpdf.text.*;
import com.itextpdf.text.pdf.*;
*/
/** A ChartGenerator for Pie Charts. */
public class PieChartGenerator extends ChartGenerator
{
public void removeSeries(int index)
{
super.removeSeries(index);
update();
}
public void moveSeries(int index, boolean up)
{
super.moveSeries(index, up);
update();
}
/** The total number of unique groups permitted in the generator, to keep from overwhelming JFreeChart. */
public static final int MAXIMUM_PIE_CHART_ITEMS = 20;
final DefaultCategoryDataset emptyDataset = new DefaultCategoryDataset();
public Dataset getSeriesDataset() { return ((MultiplePiePlot)(chart.getPlot())).getDataset(); }
public void setSeriesDataset(Dataset obj)
{
// here we will interrupt things if they're too big
if (((CategoryDataset)obj).getRowCount() > MAXIMUM_PIE_CHART_ITEMS)
{
((MultiplePiePlot)(chart.getPlot())).setDataset(emptyDataset);
setInvalidChartTitle("[[ Dataset has too many items. ]]");
}
else
{
((MultiplePiePlot)(chart.getPlot())).setDataset((DefaultCategoryDataset)obj);
if (invalidChartTitle != null)
setInvalidChartTitle(null);
}
}
public int getProspectiveSeriesCount(Object[] objs)
{
HashMap map = convertIntoAmountsAndLabels(objs);
String[] labels = revisedLabels(map);
return labels.length;
}
public int getSeriesCount()
{
SeriesAttributes[] sa = getSeriesAttributes();
return sa.length; // we do this instead of returning the columns in the dataset because hidden series don't have columns (stupid JFreeChart)
}
protected void buildChart()
{
DefaultCategoryDataset dataset = new DefaultCategoryDataset();
chart = ChartFactory.createMultiplePieChart("Untitled Chart", dataset,
org.jfree.util.TableOrder.BY_COLUMN, false, true, false);
chart.setAntiAlias(true);
//chartPanel = new ScrollableChartPanel(chart, true);
chartPanel = buildChartPanel(chart);
//chartHolder.getViewport().setView(chartPanel);
setChartPanel(chartPanel);
JFreeChart baseChart = (JFreeChart) ((MultiplePiePlot)(chart.getPlot())).getPieChart();
PiePlot base = (PiePlot) (baseChart.getPlot());
base.setIgnoreZeroValues(true);
base.setLabelOutlinePaint(java.awt.Color.WHITE);
base.setLabelShadowPaint(java.awt.Color.WHITE);
base.setMaximumLabelWidth(0.25); // allow bigger labels by a bit (this will make the chart smaller)
base.setInteriorGap(0.000); // allow stretch to compensate for the bigger label width
base.setLabelBackgroundPaint(java.awt.Color.WHITE);
base.setOutlinePaint(null);
base.setBackgroundPaint(null);
base.setShadowPaint(null);
base.setSimpleLabels(false); // I think they're false anyway
// change the look of the series title to be smaller
StandardChartTheme theme = new StandardChartTheme("Hi");
TextTitle title = new TextTitle("Whatever", theme.getLargeFont());
title.setPaint(theme.getAxisLabelPaint());
title.setPosition(RectangleEdge.BOTTOM);
baseChart.setTitle(title);
// this must come last because the chart must exist for us to set its dataset
setSeriesDataset(dataset);
}
protected void update()
{
// We have to rebuild the dataset from scratch (deleting and replacing it) because JFreeChart's
// piechart facility doesn't have a way to move series. Just like the histogram system: stupid stupid stupid.
SeriesAttributes[] sa = getSeriesAttributes();
DefaultCategoryDataset dataset = new DefaultCategoryDataset();
for(int i=0; i < sa.length; i++)
if (sa[i].isPlotVisible())
{
PieChartSeriesAttributes attributes = (PieChartSeriesAttributes)(sa[i]);
Object[] elements = attributes.getElements();
double[] values = null;
String[] labels = null;
if (elements != null)
{
HashMap map = convertIntoAmountsAndLabels(elements);
labels = revisedLabels(map);
values = amounts(map, labels);
}
else
{
values = attributes.getValues();
labels = attributes.getLabels();
}
UniqueString seriesName = new UniqueString(attributes.getSeriesName());
for(int j = 0; j < values.length; j++)
dataset.addValue(values[j], labels[j], seriesName); // ugh
}
setSeriesDataset(dataset);
}
public PieChartGenerator()
{
// buildChart is called by super() first
}
protected PieChartSeriesAttributes buildNewAttributes(String name, SeriesChangeListener stopper)
{
return new PieChartSeriesAttributes(this, name, getSeriesCount(), stopper);
}
/** Adds a series, plus a (possibly null) SeriesChangeListener which will receive a single
event if/when the series is deleted from the chart by the user. Returns the series attributes. */
public SeriesAttributes addSeries(double[] amounts, String[] labels, String name, SeriesChangeListener stopper)
{
int i = getSeriesCount();
// need to have added the dataset BEFORE calling this since it'll try to change the name of the series
PieChartSeriesAttributes csa = buildNewAttributes(name, stopper);
// set information
csa.setValues((double[])(amounts.clone()));
csa.setLabels((String[])(labels.clone()));
seriesAttributes.add(csa);
revalidate(); // display the new series panel
update();
// won't update properly unless I force it here by letting all the existing scheduled events to go through. Dumb design. :-(
SwingUtilities.invokeLater(new Runnable() { public void run() { update(); } });
return csa;
}
/** Adds a series, plus a (possibly null) SeriesChangeListener which will receive a single
event if/when the series is deleted from the chart by the user. Returns the series attributes. */
public SeriesAttributes addSeries(Object[] objs, String name, SeriesChangeListener stopper)
{
int i = getSeriesCount();
// need to have added the dataset BEFORE calling this since it'll try to change the name of the series
PieChartSeriesAttributes csa = buildNewAttributes(name, stopper);
// set information
csa.setElements((Object[])(objs.clone()));
seriesAttributes.add(csa);
revalidate(); // display the new series panel
update();
// won't update properly unless I force it here by letting all the existing scheduled events to go through. Dumb design. :-(
SwingUtilities.invokeLater(new Runnable() { public void run() { update(); } });
return csa;
}
/** Adds a series, plus a (possibly null) SeriesChangeListener which will receive a single
event if/when the series is deleted from the chart by the user. Returns the series attributes. */
public SeriesAttributes addSeries(Collection objs, String name, SeriesChangeListener stopper)
{
//int i = getSeriesCount();
// need to have added the dataset BEFORE calling this since it'll try to change the name of the series
PieChartSeriesAttributes csa = buildNewAttributes(name, stopper);
// set information
csa.setElements(new ArrayList(objs));
seriesAttributes.add(csa);
revalidate(); // display the new series panel
update();
// won't update properly unless I force it here by letting all the existing scheduled events to go through. Dumb design. :-(
SwingUtilities.invokeLater(new Runnable() { public void run() { update(); } });
return csa;
}
// Takes objects and produces an object->count mapping
HashMap convertIntoAmountsAndLabels(Object[] objs)
{
// Total the amounts
HashMap map = new HashMap();
for(int i = 0; i < objs.length; i++)
{
String label = "null";
if (objs[i] != null)
label = objs[i].toString();
if (map.containsKey(label))
map.put(label,
new Double(((Double)(map.get(label))).doubleValue() + 1));
else
map.put(label, new Double(1));
}
return map;
}
// Sorts labels from the mapping. We may get rid of this later perhaps.
String[] revisedLabels(HashMap map)
{
// Sort labels
String[] labels = new String[map.size()];
labels = (String[])(map.keySet().toArray(labels));
Arrays.sort(labels);
return labels;
}
// Returns the counts from the mapping, in the same order as the labels
double[] amounts(HashMap map, String[] revisedLabels)
{
// Extract amounts
double[] amounts = new double[map.size()];
for(int i = 0; i < amounts.length; i++)
amounts[i] = ((Double)(map.get(revisedLabels[i]))).doubleValue();
return amounts;
}
public void updateSeries(int index, Collection objs)
{
if (index < 0) // this happens when we're a dead chart but the inspector doesn't know
return;
if (index >= getNumSeriesAttributes()) // this can happen when we close a window if we use the Histogram in a display
return;
PieChartSeriesAttributes hsa = (PieChartSeriesAttributes)(getSeriesAttribute(index));
hsa.setElements(new ArrayList(objs));
}
public void updateSeries(int index, Object[] objs)
{
if (index < 0) // this happens when we're a dead chart but the inspector doesn't know
return;
if (index >= getNumSeriesAttributes()) // this can happen when we close a window if we use the Histogram in a display
return;
PieChartSeriesAttributes hsa = (PieChartSeriesAttributes)(getSeriesAttribute(index));
hsa.setElements((Object[])(objs.clone()));
}
public void updateSeries(int index, double[] amounts, String[] labels)
{
if (index < 0) // this happens when we're a dead chart but the inspector doesn't know
return;
if (index >= getNumSeriesAttributes()) // this can happen when we close a window if we use the Histogram in a display
return;
PieChartSeriesAttributes hsa = (PieChartSeriesAttributes)(getSeriesAttribute(index));
hsa.setValues((double[])(amounts.clone()));
hsa.setLabels((String[])(labels.clone()));
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy