Using JFreeChart to display recent changes in a time series
Asked Answered
V

3

37

How can I use JFreeChart to display just the most recent data in a continually updated time series?

Addenda: A complete, working example that incorporates the accepted answer is shown here. See also this variation having two series. See also this Q&A regarding setTimeBase().

Vermis answered 19/2, 2011 at 4:31 Comment(0)
B
16

You can also eliminate the zero by first advanceTime(), then appendData. (swap the way they are doing it in the example).

Berfield answered 4/5, 2011 at 18:9 Comment(3)
@Bahadır: Good question. When mine was the only answer, I accepted it to keep my rate up; but no points were awarded. After accepting, @Berfield got +15 for an answer that helped us all, and I got +2 for accepting. I'd call that win-win-win-win! :-) Sorry I overlooked this comment earlier when responding to @Sundhas.Vermis
@Vermis For future readers, it's more helpful if your answer is accepted. It was confusing for a moment or two, until I realised this was an FAQ-style question and the complete answer is below!Paschall
@DuncanJones: Good point; updated in the question. I'm leaving this as the accepted answer, as it's cited elsewhere.Vermis
V
48

The JFreeChart class DynamicTimeSeriesCollection is a good choice.

Addendum: As noted by @Bahadır, the last point of the series was persistently zero. @Don helpfully suggests advancing the time and then appending the data.

dataset.advanceTime();
dataset.appendData(newData);

enter image description here

import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JPanel;
import javax.swing.Timer;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.ValueAxis;
import org.jfree.chart.plot.XYPlot;
import org.jfree.data.time.DynamicTimeSeriesCollection;
import org.jfree.data.time.Second;
import org.jfree.data.xy.XYDataset;
import org.jfree.chart.ui.ApplicationFrame;
import org.jfree.chart.ui.UIUtils;

/**
 * @see https://mcmap.net/q/426297/-add-values-to-a-specified-series-in-a-dynamictimeseriescollection
 * @see http://stackoverflow.com/questions/5048852
 */
public class DTSCTest extends ApplicationFrame {

    private static final String TITLE = "Dynamic Series";
    private static final String START = "Start";
    private static final String STOP = "Stop";
    private static final float MINMAX = 100;
    private static final int COUNT = 2 * 60;
    private static final int FAST = 100;
    private static final int SLOW = FAST * 5;
    private static final Random random = new Random();
    private Timer timer;

    public DTSCTest(final String title) {
        super(title);
        final DynamicTimeSeriesCollection dataset =
            new DynamicTimeSeriesCollection(1, COUNT, new Second());
        dataset.setTimeBase(new Second(0, 0, 0, 1, 1, 2011));
        dataset.addSeries(gaussianData(), 0, "Gaussian data");
        JFreeChart chart = createChart(dataset);

        final JButton run = new JButton(STOP);
        run.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                String cmd = e.getActionCommand();
                if (STOP.equals(cmd)) {
                    timer.stop();
                    run.setText(START);
                } else {
                    timer.start();
                    run.setText(STOP);
                }
            }
        });

        final JComboBox combo = new JComboBox();
        combo.addItem("Fast");
        combo.addItem("Slow");
        combo.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                if ("Fast".equals(combo.getSelectedItem())) {
                    timer.setDelay(FAST);
                } else {
                    timer.setDelay(SLOW);
                }
            }
        });

        this.add(new ChartPanel(chart) {
            @Override
            public Dimension getPreferredSize() {
                return new Dimension(640, 480);
            }
        }, BorderLayout.CENTER);
        JPanel btnPanel = new JPanel(new FlowLayout());
        btnPanel.add(run);
        btnPanel.add(combo);
        this.add(btnPanel, BorderLayout.SOUTH);

        timer = new Timer(FAST, new ActionListener() {
            float[] newData = new float[1];

            @Override
            public void actionPerformed(ActionEvent e) {
                newData[0] = randomValue();
                dataset.advanceTime();
                dataset.appendData(newData);
            }
        });
    }

    private float randomValue() {
        return (float) (random.nextGaussian() * MINMAX / 3);
    }

    private float[] gaussianData() {
        float[] a = new float[COUNT];
        for (int i = 0; i < a.length; i++) {
            a[i] = randomValue();
        }
        return a;
    }

    private JFreeChart createChart(final XYDataset dataset) {
        final JFreeChart result = ChartFactory.createTimeSeriesChart(
            TITLE, "hh:mm:ss", "milliVolts", dataset, true, true, false);
        final XYPlot plot = result.getXYPlot();
        ValueAxis domain = plot.getDomainAxis();
        domain.setAutoRange(true);
        ValueAxis range = plot.getRangeAxis();
        range.setRange(-MINMAX, MINMAX);
        return result;
    }

    public void start() {
        timer.start();
    }

    public static void main(final String[] args) {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                DTSCTest demo = new DTSCTest(TITLE);
                demo.pack();
                UIUtils.centerFrameOnScreen(demo);
                demo.setVisible(true);
                demo.start();
            }
        });
    }
}
Vermis answered 19/2, 2011 at 4:34 Comment(13)
Thanks for this great sample. Do you know a way to prevent the last point of the line to be at zero?Officeholder
@Bahadır: It appears to be an intentional effect of advanceTime(). I've never had a use case for a different baseline value. Any thoughts? Don't forget to up-vote answers you found useful. :-)Vermis
"You last voted on this answer Apr 25 at 13:11" I always do. By the way, I fixed this by using TimeSeriesCollection instead of the dynamic one. Then replaced dataset.appendData with add, and setting a life time for the dataset. It gives pretty much the same functionality.Officeholder
@Bahadır: Ah, I see; thank you for that, as well as the alternative approach.Vermis
@Bahadir, can i request you to please share the code you have implemented?Illfounded
one thing i want to ask you @Vermis that can we store this graph and make it visible in our webpage? if so how ?Illfounded
@Illfounded I would use trashgod's code with Don's fix if I was doing it all over again. But I can find it tomorrow when I am at work, if you still want it.Officeholder
@Illfounded saving a graph is a different question, please create a new question for that.Officeholder
@Sundhas: For a snapshot, use ChartUtilities with servlet/JSP, as suggested here; for live, I'd use Java Web Start. @Bahadır: Thanks for confirming.Vermis
@Vermis I dont want to save it as a snapshot, i want the moving graph in my webpage.Illfounded
@Sundhas: Interactive? I'd go with an applet via JWS.Vermis
@Vermis How to add one more line and one more y-axis on the left with DynamicTimeSeries?Constance
@DươngAnhKhoa: Start with the variation cited in the question and this example.Vermis
B
16

You can also eliminate the zero by first advanceTime(), then appendData. (swap the way they are doing it in the example).

Berfield answered 4/5, 2011 at 18:9 Comment(3)
@Bahadır: Good question. When mine was the only answer, I accepted it to keep my rate up; but no points were awarded. After accepting, @Berfield got +15 for an answer that helped us all, and I got +2 for accepting. I'd call that win-win-win-win! :-) Sorry I overlooked this comment earlier when responding to @Sundhas.Vermis
@Vermis For future readers, it's more helpful if your answer is accepted. It was confusing for a moment or two, until I realised this was an FAQ-style question and the complete answer is below!Paschall
@DuncanJones: Good point; updated in the question. I'm leaving this as the accepted answer, as it's cited elsewhere.Vermis
N
8

One alternative approach to @thrashgod's answer would be to use TimeSeriesCollection and setting item age on the TimeSeries. Below code can setup a graph to show last 1 hour of data with 1 minute intervals.

private TimeSeriesCollection dataset;
private TimeSeries sensorSeries;
sensorSeries = new TimeSeries("name", Minute.class);
sensorSeries.setMaximumItemAge(60);
dataset = new TimeSeriesCollection();
dataset.addSeries(sensorSeries);

..and you will add the data as it comes with:

sensorSeries.add(new Minute(new Date()), newData);
Natatorial answered 29/6, 2011 at 7:59 Comment(1)
See also this Q&A.Vermis

© 2022 - 2024 — McMap. All rights reserved.