Highcharts shankey diagram percentage not showing - highcharts

Problem:
The nodeFormat works well for sum (point.sum), but does not work for percentage (point.percentage).
Working nodeFormat:
nodeFormat: '{point.name} {point.sum}
Not working nodeFormat:
nodeFormat: '{point.name} {point.percentage}%'
Expected output:
China >> 147 (100)%
US >> 53 (36)%
EU >> 123 (84)%
Formula for expected output:
dataSum = 147
chinaPercentage = chinaData/dataSum*100 = 147/147*100% = 100%
usPercentage = usData/dataSum*100 = 53/147*100% = 36%
euPercentage = euData/dataSum*100 = 84/147*100% = 84%
Fiddle reference:
https://jsfiddle.net/8L75ez9n/

You need to use nodeFormatter function and calculate percentage value:
dataLabels: {
nodeFormatter: function() {
const point = this.point;
const percentage = point.linksTo[0] ?
point.sum / point.linksTo[0].fromNode.sum * 100 :
100;
return point.name + ' >> ' + point.sum + ' (' + Math.round(percentage) + ')%'
}
}
Live demo: https://jsfiddle.net/BlackLabel/q845s7on/
API Reference: https://api.highcharts.com/highcharts/series.sankey.dataLabels.nodeFormatter

Related

Highcharts - Slow Stochastic Indicator Error

I have Highcharts chart rendered on yAxis[0] and whenever the user selects an indicator like Aroon Oscillator, MACD etc. it is added as a new yAxis in the rendering container. Example:
The above snapshot is with the Aroon Oscillator. All good.
When i select Slow Stochastic though i get the following:
First of all both SS$K and SS$D have the same value as you can see in the tooltip which is clearly wrong and both lines have the same color for a reason when they should not because:
I tried to log the p.series.yData[p.point.index] (the currently hovered point yData value) in my console and for my surprise I get the following:
It returns an array (but why) of 2 values, and both plots access the first value, instead of SS%D accessing the second value. This is a Slow Stochastic problem from Highcharts since the Aroon Oscillator which has the same philosophy of many plots on the same yAxis, still works fine as you can see in the first picture (where the lines have the correct color and correct values assigned).
Lastly as you can see with another indicators MACD (which has the same issue as mentioned before with Slow Stochastic) - and VPT which works as intended, when i go full range, those have a gap at start. Why is that happening?
Has anyone come across a similar indicator problem from Highcharts and if so, could anyone give me his/her lights on how to solve the problem?
Thanks in advance, Christopher.
P.S. Those are my tooltip options for highcharts:
tooltip: {
formatter: function() {
let finalDate = '';
let tooltipString = '';
$.each(this.points, function(i, p) {
// console.log(p.series.name, p.series.yData[p.point.index]);
//Starting date
let dateTo = new Date(p.x),
month = '' + (dateTo.getMonth() + 1),
day = '' + dateTo.getDate(),
year = dateTo.getFullYear();
if (month.length < 2) month = '0' + month;
if (day.length < 2) day = '0' + day;
let intradayTime = '';
if (p.point.isIntradayData) {
let hours = dateTo.getHours();
let minutes = ('0' + dateTo.getMinutes()).slice(-2);
intradayTime += ' ' + hours + ':' + minutes;
}
dateTo = [day, month, year].join('.');
//If we wanna display previous date too:
let dateFrom = '';
if (p.point.previousDate) {
dateFrom = new Date(p.point.previousDate);
month = '' + (dateFrom.getMonth() + 1);
day = '' + dateFrom.getDate();
year = dateFrom.getFullYear();
if (month.length < 2) month = '0' + month;
if (day.length < 2) day = '0' + day;
dateFrom = [day, month, year].join('.');
dateFrom += ' - ';
}
finalDate = dateFrom + dateTo + intradayTime;
if (p.series.userOptions.compare == 'percent') {
tooltipString +=
'<br>' +
`<span style="color:${p.color}; font-size:10px; font-family:\'Arial\'">` +
p.series.name +
': </span>' +
'<span style="font-size:10px; font-family:\'Arial\'">' +
' ' +
p.point.percentageDifference +
`%(${p.y})`;
('</span>');
} else if (
p.series.type == 'candlestick' ||
p.series.type == 'ohlc'
) {
tooltipString +=
'<br>' +
`<span style="color:${p.color}; font-size:10px; font-family:\'Arial\'">` +
p.series.name +
': </span>' +
'<br>' +
'<span style="font-size:10px; font-family:\'Arial\'">' +
'Open: ' +
p.point.open +
'<br>' +
'<span style="font-size:10px; font-family:\'Arial\'">' +
'High: ' +
p.point.high +
'<br>' +
'<span style="font-size:10px; font-family:\'Arial\'">' +
'Low: ' +
p.point.low +
'<br>' +
'<span style="font-size:10px; font-family:\'Arial\'">' +
'Close: ' +
p.point.close +
'</span> <br>';
} else {
let pointValueFormatted = p.y.toFixed(2);
tooltipString +=
'<br>' +
`<span style="color:${p.color}; font-size:10px; font-family:\'Arial\'">` +
p.series.name +
': </span>' +
'<span style="font-size:10px; font-family:\'Arial\'">' +
' ' +
pointValueFormatted +
'</span>';
}
});
return (
'<span style=" font-size:10px; font-family:\'Arial\'">' +
finalDate +
'</span>' +
tooltipString
);
},
borderColor: '#7fb7f0',
shared: true,
},
INDICATORS LOGIC
It's impossible to reproduce it in jsfiddle due to the size, the complexity and for security reasons due to policies. So a user can select indicators from a tabmenu like:
and they way the indicators are sorted:
//Sorting indicators.
sortIndicators() {
console.log(`Function Call: sortIndicators()`);
this.cleanIndicators(); // Just clearing the chart yaxises and the indicators array list to iterate over indicators again.
let counter = 0;
//For every indicator
for (let indicator of this.indicators) {
//If it is chart indicator we render it on yAxis[0] with our prices
if (toolsManager.isChartIndicator(indicator)) {
//some indicators have multiple arrays of data instead of one saved in an array called arrayOfObj
if (indicator.arrayOfObj) {
for (let arg of indicator.arrayOfObj) {
this.highchartOptions.series.push(arg);
}
} else {
this.highchartOptions.series.push(indicator);
}
} else {
//If it not chart indicator
this.highchartOptions.chart.height +=
2 * this.yAxisMargin + this.yAxisHeight;
let topCalculation =
456 + (counter + 1) * this.yAxisMargin + counter * this.yAxisHeight;
//yAxis to add
let addedAxis = {
title: {
enabled: false,
},
opposite: true,
min: null,
height: this.yAxisHeight,
top: topCalculation + this.yAxisMargin,
showLastLabel: false,
labels: {
enabled: true,
x: -30,
y: 0,
style: {
fontSize: '10px',
fontFamily: 'Arial',
color: '#999999',
},
},
};
this.highchartOptions.yAxis.push(addedAxis);
if (indicator.arrayOfObj) {
//some indicators have multiple arrays of data instead of one saved in an array called arrayOfObj
for (let arg of indicator.arrayOfObj) {
//for every array of that arrayOfObj
arg.yAxis = counter + 1;
this.highchartOptions.series.push(arg); //push it to series
}
} else {
indicator.yAxis = counter + 1;
if (indicator.id === 'volume') {
indicator.data = this.volumeSeries;
}
this.highchartOptions.series.push(indicator);
}
counter++;
}
}
},
the part where the magic happens is here:
if (indicator.arrayOfObj) {
//some indicators have multiple arrays of data instead of one saved in an array called arrayOfObj
for (let arg of indicator.arrayOfObj) {
//for every array of that arrayOfObj
arg.yAxis = counter + 1;
this.highchartOptions.series.push(arg); //push it to series
}
} else {
indicator.yAxis = counter + 1;
if (indicator.id === 'volume') {
indicator.data = this.volumeSeries;
}
the reason it is implemented like this is because when an indicator is selected an object is returned from the server which may have one of the two following structures:
data structure 1:
data structure 2:
And after sorting out what type of structure I am dealing with, I am pushing it into the series to display. counter variable is just used for the y-axis's. Arron Oscillator and Slow Stochastic both fulfill the first part of the conditional, because both return an object which contains and arrayOfObj (for the line colors etc as you can see in the photo uploaded).

Display additional informatin in the tooltip

I have the following highcharts graph
https://jsfiddle.net/deemgfay/
and I am trying to display the "Consum Test" values in the tooltip but without adding them to the series. I just want to add Consum (l/100km)
Total Consum (l) to the series. Is that possible with hightcharts? Please see the screenshot below.
You can set the extra series to be hidden and ignored in legend:
visible: false,
showInLegend: false
Then use tooltip formatter function (useHTML must be enabled) to display points from all series in the shared tooltip regardless of their visibility:
formatter: function() {
var html,
originalPoint = this.points[0];
// header
html = "<span style='font-size: 10px'>" + originalPoint.x + "</span><br/>";
// points
originalPoint.series.chart.series.forEach(function(series) {
var point = series.points.find((p) => p.x === originalPoint.point.x);
html += "<span style='color: " + series.color + "'>\u25CF</span> " + series.name + ": <b>" + point.y + "</b><br/>"
});
return html;
}
Live demo: https://jsfiddle.net/kkulig/1oggzsx0/
API references:
http://api.highcharts.com/highcharts/tooltip.formatter
http://api.highcharts.com/highcharts/tooltip.useHTML
This can be done by using tooltip.formatter. Here I append to tooltip info based on index of current series from index of required extra array.
formatter: function() {
var s = '<b>' + this.x + '</b>';
var reqpoint = 0;
$.each(this.points, function() {
var reqpoint = this.point.index
s += '<br/>' + this.series.name + ': ' +
this.y.toFixed(2) + 'm';
if (this.series.index == 1) {
s += '<br/>Test Consum (l): ' + extraData[reqpoint] + 'm';
}
});
return s;
},
Fiddle demo

How to create series and setup X and Y axis?

I have to make a series as shown in the image.
The Dates and the values are dynamically generating based on the user selection. The Time is in 24 hrs format. Please tell me how can i create the series for this. Also how to put the duration 1 hour for the time value.
Image for Reference:
In your series data you are using data: [Date.UTC(2012, 10, 01, 12,11,10)] you can simply change that to: data: [90 *60 * 1000] //For 90 Minute
Here is the LIVE DEMO
If you want to plot other series for other days then you should add categories in xAxies. for example for showing 3 days:
xAxis: {
categories: ['01/10/2012', '01/11/2012', '01/2/2012']
},
And in series-data use equal number of parameters as you have in your categories for 3 categories:
data: [ 50 *60 * 1000, 100 *60 * 1000, 50 *60 * 1000 ]
See the LIVE DEMO
Use formatter in tooltip to see a correct hour:minutes when you hover the series.
tooltip: {
formatter: function() {
var ms = this.y;
var x = ms / 1000;
var seconds = x % 60;
x = x / 60;
var minutes = x % 60;
x = x / 60;
var hours = parseInt(x % 24);
x = (x / 24);
var days = parseInt(x);
if(days == 0)
{
return '<b>' + this.series.name +'</b> ->' + this.x + '<br/>' + hours +':' + minutes;
} else {
return '<b>' + this.series.name +'</b><br/>' + days + ' Day ' + hours +':' + minutes;
}
}
},
See the LIVE DEMO

Highstocks tooltip doesnt work when chart is small?

I've created custom formatter function for highstocks.js:
var tooltip = [];
for (key in this.points) {//if point type is portfolio
if ( this.points[key].point.type == 'portfolio' ) {
tooltip[key] = '<span style="color:' + his.points[key].series.color +'">' + this.points[key].series.name + '</span>' + '<br/><b>'+ _('', 'Net assets: ') + _s(this.points[key].point.sum, 2) + '</b>' + '<br/><b>'+ _('', 'Чистые активы: ') + _s(this.points[key].point.netassets, 2) +'</b> (<span style="color:' + ( (this.points[key].point.change < 0 )?'#b86565':'#619000') +'">' + _s(this.points[key].point.change, 0) +'%</span>)<br/>';
} else {
tooltip[key] = '<span style="color:' + this.points[key].series.color +'">' + this.points[key].series.name + '</span>: <b>'+ _s(this.points[key].point.y, 2) +'</b> (<span style="color:' + ( (this.points[key].point.change < 0 )?'#b86565':'#619000') +'">' + _s(this.points[key].point.change, 0) +'%</span>)<br/>';
}
}
var tl = '';
for (key in tooltip) {
tl += tooltip[key]
}
var date = Highcharts.dateFormat('%d %b %Y', this.points[0].point.x);
tl = date + '<br/>' +tl;
return tl;
The feature is that this function usues not only Y of a point, but also some additinal properties, that I have added to the point: such as type.
For points that are "portfolio" type the tooltop shoold be rendered differently and has to have much more data then for "regular" point type.
The problem that I've encountered that when conatiner div has small width, my template doesnt work, although it works fine when div's width is big.
Highstocks.js does default aggregation when renders chart to relativly small area: http://api.highcharts.com/highstock#plotOptions.area.dataGrouping
When points are groupped, they lose all additional attributes, leaving only Y property, so complex tooltop wont work.
To fix it I had to disable data groupping in chart options:
plotOptions: {
series: {
dataGrouping: {
enabled: false
}
}
},
Is ther a way to display complex tooltip on small chart without disabling dataGrouping?
The question is: 'How to group point.type'?
I guess you would like to group point by type, and then display n-points in that place? Or group point as is, but in options count number of types? What if user will define myCustomFancyProperty - what then? Aggregate all own properties from point? It's getting harder and harder.. what can I advice is to create an idea here with some explanation/solution.
You can always get from grouped point x-value (this.x), and then loop over all points (accessible via this.points[0].series.options.data), find the closest point to that timestamp and display required value.
as Pawel Fus suggested, I loop throught all points to find closest value and add additinal attributes for each groupped point.
Here is th whole formmater function.
formatter: function() {
var tooltip = [];
//Тултип
for (var key in this.points) {//Если точка - пользователь
//прочитаем тип
var closestX = 0;
for (var j in this.points[key].series.options.data ) {
if ( Math.abs(this.points[key].series.options.data[j].x - this.points[key].point.x) < diff ) {
closestX = j;
}
var diff = Math.abs(this.points[key].series.options.data[j].x - this.points[key].point.x);
}
this.points[key].point.type = this.points[key].series.options.data[closestX].type;
this.points[key].point.sum = this.points[key].series.options.data[closestX].sum;
this.points[key].point.netassets = this.points[key].series.options.data[closestX].netassets;
if ( this.points[key].point.type == 'portfolio' ) { //если точка не пользователь
tooltip[key] = '<span style="color:' + this.points[key].series.color +'">' + this.points[key].series.name + '</span>' +
'<br/><b>'+ _('', 'Сумма активов: ') + _s(this.points[key].point.sum, 2) + '</b>' +
'<br/><b>'+ _('', 'Чистые активы: ') + _s(this.points[key].point.netassets, 2) +'</b> (<span style="color:' + ( (this.points[key].point.change < 0 )?'#b86565':'#619000') +'">' + _s(this.points[key].point.change, 0) +'%</span>)<br/>';
} else {
tooltip[key] = '<span style="color:' + this.points[key].series.color +'">' + this.points[key].series.name + '</span>: <b>'+ _s(this.points[key].point.y, 2) +'</b> (<span style="color:' + ( (this.points[key].point.change < 0 )?'#b86565':'#619000') +'">' + _s(this.points[key].point.change, 0) +'%</span>)<br/>';
}
}
var tl = '';
for (key in tooltip) {
tl += tooltip[key]
}
//Дата
var date = Highcharts.dateFormat('%d %b %Y', this.points[0].point.x);
tl = date + '<br/>' +tl;
return tl;
},

HighCharts: Is this.percentage accessible in the tooltip formatter?

Sample code:
formatToolTip: function() {
var tt = this.x + ' = ' + this.y + ' (this.percentage + ' %)';
return tt;
}
this.percentage is always undefined. I am a newbie working with highcharts 3.0.0
This is an old example of how you can use the formatter function to get the %. It relies on knowing the max value of the data, but you can use the getExtremes() method to pull that from the chart.
http://jsfiddle.net/jlbriggs/H3Q9h/5/
var pcnt = Highcharts.numberFormat((this.y / 415 * 100),0,'.');
http://api.highcharts.com/highcharts#Axis.getExtremes%28%29
replace the 415 in the equation with a variable set as the dataMax value of the getExtremes() method.

Resources