To Those Who Use JFreeChart for Dynamic Plotting/Large Sets

A free public discussion forum for the JFreeChart class library.

Postby parsec » Wed Nov 21, 2007 2:49 pm

david.gilbert wrote:... For tooltips, it might be possible to do a more dynamic tooltip construction that doesn't require the use of the entity collection...it is possible to convert the mouse location to data coordinates, so I'm thinking it might be possible to define an alternative tooltip mechanism that creates tooltips on-the-fly after being supplied just the data coordinates. I'll have to experiment with that.


Did any progress ever get made on this front? Our datasets aren't all that big (200-400 items), and they're static, but we're displaying several ChartPanels within a JScrollPane. Scrolling is quite sluggish, and the profiler indicates around 20% of the time being spent in the generateToolTip method (we have relatively extensive tooltips, and make calls to NumberFormatter.format etc). Simply returning an empty string from generateToolTip does make things feel snappier, so more dynamic creation would be a definite win for us.

Come to think of it, all that really needs to be done is give the XYItemEntity (or ChartEntity in general I suppose) a reference to the tooltip generator and have it generate the tooltip within its getToolTipText method on the fly. It already has the dataset, item and series it needs to call generateToolTip. This way the whole kit and kaboodle won't have to be done on every call to repaint.

*wanders of to try it out*
parsec
 
Posts: 7
Joined: Wed Jan 24, 2007 7:30 am

Postby david.gilbert » Wed Nov 21, 2007 2:54 pm

parsec wrote:Come to think of it, all that really needs to be done is give the XYItemEntity (or ChartEntity in general I suppose) a reference to the tooltip generator and have it generate the tooltip within its getToolTipText method on the fly. It already has the dataset, item and series it needs to call generateToolTip. This way the whole kit and kaboodle won't have to be done on every call to repaint.

*wanders of to try it out*


Yeah, sounds like that would be worth a try.
David Gilbert
JFreeChart Project Leader

:idea: Read my blog
:idea: Ask your company to buy the JFreeChart Developer Guide
:idea: Check out other products sold by my company Object Refinery Limited
david.gilbert
JFreeChart Project Leader
 
Posts: 11341
Joined: Fri Mar 14, 2003 10:29 am

Postby parsec » Wed Nov 21, 2007 3:33 pm

david.gilbert wrote:Yeah, sounds like that would be worth a try.


And so it was.

For posterity, all I had to do was:

Code: Select all
XYLineAndShapeRenderer renderer = new XYLineAndShapeRenderer(true, false) {
         // copied from AbstractXYItemRenderer.java except we generate a
         // custom XYItemEntity for more dynamic tooltip creation and omit
         // the url generator stuff
         @Override         
         protected void addEntity(EntityCollection entities, Shape area,
                                    final XYDataset dataset, final int series, final int item, double entityX, double entityY) {
            if (!getItemCreateEntity(series, item))
               return;
            
            if (area == null)
               area = new Ellipse2D.Double(entityX - getDefaultEntityRadius(),
                     entityY - getDefaultEntityRadius(),
                     getDefaultEntityRadius() * 2, getDefaultEntityRadius() * 2);
            
            final XYToolTipGenerator generator = getToolTipGenerator(series, item);
            XYItemEntity entity = new XYItemEntity(area, dataset, series, item, null, null) {
            public @Override String getToolTipText() {
               return generator.generateToolTip(dataset, series, item);
            }};
            entities.add(entity);
         }
      };



definitely feels quicker (but there's still profiling to do, scrolling is still pretty brutal).

-p
parsec
 
Posts: 7
Joined: Wed Jan 24, 2007 7:30 am

Postby david.gilbert » Wed Nov 21, 2007 3:59 pm

For the scrolling, did you try creating the ChartPanel instances with the 'useBuffer' flag set to true?
David Gilbert
JFreeChart Project Leader

:idea: Read my blog
:idea: Ask your company to buy the JFreeChart Developer Guide
:idea: Check out other products sold by my company Object Refinery Limited
david.gilbert
JFreeChart Project Leader
 
Posts: 11341
Joined: Fri Mar 14, 2003 10:29 am

Postby parsec » Wed Nov 21, 2007 4:20 pm

david.gilbert wrote:For the scrolling, did you try creating the ChartPanel instances with the 'useBuffer' flag set to true?


Just tried that now. Ridiculously faster, but getting some serious painting issues. I'm not sure if we're just doing things wrong (I didn't write that code and I'm not all that familiar with it at this point).

Turning on buffering caused the charts to lose their background colour (now they're just grey). By background I mean the area outside the actual data area - where the axes and labels are. And when I drag-zoom the labels and axes redraw but the previous stuff that was there doesn't clear, resulting in a jumbled mess accumulating. I'll take a look at what we might be doing that's causing that, I wouldn't think that flipping the buffer flag should cause such drastic changes. Or maybe it makes complete sense, I dunno.


And as a follow-up to the code I just posted, I made it slightly more efficient by calling getToolTipGenerator() instead of passing in the item and series, since we don't use per-series or per-item generators at all.
parsec
 
Posts: 7
Joined: Wed Jan 24, 2007 7:30 am

Postby david.gilbert » Wed Nov 21, 2007 4:26 pm

Are you using 1.0.7, or an earlier version?
David Gilbert
JFreeChart Project Leader

:idea: Read my blog
:idea: Ask your company to buy the JFreeChart Developer Guide
:idea: Check out other products sold by my company Object Refinery Limited
david.gilbert
JFreeChart Project Leader
 
Posts: 11341
Joined: Fri Mar 14, 2003 10:29 am

Postby parsec » Wed Nov 21, 2007 4:29 pm

david.gilbert wrote:Are you using 1.0.7, or an earlier version?


1.0.5. There was a regression we found in 1.0.6 so we couldn't use it (I've been meaning to report it but haven't had time to develop a test case) and I didn't notice 1.0.7 was available.
parsec
 
Posts: 7
Joined: Wed Jan 24, 2007 7:30 am

Postby parsec » Wed Nov 21, 2007 7:19 pm

ok, nevermind. the issue was with setting the background colour of the chart to null. something related to the resulting transparency perhaps. setting the colour seems to fix things, and with the buffering on the performance is way better. i knew there had to be something that wasn't right.
parsec
 
Posts: 7
Joined: Wed Jan 24, 2007 7:30 am

FastXYPlot not working with multiple series

Postby kafr » Thu Nov 29, 2007 10:28 pm

arwelbath wrote:Fat XYPlot not working with multiple series. Bug???

The snippet below plots two XYSeriesCollection on the same graph, and with a different renderer for each. But, if I replace the XYPlot with FastXYPlot, then it will only display both collections if they are plotted with the same renderer. If I try to use two different renderers, only one is drawn. Is this a bug or have I misunderstood fastXPPlot??



Hi there,

actually I did not look at your problem example (coming to think of it), so what's following may or may not apply to your problem.

But I do have a similar problem, when displaying data with 2 different renderers. One part (5 graphs) with a regular XY-LineRenderer and the other with the DeviationRenderer (exact class names I would have to look up). The regular graphs appeared in the chart (using the FastXYPlot), the Deviation part not.

The problem is that the DeviationRenderer is actually doing a very simple job on rendering the data. getVisibleItems() returns the full series data. So if your data has 1 million points it will always render the 1 million points. And the painting of the deviation area (filled with a color) is triggered only, when the last item of the series has been rendered. That's why the deviation part wasn't plotted for me. The optimization part suppressing rendering of a good part of the points, suppressed rendering the last point, and therefor the area wasn't plotted.

So, as a guess, the renderer you are using (which is not plotted), probably does the rendering in a similar simple way as the DeviationRenderer, waiting for the last item in the series.

Hope this helps!
kafr
 
Posts: 1
Joined: Thu Nov 29, 2007 9:41 pm

disable series changed event when adding series

Postby AndrewJ » Tue Feb 12, 2008 12:53 pm

Hi there, first post in this forum so apologies if the thread is not quite accurate.

I have been using some of the techniques described in this thread to speed plotting of large data sets but recently ran into a new but related problem.

If you add multiple series to an XYSeriesCollection then it will fire a change event everytime a new series is added. Is there any way to disable that event (as you can do when adding data points to the underlying series) until all the new or changed series have been added to the collection? I don't want the plot to redraw until all data are added.

One way would be to override the add method in the XYSeriesCollection but is there a better way?

thanks for any thoughts. (and thanks to David Gilbert for a great library)
Andrew
AndrewJ
 
Posts: 5
Joined: Tue Feb 12, 2008 12:36 pm
Location: Australia

Postby CameO73 » Thu Feb 28, 2008 11:13 pm

Noticed a couple of bugs in FastXYPlot (which is doing wonders for my rendering speed ... thanks nonlinear5!). The two things I noticed were that XYSplineRenderer doesn't work correctly with this method, and shapes are never drawn. Below the fixed code with two changes: the renderedPixels are cleared for every pass (fixes shape rendering) and a check for DrawSeriesLineAsPath which disables the optimization FastXYPlot uses. Enjoy!
Code: Select all
import java.awt.Graphics2D;
import java.awt.geom.Rectangle2D;
import java.util.HashSet;

import org.jfree.chart.axis.ValueAxis;
import org.jfree.chart.plot.CrosshairState;
import org.jfree.chart.plot.PlotRenderingInfo;
import org.jfree.chart.plot.SeriesRenderingOrder;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.RendererUtilities;
import org.jfree.chart.renderer.xy.XYItemRenderer;
import org.jfree.chart.renderer.xy.XYItemRendererState;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.data.general.DatasetUtilities;
import org.jfree.data.xy.XYDataset;
import org.jfree.ui.RectangleEdge;


/**
* Performs fast rendering of large datasets in nearly constant time.
*/
class FastXYPlot extends XYPlot {

   private final HashSet<Integer> renderedPixels = new HashSet<Integer>();

   public FastXYPlot(XYDataset dataset, ValueAxis domainAxis, ValueAxis rangeAxis, XYItemRenderer renderer) {
      super(dataset, domainAxis, rangeAxis, renderer);
   }

   /**
    * Determines if the item is to be rendered in the area of the plot where one of the previous
    * items has already been rendered.
    */
   private boolean hasRendered(XYDataset dataset, ValueAxis xAxis, ValueAxis yAxis, RectangleEdge domainEdge,
                        RectangleEdge rangeEdge, Rectangle2D dataArea, int series, int item) {
      boolean hasRendered = true;

      int width = (int) dataArea.getWidth();

      double xValue = dataset.getXValue(series, item);
      double yValue = dataset.getYValue(series, item);
      int x = (int) xAxis.valueToJava2D(xValue, dataArea, domainEdge);
      int y = (int) yAxis.valueToJava2D(yValue, dataArea, rangeEdge);

      int itemKey = x + width * y;
      if (!renderedPixels.contains(itemKey)) {
         renderedPixels.add(itemKey);
         hasRendered = false;
      }

      return hasRendered;
   }


   @Override
   public boolean render(Graphics2D g2,
                    Rectangle2D dataArea,
                    int index,
                    PlotRenderingInfo info,
                    CrosshairState crosshairState) {


      boolean foundData = false;
      boolean   disableOptimization = false;

      XYDataset dataset = getDataset(index);

      if (!DatasetUtilities.isEmptyOrNull(dataset)) {
         foundData = true;
         ValueAxis xAxis = getDomainAxisForDataset(index);
         ValueAxis yAxis = getRangeAxisForDataset(index);
         XYItemRenderer renderer = getRenderer(index);
         if (renderer == null) {
            renderer = getRenderer();
            if (renderer == null) { // no default renderer available
               return foundData;
            }
         }

         XYItemRendererState state = renderer.initialise(g2, dataArea, this,
               dataset, info);
         int passCount = renderer.getPassCount();

         if (renderer instanceof XYLineAndShapeRenderer) {
            disableOptimization = ((XYLineAndShapeRenderer) renderer).getDrawSeriesLineAsPath();   // in case of e.g. splines
         }

         RectangleEdge domainEdge = getDomainAxisEdge();
         RectangleEdge rangeEdge = getDomainAxisEdge();

         SeriesRenderingOrder seriesOrder = getSeriesRenderingOrder();
         if (seriesOrder == SeriesRenderingOrder.REVERSE) {
            //render series in reverse order
            for (int pass = 0; pass < passCount; pass++) {
               renderedPixels.clear();                     // need to clear every pass or else shapes won't be drawn correctly
               int seriesCount = dataset.getSeriesCount();
               for (int series = seriesCount - 1; series >= 0; series--) {
                  int firstItem = 0;
                  int lastItem = dataset.getItemCount(series) - 1;
                  if (lastItem == -1) {
                     continue;
                  }
                  if (state.getProcessVisibleItemsOnly()) {
                     int[] itemBounds = RendererUtilities.findLiveItems(
                           dataset, series, xAxis.getLowerBound(),
                           xAxis.getUpperBound());
                     firstItem = itemBounds[0];
                     lastItem = itemBounds[1];
                  }
                  for (int item = firstItem; item <= lastItem; item++) {
                     if (disableOptimization || !hasRendered(dataset, xAxis, yAxis, domainEdge, rangeEdge, dataArea, series, item)) {
                        renderer.drawItem(g2, state, dataArea, info,
                              this, xAxis, yAxis, dataset, series, item,
                              crosshairState, pass);
                     }
                  }
               }
            }
         } else {
            //render series in forward order
            for (int pass = 0; pass < passCount; pass++) {
               renderedPixels.clear();                     // need to clear every pass or else shapes won't be drawn correctly
               int seriesCount = dataset.getSeriesCount();
               for (int series = 0; series < seriesCount; series++) {
                  int firstItem = 0;
                  int lastItem = dataset.getItemCount(series) - 1;
                  if (state.getProcessVisibleItemsOnly()) {
                     int[] itemBounds = RendererUtilities.findLiveItems(
                           dataset, series, xAxis.getLowerBound(),
                           xAxis.getUpperBound());
                     firstItem = itemBounds[0];
                     lastItem = itemBounds[1];
                  }
                  for (int item = firstItem; item <= lastItem; item++) {
                     if (disableOptimization || !hasRendered(dataset, xAxis, yAxis, domainEdge, rangeEdge, dataArea, series, item)) {
                        renderer.drawItem(g2, state, dataArea, info,
                              this, xAxis, yAxis, dataset, series, item,
                              crosshairState, pass);
                     }
                  }
               }
            }
         }
      }
      return foundData;
   }

}
CameO73
 
Posts: 2
Joined: Fri Sep 07, 2007 8:36 am

Postby SamiLakka » Thu Apr 03, 2008 10:11 am

Are these fast classes going to be added to JFreeChart?
SamiLakka
 
Posts: 8
Joined: Tue May 02, 2006 2:22 pm

Postby kartben » Tue Apr 22, 2008 5:38 pm

SamiLakka wrote:Are these fast classes going to be added to JFreeChart?


No idea ...? :?
kartben
 
Posts: 1
Joined: Tue Sep 04, 2007 4:08 pm
Location: TOULOUSE

Postby RoyW » Sat May 10, 2008 7:25 am

david.gilbert wrote:The entity collection is very resource intensive, so don't use it for large datasets. For tooltips, it might be possible to do a more dynamic tooltip construction that doesn't require the use of the entity collection...it is possible to convert the mouse location to data coordinates, so I'm thinking it might be possible to define an alternative tooltip mechanism that creates tooltips on-the-fly after being supplied just the data coordinates. I'll have to expement with that.

As you surmised, if you can convert a data coordinate point to a screen point, you can convert a screen point to a data coordinate point. After that you need to look at all the datasets and see if the data point is in the dataset. Of course you need to define a "hotspot" around the cursor, test for, horizontal charts, test for multiple plots...

I have been using JFreeChart 1.0.9 for 5 weeks now and have delved deep in to its depths (thanks for making it open source, I learned a lot :) ).

Here is a solution for tooltips in "XY" Charts that do not require entities at all... (tested for several charts, may need more testing).
While testing this I found a couple of bugs in CandlestickRenderer. Will post seperate thread.
I will post a couple of demos, seperatly.

This solution extends ChartPanel and overrides getTooltipText(). It may be possible to include this in ChartPanel with a flag (generateDynamicToolTips) or simply leave it as an extended class.
(At the moment I am working on extending ChartPanel to provide more interaction; animated zooming, drag-pan, mousewheel zoom, click-dataPopup, drag-datapoint... It is from the click and drag that I needed to get the datapoint and realized the same code could be used for tooltips.

Anyway, here is my "solution". It looks unwieldy but it works and seems to be fairly efficient.
Code: Select all
package org.jfree.chart;

import org.jfree.chart.axis.ValueAxis;
import org.jfree.chart.labels.HighLowItemLabelGenerator;
import org.jfree.chart.labels.StandardXYToolTipGenerator;
import org.jfree.chart.plot.*;
import org.jfree.data.xy.OHLCDataset;
import org.jfree.data.xy.XYDataset;
import org.jfree.ui.RectangleEdge;

import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;

public class TooltipChartPanel extends ChartPanel {

    public TooltipChartPanel(JFreeChart chart) {
        this(
                chart,
                DEFAULT_WIDTH,
                DEFAULT_HEIGHT,
                DEFAULT_MINIMUM_DRAW_WIDTH,
                DEFAULT_MINIMUM_DRAW_HEIGHT,
                DEFAULT_MAXIMUM_DRAW_WIDTH,
                DEFAULT_MAXIMUM_DRAW_HEIGHT,
                DEFAULT_BUFFER_USED,
                true,  // properties
                true,  // save
                true,  // print
                true,  // zoom
                true   // tooltips
        );
    }

    public TooltipChartPanel(JFreeChart chart, boolean useBuffer) {
        this(chart,
                DEFAULT_WIDTH,
                DEFAULT_HEIGHT,
                DEFAULT_MINIMUM_DRAW_WIDTH,
                DEFAULT_MINIMUM_DRAW_HEIGHT,
                DEFAULT_MAXIMUM_DRAW_WIDTH,
                DEFAULT_MAXIMUM_DRAW_HEIGHT,
                useBuffer,
                true,  // properties
                true,  // save
                true,  // print
                true,  // zoom
                true   // tooltips
        );
    }

    public TooltipChartPanel(JFreeChart chart, boolean properties, boolean save, boolean print, boolean zoom, boolean tooltips) {
        this(chart,
                DEFAULT_WIDTH,
                DEFAULT_HEIGHT,
                DEFAULT_MINIMUM_DRAW_WIDTH,
                DEFAULT_MINIMUM_DRAW_HEIGHT,
                DEFAULT_MAXIMUM_DRAW_WIDTH,
                DEFAULT_MAXIMUM_DRAW_HEIGHT,
                DEFAULT_BUFFER_USED,
                properties,
                save,
                print,
                zoom,
                tooltips
        );
    }

    public TooltipChartPanel(JFreeChart chart, int width, int height, int minimumDrawWidth, int minimumDrawHeight, int maximumDrawWidth, int maximumDrawHeight, boolean useBuffer, boolean properties, boolean save, boolean print, boolean zoom, boolean tooltips) {
        super(chart, width, height, minimumDrawWidth, minimumDrawHeight, maximumDrawWidth, maximumDrawHeight, useBuffer, properties, save, print, zoom, tooltips);
    }

    /**
     * Returns a string for the tooltip.
     *
     * @param e  the mouse event.
     *
     * @return A tool tip or <code>null</code> if no tooltip is available.
     */
    public String getToolTipText(MouseEvent e) {
        String result = getTooltipAtPoint(e.getPoint());
        if( result != null)
            return result;
        else
            return super.getToolTipText(e);
    }

    private static int HOTSPOT_SIZE = 5;
    //TODO Add code to set and get these values
    private HighLowItemLabelGenerator hiLoTips = new HighLowItemLabelGenerator();
    private StandardXYToolTipGenerator xyTips = new StandardXYToolTipGenerator();

    /**
     * This method attempts to get a tooltip by converting the screen X,Y into Chart Area X,Y
     * and then looking for a data point in a data set that lies inside a hotspot around that value.
     * @param point The Java 2D point
     * @return A string for the data at the point or null if no data is found.
     */
    protected String getTooltipAtPoint(Point point) {
        String result = null;

        Point2D translatedPoint = this.translateScreenToJava2D(point);
        Plot plot = this.getChart().getPlot();
        PlotRenderingInfo info = this.getChartRenderingInfo().getPlotInfo();
        if (plot instanceof CombinedDomainXYPlot) {
            int index = info.getSubplotIndex(translatedPoint);
            if (index < 0)
                index = 0;
            plot = (Plot) ((CombinedDomainXYPlot) plot).getSubplots().get(index);
            info = this.getChartRenderingInfo().getPlotInfo().getSubplotInfo(index);
        }
        if (plot != null && plot instanceof XYPlot) {
            XYPlot xyPlot = (XYPlot) plot;
            ValueAxis domainAxis = xyPlot.getDomainAxis();
            ValueAxis rangeAxis  = xyPlot.getRangeAxis();
            Rectangle2D screenArea = this.scale(info.getDataArea());

            double hotspotSizeX = HOTSPOT_SIZE * this.getScaleX();
            double hotspotSizeY = HOTSPOT_SIZE * this.getScaleY();
            double x0 = point.getX();
            double y0 = point.getY();
            double x1 = x0 - hotspotSizeX;
            double y1 = y0 + hotspotSizeY;
            double x2 = x0 + hotspotSizeX;
            double y2 = y0 - hotspotSizeY;
            RectangleEdge xEdge = RectangleEdge.BOTTOM;
            RectangleEdge yEdge = RectangleEdge.LEFT;
            //Switch everything for horizontal charts
            if (xyPlot.getOrientation() == PlotOrientation.HORIZONTAL) {
                hotspotSizeX = HOTSPOT_SIZE * this.getScaleY();
                hotspotSizeY = HOTSPOT_SIZE * this.getScaleX();
                x0 = point.getY();
                y0 = point.getX();
                x1 = x0 + hotspotSizeX;
                y1 = y0 - hotspotSizeY;
                x2 = x0 - hotspotSizeX;
                y2 = y0 + hotspotSizeY;
                xEdge = RectangleEdge.LEFT;
                yEdge = RectangleEdge.BOTTOM;
            }

            double tx0 = domainAxis.java2DToValue(x0, screenArea, xEdge);
            double ty0 = rangeAxis.java2DToValue(y0, screenArea, yEdge);
            double tx1 = domainAxis.java2DToValue(x1, screenArea, xEdge);
            double ty1 = rangeAxis.java2DToValue(y1, screenArea, yEdge);
            double tx2 = domainAxis.java2DToValue(x2, screenArea, xEdge);
            double ty2 = rangeAxis.java2DToValue(y2, screenArea, yEdge);

            int datasetCount= xyPlot.getDatasetCount();
            for(int datasetIndex=0 ; datasetIndex<datasetCount ; datasetIndex++){
                XYDataset dataset = xyPlot.getDataset(datasetIndex);
                int seriesCount = dataset.getSeriesCount();
                for(int series=0 ; series<seriesCount ; series++){
                    int itemCount = dataset.getItemCount(series);
                    if(dataset instanceof OHLCDataset) {
                        //This could be optimized to use a binary search for x first
                        for(int item = 0 ; item < itemCount ; item++){
                            double xValue = dataset.getXValue(series, item);
                            double yValueHi = ((OHLCDataset)dataset).getHighValue(series, item);
                            double yValueLo = ((OHLCDataset)dataset).getLowValue(series, item);
                            //Check hi lo and swap if needed
                            if(yValueHi < yValueLo)
                            {
                                double temp = yValueHi;
                                yValueHi = yValueLo;
                                yValueLo = temp;
                            }
                            //Check if the dataset 'X' value lies between the hotspot (tx1 < xValue < tx2)
                            if(tx1 < xValue && xValue < tx2)
                                //Check if the cursor 'y' value lies between the high and low (low < ty0 < high)
                                if(yValueLo<ty0 && ty0 < yValueHi){
                                    return hiLoTips.generateToolTip(dataset, series, item);
                                }
                        }
                    } else {
                        //This could be optimized to use a binary search for x first
                        for(int item = 0 ; item < itemCount ; item++){
                            double xValue = dataset.getXValue(series, item);
                            double yValue = dataset.getYValue(series, item);
                            //Check if the dataset 'X' value lies between the hotspot (tx1< xValue < tx2)
                            if(tx1 < xValue && xValue < tx2)
                                //Check if the dataset 'Y' value lies between the hotspot (ty1 < yValue < ty2)
                                if(ty1 <yValue && yValue <ty2){
                                    return xyTips.generateToolTip(dataset, series, item);
                                }
                        }
                    }
                }
            }
        }

        return result;
    }
}

RoyW
 
Posts: 93
Joined: Wed Apr 23, 2008 7:42 pm

Postby RoyW » Sat May 10, 2008 7:38 am

Here is a demo using 40,000 points.
Number of data series = 4
Number of data points per series = 10,000

Code: Select all
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.TooltipChartPanel;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.data.xy.AbstractXYDataset;
import org.jfree.data.xy.XYDataset;

import javax.swing.*;
import java.awt.*;

public class TooltipChartDemoXY extends JFrame {
    //  ***** WARNING!! set these to an amount that your processor can handle
    public static final int NUM_DATASERIES = 4;
    public static final int NUM_DATAPOINTS = 10000;
   
    public TooltipChartDemoXY() {
        super("TooltipChartDemoXY");
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        NumberAxis domainAxis = new NumberAxis("X Values");
        NumberAxis rangeAxis = new NumberAxis("Y Values");

        XYLineAndShapeRenderer renderer = new XYLineAndShapeRenderer();
        XYDataset dataset = getDataSet(NUM_DATASERIES);

        XYPlot mainPlot = new XYPlot(dataset, domainAxis, rangeAxis, renderer);
        //Uncomment this line to test horizontal charts
//       mainPlot.setOrientation(PlotOrientation.HORIZONTAL);

        renderer.setBaseCreateEntities(false);

        JFreeChart chart = new JFreeChart(null, null, mainPlot, false);
        ChartPanel chartPanel = new TooltipChartPanel(chart);
        chartPanel.setPreferredSize(new Dimension(600, 600));

        this.add(chartPanel);
        this.pack();
    }

    protected XYDataset getDataSet(final int power) {
        return new AbstractXYDataset() {
            public int getSeriesCount() { return power; }
            public Comparable getSeriesKey(int series) { return "Series:" + series; }
            public int getItemCount(int series) { return NUM_DATAPOINTS; }
            public Number getX(int series, int item) { return new Double(item); }
            public Number getY(int series, int item) { return new Double(2 * item + series); }
        };
    }

    public static void main(String[] args) {
        new TooltipChartDemoXY().setVisible(true);
    }
}
RoyW
 
Posts: 93
Joined: Wed Apr 23, 2008 7:42 pm

PreviousNext

Return to JFreeChart - General

Who is online

Users browsing this forum: No registered users and 15 guests