Zingchart plotting X-axis with Times, not Dates - javascript

I want to plot data with my X-axis representing Timespans (elapsed times), not actual dates.
I have a series with the following (string) values:
times: "00:00:00", "00:01:00", "00:10:00", "00:11:00"
I parse these values into (int)
times: 0, 6000, 60000, 66000
But when I draw the graph, the hour field is wrong. It shows "2" instead of "0" or "00". Minutes and seconds seem fine:
Here is my json code. I played with the Hours field, with no success:
// Description of the graph to be displayed
vm.chartJson = {
type: 'line',
scaleX: {
transform: {
type: 'date',
all: '%H:%h:%G:%g:%i:%s'
}
},
series: [{ values: data }]
};
How can I display the Hours field, while still manipulating TIMES and not Datetimes? How would that go if the total number of hours goes above 24? I would be okay with either displaying the total number of hours, or adding a day field. Example:
"124:22:01" or
"5:4:22:01"
Thank you

One issue I can note is we take time in milliseconds. So one minute = 60000 milliseconds. This could be the first thing off. Tack on a zero to the end of all your values.
The second issue, I cannot duplicate your times exactly because your local machine timezone is being used and I think mine is different. We have attributes that account for this, but it may not be necessary. Read further.
You cannot display 124 hours natively in the library. Depending on your input data you can just format and plot your own values with a custom x-axis label and tokens. Since you seem to already have the string format you want, why not just continue to use that?
var customLabels = ['00:00:00', '00:01:00', '00:10:00','00:11:00'];
var myConfig = {
type: 'line',
scaleX: {
labels: customLabels
},
tooltip: {
textAlign: 'left',
text: '%kl<br>OR<br>%data-dates: %v'
},
series: [
{
values: [475, 420, 400, 500],
dataDates: customLabels, // one for each point in values array
}
]
};
zingchart.render({
id: 'myChart',
data: myConfig,
height: '100%',
width: '100%'
});
html, body {
height:100%;
width:100%;
margin:0;
padding:0;
}
#myChart {
height:100%;
width:100%;
min-height:150px;
}
.zc-ref {
display:none;
}
<!DOCTYPE html>
<html>
<head>
<script src= "https://cdn.zingchart.com/zingchart.min.js"></script>
</head>
<body>
<div id="myChart"><a class="zc-ref" href="https://www.zingchart.com">Powered by ZingChart</a></div>
</body>
</html>
Relative Documentation:
demo link
tokens. Third one down in the grid is custom tokens starting with data-.
tooltips
scales

Here is what I did to solve the issue:
// Determine the format of x-axis
var format = '%i:%s';
if (data[data.length - 1][0] >= 3600000) format = '%G:%i:%s';
// Description of the graph to be displayed
vm.chartJson = {
type: 'line',
scaleX: {
transform: {
type: 'date',
all: format
}
},
series: [{ values: data }],
"utc": true,
"timezone": 0
};
I can't display Hours more than 24, so I could display days if needed.

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.

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/

How to stop Flot Charts from re-ordering my data?

I am using a line chart. I feed the data the following:
var scheduled = [[51,1700],[52, 1750],[1,1600],[2,1675]];
var actual = [[51,1320],[52, 1550],[1,1575],[2,1600]];
In the above the first number of each set is the week of the year and I am trying to show the last 4 months of data.
However, when the chart is drawn Flot charts re-sorts the data by the first value (lowest to highest) which creates all kinds of issues. Instead of 4 columns in the series there are now 52, and the lines are quite out of whack.
I don't see anything in the documentation that says this is supposed to happen, nor do I see anything that says I can prevent it. However, for the data to be meaningful, the data must not be re-ordered.
Is there a setting I'm unaware of that can stop this behavior?
Edit : Adding plot code
var plot = $.plot('#scheduled-actual-flot-line', [
{
label: 'Scheduled Hours',
data: scheduled,
lines: { show: true, lineWidth: 2, fill: true, fillColor: { colors: [{ opacity: 0.5 }, { opacity: 0.5 }] } },
points: { show: true, radius: 4 }
},
{
label: 'Actual Hours',
data: actual,
lines: { show: true, lineWidth: 2, fill: true, fillColor: { colors: [{ opacity: 0.5 }, { opacity: 0.5 }] } },
points: { show: true, radius: 4 }
}],
{
series: {
lines: { show: true },
points: { show: true },
shadowSize: 0 // Drawing is faster without shadows
},
colors: ['#afd2f0', '#177bbb'],
legend: {
show: true,
position: 'nw',
margin: [15, 0]
},
grid: {
borderWidth: 0,
hoverable: true,
clickable: true
},
yaxis: { ticks: 4, tickColor: '#eeeeee' },
xaxis: { ticks: 12, tickColor: '#ffffff' }
}
);
Flot takes the x values as numbers and displays / sorts them accordingly. If you don't want that, you can use the category mode (see this example and this fiddle with your data).
xaxis: {
//ticks: 12,
tickColor: '#ffffff',
mode: 'categories'
}
PS: 12 ticks are not possibly with your data, as there are only 4 datapoints defined.
That flot reads all data as numbers by default is described here in the documentation.
Flotr examples use a for loop to create random data, so the first index will always be sequential.
[[51,1700],[52, 1750],[1,1600],[2,1675]];
Your arrays show that flotr must be doing a sort on the array before painting the data sets as lines, bar-graphs or whatever.
I can only suggest you create a timestamp from the months and there's a time setting you can in flotr settings to format the dates as you want.
The other way is replace your anomalous data (months) with sequential indices:
var arr = [[51,1700],[52, 1750],[1,1600],[2,1675]];
for(var i=0; i<arr.length; i++) arr[1][0] = i;
Flot is doing exactly what it should do for a line chart (or any type of x-y graph). It's showing the last two points of your dataset on the left because 1 and 2 are indeed less than 51 and 52. I'm guessing that you're trying to show data that crosses a year boundary. You need to make the first two weeks of the second year later than the last two of the first. You could use actual dates instead of week numbers, in which case Flot would handle it fine. That would also give you more flexibility in labeling the x-axis. But as a quick fix, just add 52 to the second year's data, e.g.:
var scheduled = [[51,1700],[52, 1750],[53,1600],[54,1675]];
var actual = [[51,1320],[52, 1550],[53,1575],[54,1600]];

highstock issue : pointInterval property is not working when series data set contains date and value?

I used pointInterval property to set interval as 30 days but my data set contains daily data.
That method is not working. chart is displaying all the points.
please check below image for the chart i got
https://docs.google.com/file/d/0B1wfs7NUcZnGQkk2QWdEbE9ndmM/edit
What is needed is to draw only three points for three months in the above chart.
Following is the code i used to do this.
Note that 'options.data.points' has data set in format of [[date,value],[]....]
var stockChartData = {
chart: {
renderTo: 'container'
},
title: {
text: "Stock Chart"
},
credits: {
enabled: false
},
yAxis: [
{
labels: {
align: 'left',
x: 2
},
title: {
enabled: false
},
lineWidth: 2
}
],
navigation: {
buttonOptions: {
enabled: false
}
},
scrollbar: {
enabled: false
},
series: [{data: options.data.points, type: 'line', pointInterval: 24 * 3600 * 1000 *30}]
};
stockChart = new Highcharts.StockChart(stockChartData, function (chart) {
});
please help me to solve this issue
http://jsfiddle.net/gh/get/jquery/1.7.2/highslide-software/highcharts.com/tree/master/samples/stock/plotoptions/pointinterval-pointstart/
The data set this example uses is in the format of [value,value,...] (see http://www.highcharts.com/samples/data/large-dataset.js).
So when pointInterval for a month is used, for example the last two values will be used for the values of last two months. For the same data when pointInterval for a year is used, the same last two values is used for the last two years. So the chart doesn't ignore the between values, therefore you will not have for example an average value of the month for day values.
EDIT:
You can use plotOptions.series.dataGrouping for grouping the day values in a month:
http://jsfiddle.net/worhscy0/

Categories