1

I am using line chart (chart.js) to display two pens of data over time. In the sample code below the chart is created and 30 seconds of historical data is immediately added to each pen. Thereafter a single point of realtime data (current time) is added to each pen via the OnTimer() function which is executed every second.

If one uses only one pen (const _PENS = 1;) the realtime data is correctly added to the historical data (right hand side of chart) as one would expect. Try it.

If one uses two pens (const _PENS = 2;) the realtime data is NOT added to the historical data but added to the left hand side of the chart. Try it.

How does one fix the chart configuration in the code below so that realtime data for multiple pens is always appended to the historical data?

<html>
<body>
    <canvas hidden="0" id="myLineChart"></canvas>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.9.1/chart.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.4/moment.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/chartjs-adapter-moment.min.js"></script>
    <script>
        // --- define my data set
        var MyDataset = {
            datasets: [
                {
                    label: 'Red pen (seconds)',
                    borderColor: 'red',
                    backgroundColor: 'red',
                },
                {
                    label: 'Orange pen (seconds+1)',
                    borderColor: 'orange',
                    backgroundColor: 'orange',
                },
            ],
        };

        // --- define my chart configuration
        var MyChartConfig = {
            type: "line",
            data: MyDataset,
            options: {
                plugins: {
                    title: {
                        display: true,
                        text: 'My Line Chart'
                    }
                },
                scales: {
                    x: {
                        // https://www.youtube.com/watch?v=H1y66SPBlRI
                        type: 'time',
                        time: {
                            unit: 'second',
                            displayFormats: {
                                second: 'YYYY-MM-DD, HH:mm:ss', // Format for the unit
                            }
                        },
                    },
                    y: {
                        position: 'left',
                        type: 'linear',
                        display: true,
                        title: {
                            display: true, // Set to true to display the title
                            text: 'Left axis', // The text content of the y-axis title
                        },
                    },
                },
            },
        };

        // --- set the number of pens to use
        //     using only 1 pen produces the desired and correct effect - realtime data is appended to the historical data on the right hand side of the chart
        //     using 2 pens gives a whacky result - realtime data is added to the left hand side of the chart !!!
        const _PENS = 2; // pens
        const _HISTORY = 30;  // seconds

        // --- add an OnTimer function to execute every second
        //     this function will add realtime data to the pens
        function OnTimer()
        {
            // --- add a new value to each pen at the current timestamp
            dt = new Date();
            MyLineChart.data.labels.push(dt);
            for (i = 0; i < _PENS; i++)
            {
                MyLineChart.data.datasets[i].data.push(dt.getSeconds()+i);
            }
            MyLineChart.update();
        }

        // --- create the chart
        var MyLineChart = new Chart(document.getElementById('myLineChart'), MyChartConfig);

        // --- add _HISTORY seconds of historical data to all pens on chart
        for (i = 0; i < _PENS; i++)
        {
            // --- get the time _HISTORY secs ago
            dt = new Date(new Date().getTime() - (_HISTORY*1000));
            for (j = 0; j < _HISTORY; j++)
            {
                // add one second
                dt = new Date(dt.getTime() + 1000);
                // add the new data point to the chart
                MyLineChart.data.labels.push(dt);
                MyLineChart.data.datasets[i].data.push(dt.getSeconds()+i);
            }
            MyLineChart.update();
        }

        // --- execute the OnTimer function every second
        setInterval(OnTimer, 1000);
    </script>
</body>
</html>

1 Answer 1

1

There's a simple error in the original code: in the data initialization sequence the same label is added multiple times, that is once for each "pen". So, when there are two pens, there are 2 * _HISTORY labels, while the datasets have only _HISTORY values; when the new data values are added on timer, they are paired with the duplicated (supplemental) labels already set, and the new labels are ignored.

That can be corrected easily, either by guarding the labels.push with an if(i===0) that will make the push to the labels execute just for one dataset, and not for each one:

for (i = 0; i < _PENS; i++)
{
   // --- get the time _HISTORY secs ago
   dt = new Date(new Date().getTime() - (_HISTORY*1000));
   for (j = 0; j < _HISTORY; j++)
   {
      // add one second
      dt = new Date(dt.getTime() + 1000);
      // add the new data point to the chart
      if(i === 0)
      {
         MyLineChart.data.labels.push(dt);
      }
      MyLineChart.data.datasets[i].data.push(dt.getSeconds()+i);
   }
   MyLineChart.update();
}

jsFiddle demo.

or by reversing the two fors in the initialization code:

var MyLineChart = new Chart(document.getElementById('myLineChart'), MyChartConfig);

dt = new Date(new Date().getTime() - (_HISTORY*1000));
for (j = 0; j <= _HISTORY; j++)
{
   // add one second
   dt = new Date(dt.getTime() + 1000);
   MyLineChart.data.labels.push(dt); 

   for (i = 0; i < _PENS; i++)
   {
      MyLineChart.data.datasets[i].data.push(dt.getSeconds()+i);
   }
   MyLineChart.update();
}

jsFiddle demo.

There's also a chart.js specific approach that simplifies things somehow: to use the object data structure, and eliminate the labels; that will allow one to add the same "label" (x coordinate) for each dataset, as it was in the original code:

for (i = 0; i < _PENS; i++)
{
   // --- get the time _HISTORY secs ago
   dt = new Date(new Date().getTime() - (_HISTORY*1000));
   for (j = 0; j < _HISTORY; j++)
   {
      // add one second
      dt = new Date(dt.getTime() + 1000);
      // add the new data point to the chart
      MyLineChart.data.datasets[i].data.push({x: dt, y: dt.getSeconds()+i});
   }
   MyLineChart.update();
}

The code has to be adapted for the OnTimer procedure, snippet demo:

var MyDataset = {
   datasets: [
      {
         label: 'Red pen (seconds)',
         borderColor: 'red',
         backgroundColor: 'red',
      },
      {
         label: 'Orange pen (seconds+1)',
         borderColor: 'orange',
         backgroundColor: 'orange',
      },
   ],
};

// --- define my chart configuration
var MyChartConfig = {
   type: "line",
   data: MyDataset,
   options: {
      plugins: {
         title: {
            display: true,
            text: 'My Line Chart'
         }
      },
      scales: {
         x: {
            type: 'time',
            time: {
               unit: 'second',
               displayFormats: {
                  second: 'YYYY-MM-DD, HH:mm:ss', // Format for the unit
               }
            },
         },
         y: {
            position: 'left',
            type: 'linear',
            display: true,
            title: {
               display: true, // Set to true to display the title
               text: 'Left axis', // The text content of the y-axis title
            },
         },
      },
   },
};

// --- set the number of pens to use
//     using only 1 pen produces the desired and correct effect - realtime data is appended to the historical data on the right hand side of the chart
//     using 2 pens gives a whacky result - realtime data is added to the left hand side of the chart !!!
const _PENS = 2; // pens
const _HISTORY = 10;  // seconds

// --- add an OnTimer function to execute every second
//     this function will add realtime data to the pens
function OnTimer()
{
   // --- add a new value to each pen at the current timestamp
   dt = new Date();
   //MyLineChart.data.labels.push(dt);
   for (i = 0; i < _PENS; i++)
   {
      MyLineChart.data.datasets[i].data.push({x: dt, y: dt.getSeconds()+i});
   }
   MyLineChart.update();
}

// --- create the chart
var MyLineChart = new Chart(document.getElementById('myLineChart'), MyChartConfig);

for (i = 0; i < _PENS; i++)
{
   // --- get the time _HISTORY secs ago
   dt = new Date(new Date().getTime() - (_HISTORY*1000));
   for (j = 0; j < _HISTORY; j++)
   {
      // add one second
      dt = new Date(dt.getTime() + 1000);
      // add the new data point to the chart
      MyLineChart.data.datasets[i].data.push({x: dt, y: dt.getSeconds()+i});
   }
   MyLineChart.update();
}

// --- execute the OnTimer function every second
const interval = setInterval(OnTimer, 1000);
<div style="min-height: 250px">
    <canvas id="myLineChart"></canvas>
</div>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.4/moment.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/chartjs-adapter-moment.min.js"></script>

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.