I have synchronized charts with synch’d zooming. I would like to zoomout in steps (not just back to 1:1). Does anyone have a working example of this? - highcharts

I am currently synchronizing my charts as below:
$('#container').bind('mousemove touchmove', function (e) {
var chart,
point,
points,
i;
for (i = 0; i < Highcharts.charts.length; i++) {
chart = Highcharts.charts[i];
e = chart.pointer.normalize(e); // Find coordinates within the chart
points = [];
Highcharts.each(chart.series, function(series){
point = series.searchPoint(e, true);
if (point) {
points.push(point);
point.onMouseOver(); // Show the hover marker
}
});
if (points.length > 0) {
chart.tooltip.refresh(points); // Show the tooltip
chart.xAxis[0].drawCrosshair(e, points[0]); // Show the crosshair
}
}
});
// ==================================================================================
// * Override the reset function, we don't need to hide the tooltips and crosshairs.
// * Synchronize zooming through the setExtremes event handler.
Highcharts.Pointer.prototype.reset = function () {};
// ====================================================================
function syncExtremes(e) {
var thisChart = this.chart;
if (e.trigger !== 'syncExtremes') { // Prevent feedback loop
Highcharts.each(Highcharts.charts, function (chart) {
if (chart !== thisChart) {
if (chart.xAxis[0].setExtremes) { // It is null while updating
chart.xAxis[0].setExtremes(e.min, e.max, undefined, false, { trigger: 'syncExtremes' });
}
}
});
}
}

You can calculate intermediate extremes and set them by setExtremes method, for example:
document.getElementById('zoomOut').addEventListener('click', function() {
var chart = Highcharts.charts[0],
yAxis = chart.yAxis[0],
xAxis = chart.xAxis[0],
yDistance = (yAxis.max - yAxis.min) / 2,
xDistance = (xAxis.max - xAxis.min) / 2;
yAxis.setExtremes(yAxis.min - yDistance, yAxis.max + yDistance);
xAxis.setExtremes(xAxis.min - xDistance, xAxis.max + xDistance);
});
Live demo: http://jsfiddle.net/BlackLabel/9vxgpn4z/
API Reference: https://api.highcharts.com/class-reference/Highcharts.Axis#setExtremes

Related

Need help in simplifying the code to synchronise tooltips and crosshairs for Highcharts,

I have created a series of synchronised charts, the code to link the crosshair and tooltip is rather complicated:
function syncronizeCrossHairs(chart) {
['mousemove', 'touchmove', 'touchstart'].forEach(function(eventType) {
var container = $(chart.container),
offset = container.offset(),
x;
container[0].addEventListener(eventType,
(function(evt) {
x = evt.clientX - chart.plotLeft - offset.left;
//remove old plot line and draw new plot line (crosshair) for this chart
var xAxis1 = chart1.xAxis[0],
points = [],
points1 = [],
points2 = [],
points3 = [],
e = chart1.pointer.normalize(evt); // Find coordinates within the chart
chart1.series.forEach(s => {
var point = s.searchPoint(e, true)
if (point) {
point.setState();
points.push(point)
}
})
if (points) {
var number = 0;
Highcharts.each(points, function(p, i) {
if (!p.series.visible) {
points.splice(i - number, 1);
number++;
}
})
if (points.length) {
chart1.tooltip.refresh(points); // Show the tooltip
}
}
xAxis1.drawCrosshair(x, points[0])
/*----- second chart ------*/
var xAxis2 = chart2.xAxis[0];
chart2.series.forEach(s => {
var point = s.searchPoint(e, true)
if (point) {
point.setState();
points1.push(point)
}
})
if (points1[0]) {
var number = 0;
Highcharts.each(points1, function(p, i) {
if (!p.series.visible) {
points1.splice(i - number, 1);
number++;
}
})
if (points1.length) {
chart2.tooltip.refresh(points1); // Show the tooltip
}
}
xAxis2.drawCrosshair(x, points1[0])
/*----- third chart ------*/
var xAxis3 = chart3.xAxis[0];
chart3.series.forEach(s => {
var point = s.searchPoint(e, true)
if (point) {
point.setState();
points2.push(point)
}
console.log(points2)
})
if (points2[0]) {
var number = 0;
Highcharts.each(points1, function(p, i) {
if (!p.series.visible) {
points2.splice(i - number, 1);
number++;
}
})
if (points2.length) {
chart3.tooltip.refresh(points2); // Show the tooltip
}
}
xAxis3.drawCrosshair(x, points2[0])
/* ----- fourth chart ------ */
var xAxis4 = chart4.xAxis[0];
chart4.series.forEach(s => {
var point = s.searchPoint(e, true)
if (point) {
point.setState();
points3.push(point)
}
})
if (points3[0]) {
var number = 0;
Highcharts.each(points3, function(p, i) {
if (!p.series.visible) {
points3.splice(i - number, 1);
number++;
}
})
if (points3.length) {
chart4.tooltip.refresh(points3); // Show the tooltip
}
}
xAxis4.drawCrosshair(x, points3[0])
}))
})
}
How ever I have found a better example here: http://jsfiddle.net/mushigh/a3kjrz6u/
$('#container').bind('mousemove touchmove touchstart', function(e) {
var chart,
point,
i,
event;
for (i = 0; i < Highcharts.charts.length; i = i + 1) {
chart = Highcharts.charts[i];
event = chart.pointer.normalize(e.originalEvent); // Find coordinates within the chart
point = chart.series[0].searchPoint(event, true); // Get the hovered point
if (point) {
point.onMouseOver(); // Show the hover marker
chart.tooltip.refresh(point); // Show the tooltip
chart.xAxis[0].drawCrosshair(event, point); // Show the crosshair
}
}
});
/**
* Override the reset function, we don't need to hide the tooltips and crosshairs.
*/
Highcharts.Pointer.prototype.reset = function() {
return undefined;
};
How do I adapt my code here: https://jsfiddle.net/ashenshugar/716jx4n9/
To use the simplified code in my example
I don't think that the demo which you have found is a good approach to your requirements.
The demo needs to the specific data structure, like: https://github.com/highcharts/highcharts/blob/master/samples/data/activity.json
The demo is an example to show tooltip for only one series per chart, meanwhile, you have got a few and a shared tooltip, so finally, you will need to do the same calculations to get the array of points for shared tooltip.
Except that, I think that a better approach is to clean up your code.
Notice that the functionalities to calculate points for each chart are similar and can be paste into the loop:
function syncronizeCrossHairs(chart) {
['mousemove', 'touchmove', 'touchstart'].forEach(function(eventType) {
var container = $(chart.container),
offset = container.offset(),
x;
container[0].addEventListener(eventType,
(function(evt) {
x = evt.clientX - chart.plotLeft - offset.left;
Highcharts.charts.forEach(ch => {
var e = ch.pointer.normalize(evt), // Find coordinates within the chart
points = [];
ch.series.forEach(s => {
var point = s.searchPoint(e, true);
if (point) {
point.setState();
points.push(point)
}
})
if (points) {
var number = 0;
Highcharts.each(points, function(p, i) {
if (!p.series.visible) {
points.splice(i - number, 1);
number++;
}
})
if (points.length) {
ch.tooltip.refresh(points); // Show the tooltip
}
}
ch.xAxis[0].drawCrosshair(x, points[0])
})
}))
})
}
And notice that also your afterSetextreme callback can be change to trigger this function:
function setExtremes(chart, min, max) {
Highcharts.charts.forEach(ch => {
if (ch !== chart) {
ch.xAxis[0].setExtremes(min, max)
}
})
}
Also, you can define options which are common for each chart, like it is done here: https://jsfiddle.net/gh/get/library/pure/highcharts/highcharts/tree/master/samples/highcharts/demo/gauge-solid/
Finally demo: https://jsfiddle.net/BlackLabel/hv7azdgm/

Highcharts - sync crosshair of charts with different width

I try to get a synced crosshair for multiple highcharts each with a different width.
For know the crosshair is syncing on the position of the cursor and not on the position of the point / the xAxis value (which would be prefered). Can anyone give me a hint how to achieve this?
I have changed the synced charts example in the following fiddle:
https://jsfiddle.net/3mn4x8uy/
The chart creation:
$.each(activity.datasets, function (i, dataset) {
// Add X values
dataset.data = Highcharts.map(dataset.data, function (val, j) {
return [activity.xData[j], val];
});
$('<div class="chart">')
.appendTo('#container')
.highcharts({
chart: {
marginLeft: 40+i*100, // make different width for each chart
spacingTop: 20,
spacingBottom: 20
},
sync code
$('#container').bind('mousemove touchmove touchstart', function (e) {
var chart,
point,
i,
event;
for (i = 0; i < Highcharts.charts.length; i = i + 1) {
chart = Highcharts.charts[i];
// Find coordinates within the chart
event = chart.pointer.normalize(e.originalEvent);
// Get the hovered point
point = chart.series[0].searchPoint(event, true);
if (point) {
point.highlight(e);
}
}
});
Thanks
Here you can find an example how to synchronize multiple charts based on xAxis value: http://jsfiddle.net/BlackLabel/udtkgs9m/
The code to sync a Plotline over multiple Charts:
function syncronizeCrossHairs(chart) {
var container = jQuery(chart.container),
offset = container.offset(),
x, y;
container.mousemove(function(evt) {
x = evt.clientX - chart.plotLeft - offset.left;
y = evt.clientY - chart.plotTop - offset.top;
var val = chart.xAxis[0].translate(x, true);
Highcharts.each(Highcharts.charts, function (act_chart) {
var xAxis = act_chart.xAxis[0];
xAxis.removePlotLine("myPlotLineId");
xAxis.addPlotLine({
value: val,
width: 1,
color: 'red',
//dashStyle: 'dash',
id: "myPlotLineId"
});
});
});
}

Synchronized charts example with Highstock crashes with "Cannot read property 'category' of undefined"

I want the functionality of the "Synchronized charts" example, but with Highstock. But when trying to accomplish this, I get "highstock.src.js:9991 Uncaught TypeError: Cannot read property 'category' of undefined"
This also holds directly for the example: http://www.highcharts.com/demo/synchronized-charts doesn't work when converted to Highstock: http://jsfiddle.net/9gq47g0w/
(Since StackOverflow demands me to post some code along with the fiddle, here's from Highstock, noting the point where it crashes with **):
/**
* Refresh the tooltip's text and position.
* #param {Object} point
*/
refresh: function (point, mouseEvent) {
...
// shared tooltip, array is sent over
if (shared && !(point.series && point.series.noSharedTooltip)) {
...
textConfig = {
x: ** point[0].category, ** <- here!
y: point[0].y
};
...
}
...
},
Here you can find an example of synchronized highstock charts:
http://jsfiddle.net/vw77cooj/20/
This example is using custom functions for synchronizing extremes and tooltips on charts:
function syncExtremes(e) {
var thisChart = this.chart;
if (e.trigger !== 'syncExtremes') { // Prevent feedback loop
Highcharts.each(Highcharts.charts, function(chart) {
if (chart !== thisChart) {
if (chart.xAxis[0].setExtremes) { // It is null while updating
chart.xAxis[0].setExtremes(e.min, e.max, undefined, false, {
trigger: 'syncExtremes'
});
}
}
});
}
}
$('#container').bind('mousemove touchmove touchstart', function(e) {
Highcharts.each(Highcharts.charts, function(chart) {
event = chart.pointer.normalize(e.originalEvent);
point = chart.series[0].searchPoint(event, true);
if (point) {
point.onMouseOver(); // Show the hover marker
chart.tooltip.refresh(point); // Show the tooltip
chart.xAxis[0].drawCrosshair(event, point); // Show the crosshair
}
});
});
In case of having multiple series on your chart you may change function responsible for synchronizing your tooltip:
function syncTooltip(container, p) {
var i = 0,
j = 0,
data,
series,
points = [];
for (; i < chartSummary.length; i++) {
if (container.id != chartSummary[i].container.id) {
series = chartSummary[i].series
Highcharts.each(series, function(s) {
Highcharts.each(s.data, function(point) {
if (point.x === p && point.series.yAxis.options.index !== 1) {
points.push(point)
}
})
});
chartSummary[i].tooltip.refresh(points);
}
};
}
http://jsfiddle.net/ZArZM/316/
For a nice working example on Highstock, please follow this example with MouseOver and MouseOut:
var onMouseOver = function onMouseOver() {
var x = this.x,
interactedChart = this.series.chart,
points = [],
charts = Highcharts.charts,
each = Highcharts.each;
each(charts, function(chart) {
if (chart !== interactedChart) {
each(chart.series, function(series) {
each(series.data, function(point) {
if (point.x === x && point.series.yAxis.options.index !== 1) {
points.push(point)
}
})
});
each(points, function(p) {
p.setState('hover');
});
chart.tooltip.refresh(points);
}
});
}
http://jsfiddle.net/ska1r5wq/
You can avoid many tooltip issues from examples provided on the web.
Source : https://github.com/highcharts/highcharts/issues/6595

Highcharts large data set clustering

I have tens of thousands (possibly hundreds of thousands) of points that I need plotted with Highcharts. Is there a way where I can cluster the data on the server, so it will show less than 1000 points, but as you zoom in it will make AJAX calls to the server to get the data for that zoomed region (it would probably run through the same cluster algorithm). How would this interface with the Highcharts API?
There is a highstock demo that does this http://www.highcharts.com/stock/demo/lazy-loading.
But you can do the same thing with highcharts http://jsfiddle.net/RHkgr/
The important bit is the afterSetExtremes function
...
xAxis : {
events : {
afterSetExtremes : afterSetExtremes
},
...
/**
* Load new data depending on the selected min and max
*/
function afterSetExtremes(e) {
var url,
currentExtremes = this.getExtremes(),
range = e.max - e.min;
var chart = $('#container').highcharts();
chart.showLoading('Loading data from server...');
$.getJSON('http://www.highcharts.com/samples/data/from-sql.php?start='+ Math.round(e.min) +
'&end='+ Math.round(e.max) +'&callback=?', function(data) {
chart.series[0].setData(data);
chart.hideLoading();
});
}
Here is an improvement for Barbara's answer,
It registers to the setExtremes event,
to know if this is a reset zoom event.
If it is - it gets the entire dataset,
thus allowing reset zoom to work correctly.
It also allows zooming in both x and y.
http://jsfiddle.net/DktpS/8/
var isReset = false;
...
xAxis: {
events: {
afterSetExtremes : afterSetExtremes,
setExtremes: function (e) {
if (e.max == null || e.min == null) {
isReset = true;
}
else
{
isReset = false;
}
}
},
minRange: 3600 * 1000 // one hour
},
series: [{
data: data,
dataGrouping: {
enabled: false
}
}]
});
});
});
/**
* Load new data depending on the selected min and max
*/
function afterSetExtremes(e) {
var url,
currentExtremes = this.getExtremes(),
range = e.max - e.min;
var chart = $('#container').highcharts();
var min = 0;
var max = 1.35e12;
if(!isReset)
{
min = e.min;
max = e.max;
}
chart.showLoading('Loading data from server...');
$.getJSON('http://www.highcharts.com/samples/data/from-sql.php?start=' + Math.round(min) +
'&end=' + Math.round(max) + '&callback=?', function (data) {
chart.series[0].setData(data);
chart.hideLoading();
});
}
In case when you will not have a limit of points, you can increase turboThreshold paramter.

Showing Tool-tip & Cross-hair on two independent graphs from a single hover event?

Good day,
We are trying to develop a report which contains two charts. Each chart shows different metrics on the same timescale (unix timestamp). The aim is to show the tooltip & cross hair on both charts regardless of which you hover over.
In my head I would like to get the xAxis timestamp (time) from the tooltip hover event on chart A. I would then fetch the associated series on chart B and trigger the tooltip refresh event.
As it stands, it looks like highcharts only accepts points.
I've added a mouseOver/Out event to both charts. This obtains the hoverPoints
plotOptions: {
series: {
point: {
events: {
mouseOver: function () {
$el.trigger('tooltip-open', {"$sourceEl": $el[0], "point": this});
},
mouseOut: function () {
$el.trigger('tooltip-close', {"$sourceEl": $el[0], "point": this});
}
}
}
}
}
// Where $el is the highcharts container
// ..and tool-tip-XXXXX is a custom jQuery event.
My temporary work around is:
$el.bind('tooltip-open', function (e, data) {
var $el = $(this);
if (data.$sourceEl.id == $el.id) {
return;
}
var chart = $el.highcharts();
var points = $(chart.series).map(function () {
return this.points
}).get();
var pointsForTimestamp = $.grep(points, function (p) {
return p.x == data.point.x;
});
$(pointsForTimestamp).each(function () {
this.setState('hover');
});
chart.tooltip.refresh(pointsForTimestamp);
});
$el.bind('tooltip-close', function (e, data) {
var $el = $(this);
if (data.$sourceEl.id == this.id) {
return;
}
var chart = $el.highcharts();
chart.tooltip.hide();
});
var linkGraphs = function ($elA, $elB) {
function isEventHandled(data){
data.count = data.count || 0;
if(data.count > 0){
return true
}else{
data.count++;
return false;
}
}
function bindToOpen($elA, $elB) {
$elA.bind('tooltip-open', function (e, data) {
if(!isEventHandled(data)){
$elB.trigger('tooltip-open', data);
}
});
}
function bindToClose($elA, $elB) {
$elA.bind('tooltip-close', function (e, data) {
if(!isEventHandled(data)){
$elB.trigger('tooltip-close', data);
}
});
}
bindToOpen($elA, $elB);
bindToOpen($elB, $elA);
bindToClose($elA, $elB);
bindToClose($elB, $elA);
};

Resources