Highcharts Sunburst levels radius - highcharts

I'm making a sunburst with Highcharts .NET,
This is how i setup the chart:
Highcharts higcharts = new Highcharts
{
Chart = new Chart
{
Type = ChartType.Sunburst,
Width = 700,
Height = 700
},
Title = new Title
{
Text = "Monthly Average Temperature",
X = -20
},
Subtitle = new Subtitle
{
Text = "Source: WorldClimate.com",
X = -20
},
Legend = new Legend
{
Layout = LegendLayout.Vertical,
Align = LegendAlign.Right,
VerticalAlign = LegendVerticalAlign.Middle,
BorderWidth = 0
},
Series = new List<Series>
{
new SunburstSeries
{
Name ="Test",
Data = data,
//LevelSize = new SunburstSeriesLevelSize
//{
// Unit = SunburstSeriesLevelSizeUnit.Percentage,
// Value = 100
//},
Levels = new List<SunburstSeriesLevels>
{
new SunburstSeriesLevels
{
LevelSize = new SunburstSeriesLevelsLevelSize{
Unit = SunburstSeriesLevelsLevelSizeUnit.Percentage,
Value = 90
}
},
new SunburstSeriesLevels
{
LevelSize = new SunburstSeriesLevelsLevelSize{
Unit = SunburstSeriesLevelsLevelSizeUnit.Percentage,
Value = 10
}
}
}
}
}
};
I tried many ways but the levels radius never change, did i miss something?
The only one working is the levelsize of the entire serie but i need to set the size for a specific level.
I tried to search but it looks like nobody already encountered any problem.

Level's object levelSize is able do control the size of individual level. It has two properties: unit (pixels / percentage / weight) and value (determined by the unit):
levels: [{
level: 1,
levelIsConstant: false,
levelSize: {
unit: 'pixels',
value: 30
}
}, {
level: 2,
colorByPoint: true,
dataLabels: {
rotationMode: 'parallel'
}
}, {
level: 3,
levelIsConstant: true,
levelSize: {
unit: 'weight',
value: 2
}
}, {
level: 4,
levelIsConstant: true,
levelSize: {
unit: 'percentage',
value: 30
}
}]
Live demo: http://jsfiddle.net/gh/get/library/pure/highcharts/highcharts/tree/master/samples/highcharts/plotoptions/sunburst-levelsize/
API reference: https://api.highcharts.com/highcharts/series.sunburst.levelSize

Related

Highcharts - Indicator's data not showing in line chart

I am fetching data from an endpoint
I display the data in a highchart
There are several indicators that can be selected. For each of them another yAxis is added below the main one.
My series data are of this format :
series: [
{
data: [],
id: 'prices',
step: this.hasStep,
name: this.$props.title,
fillColor: 'rgba(127,183,240,0.2)',
},
{
visible: false,
type: 'column',
id: 'volume',
name: 'Volume_hardcoded',
//linkedTo: 'prices',
data: this.volumeSeries,
},
],
I save the data in the following way (don't pay attention in the logic, it works fine):
if (this.selectedTimeSpan.tickInterval === 1) {
for (let i = 0; i < prices.length; i++) {
let xData = null;
this.selectedTimeSpan.getIntradayData
? (xData = Math.floor(new Date(prices[i].time).getTime()))
: (xData = Math.floor(new Date(prices[i].date).getTime()));
priceSeries[i] = {
x: xData,
open: prices[i].first,
high: prices[i].high,
low: prices[i].low,
close: prices[i].last,
y: prices[i].last,
volume: prices[i].tradingVolume,
};
this.volumeSeries[i] = {
x: xData,
y: prices[i].tradingVolume,
};
}
} else {
let j = 0;
for (
let i = 4;
i < prices.length;
i += this.selectedTimeSpan.tickInterval
) {
priceSeries[j] = {
x: Math.floor(new Date(prices[i].date).getTime()),
open: prices[i].first,
high: prices[i].high,
low: prices[i].low,
close: prices[i].last,
y: prices[i].last,
volume: prices[i].tradingVolume,
};
this.volumeSeries[j] = {
x: Math.floor(new Date(prices[i].date).getTime()),
y: prices[i].tradingVolume,
};
j++;
}
}
When I select these indicators (they are based on the volume), I am getting this result.(You can see a blank space below the main chart.) Instead when i swap to OHLC or candlestick my main series (series[0]) it looks works fine and it looks like this. Any idea why is that happening? I haven't touched the tooltip settings at all (in case it was there a problem). I am struggling 2 days now with it can't really figure it out. Any help would be appreciated a lot. If you need more information feel free to comment so I can provide. Thanks in advance. Chris.
Fixed, there's a flag that can be used called usedOhlcData in series object. (series[0] in my case]. We just set it to true.
series:[{
data:[],
useOhlcData:true,
...}
,{
...
}]

get tickpositions highcharts

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});
}

Set navigator min zoom

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

rangeSelector: change date beyond zoomed time range

I build a chart like the lazy loading example (http://www.highcharts.com/stock/demo/lazy-loading) but with having the input fields of the range selector.
When I zoom in everything is fine. Now I would like to change the selected range by using the input fields.
But I am not able to change the input to values beyond the zoomed area despite the fact I have more data visible in my navigator.
In the setExtremes function I am doing some calculations:
DX.IPFixGraphModule.prototype.setExtremes = function(e) {
var fromTime,
maxZoom = 30 * 60 * 1000,
now = new Date().getTime();
if (e.min) {
fromTime = e.min;
} else {
fromTime = e.dataMin;
}
fromTime = Math.round(fromTime);
var diff = now - fromTime;
// -1 month = max 1 week zoom
if (diff >= 2592000000) {
maxZoom = 7 * 24 * 60 * 60 * 1000;
}
// -1 week = max 12 hour zoom
else if (diff >= 604800000) {
maxZoom = 12 * 60 * 60 * 1000;
}
// this refers to axis
// #see http://api.highcharts.com/highstock#Axis.update
this.update({
minRange: maxZoom
}, false);
};
But the values I receive in e.min and e.max are not the original input values but already corrected to the displayed time range.
// handle changes in the input boxes
input.onchange = function () {
var inputValue = input.value,
value = (options.inputDateParser || Date.parse)(inputValue),
xAxis = chart.xAxis[0],
dataMin = xAxis.dataMin,
dataMax = xAxis.dataMax;
// If the value isn't parsed directly to a value by the browser's Date.parse method,
// like YYYY-MM-DD in IE, try parsing it a different way
if (isNaN(value)) {
value = inputValue.split('-');
value = Date.UTC(pInt(value[0]), pInt(value[1]) - 1, pInt(value[2]));
}
if (!isNaN(value)) {
// Correct for timezone offset (#433)
if (!defaultOptions.global.useUTC) {
value = value + new Date().getTimezoneOffset() * 60 * 1000;
}
// Validate the extremes. If it goes beyound the data min or max, use the
// actual data extreme (#2438).
if (isMin) {
if (value > rangeSelector.maxInput.HCTime) {
value = UNDEFINED;
} else if (value < dataMin) {
value = dataMin;
}
} else {
if (value < rangeSelector.minInput.HCTime) {
value = UNDEFINED;
} else if (value > dataMax) {
value = dataMax;
}
}
// Set the extremes
if (value !== UNDEFINED) {
chart.xAxis[0].setExtremes(
isMin ? value : xAxis.min,
isMin ? xAxis.max : value,
UNDEFINED,
UNDEFINED,
{ trigger: 'rangeSelectorInput' }
);
}
}
};
(Code taken from highstock.src.js around line 21126)
So I cannot extend my zoom beyond the current active selection, but the navigator displays more data.
Does anyone know a way to set a date beyond the currently zoomed time range?
Possible Solution
I solved it by checking the navigator range in the "afterSetExtremes" Event.
DX.IPFixGraphModule.prototype.afterSetExtremes = function(e) {
if (e.trigger === 'rangeSelectorInput') {
var fromValue = this.stockchart.rangeSelector.minInput.value,
toValue = this.stockchart.rangeSelector.maxInput.value,
fromTime = parseDateTime(fromValue),
toTime = parseDateTime(toValue),
navigatorAxis = this.stockchart.get('navigator-x-axis'),
maxValue = navigatorAxis.dataMax,
minValue = navigatorAxis.dataMin;
if (fromTime < minValue) {
fromTime = minValue;
}
if (toTime > maxValue) {
toTime = maxValue;
}
this.stockchart.xAxis[0].setExtremes(fromTime, toTime);
} else {
var fromTime,
toTime;
if (e.min) {
fromTime = e.min;
} else {
fromTime = e.dataMin;
}
fromTime = Math.round(fromTime);
if (e.max) {
toTime = e.max;
} else {
toTime = e.dataMax;
}
toTime = Math.round(toTime);
this.settings.afterSetExtremes({startTimestamp: fromTime, endTimestamp: toTime});
}
};
Or see solution below and override the method.
There is no default API for that. Extend Highcharts via overriding drawInput function (your second code snippet).
There is a part of code that you should comment out or remove - the if clause after:
// Validate the extremes. If it goes beyound the data min or max, use the
// actual data extreme (#2438).
Example: http://jsfiddle.net/epL7awo4/1/
$(function () {
(function (H) {
H.wrap(H.RangeSelector.prototype, 'drawInput', function (proceed) {
var name = arguments[1],
merge = H.merge,
createElement = H.createElement,
PREFIX = 'highcharts-',
ABSOLUTE = 'absolute',
PX = 'px',
extend = H.extend,
pInt = H.pInt,
UNDEFINED;
//drawInput: function (name) {
var rangeSelector = this,
chart = rangeSelector.chart,
chartStyle = chart.renderer.style,
renderer = chart.renderer,
options = chart.options.rangeSelector,
defaultOptions = H.getOptions(),
lang = defaultOptions.lang,
div = rangeSelector.div,
isMin = name === 'min',
input,
label,
dateBox,
inputGroup = this.inputGroup;
// Create the text label
this[name + 'Label'] = label = renderer.label(lang[isMin ? 'rangeSelectorFrom' : 'rangeSelectorTo'], this.inputGroup.offset)
.attr({
padding: 2
})
.css(merge(chartStyle, options.labelStyle))
.add(inputGroup);
inputGroup.offset += label.width + 5;
// Create an SVG label that shows updated date ranges and and records click events that
// bring in the HTML input.
this[name + 'DateBox'] = dateBox = renderer.label('', inputGroup.offset)
.attr({
padding: 2,
width: options.inputBoxWidth || 90,
height: options.inputBoxHeight || 17,
stroke: options.inputBoxBorderColor || 'silver',
'stroke-width': 1
})
.css(merge({
textAlign: 'center',
color: '#444'
}, chartStyle, options.inputStyle))
.on('click', function () {
rangeSelector.showInput(name); // If it is already focused, the onfocus event doesn't fire (#3713)
rangeSelector[name + 'Input'].focus();
})
.add(inputGroup);
inputGroup.offset += dateBox.width + (isMin ? 10 : 0);
// Create the HTML input element. This is rendered as 1x1 pixel then set to the right size
// when focused.
this[name + 'Input'] = input = createElement('input', {
name: name,
className: PREFIX + 'range-selector',
type: 'text'
}, extend({
position: ABSOLUTE,
border: 0,
width: '1px', // Chrome needs a pixel to see it
height: '1px',
padding: 0,
textAlign: 'center',
fontSize: chartStyle.fontSize,
fontFamily: chartStyle.fontFamily,
top: chart.plotTop + PX // prevent jump on focus in Firefox
}, options.inputStyle), div);
// Blow up the input box
input.onfocus = function () {
rangeSelector.showInput(name);
};
// Hide away the input box
input.onblur = function () {
rangeSelector.hideInput(name);
};
// handle changes in the input boxes
input.onchange = function () {
var inputValue = input.value,
value = (options.inputDateParser || Date.parse)(inputValue),
xAxis = chart.xAxis[0],
dataMin = xAxis.dataMin,
dataMax = xAxis.dataMax;
// If the value isn't parsed directly to a value by the browser's Date.parse method,
// like YYYY-MM-DD in IE, try parsing it a different way
if (isNaN(value)) {
value = inputValue.split('-');
value = Date.UTC(pInt(value[0]), pInt(value[1]) - 1, pInt(value[2]));
}
if (!isNaN(value)) {
// Correct for timezone offset (#433)
if (!defaultOptions.global.useUTC) {
value = value + new Date().getTimezoneOffset() * 60 * 1000;
}
// Validate the extremes. If it goes beyound the data min or max, use the
// actual data extreme (#2438).
/* if (isMin) {
if (value > rangeSelector.maxInput.HCTime) {
value = UNDEFINED;
} else if (value < dataMin) {
value = dataMin;
}
} else {
if (value < rangeSelector.minInput.HCTime) {
value = UNDEFINED;
} else if (value > dataMax) {
value = dataMax;
}
}*/
// Set the extremes
if (value !== UNDEFINED) {
chart.xAxis[0].setExtremes(
isMin ? value : xAxis.min,
isMin ? xAxis.max : value,
UNDEFINED,
UNDEFINED, {
trigger: 'rangeSelectorInput'
});
}
}
};
//},
});
}(Highcharts));
/**
* Load new data depending on the selected min and max
*/
function afterSetExtremes(e) {
var chart = $('#container').highcharts();
chart.showLoading('Loading data from server...');
$.getJSON('http://www.highcharts.com/samples/data/from-sql.php?start=' + Math.round(e.min) +
'&end=' + Math.round(e.max) + '&callback=?', function (data) {
chart.series[0].setData(data);
chart.hideLoading();
});
}
// See source code from the JSONP handler at https://github.com/highslide-software/highcharts.com/blob/master/samples/data/from-sql.php
$.getJSON('http://www.highcharts.com/samples/data/from-sql.php?callback=?', function (data) {
// Add a null value for the end date
data = [].concat(data, [
[Date.UTC(2011, 9, 14, 19, 59), null, null, null, null]
]);
// create the chart
$('#container').highcharts('StockChart', {
chart: {
type: 'candlestick',
zoomType: 'x'
},
navigator: {
adaptToUpdatedData: false,
series: {
data: data
}
},
scrollbar: {
liveRedraw: false
},
title: {
text: 'AAPL history by the minute from 1998 to 2011'
},
subtitle: {
text: 'Displaying 1.7 million data points in Highcharts Stock by async server loading'
},
rangeSelector: {
buttons: [{
type: 'hour',
count: 1,
text: '1h'
}, {
type: 'day',
count: 1,
text: '1d'
}, {
type: 'month',
count: 1,
text: '1m'
}, {
type: 'year',
count: 1,
text: '1y'
}, {
type: 'all',
text: 'All'
}],
inputEnabled: true,
selected: 4 // all
},
xAxis: {
events: {
afterSetExtremes: afterSetExtremes
},
minRange: 3600 * 1000 // one hour
},
yAxis: {
floor: 0
},
series: [{
data: data,
dataGrouping: {
enabled: false
}
}]
});
});
});

Highcharts, how can I start xAxis on an arbitrary time

I have a line chart with a datetime xAxis. I need to show ticks every 10 minutes, for that I have set tickInterval to 10*60*1000, my problem is that I need to show ticks every 10 minutes since the first date, for example, if my first point is displayed at 10:33, I need to show ticks at 10:33, 10:43, 10:53, etc, but what I have are ticks at 10:30, 10:40, 10:50 and so on, is there any way to do this?
Thanks!
It's not that straightforward because Highcharts automatically determines the labels to use when the x-axis is of the type 'datetime':
"In a datetime axis, the numbers are given in milliseconds, and tick marks are placed on appropriate values like full hours or days"
To set labels like '10:33' you need to create your own categories. Luckily these can simply be derived from your data and the desired time interval.
Here's a working example: http://jsfiddle.net/Rt7ZV/
We just take the given start date, interval and number of points and build an array of the categories to be used as the x-axis labels.
function getTimes(numTimes, interval) {
var ms = (new Date(2012, 02, 30, 10, 33)).getTime();
var times = [];
var startDate = new Date(ms);
times.push(startDate.getHours() + ":" + startDate.getMinutes());
for (var i = 1; i< numTimes; i++)
{
ms += interval;
var nextTime = (new Date()).setTime(ms);
var nextDate = new Date(nextTime);
times.push(nextDate.getHours() + ":" + pad(nextDate.getMinutes(), 2));
}
return times;
}
function pad(num, size) {
var s = num+"";
while (s.length < size) s = "0" + s;
return s;
}
var data = [1, 2, 3, 4, 5, 3, 2, 5, 7, 6, 4];
var interval = 10*60*1000
var timeCategories = getTimes(data.length, interval);
$(function () {
var chart;
$(document).ready(function() {
chart = new Highcharts.Chart({
chart: {
renderTo: 'container',
zoomType: 'x',
spacingRight: 20
},
title: {
text: 'Time series'
},
xAxis: {
categories: timeCategories,
title: {
text: null
},
startOnTick: false
},
yAxis: {
title: {
text: 'Exchange rate'
},
startOnTick: false,
showFirstLabel: true
},
tooltip: {
shared: true
},
legend: {
enabled: false
},
series: [{
type: 'line',
name: 'time series',
data: [
1, 2, 3, 4, 5, 3, 2, 5, 7, 6, 4
]
}]
});
});
});
I found the tickPositions property on xAxis, which isn't documented on highcharts, only on highstock, but seems to work fine on both. With this property you can specify which values you want to hace a tick for, and work perfectly for my problem.

Resources