Redrawing chart after dataset changed. How ?

A discussion forum for JFreeChart (a 2D chart library for the Java platform).
Locked
flanajb
Posts: 10
Joined: Tue Jul 27, 2010 3:09 pm
antibot: No, of course not.

Redrawing chart after dataset changed. How ?

Post by flanajb » Thu Aug 05, 2010 11:51 pm

It has been a long day and if anyone can help I would be most grateful.

I have extended ChartPanel to enable me to implement KeyPressed events on the chart. I have got that working and I am incrementing / decrementing the value that was mouse clicked by using the up / down arrow. Having changed the dataset I was wondering how I can get the chart to redraw ?

Thanks

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

Re: Redrawing chart after dataset changed. How ?

Post by skunk » Fri Aug 06, 2010 1:04 am

org.jfree.chart.JFreeChart defines this method

Code: Select all

public void fireChartChanged()

flanajb
Posts: 10
Joined: Tue Jul 27, 2010 3:09 pm
antibot: No, of course not.

Re: Redrawing chart after dataset changed. How ?

Post by flanajb » Fri Aug 06, 2010 10:08 am

I don't quite get this.

This is the code where I create my chart

final XYSeries altitudeSeries = new XYSeries("Altitude");
final XYSeries slopeSeries = new XYSeries("Slope");
for(GraphableObject.GraphPoint point : graphObject.getGraphPoints()) {
altitudeSeries.add(point.getDistancePoint(), point.getAltitudePoint());
slopeSeries.add(point.getDistancePoint(), point.getSlopePoint());
}

the points highlighted in red are references to Double / Float objects that I am updating. Given they are object references I would expect the chart to update with the new value when I call the fireChartChanged() method on the associated JFreeChart instance.

I am missing something simple here ?

Thanks

matinh
Posts: 483
Joined: Fri Aug 11, 2006 10:08 am
Location: Austria

Re: Redrawing chart after dataset changed. How ?

Post by matinh » Fri Aug 06, 2010 1:30 pm

In your example you create new Datasets! Did you set those datasets to be used by the chart (plot.setDataset())?
A ChartChanged event should than IMHO occur automatically and redraw your chart.

- martin

flanajb
Posts: 10
Joined: Tue Jul 27, 2010 3:09 pm
antibot: No, of course not.

Re: Redrawing chart after dataset changed. How ?

Post by flanajb » Fri Aug 06, 2010 1:36 pm

Here is the full source code of the create combined chart

public static JFreeChart createCombinedChart(GraphableObject graphObject) {
final XYSeries altitudeSeries = new XYSeries("Altitude");
final XYSeries slopeSeries = new XYSeries("Slope");
for(GraphableObject.GraphPoint point : graphObject.getGraphPoints()) {
altitudeSeries.add(point.getDistancePoint(), point.getAltitudePoint());
slopeSeries.add(point.getDistancePoint(), point.getSlopePoint());
}
final XYDataset altitudeCollection = new XYSeriesCollection(altitudeSeries);
final XYDataset slopeCollection = new XYSeriesCollection(slopeSeries);

final XYLineAndShapeRenderer renderer1 = new XYLineAndShapeRenderer(true, true);
renderer1.setBaseToolTipGenerator(new StandardXYToolTipGenerator());
final NumberAxis rangeAxis1 = new NumberAxis("Altitude(M)");
final XYPlot subplot1 = new XYPlot(altitudeCollection, null, rangeAxis1, renderer1);
subplot1.setRangeAxisLocation(AxisLocation.BOTTOM_OR_LEFT);

final XYLineAndShapeRenderer renderer2 = new XYLineAndShapeRenderer(false, true);
renderer2.setBaseToolTipGenerator(new StandardXYToolTipGenerator());
final NumberAxis rangeAxis2 = new NumberAxis("Slope(%)");
rangeAxis2.setMinorTickMarksVisible(true);
rangeAxis2.setAutoRangeIncludesZero(true);
final XYPlot subplot2 = new XYPlot(slopeCollection, null, rangeAxis2, renderer2);
subplot1.setRangeAxisLocation(AxisLocation.TOP_OR_LEFT);

final CombinedDomainXYPlot plot = new CombinedDomainXYPlot(new NumberAxis("Distance(Metres)"));
plot.setGap(5.0);
plot.add(subplot1, 2);
plot.add(subplot2, 1);
plot.setOrientation(PlotOrientation.VERTICAL);
final JFreeChart chart = new JFreeChart("Combined Altitude/Slope", plot);

// result.getLegend().setAnchor(Legend.SOUTH);
return chart;
}

Edit - I only create the chart once, so no I am not creating new datasets each time.

pooo
Posts: 9
Joined: Tue Jul 13, 2010 2:04 pm
antibot: No, of course not.

Re: Redrawing chart after dataset changed. How ?

Post by pooo » Fri Aug 06, 2010 1:50 pm

When you add values to your dataset, ie :

Code: Select all

altitudeSeries.add(point.getDistancePoint(), point.getAltitudePoint());
slopeSeries.add(point.getDistancePoint(), point.getSlopePoint());
Are you sure there is object reference ?
Did you check if your dataset values change when you click on the arrows ?

matinh
Posts: 483
Joined: Fri Aug 11, 2006 10:08 am
Location: Austria

Re: Redrawing chart after dataset changed. How ?

Post by matinh » Fri Aug 06, 2010 1:56 pm

Sorry, I misunderstood your previous posting. Double/Float is immutable, so your references change, rather than the objects themselves. What you really want to do, is update your dataset. Either re-create the dataset and set it via plot.setDataset(), or get a reference to the used dataset and update the values in question.

hth,
- martin

flanajb
Posts: 10
Joined: Tue Jul 27, 2010 3:09 pm
antibot: No, of course not.

Re: Redrawing chart after dataset changed. How ?

Post by flanajb » Fri Aug 06, 2010 2:04 pm

matinh wrote:Sorry, I misunderstood your previous posting. Double/Float is immutable, so your references change, rather than the objects themselves. What you really want to do, is update your dataset. Either re-create the dataset and set it via plot.setDataset(), or get a reference to the used dataset and update the values in question.

hth,
- martin
The reference does change, but I would just have expected the reference to point to another Double on the heap

eg
public void add(Number x, Number y) {
// argument checking delegated...
add(x, y, true);
}

Does that make sense ?

matinh
Posts: 483
Joined: Fri Aug 11, 2006 10:08 am
Location: Austria

Re: Redrawing chart after dataset changed. How ?

Post by matinh » Fri Aug 06, 2010 2:15 pm

A simple example:

Code: Select all

Double d1 = Double.valueOf(1.0d);
Double d2 = Double.valueOf(2.0d);
Double d = d1;

d1 = d2;
What do you think is the value of d after the last statement? It's still 1.0, even though d1 (to which it was refering) changed.

hth,
- martin

flanajb
Posts: 10
Joined: Tue Jul 27, 2010 3:09 pm
antibot: No, of course not.

Re: Redrawing chart after dataset changed. How ?

Post by flanajb » Fri Aug 06, 2010 7:40 pm

Thanks for that. What is the easiest option for updating the dataset ?

Once I have created my chart I don't what the hassle of having to traverse the object of the chart to locate the point that I want to update?

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

Re: Redrawing chart after dataset changed. How ?

Post by skunk » Fri Aug 06, 2010 8:32 pm

flanajb wrote:Thanks for that. What is the easiest option for updating the dataset ?
XYSeries defines these methods

Code: Select all

public void update(java.lang.Number x, java.lang.Number y)
public void updateByIndex(int index, java.lang.Number y)

flanajb
Posts: 10
Joined: Tue Jul 27, 2010 3:09 pm
antibot: No, of course not.

Re: Redrawing chart after dataset changed. How ?

Post by flanajb » Fri Aug 06, 2010 9:19 pm

stupid question, but how do I get a handle on the underlying xyseries from the actual chart instance ?

I have been using eclipse, but there seems to be so many associations with the chart that I can't see the woods for the trees.

Thanks for your help, by the way.

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

Re: Redrawing chart after dataset changed. How ?

Post by skunk » Fri Aug 06, 2010 10:38 pm

Instead of using XYSeries and XYSeriesCollection, perhaps the easiest way to achieve what you are looking for is to define your own dataset. This way nothing needs to be copied and there is only one location in memory to update.

Assuming that graphObject.getGraphPoints() returns an instance of java.util.List<GraphableObject.GraphPoint>, you could try something like this

Code: Select all

class MyAltitudeDataset extends AbstractXYDataset {
    private GraphableObject graphObject;

    MyAltitudeDataset(GraphableObject go) {
        graphObject = go;
    }
    public int getSeriesCount() {
        return 1;
    }
    public Comparable getSeriesKey(int series) {
        return "Altitude";
    }
    public DomainOrder getDomainOrder() {
        return DomainOrder.NONE;      // change to ASCENDING if your data is sorted
    }
    public double getXValue(int series, int item) {
        return graphObject.getGraphPoints().get(item).getDistancePoint();
    }
    public double getYValue(int series, int item) {
        return graphObject.getGraphPoints().get(item).getAltitudePoint();
    }
}
Create another class exactly the same except it should be called MySlopeDataset and getSeriesKey/getYValue should read

Code: Select all

class MySlopeDataset extends AbstractXYDataset {
    .
    .
    .
    public Comparable getSeriesKey(int series) {
        return "Slope";
    }
    public double getYValue(int series, int item) {
        return graphObject.getGraphPoints().get(item).getSlopePoint();
    }
}
To use these classes change your creation code to this

Code: Select all

public static JFreeChart createCombinedChart(GraphableObject graphObject) {
    final XYLineAndShapeRenderer renderer1 = new XYLineAndShapeRenderer(true, true);
    renderer1.setBaseToolTipGenerator(new StandardXYToolTipGenerator());
    final NumberAxis rangeAxis1 = new NumberAxis("Altitude(M)");
    final XYPlot subplot1 = new XYPlot(new MyAltitudeDataset(graphObject), null, rangeAxis1, renderer1);
    subplot1.setRangeAxisLocation(AxisLocation.BOTTOM_OR_LEFT);

    final XYLineAndShapeRenderer renderer2 = new XYLineAndShapeRenderer(false, true);
    renderer2.setBaseToolTipGenerator(new StandardXYToolTipGenerator());
    final NumberAxis rangeAxis2 = new NumberAxis("Slope(%)");
    rangeAxis2.setMinorTickMarksVisible(true);
    rangeAxis2.setAutoRangeIncludesZero(true);
    final XYPlot subplot2 = new XYPlot(new MySlopeDataset(graphObject), null, rangeAxis2, renderer2);
    subplot2.setRangeAxisLocation(AxisLocation.TOP_OR_LEFT);

    final CombinedDomainXYPlot plot = new CombinedDomainXYPlot(new NumberAxis("Distance(Metres)"));
    plot.setGap(5.0);
    plot.add(subplot1, 2);
    plot.add(subplot2, 1);
    plot.setOrientation(PlotOrientation.VERTICAL);
    final JFreeChart chart = new JFreeChart("Combined Altitude/Slope", plot);

    // result.getLegend().setAnchor(Legend.SOUTH);
    return chart;
}
Whenever the source data changes, simply call

Code: Select all

CombinedDomainXYPlot cdplot = (CombinedDomainXYPlot)chart.getPlot();
XYPlot subplot1 = (XYPlot)cdplot.getSubplots().get(0);
((AbstractSeriesDataset)subplot1.getDataset(0)).seriesChanged(null);  // will cause plot 1 to redraw
XYPlot subplot2 = (XYPlot)plot.getSubplots().get(1);
((AbstractSeriesDataset)subplot2.getDataset(0)).seriesChanged(null);  // will cause plot 2 to redraw
and the charts will repaint with the new data values.

This is off the top of my head so it *might* not compile 8)

flanajb
Posts: 10
Joined: Tue Jul 27, 2010 3:09 pm
antibot: No, of course not.

Re: Redrawing chart after dataset changed. How ?

Post by flanajb » Fri Aug 06, 2010 11:08 pm

Thanks for that. I will have a good luck at it tomorrow

Locked