Google Charts DateFormatter not displaying formatted values - javascript

So i'm currently trying to fix a problem with a Google Charts. I am drawing a chart that shows numeric values on the Y-axis and dates on the X-axis. I want the date to adpat according to the specified timezone. For that i'm using the DateFormatter Object from Google charts, providing the patter and the Timezone like so:
var dateFormatter = new google.visualization.DateFormat({
timeZone: _timeZone, //timezone
pattern: $scope.selectedResolution.pattern
});
The pattern attribute receives a string containing a format string and the timeZone receves a value representing the number of hours regarding the timezone offset (example, -4, -5, etc).
I Format the chart values like so:
dateFormatter.format($scope.data, 0);
Being that the column "0" is the column where i have my dates.
I then draw the chart, standard stuff:
chart = new google.visualization.ComboChart(document.getElementById(chart_name));
//generateTicks($scope.data, options);
chart.draw($scope.data, options);
The thing is that, the values showed in the X-Axis are not formatted whatsover, be it in the format or the timezon.
Problem Showcase
As you can see in the image above, the X-Axis shows "13:00" where it should show "15:00", in this particular case.
I also went and check the data i'm feeding the chart with to see if the formatter was actually doing any work on the data itself and it appears to be working properly:
Chart Data
Also, here's my charts options for reference:
var series = {}, firstSerie, row, title, tempRows = [];
// Create the data table
series[0] = {targetAxisIndex: 0, color: '#3182BD'};
series[1] = {color: '#FF7F00', type: 'line'};
options = {
tooltip: {isHtml: true},
titleTextStyle: {fontSize: 12, bold: false},
isStacked: false,
backgroundColor: 'transparent',
legend: {position: 'none'},
height: 300,
pointSize: 1,
series: series,
vAxes: {
// Adds titles to each axis.
0: {
textStyle: {color: $scope.widgetOptions.text_color || '#333333'},
format: $scope.vLabel === '%' ? '#\'%\'' : generateVerticalAxisPattern() + $filter('trustedHtml')($scope.vLabel),
minValue: 0,
gridlines: {
color: 'transparent'
}
}
},
hAxis: {
//title: graphLegend,
textStyle: {color: $scope.widgetOptions.text_color || '#333333'},
titleTextStyle: {color: $scope.widgetOptions.text_color || '#333333'},
viewWindows: {
min: moment().subtract(24, 'hours'),
max: moment()
},
gridlines: {
color: 'transparent',
units: {
hours: {format: ['HH:mm', 'ha']}
},
count: -1
},
minorGridlines: {
count: 0
}
},
vAxis: {
viewWindowMode: 'maximized',
viewWindow: {
min: $scope.widgetOptions.lower_limit
}
},
animation: {
duration: 500,
easing: 'out'
}
};
So, does anyone have any clue of what's going on? Im working with angularjs btw.
Thanks in advance

the format method, as follows, only formats the data, not the chart or chart axis.
dateFormatter.format($scope.data, 0);
basically, the above is used to display the correct format in the tooltip when hovering a data point.
to format an axis, you would normally set the format configuration option.
hAxis: {
format: $scope.selectedResolution.pattern
}
however, in this case, since you are changing the time zone,
you will need to provide custom ticks on the x-axis.
you can build an array of ticks from the data table rows,
using the formatValue method from the formatter.
var ticks = [];
for (var i = 0; i < $scope.data.getNumberOfRows(); i++) {
var dateValue = $scope.data.getValue(i, 0);
ticks.push({
v: dateValue,
f: dateFormatter.formatValue(dateValue)
});
}
// then add above to ticks to options...
hAxis: {
ticks: ticks
}
each tick will need to be an object,
with properties for v: for value,
and f: for formatted value.
the only problem with the above routine,
it assumes you want to display a tick for each row in the data table.
if you do not have room to display them all,
you will need to figure out your own algorithm to display the correct number of ticks.
edit
another method is to find the min and max dates within the data table,
using data table method getColumnRange
then build each tick after a particular duration.
for instance, to display a tick every four hours along the axis, between the min and max dates...
const FOUR_HOURS = (1000 * 60 * 60 * 4);
var ticks = [];
var dateRange = $scope.data.getColumnRange(0);
for (var i = dateRange.min.getTime(); i <= dateRange.max.getTime(); i = i + FOUR_HOURS) {
var dateValue = new Date(i);
ticks.push({
v: dateValue,
f: dateFormatter.formatValue(dateValue)
});
}

Related

Highcharts yAxis.max manage tick intervals dynamically

I'm trying to set the max and min of y value dynamically based on the data but it is having some troubles for some values. I think the highchart couldn't set values that doesn't have a proper tick.
For eg: min: 3 and max 10. Then, the highchart won't set these values instead of that it will set a value ranging from 0...14. This is just an example value, sometimes I get 36000.14 as min and 454533.18 as max.
So my logic isn't working in that case.
This is the part of code which I currently have:
data = [3.43, 3.58, 4.86, 8.55, 8.77, 4.44, 9.67]
maximum_val = Math.ceil(maximum_val) eg: 10
minimum_val = Math.floor(minimum_val) eg: 3
evolution_options.yAxis[$('#div_count_'+div).find('#y_axis_lbl').text()] = {
min: minimum_val,
max: maximum_val,
title: false,
labels: {
style: {
color: 'black'
}
}
}
Update
Fiddle : http://jsfiddle.net/e3fy1vu9/
Further Brainstorming
I think it is because the min value isn't a multiple of the max value so Highchart couldn't calculate how to split the tick. If there was any way so I could set the max value as the multiple of the min value.
Here is my advice how to calculate the max and min for yAxis from the defined data.
Demo: https://jsfiddle.net/BlackLabel/0qwt1kx7/
yAxis: {
min: Math.floor(Math.min(...data)),
max: Math.round(Math.max(...data)),
tickAmount: Math.round(Math.max(...data)) - Math.floor(Math.min(...data)) + 1 //1 count the min or max as itself
},
Please test it and let me know if it fits to your requirements. Because the tickAmount value depends on how many values is included in the data array and probably some modifications are needed for larger data.
EDIT:
Try to use this custom solution to calculate the yAxis positions:
Demo: http://jsfiddle.net/BlackLabel/ztaj6Lkd/
var positions = [],
tickNumbers = 5,
tick = Math.floor(Math.min(...data));
for(let i = 0; i < tickNumbers; i++) {
positions.push((Math.round(tick * 100) / 100));
tick += Math.round(Math.max(...data)) / tickNumbers
}
//add the max
positions.push(Math.round(Math.max(...data)))
Hi I have fixed it by setting this alignTicks:false option.
evolution_options.yAxis[$('#div_count_'+div).find('#y_axis_lbl').text()] = {
min: minimum_val,
max: maximum_val,
title: false,
labels: {
style: {
color: 'black'
}
},
alignTicks: false
}

Jqplot tooltip values incorrect when chart has two lines

I have a jqplot chart that has two lines. I'm using tooltipContentEditor to modify the text displayed in the tooltip. Prior to adding the second line, the tooltips displayed the values fine. Now that I have added another line, the tooltips always show the data for the second line (thus incorrectly for the first). How do I have the correct values displayed?
Blue line tooltip shows yellow line value:
Chart initialization (tooltipContentEditor function inside):
$.jqplot('mychart', [coords, paidCoords], {
//title:'Sales for ' + name,
animate: !$.jqplot.use_excanvas,
axesDefaults: {
tickRenderer: $.jqplot.CanvasAxisTickRenderer ,
},
axes:{
xaxis:{
renderer:$.jqplot.DateAxisRenderer,
tickInterval: tickInterval,
tickOptions: { formatString: formatString, angle: -30 }
},
yaxis:{
min:0
}
},
highlighter: {
show: true,
sizeAdjust: 7.5,
tooltipContentEditor: function(str, pointIndex, index, plot){
console.log('logging in tooltipeditor');
console.log(str);
console.log(pointIndex);
console.log(index);
console.log(plot);
var splitted = plot._plotData[1][index];
var x = splitted[0];
var y = splitted[1];
x = new Date(x);
x = x.toString();
return x + "<br/>$" + y;
}
},
cursor:{
show: true,
zoom:true,
showTooltip:false
}
});
Nevermind! I figured it out! In the tooltipContentEditor function I needed to do this:
var splitted = str.split(",");
Instead of this:
var splitted = plot._plotData[1][index];

Flowing Merge of points with same x-axis in the same series

I am currently using highstock to plot the total number of items available based on time throughout the day (which then updates real-time).
If two changes to the total number of items happens at the same time, in highstock I get a vertical bar of the difference:
So in my example image we start with 4299 things, then 53 items are removed and 50 are added (technically at the same time, but are two different transactions and are two points). With a net difference of -3. (or in otherwords, I get {x: 5:44:15 and y: 4246, change: -53}, {x: 5:44:15, y: 4296, change: 50}).
So my question:
Is it possible in highstock to merge those points to get rid of the vertical bar and use 4296 as the shown value? I was hoping I could then use the tooltip formatter to loop through 'this.points' and display a change of -53 and a change of 50 in the tooltip so the user can see what resulted in a net change of -3.
If this is not possible, I will just merge the points myself and pass all the relevant information in the point to generate the tooltip (and chart look) that I am going for, but wanted to see if I could just utilize all the functionality of highstock first - and keep these points separate.
Thanks!
Edit::
new Highcharts.StockChart({
chart : {
renderTo : 'realTimeChart',
zoomType: 'x',
backgroundColor: '#feffdd',
style: {
fontFamily: 'Segoe UI'
},
type: 'spline'
},
plotOptions: {
area: { animation: false },
arearange: { animation: false },
areaspline: { animation: false },
areasplinerange: { animation: false },
bar: { animation: false },
column: { animation: false },
columnrange: { animation: false },
gauge: { animation: false },
line: { animation: false },
pie: { animation: false },
scatter: { animation: false },
series: { animation: false },
spline: { animation: false }
},
xAxis: {
ordinal: false
},
tooltip: {
animation: false,
formatter: function() {
var p = '';
p += '<span style="font-size: 9px;">' + Highcharts.dateFormat('%A, %b %e, %Y %H:%M:%S', this.x) +'</span><br/>';
$.each(this.points, function(i, point){
p += '<span style="color:' + this.series.color + '">' + this.series.name + '</span>: <b>'+ this.y +'</b>';
if (point.point.where) {
p += '<br />' + point.point.where + ' changed by ' + point.point.change + (point.point.who ? ' (' + point.point.who + ')' : '');
}
});
return p;
}
},
rangeSelector: {
buttons: [{
count: 30,
type: 'minute',
text: '30M'
}, {
count: 1,
type: 'hour',
text: '1H'
}, {
count: 6,
type: 'hour',
text: '6H'
}, {
type: 'all',
text: 'Day'
}],
inputEnabled: false,
selected: 1
},
exporting: {
enabled: false
},
series : [{
name : 'Available',
data : data,
lineWidth: 1,
states: {
hover: {
enabled: false
}
}
}]
Data is in the format I showed previously, except the x is actually in milliseconds since epoch:
data = [
{x: 123456789, y: 2000, where: 'Location', change: 40, who: 'Joe'},
{x: 123456789, y: 1960, where: 'Location', change: -40, who: 'Bob'},
...
];
Just wanted to follow up with how I easily got around the probem. Instead of placing by second, I decided to group points together to the nearest minute rounding down (so I have blocks of minutes).
Then for each point I passed in an array of the actual points contained within that minute block as a new argument, and updated the y value for that minute block. Then I used the tooltip formatter to display all the changes within that minute block with their actual time of change. This gave me a more flowing graph instead of all these hard vertical points for the same x-axis.
To easily change a data point at a specific x-axis point, I kept a separate array of the location of the minute block inside the series.data array for highcharts, that way if I needed to update a block, I knew exactly where that time series was.
Here is how I accomplished my task:
I create the reference array:
var pointIndex = {};
I created the inital data series from historical data for the day (pulled in via ajax):
var data = [];
var time = Math.floor(actual_time / 60000) * 60000;
pointIndex[time] = data.push({x: time, y: items_available, change: [{when: actual_time}]});
So actual_time is the number of milliseconds since epoch (when the even change occured), then I round that to the nearest minute to get the minute time block, change is the argument that will hold all the actual points for display in the tooltip.
So when I add a new point I check if the minute block exists, if it does not, add a new point, otherwise update an old point:
var time = (new Date()).getTime();
var point = Math.floor(time / 60000) * 60000;
if (pointIndex[point]) {
var change = chart.series[0].data[pointIndex[point]].change;
change.push({when: time});
chart.series[0].data[pointIndex[point]].update({x: point, y: items_available, change: change});
} else {
pointIndex[point] = chart.series[0].data.length;
chart.series[0].addPoint({x: point, y: items_available, change: [{when: time}]}, false, false);
}
(In all cases I do the actual chart refresh after I am done updating points.)
Hopefully that will help anyone else who finds theirself in the same position!
Edit:: (forgot the formatter):
tooltip: {
animation: false,
formatter: function() {
var p = '';
p += '<span style="font-size: 9px;">' + Highcharts.dateFormat('%A, %b %e, %Y %H:%M', this.x) +'</span><br/>';
$.each(this.points, function(i, point){
p += '<span style="color:' + this.series.color + '">' + this.series.name + '</span>: <b>'+ this.y +'</b>';
if (point.point.change) {
for(var j = 0; j < point.point.change.length; ++j) {
p += '<br />Change at: ' + new Date(point.point.change[j].when).toTimeString();
}
}
});
return p;
}
}

jqPlot points not correct when numberTicks set

I'm trying to limit the number of ticks on a dynamic jqplot I'm setting up. Unfortunately, when I do it, the points do not look accurate. Here's what it looks like:
As you can see, the point on 10/05/12, for example, is not on it's correct axis. I realize that now these points are estimated, but the points must look accurate on the graph -- especially when hovering over the points gives you their correct date and y-axis value.
Here's my code for this intilization, if that helps:
var datepickerBegin = $("#datepicker_start").datepicker({dateFormat: "dd/mm/yyyy"}).val();
var datepickerEnd = $("#datepicker_to").datepicker({dateFormat: "dd/mm/yyyy"}).val();
var startDiff = $("#datepicker_start").datepicker("getDate");
var endDiff = $("#datepicker_to").datepicker("getDate");
alert(startDiff)
alert(endDiff)
// Check if dates are more than a week apart
if(endDiff - startDiff >= 15 * 86400 * 1000) {
var plot2 = $.jqplot('chart2', jsonurl,{
title: "Storage Space",
dataRenderer: ajaxDataRenderer,
dataRendererOptions: {unusedOptionalUrl: jsonurl},
axes: {
xaxis: {
'numberTicks' : 15,
min: datepickerBegin,
max: datepickerEnd,
renderer:$.jqplot.DateAxisRenderer,
rendererOptions:{tickRenderer:$.jqplot.CanvasAxisTickRenderer},
tickInterval: '1 day',
tickOptions:{formatString:'%#m/%#d/%Y'
}
},
yaxis: {
label: "MB",
tickOptions:{formatString:'%d '},
min: 0
}
},
highlighter: {
show: true,
sizeAdjust: 7.5
}
});
// Somehow fix the line?
plot2.redraw({});
}
Anyone out there have a fix for this? Thanks!

dates cannot be shown on highcharts

I am having difficulty showing a string separated with dates on a basic high chart
I am getting and saving the tweets from twitter into arrays and then converting the array into a string separated by commas and quotes.
Unfortunately I cant display them on the graph, I have no idea what I'm doing wrong.
function search(){
var value = $('#box').val();
var array=[];
var dateArray = [];
var dateString;
if (value!==""){$.getJSON("",
function(data){
$.each(data.results, function(i, item){
var user=item.from_user;
var created_at=new Date(item.created_at);
var month = created_at.getMonth();
var day = created_at.getDate();
var year = created_at.getFullYear();
var created= day +'/'+ month+ '/'+ year;
array.push({date:created,username:user});
});
// console.log(array);
for (var i in array) {
dateArray.push(array[i].date);
}
dateString="'" + dateArray.join("','") + "'";
console.log(dateString);
});
}
var chart;
chart = new Highcharts.Chart({
chart: {
renderTo: 'container',
type: 'line',
marginRight: 130,
marginBottom: 25
},
title: {
text: 'Monthly Average Temperature',
x: -20 //center
},
subtitle: {
text: 'Source: WorldClimate.com',
x: -20
},
xAxis: {
categories: [dateString]
},
Fixed JsFiddle
Your x-axis parameter should be as follows:
xAxis: {
categories: dateString
},
Als, just change this :-)
for (var i in array) {
dateString.push(array[i].date);
}
highchart(dateString);
Oh, and you should change to this, definately.
<input type="text" id="box" value="a"/>
Extra investigation info:
It appeared that (in the old situation)
console.log(stringArray);
highchart(stringArray);
was empty. Would you pass
highchart(["ab", "b", "c"]);
then you'd be fine. This is because you are passing an empty array, you are creating the chart before you have the JSON data. I therefore moved the creation to the JSON function.
Why not use xAxis as datetime:
xAxis: {
type: 'datetime'
},
Parse the dates into JS time from the twitter data and have something like this as your series data array:
[[<jstime1>, "big cool tweet1"], [<jstime2>, "big cool tweet2"]]

Categories

Resources