When I'm drawing BOX Plot chart with Highchart like this : JSFiddle
chart: {
type: 'boxplot',
}
where category 2 "Merchant 2" is shown on x-axis even though we don't have data for it.
How can we avoid rendering categories on x-axis when they don't have data in box plot ?
Thank you
There's no built in mechanism in Highcharts that'll filter out and hide "unused" categories.
Workaround:
breaks functionality allows to hide regions on axes. Here's an algorithm that finds the categories that don't have any points above them and applies breaks:
render: function() {
if (redrawEnabled) {
redrawEnabled = false;
var emptyCategories = [],
series = this.series,
xAxis = this.xAxis[0],
categories = xAxis.categories.slice(),
categoryFlags = new Array(categories.length).fill(false), // indicates if any point has a value for category with the given index
breaks = [],
correspondingPoint;
// find out which categories are 'used'
for (var i = 0; i < categories.length; i++) {
for (var ii = 0; ii < series.length; ii++) {
if (!series[ii].visible) {
continue;
}
correspondingPoint = series[ii].data.find((point) => point.x === i);
if (correspondingPoint) {
categoryFlags[i] = true;
break;
}
}
}
// create and apply breaks
categoryFlags.forEach(function(flag, index) {
if (!flag) {
breaks.push({
from: index - 0.5,
to: index + 0.5
});
}
});
//console.log(breaks)
xAxis.update({
breaks: breaks
});
}
redrawEnabled = true;
}
Live demo: http://jsfiddle.net/BlackLabel/fubwdm4x/
The key to understand how this solution works is that the categories are basically just information how to format axis' labels and position ticks. The tickInterval is always 1 and ticks are moved -0.5 to the left. So if you have categories like this: ['Category 1', 'Category 2', 'Category 3'] the positions of ticks are: -0.5, 0.5, 1.5 and 2.5.
API reference: https://api.highcharts.com/highcharts/yAxis.breaks
Related
I'm wondering if it's possible to define a custom ordering or format for the xAxis in stock highcharts. My dataset has a date time which would be used for the xAxis however my client has specified that it should show in the middle T-0 on the xAxis. Rest of them from the left side should be like -3m -2m -1m and from the right side +1m +2m +3m(In case of year timeframe).
Example for 1 year timeframe
I have tried using formatter function on xAxis labels. However I can not figure out how to get the middle tick first and then start chaging labels to the left and to the right from that middle position tick.
If the formatting, amount and interval of the labels is static, you can use variables from outside of the chart.
For example:
const labels = [];
let labelIndex = 0;
for (let i = -6; i < 7; i++) {
labels.push(i);
}
Highcharts.stockChart('container', {
xAxis: {
...,
labels: {
formatter: function() {
labelIndex++;
if (this.isFirst) {
labelIndex = 0;
}
const label = labels[labelIndex];
if (label < 0) {
return label + 'm';
} else if (label === 0) {
return 'T-' + label;
}
return '+' + label + 'm';
}
}
},
...
});
Live demo: http://jsfiddle.net/BlackLabel/L7uy29kw/
API Reference: https://api.highcharts.com/highstock/xAxis
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.
I want to make stacked bar chart where each portion has a width that encodes one value (say "Change" in the data below) and a height that encodes another value ("Share")
In some ways this is like a histogram with different bin sizes. There are a few "histogram" questions but none seem to address this. Plot Histograms in Highcharts
So given data like this:
Category Share Price Change
Apples 14.35 0.1314192423
Horseradish 46.168 0.1761474117
Figs 2.871 0.018874249
Tomatoes 13.954 0.0106121298
Mangoes 7.264 0.1217297011
Raisins 5.738 0.0206787136
Eggplant 6.31 0.0110160732
Other produce 3.344 0.0945377722
I can make a stacked bar that captures the "share" column in widths:
And another that captures the "change" column in heights:
And I can use an image editor to combine those into this histogram-like beast:
Which really captures that horseradish is a huge deal. So my question is, can I do that within Highcharts?
You can realise that by using snippet.
(function (H) {
var seriesTypes = H.seriesTypes,
each = H.each,
extendClass = H.extendClass,
defaultPlotOptions = H.getOptions().plotOptions,
merge = H.merge;
defaultPlotOptions.marimekko = merge(defaultPlotOptions.column, {
pointPadding: 0,
groupPadding: 0
});
seriesTypes.marimekko = extendClass(seriesTypes.column, {
type: 'marimekko',
pointArrayMap: ['y', 'z'],
parallelArrays: ['x', 'y', 'z'],
processData: function () {
var series = this;
this.totalZ = 0;
this.relZ = [];
seriesTypes.column.prototype.processData.call(this);
each(this.zData, function (z, i) {
series.relZ[i] = series.totalZ;
series.totalZ += z;
});
},
translate: function () {
var series = this,
totalZ = series.totalZ,
xAxis = series.xAxis;
seriesTypes.column.prototype.translate.call(this);
// Distort the points to reflect z dimension
each(this.points, function (point, i) {
var shapeArgs = point.shapeArgs,
relZ = series.relZ[i];
shapeArgs.x *= (relZ / totalZ) / (shapeArgs.x / xAxis.len);
shapeArgs.width *= (point.z / totalZ) / (series.pointRange / series.xAxis.max);
});
}
});
}(Highcharts));
Example: http://jsfiddle.net/highcharts/75oucp3b/
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
How can I have approximated columns to be colored based on column value?
After a lot of digging and some messages with Highcharts support, I finally got in shape.
To callback function after Highcharts complete:
}, function(chart) { // on complete
// Color approximated columns by value
var series = chart.series[0];
delete series.pointAttr.hover.fill;
delete series.pointAttr[''].fill;
// There are only values 0-5 as integers in this data series
var colors = { 0: '#fff', 1: '#999', 2: '#777', 3: '#555', 4: '#333' };
for (var i = 0; i < series.points.length; i++) {
series.points[i].graphic.attr("fill", colors[ series.points[i].y ] );
}
});
Event to xAxis for recoloring after zoom events:
xAxis : [ {
events: {
afterSetExtremes: function(event){
// Color approximated columns by value, again after zoom
var chart = angular.element('#presence-chart').highcharts();
var series = chart.series[0];
delete series.pointAttr.hover.fill;
delete series.pointAttr[''].fill;
var colors = { 0: '#fff', 1: '#50117a', 2: '#7631a0', 3: '#c171ed', 4: '#72accf' };
for (var i = 0; i < series.points.length; i++) {
series.points[i].graphic.attr("fill", colors[ series.points[i].y ] );
}
}
}
} ],
Someone could fill in here how to get hover colors as if you comment out "delete series.pointAttr[''].fill;" then after hover the bar stays with that hover color?