5

I want to simulate the situation where there is continuous incoming real time (dynamic) data and feed to the chart

Therefore, I try to demonstrate an animated line chart using d3 while the data array is required to be continuous updated every 1 second.

The original work is inspired by benjchristensen

And here is my modified html source: my source code

I try to populate the buffer[100] with random number for every 1 second by calling function startGenerator()

    function startGenerator()
    {
        //signalGenerator();    
        setInterval("signalGenerator()", 1000); //
    }

    function signalGenerator()
    {
        var buffer = new Array(100);

        var i;
        for (i = 0; i < buffer.length; i++)
        {
            buffer[i] = Math.random() * 10;
        }

        FeedDataToChart("#graph1", 300, 300, "basis", true, 100, 100, buffer, 0);       
    }

Below is the FeedDataToChart() parameter names:

function FeedDataToChart(id, width, height, interpolation, animate, updateDelay, transitionDelay, data, startIndex)

I use a counter to check for the re-draw data index, every time when re-draw new data, counter increase by 1. Until the the counter < data.length-1, the re-draw timer should stop and get new data from the buffer[100] again.

        function stopTimer()
        {
            clearInterval(myTimer);
            alert("The redraw timer stop here, get new data from buffer");
        }

        function startTimer()
        {
            if (myTimer == null)
            {
                myTimer = setInterval(function() {
                if (counter < data.length - 1)
                {
                    var v = data.shift(); // remove the first element of the array
                    data.push(v); // add a new element to the array (we're just taking the number we just shifted off the front and appending to the end)
                    if(animate) 
                    {
                        redrawWithAnimation();
                    } 
                    else 
                    {
                        redrawWithoutAnimation();
                    }
                    counter++;
                }
                else
                {
                    alert("no more data in buffer");
                    stopTimer();
                    counter = startIndex;
                }
            }, updateDelay);
            }
        }

My problem occurs when I tried to repeat the startGenerator() function, the results shows like this: line chart

I am pretty new to javascript. Can anyone pin point me how to pull data from the buffer every 1 second and update the single line chart continuously?

Thank you!

EDIT I update the source and problem has been minimized: My New Source

I add the .remove() line at the stopTimer() to remove the sgv-holded data and reset the global buffer to null and call the startGenerator() function again to pull new data when timer stop.

Now the only problem I having is that everytime when new graph is created, it create a new sgv path where below the previous one. The new graph is moving downwards everytime when it was created. Check the new source I updated today. You will catch my description after you run the code.

How can I fixed the sgv path at the same location everytime when I update it?

jhyap
  • 3,779
  • 6
  • 27
  • 47
  • 1
    Works fine for me -- http://jsfiddle.net/7RfVG/ – Lars Kotthoff Dec 01 '13 at 11:28
  • u tried to uncomment the setInterval("signalGenerator()", 1000); and run the code again. – jhyap Dec 01 '13 at 13:14
  • I see what you mean, but it's not clear to me what you're trying to do. Do you simply want to generate more data for the same line? – Lars Kotthoff Dec 01 '13 at 13:27
  • Yes, you are right. I want to generate more data for the same line and animate it continuously. – jhyap Dec 02 '13 at 00:17
  • Could you just generate more data with the same function? Do you need to call the function several times? – Lars Kotthoff Dec 02 '13 at 09:47
  • I need to generate a specific amount of data at every interval of time. And thus, I called the FeedDataToChart when the buffer is populated. I am trying to demonstrate the implementation of real time animated line chart with dynamic data. Mean, the data set will be updated for every time interval and the data should be presented in an animated moving line chart. – jhyap Dec 02 '13 at 12:13
  • What are you trying to achieve by calling the function several times? Extend the time horizon? This would be easier by simply adjusting the amount of data that is generated with each call. – Lars Kotthoff Dec 02 '13 at 12:16
  • @LarsKotthoff I called the function several times is simply because I want to simulate the situation where there is continuous incoming real time data and feed to the chart. – jhyap Dec 03 '13 at 00:17

1 Answers1

5

The problem with simply calling FeedDataToChart again is that it creates entirely new svg and path elements rather than reusing the existing ones. The inner redraw method you're using follows the standard D3 update pattern, so take that as an example for how to rework the update.

Start by separating initialization from drawing. There's no reason the scale and line generators need to change on every update, so get that out of the way early. Also create the svg element as soon as you can and don't change it unless you really need to. Likewise with the path element.

The general structure of any D3 visualization will consist of three distinct phases:

intialization - perform as soon as possible after script load

  • create scale and svg generator functions (scale, axis, line, area etc.)
  • create data cleanup & aggregation functions (nest, custom filters etc.)

creation - perform as soon as possible after DOM ready

  • create svg element (only ever call .append("svg") once per chart)
  • create chart and axis groups (see the margin conventions)

draw - perform when data is available

  • first segment by enter/update/remove selections
    • find existing data elements (.selectAll() on data element CSS class)
    • rebind data to chart
  • perform on enter selection
    • create chart data elements (only call .append(el) after .enter())
    • set chart data element CSS class (make this the canonical handle)
    • set static properties (but if you can put these in CSS all the better)
  • perform on enter and update selections
    • set dynamic properties (here is the call to .attr("d", line))
  • you may also have a remove portion (for exit selections)

For more information take a look at Mike Bostock's tutorial on selections in D3.

The create, update, remove process is actually pretty simple once you get used to it. Here's a prototypical example:

// select existing data elements and rebind data
var dots = d3.selectAll(".dot")
    .data(data);

// append a circle for new dots
dots.enter().append("circle")
    .attr("class", "dot")
    .attr("r", 4);

// remove old dots
dots.exit().remove();

// update all new and existing dots
dots.attr("cx", x)
    .attr("cy", y);

The key to writing simple and performant D3 is to minimize the changes you're making to the DOM, since DOM manipulation tends to be the major performance bottleneck.

couchand
  • 2,639
  • 1
  • 21
  • 27