I have an areaspline with only one series. The design calls for drawing x-axis gridlines that touch the series and don't extend beyond that. Is it possible to do this? Here's my code to configure the gridlines:
xAxis: {
gridLineWidth: 1,
tickAmount: 30,
lineWidth: 0,
labels: {
enabled: false
},
minPadding: 0
},
Another option (other then 2 mentioned by jlbriggs in a comment on the question) might be to extend Highcharts and change paths of grid lines in a wrapper like:
(function(H) {
var UNDEFINED;
H.wrap(H.Tick.prototype, 'render', function(p) {
p.apply(this, [].slice.call(arguments, 1)); //run original function
if (this.axis.isXAxis && this.gridLine) {
var point = this.axis.series[0].options.data[this.pos],
d = this.gridLine.attr('d').split(' ');
if (point !== UNDEFINED) {
d[2] = this.axis.chart.yAxis[0].toPixels(point);
d = d.join(' ');
} else {
d = ''; //remove if not crossing any point
}
this.gridLine.attr({
d: d
});
}
});
})(Highcharts)
Example: http://jsfiddle.net/69f5z3us/
If a grid line is not crossing any point, then the grid line is removed, but you could change that part of the code if you want to.
Related
I am trying to create a stock performance chart, very similar to this chart:
https://www.orbis.com/bm/private-client/funds/global-equity
The idea is that the y-axis always starts at 10,000 and moves from there. When the navigator range changes, the top area is redrawn by applying a factor to all the data points. And even though the chart changes, the navigator area does not change.
Any ideas on how to do this with highstock?
Thanks to #ppotaczek for the answer. It was simpler than I was trying to make it. Simplicity is genius.
This is the solution
const baseData = [100.00, 99.78, 96.50, 96.61, 97.57, 96.02, 96.42, 99.17, 100.35, 101.30, 100.92, 99.63, 97.90, 100.30, 91.66, 94.61, 95.36, 94.89, 95.43, 95.25, 96.70, 93.58, 92.53];
[100, 100.95, 100.57, 99.28, 97.56, 99.95, 91.34, 94.28, 95.03, 94.56, 95.1, 94.92]
Highcharts.stockChart('container', {
series: [{
data: baseData.slice()
}],
xAxis: {
events: {
afterSetExtremes: function(e) {
const min = e.min;
const max = e.max;
const visibleData = baseData.filter(
(dataEl, index) => index > min && index <= max + 1
);
factor = 100.00/visibleData[0];
const finalData = [];
visibleData.forEach((dataEl, index) => {
finalData.push([min + index, dataEl*factor]);
});
this.series[0].setData(finalData);
}
},
minRange: 1
},
navigator: {
adaptToUpdatedData: false
}
});
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/
I have a highcharts scatter plot for which I'm trying to fetch the x and y tickPositions in freemarker template, specifically the first and last one. Such as that I would get something as [-10,-10] (bottom left corner) at the intersection of the x-y axis and [30,40] (top right corner) at the intersection of the opposite sides, where xAxis ticks are [-10,0,10,20,30] and yAxis ticks are [-10,0,10,20,30,40]
I want these points so that I'll be able to plot a diagonal line across the scatter plot from bottom lower corner to top right corner. The line series should look like:
series: [
{
type : 'line',
<#--diagonal line-->
data :[[-10,-10], [30,40]], // to be calculated dynamically
lineWidth: 0.5,
marker : {
enabled : false
}
},
{
color: 'rgb(0,85,152)',
data: [[2,3],[6,7],[8,9]]
}
]
The problem at present is I'm unable to get [-10,-10], [30,40] data points. Is it even possible is what I'm wondering. Any help is much appreciated!
You have the getExtremes() function on an axis.
For example:
var extremes = $('#container').highcharts().yAxis[0].getExtremes();
Here is the doc, and here is a demo fiddle
Is this what you were trying to achieve ?
Edit
After your fiddle example, I understand better your need.
Here is the updated fiddle
var chart = $('#container').highcharts();
var extremeY = chart.yAxis[0].getExtremes();
var extremeX = chart.xAxis[0].getExtremes();
var lineSeries = {
type: 'line',
data: [
[extremeX.min, extremeY.min],
[extremeX.max, extremeY.max]
],
lineWidth: 0.5,
lineColor: 'rgb(0,0,0)',
marker: {
enabled: false
}
};
chart.addSeries(lineSeries);
I created an object with the properties of the line series. And using min and max (not dataMin and dataMax) properties from the object returned by getExtremes() you obtain the desired result.
Edit 2
You could put this code in the load event of the chart. It is a callback called after the chart finished loading. And here you can use this to refer to the chart :
$('#container').highcharts({
chart: {
events: {
load: function() {
var extremeY = this.yAxis[0].getExtremes();
var extremeX = this.xAxis[0].getExtremes();
var lineSeries = {
type: 'line',
data: [
[extremeX.min, extremeY.min],
[extremeX.max, extremeY.max]
],
lineWidth: 0.5,
lineColor: 'rgb(0,0,0)',
marker: {
enabled: false
}
};
this.addSeries(lineSeries);
}
},
//...
});
Here is the new updated fiddle
Since you need only a diagonal path, then you could add it using renderer
Example: http://jsfiddle.net/cr7gq4st/
function diagonal() {
var chart = this,
ren = chart.renderer,
diag = chart.diag,
attrs = {
'stroke-width': 0.5,
stroke: '#000',
zIndex: 1
},
topR = {
x: chart.plotLeft + chart.plotWidth,
y: chart.plotTop
},
bottomL = {
x: chart.plotLeft,
y: chart.plotTop + chart.plotHeight
},
d = 'M ' + bottomL.x + ' ' + bottomL.y + ' L ' + topR.x + ' ' + topR.y;
if( !diag ) { //if doesn't exist, then create
chart.diag = ren.path().attr(attrs).add();
}
chart.diag.attr({d:d});
}
I'm trying to set the min zoom (max range) of my chart. Basically I'm trying to do the opposite of the minRange property. I'm struggling for a while with this problem. I have a "solution", but I don't like it, this solution allow the user to choose a range greater then the "max range", and immediately correct it.
POSSIBLE SOLUTION
$(function() {
var lastMin;
var lastMax;
var maxRange = 12 * 30 * 24 * 3600 * 1000; //12 month
$('#container').highcharts('StockChart', {
scrollbar: {
liveRedraw: false
},
xAxis: {
events: {
afterSetExtremes: function(e) {
var max = this.max,
min = this.min;
if (lastMin && lastMax) {
if(max-min > maxRange) {
if (min < lastMin) {
min = max - maxRange;
} else {
max = min + maxRange;
}
}
}
var x = this;
setTimeout(function(){
x.setExtremes(min,max); //chart xAxis
}, 1);
lastMin = min;
lastMax = max;
}
}
},
rangeSelector: {
selected: 1
},
series: [{
name: 'USD to EUR',
data: usdeur
}]
});
});
I want to block the user from choosing a range greater than the allowed, in other words, block the navigator when it's too big
I'm also following this issue, I tried all the proposed solution, but I'm having errors ("Uncaught ReferenceError: Highcharts is not defined")
Thanks Sebastian!
I managed to find a solution (fiddle) wrapping the "render" function. Doing that I managed to really set a "min zoom" on the navigator bar.
$(function() {
var lastX0;
var lastX1;
var maxRange = 100; //100 pixels
(function (H) {
H.wrap(H.Scroller.prototype, 'render', function (proceed) {
console.log(arguments)
if(arguments[4] - arguments[3] > maxRange + 2) {
if (arguments[3] < lastX0) {
arguments[3] = lastX0;
} else {
arguments[4] = lastX1;
}
}
proceed.apply(this, [].slice.call(arguments, 1));
lastX0 = arguments[3];
lastX1 = arguments[4];
});
}(Highcharts));
$('#container').highcharts('StockChart', {
scrollbar: {
liveRedraw: true
},
series: [{
name: 'USD to EUR',
data: usdeur
}]
});
var highchart = $('#container').highcharts();
var extremes = highchart.xAxis[0].getExtremes();
var rangeTotal = extremes.max - extremes.min;
var f = maxRange / $('#container').width();
highchart.xAxis[0].setExtremes(extremes.max - (f * rangeTotal), extremes.max);
});
In the sample code I'm used a fixed amount of pixels, but in my real application i'm making it dynamic. I making this, because I can't use the data grouping property in the software that I'm working, and since the minimum size of a bar in a chart is 1 pixel (obviously) highcharts hide some bars (or points).
I'm setting the minimum zoom so all bar in the displayed range are visible , since the user can't display a higher range in the x Axis the "hidden" bar "problem" (is an awesome feature, but I can't make use of it) won't happen
I have a line chart which dynamically updates. I want to add a marker at the last point. As the chart scrolls and new points are added, I want to remove the previous marker and add it to the most recent point. Thus the "tip" of this line chart will only have a marker.
Any help would be greatly appreciated.
You can disable markers in the plotOptions, and then for the point that you want the marker to appear on, set the data as an object, like this:
http://jsfiddle.net/jlbriggs/JVNjs/281/
data: [7,12,16,32,{y:64,marker:{enabled:true,radius:5,fillColor:'#c00'}}]
You can disable marker for each point except the last one.
On start:
series: [{
name: 'Random data',
data: (function () {
// generate an array of random data
var data = [],
time = (new Date()).getTime(),
i;
for (i = -19; i <= 0; i += 1) {
data.push({
x: time + i * 1000,
y: Math.random(),
marker: {
enabled: i === 0
}
});
}
return data;
}())
}]
And disabling marker in the last point before the new point is added:
events: {
load: function () {
// set up the updating of the chart each second
var series = this.series[0];
setInterval(function () {
var x = (new Date()).getTime(), // current time
y = Math.random();
series.data[series.data.length - 1].update({
marker: { enabled: false }
}, false)
series.addPoint([x, y], true, true);
}, 1000);
}
}
You could try this.
http://jsfiddle.net/687215jj/2/