Highcharts is working when not calling Gantt chart in other views. I've played around trying to get this Gantt chart to work. Looks like there is a conflict with import map only calling highcharts and highcharts.ganttChart is not found. I've tried added the src url through a script tag directly in the html view but still getting this error.
Error msg <
Uncaught (in promise) TypeError: Highcharts.ganttChart is not a function
>
Javascript controller
```
import {Controller} from "#hotwired/stimulus"
export default class extends Controller {
connect() {
let today = new Date(),
day = 1000 * 60 * 60 * 24;
today.setUTCHours(0);
today.setUTCMinutes(0);
today.setUTCSeconds(0);
today.setUTCMilliseconds(0);
today = today.getTime();
let dateFormat = Highcharts.dateFormat;
let series
let chart; // global
async function requestData() {
const response = await fetch('http://localhost:3000/api/v1/charts/index');
if (response.ok) {
const data = await response.json();
series = data.map(function (job, i) {
let data = job.deals.map(function (phase) {
return {
id: 'deal-' + i,
project: phase.project,
client: phase.client,
phase: phase.phase,
start: new Date(phase.from).getTime(),
end: new Date(phase.to).getTime(),
y: i
};
});
return {
name: job.name,
data: data,
current: job.deals[job.current]
};
});
setTimeout(requestData, 1000);
console.log(series);
chart = Highcharts.ganttChart('container', {
series: series,
title: {
text: 'Crew Schedule'
},
tooltip: {
pointFormat: '<span>Client {point.client}</span><br/>' +
'<span>Project: {point.project}</span><br/>' +
'<span>Phase: {point.phase}</span><br/>' +
'<span>From: {point.start:%e. %b}</span><br/><span>To: {point.end:%e. %b}</span>'
},
lang: {
accessibility: {
axis: {
xAxisDescriptionPlural: 'The chart has a two-part X axis showing time in both week numbers and days.',
yAxisDescriptionSingular: 'The chart has a tabular Y axis showing a data table row for each point.'
}
}
},
scrollbar: {
enabled: true
},
rangeSelector: {
enabled: true,
selected: 0
},
accessibility: {
keyboardNavigation: {
seriesNavigation: {
mode: 'serialize'
}
},
point: {
valueDescriptionFormat: 'Assigned to {point.project} from {point.x:%A, %B %e} to {point.x2:%A, %B %e}.'
},
series: {
descriptionFormatter: function (series) {
return series.name + ', job ' + (series.index + 1) + ' of ' + series.chart.series.length + '.';
}
}
},
xAxis: {
currentDateIndicator: true
},
navigator: {
enabled: true,
liveRedraw: true,
series: {
type: 'gantt',
pointPlacement: 0.5,
pointPadding: 0.25,
accessibility: {
enabled: false
}
},
yAxis: {
min: 0,
max: 3,
reversed: true,
categories: []
}
},
yAxis: {
type: 'category',
grid: {
columns: [{
title: {
text: 'Crew'
},
categories: series.map(function (s) {
return s.name;
})
}]
}
}
})
}
return chart;
}
window.addEventListener('load', (event) => {
requestData();
});
}
}
Related
I have an API which returns JSON data like this:
[
{
"name":"Something",
"data":[
{
"x":1541096421,
"y":2
},{
"x":1541436378,
"y":4
},{
"x":1553621371,
"y":2
}
]
},{
"name":"Something else",
"data":[
{
"x":1541096421,
"y":2
},{
"x":1541436378,
"y":4
},{
"x":1553621371,
"y":2
}
]
}
]
The x axis represents date/time and the y axis is a score. It's plotted on a chart like this, using some formatting to convert the date from millisecond timestamp to a readable date format:
function renderChart(data) {
$('#chartContainer').highcharts({
chart: {
type: 'scatter',
zoomType: 'xy'
},
title: {
text: chartTitle()
},
xAxis: {
allowDecimals: false,
title: {
text: 'Date completed',
scalable: false
},
type: 'datetime',
labels: {
formatter: function () {
if (true) {
return Highcharts.dateFormat('%d-%b-%y', moment.unix(this.value));
}
else {
if (this.value > 0 && this.value < 24) {
return this.value;
}
else
return 0;
}
}
},
tickPixelInterval: 100
},
yAxis: {
title: {
text: 'Score'
}
},
plotOptions: {
scatter: {
marker: {
radius: 5
}
}
},
series: data,
exporting: {
buttons: {
contextButton: {
menuItems: Highcharts.getOptions().exporting.buttons.contextButton.menuItems.filter(item => item !== 'openInCloud')
}
}
// Tried adding this but it doesn't make any difference:
/*,
csv: {
dateFormat: '%d/%m/%Y'
}*/
},
tooltip: {
formatter: function () {
return 'Score of <b>' + this.y + '</b> posted on <b>' + Highcharts.dateFormat('%d-%b-%y', moment.unix(this.x)) + '</b>';
}
}
});
}
This works fine. However, when I click 'export to CSV' in the Highchart graph on the front-end it outputs a CSV file where the date is always showing as "18/01/1970". Obviously it's something to do with the fact that the API is returning a timestamp value, but I don't see how I can modify the format in the CSV similar to how it's done in the chart rendering code.
Can anyone advise how (preferably without modifying the data returned by the API) to get the CSV to output a correct date in day/month/year format?
Many thanks
It can be done easily by wrapping Highcharts.Chart.prototype.getDataRows method and map the data array which is used for export. Check demo and code posted below.
Code:
(function(H) {
H.wrap(H.Chart.prototype, 'getDataRows', function(proceed, multiLevelHeaders) {
var rows = proceed.call(this, multiLevelHeaders);
rows = rows.map(row => {
if (row.x) {
row[0] = Highcharts.dateFormat('%d-%b-%y', row.x * 1000);
}
return row;
});
return rows;
});
}(Highcharts));
Demo:
https://jsfiddle.net/BlackLabel/yafx8cb1/1/
Docs:
https://www.highcharts.com/docs/extending-highcharts/extending-highcharts
As per #Core972's comment, the issue here is related to the timestamp in the API returning the date as seconds rather than milliseconds. I don't believe there is a way to manipulate the date format in the CSV export specifically so it will require a change to the API which returns the data.
Wojciech Chmiel's answer demonstrates how to override Highchart's output to re-format the date from a non-ideal source.
I've implemented a polar chart in which each series has 4 values corresponding to 4 categories. When I export the chart csv, the category column contains polar coordinates. I would like to replace these with the corresponding category name. How do I do this?
Adding the categories to each series, had no effect. I also tried adding a categories property to the xAxis, but it had not effect. An xAxis.label formatter successfully returns the category name for each data polar coordinate.
const options = {
chart: {
polar: true,
},
title: {
text: '',
},
tooltip: {
valueDecimals: 2,
headerFormat: '<br/>',
},
legend: {},
pane: {
startAngle: 0,
endAngle: 360,
},
xAxis: {
tickInterval: 45,
min: 0,
max: 360,
labels: {
// eslint-disable-next-line
formatter: function() {
switch (this.value) {
case 45:
return '<b>Experience</b>'
case 135:
return '<b>Frictionless</b>'
case 225:
return '<b>Low Price</b>'
case 315:
return '<b>Brand</b>'
default:
return ''
}
},
},
},
yAxis: {
min: 0,
max: 10,
labels: {
format: '{}',
},
},
plotOptions: {
series: {
pointStart: 45,
pointInterval: 90,
},
column: {
pointPadding: 0,
groupPadding: 0,
},
},
series: kahnSeries,
}
You need to use categories property, but without options like: pointInterval, pointStart, min and max:
xAxis: {
categories: ['Experience', 'Frictionless', 'Low Price', 'Brand']
},
Live demo: http://jsfiddle.net/BlackLabel/z8cm1p39/
API Reference: https://api.highcharts.com/highcharts/xAxis.categories
To avoid changing the chart's current display, I wrapped the getCSV function and replaced the CSV category values. If there was a simpler way, please share it.
{
(function (H) {
H.wrap(H.Chart.prototype, 'getCSV', function (
proceed,
useLocalDecimalPoint
) {
// Run the original proceed method
const result = proceed.apply(
this,
Array.prototype.slice.call(arguments, 1)
)
const itemDelimiter = ','
const lineDelimiter = '\n'
const rows = result.split(lineDelimiter)
let newResult = ''
let rowCategories = false
rows.forEach((row, rowIndex) => {
const columns = row.split(itemDelimiter)
if (rowIndex === 0 && columns[0] === '"Category"') {
rowCategories = true
}
if (rowIndex > 0 && rowCategories) {
let newRow = formatter(columns[0])
columns.forEach((column, columnIndex) => {
if (columnIndex > 0) {
newRow += itemDelimiter
newRow += column
}
})
newResult += newRow
} else {
newResult += row
}
if (rowIndex < rows.length - 1) {
newResult += lineDelimiter
}
}
)
return newResult
})
}(Highcharts))
}
Following is my highcharts config, can you help me to why my alternate months are coming, and if i want to show every month, how do i do it ? Also, to change width of the bar if showing every month.
Highcharts.chart("energy_chart", {
chart: {
type: "column",
spacingBottom: 15,
spacingTop: 10,
spacingLeft: 10,
spacingRight: 10,
backgroundColor: "#f2f2f2",
events: {
load: function() {
var fin = new Date();
var finDate = fin.getDate();
var finMonth = fin.getMonth();
var finYear = fin.getFullYear();
var ini = new Date();
ini.setFullYear(ini.getFullYear() - 1);
var iniDate = ini.getDate();
var iniMonth = ini.getMonth();
var iniYear = ini.getFullYear();
if (this.yAxis[0].dataMax == 0) {
this.yAxis[0].setExtremes(null, 1);
}
//this.yAxis.set
console.log(new Date(Date.UTC(iniYear, iniMonth, iniDate)))
console.log(new Date(Date.UTC(finYear, finMonth, finDate)))
this.xAxis[0].setExtremes(
Date.UTC(iniYear, iniMonth, iniDate),
Date.UTC(finYear, finMonth, finDate)
);
},
drilldown: function(e) {
console.log('drilldown')
var charts_this = this;
var inidrillDate = new Date(e.point.x);
setTimeout(function() {
inidrillDate.setDate(0);
inidrillDate.setMonth(inidrillDate.getMonth());
var DateinidrillDate = inidrillDate.getDate();
var MonthinidrillDate = inidrillDate.getMonth();
var YearinidrillDate = inidrillDate.getFullYear();
var findrillDate = inidrillDate;
findrillDate.setMonth(findrillDate.getMonth() + 1);
findrillDate.setDate(findrillDate.getDate() - 1);
var DatefindrillDate = findrillDate.getDate();
var MonthfindrillDate = findrillDate.getMonth();
var YearfindrillDate = findrillDate.getFullYear();
console.log(Date.UTC(
YearinidrillDate,
MonthinidrillDate,
DateinidrillDate
))
console.log(Date.UTC(
YearfindrillDate,
MonthfindrillDate,
DatefindrillDate
))
charts_this.xAxis[0].setExtremes(
Date.UTC(
YearinidrillDate,
MonthinidrillDate,
DateinidrillDate
),
Date.UTC(
YearfindrillDate,
MonthfindrillDate,
DatefindrillDate
)
);
if (charts_this.yAxis[0].dataMax === 0) {
charts_this.yAxis[0].setExtremes(null, 1);
}
}, 0);
}
}
},
title: {
text: '<p className="energy_gen">Energy Generated</p>'
},
exporting: { enabled: false },
xAxis: {
type: "datetime",
labels: {
step: 1
},
dateTimeLabelFormats: {
day: "%e"
}
},
yAxis: {
title: {
text: "kWh"
}
},
credits: {
enabled: false
},
plotOptions: {
series: {
cursor: "pointer",
dataLabels: {
enabled: true,
format: "{point.y}"
},
color: "#fcd562",
point:{
events:{
click:function(event){
}
}
}
}
}
},
tooltip: {
formatter: function() {
if (this.point.options.drilldown) {
return (
"Energy generated: <b> " +
this.y +
"</b> kWh " +
"<br>" +
Highcharts.dateFormat("%b %Y", new Date(this.x))
);
} else {
return (
"Energy generated: <b> " +
this.y +
"</b> kWh " +
"<br>" +
Highcharts.dateFormat("%e %b %Y", new Date(this.x))
);
}
}
},
series: [{'data':obj.data,'name':obj.name,"color":"#4848d3"}],
drilldown: {
series: obj.data
}
});
Also, attaching the screenshot of the rendered highchart with drilldown.
,
Now drilldown graph(same sort a issue)
EDIT:
It turnsout to be a zoom issue, i.e if i zoomout enough, then it shows all points. So, how to show every point without zooimg out .
Working fiddle:
http://jsfiddle.net/gkumar77/w9ngp63u/5/
Use tickInterval property and set it to one month:
xAxis: {
type: "datetime",
tickInterval: 30 * 24 * 3600 * 1000,
labels: {
step: 1
},
dateTimeLabelFormats: {
day: "%e"
}
}
Live demo: http://jsfiddle.net/BlackLabel/q5coany2/
API: https://api.highcharts.com/highcharts/xAxis.tickInterval
I have a simple json document :
[ {
"x" : "a",
"y" : 2
}, {
"x" : "b",
"y" : 8
}, {
"x" : "c",
"y" : 4
}, {
"x" : "d",
"y" : 15
} ]
I want to visualize it using Highcharts having 4 series. I could success, however, the data appeared only as one series (see the next Figure).
Here is part of the code:
var options = {
.
.
.
series: [{ }]
};
.
.
.
var data = JSON.parse(json);
var seriesData = [];
for (var i = 0; i < data.length; i++) {
seriesData.push([data[i].x, data[i].y]);
options.xAxis.categories.push( data[i].x );
}
options.series[0].data = seriesData;
var chart = new Highcharts.Chart(options);
also updating the series
chart.series[0].update({
type: type,
});
works fine.
using
options.series.push({name: data[i].x, data: [data[i].x, data[i].y]});
creates 4 series but not appropriately visualized and also updating the series
chart.series[0].update({
type: type,
});
doesn't work, therefore, I want to focus in the first mentioned method.
any hints?
EDit: code which partially works for me:
var options = {
chart: {
renderTo: 'container',
type: 'column' //default
},
title: {
text: ''
},
yAxis: {
title: {
enabled: true,
text: 'Count',
style: {
fontWeight: 'normal'
}
}
},
xAxis: {
title: {
enabled: true,
text: '',
style: {
fontWeight: 'normal'
}
},
categories: [],
crosshair: true
} ,
plotOptions: {
pie: {
innerSize: 125,
depth: 80
},
column: {
pointPadding: 0.2,
borderWidth: 0,
grouping: false
}
},
series: [{ }]
};
// Set type
$.each(['column', 'pie'], function (i, type) {
$('#' + type).click(function () {
chart.series[0].update({
type: type
});
});
var data = get the data fom json file**
var seriesData = [];
for (var i = 0; i < data.length; i++) {
seriesData.push([data[i].x, data[i].y]);
options.xAxis.categories.push( data[i].x );
}
options.series[0].data = seriesData;
var chart = new Highcharts.Chart(options);
});
});
You have to decide whether you want to have 4 different series and update all 4 series at once or you want to have one series an then build e.g. legend on your own.
If you want to have 4 series, set grouping to false, set xAxis categories and each point should be mapped to one series with one point - the point.x should have the index of the series.
const series = data.map((point, x) => ({ name: point.x, data: [{ x, y: point.y }]}))
const chart = Highcharts.chart('container', {
chart: {
type: 'column'
},
plotOptions: {
column: {
grouping: false
}
},
xAxis: {
categories: data.map(point => point.x)
},
series: series
});
Then you can update your all 4 series:
chart.series.forEach(series => series.update({
type: series.type === 'column' ? 'scatter' : 'column'
}, false))
chart.redraw()
example: http://jsfiddle.net/pdjqrj5y/
Am trying to plot Spline graph using data fetched from MySQL.
<script>
var chart;
function requestData()
{
$.ajax({
url: 'get_hourly_data.php',
datatype: "json",
success: function(data)
{
chart.series[0].addPoint(data);
},
});
}
$(document).ready(function() {
Highcharts.setOptions({
global: {
useUTC: false
}
});
chart = new Highcharts.Chart({
chart: {
renderTo: 'graph',
type: 'spline',
events: {
load: requestData
}
},
title: {
text: 'Monitoring'
},
xAxis: {
type: 'datetime',
tickPixelInterval: 150,
maxZoom: 20 * 1000
},
yAxis: {
minPadding: 0.2,
maxPadding: 0.2,
title: {
text: 'Response time (ms)',
margin: 80
}
},
series: [{
name: 'Time',
data: [],
}]
});
});
</script>
get_hourly_data.php
<?php
// Query to fetch half an hour data from current time
$query = "SELECT UNIX_TIMESTAMP(current_ts), response_time FROM site_response WHERE current_ts >= DATE_SUB( NOW( ) , INTERVAL 30 MINUTE ) AND site_id ='2'";
$result = mysql_query($query,$db);
while($item = mysql_fetch_array($result))
{
$current_time = $item['UNIX_TIMESTAMP(current_ts)']*1000;
$response_time = $item['response_time']*1;
$ret = array($current_time, $response_time);
echo json_encode($ret);
}
?>
My JSON data looks like this
[1357536877000,0.012178182601929][1357536881000,0.0123610496521][1357536891000,0.011971950531006][1357536895000,0.011821985244751][1357536916000,0.010467052459717]
If I limit only one return value by replacing query mentioned below in get_hourly_data.php am able to plot graph with one value but not for group of values. Requirement is to fetch half an hour data from now and plot spline chart.
$query = "SELECT UNIX_TIMESTAMP(current_ts), response_time FROM site_response WHERE current_ts >= DATE_SUB( NOW( ) , INTERVAL 30 MINUTE ) AND site_id ='2' limit 1";
I suppose am doing something wrong when adding server data to graph .Probably after jQuery success function .Am first time stackoverflow user and new to jQuery. Could someone please guide me?
Posting code which works perfectly.
<script>
var chart;
function requestData()
{
$.ajax({
url: 'get_hourly_data.php',
datatype: "json",
success: function(data)
{
chart.series[0].setData(data);
},
});
}
$(document).ready(function() {
Highcharts.setOptions({
global: {
useUTC: false
}
});
chart = new Highcharts.Chart({
chart: {
renderTo: 'graph',
type: 'spline',
events: {
load: requestData
}
},
title: {
text: 'Monitoring'
},
xAxis: {
type: 'datetime',
tickPixelInterval: 150,
maxZoom: 20 * 1000
},
yAxis: {
minPadding: 0.2,
maxPadding: 0.2,
title: {
text: 'Response time (ms)',
margin: 80
}
},
series: [{
name: 'Time',
data: [],
}]
});
});
</script>
get_hourly_data.php:
$ret = array();
while($item = mysql_fetch_array($result))
{
$current_time = $item['UNIX_TIMESTAMP(current_ts)']*1000;
$response_time = $item['response_time']*1;
$y = rand(0, 100);
$ret[] = array($current_time, $response_time);
}
echo json_encode($ret);
You are echoing inside the while loop. You need to create an array of array for the data.
You need your JSON to look like
[[1357536877000,0.012178182601929],[1357536881000,0.0123610496521],[1357536891000,0.011971950531006],[1357536895000,0.011821985244751],[1357536916000,0.010467052459717]]
Something like this may work
$dataArr=new array();
while($item = mysql_fetch_array($result))
{
$current_time = $item['UNIX_TIMESTAMP(current_ts)']*1000;
$response_time = $item['response_time']*1;
$dataArr[] = array($current_time, $response_time);
}
echo json_encode($dataArr);
$query = "SELECT UNIX_TIMESTAMP(current_ts) * 1000, response_time FROM site_response WHERE current_ts >= DATE_SUB( NOW( ) , INTERVAL 30 MINUTE ) AND site_id ='2'";
$dataArr = new array();
while($item = mysql_fetch_array($result))
{
$current_time = $item['UNIX_TIMESTAMP(current_ts)'];
$response_time = $item['response_time'];
$dataArr[] = array($current_time, $response_time);
}
echo json_encode($dataArr);
On success function
success: function(data)
{
data = jQuery.parseJSON(data);
chart.series[0].addPoint(data);
}