Svelte Gantt Chart - Separate via months - javascript

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.

Related

Chart Js - Limit points on graph without limiting data

I have a chart.js chart that needs to get plotted from a large number of points (say 1000). When I plot all these points it looks pretty bad, so I looked for a way to limit those. I used the method described here:
Limit data points with chart js
This works, but there is a big problem. It misses some important highs and lows of the 1000 points, basically plotting an incorrect chart.
Is there a way to not do this without missing some values? Basically plotting the chart with all the 1000 points but displaying like 30 on it.
I have tried a few plugins (decimation, downsample), but it seems they require vectors to work (like {x,y}). My data is an array of strings used for dates on the x axis and an array of float numbers used for prices on the y axis.
Thanks!
You can use the Chart.js inbuilt Data Decimation plugin.
Your base data consists of two arrays, one contains the date strings, the other contains the prices. These can easily be converted into an array of data points (objects having an x and y property each) as follows.
data: dateStrings.map((d, i) => ({ x: Date.parse(d), y: prices[i] }))
Further you must meet all the requirements of the decimation plugin. I also had to explicitly define options.parsing: false.
Please take a look at the runnable code and see how it works.
const dateStrings = [];
const prices = [];
// create sample data (dateStrings & prices)
const date = new Date();
date.setDate(date.getDate() - 100);
for (let i = 0; i < 100; i ++) {
date.setDate(date.getDate() + 1);
dateStrings.push(date.toISOString().substring(0,10));
prices.push(parseInt(Math.random() * 1000));
}
new Chart('myChart', {
type: 'line',
data: {
datasets: [{
label: 'My Dataset',
data: dateStrings.map((d, i) => ({ x: Date.parse(d), y: prices[i] })),
lineTension: 0.3,
borderColor: 'rgb(100, 100, 255)'
}],
},
options: {
parsing: false,
plugins: {
decimation: {
enabled: true,
algorithm: 'lttb',
samples: 20,
threshold: 20
}
},
scales: {
x: {
type: 'time',
time: {
unit: 'day',
displayFormats: {
day: 'D MMM yyyy'
},
tooltipFormat: 'D MMM yyyy'
}
}
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.7.1/chart.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.2/moment.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/chartjs-adapter-moment/1.0.0/chartjs-adapter-moment.min.js"></script>
<canvas id="myChart" height="100"></canvas>

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/

Conditional formatting axis of chart.js as time scale

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?

Make Google chart show past X number of data samples and not just past 3 days

I am using angular-google-chart and angularjs to display charts.
https://github.com/angular-google-chart/angular-google-chart
I have a set of data consisting of 100 data points in json which looks like this;
[{
value: 400,
time: "2014-05-07 13:51:39"
},
{
value: 425.28,
time: "2014-05-04 16:19:58"
},
{
value: 425.6,
time: "2014-05-03 16:19:56"
},
// rest of samples fall before 2014-05-04 time range
]
The problem is that on the Google Chart, it only shows the first 2 samples because for some reason, the chart only shows samples of past recent 3 days.
Here is the relevant javascript code for configuring Google Chart options.
var google_chart_options =
{
'hAxis': {
'title': "Date",
'format': "dd-MMM-yyyy HH:mm:ss",
},
};
var horizontal_axis = {id: "t", label: "Date", type: "datetime"};
var vertical_axis= {id: "s", label: "value", type: "number"};
var type = 'LineChart';
I have some questions;
What options should I use to get Google Chart to display data points of past X days or X number of data points?
What options should I use to get Google Chart to display data points from, say, 2014-05-01 16:30 to 2014-05-07 13:30?
I will answer your 2nd question.
What options should I use to get Google Chart to display data points
from, say, 2014-05-01 16:30 to 2014-05-07 13:30?
Here is the code.
var google_chart_options =
{
'hAxis': {
'title': "Date",
'format': "dd-MMM-yyyy HH:mm:ss",
viewWindow: {
min: new Date(2014, 4, 1, 16, 30), //note that month lies from 0 to 11 (December)
max: new Date(2014, 4, 7, 13, 30),
},
}
};
The key lies in the viewWindow options under hAxis.

Kendo StockChart incorrect extrapolation with stepped line chart

We are trying to emulate a stepped area chart with Kendo UI to denote the direction/position in which an actuator travels over a period of time (+1 denoting clockwise, -1 denoting a counter-clockwise direction) during the course of a day. The period of time over which the actuator completes its transition is not constant.
The code is included below (alternatively please feel free to use the following jsfiddle at http://jsfiddle.net/pirahawk/XT6CZ/15/ to avoid any setup). We have used the Kendo Stockchart because it provides a complementary navigator component which allows the user to restrict the graph to a desired time period. However we are finding oddities in the chart api especially when the data set has large time lapses. As shown in the example below, we started off with receiving inputs that have 1 second intervals between them. Plotting these on the chart achieves the shape that we expect to see. However adding additional datapoints that are now apart by a few hours (please uncomment the data points shown), we are finding that the chart api seems to somehow incorrectly extrapolate the earlier data points.
When using the navigator to restrict the chart to the earlier period (between 2am - 2:30am), we do once again achieve the original shape. However we would like to have the same shape shown without having to do this (i.e. stop the api from extrapolating data points with large intervals between data points). Any help, advise on this issue would be greatly appreciated
var dataForSource = [{
date: new Date("December 16, 2013 02:06:00 AM"),
Count: 0
}, {
date: new Date("December 16, 2013 02:07:00 AM"),
Count: 1
},
{
date: new Date("December 16, 2013 02:09:00 AM"),
Count: 0
}, {
date: new Date("December 16, 2013 02:09:15 AM"),
Count: -1
},
{
date: new Date("December 16, 2013 02:09:45 AM"),
Count: 0
},
{
date: new Date("December 16, 2013 02:10:00 AM"),
Count: -1
}, {
date: new Date("December 16, 2013 02:15:00 AM"),
Count: 0
}
//Uncomment these out to see issue
/*
, {
date: new Date("December 16, 2013 04:10:01 PM"),
Count: -1
}
, {
date: new Date("December 16, 2013 11:55:00 PM"),
Count: 0
} */
];
var staticDataSource = new kendo.data.DataSource({
type: "line",
data: dataForSource
});
function createChart() {
$("#chart").kendoStockChart({
dataSource: staticDataSource,
dateField: "date",
series: [{
type: "line",
style: 'step',
//missingValues: "interpolate",
field: "Count",
categoryField: "date"
}],
xAxis: {
baseUnit: "seconds"
},
navigator: {
series: {
type: "line",
style: 'step',
field: "Count"
},
xAxis: {
baseUnit: "hours"
},
}
});
};
$(document).ready(createChart);
First of all, you're using wrong baseUnit in your example. It's property of categoryAxis not xAxis and seems to does not work in here.
You need to choose what do you wanna show in your chart, when you show long time period, by default data is aggregate to show in group which you define in baseUnit property. To "turn it off" you should set it for smallest available option: minutes. Now you gonna have all data points in your chart, but it's look even worse, just peek in JSFiddle what I prepared for you for see what i mean: http://jsfiddle.net/XT6CZ/18/
Regards

Categories