I use variable pie with some long and multi-lines labels (label value1 value 2).
To optimize the rendering of the chart, I use the alignTo "plotEdges" option and it's working well but it seems it cannot align on the plot edge all the lines of the labels.
My options are :
dataLabels: {
enabled: true,
alignTo: "plotEdges",
formatter: function () { return "<b>"+this.point.name+"</b><br><i>"+""+Highcharts.numberFormat(this.point.y, 0, ".", " ")+" </i> <i>("+Highcharts.numberFormat((this.y/11999*100), 2, ".", " ")+"%)</i><br><i>"+""+Highcharts.numberFormat(this.point.z, 0, ".", " ")+" </i>";}},
innerSize: "20%",
}
You can see the result here : https://jsfiddle.net/vegaelce/95ochyqz/
I would like all labels on the right of the chart right aligned (values right aligned under the title of the label).
The labels on the left of the chart are correctly left aligned near the plotEdge.
Is it possible ?
Thanks
Set the dataLabels.useHTML to true to render them as outstanding elements,
In the dataLabels.formatter callback check the label alignment and add a float styling for the according to the left:
formatter: function() {
if (this.point.labelPosition.alignment === 'left') {
return "<b>" + this.point.name + "</b><br><i style='float: right'>" + "" + Highcharts.numberFormat(this.point.y, 0, ".", " ") + " </i> <i style='float: right'>(" + Highcharts.numberFormat((this.y / 11999 * 100), 2, ".", " ") + "%)</i><br><i style='float: right'>" + "" + Highcharts.numberFormat(this.point.z, 0, ".", " ") + " </i>";
} else {
return "<b>" + this.point.name + "</b><br><i>" + "" + Highcharts.numberFormat(this.point.y, 0, ".", " ") + " </i> <i>(" + Highcharts.numberFormat((this.y / 11999 * 100), 2, ".", " ") + "%)</i><br><i>" + "" + Highcharts.numberFormat(this.point.z, 0, ".", " ") + " </i>";
}
}
Feel free to improve the code. Demo: https://jsfiddle.net/BlackLabel/2vt7394m/
API: https://api.highcharts.com/highcharts/series.line.dataLabels.useHTML
Related
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).
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
it's my first qestion in stack !
I try to add multiple hidden value in tooltip
my data example :
var sample1= [{y:3.1,ext1:'14.5',ext2:'14.5',color:'#0ef43c'},{y:2.9,ext1:'14.2',ext2:'14.5',color:'#30ff21'},{y:2.9,ext1:'14.2',ext2:'14.5',color:'#42ff2c'},etc...
var sample2= [{y:3.1,ext1:'14.5',ext2:'14.5',color:'#0ef43c'},{y:2.9,ext1:'14.2',ext2:'14.5',color:'#30ff21'},{y:2.9,ext1:'14.2',ext2:'14.5',color:'#42ff2c'},etc...
and my tooltip
tooltip: {
shared: true,
formatter: function() {
var txt = Highcharts.dateFormat("<b>%Hh</b>", this.x);
txt += "<br /><b>Sample 1: </b>"+ this.points[0].point.y + " m - " + this.points[0].point.ext1 + "s" + this.points[0].point.ext2+ "s";
txt += "<br /><b>Sample 2: </b>"+ this.points[1].point.y + " m - " + this.points[1].point.ext1+ "s" + this.points[1].point.ext2+ "s";
txt += "<br />Sample 3: :" + this.points[2].point.y + " m";
return txt;
}
},
But there is problem with hidden data , so is there a way for use a code as
sample1.data.point.ext1
instead of this.points[0].point.ext1 ?
Thanks
I am using a stock chart to show trend data. On the backend I am getting what the valueSuffix should be (or valuePrefix as the case may be). I am also formatting the date display in the tooltip. Here is the important part of the series declaration:
...
name: 'Wages',
tooltip: {
valuePrefix: '$',
valueDecimals: 0
},
...
Here is the tooltip formatter:
...
tooltip: {
formatter: function () {
var s = '<b>';
if (Highcharts.dateFormat('%b', this.x) == 'Jan') {
s = s + 'Q1';
}
if (Highcharts.dateFormat('%b', this.x) == 'Apr') {
s = s + 'Q2';
}
if (Highcharts.dateFormat('%b', this.x) == 'Jul') {
s = s + 'Q3';
}
if (Highcharts.dateFormat('%b', this.x) == 'Oct') {
s = s + 'Q4';
}
s = s + ' ' + Highcharts.dateFormat('%Y', this.x) + '</b>';
$.each(this.points, function (i, point) {
s += '<br/><span style="color: ' + point.series.color + '">' + point.series.name + ':</span>' + point.y;
});
return s;
}
}
...
Example jsFiddle.
If you notice the prefix for the dollar sign is not showing on the Wage series. I am not really sure what I am missing here.
The fix is to break up the label formatting and the value formatting into distinct sections. See example jsFiddle.
Set the chart.tooltip like:
...
tooltip: {
headerFormat: '<b>{point.key}</b><br>',
xDateFormat: '%Q'
},
...
On the xAxis I replaced the label formatter with:
...
format: '{value: %Q}'
...
Inside the series I kept my suffix/prefix/decimals the same:
...
tooltip: {
valuePrefix: '$',
valueDecimals: 0
},
...
The big change came when I found that you can set your own date format label. I created one for Quarters (which is what I did the original cumbersome code for):
Highcharts.dateFormats = {
Q: function (timestamp) {
var date = new Date(timestamp);
var y = date.getFullYear();
var m = date.getMonth() + 1;
if (m <= 3) str = "Q1 " + y;
else if (m <= 6) str = "Q2 " + y;
else if (m <= 9) str = "Q3 " + y;
else str = "Q4 " + y;
return str;
}
};
I now get the tooltip to show the correct labels.
Unless something has changed in a recent version, you can't set your tooltip options within the series object. You can set anything that you would set in the plotOptions on the series level, but not the tooltip.
You can set a check within your main tooltip formatter to check for the series name, and apply the prefix accordingly.
{{edit::
This appears to be an issue of the formatter negating the valuePrefix setting.
Old question... but I spent hours looking for an acceptable way to do this.
/!\ : Before OP's answer...
If someone is looking for a way to use the formatter provided by highcharts (pointFormat attribute) (Doc here: LABELS AND STRING FORMATTING), you will find a (bad) example here :
http://jsfiddle.net/gh/get/jquery/1.7.2/highslide-software/highcharts.com/tree/master/samples/highcharts/tooltip/pointformat/
Why "bad" ? because if you change :
pointFormat: '{series.name}: <b>{point.y}</b><br/>',
with :
pointFormat: '{series.name}: <b>{point.y:,.1f}</b><br/>',
you add thousand separators (useless here) and force number format to 1 decimal ... and loose the suffix.
The answer is : series.options.tooltip.valueSuffix (or series.options.tooltip.valuePrefix)
Example :
pointFormat: '<span style="color:{series.color}">\u25CF</span> {series.name}: <b>{point.y:,.0f}{series.options.tooltip.valueSuffix}</b><br/>',
Hope this will save you some time.
Now OP's Answer :
Based on what I said, you can change :
$.each(this.points, function (i, point) {
s += '<br/><span style="color: ' + point.series.color + '">' + point.series.name + ':</span>' + point.y;
});
For this :
$.each(this.points, function (i, point) {
s += '<br/><span style="color: ' + point.series.color + '">'
+ point.series.name + ':</span>'
+ (typeof point.series.options.tooltip.valuePrefix !== "undefined" ?
point.series.options.tooltip.valuePrefix : '')
+ point.y
+ (typeof point.series.options.tooltip.valueSuffix !== "undefined" ?
point.series.options.tooltip.valueSuffix : '');
});
This will add prefix and/or suffix if they exists
I am using highcharts in vaadin application . It works in a best manner for single line graph.
If i put 49 lines and each lines contain 2048 values . I have disabled the marker , shadow,
tooltip,animation . But still the graph takes 30 seconds to display in google chrome and more than 30 seconds in firefox. How to display in a fast manner .The code is given below
String s = "";
s+="chart = Highcharts.setOptions(Highcharts.theme);"+
" chart = new Highcharts.Chart({\n" +
" chart: {\n" +
" renderTo: 'container5',\n" +
" type: 'line',\n" +
" },\n" +
" title: {\n" +
" text: 'Intensity Graph'\n" +
" },\n" +
" xAxis: {\n" +
" min:0,\n"+
" max:2100,\n"+
" tickInterval:100,\n"+
" labels: {\n" +
" rotation: -45,\n" +
" align: 'right',\n" +
" style: {\n" +
" fontSize: '13px',\n" +
" fontFamily: 'Verdana, sans-serif'\n" +
" }\n" +
" }\n" +
" },\n" +
" yAxis: {\n" +
" title:{\n" +
" text:''\n" +
" },"+
" min:0,\n"+
" max:255,\n"+
" tickInterval:20,\n"+
" },\n" +
"plotOptions: {\n" +
" series: {\n" +
" marker: {\n" +
" enabled: false,\n" +
" shadow : false,\n"+
" animation : false\n"+
" },"+
"color: '#00FF00',\n"+
" lineWidth: 0.1"+
" },"+
" },"+
" tooltip: {\n" +
" enabled: false\n" +
" },"+
" legend: {\n" +
" enabled: false\n" +
" },\n" +
// " tooltip: {\n" +
// " formatter: function() {\n" +
// " return '<b>'+ this.x +'</b><br/>'+\n" +
// " 'lux: '+ Highcharts.numberFormat(this.y, 1) +\n" +
// " ' px';\n" +
// " }\n" +
// " },\n" +
" series: [{\n" +
" name: 'Ejection Count',\n" +
" data: [" +
"],\n" +
" dataLabels: {\n" +
" enabled: true,\n" +
" rotation: -90,\n" +
" color: '#FFFFFF',\n" +
" align: 'right',\n" +
" x: -3,\n" +
" y: 10,\n" +
" formatter: function() {\n" +
" return this.y;\n" +
" },\n" +
" style: {\n" +
" fontSize: '13px',\n" +
" fontFamily: 'Verdana, sans-serif'\n" +
" }\n" +
" }\n" +
" }]\n" +
" });" +
" });");
long len = 2,kk1 = 0;
Highchart hc = new Highchart();
byte[] cor = new byte[4584];// I am adding this array content to the graph
StringBuffer sb = new StringBuffer();
try
{
for (long l = 0; l < len; l++)
{
sb.append(" this.chart.addSeries({\n" +
" data: [" );
for (int i = 0; i < 2; i++)
{
if(i == 0)
{
//2048 points
for (int j = 0,k = 276; k < 2324; j++,k++)
{
if(j == 2323)
{
sb.append(cor[k]) ;
}
else
{
sb.append((cor[k] +",");
}
}
}
}
sb.append("],"+
" redraw :false,\n"+
" animation:false\n"+
"});") ;
}
}
catch (FileNotFoundException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
sb.append("this.chart.redraw();");
hc.drawChart(sb.toString());
}
Highcharts are not so fast to display 100k points. For that purpose I advice to use Highstock, see example with 54k points: http://www.highcharts.com/stock/demo/data-grouping where charts are build within 0.2 sec. The main speed up in Highstock is dataGrouping.