Highcharts missing information in scatterplot CSV/XLS export - highcharts

When exporting scatter plot / bubble chart data as CSV or XLS, it is missing key information, see for example: http://jsfiddle.net/11fum86u/
This is the data (extract):
series: [{
data: [
{ x: 95, y: 95, z: 13.8, name: 'BE', country: 'Belgium' },
And the axis titles are in the tooltip (but perhaps needs to be defined elsewhere):
tooltip: {
pointFormat: '<tr><th colspan="2"><h3>{point.country}</h3></th></tr>' +
'<tr><th>Fat intake:</th><td>{point.x}g</td></tr>' +
'<tr><th>Sugar intake:</th><td>{point.y}g</td></tr>' +
'<tr><th>Obesity (adults):</th><td>{point.z}%</td></tr>',
What is missing in the default export is (i) labels for y and z axis, and (ii) the names of the bubbles (in this example, country codes/names)
I was wondering how I might be able to add this information to the export.

Actually there's no z axis in bubble charts. It's quite confusing, because all points have z property. This property serves to compute the bubble size so no additional axis is needed, because everything is in 2d. zAxis is used in 3d charts.
Please refer to this live working example: http://jsfiddle.net/kkulig/udtr0emL/
You can handle first row labels via columnHeaderFormatter (http://api.highcharts.com/highcharts/exporting.csv.columnHeaderFormatter):
exporting: {
csv: {
columnHeaderFormatter: function(item, key, keyLength) {
if (item.axisTitle) {
return item.axisTitle.textStr; // x axis label
} else if (key === 'y') {
return item.yAxis.axisTitle.textStr; // y axis label
} else if (key === 'z') {
return 'Obesity (adults)'; // z axis label
}
}
}
}
To add another column (countries) added following pieces of code in these three core functions:
1. Highcharts.Chart.prototype.getDataRows
// add original point reference
rows[key].point = point;
2. Highcharts.Chart.prototype.getCSV
// Add point name and header
csv += itemDelimiter;
var point = row['point'];
if (point) {
csv += point.name
} else {
csv += "Country"
}
3. Highcharts.Chart.prototype.getTable (for XLS)
var point = row['point'],
val;
if (point) {
val = point.name;
} else {
val = "Country";
}
html += '<' + tag + ' class="text">' +
(val === undefined ? '' : val) + '</' + tag + '>';
All functions are available in export-data.src.js in this directory: https://github.com/highcharts/highcharts/tree/b4b4221b19a3a50d9ed613b6f50b12f0afcc7d06/js/modules

Related

HighChart - show tooltips for null on hover

I have a null value in my HighCharts line chart. I set connectNulls: true so that the line does not disconnect when the data is null. However I cannot hover over that null value. When I try to, it automatically jumps to the nearest non-null point.
Fiddle: https://jsfiddle.net/wmoxLy4r/2/
What I am trying to do is to:
1/ Allow hovering over null values
2/ When hovering over null values, I would like to show the value of the closest non-null value to the left. In this case it would show 129.2.
I thought about imputing the null value with the closest non-null value to its left but then the plot will be flat at that section due to 2 periods with the same values. I want the plot to looks like it does right now. Appreciate any help
You can preprocess your data and calculate the middle points. A null point doesn't have a marker and it's not possible to show a tooltip for it.
const data = [...];
const processedData = data.map((dataEl, index) => {
if (!dataEl) {
return {
y: (data[index - 1] + data[index + 1]) / 2,
isCalculatedValue: true,
marker: {
fillColor: 'red',
radius: 1
}
}
}
return dataEl;
});
By using tooltip.formatter function, you can show the previous point value in a tooltip.
tooltip: {
formatter: function(tooltip) {
let point = this.point;
if (point.isCalculatedValue) {
point = this.series.points[point.index - 1];
}
return "<span style='font-size: 10px'>" + point.category + "</span><br/>" +
"<span style='color:" + point.series.color +
"'>●</span> Line series: <b>" + point.y + "</b><br/>";
}
}
Live demo: https://jsfiddle.net/BlackLabel/nrmgaw6q/
API Reference: https://api.highcharts.com/highcharts/tooltip.formatter

How to add more attributes in tooltip series in Angular NVD3 line chart

I need to add more attributes in tooltip series in Angular NVD3 line chart, if possible, without modifying the NVD3 source code. I know there are similar posts, but none of them covers this scenario.
Here is my tooltip section in options:
interactiveLayer: {
tooltip: {
contentGenerator: function (d) {
// output is key, value, color, which is the default for tooltips
console.log(JSON.stringify(d.series[0]));
//{"key":"Name","value":1000,"color":"rgba(255,140,0, 1)"}
// and I need more attributes to be added
// into data points, such as label, count, location (see data below)
//{"key":"Name","value":1000,"color":"rgba(255,140,0, 1), "label" : "some label", "count" : 23, "location" : "Paris"}
}
}
}
And here is my data:
$scope.data =
[
{
values: FirstGraphPointsArray,
key: 'Name',
color: 'rgba(255,140,0, 1)'
},
{
values: SecondGraphPointsArray
key: 'City',
color: 'rgba(255,140,0, 1)'
}
]
Finally, the structure of the arrays in data:
FirstGraphPointsArray -> [{ x: xVariable, y: yVariable, label: labelVariable, count: countVariable, location : locationVariable }, {second element...}, {third element...}];
SecondGraphPointsArray -> [a similar array...]
How to get more attributes (label, count, location) from these arrays into the contentGenerator: function (d). As mentioned above, I only receive the default ones from within function parameter (d)
console.log(JSON.stringify(d.series[0]));
//{"key":"Name","value":1000,"color":"rgba(255,140,0, 1)"}
I came up with a solution and wanted to share it, in case someone else comes across the same task. I ended up accessing some of the parameters from d through the default route - function(d), while some of the custom ones - directly from $scope.data.
Important: using the d.index, which indicates the place of the data point in the list is critical hear. This makes sure that for any given index the parameters pulled from the function(d) and those of pulled directly, belong to the same data point (see the code below).
interactiveLayer: {
tooltip: {
contentGenerator: function (d) {
var customTooltipcontent = "<h6 style='font-weight:bold'>" + d.value + "</h6>";
customTooltipcontent += "<table class='custom-tooltip-table'>";
customTooltipcontent += "<tr style='border-bottom: 1px solid green;'><td></td><td>Name</td><td>Value</td><td>Count</td></tr>";
for (var i = 0; i < d.series.length; i++) {
customTooltipcontent += "<tr><td><div style='width:10px; height:10px; background:" + d.series[i].color + "'></div></td><td>" + d.series[i].key + "</td><td>" + d.series[i].value + "</td><td>" + $scope.data[0].values[d.index].count + "</td><td>" + $scope.data[0].values[d.index].location + "</td></tr>"
}
customTooltipcontent += "</table>";
return (customTooltipcontent);
}
}
}

How to get Node Coordinates Data in Sankey Graph

I'm trying to create something similar to the image below. Where each column has a heading with it.
I know that chart.renderer.text can be used to create & place custom text on chart. However, I'm unable to find a way to fetch the column/node coordinates data(x,y) which would help me place them.
Also is there a programmatic way to do this task. For example, a function that fetches all the columns coordinates and populates all the headings from an existing list.
To summarize:
How to fetch a columns (x,y) Coordinates?
How to dynamically place headings for all columns from a list?
Image
You can get the required coordinates and place the headers in render event, for example:
events: {
render: function() {
var chart = this,
series = chart.series[0],
columns = series.nodeColumns,
isFirst,
isLast,
xPos;
if (!series.customHeaders) {
series.customHeaders = [];
}
columns.forEach(function(column, i) {
xPos = column[0].nodeX + chart.plotLeft;
isFirst = i === 0;
isLast = i === columns.length - 1;
if (!series.customHeaders[i]) {
series.customHeaders.push(
chart.renderer.text(
headers[i],
xPos,
80
).attr({
translateX: isFirst ? 0 : (
isLast ?
series.options.nodeWidth :
series.options.nodeWidth / 2
),
align: isFirst ? 'left' : (
isLast ? 'right' : 'center'
)
}).add()
)
} else {
series.customHeaders[i].attr({
x: xPos
});
}
});
}
}
Live demo: https://jsfiddle.net/BlackLabel/6Lvdufbp/
API Reference:
https://api.highcharts.com/highcharts/chart.events.render
https://api.highcharts.com/class-reference/Highcharts.SVGRenderer#text

Highcharts skipping shared tooltip points for large data sets

Seems like Highcharts is skipping a few data points in the shared tooltip for high number of data points (2500+).
I am trying to render a dual axis chart with 2500+ data points for 4 series - using Highcharts. I am also using a shared tooltip option to render my custom tooltip html. But at times Highcharts skips 1 or 2 data points in the tooltip. For example, when I slowly hover over each of the points from left to right, then I am supposed to see '1st April' after '31st March'. But instead, I see '2nd April'. Is it a bug? Or am I missing something? (I have verified that all the dates are present in the categories passed to the Highcharts.)
tooltip: {
borderColor: '#ccc',
backgroundColor: 'transparent',
borderWidth: 0,
shadow: false,
shared: true, //show all series values together
useHTML: true,
// hideDelay: 50000,
formatter: function() {
if (props.config.type == 'pie') {
return 'Value : ' + this.y;
} else {
let html = '<div class="fixed-tooltip">';
html += formatTooltipDate(this.x);
if (this.points &&
this.points.length > 1 &&
props.config.type != "combination") { //multiple series*(see note below)
//*combination series are having 1 point, so handled in the else section as single series.
let dateIndex = props.config.data.categories.indexOf(this.x);
console.log(" date ", this.x);
console.log(" dateIndex ", dateIndex);
if (props.config.type == "dual") {
let dualAxisTitles = props.config.dualAxisTitles;
html += formatDualSeriesTooltipData(this.x, dateIndex, this.points, dualAxisTitles);
} else {
html += formatMultiSeriesTooltipData(this.x, dateIndex, this.points);
}
} else { //single series
//for combination charts have a custom tooltip logic
if (props.config.type == "combination") {
let dateIndex = props.config.data.categories.indexOf(this.x);
html += formatMultiSeriesTooltipData(this.x, dateIndex, props.config.data.series);
} else {
let seriesColor = this.points[0].point.series.color;
let seriesName = this.points[0].point.series.name;
let value = this.points[0].y;
html += formatSingleSeriesTooltipData(value);
}
}
html += '</div>';
return html;
}
}
}
Expected to see a tooltip for "1st April" data point, after "31st March". Instead seeing tooltip for "2nd April" data point.
The points are skipped if there is no enough space for them in the plot area (1px for 1 point). The solution is to set a adequate chart width:
chart: {
width: 1000
},
Live demo: http://jsfiddle.net/BlackLabel/yjk0ta43/
API: https://api.highcharts.com/highstock/chart.width

Separate panes / or style maximum gridlines in Highstock

I built a widget with multiple y Axes very similar to the official sample here: http://www.highcharts.com/stock/demo/candlestick-and-volume
I'm trying to have some sort of visual separation between the chart panes, either by
applying a special style to the maximum grid line for each pane, or
adding a horizontal line in the whitespace between the panes
The only approach i got working is using PlotLines, but I'd rather have a separator that's independent of zoom levels. Any ideas on how to achieve this?
Use Renderer to draw a path between y axes.
function drawSeparator() {
let separator = this.separator;
const options = this.options.chart.separator;
if (options && options.enabled) {
if (!separator) {
this.separator = separator = this.renderer.path({
d: 'M 0 0',
'stroke-width': options.width === undefined ? 1 : options.width,
stroke: options.color || 'black'
}).add();
}
const topAxisBottom = this.yAxis[0].top + this.yAxis[0].height;
const bottomAxisTop = this.yAxis[1].top;
const y = topAxisBottom + (bottomAxisTop - topAxisBottom) / 2;
separator.attr({
d: `M 0 ${y} L ${this.chartWidth} ${y}`
});
}
}
Call the method on load/redraw event
chart: {
events: {
load: drawSeparator,
redraw: drawSeparator
},
separator: {
enabled: true,
width: 3,
color: 'blue'
}
},
You can modify the path's d attribute, the path starts from axis.left and stops on axis.left + axis.width
Live example and output
http://jsfiddle.net/L11uqxgq/

Resources