JFreeChart timeseries is not refreshing - java

I have developed a timeseries JFreeChart by using code from this thread.
I want to add this to my main panel, which contains four other panels. So I created a method
package com.garnet.panel;
import java.awt.*;
import java.awt.event.*;
import java.text.*;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.GregorianCalendar;
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.*;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYItemRenderer;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.data.time.DynamicTimeSeriesCollection;
import org.jfree.data.time.Second;
import org.jfree.data.xy.XYDataset;
import org.jfree.ui.RectangleInsets;
import org.jfree.ui.RefineryUtilities;
public class PrepareChart {
private static final String TITLE = "Dynamic Series";
private static final int COUNT = 2 * 60;
private Timer timer;
private static float lastValue = 49.62f;
ValueAxis axis;
DateAxis dateAxis;
public JFreeChart chart;
public PrepareChart() {
super();
final DynamicTimeSeriesCollection dataset = new DynamicTimeSeriesCollection(1, COUNT, new Second());
// Get the calender date time which will inserted into time series chart
// Based on time we are getting here we disply the chart
Calendar calendar = new GregorianCalendar();
int date = calendar.get(Calendar.DAY_OF_MONTH);
int month = calendar.get(Calendar.MONTH);
int year = calendar.get(Calendar.YEAR);
int hours = calendar.get(Calendar.HOUR_OF_DAY);
int minutes = calendar.get(Calendar.MINUTE);
int seconds = calendar.get(Calendar.SECOND);
System.out.println("In caht constructor methoed");
dataset.setTimeBase(new Second(seconds, minutes-2, hours, date, month, year));
dataset.addSeries(gaussianData(), 0, "Currency Rate");
chart= createChart(dataset);
timer = new Timer(969, new ActionListener() {
float[] newData = new float[1];
#Override
public void actionPerformed(ActionEvent e) {
newData[0] = randomValue();
System.out.println("In caht timer methoed");
dataset.advanceTime();
dataset.appendData(newData);
}
});
}
private float randomValue() {
double factor = 2 * Math.random();
if (lastValue >51){
lastValue=lastValue-(float)factor;
}else {
lastValue = lastValue + (float) factor;
}
return lastValue;
}
// For getting the a random float value which is supplied to dataset of time series chart
private float[] gaussianData() {
float[] a = new float[COUNT];
for (int i = 0; i < a.length; i++) {
a[i] = randomValue();
}
return a;
}
// This methode will create the chart in the required format
private JFreeChart createChart(final XYDataset dataset) {
final JFreeChart result = ChartFactory.createTimeSeriesChart(TITLE, "hh:mm:ss", "Currency", dataset, true, true, false);
final XYPlot plot = result.getXYPlot();
plot.setBackgroundPaint(Color.BLACK);
plot.setDomainGridlinePaint(Color.WHITE);
plot.setRangeGridlinePaint(Color.WHITE);
plot.setAxisOffset(new RectangleInsets(5.0, 5.0, 5.0, 5.0));
plot.setDomainCrosshairVisible(true);
plot.setRangeCrosshairVisible(true);
XYItemRenderer r = plot.getRenderer();
if (r instanceof XYLineAndShapeRenderer) {
XYLineAndShapeRenderer renderer = (XYLineAndShapeRenderer) r;
renderer.setBaseShapesVisible(true);
renderer.setBaseShapesFilled(true);
renderer.setBasePaint(Color.white);
renderer.setSeriesPaint(0,Color.magenta);
}
dateAxis= (DateAxis)plot.getDomainAxis();
DateTickUnit unit = null;
unit = new DateTickUnit(DateTickUnitType.SECOND,30);
DateFormat chartFormatter = new SimpleDateFormat("HH:mm:ss");
dateAxis.setDateFormatOverride(chartFormatter);
dateAxis.setTickUnit(unit);
NumberAxis rangeAxis = (NumberAxis) plot.getRangeAxis();
rangeAxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits());
rangeAxis.setRange(lastValue-4, lastValue+4);
return result;
}
public void start(){
timer.start();
}
public JPanel getChartPanel(){
EventQueue.invokeLater(new Runnable() {
public void run() {
PrepareChart chart = new PrepareChart();
System.out.println("In caht getter methoed");
chart.start();
}
});
return new ChartPanel(chart);
}
}
I am calling this code inside one of my panel constructors like this:
public class ChartPanel extends JPanel{
private Dimension dim;
private PrepareChart chart;
public JPanel jChart;
public ChartPanel(){
dim = super.getToolkit().getScreenSize();
this.setBounds(2,2,dim.width/4,dim.height/4);
chart = new PrepareChart();
jChart =chart.getChartPanel();
this.add(jChart);
}
But when I add this panel to the frame, the graph is not changing dynamically.

OK, I think I have spotted your problem, but I can't be completely sure without seeing how you use all this code.
Your main issue lies here:
public JPanel getChartPanel(){
EventQueue.invokeLater(new Runnable() {
public void run() {
PrepareChart chart = new PrepareChart();
System.out.println("In caht getter methoed");
chart.start();
}
});
return new ChartPanel(chart);
}
In your Runnable, you recreate a new instance of PrepareChart and you start it. This does not make any sense:
Your enclosing PrepareChart instance is never started (hence you don't see it updated dynamically)
The instance you create in your runnable cannot be reached by anyone/anything so that instance if lost forever in the AWT event-queue.
So instead, I would only be using the following:
public JPanel getChartPanel() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
start();
}
});
return new ChartPanel(chart);
}
This is a small main method that I wrote which seemed to do the trick.
public static void main(String[] args) {
PrepareChart prepareChart = new PrepareChart();
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(prepareChart.getChartPanel());
frame.pack();
frame.setVisible(true);
}
Consider renaming your class ChartPanel because it is conflicting with the names of JFreeChart which is confusing. Also, I don't see the use of it since you could perform all that directly on the ChartPanel returned by PrepareChart.
Btw, it is quite odd to put the call to start() in a getter-method.

Related

Multiple graphs in multiple figures using jFreeChart

I am trying to use jFreechart to generate two figures each of which with 12 graphs (being referred as series in jFreeChart ). However some of the graphs get simply skipped! I know I have synchronization issue here and tried to used the method the user #trashgod provided me here however I failed. I know the way I use swingworker is wrong! I dont know how to fix it
Each figure should contain 10 graphs which are parallel horizontal straight lines. As you see in the attached image some of the lines are missing. The two figures have to be identical too ( which are not). In practice I will have to generate multiple graphs in several locations of my applications at various times(random time interval between each figure and even graphs of individual figures)
Any help will be very much appreciated
Exception in thread "AWT-EventQueue-0" java.lang.IllegalArgumentException: This dataset already contains a series with the key Plot 11
at org.jfree.data.xy.XYSeriesCollection.addSeries(XYSeriesCollection.java:154)
at swing.FastChart2$MySwingWorker.process(FastChart2.java:192)
at javax.swing.SwingWorker$3.run(SwingWorker.java:414)
at sun.swing.AccumulativeRunnable.run(AccumulativeRunnable.java:112)
at javax.swing.SwingWorker$DoSubmitAccumulativeRunnable.run(SwingWorker.java:832)
at sun.swing.AccumulativeRunnable.run(AccumulativeRunnable.java:112)
at javax.swing.SwingWorker$DoSubmitAccumulativeRunnable.actionPerformed(SwingWorker.java:842)
at javax.swing.Timer.fireActionPerformed(Timer.java:312)
at javax.swing.Timer$DoPostEvent.run(Timer.java:244)
at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:251)
at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:733)
at java.awt.EventQueue.access$200(EventQueue.java:103)
at java.awt.EventQueue$3.run(EventQueue.java:694)
at java.awt.EventQueue$3.run(EventQueue.java:692)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:76)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:703)
at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:242)
at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:161)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:150)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:146)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:138)
at java.awt.EventDispatchThread.run(EventDispatchThread.java:91)
package swing;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.geom.Ellipse2D;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.IOException;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingWorker;
import org.jfree.chart.*;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYItemRenderer;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;
import org.jfree.util.ShapeUtilities;
public class FastChart2 extends JFrame {
private XYSeries [] xySeries ;
private XYPlot xyPlot;
private XYSeriesCollection xySeriesCollection;
private String title;
private static int instanceNum=0;
private int figNum=0;
private ChartPanel chartPanel;
public FastChart2(String s) {
super(s);
figNum = instanceNum;
instanceNum++;
init(s);
}
private void init(String s){
title = s;
xySeries = new XYSeries[12];
for (int i = 0; i < xySeries.length; i++) {
xySeries[i] = new XYSeries("Plot "+i);
}
xySeriesCollection = new XYSeriesCollection();
JFreeChart chart = ChartFactory.createScatterPlot(
title, "X", "Y", xySeriesCollection,
PlotOrientation.VERTICAL, true, true, false);
xyPlot = chart.getXYPlot();
xyPlot.setDomainCrosshairVisible(true);
xyPlot.setRangeCrosshairVisible(true);
chartPanel = createChartPanel(chart);
add(chartPanel, BorderLayout.CENTER);
JPanel control = new JPanel();
add(control, BorderLayout.SOUTH);
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
pack();
setLocationRelativeTo(null);
setVisible(true);
}
private ChartPanel createChartPanel(JFreeChart chart) {
XYItemRenderer renderer = xyPlot.getRenderer();
renderer.setSeriesPaint(0, Color.magenta);
renderer.setSeriesPaint(1, Color.green);
renderer.setSeriesPaint(2, Color.blue);
renderer.setSeriesPaint(4, Color.black);
renderer.setSeriesPaint(3, Color.yellow);
Shape cross = ShapeUtilities.createDiagonalCross(3, 0);
Shape plus = ShapeUtilities.createRegularCross(4,0);
for (int i = 0; i <=3; i++) {
renderer.setSeriesShape(0+i, new Rectangle(-1, -1, 2, 2));
renderer.setSeriesShape(4+i, new Ellipse2D.Float(-2F, -2F, 5F, 5F));
renderer.setSeriesShape(8+i, cross);
}
NumberAxis domain = (NumberAxis) xyPlot.getDomainAxis();
domain.setRange(0,1000);
NumberAxis range = (NumberAxis) xyPlot.getRangeAxis();
range.setRange(0,1200);
return new ChartPanel(chart);
}
public void multiPlot(){
Thread thread = null;
thread = new Thread (){
public void run() {
final double [] x = new double[1000];
final double [] y = new double[1000];
try{
for (int k = 0; k < 12; k++) {
for (int i = 0; i < y.length; i++) {
x[i] = i;
y[i] = k*100;
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
}
plot2d(k % 12, x, y," Fig:"+figNum+" Seri:"+k);
}
} catch (Exception e){
System.out.println();
}
}
};
thread.start();
}
public synchronized void plot2d( final int iSeriesN, final double [] dX, final double [] dY, final String sT){
if (dY.length != dX.length){
throw new IllegalArgumentException("Error! inputs x and y have to be of same size.");
}
MySwingWorker mySwingWorker = new MySwingWorker( iSeriesN, dX, dY, sT);
mySwingWorker
.addPropertyChangeListener(new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent pcEvt) {
if (pcEvt.getNewValue() == SwingWorker.StateValue.DONE) {
System.out.println("done");
}
if ("progress".equals(pcEvt.getPropertyName())) {
System.out.println("progress");
}
}
});
mySwingWorker.execute();
}
private class MySwingWorker extends SwingWorker<Void, Double> {
private double [] dX ;
private double [] dY ;
private String title;
private int iSeriesN;
private MySwingWorker(int iSeriesN, double [] ix, double[] iy, String st){
dX = ix.clone();
dY = iy.clone();
title= st;
this.iSeriesN = iSeriesN;
xySeriesCollection.removeAllSeries();
System.out.println("xySeriesCollection.removeAllSeries();");
}
#Override
public Void doInBackground() throws IOException {
// chartPanel.getChart().removeChangeListener((ChartChangeListener) chartPanel);
xySeries[iSeriesN].clear();
for (int i = 0; i < dX.length; i++) {
xySeries[iSeriesN].add(dX[i], dY[i]);
}
for (int i = 0; i < xySeries.length; i++) {
setProgress(i * (100 / xySeries.length));
publish(Double.valueOf(i));
try {
Thread.sleep(10);
} catch (InterruptedException e) {
} // simulate latency
}
return null;
}
#Override
protected void process(List<Double> chunks) {
for (double d : chunks) {
xySeriesCollection.addSeries(xySeries[(int) d]);
}
}
#Override
protected void done() {
try {
// chartPanel.getChart().addChangeListener((ChartChangeListener) chartPanel);
xySeries[iSeriesN].setKey(title);
} catch (Exception ignore) {
}
}
}
public XYSeries addXY(final int iSeriesN, final double [] dX, final double [] dY){
XYSeries series = new XYSeries("Plot ");
for (int i = 0; i < dX.length; i++) {
series.add(dX[i], dY[i]);
}
return series;
}
public static void main(String args[]) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
FastChart2 [] demo = new FastChart2[2];
for (int i = 0; i < demo.length; i++) {
demo[i] = new FastChart2("Figure "+i);
demo[i].multiPlot();
}
}
});
}
}
I know the way I use swingworker is wrong! I dont know how to fix it
Before start I have some tips:
Get rid of the arrays: you have several of them and you'll see they
only mess the things up because you'll need indexes and loops
everywhere to work with them and it's too easy make a mistake. I'd
especially remove this one:
private XYSeries [] xySeries; //XYSeriesCollection is intended to keep a series list, so...
Don't make your class extend from JFrame (or any Swing component) if you don't will add any functionality. You just can use a variable instead.
Besides the SwingWorker implementation needs to be fixed, it's more disturbing having a new Thread that calls this SwingWorker. Get rid of it too (it's no needed).
As #trahsgod pointed out in this comment, XYSeriesCollection is the chart's model so the key is working with it.
Having said this, about your SwingWorker implementation, it should look like this:
SwingWorker<Void, XYSeries> worker = new SwingWorker<Void, XYSeries>() {
#Override
protected Void doInBackground() throws Exception {
/*
* This part is extracted from your multiPlot() method
* I've just reduced the scale factor and get rid of double arrays
*/
int numberOfElements = 100; // this is the number of elementes in X axis
for(int y = 0; y < 12; y++) { // we want 12 series
XYSeries series = new XYSeries("Plot " + y);
for (int x = 0; x < numberOfElements; x++) {
series.add(x, y*10); //add x,y point
}
publish(series);
Thread.sleep(100);// just for animation purpose
}
return null;
}
#Override
protected void process(List<XYSeries> chunks) {
for(XYSeries series : chunks){
/*
* Add the series to the "model" here.
* It will notify the "view" data has been changed and this last one will be updated
* It's important make this call here to ensure the "view" is updated in the EDT.
*/
xySeriesCollection.addSeries(series);
}
}
};
Working example
Here is a complete working example based on your work that you can take as start point. Hope it be helpful :)
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Ellipse2D;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import org.jfree.chart.*;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYItemRenderer;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;
import org.jfree.util.ShapeUtilities;
public class FreeChartDemo {
XYSeriesCollection xySeriesCollection;
String title;
public FreeChartDemo(String title){
this.title = title;
}
public void initGUI(){
JButton clearChart = new JButton("Clear chart");
clearChart.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
xySeriesCollection.removeAllSeries();
}
});
JButton fillChart = new JButton("Fill chart") ;
fillChart.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
xySeriesCollection.removeAllSeries();
fillChart();
}
});
JPanel controlPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
controlPanel.add(clearChart);
controlPanel.add(fillChart);
JPanel content = new JPanel(new BorderLayout(5, 5));
content.add(getFreeChartPanel(), BorderLayout.CENTER); //add the ChartPanel here
content.add(controlPanel, BorderLayout.SOUTH);
JFrame frame = new JFrame("JFreeChart demo");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(content);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
private JPanel getFreeChartPanel(){
xySeriesCollection = new XYSeriesCollection();
JFreeChart chart = ChartFactory.createScatterPlot(title, "X axis", "Y axis", xySeriesCollection,
PlotOrientation.VERTICAL, true, true, false);
XYPlot plot = chart.getXYPlot();
plot.setDomainCrosshairVisible(true);
plot.setRangeCrosshairVisible(true);
XYItemRenderer renderer = plot.getRenderer();
renderer.setSeriesPaint(0, Color.magenta);
renderer.setSeriesPaint(1, Color.green);
renderer.setSeriesPaint(2, Color.blue);
renderer.setSeriesPaint(4, Color.black);
renderer.setSeriesPaint(3, Color.yellow);
Shape cross = ShapeUtilities.createDiagonalCross(3, 0);
for (int i = 0; i <= 3; i++) {
renderer.setSeriesShape(0+i, new Rectangle(-1, -1, 2, 2));
renderer.setSeriesShape(4+i, new Ellipse2D.Float(-2F, -2F, 5F, 5F));
renderer.setSeriesShape(8+i, cross);
}
NumberAxis domain = (NumberAxis) plot.getDomainAxis();
domain.setRange(0,100);
NumberAxis range = (NumberAxis) plot.getRangeAxis();
range.setRange(0,120);
return new ChartPanel(chart);
}
private void fillChart() {
SwingWorker<Void, XYSeries> worker = new SwingWorker<Void, XYSeries>() {
#Override
protected Void doInBackground() throws Exception {
int numberOfElements = 1000;
for(int y = 0; y < 12; y++) {
XYSeries series = new XYSeries("Plot " + y);
for (int x = 0; x < numberOfElements; x++) {
series.add(x, y*10); //add x,y point
}
publish(series);
Thread.sleep(100);// just for animation purpose
}
return null;
}
#Override
protected void process(List<XYSeries> chunks) {
for(XYSeries series : chunks){
xySeriesCollection.addSeries(series);
}
}
};
worker.execute();
}
public static void main(String args[]) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new FreeChartDemo("JFreeChart #1").initGUI();
new FreeChartDemo("JFreeChart #2").initGUI();
}
});
}
}
You're doing the same thing -- calling Swing from a background thread.
Here you create a new thread in multiPlot, and then call the Swing Timer from that thread -- don't do that -- a Swing Timer should only be started on the Swing event dispatch thread (or EDT). Have you tried using a SwingWorker instead? If so, what has been your result?
And you appear to be using a Swing Timer with a delay of 0 and then stopping it immediately. If so, that's a bit odd, and suggests that you shouldn't be using a timer at all.

Dynamic JFreeChart at 30 second intervals

I am new in JFreeChart and new to Java also.
I have this data from our claims table:
SEQ IP_CLAIMS_RECEIVED HB_CLAIMS_RECEIVED IP_AVERAGE_RESPONSE HB_AVERAGE_RESPONSE
30 Seconds 29 19 4 4
This data every 30 seconds will change giving the amount of claims processed and with the avarage seconds it took to process this.
So my graph I want it on the Y-Axis to display the round seconds like (0,,5,10,15,20,25,30,35,40) and then on the X-Axis to display the time - the 30 seconds intervals showing the refresh times. And then the spike-lines would show the average-response times... And if it will be possible have two combined graphs one for IP Claims and another for HB Claims. I have a perfect example of the graph but its confusing me a bit here is the code of it below:
package timeseriesdemo;
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.ui.ApplicationFrame;
import org.jfree.ui.RefineryUtilities;
/** #see https://stackoverflow.com/questions/5048852 */
public class TimeSeriesDemo 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 TimeSeriesDemo(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), 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();
System.out.println("dataset1 : " + dataset.advanceTime());
dataset.advanceTime();
dataset.appendData(newData);
}
});
}
private float randomValue() {
System.out.println("randomvalue : " + (float) (random.nextGaussian() * MINMAX / 3));
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", "Claims Received", 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() {
TimeSeriesDemo demo = new TimeSeriesDemo(TITLE);
demo.pack();
RefineryUtilities.centerFrameOnScreen(demo);
demo.setVisible(true);
demo.start();
}
});
}
}

Random errors when changing series using JFreeChart

I'm making a GUI that display result of background calculations. But before that, I wanted to test changing the dataset. Here is my code:
DefaultXYDataset dataset = new DefaultXYDataset();
#Override
public void run() {
// TODO Auto-generated method stub
for (int i = 0; i < periods; i++) {
series[0][i] = (double) i;
series[1][i] = 0;
}
dataset.addSeries("Series0", series);
for (int it = 0; it < 10; it++) {
series[1][random.nextInt(periods)] = random.nextInt(100) / 2;
double[][] d = new double[2][periods];
for (int i = 0; i < periods; i++) {
d[0][i] = series[0][i];
d[1][i] = series[1][i];
}
dataset.removeSeries("Series0");
dataset.addSeries("Series0", series);
// try {
// Thread.sleep(100);
// } catch (java.lang.InterruptedException ex) {
// }
}
As you can see, I want to change points on the graph (every time it finishes 'some complicated computations') - this change is in the thread invoked by me in another class. My problem is that this whole concept is not working. It throws 'Series index out of bounds'-IllegalArgumentException, 'index out of bounds' - of some library inner arraylist etc.. I'm not using DynamicTimeSeriesCollection because I need the X axis to be the number of my inner iterations not the time period, and also update when 'some computations' are finished not every some time period. Can you tell me what I'm doing wrong? Or is there a better way to update/refresh the graph?
Your snippet is incorrectly synchronized; you should update your dataset from the process() method of a SwingWorker, as shown here. Because your domain is "the number of my inner iterations", don't use a DateAxis; instead, use a NumberAxis, as shown in ChartFactory.createXYLineChart().
Addendum: This variation on the example plots the worker's progress on a line chart. Note that createXYLineChart() uses NumberAxis for both domain and range. Given a series in the line chart's dataset, note also how the implementation of process() can safely update the dataset as new data arrives; the listening chart will update itself in response.
private XYSeries series = new XYSeries("Result");
…
#Override
protected void process(List<Double> chunks) {
for (double d : chunks) {
label.setText(df.format(d));
series.add(++n, d);
}
}
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.beans.PropertyChangeEvent;
import java.text.DecimalFormat;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JProgressBar;
import javax.swing.SwingWorker;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.data.xy.XYDataset;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;
/**
* #see https://stackoverflow.com/a/13205322/230513
* #see https://stackoverflow.com/questions/4637215
*/
public final class ChartWorker {
private static final String S = "0.000000000000000";
private final JProgressBar progressBar = new JProgressBar();
private final JLabel label = new JLabel(S, JLabel.CENTER);
private final XYSeries series = new XYSeries("Result");
private final XYDataset dataset = new XYSeriesCollection(series);
private void create() {
JFrame f = new JFrame("√2");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(progressBar, BorderLayout.NORTH);
JFreeChart chart = ChartFactory.createXYLineChart(
"Newton's Method", "X", "Y", dataset,
PlotOrientation.VERTICAL, false, true, false);
XYPlot plot = (XYPlot) chart.getPlot();
plot.getRangeAxis().setRange(1.4, 1.51);
plot.getDomainAxis().setStandardTickUnits(
NumberAxis.createIntegerTickUnits());
XYLineAndShapeRenderer renderer
= (XYLineAndShapeRenderer) plot.getRenderer();
renderer.setSeriesShapesVisible(0, true);
f.add(new ChartPanel(chart) {
#Override
public Dimension getPreferredSize() {
return new Dimension(640, 480);
}
}, BorderLayout.CENTER);
f.add(label, BorderLayout.SOUTH);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
runCalc();
}
private void runCalc() {
progressBar.setIndeterminate(true);
TwoWorker task = new TwoWorker();
task.addPropertyChangeListener((PropertyChangeEvent e) -> {
if ("progress".equals(e.getPropertyName())) {
progressBar.setIndeterminate(false);
progressBar.setValue((Integer) e.getNewValue());
}
});
task.execute();
}
private class TwoWorker extends SwingWorker<Double, Double> {
private static final int N = 5;
private final DecimalFormat df = new DecimalFormat(S);
double x = 1;
private int n;
#Override
protected Double doInBackground() throws Exception {
for (int i = 1; i <= N; i++) {
x = x - (((x * x - 2) / (2 * x)));
setProgress(i * (100 / N));
publish(x);
Thread.sleep(1000); // simulate latency
}
return x;
}
#Override
protected void process(List<Double> chunks) {
for (double d : chunks) {
label.setText(df.format(d));
series.add(++n, d);
}
}
}
public static void main(String[] args) {
EventQueue.invokeLater(new ChartWorker()::create);
}
}

JFreeChart scaling of Boxplots with several Categories

i am currently working on a java-based project using JFreeChart to display boxplots.
My Problem is how to display a chart containing boxplots for a CategoryDataset with about 20 Categories and 5+ Series.
Currently if the preferred size of the ChartPanel is not set, the Legend, Labels and Annotations are readable but the Boxplots are too small. Or the size of the ChartPanel is set so that the Boxplots have an acceptable size but then the legend, labels and annotations are horizontally stretched.
My question is, how to correctly scale the boxplots without scaling the legend, axis Labels and annotations of the Chart? Is it possible to scale the Plot without scaling all the elements of the Chart?
Code Example
import java.awt.Color;
import java.awt.Dimension;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.CategoryAxis;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.plot.CategoryPlot;
import org.jfree.chart.renderer.category.BoxAndWhiskerRenderer;
import org.jfree.data.statistics.DefaultBoxAndWhiskerCategoryDataset;
public class StretchedBoxAndWhiskerExample{
DefaultBoxAndWhiskerCategoryDataset dataset;
JFreeChart chart;
ChartPanel chartPanel;
JFrame frame;
JScrollPane scrollPane;
public StretchedBoxAndWhiskerExample() {
createCategoryBoxplot();
frame = new JFrame();
scrollPane = new JScrollPane(chartPanel);
scrollPane.setPreferredSize(new Dimension(800,700));
scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
frame.add(scrollPane);
frame.pack();
frame.setVisible(true);
}
private void createCategoryBoxplot(){
dataset = createCategoryDataset();
CategoryAxis xAxis = new CategoryAxis("");
NumberAxis yAxis = new NumberAxis("Score");
BoxAndWhiskerRenderer renderer = new BoxAndWhiskerRenderer();
CategoryPlot plot = new CategoryPlot(dataset, xAxis, yAxis, renderer);
createJFreeChart(plot,"Test");
// Design
renderer.setFillBox(false);
renderer.setMeanVisible(false);
chart.setBackgroundPaint(Color.white);
plot.setBackgroundPaint(Color.lightGray);
plot.setDomainGridlinePaint(Color.white);
plot.setDomainGridlinesVisible(true);
plot.setRangeGridlinePaint(Color.white);
plot.getRangeAxis().setRange(-10.5, 10.5);
chartPanel = new ChartPanel(chart);
chartPanel.setPreferredSize(new Dimension(3250,600));
}
private DefaultBoxAndWhiskerCategoryDataset createCategoryDataset() {
dataset = new DefaultBoxAndWhiskerCategoryDataset();
ArrayList<Double> values = createSampleData();
ArrayList<String> categories = createSampleCategories();
for (int i=0;i<=5;i++){
for (String category : categories){
dataset.add(values,i,category);
}
}
return dataset;
}
private ArrayList<String> createSampleCategories() {
ArrayList<String> tmp = new ArrayList<String>();
for (int i=0;i<=20;i++){
tmp.add("Category"+i);
}
return tmp;
}
private ArrayList<Double> createSampleData() {
ArrayList<Double> tmp = new ArrayList<Double>();
for (int i=0;i<10;i++){
tmp.add(5.0);
tmp.add(7.0);
tmp.add(2.0);
tmp.add(4.0);
}
return tmp;
}
private void createJFreeChart(CategoryPlot plot, String title){
chart = new JFreeChart(title, plot);
}
public static void main(String[] args) throws IOException {
StretchedBoxAndWhiskerExample demo = new StretchedBoxAndWhiskerExample();
}
}
Set the preferred size of the containing ChartPanel, not the chart, as shown here and here.
Addendum: I don't think you can usefully add a chart to a scroll pane. Instead, create a class similar to SlidingCategoryDataset that implements BoxAndWhiskerCategoryDataset. Add a scroll bar to the frame that controls the first displayed index.
Addendum: A somewhat less ambitious approach is simply to page a portion of the data set using some suitable control, as shown in the example below.
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.swing.AbstractAction;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.CategoryAxis;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.plot.CategoryPlot;
import org.jfree.chart.renderer.category.BoxAndWhiskerRenderer;
import org.jfree.data.statistics.DefaultBoxAndWhiskerCategoryDataset;
/** #see https://stackoverflow.com/questions/6844759 */
public class BoxAndWhiskerDemo {
private static final int COLS = 20;
private static final int VISIBLE = 4;
private static final int ROWS = 5;
private static final int VALUES = 10;
private static final Random rnd = new Random();
private List<String> columns;
private List<List<List<Double>>> data;
private DefaultBoxAndWhiskerCategoryDataset dataset;
private CategoryPlot plot;
private ChartPanel chartPanel;
private JPanel controlPanel;
private int start = 0;
public BoxAndWhiskerDemo() {
createData();
createDataset(start);
createChartPanel();
createControlPanel();
}
private void createData() {
columns = new ArrayList<String>(COLS);
data = new ArrayList<List<List<Double>>>();
for (int i = 0; i < COLS; i++) {
String name = "Category" + String.valueOf(i + 1);
columns.add(name);
List<List<Double>> list = new ArrayList<List<Double>>();
for (int j = 0; j < ROWS; j++) {
list.add(createValues());
}
data.add(list);
}
}
private List<Double> createValues() {
List<Double> list = new ArrayList<Double>();
for (int i = 0; i < VALUES; i++) {
list.add(rnd.nextGaussian());
}
return list;
}
private void createDataset(int start) {
dataset = new DefaultBoxAndWhiskerCategoryDataset();
for (int i = start; i < start + VISIBLE; i++) {
List<List<Double>> list = data.get(i);
int row = 0;
for (List<Double> values : list) {
String category = columns.get(i);
dataset.add(values, "s" + row++, category);
}
}
}
private void createChartPanel() {
CategoryAxis xAxis = new CategoryAxis("Category");
NumberAxis yAxis = new NumberAxis("Value");
BoxAndWhiskerRenderer renderer = new BoxAndWhiskerRenderer();
plot = new CategoryPlot(dataset, xAxis, yAxis, renderer);
JFreeChart chart = new JFreeChart("BoxAndWhiskerDemo", plot);
chartPanel = new ChartPanel(chart);
}
private void createControlPanel() {
controlPanel = new JPanel();
controlPanel.add(new JButton(new AbstractAction("\u22b2Prev") {
#Override
public void actionPerformed(ActionEvent e) {
start -= VISIBLE;
if (start < 0) {
start = 0;
return;
}
createDataset(start);
plot.setDataset(dataset);
}
}));
controlPanel.add(new JButton(new AbstractAction("Next\u22b3") {
#Override
public void actionPerformed(ActionEvent e) {
start += VISIBLE;
if (start > COLS - VISIBLE) {
start = COLS - VISIBLE;
return;
}
createDataset(start);
plot.setDataset(dataset);
}
}));
}
public ChartPanel getChartPanel() {
return chartPanel;
}
public JPanel getControlPanel() {
return controlPanel;
}
public static void main(String[] args) throws IOException {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
BoxAndWhiskerDemo demo = new BoxAndWhiskerDemo();
frame.add(demo.getChartPanel(), BorderLayout.CENTER);
frame.add(demo.getControlPanel(), BorderLayout.SOUTH);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
}

Using JFreeChart to display recent changes in a time series

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().
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);
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 http://stackoverflow.com/a/15521956/230513
* #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();
}
});
}
}
You can also eliminate the zero by first advanceTime(), then appendData. (swap the way they are doing it in the example).
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);

Categories