We're currently generating a polar chart in HighCharts that renders correctly on the client-side (in the browser), and correctly applies formatters to the xAxis, yAxis, and plotOptions. Here is a jsFiddle that shows how it is rendering (correctly) in the browser: https://jsfiddle.net/cmodzelewski/38f03Lse/1/
On the server side, we are constructing a JSON payload and sending it to a node-export-server instance and returning a PNG. Based on our research, it is clear that formatters need to be included in the callback key of our JSON payload and passed to the server as a string, rather than in the infile key.
That's fine, and so we're converting our formatter functions into strings, composing them into the options object in our callback key, and then redrawing the chart at the end of our callback key.
This approach works great for non-polar charts, but if polar == true the export server returns a valid PNG from the export server, however that chart does not apply our formatter functions to the xAxis, yAxis, or plotOptions.series.
Here is the JSON payload that we are sending to a node-export-server instance:
{
"callback": "function (chart) {var options = chart.options;var xAxisFormatter = function () { var extra_hrs = 0; if (this.value == 0) { extra_hrs = 12; }; return ((this.value / 0.5)/60) + extra_hrs + ':00'; };if (\"labels\" in options[\"xAxis\"]) { options[\"xAxis\"][\"labels\"][\"formatter\"] = xAxisFormatter; } else { options[\"xAxis\"][\"labels\"] = { \"formatter\": xAxisFormatter, \"style\": { \"fontSize\": \"8px\" } }; };var yAxisFormatter = function () { return Highcharts.numberFormat(this.value, 2, '.', ',') + \"%\"; };if (\"labels\" in options[\"yAxis\"]) { options[\"yAxis\"][\"labels\"][\"formatter\"] = yAxisFormatter; } else { options[\"yAxis\"][\"labels\"] = { \"formatter\": yAxisFormatter, \"style\": { \"fontSize\": \"8px\" } };};var plotOptionsFormatter = function () { return Highcharts.numberFormat(this.value, 2, '.', ',') + \"%\"; };options[\"plotOptions\"][\"series\"][\"dataLabels\"][\"formatter\"] = plotOptionsFormatter;chart = new Highcharts.chart(chart.container, options);chart.redraw();}",
"infile": "{chart: {backgroundColor: \"white\", borderWidth: 0, height: 300, polar: true, width: 300}, colors: [\"#16C1F3\", \"#3C6E71\", \"#EAC435\", \"#E63946\", \"#33658A\", \"#DFD6A7\", \"#627264\", \"#86CCA5\", \"#6268B0\", \"#E8D33F\", \"#DA2C38\"], credits: {enabled: false, position: {align: \"right\", verticalAlign: \"bottom\"}, text: \"(c) Insight Industry Inc., 2017.\"}, exporting: {enabled: false}, legend: {enabled: false}, plotOptions: {column: {groupPadding: 0, pointPadding: 0}, series: {dataLabels: {}, pointInterval: 30, pointStart: 0}}, series: [{data: [{name: \"12:00am - 4:59am\", x: 0, y: 2.737994945240101}, {name: \"5:00am - 5:29am\", x: 150, y: 1.6287559674248806}, {name: \"5:30am - 5:59am\", x: 165, y: 1.6849199663016006}, {name: \"6:00am - 6:29am\", x: 180, y: 5.9112608817747825}, {name: \"6:30am - 6:59am\", x: 195, y: 11.513619769727605}, {name: \"7:00am - 7:29am\", x: 210, y: 17.98652064026959}, {name: \"7:30am - 7:59am\", x: 225, y: 17.733782645324347}, {name: \"8:00am - 8:29am\", x: 240, y: 20.190957596180848}, {name: \"8:30am - 8:59am\", x: 255, y: 7.848918843021623}, {name: \"9:00am - 9:59am\", x: 270, y: 8.438640831227183}, {name: \"10:00am - 10:59am\", x: 300, y: 3.1592249368155008}, {name: \"11:00am - 11:59am\", x: 330, y: 1.1654029766919405}], name: null, pointPlacement: \"on\", type: \"column\"}], title: {text: null}, tooltip: {style: {fontSize: \"10px\"}, valueDecimals: 1, valueSuffix: \"%\"}, xAxis: {labels: {style: {fontSize: \"10px\"}}, max: 360, min: 0, tickInterval: 30, title: {text: null}}, yAxis: {labels: {style: {fontSize: \"10px\"}}, max: 25.0, min: 0, showLastLabel: false, tickInterval: 5, title: {style: {color: \"#0A3B61\", fontSize: \"9px\", fontWeight: \"bold\"}, text: \"Workers, Aged 25+\"}}}",
"scale": 2,
"type": "png"
}
We have recreated what we (suspect) is the process that the node export server goes through, and have definitely recreated the (weird) behavior that we're seeing in this jsFiddle: https://jsfiddle.net/cmodzelewski/v4gm6t9a/2/
Are we missing something blatantly obvious (which is what we suspect)? Or is there a better way of doing this to get the behavior we're looking for?
Any help would be very much appreciated!
In your fiddle with the suspected process, and in your callback, you are setting the axis labels in the following way:
options["yAxis"]["labels"]
This will set an object labels on the yAxis element, however, since you can have several axis in highcharts, they are indexed, and stored as such. That means you have to edit the first axis like this:
options["yAxis"][0]["labels"]
Here is a picture of the yAxis object in the incorrect configuration:
Here is a picture of the yAxis object in the correct configuration:
Working example using your second fiddle: https://jsfiddle.net/ewolden/v4gm6t9a/4/
Related
I'm trying to assign categories to the Y Axis on a column chart. I've tried using categories but this causes a peculiar result with the columns not reaching the X Axis, and the values being incorrect
https://jsfiddle.net/fmgLa5h4/1/
I've tried various other properties to correct this but to no avail.
I've also tried using labels.formatter, which gets a slightly more desirable result but with the labels aligned to the ticks - again I've tried other ways to correct this without success
https://jsfiddle.net/fmgLa5h4/2/
You can try to mapping the data using data.seriesMapping and set your own dataLabels or categories, take a look at this example:
var categories = ['A', 'B', 'C', 'D', 'E'],
yAxisLabels = ['10', '20', '30', '40', '50'];
Highcharts.chart('container', {
data: {
csv: document.getElementById('data').innerHTML,
seriesMapping: [{
// x: 0, // X values are pulled from column 0 by default
// y: 1, // Y values are pulled from column 1 by default
label: 2 // Labels are pulled from column 2 and picked up in the dataLabels.format below
}]
},
chart: {
type: 'column'
},
title: {
text: 'Daily runs'
},
xAxis: {
minTickInterval: 24 * 36e5
},
yAxis: {
labels: {
formatter: function() {
return yAxisLabels[this.value] + ', ' + this.value || '';
},
},
tickInterval: 1,
max: categories.length - 1,
},
legend: {
enabled: false
},
plotOptions: {
series: {
dataLabels: {
enabled: true,
format: '{point.label}'
},
}
}
});
For formatting data tables, yAxis.labels.formatter is a good option. It is possible that your data will need to be formatted before you map it.
Sample concept:
data = data.replace(/\r\n/g, '\n') // Unix
.replace(/\r/g, '\n') // Mac
.split('\n');
data.forEach(function(row, i){
if (i) {
row = row.split(',');
data[i] = row.concat([
categories.indexOf(row[1]),
categories.indexOf(row[3])
]).join(',');
}
});
data = data.join('\n');
Live demo:
https://jsfiddle.net/BlackLabel/g5svkrac/
API References:
https://api.highcharts.com/highcharts/yAxis.labels.formatter
https://api.highcharts.com/highcharts/data.seriesMapping
You meant to positioning the label, you can use yAxis.labels.y to position offset of all labels relative to the tick positions on the axis.
https://jsfiddle.net/BlackLabel/g5svkrac/1/
I'm trying to populate a map of the U.S. in HighMaps with data from an html table. The map is showing but the data point is not. It's a latitude/longitude point. The documentation is sparse, so I'm not sure what I'm doing wrong. Here's a JSFiddle: https://jsfiddle.net/sfjeld/1wjm04fc/6/
Highcharts.mapChart('container', {
chart: {
map: 'countries/us/custom/us-all-territories'
},
series: [{
name: 'Basemap',
dataLabels: {
enabled: true,
format: '{point.name}'
}
},
{
// Specify points using lat/lon
type: 'mappoint',
data: {
table: 'pvsoiling-table',
startRow: 1,
startColumn: 1,
endColumn: 2
},
name: 'PV',
marker: {
fillColor: 'white',
lineColor: 'black',
lineWidth: 2,
radius: 10
},
color: Highcharts.getOptions().colors[1]
}
]
});
thanks,
Shauna
You can make the following changes to your code:
At the start of your <script> section, load your HTML table data into a JavaScript array:
var mapData = [];
Highcharts.data({
table: document.getElementById('pvsoiling-table'),
startColumn: 1,
endColumn: 2,
firstRowAsNames: true,
complete: function (options) {
options.series[0].data.forEach(function (p) {
mapData.push({
lat: p[0],
lon: p[1]
});
});
//console.log(mapData);
}
});
We will refer to this mapData array later on. Here is what it contains:
[
{ "lat": 33.3, "lon": -111.67 }
]
Make changes to the series section in your Highcharts.mapChart.
series: [{
name: 'Basemap',
nullColor: '#e0f9e3',
dataLabels: {
enabled: true,
format: '{point.name}'
}},
{
// Specify points using lat/lon
data: mapData,
type: 'mappoint',
name: 'PV',
marker: {
fillColor: 'white',
lineColor: 'black',
lineWidth: 1,
radius: 3
},
color: Highcharts.getOptions().colors[1]
}
]
The key part to note is the data: mapData option. The JavaScript mapData variable is the exact array that we need to represent a set of points on the map. In our case the array only contains one point - but that's because there is only one relevant row of data in the HTML table.
My map ends up looking like this:
It looks like the marker is in or near Phoenix, AZ.
Final notes:
(a) I also adjusted the marker to have lineWidth: 1 and radius: 3 (a bit smaller).
(b) I added a document ready function around everything, to ensure the DataTable is loaded before you try to read its data.
(c) There may be a more elegant way to do this, but I followed the approach in this demo. The demo actually shows how to join 2 different sets of data - not what you need. But it does show a good approach for extracting the HTML data so it can be used in the map.
Using the the Highcharts.data({...}) approach lets you access any HTML table. But since you are using DataTables, you can choose to do the following, instead. It uses the DataTables API to access each row of data:
var mapData = [];
$('#pvsoiling-table').DataTable({
"initComplete": function(settings, json) {
var api = this.api();
api.rows().data().each(function (p) {
mapData.push({ lat: Number(p[1]), lon: Number(p[2]) });
});
// your Highcharts.mapChart() logic, in a function:
buildChart();
}
});
I'm new to Highcharts and have been experimenting with the Gantt chart type, unfortunately there isnt a whole heap of documentation around right now.
My requirement is quite basic, I need to plot multiple series of milestones in order to show slippage of different projects. I've used some example code I found online to start me off however I need to remove the blank ('My 1st Row') line between the series. see:-
https://jsfiddle.net/z86aq99g
var today = new Date(),
day = 1000 * 60 * 60 * 24;
var grey_OrigTargDate = '#A6A6A6';
var blue_Completed = '#2e75b6';
var green_Ontarget = '#00b050';
var yellow_OfftrackMinor = '#ffc000';
var red_OffTrackMajor = '#ff0000';
// Set to 00:00:00:000 today
today.setUTCHours(0);
today.setUTCMinutes(0);
today.setUTCSeconds(0);
today.setUTCMilliseconds(0);
today = today.getTime();
// THE CHART
Highcharts.setOptions({
colors: ['#058DC7']
});
Highcharts.ganttChart('container', {
title: {
text: 'Gantt Chart Test'
},
xAxis: {
currentDateIndicator: true,
min: today - 3 * day,
max: today + 18 * day
},
series: [{
name: 'Test series',
data: [{
taskName: 'My 1st row',
id: 'r1',
start: today + 4.5 * day,
milestone: true,
color: grey_OrigTargDate
}, {
taskName: 'My 1st row',
id: 'r2',
start: today + 7.5 * day,
milestone: true,
color: green_Ontarget
}, {
taskName: 'My 2nd row',
id: 'r3',
start: today + 9.5 * day,
milestone: true,
color: blue_Completed
}, {
taskName: 'My 2nd row',
id: 'r4',
start: today + 11.5 * day,
milestone: true,
color: red_OffTrackMajor
}]
}]
});
I am trying to chart 2 milestones on each line. I know that the x range series chart could accomplish something similar but I'd much prefer to use the gantt option. I'd appreciate any help.
You can solve this problem by assigning categories to the Y-axis
yAxis: {
type: 'category',
categories: ['A', 'B'],
reversed: true,
},
In order for this to work you could change the taskName with y: 0 / y: 1. It will work if your taskName is identical to your category names, but using y: 0 is more dynamic if you were to change the categories.
Your code will look like this in the end:
var today = new Date(),
day = 1000 * 60 * 60 * 24;
var grey_OrigTargDate = '#A6A6A6';
var blue_Completed = '#2e75b6';
var green_Ontarget = '#00b050';
var yellow_OfftrackMinor = '#ffc000';
var red_OffTrackMajor = '#ff0000';
// Set to 00:00:00:000 today
today.setUTCHours(0);
today.setUTCMinutes(0);
today.setUTCSeconds(0);
today.setUTCMilliseconds(0);
today = today.getTime();
// THE CHART
Highcharts.setOptions({
colors: ['#058DC7']
});
Highcharts.ganttChart('container', {
title: {
text: 'Gantt Chart Test'
},
xAxis: {
currentDateIndicator: true,
min: today - 3 * day,
max: today + 18 * day
},
yAxis: {
type: 'category',
categories: ['A', 'B'],
reversed: true,
},
series: [{
name: 'Test series',
data: [{
y: 0, // taskName: 'A' works too
id: 'r1',
start: today + 4.5 * day,
milestone: true,
color: grey_OrigTargDate
}, {
y: 0,
id: 'r2',
start: today + 7.5 * day,
milestone: true,
color: green_Ontarget
},{
y: 1,
id: 'r3',
start: today + 9.5 * day,
milestone: true,
color: blue_Completed
}, {
y: 1,
id: 'r4',
start: today + 11.5 * day,
milestone: true,
color: red_OffTrackMajor
}]
}]
});
Here is a working JSFiddle https://jsfiddle.net/z86aq99g/1/
in my application I use angular Highstock for charts. I have a lot of data (many millions), and now i want to put the existing buttons for set date range (1week, 1 month, 3 months, 1year, all), but also to click on any button to send a new request to the server to not accumulating data. Example, if pressed button "one week", that only the server retrieves the data for one week, if you click on a button to one month, to take data for a single month. I do not want to call unnecessary data, and that the server slows down, if I'm looking for data for one month, and the server comes back to me all data. Here is the code. Thanks in advance
> https://jsfiddle.net/tf7pr1ft/
Thnx to #Grzegorz Blachliński i fix this. here is code
$scope.chartConfig1 = {
xAxis: {
ordinal: false,
//za dodat date range postavit events i range selectors
events : {
afterSetExtremes: getDateRange //here i add my custom function
}
},
yAxis: {
plotLines: [{
color: '#FF0000',
width: 1,
value: 11.50,
label: {text: '11.50'}
}]
},
options: {
chart: {
zoomType: 'x',
backgroundColor: 'rgba(255, 255, 255, 1)',
polar: true,
type: 'line',
borderRadius: 5
},
legend: {
enabled: true
},
rangeSelector: {
selected : 0, //here i defined default range
enabled: true,
inputStyle: {
color: 'black'
}
},
navigator: {
enabled: true
}
},
series: [],
title: {
text: 'Solar and Battery voltage average'
},
useHighStocks: true
},
$scope.chartConfig1.series.push({
id: 1,
name: "Solar voltage average",
data: $scope.data[0],
tooltip: {
valueDecimals: 2
}
}, {
id: 2,
name: "Battery voltage average",
data: $scope.data[1],
tooltip: {
valueDecimals: 2
}
}
);
and here is my custom function
getDateRange = function (e) {
var date1 = new Date(e.min);
var date2 = new Date(e.max);
var timeDiff = Math.abs(date2.getTime() - date1.getTime());
var diffDays = Math.ceil(timeDiff / (1000 * 3600 * 24));
$http.get(serviceBase + 'aaaaa/aaaaaa/' + $stateParams.klupaID + '/aaaaaaa?days=' + diffDays, function(data){
//ukoliko buden dodavat još koji config obavezno dodat broj koliko ih ima na i<=8
for (i=1; i<=8; i++){
$scope.chartConfigString = 'chartConfig' + i;
$scope.chartConfigString.series[0].setData(data);
$scope.chartConfigString.hideLoading();
}
});
};
Now i have another problems, i want to have four selectors (buttons), 1months, 3months, 1year and ALL. I set default range, when open app, on 1month, and here is problem. Buttons for another range is disabled, beacause i get data from url only for one month. I want to have enabled all buttons for select any range. Thnx
I'm working on project to display stock information using Highstock.
Question 1:
I tried to display ohlc data using ohlc graph, high and low using areasplinerange graph, and volume using column chart.
Everything works fine, if i zoom 1m, 3m, 6m, YTD, and 1y. Here is the snapshot. link1. But if zoom to All, The graph messed up like this link2.
Am i wrong in my coding or it's bug?
Question 2:
In the same chart, I have code to change the type of graph from ohlc to line. It works fine when I zoom to 1m, 3m. Here is the snapshot link3. But it show me no line chart when i zoom to 6m, 1y, and All. Here is the snapshot link4.
How can this happen?
Thank you for your help.
The Code:
Here is the code that i used to display the chart
$.getJSON(url, function (data1) {
$.getJSON(urlprediction, function (data2) {
var ohlc = [],
volume = [],
closed = [],
forecast = [],
dataLength1 = data1.length;
dataLength2 = data2.length;
for (i = 0; i < dataLength1; i++) {
ohlc.push([
data1[i][0], // the date
data1[i][1], // open
data1[i][2], // high
data1[i][3], // low
data1[i][4] // close
]);
closed.push([
data1[i][0], // the date
data1[i][4] // close
]);
volume.push([
data1[i][0], // the date
data1[i][5] // the volume
])
}
for (i = 0; i < dataLength2; i++) {
forecast.push([
data1[i][0], // the date
data1[i][1],
data1[i][2], // close
])
}
// set the allowed units for data grouping
var groupingUnits = [[
'week', // unit name
[1] // allowed multiples
], [
'month',
[1, 2, 3, 4, 6]
]];
$('#container').highcharts('StockChart', {
rangeSelector: {
selected: 1
},
title: {
text: title
},
yAxis: [{
title: {
text: 'OHLC'
},
height: 360,
lineWidth: 2
}, {
title: {
text: 'Volume'
},
top: 433,
height: 100,
offset: 0,
lineWidth: 2
}],
series: [{
type: 'ohlc',
name: stockname,
data: ohlc,
}, {
type: 'areasplinerange',
name: stockname,
data: data2,
}, {
type: 'column',
name: 'Volume',
data: volume,
yAxis: 1,
}]
});
});
});