Conditional formatting axis of chart.js as time scale - javascript

My line chart has date values like 2016-06-01, 2016-06-01, etc on x axis. According to date range loaded dynamically from json, I want to format it depending on the number of values:
In case of a small amount of values (<= 60 days) I would like to show only day and month (DD-MM) like 01-06, 02-16, etc
In case of a big amount of values (> 60 days) I would like to show only month and year (MM-YY) like 06-16, 07-16, etc.
DD-MM can be achieved like that:
xAxes: [{
type: 'time',
unit: 'day',
time: {
displayFormats: {
day: 'DD-MM',
}
}
}]
MM-YY like that:
xAxes: [{
type: 'time',
unit: 'month',
time: {
displayFormats: {
day: 'MM-YY',
}
}
}]
But I'm quite lost how to combine this two and make it happen on a condition like the number of values. Is there a way to do that with ticks option of scales axis or do I have to write a new scale type?

Related

Svelte Gantt Chart - Separate via months

I'm trying to have a gantt chart with all 12 months in it. I'm doing this by setting the from & to values to currentDate.clone().endOf('year') & currentDate.clone().endOf('year'). But the gantt chart stopping appearing when setting these values, if I print out the dates they are valid & weirdly if I change the values to startOf('week') & endOf('week)` it renders the chart fine. Is this possible to do?
Package I'm using: https://github.com/ANovokmet/svelte-gantt
Edit:
My code is based on this index.js file: https://github.com/ANovokmet/svelte-gantt/blob/gh-pages/index.js
It might be simpler to ask, how can I modify it to show months instead of hours/days?
function time(input) {
return moment(input, "MMMM DD YYYY");
}
const currentStart = time(responseData.currentDate);
const currentEnd = time(responseData.currentDate);
gantt.$set({
fitWidth: false,
columnUnit: 'month',
rowPadding: 6,
rowHeight: 52,
columnOffset: 28.8,
magnetOffset: 15,
from: currentStart.clone().startOf('year'),
to: currentStart.clone().endOf('year'),
minWidth: 800,
headers: [{ unit: 'month', format: 'MMMM YYYY' }, { unit: 'day', format: 'ddd DD' }]
});
TL;DR:
Svelte Gantt Year + Months example
(note: svelte-gantt behavior is a bit finicky in the REPL, you might have to resize the width of the render panel to display the default 10 rows/tasks).
Important Update
I have noticed that there is an issue in the months' column headers, with January and May being repeated, February being skipped, and ultimately a one-month offset from May onwards (causing December to disappear as well). This is because of the way svelte-gantt handles month duration internally: all months are set to 30 days (!!). You can see this in this source file at line 118. The author even commented that this was incorrect...
Knowing this, you might want to reconsider using this module for an accurate representation of a yearly/monthly Gantt diagram, unless you fork the project and implement a correct handling of variable month durations yourself!
How I got there
I used the svelte-gantt large dataset demo source as a starting point, removing the GanttOptions import and component (irrelevant for this question). What I was left with was a Gantt diagram for a single day, hourly columns, and 100 generated rows & tasks (which I shortened to 10). The key info there was obviously the options object passed to the SvelteGantt constructor as props:
let options = {
dateAdapter: new MomentSvelteGanttDateAdapter(moment),
rows: data.rows,
tasks: data.tasks,
timeRanges,
columnOffset: 15,
magnetOffset: 15,
rowHeight: 52,
rowPadding: 6,
headers: [{ unit: 'day', format: 'MMMM Do' }, { unit: 'hour', format: 'H:mm' }],
fitWidth: true,
minWidth: 800,
from: currentStart,
to: currentEnd,
tableHeaders: [{ title: 'Label', property: 'label', width: 140, type: 'tree' }],
tableWidth: 240,
ganttTableModules: [SvelteGanttTable]
}
The changes needed to get from a single day diagram with hourly columns to a single year diagram with monthly columns were quite straightforward.
I removed the timeRanges option (which was used to set lunch & dinner time ranges in the daily diagram but were no longer useful in the yearly diagram - although you might want to use them again for bank holidays, or summer vacation periods for instance), and the associated data.
Then I added the columnUnit option, setting it to 'month', and changed the columnOffset option, setting it to 1, in order to have 1 column per month.
Finally I adjusted the headers option to display the year in the top line, and the abbreviated months in the second line (column headers): headers: [{ unit: 'year', format: 'YYYY' }, { unit: 'month', format: 'MMM' }].
The final, modified options object:
let options = {
dateAdapter: new MomentSvelteGanttDateAdapter(moment),
rows: data.rows,
tasks: data.tasks,
columnUnit: 'month',
columnOffset: 1,
magnetOffset: 15,
rowHeight: 52,
rowPadding: 6,
headers: [{ unit: 'year', format: 'YYYY' }, { unit: 'month', format: 'MMM' }],
fitWidth: true,
minWidth: 800,
from: currentStart,
to: currentEnd,
tableHeaders: [{ title: 'Label', property: 'label', width: 140, type: 'tree' }],
tableWidth: 240,
ganttTableModules: [SvelteGanttTable]
}
I then modified the currentStart and currentEnd values to reflect the new range of the diagram (a full year) by setting them to the beginning and the end of the current year, repectively:
const currentStart = moment().clone().startOf('year');
const currentEnd = moment().clone().endOf('year');
Finally, I had to modify the task generation process in order to randomly generate tasks that were meaningful enough for the new scale/range of the diagram:
// start of task (random day (1-20) + month (1-12))
const rand_d = (Math.random() * 20) | 0 + 1
const rand_m = (Math.random() * 12) | 0 + 1
const from = moment(`${rand_d} ${rand_m}`, 'D M')
// duration of task (random, 5 to 60 days)
const rand_l = (Math.random() * 55) | 0 + 5
const to = from.clone().add(rand_l, 'days')
tasks.push({
type: 'task',
id: ids[i],
resourceId: i,
label: 'Task #' + ids[i],
from,
to,
classes: colors[(Math.random() * colors.length) | 0],
generation
});
As a final cleanup, since by now I was no longer using the time() utility function, relying entirely on moment instead, I also removed the import { time } from '../utils'; statement.

How to set default year in chartjs timeseries charts? Why is default year in chart.js version 3 2001?

I've data and chart options coming from Backend. I'm trying to set up charts based on the min/max values provided. Now these min/max values, at times, should match the same as that of the values for chart e.g.,
data : [{x: "Feb '21", y: 302}, {x: "Mar '21", y 123}]
and the options hold :
options: {
scales: {
x:{
max:"Jul '21",
min:"Feb '21",
time: {
unit: 'month',
displayFormats: {
month: MMM 'YY
}
}
}
}
So when it renders the chart, it generates the ticks on x-axis as Feb '01, Mar '01,...,'Jul '01
Can someone please help me understand, why does the default year falls back to 2001?
How can I modify that to accept 2021 by deafult?
This happens only in when the labelFormat is MMM 'YY. The other kind of formats understands that it is 2021.

Hide specific x axis **labels** in Chartjs

I would like to hide specific x axis labels that are less than moment().valueOf(). So If I have 12:40, 12:45, 12:50 and 12:55 is the current time then I want to display 12:55 and hide the past time interval labels. Key here is to only hide the labels not get rid of the them.
My chart max x-axis time is always present time and increases as you pan right.
I've managed to get filtered ticks time as an array ["12:55"] but the chart still shows all the other time before 12:55 .
My xAxis configuration
xAxes: [
{
xAxisID: 'x-1',
type: "time",
time: {
unit: "minute",
displayFormats: { minute: 'HH:mm' },
stepSize: 5,
min: chartMin,
max: chartDisplayMax,
},
afterBuildTicks: function (axis) {
const { ticks } = axis;
const ticksToDisplay= ticks && ticks.filter(tick => tick >= moment().subtract(5, "minutes").format('HH:mm'));
return ticksToDisplay;
},
ticks: {
display: true,
},
},
],

How to plot date/time on X but display only dates in Chart.js?

I have a series of data points, each for a specific date/time. The time between each point varies, and the range covers several days (up to 30 days). There may be multiple points per day, or there may be days with no data points.
I would like the X scale to show the dates (with day of week), and for the data points to be plotted where they should go based on both the date and time of day. For instance, 2018-02-15 12:00:00 would be half-way between the labels for 02-15 and 02-16.
Here is some sample code/data:
var chartData = {
type:'line',
data: {
datasets: [
{
label:'Depression',
backgroundColor:'#00f',
borderColor:'#00f',
data:[
{y:3,t:Date.parse('2018/02/20 05:01:16')},
{y:4,t:Date.parse('2018/02/20 15:03:09')},
{y:5,t:Date.parse('2018/02/21 05:04:09')},
{y:1,t:Date.parse('2018/02/21 08:06:09')},
{y:5,t:Date.parse('2018/02/22 05:05:09')},
],
fill:false,
},
{
label:'Anxiety',
backgroundColor:'#d0d',
borderColor:'#d0d',
data:[
{y:6,t:Date.parse('2018/02/20 05:01:16')},
{y:2,t:Date.parse('2018/02/20 15:03:09')},
{y:4,t:Date.parse('2018/02/21 05:04:09')},
{y:6,t:Date.parse('2018/02/21 08:06:09')},
{y:3,t:Date.parse('2018/02/22 05:05:09')},
],
fill:false,
},
{
label:'Activity',
backgroundColor:'#f90',
borderColor:'#f90',
data:[
{y:4,t:Date.parse('2018/02/20 05:01:16')},
{y:1,t:Date.parse('2018/02/20 15:03:09')},
{y:4,t:Date.parse('2018/02/21 05:04:09')},
{y:7,t:Date.parse('2018/02/21 08:06:09')},
{y:3,t:Date.parse('2018/02/22 05:05:09')},
],
fill:true,
},
{
label:'Physical Health',
backgroundColor:'#ffc',
borderColor:'#cc0',
data:[
{y:-2,t:Date.parse('2018/02/20 05:01:16')},
{y:-3,t:Date.parse('2018/02/20 15:03:09')},
{y:-2,t:Date.parse('2018/02/21 05:04:09')},
{y:-6,t:Date.parse('2018/02/21 08:06:09')},
{y:-5,t:Date.parse('2018/02/22 05:05:09')},
],
fill:true,
},
],
fill:false,
},
'options': {
responsive:true,
title:{
display:true,
text:'Recent History',
},
scales: {
xAxes:[{
display:true,
time: {
min:Date.parse('2018/02/01 00:00:00'),
max:Date.parse('2018/03/01 00:00:00'),
displayFormats:{
day:'ddd MM/DD',
},
unit:'day',
round:'day',
},
distribution:'linear',
scaleLabel: {
display:true,
labelString:'Date'
},
bounds:'data',
ticks: {
source:'data',
},
}],
yAxes:[{
display:true,
scaleLabel:'Rating'
}]
},
}
};
The code above only displays the first two data points in each series.
Thanks!
http://www.chartjs.org/docs/latest/axes/cartesian/time.html#ticks-source
http://www.chartjs.org/docs/latest/axes/labelling.html#scale-title-configuration
You could try changing
ticks: {
source:'data',
},
... to source:'auto' which auto-truncates tick labels for you, and just see what that looks like ...
The time-scale display format for X-axis is described here, so you can format it to whatever Moment.js time format you want, e.g. 'YYYY-mm-dd:
xAxes: [{
type: 'time',
time: {
displayFormats: {
quarter: 'MMM YYYY'
}
}
}]
... Though it looks like maybe you have that in there already. Is it not showing any x-axis ticks? Is it showing any errors?
Or you could try following this example to override the callback method for a custom 'tick' if you want to include something besides the date/time. Their example was to append a dollar sign before all the values. If you only want to deal w/date-time then the above solution ought to be enough.

Price history with chart js

I have this kind of data array:
[
{
"price":"49",
"date":"21\/01\/2018"
},
{
"price":"30",
"date":"01\/01\/2018"
},
{
"price":"32",
"date":"15\/11\/2017"
}
]
Now I want to create a chart with chartjs, that shows me a price curve for the last 12 month.
I wrote this little script to generate me the past months:
function getPreviousMonths() {
var months = [];
for (i = 0; i < 12; i++) {
var month = moment().subtract(i, 'months').format('MMMM Y');
months.push(month);
}
return months.reverse();
}
How can I create the chartjs chart now? I looked in the docs, but got very confused when it comes to set dates within axes...
See http://www.chartjs.org/docs/latest/axes/cartesian/time.html for setting time scale on xAxes, then you have to convert your date field to a real date object:
xAxes: [{
type: 'time',
distribution: 'linear',
ticks: {
source: 'labels'
},
time: {
unit: 'month',
unitStepSize: 1,
displayFormats: {
'month': 'MMM'
}
}
}
Check this jsfiddle showing an example of time serie rendered as a line: https://jsfiddle.net/beaver71/9f9a2z88/
You have 2 separate your data array into 2 different arrays. One of dates (say dates_array) and another of price (say price_array). Then you just have to create new chart.
var chart = new Chart(element, {
type: 'line',
data: {
labels: dates_array,
datasets: [{
label: '# price',
data: price_array
}]
}
});
Here, element is the element in which chart will be shown. labels will be assigned the date array and data will be assigned price array. You can check this jsfiddle https://jsfiddle.net/j7gta8yn/

Categories