real time plotting

A discussion forum for JFreeChart (a 2D chart library for the Java platform).
Locked
azedor
Posts: 1
Joined: Sat Sep 19, 2009 5:22 pm
antibot: No, of course not.

real time plotting

Post by azedor » Sat Sep 19, 2009 8:35 pm

I'm writting genetic algorithm and want to visualize it in real time. Generally speaking i want plot min, max and average value of fitness function. 3 curves in one plot area. I'm completely new with JFreeChart so code below isn't perfect, but this is how plotting looks like now.

Code: Select all

package sat;

import java.util.Random;

import javax.swing.JFrame;

import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.plot.PiePlot3D;
import org.jfree.chart.plot.Plot;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.StandardXYItemRenderer;
import org.jfree.data.general.DatasetGroup;
import org.jfree.data.general.DefaultPieDataset;
import org.jfree.data.general.PieDataset;
import org.jfree.data.xy.XYDataset;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;

public class ChartFrame extends JFrame {
	private static final int MAX_ITERATION = 1000;
	private static final int SLEEP_TIME = 100;
	private XYSeries minFitSerie;
	private XYSeries avgFitSerie;
	private XYSeries maxFitSerie;
	
	private int N = 50;
	private double[] minFitVal = new double[N];
	private double[] maxFitVal = new double[N];
	private double[] avgFitVal = new double[N];
	
	private Random rand = new Random();
	private Algorithm alg;
	
	public ChartFrame() {
		super("Chart");
		setSize(1024, 768);
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		getContentPane().add(makeChart());
		setVisible(true);
		
		alg = new Algorithm();
		alg.initAlgorithm();

		run();
	}
	
	public void run() {
		for(int i = 0; i < MAX_ITERATION; i++) {
			alg.runIteration();
			updateSeries();
			
			try {
				Thread.sleep(SLEEP_TIME);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			repaint();
		}
	}
	
	private void updateSeries() {
		minFitSerie.clear();
		maxFitSerie.clear();
		avgFitSerie.clear();

		
		for(int i = 0; i < N - 1; i++) {
			minFitVal[i] = minFitVal[i+1];
			maxFitVal[i] = maxFitVal[i+1];
			avgFitVal[i] = avgFitVal[i+1];
			minFitSerie.add(i, minFitVal[i]);
			maxFitSerie.add(i, maxFitVal[i]);
			avgFitSerie.add(i, avgFitVal[i]);
		}
		int i = N - 1;
		double v1 = alg.getCurrentMinFitness();
		double v2 = alg.getCurrentMaxFitness();
		double v3 = alg.getCurrentAvgFitness();
		minFitVal[i] = v1;
		maxFitVal[i] = v2;
		avgFitVal[i] = v3;
		minFitSerie.add(i, minFitVal[i]);
		maxFitSerie.add(i, maxFitVal[i]);
		avgFitSerie.add(i, avgFitVal[i]);
	}
	
	ChartPanel makeChart() {
		minFitSerie = new XYSeries("min"); 
		maxFitSerie = new XYSeries("max"); 
		avgFitSerie = new XYSeries("avg");
		XYDataset xyDatasetMin = new XYSeriesCollection(minFitSerie);
		XYDataset xyDatasetMax = new XYSeriesCollection(maxFitSerie);
		XYDataset xyDatasetAvg = new XYSeriesCollection(avgFitSerie);
		
		JFreeChart chart = ChartFactory.createXYLineChart("Population overview", "Pupulation number", "Fitness",
				null, PlotOrientation.VERTICAL, true, true, false);
		XYPlot plot = chart.getXYPlot();
		plot.setDataset(0, xyDatasetMin);
		plot.setRenderer(0, new StandardXYItemRenderer());
		plot.setDataset(1, xyDatasetMax);
		plot.setRenderer(1, new StandardXYItemRenderer());
		plot.setDataset(2, xyDatasetAvg);
		plot.setRenderer(2, new StandardXYItemRenderer());
		return new ChartPanel(chart);
		
	}
	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		ChartFrame frame = new ChartFrame();
	}

}
What i wanted accomplish is to draw 3 lines with different colors in real time. This code works but not perfect and i perhaps is very inefficient.
Firtst fundamental question: did i use correct type of chart, did process of creating, and updating values, redrawing is correct ? I doubt because i have got exception after about 60 iterations and it repeats with period about 50-70 iterations.

Code: Select all

Exception in thread "AWT-EventQueue-0" java.lang.IndexOutOfBoundsException: Index: 47, Size: 0 // Index value in each exception repetition differs
	at java.util.ArrayList.RangeCheck(Unknown Source)
	at java.util.ArrayList.get(Unknown Source)
	at org.jfree.data.xy.XYSeries.getDataItem(XYSeries.java:616)
	at org.jfree.data.xy.XYSeriesCollection.getX(XYSeriesCollection.java:320)
	at org.jfree.data.xy.AbstractXYDataset.getXValue(AbstractXYDataset.java:75)
	at org.jfree.chart.renderer.RendererUtilities.findLiveItemsUpperBound(RendererUtilities.java:181)
	at org.jfree.chart.renderer.RendererUtilities.findLiveItems(RendererUtilities.java:262)
	at org.jfree.chart.plot.XYPlot.render(XYPlot.java:3729)
	at org.jfree.chart.plot.XYPlot.draw(XYPlot.java:3310)
	at org.jfree.chart.JFreeChart.draw(JFreeChart.java:1235)
	at org.jfree.chart.ChartPanel.paintComponent(ChartPanel.java:1668)
	at javax.swing.JComponent.paint(Unknown Source)
	at javax.swing.JComponent.paintToOffscreen(Unknown Source)
	at javax.swing.RepaintManager$PaintManager.paintDoubleBuffered(Unknown Source)
	at javax.swing.RepaintManager$PaintManager.paint(Unknown Source)
	at javax.swing.BufferStrategyPaintManager.paint(Unknown Source)
	at javax.swing.RepaintManager.paint(Unknown Source)
	at javax.swing.JComponent._paintImmediately(Unknown Source)
	at javax.swing.JComponent.paintImmediately(Unknown Source)
	at javax.swing.RepaintManager.paintDirtyRegions(Unknown Source)
	at javax.swing.RepaintManager.paintDirtyRegions(Unknown Source)
	at javax.swing.RepaintManager.seqPaintDirtyRegions(Unknown Source)
	at javax.swing.SystemEventQueueUtilities$ComponentWorkRequest.run(Unknown Source)
	at java.awt.event.InvocationEvent.dispatch(Unknown Source)
	at java.awt.EventQueue.dispatchEvent(Unknown Source)
	at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
	at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
	at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
	at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
	at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
	at java.awt.EventDispatchThread.run(Unknown Source)
Besides this exception, i can notice errors with chart rendering (especially blinking). Sometimes values on x axis are changing and it results in sudden shift of entire plot what makes ugly effect, because the only thing i want to changing in each frame are only this 3 curves, nothing more.
Next thing i want to change are values on y axis. I know a priori that values are from certain interval <a, b>, and let's say that a = 180, b = 200. And now on Y axis i have values: 0, 10, 20, 30, ... 200. It causes that curves are almost on the top of the screen. So i would like have change to customize Y values and for this example they should be sth like that: 0, 50, 100, 150, 200, 250, 300;

I would appreciate if some one show me general idea how to do such plotting in most efficient way and without errors in rendering.

Edit. Here is working code(previous one required class with algorithm), you can try it and will see problems i described above. So it would be sufficient for me if one improves this code (what i really care is to how to get rid of blinking, changing axis, and background lines, these are not static now).

Code: Select all

import java.util.Random;
import javax.swing.JFrame;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.StandardXYItemRenderer;
import org.jfree.data.xy.XYDataset;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;

public class ChartFrame extends JFrame {
	private static final int MAX_ITERATION = 100000000;
	private static final int FPS = 25;
	private final int REFRESH_RATE = 1000/FPS;
	private XYSeries minFitSerie;
	private XYSeries avgFitSerie;
	private XYSeries maxFitSerie;
	
	private int N = 50;
	private double[] minFitVal = new double[N];
	private double[] maxFitVal = new double[N];
	private double[] avgFitVal = new double[N];
	
	private long lastTime;
	
	private Random rand = new Random();
	
	public ChartFrame() {
		super("Chart");
		setSize(1024, 768);
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		getContentPane().add(makeChart());
		setVisible(true);
		lastTime = System.nanoTime();
		
		run();
	}
	
	public void run() {
		for(int i = 0; i < MAX_ITERATION; i++) {
			//alg.runIteration(); here complex calculations
			generateSeries();
			long currentTime = System.nanoTime();
			long elapsed = currentTime - lastTime;
			elapsed /= 1000000;
			if(elapsed < REFRESH_RATE) continue;
			lastTime = currentTime;
			repaint();
		}
	}
	
	private void generateSeries() {
		minFitSerie.clear();
		maxFitSerie.clear();
		avgFitSerie.clear();
		
		for(int i = 0; i < N - 1; i++) {
			minFitVal[i] = minFitVal[i+1];
			maxFitVal[i] = maxFitVal[i+1];
			avgFitVal[i] = avgFitVal[i+1];
			minFitSerie.add(i, minFitVal[i]);
			maxFitSerie.add(i, maxFitVal[i]);
			avgFitSerie.add(i, avgFitVal[i]);
		}
		int i = N - 1;
		double v1 = rand.nextDouble()*10 + 180;
		double v2 = rand.nextDouble()*10 + 190;
		double v3 = (v1 + v2)/2;
		minFitVal[i] = v1;
		maxFitVal[i] = v2;
		avgFitVal[i] = v3;
		minFitSerie.add(i, minFitVal[i]);
		maxFitSerie.add(i, maxFitVal[i]);
		avgFitSerie.add(i, avgFitVal[i]);
	}
	
	ChartPanel makeChart() {
		minFitSerie = new XYSeries("min"); 
		maxFitSerie = new XYSeries("max"); 
		avgFitSerie = new XYSeries("avg");
		XYDataset xyDatasetMin = new XYSeriesCollection(minFitSerie);
		XYDataset xyDatasetMax = new XYSeriesCollection(maxFitSerie);
		XYDataset xyDatasetAvg = new XYSeriesCollection(avgFitSerie);
		
		JFreeChart chart = ChartFactory.createXYLineChart("Population overview", "Population number", "Fitness",
				null, PlotOrientation.VERTICAL, true, true, false);
		chart.setBackgroundImageAlpha(0.1f);
		XYPlot plot = chart.getXYPlot();

		
		
		plot.setDataset(0, xyDatasetMin);
		plot.setRenderer(0, new StandardXYItemRenderer());
		plot.setDataset(1, xyDatasetMax);
		plot.setRenderer(1, new StandardXYItemRenderer());
		plot.setDataset(2, xyDatasetAvg);
		plot.setRenderer(2, new StandardXYItemRenderer());
		return new ChartPanel(chart);
		
	}

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		ChartFrame frame = new ChartFrame();
	}

}


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

Re: real time plotting

Post by david.gilbert » Tue Sep 22, 2009 9:57 pm

The biggest problem I can see in your code is that you clear the data series for every update and repopulate them entirely. You should leave the existing data in each series, and just add one new data point to each series.

Another issue (less important) is that you have three datasets and three renderers in your chart. But each renderer is the same, and each dataset contains only one series. You could put all three series into one dataset, and use a single renderer.
David Gilbert
JFreeChart Project Leader

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

paradoxoff
Posts: 1634
Joined: Sat Feb 17, 2007 1:51 pm

Re: real time plotting

Post by paradoxoff » Tue Sep 22, 2009 10:25 pm

JFreeChart is not thread-safe. When you are clearing the series in your generateSeries-method, it might well be the case that the plot is currently trying to access the items during the rendering that takes place in another thread.
Running your code brought my system top 100 % cpu load, and I wasn´t even able to bring the task manager on screen!
Dynamic stuff like that is normally a good reason to develop a custom XYDataset implementation. I have added a little one in the code below. If you ckec the code carefully, you will also find the answers to your questions cocnerning the axes ranges.
The new dataset implementation performs the calculations in the background and informs the plot about changes if neccessary

Code: Select all

    import java.util.Arrays;
    import java.util.Random;
    import javax.swing.JFrame;
    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.StandardXYItemRenderer;
    import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
    import org.jfree.data.xy.AbstractXYDataset;
    import org.jfree.data.xy.XYDataset;
    import org.jfree.data.xy.XYSeries;
    import org.jfree.data.xy.XYSeriesCollection;

    public class ChartFrame extends JFrame {
       private static final int MAX_ITERATION = 100000000;
       private static final int FPS = 25;
       private final int REFRESH_RATE = 1000/FPS;
       private XYSeries minFitSerie;
       private XYSeries avgFitSerie;
       private XYSeries maxFitSerie;
       
       private int N = 50;
       private double[] minFitVal = new double[N];
       private double[] maxFitVal = new double[N];
       private double[] avgFitVal = new double[N];
       
       private long lastTime;
       
       private Random rand = new Random();
       
       public ChartFrame() {
          super("Chart");
          setSize(1024, 768);
          setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
          getContentPane().add(makeChart());
          setVisible(true);
          lastTime = System.nanoTime();
          
          //run();
       }
       
       public void run() {
          for(int i = 0; i < MAX_ITERATION; i++) {
             //alg.runIteration(); here complex calculations
             generateSeries();
             long currentTime = System.nanoTime();
             long elapsed = currentTime - lastTime;
             elapsed /= 1000000;
             if(elapsed < REFRESH_RATE) continue;
             lastTime = currentTime;
             //repaint();
          }
       }
       
       private void generateSeries() {
          minFitSerie.clear();
          maxFitSerie.clear();
          avgFitSerie.clear();
          
          for(int i = 0; i < N - 1; i++) {
             minFitVal[i] = minFitVal[i+1];
             maxFitVal[i] = maxFitVal[i+1];
             avgFitVal[i] = avgFitVal[i+1];
             minFitSerie.add(i, minFitVal[i]);
             maxFitSerie.add(i, maxFitVal[i]);
             avgFitSerie.add(i, avgFitVal[i]);
          }
          int i = N - 1;
          double v1 = rand.nextDouble()*10 + 180;
          double v2 = rand.nextDouble()*10 + 190;
          double v3 = (v1 + v2)/2;
          minFitVal[i] = v1;
          maxFitVal[i] = v2;
          avgFitVal[i] = v3;
          minFitSerie.add(i, minFitVal[i]);
          maxFitSerie.add(i, maxFitVal[i]);
          avgFitSerie.add(i, avgFitVal[i]);
       }
       
       ChartPanel makeChart() {
          minFitSerie = new XYSeries("min");
          maxFitSerie = new XYSeries("max");
          avgFitSerie = new XYSeries("avg");
          XYDataset xyDatasetMin = new XYSeriesCollection(minFitSerie);
          XYDataset xyDatasetMax = new XYSeriesCollection(maxFitSerie);
          XYDataset xyDatasetAvg = new XYSeriesCollection(avgFitSerie);
          
          JFreeChart chart = ChartFactory.createXYLineChart("Population overview", "Population number", "Fitness",
                null, PlotOrientation.VERTICAL, true, true, false);
          chart.setBackgroundImageAlpha(0.1f);
          ((NumberAxis)chart.getXYPlot().getDomainAxis()).setRange(0, 500);
          ((NumberAxis)chart.getXYPlot().getRangeAxis()).setAutoRangeIncludesZero(false);
          ((NumberAxis)chart.getXYPlot().getRangeAxis()).setRange(180,200);
          XYPlot plot = chart.getXYPlot();

          
          SimpleXYDataset dataset = new SimpleXYDataset();
          plot.setDataset(0, dataset);
          plot.setRenderer(0, new XYLineAndShapeRenderer(true, false));
          //plot.setDataset(1, xyDatasetMax);
          //plot.setRenderer(1, new XYLineAndShapeRenderer(true, false));
          //plot.setDataset(2, xyDatasetAvg);
          //plot.setRenderer(2, new XYLineAndShapeRenderer(true, false));
          new Thread(dataset).start();
          return new ChartPanel(chart);
          
       }

       /**
        * @param args
        */
       public static void main(String[] args) {
          // TODO Auto-generated method stub
          ChartFrame frame = new ChartFrame();
       }

    }
    class SimpleXYDataset extends AbstractXYDataset implements Runnable{
    	private final int N = 500;
    	private double[][] data;
    	private final int REPETITIONS = 10;
        private Random rand = new Random();

	   	SimpleXYDataset(){
    		super();
    		data = new double[4][N];
    		Arrays.fill(data[0], Double.NaN);
    		Arrays.fill(data[1], Double.NaN);
    		Arrays.fill(data[2], Double.NaN);
    		Arrays.fill(data[3], Double.NaN);
    		
    	} 
    	public int getSeriesCount(){
    		return 3;
    	}
    	public Comparable getSeriesKey(int series){
    		return "Series " + series;
    	}
    	public Number getX(int series, int item){
    		return new Double(data[0][item]);
    	}
    	public Number getY(int series, int item){
    		return new Double(data[1 + series][item]);
    	}
    	public int getItemCount(int series){
    		return N;
    	}
    	public void run(){
    		for(int r = 0; r < REPETITIONS; r++){
	    		for(int i = 0; i < N; i++){
		    		data[0][i] = i;
			        double v1 = rand.nextDouble()*10 + 180;
			        double v2 = rand.nextDouble()*10 + 190;
			        double v3 = (v1 + v2)/2;
			        data[1][i] = v1;
			        data[2][i] = v2;
			        data[3][i] = v3;
		    		fireDatasetChanged();
		    		try{
		    			Thread.sleep(25);
		    		}
		    		catch(InterruptedException e){
		    		}
	    		}
    			double[][] newData = new double[4][N];
	    		Arrays.fill(newData[0], Double.NaN);
	    		Arrays.fill(newData[1], Double.NaN);
	    		Arrays.fill(newData[2], Double.NaN);
	    		Arrays.fill(newData[3], Double.NaN);
	    		data = newData;
	    		fireDatasetChanged();
    		}
    	}
    }



liferiot
Posts: 6
Joined: Thu Feb 18, 2010 1:44 pm
antibot: No, of course not.

Re: real time plotting

Post by liferiot » Thu Feb 18, 2010 1:52 pm

Hi!

i digged out this old post because i have exatly the same problem.

Having real-Time data there is a need to clear() series or remove single elements from it.
As i found out here the occuring exceptions come from non thread safety.

A custom XYDataset implementation is a problem due to wanting to use JFreeChart in a commercial app.
After the lesser GNU the use of the Library is allowed but not more.

Is there any othere solution to get it thread safe?

jleech
Posts: 62
Joined: Fri Oct 26, 2007 9:18 pm

Re: real time plotting

Post by jleech » Fri Feb 19, 2010 4:57 pm

I'm not sure if your interpretation of the LGPL is correct. I am not a lawyer, and this is not legal advice, but you should be able to create your own implementation of XYDataset or any other interfaces etc. within JFreeChart, that are inputs to the library. So long as you are not modifying the source code itself.

And if thread safety of the modifications to the existing dataset from your code is the only problem, just make sure all the threads that do so in a synchronized manner. Not a big deal.

Locked