second, i've made some changes to the tooltip generation mechanism which might be useful to others. when the number of data points is large (say, 10K+), pre-generation of tooltips becomes a very expensive operation, in terms of memory usage. the following code allows for on-the-fly tooltip generation (no changes to the library, though a clean design would require changes)
Code: Select all
usage...
JFreeChart chart = ...
// indicates that a buffer should be used so that the image isn't re-generated each time
boolean useBuffer = true;
// if true, the series/category will be collected and used in the generated tooltip text
boolean collectSeries = true;
boolean collectCategory = true;
ToolTipGenerator generator = StandardCategoryToolTipGenerator.CreateAdapter( collectSeries, collectCategory );
ToolTipGeneratingChartPanel panel = new ToolTipGeneratingChartPanel( chart, useBuffer, generator );
public class ToolTipGeneratingChartPanel extends ChartPanel {
private ToolTipGenerator _generator;
public ToolTipGeneratingChartPanel( JFreeChart chart, boolean useBuffer, ToolTipGenerator generator ) {
super( chart, useBuffer );
_generator = generator;
}
public ToolTip getToolTip( MouseEvent e ) {
ChartEntity entity = getEntityForPoint( e.getX(), e.getY() );
return _generator.generateToolTip( getChart(), entity );
}
public String getToolTipText(MouseEvent e) {
ToolTip tooltip = getToolTip( e );
if( tooltip != null)
return tooltip.getText();
return null;
}
}
public interface ToolTipGenerator {
ToolTip generateToolTip( JFreeChart chart, ChartEntity entity );
}
public interface ToolTip {
String getText();
}
public interface CategoryToolTipGenerator {
ToolTip generateToolTip( CategoryDataset data, CategoryItemEntity entity );
public static class Adapter implements ToolTipGenerator {
private CategoryToolTipGenerator _generator;
public Adapter( CategoryToolTipGenerator generator ) {
_generator = generator;
}
public ToolTip generateToolTip( JFreeChart chart, ChartEntity entity ) {
return _generator.generateToolTip( chart.getCategoryPlot().getCategoryDataset(), (CategoryItemEntity) entity );
}
}
}
public class StandardCategoryToolTipGenerator implements CategoryToolTipGenerator {
public static ToolTipGenerator CreateAdapter( boolean collectSeries, boolean collectCategory ) {
StandardCategoryToolTipGenerator generator = new StandardCategoryToolTipGenerator( collectSeries, collectCategory );
return new CategoryToolTipGenerator.Adapter( generator );
}
private boolean _collectSeries;
private boolean _collectCategory;
public StandardCategoryToolTipGenerator() {
this( true, true );
}
public StandardCategoryToolTipGenerator( boolean collectSeries, boolean collectCategory ) {
_collectSeries = collectSeries;
_collectCategory = collectCategory;
}
public ToolTip generateToolTip( CategoryDataset data, CategoryItemEntity entity ) {
Object series = null;
if( _collectSeries )
series = data.getRowKey( entity.getSeries() );
Object category = null;
if( _collectCategory )
category = entity.getCategory();
Object value = data.getValue( entity.getSeries(), entity.getCategoryIndex() );
return createCategoryToolTip( series, category, value );
}
/**
* rather than having a separate formatting mechanism, if a different text format
* is required, just override this method and return a subclass of CategoryToolTip
* which specializes ToolTip:getText()
*/
protected ToolTip createCategoryToolTip( Object series, Object category, Object value ) {
return new CategoryToolTip( series, category, value );
}
}
public class CategoryToolTip implements ToolTip {
private Object _series;
private Object _category;
private Object _value;
public CategoryToolTip( Object series, Object category, Object value ) {
_series = series;
_category = category;
_value = value;
}
public Object getSeries() {
return _series;
}
public Object getCategory() {
return _category;
}
public Object getValue() {
return _value;
}
/* the get*String() methods act as simple formatting hooks */
public String getSeriesString() {
return _series != null ? _series.toString() : null;
}
public String getCategoryString() {
return _category != null ? _category.toString() : null;
}
public String getValueString() {
return _value != null ? _value.toString() : null;
}
public String getText() {
String result = "";
if( getSeries() != null )
result += getSeriesString();
if( getCategory() != null ) {
if( result.length() > 0 )
result += ", ";
result += getCategoryString();
}
if( getValue() != null ) {
if( result.length() > 0 )
result += " = ";
result += getValueString();
}
return result;
}
}
i'm new to the library, so even though it works, it's possible (read probable) there are issues with the code; comments are welcome! also, i've only implemented category tooltips, since that's all i've needed, but the same pattern should be applicable to the other types. if there's interrest, i'd be happy to code the rest.
one final comment... a simple refactoring would allow ChartFactory to be used in conjunction with custom tooltip/url generators...
Code: Select all
public static JFreeChart createHorizontalBarChart(String title, String domainAxisLabel, String rangeAxisLabel,
CategoryDataset data, boolean legend, boolean tooltips, boolean urls)
{
CategoryToolTipGenerator tooltipGenerator = null;
if (tooltips) {
tooltipGenerator = new StandardCategoryToolTipGenerator();
}
CategoryURLGenerator urlGenerator = null;
if (urls) {
urlGenerator = new StandardCategoryURLGenerator();
}
return createHorizontalBarChart( title, domainAxisLabel, rangeAxisLabel, data, legend, tooltipGenerator, urlGenerator );
}
public static JFreeChart createHorizontalBarChart(String title, String domainAxisLabel, String rangeAxisLabel,
CategoryDataset data, boolean legend,
CategoryToolTipGenerator tooltips, CategoryURLGenerator urls)
{
CategoryAxis domainAxis = new VerticalCategoryAxis(domainAxisLabel);
ValueAxis rangeAxis = new HorizontalNumberAxis(rangeAxisLabel);
CategoryItemRenderer renderer = new HorizontalBarRenderer(tooltipGenerator,
urlGenerator);
Plot plot = new HorizontalCategoryPlot(data, domainAxis, rangeAxis, renderer);
JFreeChart chart = new JFreeChart(title, JFreeChart.DEFAULT_TITLE_FONT, plot, legend);
return chart;
}
Mike Lueders