Shared Crosshair Highlighting /w Non-sequential Series - highcharts

I have two lines of date data. One line plots a point sequentially every day. The other line plots non-sequental points that fall on one of the days of the previous line.
The problem is that the crosshair functionality behaves weirdly when I do this. See the example linked below. You will notice that although the tooltip and point highlighting shows for the proper point, the crosshair gets stuck on the non-sequential point along the x-axis, even though this is not the current position of my mouse on the chart. The exception to this is when you hover your mouse directly across the line. The crosshair follows the mouse properly then.
See example here: http://jsfiddle.net/2H9m3/1/
$(function () {
$('#container').highcharts({
tooltip: {
shared: true,
crosshairs: true
},
xAxis: {
type: "datetime", maxPadding: 0, minPadding: 0
},
yAxis: {
min: 0
},
series: [{
name: 'Line',
data: GenerateData(10, 50)
}, {
name: "Events",
color: "#EF4B68",
lineWidth: 0,
marker: {
enabled: true,
symbol: "triangle",
radius: 6
},
data: [
[5 * 86400000, 0],
[21 * 86400000, 0]
]
}]
});
});
function GenerateData(min, max) {
var data = [];
for (i = 0; i < (30 * 86400000); i = i + 86400000) {
data.push([i, GetRandomInt(min, max)]);
}
return data;
}
function GetRandomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
I am not sure if this is a bug or something that I am doing wrong.

The issue here is subtle. It is actually caused by the fact that the tooltip is linked over the top-most series. Since you are creating your linear series first then your non-sequential series the top-most series is the 2 points. So, in order to recapture the 'tooltip` and your crosshair you have to hover closer to the series that is behind the non-sequential series.
Fixes:
Make the non-sequential series the first one you add.
Give an index value to whichever series you want on top that is larger than the least-full series.
I went with option 2:
series: [{
name: 'Line',
index: 1,
data: GenerateData(10, 50)
}, {
name: "Events",
index: 0,
color: "#EF4B68",
lineWidth: 0,
marker: {
enabled: true,
symbol: "triangle",
radius: 6
},
data: [
[5 * 86400000, 0],
[21 * 86400000, 0]
]
}]
Example here.

Looks like a bug in new crosshairs core.
Reported to devs: https://github.com/highslide-software/highcharts.com/issues/2816
Thanks!

Related

Highcharts / Highstock scroll default position

I have a HighStock charge with a scrollbar very similar to this one
http://jsfiddle.net/gh/get/jquery/3.1.1/highslide-software/highcharts.com/tree/master/samples/stock/demo/navigator-disabled/
Highcharts.stockChart('container', {
rangeSelector: {
selected: 1
},
title: {
text: 'AAPL Stock Price'
},
navigator: {
enabled: false
},
series: [{
name: 'AAPL Stock Price',
data: data,
tooltip: {
valueDecimals: 2
}
}]
});
I want the default scroll position to be somewhere in the middle of the chart though, is there any way to do this?
You can do this with xAxis.setExtremes on the chart.events.load call. A simple example (using arbitrary 1 year range):
chart: {
events: {
load: function() {
this.xAxis[0].setExtremes(
Date.UTC(2013, 0, 1),
Date.UTC(2013, 11, 31)
);
}
}
},
Example jsFiddle.
If you need to determine what your "middle" is it can depend on your data source where you could pre-calculate it or you could do it also on the chart.events.load call.
For fun I added the mid-point calculations. This uses moment.js because it is much easier than rolling your own time methods.
minX = data[0][0];
maxX = data[data.length - 1][0];
midX = minX + ((maxX - minX) / 2);
var midDate = new Date(midX);
var startDate = moment(midDate);
var endDateMoment = moment(midDate);
startDate.subtract(1.5, 'months');
endDateMoment.add(1.5, 'months');
Note that I am addinng/subtracting 1.5 months to make your 3 month range selector valid. The moment.js library rounds those up to 2 months. You get the idea though. So your setExtremes becomes:
this.xAxis[0].setExtremes(
startDate.valueOf(), //.valueOf() in this context converts to javascript time
endDateMoment.valueOf()
);
And here is live jsFiddle.

Highcharts display priority of overlapping labels

When data labels overlap in Highcharts, only one data label is displayed. This seems to be handled randomly. See fiddle:
http://jsfiddle.net/lamarant/rmxLd1d4/
$(function () {
$('#container').highcharts({
title: {
text: 'Label Test'
},
series: [{
type: 'line',
data: [ 10, 10, 10, 10, 10, 10, 50],
dataLabels: {
enabled: true,
color: 'blue',
zIndex: 10
},
zIndex: 10
},
{
type: 'line',
data: [ 11, 11, 11, 11, 11, 11, 49],
dataLabels: {
enabled: true,
color: 'red',
zIndex: 10
},
zIndex: 20
}]
});
});
Notice that the first data label displayed in the chart is from the second series while the rest are from the first series.
I tried setting the priority of the label display using zIndex in both series and series.dataLabel with no luck.
Is there any way to set it so that a designated series always takes priority when Highcharts determines which label to display?
One possible fix is supplying a labelrank for each point, which is used to evaluate their sorting order. If you supply this rank for each point in a series, that series should be considered "above" series without such a rank (or with a lower rank integer value).
In code, manually for each point:
series: [{
data: [
{ y: 11, labelrank: 1 },
{ y:11, labelrank: 1 }
// ...
],
// ...
}]
In code, using the callback function (JSFiddle):
$('#container').highcharts({
// Options ...
}, function() {
var mySeriesIndex = 1;
// For each point in the preferred series
for(i = 0; i < this.series[mySeriesIndex].points.length; i++)
// Set a labelrank
this.series[mySeriesIndex].points[i].labelrank = 1;
});
My current take on the issue itself is this:
With the current implementation of the overlap logic it seems to me that this is a bit of an unintended behavior. The source code uses Array.sort in a way that shifts the positions of the point labels, since Array.sort is not guaranteed to be stable (same-value items wont retain their original order).
If you check your example in Firefox it should work as intended (their implementation of Array.sort is different), while in Chrome it doesn't (not stable). You could hope that this little feature is fixed.

How to force points above maximum to be on maximum grid line, but still show real value in tooltip

I'm drawing a really simple line chart with hours slept each day. So hours are on the y axis and dates on the x axis. The y axis goes from 0 to 23, but interesting values are usually between 3 and 12, so I only want to draw these values. This is easy, just define min and max on yaxis.
But now highcharts will still draw values above and below the min and max y valus. What I want is to have it just draw values above 12 on the 12 max grid line, and show the real value in the tooltip, and the inverse for values below 3.
Illustration of problem: https://jsfiddle.net/HhP39/10/
$(function () {
$('#container').highcharts({
chart: {},
yAxis: {
title: {
text: 'Hours'
},
min: 3,
max: 12,
tickInterval: 1
},
series: [{
data: [2,6,13,4,9,12,6,8,1,15,9,7,5,8,14,5,8,2,3]
}]
});
});
As you can see, values just out of range, such as 2, will still produce a tooltip if you hover over where they are. I want all values like these to move to be on the min and max lines.
Anybody got any ideas?
Take a look at this update to your fiddle: https://jsfiddle.net/HhP39/7/
It's creating an array of the original values and then an array of the adjusted values. The graph actually charts the adjusted values but the tooltip pulls from the original values.
data = [2,6,13,4,9,12,6,8,1,15,9,7,5,8,14,5,8,2,3];
series = [];
tooltips = [];
for (var i in data) {
tooltips.push(data[i]);
series.push( Math.max(3, Math.min(data[i], 12)) );
}
And then chart it with:
$('#container').highcharts({
chart: {},
yAxis: {
title: {
text: 'Hours'
},
min: 3,
max: 12,
tickInterval: 1
},
tooltip: {
formatter: function() {
return ('Real value: ' + data[this.x]);
}
},
series: [{
data: series
}]
});

Highcharts with inverted y-axis, but not inverted data?

Is it possible to invert the y-axis, but not the data? I have a ranking which should start with 1 at the top, not at the bottom, but the columns should stay just normal.
Thanks for any hints!
You can use reversed yAxis, and column series, where each point will have y and low values, where low values are the same as the lowest number. See: http://jsfiddle.net/JVNjs/303/
var max = 10;
var chart = new Highcharts.Chart({
chart: {
renderTo: 'container'
},
title: {
text: 'Chart Title'
},
credits: {
enabled: false
},
yAxis: {
min: 0,
max: max,
tickInterval: 2,
reversed: true
},
series: [{
type: 'column',
data: [{
y: 1,
low: max
}, {
y: 3,
low: max
}, {
y: 6,
low: max
}]
}]
There is no good easy direct way to do it (afaik).
There are a variety of approaches you could take, however.
This example uses a reversed axis and stacked columns:
http://jsfiddle.net/jlbriggs/JVNjs/301/
stacking:'normal',
It relies on adding a second series that is the difference between your value and the max, which means you must also specify a y axis max.
I left the second series a light grey so you can see them, but you can set their opacity to 0 so that they are invisible.
Other options might include doing some calculations on the y axis label formatter to display the axis labels as you want without having to touch the data.
Highcharts provides an option for this called reversed setting it true will do the work for you.
http://api.highcharts.com/highcharts#yAxis.reversed
reversed: true
here is the jsfiddle
http://jsfiddle.net/bmbhD/

Possible to use xAxis with type "datetime" and yAxis with categories?

I have a very simple example using Highcharts which uses "datetime" on one axis and categories on the other. It renders with no points and does not show the categories labels at all. I'm wondering now if you can't use that combination of types. Here is the code:
var chart = new Highcharts.Chart({
chart: {
renderTo: 'container'
},
xAxis: {
type: 'datetime'
},
yAxis: {
categories: ['p1', 'p2']
},
series: [{
type: 'scatter',
data: [
{
name: 'Deliv1',
x: Date.UTC(2011,0,1),
y: 'p1'
},
{
name: 'Deliv2',
x: Date.UTC(2012,0,1),
y: 'p2'
}
]
}]
});
The answer to my problem was given on the highcharts forum. I thought I'd report back here what the solution was. I was errantly using y: 'p1' and y: 'p2' for values on the points. The y values actually are the indexes of the categories. Here is the updated code which works:
data: [
{
name: 'Deliv1',
x: Date.UTC(2011,0,1),
y: 0
},
{
name: 'Deliv2',
x: Date.UTC(2012,0,1),
y: 1
}
]
It's possible but you'll need to pretend the y values are numeric.
Probably by having an array with the actual Y-value and a number (maybe index) then the point's y value to the number and for the y-axis settings add the label's formatter to return the actual y-value based on the value.
You'll also need to adjust the min, max, interval and if you're using tooltips add a similar formatter to get the y-value.
(If I get more time, I'll try to create an example).

Resources