Highcharts not Drawing point on setInterval - javascript
I have data being put on an api every 30 seconds on a backend. On the frontend I am using highcharts to visualize the data and a setInterval setup to retrieve the new data every 30 seconds. My problem is that on that setInterval, the line graph disappears or does not draw to the next new dot. Does anyone now why this is?
fiddle: http://jsfiddle.net/b8tf281n/3/
code:
chart1 = {
yAxisMin: 40,
yAxisMax: 100
};
// empty objects for our data and to create chart
seriesData = [];
BPM = [];
time1 = [];
// console.log(chart1.data.series);
$(function () {
$(document).ready(function () {
Highcharts.setOptions({
global: {
useUTC: false
}
});
var url = 'http://msbandhealth.azurewebsites.net/odata/PulsesAPI/';
$.ajax({
url: url,
dataType: 'json',
context: seriesData,
success: function (data) {
// structure our data
for (var i = 0; i < data.value.length; i++) {
bpm = data.value[i].BPM;
time = data.value[i].Time;
BPM.push({
x: moment(time),
y: bpm
});
// console.log(BPM);
time1.push(time);
}
console.log((new Date).getTime());
console.log(moment(time, "DD.MM.YYYY hh:mm:ss"));
console.log(BPM);
console.log(BPM[BPM.length - 1]);
// console.log(seriesData);
// set our data series and create new chart
chart1.data.series[0].data = BPM;
chart = new Highcharts.Chart(chart1.data);
$('#container').css({
height: '400px'
});
// console.log(sortedBPM);
// console.log(time1);
}
});
// give highcharts something to render to
var container = document.getElementById("container");
chart1.data = {
chart: {
renderTo: container,
type: 'spline',
animation: Highcharts.svg, // don't animate in old IE
marginRight: 10,
events: {
load: function () {
setInterval(function () {
// find last data points
var test = BPM[BPM.length - 1];
var x = (new Date).getTime(),
y = test.y;
console.log(x);
shift = chart.series[0].data.length < 30;
chart.series[0].addPoint([x, y], true, true);
},
30000);
}
}
},
title: {
text: 'Microsoft Band: Real Time Pulse Analysis'
},
xAxis: {
type: 'datetime',
tickPixelInterval: 150,
dateTimeLabelFormats: {
},
},
yAxis: {
min: chart1.yAxisMin,
max: chart1.yAxisMax,
title: {
text: 'Heart Rate'
},
plotLines: [{
value: 0,
width: 1,
color: '#808080'
}]
},
tooltip: {
formatter: function () {
return '<b>' + this.series.name + '</b><br/>' + Highcharts.dateFormat('%H:%M:%S', this.x) + '<br/>' + Highcharts.numberFormat(this.y, 2);
}
},
legend: {
enabled: false
},
exporting: {
enabled: false
},
series: [{
name: 'Beats Per Minute',
data: []
}]
};
});
});
Your test.y is the problem. It returns undefined after one interval. Somehow BPM is changing in it's structure - changing each object from {x,y} to [0,1] - therefore I used:
y = (test.y !== undefined)? test.y : test[1];
to either get the previous structure or the new. I also set the interval to 3 seconds for you to see the difference easier. Here's the DEMO.
Related
Render chart only once with multiple events : Highcharts
So what I have is a chart in which I have events function which loads data for multiple sets. suppose I have data of 3000 points. The first data set renders the first 1000 points and after that second data set renders 2000 points. for which I am calling my 'events' function . but the problem arises that after showing the first 1000 set of data. The chart starts from the begining. I don't want that. That's why I need a solution so that my Highchart's chart render only once and the event function loads continuously. Here's a snip of my Highchart's js Highcharts.chart("chartcontainer", { // make thsi chart load only once. chart: { type: 'line', animation: Highcharts.svg, // don't animate in old IE marginRight: 10, //Load this event function as the data updates events: { load: function() { var series = this.series[0], chart = this; setInterval(function() { //some logic regarding the chart //.. v = { y: y, x: x }; console.log("V value", v); series.addSeries(v, false, true); counter++; localcounter++; } else { oldcounter=counter; flagToreload=1; } }, 1000/130); setInterval(function() { chart.redraw(false); }, 100); } } }, time: { useUTC: false }, title: { text: 'Live random data' }, xAxis: { type: 'Value', gridLineWidth: 1 }, yAxis: { title: { text: 'Value' }, plotLines: [{ value: 0, width: 1, color: '#808080' }], gridLineWidth: 1 }, tooltip: { headerFormat: '<b>{series.name}</b><br/>', pointFormat: '{point.x:%Y-%m-%d %H:%M:%S}<br/>{point.y:.2f}' }, exporting: { enabled: false }, series: [{ animation: false, name: 'Random data', data: (function() { // generate an array of random data var data = [], time = counter, i; for (i = -1000; i <= 0; i += 1) { data.push([ counter, null ]); } return data; }()) }] });
You can use: addPoint method: chart: { events: { load: function() { var newData, chart = this, series = chart.series[0]; setInterval(function() { newData = getRandomData(); newData.forEach(function(el) { series.addPoint(el, false); }); chart.redraw(); }, 2000); } } } Live demo: http://jsfiddle.net/BlackLabel/2a8qswhf/ API Reference: https://api.highcharts.com/class-reference/Highcharts.Series#addPoint setData method: chart: { events: { load: function() { var newData, chart = this, combinedData, series = chart.series[0]; setInterval(function() { newData = getRandomData(); combinedData = series.userOptions.data.concat(newData); series.setData(combinedData); }, 2000); } } } Live demo: http://jsfiddle.net/BlackLabel/Lmsk8yw9/ API Reference: https://api.highcharts.com/class-reference/Highcharts.Series#setData
Stop Highchart when end of the Array element reached
So What I am trying to Create is a Chart in which around 130 points data are flowing in a single second time. Now, Suppose I Have an array of 1000 elements which I want to render in Highchart. I am able to render the Highchart with the 1000 elements of array, but the problem is that after the end of the array, the Highchart is not stopping. it keeps running. I just want to stop the Highchart when it reaches end of the array. Here is the Code that I have tried var data = [7,10,8,6,8,2,5,2,2,2,3,4,4,10,6,10,5,10,0,2,9,5,7,0,5,0,5,7,8,7,5,1,9,1,5,7,10,3,7,0,2,10,8,8,1,1,0,7,6,3,1,10,0,8,3,5,5,10,3,8,4,0,3,5,8,7,0,0,0,8,7,3,2,1,0,6,7,7,3,7,5,0,1,0,0,4,9,4,4,3,0,2,10,9,0,2,7,6,5,9,0,6,7,9,0,6,2,7,1,6,5,1,3,1,0,5,0,4,2,7,2,0,7,0,3,10,6,3,4,9,9,10,10,9,5,9,8,5,3,2,5,9,5,10,10,7,8,3,8,9,5,3,9,0,1,2,6,4,1,9,0,5,0,5,6,4,1,1,3,8,2,0,7,6,4,0,6,5,5,6,4,6,6,3,3,5,5,8,7,3,4,7,5,6,1,4,2,4,7,9,8,4,0,3,8,6,2,6,10,8,10,1,10,8,6,8,8,3,8,7,8,6,7,10,9,10,0,7,7,8,1,10,1,4,7,10,7,0,9,6,3,3,3,1,4,3,4,5,0,5,9,0,6,1,8,5,1,8,9,4,0,4,5,4,3,8,2,10,5,9,4,9,5,3,6,5,7,2,2,7,1,1,9,7,4,1,7,5,2,10,6,4,3,5,7,2,4,5,7,3,2,0,8,5,5,7,8,2,9,5,9,1,2,0,7,8,10,5,6,9,2,5,1,9,4,0,7,4,4,7,10,0,8,6,0,6,4,4,8,3,9,1,3,0,0,4,4,2,5,3,3,6,1,4,6,6,4,9,7,0,2,2,1,2,2,6,2,0,2,5,7,8,8,1,7,3,9,0,0,0,9,4,9,8,9,7,10,8,2,0,5,10,10,0,1,7,5,9,2,8,10,10,4,6,6,6,10,10,5,1,5,2,1,9,3,2,5,3,1,3,10,7,1,3,5,3,0,6,7,5,1,10,5,9,0,9,2,3,0,10,0,6,9,8,3,8,7,0,10,4,2,2,5,6,7,0,10,10,9,9,8,6,10,5,9,0,6,4,8,5,0,4,4,4,1,8,3,0,9,3,3,9,1,5,0,10,1,1,3,10,6,5,8,0,6,5,2,7,10,4,2,0,10,1,7,6,2,0,1,6,10,10,6,6,10,1,4,7,5,6,5,4,1,5,6,9,1,8,4,7,2,8,5,9,7,3,9,2,2,8,10,6,10,10,7,8,7,2,6,8,7,4,6,1,5,9,2,0,5,4,4,0,4,5,7,2,3,8,9,1,0,8,6,10,8,3,7,3,3,6,7,2,7,6,6,2,8,6,9,6,8,5,4,8,4,6,7,9,8,7,8,5,8,2,0,8,5,0,5,2,8,4,7,0,7,1,4,8,10,8,2,5,10,9,1,9,3,0,6,9,2,10,5,1,4,0,4,9,1,1,3,5,0,5,5,2,5,1,1,7,0,2,6,8,10,7,9,3,0,9,4,2,4,6,2,2,9,2,7,3,6,6,9,2,2,0,2,9,1,5,7,10,10,1,8,5,9,9,4,1,7,1,0,0,6,7,7,7,2,9,9,2,10,5,4,2,3,10,9,9,7,1,1,6,8,4,9,6,7,4,9,4,2,7,6,2,6,0,1,5,5,8,9,5,1,7,8,7,3,2,3,1,1,2,5,6,4,10,8,10,3,3,1,0,6,7,0,7,4,8,4,7,6,0,7,1,2,3,10,4,1,10,7,9,4,7,7,10,10,10,6,8,9,7,3,10,9,0,3,6,2,10,5,3,6,8,5,0,2,3,1,6,9,10,9,10,7,5,8,7,4,4,3,10,2,3,4,3,10,8,5,9,8,5,6,2,4,9,1,5,0,7,1,1,4,4,10,4,7,8,8,9,7,1,9,5,5,4,3,0,0,9,5,6,3,3,4,6,1,2,6,2,8,4,10,0,9,1,10,2,7,3,6,3,9,2,3,5,7,8,1,1,9,3,4,6,7,6,2,2,8,0,10,3,3,5,7,4,1,3,9,5,4,3,3,2,9,8,7,8,7,6,8,7,7,1,2,10,10,2,9,3,2,3,2,9,9,0,3,0,4,9,6,0,1,6,9,5,8,7,9,5,6,8,3,5,3,4,9,8,0,3,4,8,6,9,4,7,1,9,8,0,4,8,1,0,7,3,3,1,6,9,9,5,4,3,8,8,9,10,4,9,0,7,8,6,9,8,4,10,4,1,2,5,4,0,6,4,1]; var json_array = data ; var i = 0; function next() { return json_array[i++]; // i++; } Highcharts.chart('container', { chart: { type: 'line', animation: Highcharts.svg, // don't animate in old IE marginRight: 10, events: { load: function() { // set up the updating of the chart each second var series = this.series[0], chart = this; var count = 0 ; setInterval(function() { var x = (new Date()).getTime(), // current time y =next(); console.log(y) ; series.addPoint([x, y], false, true); chart.redraw(false); }, 1000/130); } } }, time: { useUTC: false }, title: { text: 'ECG Graph Plot From MySQl Data' }, xAxis: { type: 'datetime', labels: { enabled: false }, tickPixelInterval: 150 }, yAxis: { //max: 1.5, //min: -1.5, title: { text: 'Value' }, plotLines: [{ value: 0, width: 1, color: '#808080' }] }, tooltip: { headerFormat: '<b>{series.name}</b><br/>', pointFormat: '{point.x:%Y-%m-%d %H:%M:%S}<br/>{point.y:.2f}' }, legend: { enabled: false }, exporting: { enabled: false }, series: [{ animation: false, name: 'ECG Graph Plot From MySQl Data', dataGrouping: { enabled: false }, data: (function() { // generate an array of random data var data = [], time = (new Date()).getTime(), i; for (i = -1000 ; i <= 0; i += 1) { data.push([ time + i * 10, null ]); } return data; }()) }] }); I Have also created a Fiddle for it as you can see below. https://jsfiddle.net/abnitchauhan/toqaxLj7/
Just clear Interval when it reached the last element of the array. check out following code. var draw = setInterval(function() { var x = (new Date()).getTime(), // current time y =next(); console.log(y) ; if (i == data.length) { clearInterval(draw); return; } series.addPoint([x, y], false, true); chart.redraw(false); }, 1000/130); here is the fiddle code link. https://jsfiddle.net/geekcode/8nL53x01/1/
Updating highcharts live data not working
I have some UV Sensors (currently running on Thingspeak) - but I need to have multiple series on the same chart, so I made a sample .php page on my website. I have the basic chart working nicely, but I have not been able to get it to do live updates - my coding skills are very lacking & I would appreciate any help I can get! The sample chart is here: http://www.sesupply.co.nz/test.php I have the code on JSFiddle here: https://jsfiddle.net/SESupply/9xn65qrL/9/ // variables for the first series var series_1_channel_id = 43330; var series_1_field_number = 4; var series_1_read_api_key = '7ZPHNX2SXPM0CA1K'; var series_1_results = 480; var series_1_color = '#d62020'; var series_1_name = 'Zims Sensor'; // variables for the second series var series_2_channel_id = 45473; var series_2_field_number = 2; var series_2_read_api_key = 'N12T3CWQB5IWJAU9'; var series_2_results = 480; var series_2_color = '#00aaff'; var series_2_name = 'UVM30A'; // chart title var chart_title = 'UV Sensors Zim / UVM30A'; // y axis title var y_axis_title = 'UV Index'; // user's timezone offset var my_offset = new Date().getTimezoneOffset(); // chart variable var my_chart; // when the document is ready $(document).on('ready', function () { // add a blank chart addChart(); // add the first series addSeries(series_1_channel_id, series_1_field_number, series_1_read_api_key, series_1_results, series_1_color, series_1_name); // add the second series addSeries(series_2_channel_id, series_2_field_number, series_2_read_api_key, series_2_results, series_2_color, series_2_name); }); // add the base chart function addChart() { // variable for the local date in milliseconds var localDate; // specify the chart options var chartOptions = { chart: { renderTo: 'chart-container', defaultSeriesType: 'spline', zoomType: 'x', // added here panning: true, panKey: 'shift', backgroundColor: '#ffffff', events: { load: addSeries } }, title: { text: chart_title }, subtitle: { text: 'Click and drag to zoom in. Hold down shift key to pan.' }, plotOptions: { series: { marker: { radius: 2 }, animation: true, step: false, borderWidth: 0, turboThreshold: 0 } }, scrollbar: { enabled: true // barBackgroundColor: 'gray', // barBorderRadius: 7, // barBorderWidth: 0, // buttonBackgroundColor: 'gray', // buttonBorderWidth: 0, // buttonArrowColor: 'yellow', // buttonBorderRadius: 7, // rifleColor: 'yellow', // trackBackgroundColor: 'white', // trackBorderWidth: 1, // trackBorderColor: 'silver', // trackBorderRadius: 7 }, tooltip: { // reformat the tooltips so that local times are displayed formatter: function () { var d = new Date(this.x + (my_offset * 60000)); var n = (this.point.name === undefined) ? '' : '<br>' + this.point.name; return this.series.name + ':<b>' + this.y + '</b>' + n + '<br>' + d.toDateString() + '<br>' + d.toTimeString().replace(/\(.*\)/, ""); } }, xAxis: { type: 'datetime', title: { text: 'Date' } }, rangeSelector: { enabled: true, buttons: [{ type: 'minute', count: 60, text: 'Hour' }, { type: 'day', count: 1, text: 'Day' }, { type: 'week', count: 1, text: 'Week' }, { type: 'all', text: 'All' }] }, yAxis: { title: { text: y_axis_title } }, exporting: { enabled: true }, legend: { enabled: true }, credits: { text: 'ThingSpeak.com', href: 'https://thingspeak.com/', style: { color: '#D62020' } } }; // draw the chart my_chart = new Highcharts.Chart(chartOptions); } // add a series to the chart function addSeries(channel_id, field_number, api_key, results, color, name) { var field_name = 'field' + field_number; // get the data with a webservice call $.getJSON('https://api.thingspeak.com/channels/' + channel_id + '/fields/' + field_number + '.json?offset=0&round=2&results=' + results + '&api_key=' + api_key, function (data) { // blank array for holding chart data var chart_data = []; // iterate through each feed $.each(data.feeds, function () { var point = new Highcharts.Point(); // set the proper values var value = this[field_name]; point.x = getChartDate(this.created_at); point.y = parseFloat(value); // add location if possible if (this.location) { point.name = this.location; } // if a numerical value exists add it if (!isNaN(parseInt(value))) { chart_data.push(point); } }); // add the chart data my_chart.addSeries({ data: chart_data, name: name, color: color }); }); setTimeout(addSeries, 1000); } cache: false; // converts date format from JSON function getChartDate(d) { // offset in minutes is converted to milliseconds and subtracted so that chart's x-axis is correct return Date.parse(d) - (my_offset * 60000); } I have tried following the livedata example but seem to be failing miserably. The sensors update about every 60 seconds (only during the day - as there is no UV at night, I put the sensors into "sleep" mode to save battery power)
Javascript,Highcharts : Avoiding parallel function calls
I am developing a dashboard, where there are 5 buttons, and on click of each button, corresponding chart is displayed in the same div. The structure of my code is as follows : $(document).ready(function(){ $("button").click(function(){ function requestData() { $.ajax({ url : ...., success : function(){ ..... //Real Time Plotting of Data chart.series[0].addPoint(eval(point), true, shift); setTimeout(requestData, 2000); } }); } chart = new Highcharts.Chart({ chart: { renderTo: 'chart', defaultSeriesType: 'spline' }, .... .... }); }); }); The Problem : On every click of the button, a parallel requestData() starts, multiple parallel threads run at the same time. This leads in random addPoint and increase in memory consumed. Also, when checked with Highcharts.Chart in the console, after every click, a undefined objects adds up. How do I restructure the code for optimum performance ?
Refer the code below, your high chart implement can be like this $(function () { $(document).ready(function () { $('#container').highcharts({ chart: { type: 'spline', animation: Highcharts.svg, // don't animate in old IE marginRight: 10, events: { load: function () { // set up the updating of the chart each second var series = this.series[0]; setInterval(function () { var x = (new Date()).getTime(), // current time y = Math.random(); series.addPoint([x, y], true, true); }, 1000); } } }, title: { text: 'Live random data' }, xAxis: { type: 'datetime', tickPixelInterval: 150 }, yAxis: { title: { text: 'Value' }, plotLines: [{ value: 0, width: 1, color: '#808080' }] }, tooltip: { formatter: function () { return '<b>' + this.series.name + '</b><br/>' + Highcharts.dateFormat('%Y-%m-%d %H:%M:%S', this.x) + '<br/>' + Highcharts.numberFormat(this.y, 2); } }, legend: { enabled: false }, exporting: { enabled: false }, series: [{ name: 'Random data', data: (function () { // generate an array of random data var data = [], time = (new Date()).getTime(), i; for (i = -19; i <= 0; i += 1) { data.push({ x: time + i * 1000, y: Math.random() }); } return data; }()) }] }); }); }); Basically, you do not need to make your arrangement to live plotting, high chart has this option UPDATE Refer live fiddle http://jsfiddle.net/anilk/3u0ng35s/ Replace below data option to your ajax call data: (function () { // generate an array of random data var data = [], time = (new Date()).getTime(), i; for (i = -19; i <= 0; i += 1) { data.push({ x: time + i * 1000, y: Math.random() }); } return data; }
Adding series dynamically in highcharts
(function($){ $(function () { $(document).ready(function() { Highcharts.setOptions({ global: { useUTC: false } }); var i=0; var chart = new Highcharts.Chart({ chart: { type: 'spline', renderTo: 'container', animation: Highcharts.svg, // don't animate in old IE marginRight: 10, events: { load: function() { // set up the updating of the chart each second var series = this.series[0]; setInterval(function() { var Name = new Array(); Name[0] = "Random data"; Name[1] = "Volvo"; var length=chart.series.length; var flag=0; var index=0; var x = (new Date()).getTime(), // current time y = Math.random(); for (var k=0;k<Name.length;k++) { for(var j=0;j<chart.series.length;j++) { if(chart.series[j].name==Name[k]) { flag=1; index=j; x = (new Date()).getTime(); y = Math.random(); break; } } if(flag==1) { chart.series[index].addPoint([x, y], true, true); flag=0; } else { chart.addSeries({name: '' + Name[k] + '', data: [] }); chart.series[length].addPoint([x, y+1], true); length=length+1; } } }, 1000); } } }, title: { text: 'Live random data' }, xAxis: { type: 'datetime', tickPixelInterval: 150 }, yAxis: { title: { text: 'Value' }, plotLines: [{ value: 0, width: 1, color: '#808080' }] }, tooltip: { formatter: function() { return '<b>'+ this.series.name +'</b><br/>'+ Highcharts.dateFormat('%Y-%m-%d %H:%M:%S', this.x) +'<br/>'+ Highcharts.numberFormat(this.y, 2); } }, legend: { enabled: false }, exporting: { enabled: false }, series: [{ name: 'Random data', data: (function() { // generate an array of random data var data = [], time = (new Date()).getTime(), i; for (i = -19; i <= 0; i++) { data.push({ x: time + i * 1000, y: Math.random() }); } return data; })() }] }); }); }); })(jQuery); I am able to add series and add point in charts but the series that I add after initialization, which is "volvo", is not drawing lines between its points. What might be the problem? And is there any other way of comparing arrays and adding points without a for-loop? Because I can get millions of series at times and I don't want to be looping over arrays to check if it exists or not. So is there any efficient way of finding wheteher a list already exists, and if it does what is its index? here is its fiddle: www.jsfiddle.net/2jYLz/
It is related with fact that you have enabled shifting in addPoint() when you add new serie. In other words, shifting remove first point and add new in the end of serie. So when you have one point it caused your scenario. So you need to disable shipfing, and when lenght of series.data achieve i.e 10 points, shifting should be enabled.