All Downloads are FREE. Search and download functionalities are using the official Maven repository.

sim.util.media.chart.PieChartGenerator Maven / Gradle / Ivy

Go to download

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