Print

Print


Currently, using the Console "Model" tab and inspectors for data, the bar and pie chart values ("y axis" in bar chart) is generated from *counts* of the Objects[ ].

I suggest adding a new feature to produce bar and pie charts from data, where each datum is a pair: label (String) and value (double). I have implemented this using the code below, but it would be nice if this were added to the existing classes rather than as a separate chart type ("Make Bar Chart from Values")

By the way, the documentation in the MASON manual doesn't clearly explain what data type is required for each inspector (e.g. data of type double[ ] cannot be used in a bar chart).

Russ Thomas

Here is the code.  The main customization is the last file: CustomBarChartGenerator.  I put "BarChartDatum" in sim.util but that may not be the best place. Also a more generic name would be better.
-----------------
package sim.portrayal.inspector
file: propertyinspector.classes

# This is the propertyinspector.classes file.  When a
# user chooses from various PropertyInspector subclasses,
# the ones presented to him are selected from this file,
# assuming they can respond to the type of property he
# is examining.  The common place for this to occur is
# when a user clicks on the magnifying glass icon next
# to a property in a SimpleInspector, producing a pop-up
# menu constructed from the classes below.  Add your
# PropertyInspector subclass to the list below if you wish
# it to be included for consideration in that menu.
# Empty lines in this file are ignored, as 
# is any part of a line which starts with a pound sign.
# RCT added ValuesBarChartingPropertyInspector

sim.portrayal.inspector.StreamingPropertyInspector
sim.portrayal.inspector.TimeSeriesChartingPropertyInspector
sim.portrayal.inspector.HistogramChartingPropertyInspector
sim.portrayal.inspector.ScatterPlotChartingPropertyInspector
sim.portrayal.inspector.PieChartChartingPropertyInspector
sim.portrayal.inspector.BarChartChartingPropertyInspector
sim.portrayal.inspector.BubbleChartChartingPropertyInspector
sim.portrayal.inspector.BoxPlotChartingPropertyInspector
sim.portrayal.inspector.ValuesBarChartingPropertyInspector

-------------------------
package sim.portrayal.inspector;

import java.awt.Frame;
import sim.display.GUIState;
import sim.engine.Stoppable;
import sim.portrayal.inspector.PieChartChartingPropertyInspector;
import sim.util.Properties;
import sim.util.media.chart.BarChartGenerator;
import sim.util.media.chart.ChartGenerator;
import sim.util.media.chart.CustomBarChartGenerator;


/**
 *
 * @author russellthomas
 */
/** A property inspector which generates various bar charts of data.  The bar charts update in real-time as
    requested by the user.  Data properties for which
    the BarChartChartingPropertyInspector will operate include:
        
    <ul>
    <li>Any array of BarChartDatum
    </ul>
        
    <p>CustomBarChartChartingPropertyInspector registers itself with the property menu option "Make Bar Chart from Values".
*/
public class ValuesBarChartingPropertyInspector extends PieChartChartingPropertyInspector{

    protected boolean validChartGenerator(ChartGenerator generator) { return generator instanceof BarChartGenerator; }
        
    public static String name() { return "Make Bar Chart from Values"; }
    public static Class[] types() 
        {
        return new Class[]
            {
            new Object[0].getClass(), java.util.Collection.class,
            };
        }

    public ValuesBarChartingPropertyInspector(Properties properties, int index, Frame parent, final GUIState simulation)
        {
        super(properties,index,parent,simulation);
        }
    
    public ValuesBarChartingPropertyInspector(Properties properties, int index, final GUIState simulation, ChartGenerator generator)
        {
        super(properties, index, simulation, generator);
        }
    
    protected ChartGenerator createNewGenerator()
        {
        return new CustomBarChartGenerator()
            {
            public void quit()
                {
                super.quit();
                Stoppable stopper = getStopper();
                if (stopper!=null) stopper.stop();

                // remove the chart from the GUIState's charts
                getCharts(simulation).remove(this);
                }
            };
        }
    }

--------------

package sim.util;
/**
 *
 * @author russellthomas
 */
public class BarChartDatum implements SimpleDatum{
    String label = "";
    public double value = 0.0;
    
    public BarChartDatum(String n, double v){
        this.label = n;
        this.value = v;
    }
    
    @Override
    public String toString(){
        return label;
    }
    
    @Override
    public double getValue(){
        return value;
    }
    
}
----------------

package sim.util;

/**
 *
 * @author russellthomas
 */
public interface  SimpleDatum {
    @Override
    String toString();
    double getValue();
    
}

-------------------

package sim.util.media.chart;

/**
 *
 * @author russellthomas
 */

import java.util.*;

// From JFreeChart
import org.jfree.data.category.*;

import sim.util.*;


public class CustomBarChartGenerator extends BarChartGenerator {

       @Override
       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())
                {
                BarChartSeriesAttributes attributes = (BarChartSeriesAttributes)(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);
        }

    // RCT MODIFIED Returns labels from object "toString" and amounts from "getValue()" in each object
    //   Objects must implement SimpleDatum interface with getValue()
    HashMap convertIntoAmountsAndLabels(Object[] objs)
        {
        // Total the amounts
        HashMap map = new HashMap();
        for(int i = 0; i < objs.length; i++)
            {
            BarChartDatum d = (BarChartDatum)objs[i];
            String label = "null";
            if (d != null)
                label = d.toString();
            if (map.containsKey(label))
                map.put(label,
                    new Double(d.getValue()));
            else
                map.put(label,
                    new Double(d.getValue()));
            }
        return map;
        }
    
    // 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;
        }
    
    // 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;
        }

}