Defining 'step' attribute in Zingchart renders the graph unusable - javascript

Scenario:
I am able to get a status graph with Zingchart, using a stacked hbar and plotting data intervals where the status of an element doesn't change:
However, I would like to have more resolution in the axis that provides the time.
Problem:
I am not able to increase the number of ticks in the y-axis (this is an stacked hbar graph, so the usual x-axis is actually the y-axis).
If I introduce step:"1hour", in the scaleY field I get back to epoch 0 and this is what I obtain:
Question:
What am I doing wrong? I would like either:
To have more time references in the axis of the time.
Or (and this may be another question) to be able to know on mouse over (in the tooltip?) at what time I am. Since I am plotting time increments I lost the actual time on where the change of status happens. Is there a direct way to do this in this particular situation? Or do I have to carry another data-customValue for the actual date of the start / end of the interval?
In the code, I jump from epoch 0 to the time I start having data, and define minValue to plot as the first epoch I have data minus one second.
This is the working code (no step attribute defined):
<!DOCTYPE html>
<html>
<head>
<script src="/home/eballes/Work/backup/zingchart_test/zingchart_2.3.3/zingchart.min.js"></script>
<script>
zingchart.MODULESDIR = "/home/eballes/Work/backup/zingchart_test/zingchart_2.3.3/modules/";
</script>
<style></style>
</head>
<body>
<div id='myChart'></div>
<script>
var myConfig = {
type: "hbar",
utc:true,
title: {
text: 'Status'
},
scaleY:{
transform:{
type:'date',
all:"%d/%M/%Y\n%H:%i:%s",
},
minValue:1456693864000,
zooming:true,
label:{
"text": "Time",
},
tick:{
"line-color":"black",
"line-width":"2px",
"size":8,
},
maxItems:10,
itemsOverlap:true,
item:{
"font-size":10
},
},
scaleX:{
label:{
"text": "Item",
},
},
plot:{
stacked:true,
exact:false,
barWidth:10,
rules:[
{
rule:'%data-customValue == 0',
alpha:0, // Transparent
},
{
rule:'%data-customValue == 1',
backgroundColor:'#0000FF' // Dark Blue
},
{
rule:'%data-customValue == 2',
backgroundColor:'#00FFFF' // Light Blue
},
{
rule:'%data-customValue == 3',
backgroundColor:'#FF7F50' // Orange
},
{
rule:'%data-customValue == 4',
backgroundColor:'#FFFF00' // Yellow
},
{
rule:'%data-customValue == 5',
backgroundColor:'#EE82EE' // Purple
},
{
rule:'%data-customValue == 6',
backgroundColor:'#00FF00' // Green
},
{
rule:'%data-customValue == 7',
backgroundColor:'#FF0000' // Red
},
]
},
tooltip:{
jsRule:"CustomFn.formatTooltip()",
},
series : [
{
values : [
[1,1456693864000],
[2,1456693864000],
.... // From 1 to 36
[36,1456693864000],
],
'data-customValue':[0,0,0,0,0,0,
0,0,0,0,0,0,
0,0,0,0,0,0,
0,0,0,0,0,0,
0,0,0,0,0,0,
0,0,0,0,0,0],
},
{
values : [
[11, 32000], [12, 10270000], [14, 5033000], [18, 79000], [20, 43000], [24, 76000], [26, 4043000], [8, 33000], [9, 63000],
],
'data-customValue':[2, 6, 6, 0, 1, 2, 6, 1, 0, ],
},
{
values : [
[14, 3000], [19, 20000], [26, 1000], [8, 86000], [9, 13000],
],
'data-customValue':[2, 2, 2, 0, 1, ],
},
// All intervals
]
};
CustomFn = {};
CustomFn.formatTooltip = function(p){
var hours = Math.floor(p.value / 3600000);
var minutes = Math.floor((p.value % 3600000) / 60000);
var seconds = (p.value % 60000)/1000;
var tooltipText = "Item: " + p.scaletext + "\nDuration: ";
var hoursText = (hours == 0) ? "" : (hours + "h ");
var minutesText = (minutes == 0) ? "" : (minutes + "m");
var secondsText = (seconds == 0) ? "" : (seconds + "s");
tooltipText = tooltipText + hoursText + minutesText + secondsText + "\n";
tooltipText = tooltipText + "Value: " + p['data-customValue'];
var alpha = 70;
// We don't want any tooltip for the time-jump
if (hours > 300000) {
tooltipText = "";
alpha = 0;
}
return {
text : tooltipText,
backgroundColor : "#222",
alpha: alpha,
}
};
zingchart.render({
id : 'myChart',
data : myConfig,
height: 670,
width: '100%'
});
</script>
</body>
</html>
This is the link to the Plunker with the whole example, not working. If you remove the line step:"1hour", it will work.
Funny enough, step:20 works fine. But I would prefer to have my steps in a normal way, like hourly.
Note: Just to provide some more context, this question is the continuation of this other previous question.

Full Disclosure, I'm a member of the ZingChart team.
The first problem being
step:'1hour'
That string value is being interpreted by our library as the value 0. This is because the string '1hour' does not match any of our keywords nor does it evaluate to a Number type. We are not simply using parseInt so it does not evaluate to 1.
If you want an hourly step you would do the step count in milliseconds. This is documented in our scales page.
step:3600000
For display the scale value we do have a tokens list which allows you to add values from your scale to your tooltip. In your customFn.formatTooltip you would add this line.
...
tooltipText = tooltipText + "Value: " + p['data-customValue'];
tooltipText += '<br> %vv'
The %vv is in our tokens list and will grab the transformed value off the y-axis. If you did %vt it would give you the millisecond value.

Related

Highcharts | Custom plotOptions->series->dataLabels

I'm trying to do a really specific graph with HighChart, I hope I will be clear.
I give an example, I have votes for each country like this :
France 2 yes / 1 No
England 1 yes / 0 No
I have to display in plotOptions->series->dataLabels the information like this :
France yes : 2 votes/66% Because we have 3 votes in total for France
France no : 1 votes/33%
England yes : 1 votes/100% Because we have 1 vote in total for England
England no : 0 votes/0%
In Php / Symfony 3 / Javascript I did something like this :
$ob->plotOptions->series(
array(
'dataLabels' => array(
'enabled' => true,
'formatter' => new Expr('function () {
return this.y + "<br/>(" + Math.round(((this.y / '.$numberOfResultsByCountry.') * 100) * 100) / 100 + " %)"
}')
)
)
);
It works, but the calcul is wrong, $numberOfResultsByCountry change in function of my country, for France the value is 3, for England the value is 1.
How can I custom the dataLabels for each result ? I checked doc (http://api.highcharts.com/highcharts#plotOptions.series.dataLabels) and It seems that plotOptions->series->dataLabels does not accept an array here. So I have no idea how to do it.
I hope I have been clear to explain my problem.
TY
I think that you can add your total votes number as a 'total' parameter, and then find this parameter using this.point.total.
$(function() {
$('#container').highcharts({
xAxis: {
type: 'category'
},
series: [{
type: 'column',
keys: ['x', 'y', 'total'],
data: [
[0, 3, 5],
[1, 1, 4],
[2, 3, 3],
[3, 0, 4]
],
dataLabels: {
enabled: true,
formatter: function() {
return this.y + "<br/>(" + Math.round(((this.y / this.point.total) * 100) * 100) / 100 + " %)"
}
}
}]
});
});
I am using keys for adding new parameter to my points. You can find information about this parameter here: http://api.highcharts.com/highcharts#plotOptions.series.keys
Here you can find an example how it can work:
http://jsfiddle.net/m28ob7r7/
Regards,

Google Chart 2 Lines with not the same number of data

I'm trying to create a dynamic Google Chart line.
I have 2 lines in a range of a day (from 00h00 to 23h50 - 0am to 12pm) with a point for each lines every 10 minutes (00h00, 00h10, 00h20 etc...).
So for each lines I have 144 values for a day (1*6*24 - 6 / hours because I have 1 every 10 minutes).
For the first line, I have all the values at the start of the day (144 values) but for the other one, I'm drawing it according the current hour (exemple if it's 01:00 - 1am, I have only 6 values).
So i'm trying to draw the lines but I have a problem if my secondes lines doesn't have 144 values.
Here is the function to draw the chart with the different lines
function drawChart(chart_div) {
var data = get_data();
var chart = new google.visualization.ComboChart(document.getElementById(chart_div));
chart.draw(data, {
height: 300,
width: 1000,
chartArea:{left:40,top:5,width:"100%",height:"80%"},
seriesType: 'line',
series: {
0: {
color: '#0080FF'
},
1: {
color: '#bdbdbd',
enableInteractivity: false
}
},
tooltip: {
isHtml: true
},
legend: {
position: 'none'
},
vAxis: {
gridlines: {
color: '#e5e5e1'
}
},
hAxis: {
viewWindow: {
min: 0,
max: 144
},
ticks: [0, 24, 48, 72, 96, 120, 144] // display labels every 24
}
});}
Here is the code to launch the function to draw the chart
google.load('visualization', '1', {'packages':['corechart']});
google.setOnLoadCallback(function() {
drawChart("chart_one");
});
And here is the function to generate the data (They are currently randomly generate but I'll take them from DB later)
function get_data(){
var data = new google.visualization.DataTable();
data.addColumn('string', 'Time');
data.addColumn('number', 'Heartbeat (%)');
data.addColumn('number', 'Heartbeat moy (%)');
var total = 0;
for(i = 0; i < 24; i++){
for(j=0; j <= 5; j++){
var num = "";
if(i < 10){
num += "0";
}
if(total < 98){
data.addRows([[num + i + "h"+j+"0", Math.floor((Math.random() * 10) + 91), Math.floor((Math.random() * 10) + 91)]]);
} else {
data.addRows([[num + i + "h"+j+"0", "", Math.floor((Math.random() * 10) + 91)]]);
}
total++;
}
}
return data;
}
So basically I would have something like that :
But if I'm setting the secondes line values to "", it say
Error: Type mismatch. Value does not match type number in column index 2
Does anyone have any idea how to bypass the value for the seconde lines if I doesn't have it ?
Use null instead of an empty string "" for points that don't have any data

Flotr2 - hover on point in line chart

I have a Flotr2 barchart with linecharts on them, when you hover the linechart you should see the coordinates in a tooltip.
The problem is that the tooltips are not show for the 2 lowest points in the linechart on top of the green bar chart.
See this jsfiddle: http://jsfiddle.net/P855Q/1/
This is the javascript for the charts:
(function basic_bars (container, horizontal) {
var NumberOfBars = 3;
var barcenters = [];
var barWidth = ( (1 / NumberOfBars) * 0.8 )
var barcenterAdjust = (barWidth * (NumberOfBars/2)) - (barWidth/2.2);
for (i = 0; i < NumberOfBars; i++) {
var barcenter = (i *(( 1 / NumberOfBars ) * 0.8));
barcenter = barcenter - barcenterAdjust;
barcenters.push(barcenter);
}
var dummyVar,
d0=[
[barcenters[0] + 3, 5],
[barcenters[0] + 3, 8.1333],
[barcenters[0] + 3, 9]
],
d7=[
[barcenters[1] + 3, 5],
[barcenters[1] + 3, 8.1333],
[barcenters[1] + 3, 9]
],
d14=[
[barcenters[2] + 3, 5],
[barcenters[2] + 3, 8.1333],
[barcenters[2] + 3, 9]
];
var a = [[3,8.303000]];
var b = [[3,8.574500]];
var c = [[3,8.222200]];
Flotr.draw(
container,[
{ data : a, label : 'a' , bars: {show : true, horizontal : false, shadowSize : 0, barWidth : 0.8, grouped: true, mouse:{track:false}} },
{ data : b, label : 'b' , bars: {show : true, horizontal : false, shadowSize : 0, barWidth : 0.8, grouped: true, mouse:{track:false}} },
{ data : c, label : 'c' , bars: {show : true, horizontal : false, shadowSize : 0, barWidth : 0.8, grouped: true, mouse:{track:false}} },
{ data : d7 , lines : { show : true }, points : { show : true },mouse:{track:true,trackFormatter: function(obj){ return obj.y; },relative: true}, color: '#999999'},
{ data : d14 , lines : { show : true }, points : { show : true },mouse:{track:true,trackFormatter: function(obj){ return obj.y; },relative: true}, color: '#999999'},
{ data : d0 , lines : { show : true }, points : { show : true },mouse:{track:true,trackFormatter: function(obj){ return obj.y; },relative: true}, color: '#999999'},
],
{
yaxis : {min : 1,max: 10,noTicks: 10,tickDecimals: 0,autoscaleMargin : 1},
xaxis : {min: 0,ticks: [["1", "1"],["2", "2"],["3", "3"],["4", "4"],["5", "5"],["6", "6"],["7", "7"],["8", "8"]]}
}
);
})(document.getElementById("example"));
I have analysed its details and found out that the fiddle is fine!
Also code looks fine,(Except barcenters part of code which seems of causing some issue..
but with some value tags the event is not firing which will change value for
<div class="flotr-mouse-value">
the above is the div with class which gives us the tooltip for the point value.
It is relocated dynamically each time we hover on any point.
I have done some tweaking to test whether the reason is due to drawing on same point 3 but with that I found no issue..(although one point started functioning) B(8.1) which was not working started working !! without any certain change But B(5.0) is still not working..
Now as the point is not getting focus>> the even is not fired which enables the toolTip(which shows point value)
As in the element <div id="example"></div> is dynamically filled by the script we can conclude the floatr script has some internal bug within the overlay drawing part,that is causing this issue..
Update 1: One weird behavior observed with the Bar in center, as that is only seems to not working, with the overlay lines changed on that; check updated fiddle
I am still looking in details, if something new comes up in will Update my answer.

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;
}
}

How to set Date Value for x Axis using gRaphael Line Graph

I've recently started to use gRaphael for my graphing needs and am pretty impressed so far. However, I have run into some difficulty when producing line graphs, specifically that when I attempt to set the values for the X axis to dates, the graph fails to render. My code to generate the graph is:
<script type='text/javascript' charset='utf-8'>
var r = Raphael('holder');
var lines = r.g.linechart(20, 20, 600, 300, [[1, 2, 3, 4, 5, 6, 7]], [['4.16','6.35','1.77','3.1','9.79','10.03','-0.3']], {nostroke: false, axis: '0 0 1 1', symbol: 'o', smooth: false}).hoverColumn(function () {
this.tags = r.set();
for (var i = 0, ii = this.y.length; i < ii; i++) {
this.tags.push(r.g.tag(this.x, this.y[i], this.values[i], 160, 10).insertBefore(this).attr([{fill: '#fff'}, {fill: this.symbols[i].attr('fill')}]));
}
}, function () {
this.tags && this.tags.remove();
});
lines.symbols.attr({r: 3});
</script>
<div id='holder'></div>
How would I be able to replace the X axis values '1, 2, 3, 4, 5, 6, 7' with say, 'Jan 2001, Feb 2001, Mar 2001...etc...etc....'?
Many thanks indeed, all help much appreciated!
At first you have to give the chart some values that it will not complain about.
In your case you could save the UNIX timestamp etc
Then you can alter the values using something like this (Using prototype etc):
lines.axis[0].text.items.each( function ( index, label ) {
//Get the timestamp you saved
originalText = label.attr('text');
newText = 'CONVERT TIMESTAMP TO DATE USING originalText as input HERE';
//label.rotate(75);
label.attr({'text': newText});
});
The .each can be replaced by a regular
for(var x = 0; x < lines.axis[0].text.length; x++)
loop if you like.
$.each(lines.axis[0].text.items , function ( index, label ) {
var months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'],
date = new Date(parseInt(label.attr("text"))),
day = date.getDate(),
month = months[date.getMonth()];;
dateText = month + " " + day;
//label.rotate(75);
label.attr({'text': dateText});
});

Categories

Resources