Sunday, 8 July 2012

How to draw Dynamic Line or Timeseries Chart in Java using jfreechart library?


Today we are going to write a code to draw a dynamic timeseries-cum-line chart in java.  The only difference between simple and dynamic chart is that a dynamic event is used to create a new series and update the graph. In out example we are using timer which automatically calls a funtion after every 1/4 th second and graph is updated with random data. Let's try with the code :

Note : I had tried my best to provide complete documentation along with code. If at any time anyone have any doubt or question please post in comments section.


DynamicLineAndTimeSeriesChart.java



import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.Timer;
import javax.swing.JPanel;

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.Millisecond;
import org.jfree.data.time.TimeSeries;
import org.jfree.data.time.TimeSeriesCollection;
import org.jfree.data.xy.XYDataset;
import org.jfree.ui.ApplicationFrame;
import org.jfree.ui.RefineryUtilities;

/**
 * An example to show how we can create a dynamic chart.
*/
public class DynamicLineAndTimeSeriesChart extends ApplicationFrame implements ActionListener {

    /** The time series data. */
    private TimeSeries series;

    /** The most recent value added. */
    private double lastValue = 100.0;
   
    /** Timer to refresh graph after every 1/4th of a second */
    private Timer timer = new Timer(250, this);
   
    /**
     * Constructs a new dynamic chart application.
     *
     * @param title  the frame title.
     */
    public DynamicLineAndTimeSeriesChart(final String title) {

        super(title);
        this.series = new TimeSeries("Random Data", Millisecond.class);
       
        final TimeSeriesCollection dataset = new TimeSeriesCollection(this.series);
        final JFreeChart chart = createChart(dataset);
       
        timer.setInitialDelay(1000);
       
        //Sets background color of chart
        chart.setBackgroundPaint(Color.LIGHT_GRAY);
       
        //Created JPanel to show graph on screen
        final JPanel content = new JPanel(new BorderLayout());
       
        //Created Chartpanel for chart area
        final ChartPanel chartPanel = new ChartPanel(chart);
       
        //Added chartpanel to main panel
        content.add(chartPanel);
        
        //Sets the size of whole window (JPanel)
        chartPanel.setPreferredSize(new java.awt.Dimension(800, 500));
       
        //Puts the whole content on a Frame
        setContentPane(content);
       
        timer.start();

    }

    /**
     * Creates a sample chart.
     *
     * @param dataset  the dataset.
     *
     * @return A sample chart.
     */
    private JFreeChart createChart(final XYDataset dataset) {
        final JFreeChart result = ChartFactory.createTimeSeriesChart(
            "Dynamic Line And TimeSeries Chart",
            "Time",
            "Value",
            dataset,
            true,
            true,
            false
        );
       
        final XYPlot plot = result.getXYPlot();
       
        plot.setBackgroundPaint(new Color(0xffffe0));
        plot.setDomainGridlinesVisible(true);
        plot.setDomainGridlinePaint(Color.lightGray);
        plot.setRangeGridlinesVisible(true);
        plot.setRangeGridlinePaint(Color.lightGray);
                
        ValueAxis xaxis = plot.getDomainAxis();
        xaxis.setAutoRange(true);
       
        //Domain axis would show data of 60 seconds for a time
        xaxis.setFixedAutoRange(60000.0);  // 60 seconds
        xaxis.setVerticalTickLabels(true);
       
        ValueAxis yaxis = plot.getRangeAxis();
        yaxis.setRange(0.0, 300.0);
       
        return result;
    }
    /**
     * Generates an random entry for a particular call made by time for every 1/4th of a second.
     *
     * @param e  the action event.
     */
    public void actionPerformed(final ActionEvent e) {
       
        final double factor = 0.9 + 0.2*Math.random();
        this.lastValue = this.lastValue * factor;
       
        final Millisecond now = new Millisecond();
        this.series.add(new Millisecond(), this.lastValue);
       
        System.out.println("Current Time in Milliseconds = " + now.toString()+", Current Value : "+this.lastValue);
    }

    /**
     * Starting point for the dynamic graph application.
     *
     * @param args  ignored.
     */
    public static void main(final String[] args) {

        final DynamicLineAndTimeSeriesChart demo = new DynamicLineAndTimeSeriesChart("Dynamic Line And TimeSeries Chart");
        demo.pack();
        RefineryUtilities.centerFrameOnScreen(demo);
        demo.setVisible(true);

    }

}  


OUTPUT : 




Thanks!!!!!!!!!!! Enjoy Programming :)

 

24 comments:

  1. Can We have a scroller to see the previously plotted points.

    ReplyDelete
  2. Yes sure you can do it.

    Following is the procedure to achieve this functionality.

    1) Add slider to main panel.

    2) Get minimum value of domain axis.

    3) Set interval for domain axis for which you wanna show values, for example in this example i can opt for 60 or 120 seconds.

    4) Write a state change function which will trigger on change of slider or scroller value. Set domain axis minimum and maximum limit as per the slider value.

    That's it.

    As reference you can also use following link, a very good example of scrollbar implementation.

    http://stackoverflow.com/questions/3231840/jfreechart-scroll-xybarchart-horizontally-chart-translation-and-navigation

    ReplyDelete
  3. Hi Shiv.. Can u please help me in ploting some values which i'l be retrieving dynamicaly from Database.

    I had used line chart.

    These values are on X-axis. I plotted the first 10 values. Then For dynamicity, I was adding a new(11th) value and deleting the first one. So looked like a moving chart.

    It worked fine. But the problem is, I am unable to see the previous data plotted.

    I am new to JFreeChart.
    Please help me.

    ReplyDelete
  4. have you tried slider as per my previous comment?

    ReplyDelete
  5. yes i added the slider in the above code. But dint added anything in state change.

    Coz I found that you are giving the X axis value as time.

    I m realy confused.

    Pleas guide me how can i do same as you have done above.
    Thanks.

    ReplyDelete
  6. hmm...I have to gone through your code. Can you please post your code here.

    ReplyDelete
  7. public class LineChartApplet extends JApplet
    {
    private static final long serialVersionUID = 1L;

    final CategoryDataset dataset = new DefaultCategoryDataset();



    public void init()
    {
    this.setSize(600, 270);
    final JFreeChart chart = createChart(dataset);
    final ChartPanel chartPanel = new ChartPanel(chart);

    chartPanel.setPreferredSize(
    new java.awt.Dimension(500, 270));

    setContentPane(chartPanel);
    }




    private JFreeChart createChart(final CategoryDataset dataset)
    {
    final JFreeChart chart = ChartFactory.createLineChart(
    "RPM", // chart title
    "X", // domain axis label
    "Y", // range axis label
    dataset, // data
    PlotOrientation.VERTICAL, // orientation
    true, // include legend
    true, // tooltips
    false // urls
    );


    final Shape[] shapes = new Shape[3];
    int[] xpoints;
    int[] ypoints;

    // right-pointing triangle
    xpoints = new int[] {-3, 3, -3};
    ypoints = new int[] {-3, 0, 3};
    shapes[0] = new Polygon(xpoints, ypoints, 3);

    // vertical rectangle
    shapes[1] = new Rectangle2D.Double(-2, -3, 3, 6);

    // left-pointing triangle
    xpoints = new int[] {-3, 3, 3};
    ypoints = new int[] {0, -3, 3};
    shapes[2] = new Polygon(xpoints, ypoints, 3);

    final DrawingSupplier supplier =
    new DefaultDrawingSupplier(

    DefaultDrawingSupplier.DEFAULT_PAINT_SEQUENCE,

    DefaultDrawingSupplier.DEFAULT_OUTLINE_PAINT_SEQUENCE,

    DefaultDrawingSupplier.DEFAULT_STROKE_SEQUENCE,

    DefaultDrawingSupplier.DEFAULT_OUTLINE_STROKE_SEQUENCE,
    shapes
    );

    final CategoryPlot plot = chart.getCategoryPlot();

    plot.setDrawingSupplier(supplier);



    chart.setBackgroundPaint(Color.lightGray);

    // set the stroke for each series...
    plot.getRenderer().setSeriesStroke(
    0,
    new BasicStroke(
    2.0f, BasicStroke.CAP_ROUND,
    BasicStroke.JOIN_ROUND,
    1.0f, new float[] {10.0f, 6.0f}, 0.0f
    )
    );

    plot.getRenderer().setSeriesStroke(
    1,
    new BasicStroke(
    2.0f, BasicStroke.CAP_ROUND,
    BasicStroke.JOIN_ROUND,
    1.0f, new float[] {6.0f, 6.0f}, 0.0f
    )
    );

    plot.getRenderer().setSeriesStroke(
    2,
    new BasicStroke(
    2.0f, BasicStroke.CAP_ROUND,
    BasicStroke.JOIN_ROUND,
    1.0f, new float[] {2.0f, 6.0f}, 0.0f
    )
    );

    // customise the renderer...
    final LineAndShapeRenderer renderer = (LineAndShapeRenderer) plot.getRenderer();

    renderer.setItemLabelsVisible(false);

    // customise the range axis...
    final NumberAxis rangeAxis = (NumberAxis) plot.getRangeAxis();
    rangeAxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits());
    rangeAxis.setAutoRangeIncludesZero(false);
    rangeAxis.setUpperMargin(0.12);

    return chart;
    }

    public void SetLine(double rpm , int xAxisVal , int columnKey)
    {

    ((DefaultCategoryDataset)dataset).removeValue("rpm", columnKey);
    ((DefaultCategoryDataset)dataset).addValue(rpm , "rpm" , new Integer(xAxisVal));
    }

    public void SetLine(double rpm , int xAxisVal)
    {
    ((DefaultCategoryDataset)dataset).addValue(rpm , "rpm" , new Integer(xAxisVal));
    }


    }

    /*
    The last two functions are for adding values.
    the first one is to add one value and remove the first one.
    and the second method is only for adding the value
    */

    ReplyDelete
  8. @Mukesh : After going through your code i think your x-axis values fall between -3 to 3. Right? If yes then it's difficult to move backward using any logic :(

    ReplyDelete
  9. @Shiv : Can u please explain why it is so..

    If it is not at all possible den can u please give me other solution. And can your example serve my purpose.

    Thanks.

    ReplyDelete
  10. Because your x-axis values are between -3 to 3. But i'm not able to get then how your graph is moving because if at x=-3 you are having another value then it will overwrite the previous one. Can you please send your chat screenshot on dirtyhandsphp@gmail.com

    ReplyDelete
  11. Hi shiv. I have send you the screenshot on the given mailid.

    Can your example serve my purpose i.e. can I I/P (x,y) values.

    ReplyDelete
  12. Mukesh : Got your email. Now everything is clear. You are using Linechart to draw your graph and my example is based on TimeSeriesChart. As per your requirements you have to work with LineChart only (Because on x-axis you are using values instead of time). So this example won't meet your purpose. But for your help I am sending an example that would for sure will work for you. Check your email.

    ReplyDelete
  13. thanx.. i'l check it and will tell you if any problem..

    ReplyDelete
  14. Replies
    1. hi Modi...I need a line chart in that the x axis is continuously moving with respect to the time interval of every min and y value update is get from the another one class.So can you help me for that application.

      Delete
    2. Hi Bala, For that you have to make few changes.

      set timer as one minute as you want the chart to be updated after every minute

      /** Timer to refresh graph after every 60 seconds */
      private Timer timer = new Timer(60000, this);

      //Use first parameter as minute instead of milliseconds
      this.series.add(new Minute(), this.lastValue);

      and

      //Domain axis would show data of 60 minutes for a time
      //it would show graph of 1 hour
      xaxis.setFixedAutoRange(3600000.0); // 60 minutes

      Please let me know if it helped?

      Delete
    3. hi i need a dynamic line chart in web page.Can u help me.Its in simple JSP not with servlet.Only with simple beans.The updated value comes in separate class..I wrote the code that code get the value for every 5 min.And its updated in one table shown in the JSP.I m struct in the moving Graph.Pls help.If u hav any code mail 2 my id pls.kishorekumar224@gamil.com

      Delete
    4. I think following page can solve your problem

      http://www.jfree.org/phpBB2/viewtopic.php?t=9621

      Delete
  15. Are you interested in part time (freelance) projects?

    ReplyDelete
  16. Hi Shiv,
    I get following exception while running your code.

    Exception in thread "main" java.lang.NoClassDefFoundError: org/jfree/ui/ApplicationFrame
    at java.lang.ClassLoader.defineClass1(Native Method)
    at java.lang.ClassLoader.defineClassCond(ClassLoader.java:631)
    at java.lang.ClassLoader.defineClass(ClassLoader.java:615)
    at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:141)
    at java.net.URLClassLoader.defineClass(URLClassLoader.java:283)
    at java.net.URLClassLoader.access$000(URLClassLoader.java:58)
    at java.net.URLClassLoader$1.run(URLClassLoader.java:197)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(URLClassLoader.java:190)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:306)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:247)
    Caused by: java.lang.ClassNotFoundException: org.jfree.ui.ApplicationFrame
    at java.net.URLClassLoader$1.run(URLClassLoader.java:202)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(URLClassLoader.java:190)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:306)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:247)
    ... 12 more





    I didn't have jfreechart. So I downloaded jcommon-1.0.18.jar and jfreechart-1.0.0.jar and compiled the code as:

    javac -classpath jfreechart-1.0.0.jar:jcommon-1.0.18.jarDynamicLineAndTimeSeriesChart.java


    ReplyDelete
    Replies
    1. Have you imported all the packages mentioned in example?

      Delete
  17. shiv, i need a chat exactly like this;
    with timeseries chart, 3 lines starts from right and goes to left in every 5 seconds. need multiple lines on the go :) my mail: ersi_n@yahoo.com thanks if u help me..

    ReplyDelete
    Replies
    1. Hi Ersin, It's too late, but have you done with the issue?

      Delete

Thanks for your valuable comments.