I am using highstock to show fund data in a line graph, the data series I am using is missing dates (weekends mostly). I have no control over the source code of the data series, though I have formatted it by looping through it in JS so it can be used with Highstock. The problem occurs when I use the rangeSelector on missing dates, when I choose From in the range selector to a weekend date (a date that is missing in the data series) the compare percentage starts calculating from the next available data point.
Example
Go to this demo: http://www.highcharts.com/stock/demo/compare
Choose 14 dec, 2014 as the From date.
The calculation starts from 2014-12-15 as the 0 value, which is a monday. I want it to always start the compare percentage at the previous data point, not the next. I would want it to start from 2014-12-12 which is a friday.
How I initiate highstocks in JS:
var opts = {
chart : {
backgroundColor: '#f9f9fa',
borderWidth: 0,
type: 'line'
},
rangeSelector : {
buttons: [{
type: 'month',
count: 3,
text: '3m'
}, {
type: 'month',
count: 6,
text: '6m'
}, {
type: 'ytd',
text: 'YTD'
}, {
type: 'year',
count: 1,
text: '1y'
}, {
type: 'all',
text: 'All'
}],
selected : 3,
inputEnabled: false
},
plotOptions: {
series: {
compare: 'percent'
}
},
title : {
text : ''
},
navigator : {
enabled : false
},
credits: {
enabled: false
},
series : [{
name : '',
data : seriesData,
lineColor : '#002855',
tooltip: {
valueDecimals: 4
},
turboThreshold : 0
}],
xAxis : {
dateTimeLabelFormats: {
millisecond: '%H:%M:%S.%L',
second: '%H:%M:%S',
minute: '%H:%M',
hour: '%H:%M',
day: '%e %b.',
week: '%b %y',
month: '%b %y',
year: '%Y'
},
ordinal: false,
minTickInterval : 4 * 7 * 24 * 3600 * 1000
},
yAxis : {
labels: {
format: '{value} %'
}
},
tooltip : {
valueSuffix : '',
pointFormat : '<span style="color:{point.color}">\u25CF</span> {point.y} SEK ({point.change}%)<br/>'
}
};
$(el).highcharts('StockChart', opts);
My seriesData var looks like this after I have formatted it for use in Highstock:
[
{ name="2005-01-03", x=1104710400000, y=100 },
{ name="2005-01-04", x=1104796800000, y=99.9983 },
{ name="2005-01-05", x=1104883200000, y=99.9175014 },
{ name="2005-01-07", x=1105056000000, y=100.0739722 },
// This continues to the current date
]
x is a timestamp.
y is the stock value
name is a string representing the timestamp in x
el is defined before, and is a element node object.
Is it possible to make the compare start at the previous available data point and not the next when a date in the data series does not exist?
Any help regarding this issue will be appreciated!
Edit: Added my solution as an answer below
I found afterSetExtremes very useful to modify min and max, I check if the date is a saturday or sunday. If its a saturday I go -1 day back in time, if its a sunday I go -2 days back in time. This should cover all normal weekends for me, and it works quite good. Though it wont cover holidays that occur on weekdays. I also do not modify the max time, though I might need to do that.
My current xAxis:
xAxis : {
events: {
afterSetExtremes: function(e){
var maxTimestamp = this.max,
minTimestamp = this.min;
var dateMin = new Date( minTimestamp ),
dateMax = new Date( maxTimestamp ),
minDayOfTheWeek = dateMin.getDay();
// Test to see if its saturday or sunday
if( minDayOfTheWeek === 6 || minDayOfTheWeek === 0 ){
if( minDayOfTheWeek === 6 ){
dateMin.setDate( dateMin.getDate() - 1 );
}else if( minDayOfTheWeek === 0 ){
dateMin.setDate( dateMin.getDate() - 2 );
}
this.setExtremes( dateMin.getTime(), dateMax.getTime() );
}
}
},
dateTimeLabelFormats: {
millisecond: '%H:%M:%S.%L',
second: '%H:%M:%S',
minute: '%H:%M',
hour: '%H:%M',
day: '%e %b.',
week: '%b %y',
month: '%b %y',
year: '%Y'
},
ordinal: false,
minTickInterval : 4 * 7 * 24 * 3600 * 1000
}
And added this to the chart cause afterSetExtremes do not run on load for me:
chart : {
backgroundColor: '#f9f9fa',
borderWidth: 0,
events:{
load: function(){
var max = this.xAxis[0].max,
min = this.xAxis[0].min;
this.xAxis[0].setExtremes( min, max );
}
},
type: 'line'
}
Related
I use Chart.js to plot a line chart. My x axis is a timeseries.
However, I have ticks overlapping due to weekend data.
Is there any way to avoid overlapping ticks? I used "autoskip: true" but it doesn't seem to work.
Here's how my configs look like:
x: {
parsing: false,
type: "timeseries",
time: {
displayFormats: {
second: "HH:mm",
minute: "HH:mm",
hour: "HH:mm",
day: "MMM d",
},
tooltipFormat: "d MMM HH:mm",
},
grid: {
display: false,
},
ticks: {
autoskip: true,
},
},
And this is how the overlapping looks like:
You can achieve what you're looking for by writing a callback function that checks if the value is a weekend day
new Date(Date.parse(value)).getDay() == 1 || new Date(Date.parse(value)).getDay() == 2
If it is we just don't return it. Otherwise we return the value without changes.
This is how your config should look like:
x:{
type: 'timeseries'
time:{
unit:'day',
},
ticks:{
callback: function(value){
//if weekend dont return the tick label
if(new Date(Date.parse(value)).getDay() == 1 || new Date(Date.parse(value)).getDay() == 2 )
return
return value
}
}
}
I am quite new to stackoverflow, django and highcharts so I apologies for any inconvenience.
So I am currently working with displaying time and dates in highcharts when I noticed a small problem.
When looking at the chart everything looks fine like this.
But when I hover my mouse about a point on the chart it shows a different value. It looks like this
I would like so that when focusing on a point it will display as HH:MM:SS like it does on the side instead of the total amount of microseconds. What do I need to change to make it happen?
Here is the code for the chart
<script>
$(function () {
$('#container').highcharts({
chart:
{
type: 'line'
},
title:
{
text: 'Time worked by {{user_to_show}}'
},
xAxis:
{
categories: {{dates|safe}}
},
yAxis:
[{
title:
{
text: ''
},
gridLineWidth: 1,
type: 'datetime', //y-axis will be in milliseconds
dateTimeLabelFormats:
{ //force all formats to be hour:minute:second
second: '%H:%M:%S',
minute: '%H:%M:%S',
hour: '%H:%M:%S',
day: '%H:%M:%S',
week: '%H:%M:%S',
month: '%H:%M:%S',
year: '%H:%M:%S'
},
opposite: true
}],
plotOptions:
{
series:
{
dataLabels:
{
enabled: true,
formatter: function()
{
if( this.series.index == 0 )
{
return secondsTimeSpanToHMS(this.y/1000) ;
}
else
{
return this.y;
}
}
}
}
},
series:
[{
name: 'Time',
yAxis: 0,
data: {{time|safe}}
}]
});
});
function secondsTimeSpanToHMS(s) {
var h = Math.floor(s / 3600); //Get whole hours
s -= h * 3600;
var m = Math.floor(s / 60); //Get remaining minutes
s -= m * 60;
return h + ":" + (m < 10 ? '0' + m : m) + ":" + (s < 10 ? '0' + s : s); //zero padding on minutes and seconds
}
</script>
If you would like more code, data or information please tell me.
Thanks in advance for anyone that replies.
I was able to find a solution to my problem. Had to add:
tooltip:
{
formatter: function ()
{
var text = this.x + ': ' + secondsTimeSpanToHMS(this.y/1000);
return text;
}
},
and it gave this result:
(Would mark this post as answered but stackoverflow wont let me do it until 2 days)
I've built something like this. I get my data from the server, put it in an object called series, and pass it to 'series' in Highcharts code block. Basically, for every staff, there will be a date, and my default value(Y-Axis) is '1' for now. However, I can't get dates on the chart as expected even if it looks that I had correct data and did correct parsing. Unexpectedly, I get my millisecond values as Y-axis values, which does not make any sense, and every staff has a default date, which is 1 January. (For ex., staff 1, 1 January, x-axis value = 1554422400000)
I get dates like this, 19-02-2019 17:32. Then I split them, and use it like this,
([Date.UTC(parseInt(yearsplit[0]), datesplit[1]-1, parseInt(datesplit[0])), 1])
which looks exactly the same format in Highcharts, ([Date.UTC(1971, 2, 16), 0.86])
var responsePromise = $http.post('statistics/getAllProtocolRecords', data, null);
responsePromise.success(function (dataFromServer, status, headers, config) {
var series = [{
name: "",
data: []
}];
dataFromServer.protocolRecords.forEach((data) => {
var datesplit = data.checkupDate.split("-");
var yearsplit = datesplit[2].split(" ");
series.push({
name: data.staff,
data: [Date.UTC(parseInt(yearsplit[0]), datesplit[1]-1, parseInt(datesplit[0])), 1]
})
});
series.shift();
Highcharts.chart('container', {
chart: {
type: 'spline'
},
title: {
text: 'Toplam Muayene Kaydı (' + sysrefHcCheckupType + ')'
},
subtitle: {
text: ''
},
xAxis: {
type: 'datetime',
dateTimeLabelFormats: { // don't display the dummy year
month: '%e. %b',
year: '%b'
},
title: {
text: 'Tarih'
}
},
yAxis: {
title: {
text: 'Toplam Muayene (Gün)'
},
min: 0
},
tooltip: {
headerFormat: '<b>{series.name}</b><br>',
pointFormat: '{point.x:%e. %b}: {point.y:%f} '
},
plotOptions: {
spline: {
marker: {
enabled: true
}
}
},
colors: ['#00bdff', '#FF0700', '#df0300', '#ff0700', '#c0df00'],
series: series
});
});
I've just realized that I'd made a little mistake in push function. In series.push, 'data' should like this, surrounded by array brackets:
data: [
[ Date.UTC(parseInt(yearsplit[0]), parseInt(datesplit[1])-1, parseInt(datesplit[0])), 1]
]
i added this code
scales:
{
xAxes:
[{
type: 'time',
time:
{
unit: 'month',
displayFormats: { month: 'MM' },
max: '2017-10-09 18:43:53',
min: '2017-10-02 18:43:53'
}
}]
},
to the options but it does not work. Any ideas what i'm making wrong?
FIDDLE ->
https://jsfiddle.net/o473y9pw/
Since you wish to use time for x-axis, your labels array should be an array of date/time string (labels array is correspondent to x-axis).
You would also need to set the parser property (to parse the date/time correctly), and x-axis' ticks source to data (to properly generate x-axis ticks).
UPDATE
If you only have a min and max date then, you can create a nice little plugin to populate the labels (date) array dynamically, as such :
plugins: [{
beforeInit: function(chart) {
var time = chart.options.scales.xAxes[0].time, // 'time' object reference
// difference (in days) between min and max date
timeDiff = moment(time.max).diff(moment(time.min), 'd');
// populate 'labels' array
// (create a date string for each date between min and max, inclusive)
for (i = 0; i <= timeDiff; i++) {
var _label = moment(time.min).add(i, 'd').format('YYYY-MM-DD HH:mm:ss');
chart.data.labels.push(_label);
}
}
}]
note: moment.js is used to make calculations easier.
ᴡᴏʀᴋɪɴɢ ᴇxᴀᴍᴘʟᴇ ⧩
( for demonstration purposes, I've changed the time unit to day )
$(document).ready(function() {
new Chart(document.getElementById("chartBox"), {
type: 'line',
data: {
datasets: [{
data: [12, 19, 3, 5, 2, 3, 32, 15],
label: "",
borderWidth: 2,
borderColor: "#3e95cd",
fill: false,
pointRadius: 0
}]
},
options: {
scales: {
xAxes: [{
type: 'time',
time: {
parser: 'YYYY-MM-DD HH:mm:ss',
unit: 'day',
displayFormats: {
day: 'ddd'
},
min: '2017-10-02 18:43:53',
max: '2017-10-09 18:43:53'
},
ticks: {
source: 'data'
}
}]
},
legend: {
display: false
},
animation: {
duration: 0,
},
hover: {
animationDuration: 0,
},
responsiveAnimationDuration: 0
},
plugins: [{
beforeInit: function(chart) {
var time = chart.options.scales.xAxes[0].time, // 'time' object reference
timeDiff = moment(time.max).diff(moment(time.min), 'd'); // difference (in days) between min and max date
// populate 'labels' array
// (create a date string for each date between min and max, inclusive)
for (i = 0; i <= timeDiff; i++) {
var _label = moment(time.min).add(i, 'd').format('YYYY-MM-DD HH:mm:ss');
chart.data.labels.push(_label);
}
}
}]
});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/moment#latest/moment.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.0/Chart.min.js"></script>
<canvas id="chartBox"></canvas>
The dataset should be an array of objects with properties x for time and y for value.
$(document).ready(function() {
var data = [{
x: new moment().add(-10, "months"),
y: Math.random() * 100
},
{
x: new moment().add(-8, "months"),
y: Math.random() * 100
},
{
x: new moment().add(-6, "months"),
y: Math.random() * 100
},
{
x: new moment().add(-4, "months"),
y: Math.random() * 100
},
{
x: new moment().add(-2, "months"),
y: Math.random() * 100
},
{
x: new moment().add(-0, "months"),
y: Math.random() * 100
},
];
new Chart(document.getElementById("chartBox"), {
type: 'line',
data: {
datasets: [{
data: data,
borderColor: "#3e95cd",
fill: false
}]
},
options: {
scales: {
xAxes: [{
type: 'time'
}]
},
legend: false
}
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.0/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.18.1/moment.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.0/Chart.bundle.min.js"></script>
<canvas id="chartBox"></canvas>
Fiddle
If the xAxis label is in date format use this code
time:
{
format: 'MM',
unit: 'month',
displayFormats: { month: 'MM' },
max: '2017-10-09 18:43:53',
min: '2017-10-02 18:43:53'
}
If the xAxis label is as what u used numerals use
time:
{
format: 'MM',
unit: 'month',
parser:'MM',
displayFormats: { month: 'MM' },
max: '2017-10-09 18:43:53',
min: '2017-00-02 18:43:53'
}
You might want to change a few things.
Your data should include time data.
If you set scale unit to be month, your min max should be more than one month to see the actual scales.
Here's simplified working example.
https://jsfiddle.net/f2xmkkao/
Background info: I'm tracking updates of a game which happens like once a day, so I add a unix timestamp in milliseconds to my database, which all works fine.
What I don't know is how to make the y-axis of a highchart a 'static' 24 hour range. What I want is something like this:
(dont look my paint skills)
I've got the x-axis working but I don't know how to set the y-axis correct, to a static 24h range.
My current progress here:
$('#chart').highcharts({
chart: {
type: 'column'
},
xAxis: {
type: 'datetime',
tickInterval: 24 * 3600 * 1000,
},
yAxis: {
labels: {
formatter: function() {
return moment.utc(this.value).format('HH:mm:ss');
}
},
title: {
text: 'static 24h range on this axis'
},
},
series: [{
name: 'time',
data: [[1447459200000, 1447498374000], [1447545600000, 1447559210000]]
}]
});
http://jsfiddle.net/yrqzcys2/8/
Is there a way to achieve this, I've searched around and couldn't find a solution to this situation.
Any help is appreciated!
EDIT:
So the data in the series is a unix timestamp which converted to a datetime, it will contain a date and a time. Basically I want the date on the x-axis and the time on the y-axis. Thus the 24hour range on the y-axis.
I figured it out with help of jlbriggs by calculating the number of seconds. This is done by subtracting the unix timestamp of the date on 00:00 from the exact unix timestamp.
http://jsfiddle.net/yrqzcys2/16/
$(function() {
var data = [];
/* Pretend that this part of code is in a loop and add an array to the main data array */
// from database
var datetime = moment.utc("2015-11-14 10:52:54");
var day = moment.utc(datetime.format('YYYY-MM-DD')).valueOf();
var exact = datetime.valueOf();
var temp = [];
temp.push(day);
temp.push(exact - day);
data.push(temp);
/* End of loop */
$('#chart').highcharts({
chart: {
type: 'column'
},
xAxis: {
type: 'datetime',
tickInterval: 24 * 3600 * 1000,
},
yAxis: {
labels: {
formatter: function() {
return moment.utc(this.value).format('HH:mm:ss');
}
},
title: {
text: 'Now shows a \'static\' range'
},
tickInterval: 3600 * 1000,
},
tooltip: {
useHTML: true,
formatter: function() {
return moment.utc(this.y, 'x').format('HH:mm:ss z');
}
},
series: [{
name: 'time',
data: data
}]
});
});