I have to say that JFreeChart is a great toolkit. I'm very impressed with all of the work that has been done and the feature set available.
I have been using the .20 library with great success and now I find myself trying to use JFreeChart in a way that I know you never intended.

You see... I'm using JFreeChart as an interactive tool to drill down into data via a mouse click. As feedback to the user, as they move the mouse over the 3d Bars, the bar changes color.
I had this working weeks ago, but the powers that be decided they wanted something else. Now the time has come to revisit this issue and for some reason I cannot get the same performance that I had before.
I'm hoping someone can give me a few pointers on how to squeeze out the most performance when repainting. I realize that JFreeChart by design is not meant for this, as it repaints everything whenever there is the slightest change. Given that... is there anything that I can do to eek out a bit more performance? I'm noticing a 1.5 second delay repainting the bar graph on a linux 700mhz pII box. (which is the targeted low end web browser client)

Any ideas?
-Dennis
Code: Select all
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.GradientPaint;
import java.awt.Paint;
import javax.swing.JPanel;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartMouseEvent;
import org.jfree.chart.ChartMouseListener;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.CategoryAxis;
import org.jfree.chart.axis.CategoryLabelPositions;
import org.jfree.chart.axis.LogarithmicAxis;
import org.jfree.chart.entity.CategoryItemEntity;
import org.jfree.chart.entity.ChartEntity;
import org.jfree.chart.event.ChartChangeEvent;
import org.jfree.chart.event.ChartChangeListener;
import org.jfree.chart.labels.ItemLabelAnchor;
import org.jfree.chart.labels.ItemLabelPosition;
import org.jfree.chart.labels.StandardCategoryLabelGenerator;
import org.jfree.chart.plot.CategoryPlot;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.renderer.BarRenderer3D;
import org.jfree.data.CategoryDataset;
import org.jfree.data.DefaultCategoryDataset;
import org.jfree.ui.TextAnchor;
/**
*
* @author dklotz
*/
public class CallQDistributionChart extends JPanel implements TableModelListener, ChartMouseListener, ChartChangeListener
{
private static boolean DEBUG = false;
protected DefaultCategoryDataset dataset = new DefaultCategoryDataset();
private CallQGraphTableModel dbModel;
private JFreeChart chart;
private int selectedItem;
public Color barColor = Color.GREEN;
private ChartPanel chartPanel;
private String sourceColumnName;
private CallQDistributionChartToolTipGenerator tooltips;
private CallQStackedBarRenderer3D renderer;
private int oldSelectedItem = -1;
private Paint oldpaint = null;
static class LabelGenerator extends StandardCategoryLabelGenerator
{
public String generateItemLabel (final CategoryDataset dataset,
final int series,
final int category)
{
return dataset.getRowKey (series).toString ();
}
}
/** Creates a new instance of CallQDistributionChart */
public CallQDistributionChart(CallQGraphTableModel dbModel, String sourceColumnName)
{
super();
this.dbModel = dbModel;
this.sourceColumnName = sourceColumnName;
setLayout(new BorderLayout());
tooltips = new CallQDistributionChartToolTipGenerator();
createDataset();
chart = createChart();
chartPanel = new ChartPanel(chart);
chartPanel.addChartMouseListener (this);
add(chartPanel, BorderLayout.CENTER);
}
public JFreeChart getChart()
{
return chart;
}
protected void removeDataset()
{
dataset.clear();
}
protected void createDataset()
{
StringBuffer chartData;
StringBuffer tooltipData;
long value;
//data[row][0] lower limit of attribut value range (inclusive)
//data[row][1] upper limit of attribute value range (inclusive)
//data[row][2] actual minimum value of attribute in the range
//data[row][3] actual maximum value of attribute in the range
//data[row][4] number of data values (instances) with attribute values falling
// within the specified range.
for (int i=0; i<dbModel.getRowCount (); i++)
{
if (DEBUG)
{
System.out.print ("Min=" + dbModel.getDistributionValueAt (i, 0));
System.out.print (" : Max=" + dbModel.getDistributionValueAt (i, 1));
System.out.print (" : actual Min=" + dbModel.getDistributionValueAt (i, 2));
System.out.print (" : actual Max=" + dbModel.getDistributionValueAt (i, 3));
System.out.println (" : Num instances=" + dbModel.getDistributionValueAt (i, 4));
}
chartData = null;
chartData = new StringBuffer(20);
tooltipData = null;
tooltipData = new StringBuffer(20);
value = ((Long)dbModel.getDistributionValueAt (i, 0)).longValue ();
chartData.append (value);
chartData.append (" - ");
chartData.append (dbModel.getDistributionValueAt (i, 1));
tooltipData.append ("Actual data range: ");
tooltipData.append ("(");
tooltipData.append (dbModel.getDistributionValueAt (i, 2));
tooltipData.append ("-");
tooltipData.append (dbModel.getDistributionValueAt (i, 3));
tooltipData.append (")");
tooltips.addToolTip (tooltipData.toString ());
dataset.addValue (((Long) dbModel.getDistributionValueAt (i, 4)).doubleValue (), chartData.toString (), chartData.toString ());
}
}
public JFreeChart createChart()
{
String timeLabel = (dbModel.getKeyLevel() == ColDescr.KEY_HOUR?"Time in Hours": "Time in Minutes");
String chartTitle = "Distribution of " + this.sourceColumnName;
JFreeChart chart = ChartFactory.createBarChart3D (chartTitle,
"Distribution Range",
"Number of Calls",
dataset,
PlotOrientation.VERTICAL,
false,
true,
false);
chart.setBackgroundPaint (new GradientPaint (0, 0, Color.white, 0, 1000, CallQBaseUI.backGroundColor));
CategoryPlot plot = chart.getCategoryPlot();
plot.setDomainGridlinePaint(Color.LIGHT_GRAY);
plot.setRangeGridlinePaint(Color.LIGHT_GRAY);
plot.setDomainGridlinesVisible(true);
plot.setRangeGridlinesVisible(true);
// modified version to allow greater bar widths when data is arranged
// with 1 category item in a series and there are multiple series...
renderer = new CallQStackedBarRenderer3D();
renderer.setLabelGenerator (new CallQDistributionChart.LabelGenerator());
renderer.setItemLabelsVisible (true);
renderer.setToolTipGenerator (tooltips);
final ItemLabelPosition p = new ItemLabelPosition (ItemLabelAnchor.OUTSIDE12,
TextAnchor.CENTER_LEFT,
TextAnchor.CENTER_LEFT,
0.0);
renderer.setPositiveItemLabelPosition (p);
renderer.setItemLabelAnchorOffset (18.0);
final ItemLabelPosition p2 = new ItemLabelPosition (ItemLabelAnchor.OUTSIDE2,
TextAnchor.CENTER_LEFT,
TextAnchor.CENTER_LEFT,
0.0);
renderer.setPositiveItemLabelPositionFallback (p2);
renderer.setItemMargin(0.01); // one percent
int numSeries = dataset.getRowCount ();
for (int i=0; i < numSeries; i++)
{
renderer.setSeriesPaint(i, Color.BLUE);
}
// dk renderer.setDrawBarOutline (true);
// dk renderer.setOutlinePaint (Color.BLACK);
plot.setRenderer (renderer);
final LogarithmicAxis rangeAxis = new LogarithmicAxis("Number of Calls");
rangeAxis.setAllowNegativesFlag (true);
rangeAxis.setStrictValuesFlag (false);
rangeAxis.setLog10TickLabelsFlag (false);
plot.setRangeAxis (rangeAxis);
CategoryAxis axis = plot.getDomainAxis ();
axis.setCategoryLabelPositions(CategoryLabelPositions.UP_45);
axis.setCategoryMargin(0.30); // Creates a normal sized bar
return chart;
}
public void tableChanged (TableModelEvent e)
{
removeDataset ();
createDataset ();
}
public ChartPanel getChartPanel()
{
return chartPanel;
}
public void chartMouseClicked (ChartMouseEvent event)
{
Object o = event.getEntity ();
if (o instanceof CategoryItemEntity)
{
CategoryItemEntity cie = (CategoryItemEntity) o;
Integer categoryindex;
int i;
selectedItem = cie.getSeries ();
if ((cie.getCategory () != null) &&
(cie.getCategoryIndex () >= 0))
{
i = cie.getCategoryIndex ();
// for future use... do the click drill down event now...
}
}
}
/**
* As the mouse is moved over a bar, change its color to red.
*/
public void chartMouseMoved (ChartMouseEvent event)
{
ChartEntity ce = chartPanel.getEntityForPoint (event.getTrigger ().getX (), event.getTrigger ().getY ());
if ((ce != null) &&
(ce.getClass () == CategoryItemEntity.class))
{
Integer categoryindex;
int i;
CategoryItemEntity cie = (CategoryItemEntity) ce;
selectedItem = cie.getSeries ();
if ((cie.getCategory () != null) &&
(cie.getCategoryIndex () >= 0))
{
i = cie.getCategoryIndex ();
categoryindex = new Integer(i);
if (oldSelectedItem != selectedItem)
{
if (oldpaint != null)
{
event.getChart ().getCategoryPlot ().getRenderer ().setSeriesPaint (oldSelectedItem, oldpaint);
}
oldSelectedItem = selectedItem;
oldpaint = event.getChart ().getCategoryPlot ().getRenderer ().getSeriesPaint (selectedItem);
event.getChart ().getCategoryPlot ().getRenderer ().setSeriesPaint (selectedItem, Color.RED);
}
}
}
else
{
if ((oldpaint != null) &&
(oldSelectedItem != -1))
{
event.getChart ().getCategoryPlot ().getRenderer ().setSeriesPaint (oldSelectedItem, oldpaint);
oldSelectedItem = -1;
oldpaint = null;
}
}
}
public void chartChanged (ChartChangeEvent ce)
{
if (DEBUG)
{
System.out.println (this.getClass ().getName () + ": chartChanged: " + ce.toString ());
}
}
}