I'm trying to put a second line on a Highchart using ajax with dynamic (periodic) updating. First line works fine using ajax. I think the problem is the format of the incoming data, but also how I'm splitting the data for each of the lines.
I have control of the format of the ajax data so it be could posted in most any form but it works for the first line.
Here is the ajax data that is received with each request:
[Date.parse("2013/02/14 14:29:00 -0000"), 51, 216510]
This will create a point for the first line at 51 but not anything for the second line which should be at 216510.
The following is the javascript I'm using:
var chart; // global
/**
* Request data from the server, add it to the graph and set a timeout to request again
*/
function requestData() {
$.ajax({
url: '/htbin/count_since_total',
success: function(point) {
var series = chart.series[0],
shift = series.data.length > 30; // shift if the series is longer than 300 (drop oldest point)
// add the point
chart.series[0].addPoint(eval(point), true, shift);
// call it again after one second
setTimeout(requestData, 1000);
},
cache: false
});
}
$(document).ready(function() {
chart = new Highcharts.Chart({
chart: {
renderTo: 'container',
events: {
load: requestData
}
},
title: {
text: 'time'
},
xAxis: {
type: 'datetime'
},
yAxis: [{ // Primary yAxis
title: {text: 'count'},
opposite: false
}, { // Secondary yAxis
title: {text: 'Total'},
opposite: true
}],
series: [{
yAxis: 0,
name: 'number',
data: []
},{
yAxis: 1,
name: 'Total',
data: []
}],
});
});
EDIT 1:
Not working, no second line with suggested changes. I'm thinking that incoming data format is incorrect or it needs processing after receiving. Also how does the data get assigned to the proper line?
series: [{
yAxis: 0,
name: 'number',
data: []
},{
yAxis: 1,
name: 'Total',
data: []
}],
--------- end EDIT 1 ----------------
In your success-action/function, you are only updating series[0].
If you want to add a point to the second line, you'll have to add a point to series[1] as well.
Add a for loop to do the same for each serie.
success: function(point) {
for(var i = 0, length = chart.series.length; i < length; i++) {
var serie = chart.series[i],
// shift if the series is longer than 300 (drop oldest point)
var shift = serie.data.length > 30;
// add the point
serie.addPoint(eval(point), true, shift);
}
// call it again after one second
setTimeout(requestData, 1000);
}
Related
OK. Rephrasing an unanswered earlier question.
I have a chart as shown below with the data loading dynamically through ajax.
A simplified version (without ajax) of this graph works fine as shown in this jsfiddle.
The code below is the chart as I use it, straight from its context.
The idea is there are several timeseries (x-axis) in the graph and I wish to display the wind characteristics of speed and direction at those same times through windbarbs. The windbarbs take two arguments at each defined x-value all other series take one argument (the y-axis).
My question is: why doesn't it work (why the stackdump) while it does work in jsfiddle (without the ajax call and thus without the addSeries call?
As a derivative: in the jsfiddle the onSeries attribute does not work. What is wrong there?
And finally: is it possible to get the windbarbs above the series graphs and not fixed on the x-axis?
The problem seems to be in the loading of the wind data after the loading of the other series. As that part is in the ajax call with the (idx == 'wind') condition it is quite easy to spot. It breaks in the setData call of highStock with the following stackdump:
Uncaught TypeError: r is undefined
setData highstock.src.js:33902
init highstock.src.js:33182
init highstock.src.js:54816
init windbarb.src.js:361
initSeries highstock.src.js:27886
addSeries highstock.src.js:36888
fireEvent highstock.src.js:2112
addSeries highstock.src.js:36887
success line 2 > injectedScript:176
success line 2 > injectedScript:171
jQuery 6
doSensorIn2p5 line 2 > injectedScript:162
SetGraphView0 line 2 > injectedScript:276
onclick (index):1
Without the wind data it works fine.
var doSensorIn10 = function(){
let ReferenceColours = ['#79bc6a', '#bbcf4c', '#eec20b', '#f29305', '#960018' ];
let ReferenceConcentrations10 = [0,25,50,90,180];
let t ={chart: { renderTo: 'chartcontainerIn10',type: 'spline',alignTicks: false, zoomType: 'xy', pinchType: 'xy'},
title: { text: 'AirQuality Sensor In for PM10'},
credits: { enabled: true},
xAxis: { type: 'datetime', ordinal: false, dateTimeLabelFormats: { day: '%e %b',week: '%e %b %y',month: '%b %y',year: '%Y'} },
yAxis:
[{
title: { text: 'Luchtkwaliteit (μg/m3)'},
opposite: false, labels: { align: 'right',x: -5},
},
{
linkedTo: 0,
gridLineWidth: 0,
opposite: true,
title: { text: null},
labels: { align: 'left',x: 5},
tickInterval: 20
}],
legend: { enabled: true},
tooltip: { valueSuffix: 'μg/m3',valueDecimals: 1,xDateFormat: '%A, %b %e, %H:%M'},
series:[],
rangeSelector:
{
buttons:[{ count: 6,type: 'hour',text: '6h'},
{ count: 12,type: 'hour',text: '12h'},
{ type: 'all',text: 'All'}],
inputEnabled: false
}
};
let chart = new Highcharts.StockChart(t);
chart.showLoading();
$.ajax({
url: 'airlinkdataIn10.json',
cache: false,
dataType: 'json',
success: function(resp){
let titles = {
'In_pm10': 'In_pm10','In_pm10_1hr': 'In_pm10_1hr','In_pm10_3hr': 'In_pm10_3hr','In_pm10_24hr': 'In_pm10_24hr','In_pm10_nowcast': 'In_pm10_nowcast','wind': 'wind'}
let idxs = ['In_pm10','In_pm10_1hr','In_pm10_3hr','In_pm10_24hr','In_pm10_nowcast','wind']
let cnt = 0;
idxs.forEach(function(idx) {
console.log('idx = ' + idx);
if (idx in resp) {
if (idx == 'wind') {
console.log('Doing Wind correctly : Before addSeries');
chart.addSeries({name: titles[idx], type: 'windbarb', showInLegend: false, onSeries: 'InPM2p5', tooltip: {valueSuffix: ' m/s'}, data: resp[idx] }, false);
console.log('Doing Wind correctly : After addSeries');
}
else {
console.log('Doing ' + idx + ' correctly');
chart.addSeries({name: titles[idx], data: resp[idx]}, false);
}
chart.series[cnt].options.zIndex = cnt+50;
}
cnt++;
});
chart.hideLoading();
chart.redraw();
}
}
)};
The data series (in short version) is as follows:
{"In_pm2p5":[[1609484460000,26.20], ... ]],
"In_pm2p5_1hr":[[1609484460000,32.90], ... ]],
...
"wind":[[1609484460000,0.0,194], ...]]}
Each parameter has 2880 values, wind may have one value less (which I tested in jsfiddle and does not seem to be a problem).
Thank you for your wide and clear description!
In this case it is hard to say why the chart is not rendering properly without reproduction of your data fetching. The addSeries feature should work without any issues with the windbarb type series.
Demo: https://jsfiddle.net/BlackLabel/ktLyunw8/
2.
in the jsfiddle the onSeries attribute does not work. What is wrong there?
I cannot see it, everything seems to work fine:
3.
And finally: is it possible to get the windbarbs above the series graphs and not fixed on the x-axis?
Like in the case of using the onSeries feature? Or render it totally above the plot area?
I'd like to render it above the plot area or in the top of the plot area (like without onSeries it is at the bottom) so all windbarbs in one line.
In this case, you can render the second xAxis and assign the winbarb series to it.
API: https://api.highcharts.com/highcharts/xAxis.opposite
Demo: https://jsfiddle.net/BlackLabel/ud0kyrgh/
i Have created a Highchart using the Following Highchart's Demo:
https://www.highcharts.com/demo/dynamic-update
Now What I did I created my Own function to add dynamic values to the Chart.
I created a function to get the dynamic data from a particular php file whose data changes on every page load event.
I am getting the data values in the getData function console.log
Here is the Script That I am using.
<script type="text/javascript">
$(document).ready(function(){
function getData(){
$.ajax({
type: 'GET',
url: 'data.php',
success: function(data){
// var number = document.write(data) ;
console.log(data);
return data ;
}
}) ;
}
Highcharts.chart('chart', {
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 = getData();
console.log(y);
series.addPoint([x, y], true, true);
}, 1000);
}
}
},
time: {
useUTC: false
},
title: {
text: 'Live random data'
},
xAxis: {
type: 'datetime',
tickPixelInterval: 150
},
yAxis: {
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: [{
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: getData()
});
}
return data;
}())
}]
});
});
</script>
Now as you can see that I have created a getData function and getting the data value in return.
On console log under the getData function, I am getting integer Value in return every one second.
the problem is that under the Highchart's function, I am not able to get the data values using getData function, it's returning undefined in the console .
Highchart's is running but it does not show any data points. it is moving but without showing any data points.
Please correct me in the area , where I am doing wrong.
Any help is appreciated. Thanks
ajax calls are run asynchronously so you cant really return data from it.
instead you should render chart inside the ajax success function.
A good example is here already.
https://www.highcharts.com/docs/working-with-data/live-data
Basically
1. point on load to call a function getData
2. in Getdata call ajax function.
3. in success of ajax render chart with new data.
document.addEventListener('DOMContentLoaded', function() {
chart = Highcharts.chart('container', {
chart: {
type: 'spline',
events: {
load: requestData
}
},
title: {
text: 'Live random data'
},
xAxis: {
type: 'datetime',
tickPixelInterval: 150,
maxZoom: 20 * 1000
},
yAxis: {
minPadding: 0.2,
maxPadding: 0.2,
title: {
text: 'Value',
margin: 80
}
},
series: [{
name: 'Random data',
data: []
}]
});
});
/**
* Request data from the server, add it to the graph and set a timeout
* to request again
*/
function requestData() {
$.ajax({
url: 'live-server-data.php',
success: function(point) {
var series = chart.series[0],
shift = series.data.length > 20; // shift if the series is
// longer than 20
// add the point
chart.series[0].addPoint(point, true, shift);
// call it again after one second - add this if you want to auto refresh
// setTimeout(requestData, 1000);
},
cache: false
});
}
I've a question about how to create a dynamic chart using json, I tried and my graph didn't show a result, when I checked out, I've no error with my code. This is my code :
<script>
var chart; // global
function requestData() {
$.ajax({
url: 'api_heartrate.php',
success: function(point) {
var series = chart.series[0],
shift = series.data.length > 20; // shift if the series is longer than 20
// add the point
chart.series[0].addPoint(eval(point), true, shift);
// call it again after one second
setTimeout(requestData, 1000);
},
cache: false
});
}
$(document).ready(function() {
chart = new Highcharts.Chart({
chart: {
renderTo: 'container',
defaultSeriesType: 'spline',
events: {
load: requestData
}
},
title: {
text: 'Live random data'
},
xAxis: {
type: 'datetime',
tickPixelInterval: 150,
maxZoom: 20 * 1000
},
yAxis: {
minPadding: 0.2,
maxPadding: 0.2,
title: {
text: 'Value',
margin: 80
}
},
series: [{
name: 'Random data',
data: []
}]
});
});
</script>
</head>
<body>
<div id="container" style="width: 800px; height: 400px; margin: 0 auto"></div>`
this is my json :
http://health.barrukurniawan.tech/api_heartrate.php
[{"time":"2018-08-02 09:30:11","nilai_sensor":"78"}]
I tried following a tutorial from this link :
Highcharts Dynamic Chart with MySQL Data doesn't reload
Thanks for your attention, gladly waiting for an answer :)
There are multiple small errors in your approach
eval is bad, parse it using JSON.parse instead.
During load, chart is not defined yet, so your callback will not work.
Highcharts needs time in milliseconds since 1970.
highcharts expects an object {x: , y: ,...} you give it {time: , nilai_sensor: }.
Solutions:
point = JSON.parse(point)
events: {
load: function() {
setInterval(function() {
requestData(chart)
}, 1000);
}
}
new Date(point[0].time).getTime()
{x: new Date(point[0].time).getTime(), y: point[0].nilai_sensor}
Here is a working example using your input with static data(and some added time to keep it moving): https://jsfiddle.net/ewolden/md975oLk/23/
I am loading Highcharts like this.
var options = {
credits: {
enabled: false
},
chart: {
renderTo: 'chart_box',
type: 'areaspline'
},
title: {
text: ''
},
xAxis: {
crosshairs: true,
labels: {
step: 5,
rotation: -45
}
},
series: []
};
Then I have a function which is called when graph needs to be loaded. Upon calling the function, data is fetched through AJAX and assigned to series and date lie this:
$.ajax({
url: 'url/charts',
type: 'post',
data: data
}).done(function(data) {
var dateCount = data.dates.length;
var stepCount = 1;
if (dateCount > 10) {
stepCount = 5;
}
options.xAxis.categories = data.dates;
$.each(data.series, function(name, elem) {
options.series.push({
name: name.replace('_', ' ').toUpperCase().trim(),
data: elem
})
});
chart = new Highcharts.Chart(options);
});
The issue here is that even though I have given step as 5 , it is showing dates with 15 dates interval. I mean in xAxis labels. It seems like it will be multiplied by three always. If I give 2, it will show 6 days interval in labels. Everything working fine in a chart which is not using AJAX to load data.
I'm attempting to combine a couple of different chart demos from Highcharts.
My examples are: Data classes and popup and Small US with data labels
I want the map from the first with the popup feature of the second. I need to connect the map to my own google spreadsheet but for now I'm just trying to get the data from the first example to work.
This is what I have so far but can't seem to get any data in the map. I thought I had a joinBy problem, and I may still, but when I set joinBy to null I thought "the map items are joined by their position in the array", yet nothing happened.
https://jsfiddle.net/9eq6mydv/
$(function () {
// Load the data from a Google Spreadsheet
// https://docs.google.com/a/highsoft.com/spreadsheet/pub?hl=en_GB&hl=en_GB&key=0AoIaUO7wH1HwdFJHaFI4eUJDYlVna3k5TlpuXzZubHc&output=html
Highcharts.data({
googleSpreadsheetKey: '0AoIaUO7wH1HwdDFXSlpjN2J4aGg5MkVHWVhsYmtyVWc',
googleSpreadsheetWorksheet: 1,
// custom handler for columns
parsed: function (columns) {
// Make the columns easier to read
var keys = columns[0],
names = columns[1],
percent = columns[10],
// Initiate the chart
options = {
chart : {
renderTo: 'container',
type: 'map',
borderWidth : 1
},
title : {
text : 'US presidential election 2008 result'
},
subtitle: {
text: 'Source: <a href="http://en.wikipedia.org/wiki/United_States_presidential_election,' +
'_2008#Election_results">Wikipedia</a>'
},
mapNavigation: {
enabled: true,
enableButtons: false
},
legend: {
align: 'right',
verticalAlign: 'top',
x: -100,
y: 70,
floating: true,
layout: 'vertical',
valueDecimals: 0,
backgroundColor: (Highcharts.theme && Highcharts.theme.legendBackgroundColor) || 'rgba(255, 255, 255, 0.85)'
},
colorAxis: {
dataClasses: [{
from: -100,
to: 0,
color: '#C40401',
name: 'McCain'
}, {
from: 0,
to: 100,
color: '#0200D0',
name: 'Obama'
}]
},
series : [{
data : data,
dataLabels: {
enabled: true,
color: '#FFFFFF',
format: '{point.code}',
style: {
textTransform: 'uppercase'
}
},
mapData: Highcharts.geojson(Highcharts.maps['countries/us/custom/us-small']),
joinBy: keys,
name: 'Democrats margin',
point: {
events: {
click: pointClick
}
},
tooltip: {
ySuffix: ' %'
},
cursor: 'pointer'
}, {
type: 'mapline',
data: Highcharts.geojson(Highcharts.maps['countries/us/custom/us-small'], 'mapline'),
color: 'silver'
}]
};
/**
* Event handler for clicking points. Use jQuery UI to pop up
* a pie chart showing the details for each state.
*/
function pointClick() {
var row = this.options.row,
$div = $('<div></div>')
.dialog({
title: this.name,
width: 400,
height: 300
});
window.chart = new Highcharts.Chart({
chart: {
renderTo: $div[0],
type: 'pie',
width: 370,
height: 240
},
title: {
text: null
},
series: [{
name: 'Votes',
data: [{
name: 'Obama',
color: '#0200D0',
y: parseInt(columns[3][row], 10)
}, {
name: 'McCain',
color: '#C40401',
y: parseInt(columns[4][row], 10)
}],
dataLabels: {
format: '<b>{point.name}</b> {point.percentage:.1f}%'
}
}]
});
}
// Read the columns into the data array
var data = [];
$.each(keys, function (i, key) {
data.push({
key: key,//.toUpperCase(),
value: parseFloat(percent[i]),
name: names,
row: i
});
});
// Initiate the chart
window.chart = new Highcharts.Map(options);
},
error: function () {
$('#container').html('<div class="loading">' +
'<i class="icon-frown icon-large"></i> ' +
'Error loading data from Google Spreadsheets' +
'</div>');
}
});
});
UPDATE:
I wanted to share with everyone my final solution. Although Ondkloss did a magnificent job answering my question the popup feature still didn't work and this is because I forgot to include the jQuery for the .dialog call. Once I included that I had an empty popup with a highchart error 17, this is because the highmaps.js code doesn't include the pie chart class. So I had to add the highcharts.js code and include map.js module afterward. You can see my final jsfiddle here.
Thanks again to Ondkloss for the excellent answer!
The problem here mostly comes down to the use of joinBy. Also to correct it there are some required changes to your data and mapData.
Currently your joinBy is an array of strings, like ["al", "ak", ...]. This is quite simply not an accepted format of the joinBy option. You can read up on the details in the API documentation, but the simplest approach is to have a attribute in common in data and mapData and then supply a string in joinBy which then joins those two arrays by that attribute. For example:
series : [{
data : data,
mapData: mapData,
joinBy: "hc-key",
]
Here the "hc-key" attribute must exist in both data and mapData.
Here's how I'd create the data variable in your code:
var data = [];
$.each(keys, function (i, key) {
if(i != 0)
data.push({
"hc-key": "us-"+key,
code: key.toUpperCase(),
value: parseFloat(percent[i]),
name: names[i],
row: i
});
});
This skips the first key, which is just "Key" (the title of the column). Here we make the "hc-key" fit the format of the "hc-key" in our map data. An example would be "us-al". The rest is just metadata that will be joined in. Note that you were referencing your data in the options prior to filling it with data, so this has to be moved prior to this.
This is how I'd create the mapData variable in your code:
var mapData = Highcharts.geojson(Highcharts.maps['countries/us/custom/us-small']);
// Process mapdata
$.each(mapData, function () {
var path = this.path,
copy = { path: path };
// This point has a square legend to the right
if (path[1] === 9727) {
// Identify the box
Highcharts.seriesTypes.map.prototype.getBox.call(0, [copy]);
// Place the center of the data label in the center of the point legend box
this.middleX = ((path[1] + path[4]) / 2 - copy._minX) / (copy._maxX - copy._minX);
this.middleY = ((path[2] + path[7]) / 2 - copy._minY) / (copy._maxY - copy._minY);
}
// Tag it for joining
this.ucName = this.name.toUpperCase();
});
The first part is your "standard map data". The rest is to correctly center the labels for the popout states, and gotten directly from the example.
And voila, see this JSFiddle demonstration to witness your map in action.
I suggest doing some console.log-ing to see how data and mapData have the hc-key in common and that leads to the joining of the data in the series.