Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to plot animated line chart using d3 while the data table is required to be updated every 1 second?

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?

like image 843
jhyap Avatar asked Nov 29 '13 09:11

jhyap


1 Answers

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.

like image 67
couchand Avatar answered Oct 11 '22 03:10

couchand