Highcharts: Change scale sizes - javascript

Let´s say you have an x-axis that goes [0, 3, 6, ...] and a y-axis that is like [0, 5, 10, ...].
Highcharts handles those values so that automatically, somehow a difference of 5 in y direction does not look bigger than a difference of 3 in x direction.
How can you change the distances between the values / make a 5 on the y axis appear as big as 5/3 of the change on the x axis? (so that p.e. a line from (0,0) to point (5,5) has a 45° angle)
Code example:
$.getJSON('https://cdn.jsdelivr.net/gh/highcharts/highcharts#v7.0.0/samples/data/usdeur.json', function (data) {
Highcharts.chart('container', {
chart: {
zoomType: 'x'
},
title: {
text: 'USD to EUR exchange rate over time'
},
subtitle: {
text: document.ontouchstart === undefined ? 'Click and drag in the plot area to zoom in' : 'Pinch the chart to zoom in'
},
xAxis: {
type: 'datetime'
},
yAxis: {
title: {
text: 'Exchange rate'
}
},
legend: {
enabled: false
},
plotOptions: {
area: {
fillColor: {
linearGradient: {
x1: 0,
y1: 0,
x2: 0,
y2: 1
},
stops: [
[0, Highcharts.getOptions().colors[0]],
[1, Highcharts.Color(Highcharts.getOptions().colors[0]).setOpacity(0).get('rgba')]
]
},
marker: {
radius: 2
},
lineWidth: 1,
states: {
hover: {
lineWidth: 1
}
},
threshold: null
}
},
series: [{
type: 'area',
name: 'USD to EUR',
data: data
}]
});
});
taken from demo

In the load event, you can calculate and adjust the height or width of the chart:
chart: {
events: {
load: function() {
var xAxis = this.xAxis[0],
yAxis = this.yAxis[0];
// Adjust xAxis
this.setSize(
yAxis.height / (yAxis.max - yAxis.min) *
(xAxis.max - xAxis.min) + this.plotLeft + this.chartWidth -
(this.plotLeft + this.plotWidth),
null,
false
);
}
}
},
Live demo: http://jsfiddle.net/BlackLabel/64Lxutce/
or if you do not want to change the size, you can adjust one of the axis extremes:
chart: {
events: {
load: function() {
var xAxis = this.xAxis[0],
yAxis = this.yAxis[0],
xAxisMax = xAxis.width /
(yAxis.height / (yAxis.max - yAxis.min)),
yAxisMax = yAxis.height /
(xAxis.width / (xAxis.max - xAxis.min));
if (xAxisMax < xAxis.max) {
this.update({
yAxis: {
max: yAxisMax - yAxis.min
}
}, true, true, false);
} else {
this.update({
xAxis: {
max: xAxisMax - xAxis.min
}
}, true, true, false);
}
}
}
},
Live demo: http://jsfiddle.net/BlackLabel/w3byrL28/
API Reference:
https://api.highcharts.com/highcharts/chart.events.load
https://api.highcharts.com/class-reference/Highcharts.Chart#update
https://api.highcharts.com/class-reference/Highcharts.Chart#setSize

Related

Two synchronized vertical lines in highcharts

I recently came across this really nice example: https://jsfiddle.net/BlackLabel/7t59w4po/
Basically, what it does is that it synchronizes the drag of a line in one graph in all the other graphs.
I was wondering if someone could help me out to reproduce the same example, but instead of one vertical line, I would like to have two. Is this possible?
Thank you!
JS Code:
/*
The purpose of this demo is to demonstrate how multiple charts on the same page
can be linked through DOM and Highcharts events and API methods. It takes a
standard Highcharts config with a small variation for each data set, and a
mouse/touch event handler to bind the charts together.
*/
/**
* In order to synchronize tooltips and crosshairs, override the
* built-in events with handlers defined on the parent element.
*/
['mousemove', 'touchmove', 'touchstart'].forEach(function(eventType) {
document.getElementById('container').addEventListener(
eventType,
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);
// Get the hovered point
point = chart.series[0].searchPoint(event, true);
if (point) {
point.highlight(e);
}
}
}
);
});
/**
* Override the reset function, we don't need to hide the tooltips and
* crosshairs.
*/
Highcharts.Pointer.prototype.reset = function() {
return undefined;
};
/**
* Highlight a point by showing tooltip, setting hover state and draw crosshair
*/
Highcharts.Point.prototype.highlight = function(event) {
event = this.series.chart.pointer.normalize(event);
this.onMouseOver(); // Show the hover marker
this.series.chart.tooltip.refresh(this); // Show the tooltip
this.series.chart.xAxis[0].drawCrosshair(event, this); // Show the crosshair
};
/**
* Synchronize zooming through the setExtremes event handler.
*/
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'
}
);
}
}
});
}
}
/**
* Synchronize annotations drag&drop
*/
function syncAnnotations(e) {
var thisChart = this.chart;
var newX = this.options.shapes[0].points[0].x
if (e.type !== 'afterUpdate') {
Highcharts.each(Highcharts.charts, function(chart) {
if (chart !== thisChart) {
chart.annotations[0].update({
labels: [{
point: {
x: newX
}
}],
shapes: [{
points: [{
x: newX,
xAxis: 0,
y: 0
}, {
x: newX,
xAxis: 0,
y: 1000
}]
}]
});
}
});
}
}
// Get the data. The contents of the data file can be viewed at
Highcharts.ajax({
url: 'https://cdn.jsdelivr.net/gh/highcharts/highcharts#v7.0.0/samples/data/activity.json',
dataType: 'text',
success: function(activity) {
activity = JSON.parse(activity);
activity.datasets.forEach(function(dataset, i) {
// Add X values
dataset.data = Highcharts.map(dataset.data, function(val, j) {
return [activity.xData[j], val];
});
var chartDiv = document.createElement('div');
chartDiv.className = 'chart';
document.getElementById('container').appendChild(chartDiv);
Highcharts.chart(chartDiv, {
chart: {
marginLeft: 40, // Keep all charts left aligned
spacingTop: 20,
spacingBottom: 20
},
title: {
text: dataset.name,
align: 'left',
margin: 0,
x: 30
},
credits: {
enabled: false
},
legend: {
enabled: false
},
xAxis: {
crosshair: true,
events: {
setExtremes: syncExtremes
},
labels: {
format: '{value} km'
}
},
yAxis: {
title: {
text: null
}
},
annotations: [{
draggable: 'x',
animation: {
defer: false
},
events: {
drag: syncAnnotations,
afterUpdate: syncAnnotations
},
shapes: [{
strokeWidth: 3,
type: 'path',
points: [{
x: 3,
y: 0,
xAxis: 0
}, {
x: 3,
y: 1000,
xAxis: 0
}]
}],
labels: [{
point: {
x: 3,
y: 30,
xAxis: 0
},
shape: 'rect',
formatter: function(e) {
// Use shape options because value is available there. Label use translation only
return this.target.annotation.shapes[0].options.points[0].x.toFixed(3);
}
}]
}],
tooltip: {
positioner: function() {
return {
// right aligned
x: this.chart.chartWidth - this.label.width,
y: 10 // align to title
};
},
borderWidth: 0,
backgroundColor: 'none',
pointFormat: '{point.y}',
headerFormat: '',
shadow: false,
style: {
fontSize: '18px'
},
valueDecimals: dataset.valueDecimals
},
series: [{
data: dataset.data,
name: dataset.name,
type: dataset.type,
color: Highcharts.getOptions().colors[i],
fillOpacity: 0.3,
tooltip: {
valueSuffix: ' ' + dataset.unit
}
}]
});
});
}
});
You only need to add another annotation:
annotations: [{
...,
{
...
}],
And improve the syncAnnotations function a little bit:
function syncAnnotations(e) {
var thisChart = this.chart;
var newX = this.options.shapes[0].points[0].x
var index = this.chart.annotations.indexOf(this);
if (e.type !== 'afterUpdate') {
Highcharts.each(Highcharts.charts, function(chart) {
if (chart !== thisChart) {
chart.annotations[index].update({
...
});
}
});
}
}
Live demo: https://jsfiddle.net/BlackLabel/jwtLc379/
API Reference: https://api.highcharts.com/highcharts/annotations

Highligligh particular areas based on X-axis data

SO What I am trying to do is that I am trying to fetch data from CSV File, and from other CSV file I am trying to Highlight a particular area from the Chart.
For Eg.:
This is the Chart I am getting .
By adding the Following Code.
$.get('abc.csv', function(data) {
var lines = []
lines = data.split('\n');
console.log(lines);
var ecgData=[];
$.each(lines, function(lineNo, lineContent){
if(lineNo >= 0)
{
ecgData[lineNo-0] = parseFloat(lineContent.substring(lineContent.lastIndexOf(",")+1) );
//gibber=500;
//m=m+500;
}//console.log('PPG Data', ppgData[ppgNo-0])
});
featurex = [5,10,14,34,56,78,90,95] ;
featurey = [0,0,1,0,0,3,0,2];
zip = (xs, ys) => xs.reduce((acc, x, i) => (acc.push([x, ys[i]]), acc), []);
//console.log(ecg);
console.log(ecgData);
Highcharts.chart('ecg', {
chart: {
type: 'line',
zoomType: 'xy',
panning: true,
panKey: 'shift'
},
credits: {
enabled: false
},
title: {
text: 'ECG Data'
},
subtitle: {
text: ''
},
xAxis: {
crosshair: false
},
yAxis: {
title: {
text: 'ECG Peaks'
}
},
tooltip: {
enabled: false
},
plotOptions: {
column: {
pointPadding: 0.2,
borderWidth: 0
}
},
series: [{
name: '',
lineWidth: 1,
data: ecgData,
animation: {
duration: 14000
}
},
{ type: 'column',
name: 'Features',
data: zip(featurex, featurey),
animation: {
duration: 14000
}
}
]
});
});
My Chart :
Now as you can see from the Chart. I am getting the features data as bars in the chart.
featurex = [5,10,14,34,56,78,90,95] ;
featurey = [0,0,1,0,0,3,0,2];
but that is not what I want what I want is that where the features x value is 1, I want to highlight that area with a particular color, where it is 2, it should be filled with other color Like an example below:
Note: its just an example how the data should look don't math the data with the above image data.
I hope my question is clear.
In the load event you can check if a point meets your condition and add plotBands to your chart.
chart: {
events: {
load: function() {
var xAxis = this.xAxis[0],
points = this.series[0].points,
from,
to,
plotBands = [];
points.forEach(function(point, i) {
from = points[i - 1] ? points[i - 1].x : point.x;
to = points[i + 1] ? points[i + 1].x : point.x;
if (point.y === 1) {
plotBands.push({
color: 'blue',
from: from,
to: to
});
} else if (point.y === 2) {
plotBands.push({
color: 'green',
from: from,
to: to
});
}
});
xAxis.update({
plotBands: plotBands
});
}
}
}
Live demo: http://jsfiddle.net/BlackLabel/vm0ouwp5/
API Reference: https://api.highcharts.com/highcharts/xAxis.plotBands

Highcharts.js question: is it possible to keep some part of chart in visible area always, even if "overscoll" option is set

I need to show some empty space to far right of the chart. To do so I use "overscroll" option (https://api.highcharts.com/highstock/xAxis.overscroll). But if user zoom in chart and pans chart to far right there can be empty space without any part of candlestick chart displayed (https://screencast-o-matic.com/watch/cqnfFq3CII). Please advise is it possible to implement following chart behaviour and how to do so: to keep some part of chart in visible area always, even if "overscoll" option is set and user pans chart to the far right? Thanks!
Here is my code:
var ohlc = JSON.parse(ohlcStringified),
volume = JSON.parse(volumeStringified);
var interval = ohlc[ohlc.length - 1].x - ohlc[ohlc.length - 2].x;
var chart = Highcharts.stockChart('container', {
chart: {
borderWidth: 1,
panning: true,
},
title: {
text: 'Chart'
},
legend: {
enabled: true
},
rangeSelector: {
selected: 1,
enabled: false
},
scrollbar: {
enabled: false
},
xAxis: {
minPadding: 0.2,
overscroll: 50 * interval,
},
yAxis: [{
height: '40%'
}, {
top: '40%',
height: '30%',
offset: 0
}, {
top: '70%',
height: '30%',
offset: 0
}],
series: [{
type: 'candlestick',
id: 'candlestick',
name: 'AAPL',
data: ohlc,
tooltip: {
valueDecimals: 2
},
dataGrouping: {
enabled: false,
}
}, {
type: 'column',
id: 'volume',
name: 'Volume',
data: volume,
yAxis: 1,
dataGrouping: {
enabled: false,
}
}]
});
Here is live demo: http://jsfiddle.net/ogorobets/bfcs9gx7/2/
It's possible, however, it requires some custom logic. It can be achieved using xAxis.events.afterSetExtremes callback where you can check if the current axis minimum is greater than your limit (a value lower than maximum xData value). When it is true, set new axis extremes with your limit as a minimum value. Check the code and demo posted below.
Code:
xAxis: {
minPadding: 0.2,
overscroll: 50 * interval,
events: {
afterSetExtremes: function() {
var chart = this.chart,
xData = chart.series[0].xData,
maxValue = xData[xData.length - 5],
min = chart.xAxis[0].min,
max = chart.xAxis[0].max
if (min > maxValue) {
chart.xAxis[0].setExtremes(maxValue, max, true, false);
}
}
}
}
Demo:
https://jsfiddle.net/BlackLabel/p6d73nk8/
API reference:
https://api.highcharts.com/highcharts/xAxis.events.afterSetExtremes
https://api.highcharts.com/class-reference/Highcharts.Axis#setExtremes

How can I contain a label on a Highcharts area chart to the series' area

For some reason no matter what we try the labels on our area chart series seem to have a mind of their own. Even though a short label looks like it could fit inside the area of the data, it puts it right on the end line, bleeding out of the area.
We suspect it might be due to having min and max dates that are beyond the series min and max, but these buffer zones are a requirement.
Is there an option to make labels be contained to their own series and not bleed off into whitespace?
Below is the example chart configuration and here is the JSFiddle: http://jsfiddle.net/sLqu34cn/
Highcharts.chart('container', {
chart: {
type: "area",
height: 200
},
legend: {
enabled: false
},
plotOptions: {
area: {
stacking: "percent",
pointPlacement: "on"
},
series: {
lineWidth: 0,
fillOpacity: 1,
marker: {
enabled: false
},
label: {
style: {
color: "white",
textOutline: "1px black"
}
}
}
},
series: [
{
name: "Two",
data: [[1532217600000, 1], [1532822400000, 0]],
color: "#41B6E6"
},
{
name: "Three",
data: [[1532217600000, 0], [1532822400000, 2]],
color: "#0072CE"
}
],
xAxis: {
tickWidth: 1,
title: {
enabled: false
},
labels: {
format: "{value: %b %e}"
},
max: 1533243166375,
min: 1530478366375,
type: "datetime"
},
yAxis: {
tickInterval: 20,
title: {
text: null
},
labels: {
format: "{value}%"
},
max: 100,
min: 0
},
tooltip: {}
});
Probably not the perfect solution, but you can create some customization to position the series labels. This is an example how to manually calculate the center of area in triangle shape:
Highcharts.wrap(Highcharts.Chart.prototype, 'drawSeriesLabels', function(proceed) {
proceed.apply(this, Array.prototype.slice.call(arguments, 1));
var chart = this,
plotTop = chart.plotTop,
plotLeft = chart.plotLeft,
series = chart.series,
height = chart.yAxis[0].height,
x1,
x2,
y1,
y2;
x1 = ((series[0].graphPath[1] + plotLeft) * 2 + series[0].graphPath[4] + plotLeft) / 3;
y1 = (height + plotTop + series[0].graphPath[2] + plotTop + series[0].graphPath[5] + plotTop) / 3;
x2 = (series[1].graphPath[1] + plotLeft + (series[1].graphPath[4] + plotLeft) * 2) / 3;
y2 = ((series[1].graphPath[2] + plotTop) * 2 + series[1].graphPath[5] + plotTop) / 3;
series[0].labelBySeries.attr({
x: x1,
y: y1,
align: 'center'
});
series[1].labelBySeries.attr({
x: x2,
y: y2,
align: 'center'
});
});
Live demo: http://jsfiddle.net/BlackLabel/34z8od5f/
Docs: https://www.highcharts.com/docs/extending-highcharts/extending-highcharts

HighCharts - timeseries chart - irregular datetime interval on xAxis

I need to make a chart on which xAxis is of type 'datetime', but have irregular intervals:
http://jsfiddle.net/cz6rL/
this is the code:
$(function () {
$('#chart1').highcharts({
chart: {
zoomType: 'x',
spacingRight: 20
},
title: {
text: 'Incomes/Outcomes'
},
subtitle: {
text: document.ontouchstart === undefined ?
'Click and drag in the plot area to zoom in' :
'Pinch the chart to zoom in'
},
xAxis: {
type: 'datetime',
minRange: 15 * 24 * 3600000,
title: {
text: null
}
},
yAxis: {
title: {
text: 'Euro(€)'
}
},
tooltip: {
shared: true
},
legend: {
enabled: true
},
plotOptions: {
area: {
fillColor: {
linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1},
stops: [
[0, Highcharts.getOptions().colors[9]],
[1, Highcharts.Color(Highcharts.getOptions().colors[0]).setOpacity(0).get('rgba')]
]
},
//lineWidth: 1,
marker: {
enabled: false
},
shadow: false,
states: {
hover: {
lineWidth: 1
}
},
threshold: null
}
},
series: [{
type: 'area',
pointInterval: 24 * 3600 * 1000,
pointStart: Date.UTC(2014, 0, 01),
data: [["31/12/2013", 345.2], ["09/01/2014", 494.79999999999995], ["20/01/2014", 137.2], ["22/01/2014", 210.0],
["23/01/2014", 220.4], ["24/01/2014", 871.0], ["28/01/2014", 420.0], ["30/01/2014", 420.0], ["31/01/2014", 2057.15],
["05/02/2014", 191.2], ["06/02/2014", 81.6], ["07/02/2014", 295.2], ["11/02/2014", 135.12], ["12/02/2014", 189.2],
["13/02/2014", 210.0], ["14/02/2014", 315.2], ["17/02/2014", 462.79999999999995], ["18/02/2014", 544.4],
["19/02/2014", 715.4399999999999], ["20/02/2014", 971.2], ["21/02/2014", 418.0], ["02/02/2015", 366.0]]
}]
});
});
you can see that series values do not correspond to xAxis values. How can I fix it: having same days on xAxis or having months corresponding to series values days?
thanks
Luke
You can remove the pointStart assignment, highcharts will determine the range based on trhe values you provide it. Highcharts will take a look at the range of data you supply it and auto generate the tick marks based on your tickInterval settings, and the available dimensions of your chart. If you need the tick marks on the axis to be specifically the dates you have in you data, you should not used the datetime type axis.
Highcharts handles all date data value in unix/epoch time (number of seconds since 1/1/1970). If you want to use datetime axis, you must supply your data in that format.
Change all your date values such as
["31/12/2013", 345.2]
to
[Date.UTC(2013, 11, 31), 345.2]

Categories