thanks, comments

A discussion forum for JFreeChart (a 2D chart library for the Java platform).
Locked
lewders
Posts: 1
Joined: Wed Mar 26, 2003 8:49 pm

thanks, comments

Post by lewders » Wed Mar 26, 2003 10:38 pm

first off, i'd like to thank the dev team for a great library; it is very much appreciated.

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;
  }
}

note: the primary motivation for returning ToolTip from ToolTipGenerator rather than String is that it simplifies implementation of drag (e.g. drag&drop) support, a requirement of mine that may not be widely applicable.

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;
    }
hope this is useful,

Mike Lueders
Last edited by lewders on Thu Mar 27, 2003 12:22 am, edited 1 time in total.

Fran Tonello
Posts: 8
Joined: Thu Mar 20, 2003 7:45 pm

Post by Fran Tonello » Wed Mar 26, 2003 10:55 pm

Wow, that looks great!

In your travels, did you notice whether the pre-generated tooltips were cleaned-up properly before? -We're seeing an OutOfMemoryError on Windows when we plot around 150 data points on three line charts.

Fran

Locked