Highcharts does not render values received from Flask-socketio - javascript

var myChart = Highcharts.chart('containerX', {
chart: {
type: 'spline',
animation: Highcharts.svg, // don't animate in old IE
marginRight: 10,
events: {
load: function () {
var socket = io.connect('http://' + document.domain + ':' + location.port + '/test');
var series = this.series[0]
var numbers_received = []
socket.on('newnumber', function(msg) {
var x = (new Date()).getTime() // current time
var y = msg.number
series.addPoint([x, y], true, true);
});
......
The x-axis of Highchart is moving on my HTML page based on the (new Date()).getTime(), and the y-axis is scaling based on the value received from flask-socketio, but nothing is displayed on the chart.
ps: The value received is changing every seconds.
Also I can see the value msg.number showing on the terminal.
Can anyone help? I am new to highcharts and flask-socketio. Thanks.

You need to pass y values to the addPoint method as numbers not as strings.
var y = parseFloat(msg.number);
Live demo: http://jsfiddle.net/BlackLabel/6m4e8x0y/4908/

Related

How to include data from point in Highcharts xAxis label?

I am trying to include some extra data from my points in the xAxis label of a Highcharts line chart.
I'm creating my points like this. xAxis is 'datetime' type, and I'm using custom tickPositions (this part works fine).
for (row of results) {
var point = {x:Date.parse(row.time), y:row.value, magicNumber:row.ID};
data_series.data.push(point);
chartConfig.xAxis.tickPositions.push(Date.parse(row.time));
}
In the tooltip I'm able to do the following (this works):
tooltip: {
formatter: function() {
return 'ID: ' + this.point.magicNumber + ' Value:' + this.point.y.toFixed(3);
},
},
How do I do the equivalent in the xAxis formatter? Not clear from the docs if this is possible.
xAxis: {
type: 'datetime',
labels: {
rotation: 90,
formatter: function () {
var ID = **What goes here to obtain magicNumber?**
var datetime = new Date(this.value);
return ID.toString() + ' ' + datetime.toISOString();
}
},
tickPositions: []
}
Try this, console.log(this); should give you a hint if my solution doesn't works.
xAxis: {
type: 'datetime',
labels: {
rotation: 90,
formatter: function () {
console.log(this);
var ID = this.chart.series[0].data[this.pos].magicNumber;
var datetime = new Date(this.value);
return ID.toString() + ' ' + datetime.toISOString();
}
},
tickPositions: []
}
Both formatters has different context, so one way out of that is by iterate on every point of your data (inside of the xAxis.labels.formatter function), find the point which has the same x as this.value, and assign it to some variable. It should be enough, but there is a likelihood of a returning empty object, when none of points won't fit to the tick value.
xAxis: {
type: 'datetime',
labels: {
formatter() {
var points = this.chart.userOptions.series[0].data
var sameTimestampPoint = points.filter(p => p.x === this.value)[0]
var magicValue = sameTimestampPoint ? sameTimestampPoint.magicValue : ""
return magicValue + '<br/>' + this.value
}
}
}
Live example: https://jsfiddle.net/2r8z5wny/

Highchart timeseries group data daily/weekly tooltip formatter

I'm using Highstocks with data grouping turned on by default.
When handling the "point -> events -> mouseOver" event,
I want to found the exact x-y location of the hovered point.
What I am doing right now is:
// When data is grouped as daily/weekly
tooltip: {
useHTML: true,
hideDelay: 0,
snap: 0,
backgroundColor: 'rgba(255,255,255,1)',
formatter: function() {
let item;
// When zoomed out and data is grouped or graph grouped by day or week
if(this.series.hasGroupedData) {
// let xPos = this.series.xData[Math.round(this.point.clientX)];
// let yPos = this.series.yData[Math.round(this.point.plotY)];
// find the timestamp in the middle of the timestamp grouping
let maxClientX = Math.round(this.series.groupedData[this.series.groupedData.length - 1].clientX);
let point_position_percent = this.point.clientX / maxClientX;
let point_position_index = Math.round(point_position_percent * this.series.xData.length);
let xPos = this.series.xData[point_position_index];
item = _.find(this.series.options.data, {x:xPos});
}
// When zoomed in
else {
item = this.point;
}
let tooltipFormat = this.key+'<table><tr><td><span style="color:{series.color}">\u25CF</span></td><td>'+this.series.name+': </td>' + '<td><b>'+this.y+'</b></td></tr>' +
'<tr><td></td><td>Data 1: </td>' + '<td><b>'+item.data1+'</b></td></tr>' +
'<tr><td></td><td>Data 2: </td>' + '<td><b>'+item.data2+'</b></td></tr>' +
'<tr><td></td><td>Data 3: </td>' + '<td><b>'+item.data3+'</b></td></tr></table>';
return tooltipFormat;
}
}
It works by simply finding the 'percentage' of the point on the X axis, then converting this percentage to the equivalent index in the data set.

Javascript/jQuery plug-in Highcharts does not recognize my data array. Is it my syntax?

I have successfully called an ajax request to my rails db and grabbed the return data, putting each returned object's "level" and "date" values into an array within an array, or a coordinate pair in an array with my search() function (below). Next, that function calls drawChart(object_data) which should set the array within an array data to the series. Only problem is it's not working and I do not know why. The object_data array that I am putting in the chart falls in line with examples on the Highcharts website, but it does not seem to be working for some reason.
Search function:
function search() {
var full_date_from = $( "#from" ).datepicker("getDate");
var full_date_to = $( "#to" ).datepicker("getDate");
var date_from = full_date_from.getMonth()+1 + "-" + full_date_from.getDate() + "-" + full_date_from.getFullYear();
var date_to = full_date_to.getMonth()+1 + "-" + full_date_to.getDate() + "-" + full_date_to.getFullYear();
$.ajax({
url:"<%= url_for :controller => 'objects', :action => 'search' %>",
data:'date_from=' + encodeURIComponent(date_from) + '&date_to=' + encodeURIComponent(date_to) + '&river_id=' + <%= #id %>,
dataType: "json",
cache:false,
success:function (data) {
var levels = [];
var dates = [];
var object_data = [];
for (var i = 0; i < data.length; i++) {
var dt1 = parseInt(data[i].date.substring(8,10));
var mon1 = parseInt(data[i].date.substring(5,7));
var yr1 = parseInt(data[i].date.substring(0,4));
var date1 = Date.UTC(yr1, mon1-1, dt1);
levels[i] = data[i].level;
dates[i] = date1;
var temp = [dates[i], levels[i]];
object_data.push(temp);
}
drawChart(object_data);
}
});
}
drawChart function:
function drawChart(object_data)
{
var chart = $('#container').highcharts();
chart.series[0].setData(object_data);
}
chart initialization:
$(function () {
$('#container').highcharts({
xAxis: {
type: 'datetime'
},
series: [{
data: []
}]
});
});
The way you have it written, drawChart() is re-initializing Highcharts on #container every time you call it, effectively resetting any options you would have set.
You should only have to call $('#container').highcharts({...}); once.
Toss your empty initialization and change drawChart() to
function drawChart(object_data) {
$('#container').highcharts({
xAxis: { type: 'datetime' },
series: [{ data: object_data }]
});
}

JavaScript Variable and passing data issues

I have an issue in that the $.getJSON segment of code works fine and produces a variable called 'zippy'. I need to access 'zippy' under 'series: data' further down in the code.
I have tried a number of things unfortunately I can't make it work. The easiest would be way to 'return data' $.getJSON(jsonUrl,function(zippy) out of the function(zippy) call but I'm lost as to how to make that data available.
$(function() {
$(document).ready(function() {
Highcharts.setOptions({
global: {
useUTC: false
}
});
console.log("+++++++++++++++++++++++++++++++++++++");
var jsonUrl = "http://www.someurl.com/thing.php?callback=?";
$.getJSON(jsonUrl, function(zippy) {
for(i = 0; i < zippy.cpmdata.length; i++) {
console.log("TIMESTAMP: " + zippy.cpmdata[i].timestamp + " AFTER: ");
zippy.cpmdata[i].timestamp = Date.parse(zippy.cpmdata[i].timestamp).getTime() / 1000;
//var unixtime Date.parse(temptime).getTime()/1000
console.log(" TESST " + zippy.cpmdata[i].timestamp + " \r\n");
}
});
console.log("+++++++++++++++++++++++++++++++++++++");
var chart;
chart = new Highcharts.Chart({
chart: {
renderTo: 'container',
type: 'spline',
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);
}
}
},
series: [{
name: 'Random data',
data: (function() {
// generate an array of random data
var data = [],
time = (new Date()).getTime(),
i;
console.log("++NEED ACCESS HERE FOR ZIPPY++");
console.log(" =============== \r\n");
console.log(" FINAL " + zippy.cpmdata[5].timestamp + " \r\n");
return data;
})()
}]
}
Your problem is that getJSON is asynchronous. What's happening in your code is this:
document.ready is triggered
getJSON is called and registers a callback "function(zippy)"
note that getJSON returns immediately without executing the callback
You try to draw a chart using HighCharts
... several hundred milliseconds later
The browser makes the JSON request
... several hundred milliseconds later
The JSON request returns with data and triggers the
callback to "function(zippy)"
"function(zippy)" is executed
So you see. The problem is not how "function(zippy)" is executed but when it is executed. As such, you cannot execute code that wants to use the return value of the JSON request outside of the callback function. (Actually you can but we'll ignore polling with setTimeout or using synchronous ajax for now)
The solution is to move all the code that you want to run later on inside the callback function:
$.getJSON(jsonUrl, function(zippy) {
for(i = 0; i < zippy.cpmdata.length; i++) {
console.log("TIMESTAMP: " + zippy.cpmdata[i].timestamp + " AFTER: ");
zippy.cpmdata[i].timestamp = Date.parse(zippy.cpmdata[i].timestamp).getTime() / 1000;
//var unixtime Date.parse(temptime).getTime()/1000
console.log(" TESST " + zippy.cpmdata[i].timestamp + " \r\n");
}
var chart;
chart = new Highcharts.Chart({
chart: {
renderTo: 'container',
type: 'spline',
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);
}
}
},
series: [{
name: 'Random data',
data: (function() {
// generate an array of random data
var data = [],
time = (new Date()).getTime(),
i;
console.log(" FINAL " + zippy.cpmdata[5].timestamp + " \r\n");
return data;
})()
}]
});
You need to put all of the new Highcharts.Chart({...}) stuff inside the getJSON callback, because you need to wait until the json request completes before creating the chart. See my code comment that says CUT AND PASTE chart = new Highcharts.Chart({...}) STUFF HERE!!!.
$(document).ready(function() {
Highcharts.setOptions({
global: {
useUTC: false
}
});
console.log("+++++++++++++++++++++++++++++++++++++");
var jsonUrl = "http://www.someurl.com/thing.php?callback=?";
$.getJSON(jsonUrl, function(zippy) {
for(i = 0; i < zippy.cpmdata.length; i++) {
console.log("TIMESTAMP: " + zippy.cpmdata[i].timestamp + " AFTER: ");
zippy.cpmdata[i].timestamp = Date.parse(zippy.cpmdata[i].timestamp).getTime() / 1000;
//var unixtime Date.parse(temptime).getTime()/1000
console.log(" TESST " + zippy.cpmdata[i].timestamp + " \r\n");
// CUT AND PASTE chart = new Highcharts.Chart({...}) STUFF HERE!!!
}
});
console.log("+++++++++++++++++++++++++++++++++++++");
});

jqPlot X-axis rendering

I've combined a jquery datepicker widget with jqplot, and am using the values of the datepicker to recreate the plot when something changes.
However, during large date ranges, the labels on the x-axis run together to look very ugly.
Here's it regularly:
Here's it with a large range:
My question is -- is it possible to change the rendering of the axis, so if there is a large range that not every single date shows? Maybe 2 or 3, but always including the beginning date and end date.
Here's my code -- please let me know if you can help!
$(document).ready(function(){
var ajaxDataRenderer = function(url, plot, options) {
var ret = null;
$.ajax({
async: false,
url: url,
type: "GET",
dataType:"json",
data: {metricName: ""},
success: function(data) {
ret = data;
},
error:function (xhr, ajaxOptions, thrownError){
alert(xhr.responseText);
}
});
return ret;
};
//var jsonurl = "reports/reportData.json";
var jsonurl = "tenant/metrics/get.json";
var currentTime = new Date()
var month = currentTime.getMonth() + 1;
var day = currentTime.getDate();
var year = currentTime.getFullYear();
var today = month + "-" + day + "-" + year;
var currentDatePlus = new Date(new Date().getTime() + 24 * 60 * 60 * 1000);
var dayPlus = currentDatePlus.getDate()
var monthPlus = currentDatePlus.getMonth() + 1
var yearPlus = currentDatePlus.getFullYear()
var tomorrow = monthPlus + "/" + dayPlus + "/" + yearPlus;
function getLastWeek(){
var today = new Date();
var lastWeek = new Date(today.getFullYear(), today.getMonth(), today.getDate() - 7);
return lastWeek ;
}
var lastWeek = getLastWeek();
var lastWeekMonth = lastWeek.getMonth() + 1;
var lastWeekDay = lastWeek.getDate();
var lastWeekYear = lastWeek.getFullYear();
var lastWeekDisplay = lastWeekMonth + "/" + lastWeekDay + "/" + lastWeekYear;
var datepickerBegin = $("#datepicker_start").val();
var datepickerEnd = $("#datepicker_to").val();
$('#applyBtn').click( function() {
// Check to make sure the datepicker isn't empty
if ($("#datepicker_start").val() !== "" && $("#datepicker_to").val() !== "") {
var datepickerBegin = $("#datepicker_start").val();
var datepickerEnd = $("#datepicker_to").val();
//Recreate the plot
var plot2 = $.jqplot('chart2', jsonurl,{
title: "",
dataRenderer: ajaxDataRenderer,
dataRendererOptions: {unusedOptionalUrl: jsonurl},
axes: {
xaxis: {
//'numberTicks' : 7,
min: datepickerBegin,
max: datepickerEnd,
renderer:$.jqplot.DateAxisRenderer,
rendererOptions:{tickRenderer:$.jqplot.CanvasAxisTickRenderer},
tickInterval: '1 day',
tickOptions:{formatString:'%#m/%#d/%Y'
}
//rendererOptions: {sdaTickInterval: [1, 'month']}
},
yaxis: {
label: "MB",
tickOptions:{formatString:'%d '},
// Comment the next line out to allow negative values (and therefore rounded ones)
min: 0
}
},
highlighter: {
show: true,
sizeAdjust: 7.5
}
});
//redraw the plot now.
//plot2.reInitialize({});
plot2.replot({});
}
})
// The default plot
var plot2 = $.jqplot('chart2', jsonurl,{
title: "",
dataRenderer: ajaxDataRenderer,
dataRendererOptions: {unusedOptionalUrl: jsonurl},
axes: {
xaxis: {
//'numberTicks' : 7,
min: lastWeekDisplay,
max: tomorrow,
renderer:$.jqplot.DateAxisRenderer,
rendererOptions:{tickRenderer:$.jqplot.CanvasAxisTickRenderer},
tickInterval: '1 day',
tickOptions:{formatString:'%#m/%#d/%Y'
}
//rendererOptions: {sdaTickInterval: [1, 'month']}
},
yaxis: {
label: "MB",
tickOptions:{formatString:'%d '},
// Comment the next line out to allow negative values (and therefore rounded ones)
min: 0
}
},
highlighter: {
show: true,
sizeAdjust: 7.5
}
});
});
Okay - I've got a better grasp on this issue.
I've limited the tick number using the 'numberTicks' property, so it has a certain number if the date range is over a specificed value.
The only issue left is that, with numberTicks, the intersections aren't always correct (meaning that on the x-axis 10/05/12, the value isn't always placed there when numberTicks is limited.

Categories

Resources