Slow redrawing of complicated charts, and popup menus

A discussion forum for JFreeChart (a 2D chart library for the Java platform).
Locked
dan1son
Posts: 12
Joined: Thu Jun 29, 2006 7:06 pm
Location: Austin, TX

Slow redrawing of complicated charts, and popup menus

Post by dan1son » Thu Jun 29, 2006 9:53 pm

There is one thing really keeping me from being able to use this library. I am charting multiple datasets with around one thousand data points each. If I have anymore than 2 of these on the screen at the same time redrawing the window, or even simply having a popup menu on top of the graphpanel takes a significant amount of time. The time required varies depending on the amount of data currently displayed, but it's noticable after about 3 timeseries datasets being graphed.

I'm completely OK with it taking some time to draw the graph the first time, but I feel as though something like a popup menu, or redraw of the already drawn chart should be instant.

I've been searching around the forums and haven't found much related to this, so I'm wondering if it's something I'm doing wrong, or if it's inherent in the way it draws.

Thank you.

david.gilbert
JFreeChart Project Leader
Posts: 11734
Joined: Fri Mar 14, 2003 10:29 am
antibot: No, of course not.
Contact:

Post by david.gilbert » Fri Jun 30, 2006 2:19 pm

The ChartPanel class draws the chart into a BufferedImage (unless you turn this facility off via the constructor) then draws that onto the panel...so that if the chart dimensions and content haven't changed, a redraw is simply a matter of repainting the image (which is much faster than redrawing a chart that has thousands of data points).

It sounds like this is not working for you. Can you supply any more information about how you set up your chart panel? This could be a bug, and if it is I'd like to track it down.
David Gilbert
JFreeChart Project Leader

:idea: Read my blog
:idea: Support JFree via the Github sponsorship program

dan1son
Posts: 12
Joined: Thu Jun 29, 2006 7:06 pm
Location: Austin, TX

Post by dan1son » Fri Jun 30, 2006 3:28 pm

Sure.

Basically the chartpanel is being created when a plugin is openned. After that it is placed inside of another jpanel using borderlayout so I can have some extra information at the top. I keep this set to invisible since I seem to get problems with it attempting to draw a blank plot.

I have just added the forcing of the usebuffer (same thing happens). I also have tried it with and without using a ChartChangeEvent (doesn't seem to matter either). I have also attempted to remove notification of changes (I wasn't sure if it was looking for changes).

Any help would be greatly appreciated.

I have noticed that right after the chart is drawn (maybe 1 second) the popup menus will display instantaneously. After that it seems to be slow again.

Some of the code

Code: Select all

        // Creation of the chartpanel for the first time
        _chartPanel = new ChartPanel(new JFreeChart(new XYPlot()), true);
        _chartPanel.setVisible(false);
        upperPane.setVisible(false);
        upperPane.add(_chartPanel, BorderLayout.CENTER);

 public void clear() {
        _chartPanel.setChart(new JFreeChart(new XYPlot()));
        _dataSetCount = 0;
        _chartPanel.setVisible(false);
        _chartPanel.getParent().setVisible(false);
}

public void addTimeSeries(TimeSeries series, Sensor sensor) {
        System.out.println("Size of series: " + series.getItemCount());
        boolean added = false;
//        _timeSeriesCollection.addSeries(series);
        for (int i=0; i<_chartPanel.getChart().getXYPlot().getDatasetCount(); i++) {
            if (_dataSetCount != 0 && ((TimeSeriesCollection)_chartPanel.getChart().getXYPlot().getDataset(i)).getSeries(0).getRangeDescription().equals(sensor.getUnitsShortLabel(UnitsCache.getInstance()))) {
                ((TimeSeriesCollection)_chartPanel.getChart().getXYPlot().getDataset(i)).addSeries(series);
                added = true;
                break;
            }
        }
        if (!added) {
            if (sensor.isNoSmooth() || sensor instanceof StateSensor) {
                JFreeChart newChart = ChartFactory.createXYStepChart(sensor.getUnitsShortLabel(UnitsCache.getInstance()), "", sensor.getUnitsShortLabel(UnitsCache.getInstance()), new TimeSeriesCollection(series), PlotOrientation.VERTICAL, true, true, false);
                _chartPanel.getChart().getXYPlot().setDataset(_dataSetCount, newChart.getXYPlot().getDataset());
                _chartPanel.getChart().getXYPlot().setRenderer(_dataSetCount, newChart.getXYPlot().getRenderer());
                _chartPanel.getChart().getXYPlot().setRangeAxis(_dataSetCount, newChart.getXYPlot().getRangeAxis());
                _chartPanel.getChart().getXYPlot().mapDatasetToRangeAxis(_dataSetCount, _dataSetCount);
                _chartPanel.getChart().getXYPlot().setDomainAxis(newChart.getXYPlot().getDomainAxis());
                _chartPanel.getChart().getXYPlot().mapDatasetToDomainAxis(_dataSetCount, 0);
                _dataSetCount++;
                }
            else {
                JFreeChart newChart = ChartFactory.createTimeSeriesChart(sensor.getUnitsShortLabel(UnitsCache.getInstance()), "", sensor.getUnitsShortLabel(UnitsCache.getInstance()), new TimeSeriesCollection(series), true, true, false);
                _chartPanel.getChart().getXYPlot().setDataset(_dataSetCount, newChart.getXYPlot().getDataset());
                _chartPanel.getChart().getXYPlot().setRenderer(_dataSetCount, newChart.getXYPlot().getRenderer());
                _chartPanel.getChart().getXYPlot().setRangeAxis(_dataSetCount, newChart.getXYPlot().getRangeAxis());
                _chartPanel.getChart().getXYPlot().mapDatasetToRangeAxis(_dataSetCount,_dataSetCount);
                _chartPanel.getChart().getXYPlot().setDomainAxis(newChart.getXYPlot().getDomainAxis());
                _chartPanel.getChart().getXYPlot().mapDatasetToDomainAxis(_dataSetCount, 0);
                _dataSetCount++;
            }
        }
        _chartPanel.chartChanged(new ChartChangeEvent(ChartChangeEventType.NEW_DATASET));
        if (!_chartPanel.getParent().isVisible()) {
            _chartPanel.setVisible(true);
            _chartPanel.getParent().setVisible(true);
        }
    }

caa
Posts: 21
Joined: Mon Feb 14, 2005 11:28 pm

Post by caa » Mon Jul 03, 2006 10:08 pm

If you have a lot of data, it can take JFreeChart some not insignificant amount of time to draw it.
That's why I wrote ThreadChart.
You can find more info about it in this this thread: http://www.jfree.org/phpBB2/viewtopic.php?t=17965
You need to be using java >= 1.4 for this.
But it does not block the EDT while the chart is being drawn.

I did notice when I was testing ThreadChart I was seeing redraw requests when I just clicked on the ChartPanel, even though nothing changed.
If ThreadChart does not get a ChartChanged event or the size to draw changes, it send back what it has buffered.
Charles Anderson

dan1son
Posts: 12
Joined: Thu Jun 29, 2006 7:06 pm
Location: Austin, TX

Post by dan1son » Wed Jul 05, 2006 10:05 pm

Based on what David said the chart shouldn't attempt to redraw if nothing changes. So that does sound like a bug, especially if you've noticed the same thing.

I am drawing many large plots on the same chart. Could be upwards of 50 - 1000 point plots of xyplot or stepxyplots. I am limiting the number of points to 1000 mainly because the screen can't display anymore than that, it would run out of memory if I threw a few hundred thousand points at it, and it is extremely slow.

I am trying to replace some old nasty custom graphing code with JFreeChart. I just can't handle the slowness when it's not needing to redraw. I don't mind it taking a while to initially draw the graphs or even zoom. Considering the amount of data I am trying to handle, multiple seconds to draw a big graph is acceptable.

dan1son
Posts: 12
Joined: Thu Jun 29, 2006 7:06 pm
Location: Austin, TX

Post by dan1son » Wed Jul 05, 2006 10:14 pm

After a little more messin around it looks like it's attempting to redraw if I click on the panel without dragging the mouse.

If I click on the panel and drag the mouse to the upper-left it will not spike my CPU unless it needs to zoom out, but if I click and don't drag my mouse I get a CPU spike, equal to that of when I unzoom back to the full chart.

So the popupmenu thing seems to be slow when I left click then right click before it's finished, what appears to be, redrawing.

I would imagine it shouldn't redraw if i just click on the chart with my left mouse button.

skunk
Posts: 1087
Joined: Thu Jun 02, 2005 10:14 pm
Location: Brisbane, Australia

Post by skunk » Wed Jul 05, 2006 10:58 pm

Dont know if its a bug or not, but if you read the source for ChartPanel, it certainly looks like it was coded to behave this way...

Code: Select all

1634        public void mouseClicked(MouseEvent event) {
.
.
.
1641            this.chart.setNotify(true);  // force a redraw 

caa
Posts: 21
Joined: Mon Feb 14, 2005 11:28 pm

Post by caa » Thu Jul 06, 2006 2:23 am

Yes, it does look like it was coded it that way. I'd like to hear the rational behind this decision. I've got some charts with close to 500,000 data points, and it can take some time to draw. Redrawing just because you click on the chart without changing anything is not good for me.
Charles Anderson

david.gilbert
JFreeChart Project Leader
Posts: 11734
Joined: Fri Mar 14, 2003 10:29 am
antibot: No, of course not.
Contact:

Post by david.gilbert » Mon Jul 10, 2006 3:40 pm

If my memory is correct, the redraw is forced because the anchor point has changed, and so the cross-hair location has potentially changed...the only way to know, is to redraw the chart (because the crosshair location is determined during the drawing process).

There are probably lots of cases where the redraw is redundant, so of course it would be good to find a way to avoid it whenever possible...
David Gilbert
JFreeChart Project Leader

:idea: Read my blog
:idea: Support JFree via the Github sponsorship program

vsharma
Posts: 1
Joined: Mon Jul 10, 2006 6:52 pm

Post by vsharma » Mon Jul 10, 2006 7:14 pm

Would it be possible to use a separate buffer to draw the data items and other time consuming entities? This buffer is refreshed only when there is a size and/or content change and is zapped on to the final image.

I had to use this approach to prevent redrawing of all my data points on every mouse click. However, I added this additional buffer in my custom XYPlot and not in the ChartPanel. I am sure there are easier or better approaches but this one worked for me.

dan1son
Posts: 12
Joined: Thu Jun 29, 2006 7:06 pm
Location: Austin, TX

Post by dan1son » Tue Jul 18, 2006 4:38 pm

So what is the recommended method to remedy this. I am not using crosshairs so I have no interest in a redraw when the chart is simply clicked on. I don't really think running it in a thread will help much either since I really don't want it even attempting to redraw unless the size changes or a zoom in-out happens.

Sorry it took a bit to get back to ya, I've been out of town.

I appreciate the help,
Dan

dan1son
Posts: 12
Joined: Thu Jun 29, 2006 7:06 pm
Location: Austin, TX

Post by dan1son » Tue Jul 25, 2006 3:39 pm

Just bouncing this to see if there has been any progress. Still looking for a usable solution to fix the issue where it redraws on a click.

Thanks again,
Dan

kelly
Posts: 7
Joined: Tue Aug 01, 2006 4:58 pm

Post by kelly » Tue Aug 01, 2006 5:00 pm

I'm having the same problem. Has anyone found a simple way to fix it?

Rachel M
Posts: 6
Joined: Mon Sep 26, 2005 8:06 pm

Post by Rachel M » Thu Aug 31, 2006 8:43 pm

Hey,
Any luck anyone?

I just changed 2 things:
1) Make sure to use the ChartPanel constructor with the useBuffer parameter (set to True). It defaults to false, i was surprised to see.

2) Adding mouseClicked to my ChartPanel subclass and basically just taking out that call to setNotify.

It seems a little better. Hopefully it wont break anything...

Did you try that, dan1son or kelly?
-Rachel

Locked