HighCharts - update / redraw / addAxis when detect selection event - highcharts

I have a datetime chart to show daily data within 3 years in a series,
I set the x-Axis formatter to display month of a year in the general view
Screen Capture of General View
I set the 'zoomtype' to 'x', when it zoom to daily level,
the x-axis still show the month base on the formatter I set before
Screen Capture zoomto daily level
How can I change my x-axis formatter when detect zoom action?
formatter: function () { // Single digit month
var mth = Highcharts.dateFormat('%b', this.value).replace(/^[0]+/g, " ");
var year = Highcharts.dateFormat('%Y', this.value);
if (mth == 'Jan')
return mth + '<br>' + year;
else
return mth;
},
Some Method I have tried, but failed :
1) Make 3 different x-axis stand for 'day', 'month' and 'year', add/remove those axis when different zoom level is detected
2) use chart.update() in selection event
3) use chart.redraw() in selection event
Thanks!!
update
Thanks Pawel, I have tried below syntax inside the xAxis Formatter, but seems cannot return the getExtremes correctly,
please kindly advice, thanks!!
var extreme = this.getExtremes();
var extreme = this.xAxis[0].getExtremes();

Related

highstock selected rangeSelector is computed from the current end date in the slider and not from the start date

using highcharts#9.1.1
I am rendering a chart with initial rangeSelector = 1y but it's not rendering properly, the chart selects the last year of data instead of the first year. my data is ordered asc by timestamp btw.
same happens when I explicitly select a range, it computes the range from the current end date in the slider as opposed to the start date.
how can I make it behave differently?
https://jsfiddle.net/6vLgdrx7/4/
You can use setExtremes method in chart's load event and set the initial selected area.
chart: {
...,
events: {
load: function() {
const xAxis = this.xAxis[0];
xAxis.setExtremes(
xAxis.dataMin,
xAxis.dataMin + (xAxis.max - xAxis.min),
true,
false
);
}
}
}
Live demo: https://jsfiddle.net/BlackLabel/hm4xLzft/
API Reference: https://api.highcharts.com/class-reference/Highcharts.Axis#setExtremes

Amcharts 4, xychart, limiting the number of tooltips and combining infos in one tooltip

I am using amcharts 4 to display temperature lines. At times there are many stations so I would like to have just one tooltip and just for the value the cursor is at instead of one tooltip for every line (because then they overlap and some are unreadable).
And there might be several stations with the same temperature so I would have to list all of them in the tooltip.
Anyone knows how to achieve that?
In amcharts 3 I used a balloonFunction attached to the graphs to create my own tooltip. But yet I couldn't find how to do it with the series in amcharts 4.
Thanks for a hint!
So as David Liang mentioned, since all the data items converge along their x axis value (a datetime in this case), you can limit tooltips down to one by only setting one series' tooltipText, and it will have access to the rest of the data fields via data placeholders. E.g. even though series1's value field is E852_t4m, it can use series30's value by just putting "{median_tBel}".
But if you want to have a tooltip based on which line you're hovering over, how to do that depends whether or not you require the Chart Cursor.
If you don't need it, simply set the tooltipText on the line's bullets, e.g.
series1.bullets.getIndex(0).tooltipText = "{name} {valueY}°C";
Here's a demo of your fiddle with that:
https://codepen.io/team/amcharts/pen/803515896cf9df42310ecb7d8d7a2fb7
But if you require Chart Cursor, unfortunately there isn't a supported option at the moment. There's a kind of workaround but it's not the best experience. You start with doing the above. The Chart Cursor will trigger hover effects on all lines and their bullets, including triggering their tooltips. A bullet's tooltip is actually its series' (series1.bulletsContainer.children.getIndex(0).tooltip === series1.tooltip). If we remove the reference to the bullet's tooltip, e.g. series1.bullets.getIndex(0).tooltip = undefined;, the chart will check up the chain and refer to series' anyway. If we do the same to the series' tooltip, it'll go up the chain to chart.tooltip, if we do this to all series, we basically turn chart.tooltip into a singleton behavior of sorts. But it's not as responsive to mouseovers.
You'll see what I mean with this demo:
https://codepen.io/team/amcharts/pen/244ced223fe647ad6df889836da695a8
Oh, also in the above, you'll have to adjust the chart's tooltip to appear on the left/right of bullets with this:
chart.tooltip.pointerOrientation = "horizontal";
Edit:
Since the first method sufficed, I've updated it with an adapter that checks for other fields in range. In the adapter, the target will be the CircleBullet, target.dataItem.valueY is the currently hovered value, and target.dataItem.dataContext are the other fields at the same date.
This is how I modified tooltipText to show other series within +/-0.5C range of the currently-hovered bullet:
// Provide a range of values for determining what you'll consider to be an "overlap"
// (instead of checking neighboring x/y coords.)
function inRange(valueA, rangeA, rangeB) {
return valueA >= rangeA && valueA <= rangeB;
}
// Provide adapters for tooltipText so we can modify them on the fly
chart.series.each(function(series) {
series.bullets
.getIndex(0)
.adapter.add("tooltipText", function(tooltipText, target) {
// the other data fields will already match on the date/x axis, so skip
// the date and this bullet's data fields.
// (target.dataItem.component is the target's series.)
var skipFields = ["date", target.dataItem.component.dataFields.valueY];
// this bullet's value
var hoveredValue = target.dataItem.valueY;
// all the other data fields at this date
var data = target.dataItem.dataContext;
// flag for adding additional text before listing other nearby bullet values
var otherPoints = false;
Object.keys(target.dataItem.dataContext).forEach(function(field) {
// if the field is neither date, nor bullet's
if (!~skipFields.indexOf(field)) {
if (inRange(data[field], hoveredValue - 0.5, hoveredValue + 0.5)) {
if (!otherPoints) {
tooltipText += "\n\nOthers:";
otherPoints = true;
}
// Keep {data placeholder} notation to retain chart formatting features
tooltipText += "\n" + field + ": {" + field + "}°C";
}
}
});
return tooltipText;
});
});
If your series' data points have different x values, it's impossible to combine all the information into one tooltip.
But if they do have same x values, you can just turn on the tooltip for just one of the series:
...,
series: [{
type: "LineSeries",
tooltipHTML: `xxx`,
...
}, {
type: "LineSeries",
...
}, {
type: "LineSeries",
...
}],
...
And within the tooltip HTML, you have access to the data:
...,
tooltipHTML: `
<strong>Year: </strong>{year}<br />
<strong>Cars: </strong>{cars}<br />
<strong>Motorcycles: </strong>{motorcycles}<br />
<strong>Bicycles: </strong>{bicycles}
`,
...
demo: http://jsfiddle.net/davidliang2008/aq9Laaew/286519/

Highstock: how to make series name of first column to be dynamic on tooltip, year wise

I have to display two bars in one chart, where first series (bar) is of year 2015, and second is of year 2016. But when the chart contains data for two years. Then I should need tooltip {series.name} to pick {point.key} and extract year from it lets says, 2016, then on run time change series.name to 2016 for first bar and second bar to be {2016 - 1} = 2015 on tooltip. So if data for bars is from 3-4 years, in order for first series, right now it's taking only series.name which I added, but how to make it dynamic for first series year wise that we can easily pick from {point.key}. I know highstock isn't meant to work like that, but this kind of trick I'm applying to be able to generate comparative reports for this year vs last year bars.
Note: In order to plot graph on one point, timestamps for both bars will remain same, but values will be from last year to be able to plot two bars together.
So I need to change Previous Year to 2010 and Current Year to 2011 on tooltip dynamically by picking year from headerText that is Week from Monday, Aug 9, 2011.
JS Fiddle: https://jsfiddle.net/sfwcqk1w/
You have full control what it is displayed in a tooltip via tooltip.formatter. You can make your own calculations there. In the tooltip you have access to all points from the series.
Adding additional properties for points with enabled data grouping will not work - in a tooltip you get grouped points - and those points do not have additional properties.
Your tooltip might look like this - it includes some default formatting:
formatter: function() {
var points = this.points;
var headerFormat = '<span style="font-size: 10px">' + Highcharts.dateFormat('Week from %A, %b %e, %Y', this.x) + '</span><br/>';
var pointFormat = '',
previousYear;
points.forEach(function(point) {
var name = point.series.name,
part;
if (name === 'Previous Year') {
previousYear = part = new Date(point.x).getFullYear();
} else if (name === 'Current Year') {
part = previousYear + 1
} else {
part = name;
}
pointFormat += '<span style="color:' + point.color + '">\u25CF</span> ' + part + ': <b>' + point.y + '</b><br/>';
});
return headerFormat + pointFormat;
}
example: https://jsfiddle.net/sfwcqk1w/3/

Snap to month in highchart stock navigator

I've got a chart that where allow the user to view the data grouped by day, week, or month. The problem is that if you use the navigator the month columns grow and shrink as the navigator moves or is resized. If the user moves the navigator to the middle of a month, the column graph above shows that that month is off by half. Is there a way to make the navigator be limited by monthly increments?
Only solution is to use afterSetExtremes() or setExtremes() and there update extremes to required ones.
I think this will work, edited to UTC date
I also wrote in a 1 second delay to avoid it triggering too much
events: {
afterSetExtremes: function (e) {
minFirstDay = new Date(e.min);
minFirstDay = Date.UTC(minFirstDay.getFullYear(), minFirstDay.getMonth(), 1);
maxFirstDay = new Date(e.max);
maxFirstDay = Date.UTC(maxFirstDay.getFullYear(), maxFirstDay.getMonth(), 1);
setTimeout(function(){
if (e.min != minFirstDay || e.max != maxFirstDay) {
navChart.xAxis[0].setExtremes(minFirstDay, maxFirstDay);
console.log('date range updated');
};
}, 1000);
}
}

Highcharts - append % to first axis label?

When styling a chart axis, a common style in publications is to include the unit or percentage sign only on the topmost label of the Y-axis. I am trying to figure out how to do this in Highcharts.
I know I can hard-code a number, like this:
yAxis: {
labels: {
formatter: function() {
if ( this.value == 12 ) {
return this.value + "%";
} else {
return this.value;
}
}
},
But this is not a very flexible solution.
Is there a way to test for the first (or nth) value displayed? Is there a way to get the index of a value within the formatter function?
2013 Update
This works in most circumstances:
if ( this.value == this.axis.getExtremes().max )
return this.value + '%';
else
return this.value;
However, it does not work if you have yAxis.endOnTick set to false—because in that case the maximum value of the axis is not the same as the highest label on the axis.
Is there a way to get that highest label from within the formatter function?
As far as I know, it isn't possible to find the 'nth' item on an axis, but first and last can be dome using the properties this.isFirst and this.isLast' in stead of checkingthis.value`
If you want to, you can also access the axis itself directly using this.axis, so you should be able to run whatever complex logic you want on the axis itself..
With 4.2.x version of highchart I have achieved it with following code
labels: {
formatter: function () {
if(this.isFirst)
return this.value + "%";
else
return this.value;
}
}
}
You could also add [%] to the name of the yAxis. Then you wouldn't have to tamper with the ticks.
You can draw at max value plotLine and after extremes are changed - remove and add new one). Then label for that plotLine you can set as '%' and move to the left side.
Another solution is to use axis title - set it to top and remove rotation.
The last solutions (or rather workaround) is to use second yAxis, and set there min and max(or tickPositions), and show only last label, and first hide.

Resources