sim.util.media.chart.ChartGenerator 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 2006 by Sean Luke
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.awt.geom.*;
import java.util.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.*;
import javax.swing.event.*;
import java.io.*;
// From MASON (cs.gmu.edu/~eclab/projects/mason/)
import sim.util.gui.LabelledList;
import sim.util.gui.NumberTextField;
import sim.util.gui.PropertyField;
// From JFreeChart (jfreechart.org)
import org.jfree.data.xy.*;
import org.jfree.chart.*;
import org.jfree.chart.axis.*;
import org.jfree.chart.event.*;
import org.jfree.chart.plot.*;
import org.jfree.data.general.*;
import org.jfree.chart.renderer.xy.*;
import org.jfree.data.general.*;
import org.jfree.chart.title.*;
// from iText (www.lowagie.com/iText/)
import com.lowagie.text.*;
import com.lowagie.text.pdf.*;
/**
ChartGenerator is a JPanel which displays a chart using the JFreeChart library. The class is abstract:
you'll need to use a concrete subclass to build a specific kind of chart.
The facility allows multiple time series to be displayed at one time, to be exported to PDF,
and to be dynamically added and removed.
Subclasses need to override at least four methods: getSeriesDataset(), update(), removeSeries(index), and buildChart().
Note that ChartGenerator has no standard API for adding a series to the chart, nor any standard way to modify
this series once it has been added. This is because JFreeChart has non-standard, non-consistent APIs for different
kinds of charts. You will need to implement these on a per-chart basis as you see fit.
ChartGenerator displays three regions:
- The chart proper, stored in a chartPanel. This panel is in turn stored in a JScrollPane.
- The globalAttributes, a collection of Components on the top-left which control global features
of the chart (its title, axis labels, etc.)
- The seriesAttributes, a scrollable collection of Components on the bottom-left which control features
of each separate series in the chart. Each seriesAttribute is associated in turn with a Stoppable (stored in
the list stoppables) which will have its stop() method called when the series is deleted from
the chart.
*/
public abstract class ChartGenerator extends JPanel
{
/** A holder for global attributes components */
protected Box globalAttributes = Box.createVerticalBox();
/** A holder for series attributes components */
protected Box seriesAttributes = Box.createVerticalBox();
public SeriesAttributes getSeriesAttributes(int seriesIndex)
{
Component[] c = seriesAttributes.getComponents();
return (SeriesAttributes)c[seriesIndex];
}
/** The chart */
protected JFreeChart chart;
/** The panel which holds and draws the chart */
protected ChartPanel chartPanel;
/** The JScrollPane which holdw the ChartPanel */
protected JScrollPane chartHolder = new JScrollPane();
/** The JFrame which stores the whole chart. Set in createFrame(), else null. */
protected JFrame frame;
/** The global attributes chart title field. */
protected PropertyField titleField;
/** The global attributes domain axis field. */
protected PropertyField xLabel;
/** The global attributes range axis field. */
protected PropertyField yLabel;
/** The global attributes logarithmic range axis check box. */
protected JCheckBox yLog;
/** The global attributes logarithmic domain axis check box. */
protected JCheckBox xLog;
public void setXAxisLogScaled(boolean isLogScaled){xLog.setSelected(isLogScaled);}
public boolean isXAxisLogScaled(){return xLog.isSelected();}
public void setYAxisLogScaled(boolean isLogScaled){yLog.setSelected(isLogScaled);}
public boolean isYAxisLogScaled(){return yLog.isSelected();}
/** Override this to return the JFreeChart data set used by your Chart. For example, time series charts
might return the XYSeriesCollection. */
public abstract AbstractSeriesDataset getSeriesDataset();
/** Override this to update the chart to reflect new data. */
public abstract void update();
/** Override this to remove a series from the chart. */
public abstract void removeSeries(int index);
/** Override this to move a series relative to other series. */
public abstract void moveSeries(int index, boolean up);
/** Override this to construct the appropriate kind of chart. This is the first thing called from the constructor; so certain
of your instance variables may not have been set yet and you may need to set them yourself. */
protected abstract void buildChart();
/** Deletes all series from the chart. */
public void removeAllSeries()
{
for(int x = getSeriesDataset().getSeriesCount()-1 ; x>=0 ; x--)
removeSeries(x);
}
/** Prepares the chart to be garbage collected. If you override this, be sure to call super.quit() */
public void quit()
{
removeAllSeries();
}
/** Returns the ChartPanel holding the chart. */
public ChartPanel getChartPanel() { return chartPanel; }
/** Adds a global attribute panel to the frame */
public void addGlobalAttribute(Component component)
{
globalAttributes.add(component);
}
/** Returns the global attribute panel of the given index. */
public Component getGlobalAttribute(int index)
{
// at present we have a PDF button and a chart global panel --
// then the global seriesAttributes start
return globalAttributes.getComponent(index+2);
}
/** Returns the number of global attribute panels. */
public int getGlobalAttributeCount()
{
// at present we have a PDF button and a chart global panel --
// then the global seriesAttributes start
return globalAttributes.getComponentCount()-2;
}
/** Remooves the global attribute at the given index and returns it. */
public Component removeGlobalAttribute(int index)
{
Component component = getGlobalAttribute(index);
globalAttributes.remove(index);
return component;
}
/** Sets the title of the chart (and the window frame). */
public void setTitle(String title)
{
chart.setTitle(title);
chart.titleChanged(new TitleChangeEvent(new org.jfree.chart.title.TextTitle(title)));
if (frame!=null) frame.setTitle(title);
titleField.setValue(title);
}
/** Returns the title of the chart */
public String getTitle()
{
return chart.getTitle().getText();
}
/** Sets the name of the Range Axis label -- usually this is the Y axis. */
public void setRangeAxisLabel(String val)
{
XYPlot xyplot = (XYPlot)(chart.getPlot());
xyplot.getRangeAxis().setLabel(val);
xyplot.axisChanged(new AxisChangeEvent(xyplot.getRangeAxis()));
yLabel.setValue(val);
}
/** Returns the name of the Range Axis Label -- usually this is the Y axis. */
public String getRangeAxisLabel()
{
return ((XYPlot)(chart.getPlot())).getRangeAxis().getLabel();
}
/** Sets the name of the Domain Axis label -- usually this is the X axis. */
public void setDomainAxisLabel(String val)
{
XYPlot xyplot = (XYPlot)(chart.getPlot());
xyplot.getDomainAxis().setLabel(val);
xyplot.axisChanged(new AxisChangeEvent(xyplot.getDomainAxis()));
xLabel.setValue(val);
}
/** Returns the name of the Domain Axis label -- usually this is the X axis. */
public String getDomainAxisLabel()
{
return ((XYPlot)(chart.getPlot())).getDomainAxis().getLabel();
}
/** Generates a new ChartGenerator with a blank chart. Before anything else, buildChart() is called. */
public ChartGenerator()
{
// create the chart
buildChart();
JSplitPane split = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true);
split.setBorder(new EmptyBorder(0,0,0,0));
JScrollPane scroll = new JScrollPane();
JPanel b = new JPanel();
b.setLayout(new BorderLayout());
b.add(seriesAttributes, BorderLayout.NORTH);
b.add(new JPanel(), BorderLayout.CENTER);
scroll.getViewport().setView(b);
scroll.setBackground(getBackground());
scroll.getViewport().setBackground(getBackground());
JPanel p = new JPanel();
p.setLayout(new BorderLayout());
LabelledList list = new LabelledList("Chart");
globalAttributes.add(list);
JLabel j = new JLabel("Right-Click or Control-Click");
j.setFont(j.getFont().deriveFont(10.0f).deriveFont(java.awt.Font.ITALIC));
list.add(j);
j = new JLabel("on Chart for More Options");
j.setFont(j.getFont().deriveFont(10.0f).deriveFont(java.awt.Font.ITALIC));
list.add(j);
/*
titleField = new JTextField();
titleField.setText(chart.getTitle().getText());
titleField.addKeyListener(new KeyListener()
{
public void keyReleased(KeyEvent keyEvent) {}
public void keyTyped(KeyEvent keyEvent) {}
public void keyPressed(KeyEvent keyEvent)
{
if (keyEvent.getKeyCode() == KeyEvent.VK_ENTER)
{
setTitle(titleField.getText());
}
else if (keyEvent.getKeyCode() == KeyEvent.VK_ESCAPE)
titleField.setText(getTitle());
}
});
titleField.addFocusListener(new FocusAdapter()
{
public void focusLost ( FocusEvent e )
{
setTitle(titleField.getText());
}
});
*/
titleField = new PropertyField()
{
public String newValue(String newValue)
{
setTitle(newValue);
getChartPanel().repaint();
return newValue;
}
};
titleField.setValue(chart.getTitle().getText());
list.add(new JLabel("Title"), titleField);
/*
xLabel = new JTextField();
xLabel.setText(getDomainAxisLabel());
xLabel.addKeyListener(new KeyListener()
{
public void keyReleased(KeyEvent keyEvent) {}
public void keyTyped(KeyEvent keyEvent) {}
public void keyPressed(KeyEvent keyEvent)
{
if (keyEvent.getKeyCode() == KeyEvent.VK_ENTER)
{
setDomainAxisLabel(xLabel.getText());
}
else if (keyEvent.getKeyCode() == KeyEvent.VK_ESCAPE)
xLabel.setText(getDomainAxisLabel());
}
});
xLabel.addFocusListener(new FocusAdapter()
{
public void focusLost ( FocusEvent e )
{
setDomainAxisLabel(xLabel.getText());
}
});
*/
xLabel = new PropertyField()
{
public String newValue(String newValue)
{
setDomainAxisLabel(newValue);
getChartPanel().repaint();
return newValue;
}
};
xLabel.setValue(getDomainAxisLabel());
list.add(new JLabel("X Label"), xLabel);
/*
yLabel = new JTextField();
yLabel.setText(getRangeAxisLabel());
yLabel.addKeyListener(new KeyListener()
{
public void keyReleased(KeyEvent keyEvent) {}
public void keyTyped(KeyEvent keyEvent) {}
public void keyPressed(KeyEvent keyEvent)
{
if (keyEvent.getKeyCode() == KeyEvent.VK_ENTER)
{
setRangeAxisLabel(yLabel.getText());
}
else if (keyEvent.getKeyCode() == KeyEvent.VK_ESCAPE)
yLabel.setText(getRangeAxisLabel());
}
});
yLabel.addFocusListener(new FocusAdapter()
{
public void focusLost ( FocusEvent e )
{
setRangeAxisLabel(yLabel.getText());
}
});
*/
yLabel = new PropertyField()
{
public String newValue(String newValue)
{
setRangeAxisLabel(newValue);
getChartPanel().repaint();
return newValue;
}
};
yLabel.setValue(getRangeAxisLabel());
list.add(new JLabel("Y Label"), yLabel);
xLog = new JCheckBox();
xLog.addChangeListener(new ChangeListener(){
public void stateChanged(ChangeEvent e)
{
if(xLog.isSelected())
{
LogarithmicAxis logAxis = new LogarithmicAxis(xLabel.getValue());
logAxis.setStrictValuesFlag(false);
chart.getXYPlot().setDomainAxis(logAxis);
}
else
chart.getXYPlot().setDomainAxis(new NumberAxis(xLabel.getValue()));
}
});
list.add(new JLabel("Log X axis"), xLog);
yLog = new JCheckBox();
yLog.addChangeListener(new ChangeListener(){
public void stateChanged(ChangeEvent e)
{
if(yLog.isSelected())
{
LogarithmicAxis logAxis = new LogarithmicAxis(yLabel.getValue());
logAxis.setStrictValuesFlag(false);
chart.getXYPlot().setRangeAxis(logAxis);
}
else
chart.getXYPlot().setRangeAxis(new NumberAxis(yLabel.getValue()));
}
});
list.add(new JLabel("Log Y axis"), yLog);
final JCheckBox legendCheck = new JCheckBox();
legendCheck.setSelected(false);
ItemListener il = new ItemListener()
{
public void itemStateChanged(ItemEvent e)
{
if (e.getStateChange() == ItemEvent.SELECTED)
{
LegendTitle title = new LegendTitle(chart.getXYPlot());
title.setLegendItemGraphicPadding(new org.jfree.ui.RectangleInsets(0,8,0,4));
chart.addLegend(title);
}
else
{
chart.removeLegend();
}
}
};
legendCheck.addItemListener(il);
list.add(new JLabel("Legend"), legendCheck);
final JCheckBox aliasCheck = new JCheckBox();
aliasCheck.setSelected(chart.getAntiAlias());
il = new ItemListener()
{
public void itemStateChanged(ItemEvent e)
{
chart.setAntiAlias( e.getStateChange() == ItemEvent.SELECTED );
}
};
aliasCheck.addItemListener(il);
list.add(new JLabel("Antialias"), aliasCheck);
JPanel pdfButtonPanel = new JPanel();
pdfButtonPanel.setLayout(new BorderLayout());
JButton pdfButton = new JButton( "Save as PDF" );
pdfButtonPanel.add(pdfButton,BorderLayout.WEST);
pdfButton.addActionListener(new ActionListener()
{
public void actionPerformed ( ActionEvent e )
{
FileDialog fd = new FileDialog(frame,"Choose PDF file...", FileDialog.SAVE);
fd.setFile(chart.getTitle().getText() + ".pdf");
fd.setVisible(true);;
String fileName = fd.getFile();
if (fileName!=null)
{
Dimension dim = chartPanel.getPreferredSize();
generatePDF( chart, dim.width, dim.height, fd.getDirectory() + fileName );
}
}
});
globalAttributes.add(pdfButtonPanel);
// we add into an outer box so we can later on add more global seriesAttributes
// as the user instructs and still have glue be last
Box outerAttributes = Box.createVerticalBox();
outerAttributes.add(globalAttributes);
outerAttributes.add(Box.createGlue());
p.add(outerAttributes,BorderLayout.NORTH);
p.add(scroll,BorderLayout.CENTER);
p.setMinimumSize(new Dimension(0,0));
p.setPreferredSize(new Dimension(200,0));
split.setLeftComponent(p);
chartHolder.setMinimumSize(new Dimension(0,0));
split.setRightComponent(chartHolder);
setLayout(new BorderLayout());
add(split,BorderLayout.CENTER);
// set the default to be white, which looks good when printed
chart.setBackgroundPaint(Color.WHITE);
}
/** Returns a JFrame suitable or housing the ChartGenerator. This frame largely calls chart.quit() when
the JFrame is being closed. */
public JFrame createFrame( final sim.display.GUIState state )
{
frame = new JFrame()
{
public void dispose()
{
quit();
super.dispose();
}
};
// these bugs are tickled by our constant redraw requests.
frame.addComponentListener(new ComponentAdapter()
{
// Bug in MacOS X Java 1.3.1 requires that we force a repaint.
public void componentResized (ComponentEvent e)
{
// Utilities.doEnsuredRepaint(ChartGenerator.this);
}
});
frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
frame.getContentPane().setLayout(new BorderLayout());
frame.getContentPane().add(this,BorderLayout.CENTER);
frame.setResizable(true);
frame.pack();
frame.setTitle(chart.getTitle().getText());
return frame;
}
/* Generates PDF from the chart, saving ou to the given file. width and height are the
desired width and height of the chart in points. */
void generatePDF( JFreeChart chart, int width, int height, String fileName )
{
try
{
Document document = new Document(new com.lowagie.text.Rectangle(width,height));
PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(fileName));
document.addAuthor("MASON");
document.open();
PdfContentByte cb = writer.getDirectContent();
PdfTemplate tp = cb.createTemplate(width, height);
Graphics2D g2 = tp.createGraphics(width, height, new DefaultFontMapper());
Rectangle2D rectangle2D = new Rectangle2D.Double(0, 0, width, height);
chart.draw(g2, rectangle2D);
g2.dispose();
cb.addTemplate(tp, 0, 0);
document.close();
}
catch( Exception e )
{
e.printStackTrace();
}
}
static
{
// quaquaify
try
{
String version = System.getProperty("java.version");
// both of the following will generate exceptions if the class doesn't exist, so we're okay
// trying to put them in the UIManager here
if (version.startsWith("1.3"))
{
// broken
//UIManager.put("ColorChooserUI",
// Class.forName("ch.randelshofer.quaqua.Quaqua13ColorChooserUI").getName());
}
else // hope there's no one using 1.2!
UIManager.put("ColorChooserUI",
Class.forName("ch.randelshofer.quaqua.Quaqua14ColorChooserUI").getName());
}
catch (Exception e) { }
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy