Chartjs X axis label and tick formatting - javascript

I am using Chartjs to display a time series line graph.
My setup is as following...
this.chartSetup = {
type: 'line',
data: {
labels: this.times,
datasets: [{
fill: true,
lineTension: 0.1,
backgroundColor: "rgba(75,192,192,0.4)",
borderColor: "rgba(75,192,192,1)",
label: title,
data: this.vals,
pointRadius: 0,
}]
},
options: {
spanGaps: true,
legend: {
position: 'bottom',
labels: {
boxWidth: 10
}
},
tooltips: {
enabled: false
},
responsive: true,
maintainAspectRatio: false,
scales: {
xAxes: [{
ticks: {
stepSize: 6,
unitStepSize: 5
},
type: 'time',
time: {
displayFormats: {
hour: 'h:mm a',
minute: 'h:mm a',
}
}
}],
},
}
};
Chart.defaults.global.animation.easing = "easeOutBounce";
this.chart = new Chart(this.canvas.nativeElement, this.chartSetup);
My chart looks like the following screen shot
The data is Date for the X axis (Labels), and just numbers for the Y.
The time data goes from 6am to 6pm (12 hours worth)
I have a couple of issues here all relating to the X axis label formatting.
The initial 6am label is being cut off
How can I change the X axis label rotation (so perhaps by 90 degrees will fix the truncated first value)
My data goes to 6pm, but it is showing an extra X axis value (7pm). Can I get rid of this?
Thanks in advance for any suggestions here.

What worked for me was setting
autoskip: true
autoSkipPadding: 30
in the tick configuration.

1,2 - set minRotation = 90
3 - set Max value on your x axes

Related

Chart.js : How I change the x axes ticks labels alignment in any sizes?

How can I move my labels on my x axes in between another x axes label. Nothing seems to work and I was unable to find anything on the docs. Is there a workaround? I'm using line chart time series.
https://www.chartjs.org/samples/latest/scales/time/financial.html
Currently, with the code I have its generating the figure below:
var cfg = {
elements:{
point: {
radius: 4
}
},
data: {
datasets: [
{
label: 'vsy',
backgroundColor: color(window.chartColors.red).alpha(0.5).rgbString(),
borderColor: window.chartColors.red,
data: firstData,
type: 'line',
pointRadius: 2,
fill: false,
lineTension: 0,
borderWidth: 2
},
{
label: 'de vsy',
backgroundColor: color(window.chartColors.blue).alpha(0.5).rgbString(),
borderColor: window.chartColors.blue,
data: dataMaker(15),
type: 'line',
pointRadius: 2,
fill: false,
lineTension: 0,
borderWidth: 2
}
],
},
options: {
animation: {
duration: 0
},
scales: {
xAxes: [{
type: 'time',
distribution: 'series',
offset: true,
time: {
unit: 'month',
displayFormats: {
month: 'MMM'
}
},
ticks: {
autoSkip: true,
autoSkipPadding: 75,
sampleSize: 100
},
}],
yAxes: [{
gridLines: {
drawBorder: false
}
}]
},
tooltips: {
intersect: false,
mode: 'index',
}
}
};
This is what I have now:
I want the labels on the x-axis to be on center instead of below the y axis grid line.
Thanks to uminder, with his comment it solves the issue but now I have a conflicting tooltip which lie on a same grid. When I hover to april line first point it shows me mar 30 which lies just above it and vice versa.
I fixed it by changing the mode to nearest but why is it activating the another point?
The option you're looking for is offsetGridLines.
If true, grid lines will be shifted to be between labels.
xAxes: [{
...
gridLines: {
offsetGridLines: true
}
In most cases, this produces the expected result. Unfortunately it doesn't work for time axes as documented in Chart.js issue #403. Thanks to Antti Hukkanen, there exists a workaround.
Please have a look at below runnable code snippet to see how it works.
function generateData() {
var unit = 'day';
function randomNumber(min, max) {
return Math.random() * (max - min) + min;
}
function randomPoint(date, lastClose) {
var open = randomNumber(lastClose * 0.95, lastClose * 1.05).toFixed(2);
var close = randomNumber(open * 0.95, open * 1.05).toFixed(2);
return {
t: date.valueOf(),
y: close
};
}
var date = moment().subtract(1, 'years');
var now = moment();
var data = [];
for (; data.length < 600 && date.isBefore(now); date = date.clone().add(1, unit).startOf(unit)) {
data.push(randomPoint(date, data.length > 0 ? data[data.length - 1].y : 30));
}
return data;
}
var TimeCenterScale = Chart.scaleService.getScaleConstructor('time').extend({
getPixelForTick: function(index) {
var ticks = this.getTicks();
if (index < 0 || index >= ticks.length) {
return null;
}
// Get the pixel value for the current tick.
var px = this.getPixelForOffset(ticks[index].value);
// Get the next tick's pixel value.
var nextPx = this.right;
var nextTick = ticks[index + 1];
if (nextTick) {
nextPx = this.getPixelForOffset(nextTick.value);
}
// Align the labels in the middle of the current and next tick.
return px + (nextPx - px) / 2;
},
});
// Register the scale type
var defaults = Chart.scaleService.getScaleDefaults('time');
Chart.scaleService.registerScaleType('timecenter', TimeCenterScale, defaults);
var cfg = {
data: {
datasets: [{
label: 'CHRT - Chart.js Corporation',
backgroundColor: 'red',
borderColor: 'red',
data: generateData(),
type: 'line',
pointRadius: 0,
fill: false,
lineTension: 0,
borderWidth: 2
}]
},
options: {
animation: {
duration: 0
},
scales: {
xAxes: [{
type: 'timecenter',
time: {
unit: 'month',
stepSize: 1,
displayFormats: {
month: 'MMM'
}
},
gridLines: {
offsetGridLines: true
}
}],
yAxes: [{
gridLines: {
drawBorder: false
}
}]
},
tooltips: {
intersect: false,
mode: 'index'
}
}
};
var chart = new Chart('chart1', cfg);
<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.9.3/Chart.min.js"></script>
<canvas id="chart1" height="90"></canvas>
For chartJs v3 you can use offset property:
scales: {
x: {
grid: {
offset: true
}
},
...
}

ChartJS, Primeng, Gap first and end of line chart

I am using primeng chart component which uses chartjs. We are using chartjs 2.5.0 alongside primeng 4.0 and angular 4.
I created a dynamic chart and I put the data into chart after it came to us through some services. The problem is, after a while chartjs will put a gap at first and end of the chart.
Here is our options for chartjs:
this.options = {
responsive: true,
tooltips: {
mode: 'index',
intersect: false, // all points in chart to show tooltip
callbacks: { // adding labels as title in tooltip
title: function(tooltipItems, data) {
let date = tooltipItems[0].xLabel;
return me._rhDatePipe.transform(date, 'time');
}
}
},
hover : {
mode: 'index',
intersect: false
},
scales: {
xAxes: [{
type: 'time',
display: false, // preventing labels from being displayed
max: 20
}],
yAxes: [{
ticks: {
maxTicksLimit: 3
}
}]
}
}
and here is our first data settings:
this.data = {
labels: this.labels, // current time as label
datasets: [
{
label: me._messageService.translate('chart-label-buy'),
data: this.buyingData,
fill: false,
borderColor: "#2453db",
lineTension: 0,
borderWidth: 1.5,
radius: 0 // removing dot points on chart
},
{
label: me._messageService.translate('chart-label-sale'),
data: this.sellingData,
fill: false,
borderColor: "#f44336",
borderWidth: 1.5,
lineTension: 0,
radius: 0 // removing dot points on chart
},
{
label: me._messageService.translate('chart-label-last-trade'),
data: this.lastPriceData,
fill: false,
borderColor: "#000000",
lineTension: 0,
borderWidth: 1.5,
radius: 0 // removing dot points on chart
}
]
}
and here is the loop which will update the chart:
if(sortedKeysList != null) {
for(let key in sortedKeysList) {
let currentTime: number = sortedKeysList[key];
// just add new points
if(!this.currentTimes.includes(currentTime)) {
let date = new Date(currentTime);
this.labels.push(date);
this.currentTimes.push(currentTime);
this.buyingData.push(this.bestLimitsChart[currentTime].buy);
this.sellingData.push(this.bestLimitsChart[currentTime].sale);
if(this.bestLimitsChart[currentTime].price != 0)
this.lastPriceData.push(this.bestLimitsChart[currentTime].price);
else
this.lastPriceData.push(null);
this.updateChart();
}
}
}
and the picture of chart:
I do not know what is going on. Any helps will greatly appreciated.
I finally found the problem,
for other people facing this issue, you can add unit to your axis:
xAxes: [{
type: 'time',
time: {
displayFormats: {
minute: 'h:mm', // formatting data in labels
},
unit: 'minute' // destroy first and end gaps
},
display: false, // preventing labels from being displayed
}],
similar issue on github:
https://github.com/chartjs/Chart.js/issues/2277#issuecomment-314662961

How to display x and y axis values of where the crosshair line intersects in series using Highcharts?

I have been working on a project that uses highchart to draw multiple line charts. With that, I have already achieved some configuration (eg. x-axis crosshair, fixed tooltip, zooming) for my chart in accordance to the business requirements. As such, one requirement is that when x-axis crosshair moves from left to right (or vice versa) within the plotted chart, the selected values on y-axis and dates and time on x-axis will be displayed in the fixed tooltip.
Example:
Series CDT158 has a value of 100.46 (as shown in Chart 1) on 27 Apr 2017 20:49:48 pm. When I move the x-axis crosshair to the right, it jumps directly to the next point which has a value of 103.47 (as shown in Chart 2) on 27 Apr 2017 20:50:38 pm.
Is it possible when the crosshair passes through the series (from one point to the next point), the y-axis values between 100.46 and 103.47 as well the datetime values on x-axis will be rendered in the fixed tooltip? I also want to display the CDEP158 y-axis values simultaneously in the fixed tooltip. How can I achieve this?
[Chart 1]
[Chart 2]
This is the Highchart initialization.
myChart = new Highcharts.Chart({
chart: {
renderTo: 'container',
type: 'line',
zoomType: 'xy',
panning: true,
panKey: 'shift',
plotBorderWidth: 1
},
title: {
text: ''
},
legend: {
layout: 'horizontal',
align: 'left',
itemDistance: 10,
borderWidth: 0,
itemMarginTop: 0,
itemMarginBottom: 0,
padding: 20
},
plotOptions: {
series: {
states: {
hover: {
enabled: false
}
},
dataLabels: {
enabled: false,
format: '{y}'
},
allowPointSelect: false
}
},
xAxis: {
type: 'datetime',
labels: {
rotation: -65,
style: {
fontSize: '9px',
fontFamily: 'Verdana, sans-serif'
}
},
crosshair: true,
dateTimeLabelFormats: {
second: '%d %b %Y <br/> %H:%M:%S %P',
}
},
yAxis: {
gridLineColor: '#DDDDDD',
gridLineWidth: 0.5
},
tooltip: {
positioner: function () {
return {
x: this.chart.plotLeft,
y: this.chart.plotTop
}
},
useHTML: true,
formatter: function (tooltip) {
var formattedDate = Highcharts.dateFormat('%d %b %Y <br/> %H:%M:%S %P', new Date(this.x));
const header = `<span style="font-size: 8px">${formattedDate}</span><br/>`;
let body = this.points.reduce((body, p) => body + `<small><font color="${p.series.color}"><strong>${p.series.name}</strong></font>: <strong>${p.y}</strong></small><br/>`, '');
return header + body;
},
shared: true,
valueDecimals: 2,
//followPointer: true,
shadow: false,
borderWidth: 0,
backgroundColor: 'rgba(255,255,255,0.8)'
},
series: [{
name: 'CDT158',
data: [
[1493326164493, 87.54],
[1493326174018, 97.09],
[1493326188245, 100.46],
[1493326238635, 103.47],
[1493326284930, 106.12]
]
},{
name: 'CDEP158',
data: [
[1493326165993, 94.34],
[1493326174772, 88.87],
[1493326188895, 98.25],
[1493326272411, 96.48],
[1493326299520, 100.13]
]
}]
});
I'm pretty sure that this can be achieved through Highcharts but, my limited knowledge and experience in Highchart stops me to figure it out.
I have posted a sample fiddle that illustrate the above scenario.
Any help is greatly appreciated.

Why does Chartjs line chart does not animate to new values?

I am using a chartjs line chart showing time data where I want to update with new values from an ajax call.
I have time on the X axis (1 days on 15 minutes intervals) , and just numbers for the Y axis.
So it looks like the following..
My setup is as follows.
this.chartSetup = {
type: 'line',
data: {
labels: this.times,
datasets: [{
fill: true,
lineTension: 0.1,
backgroundColor: "rgba(75,192,192,0.4)",
borderColor: "rgba(75,192,192,1)",
label: title,
data: this.vals,
pointRadius: 0,
}]
},
options: {
spanGaps: true,
legend: {
position: 'bottom',
labels: {
boxWidth: 10
}
},
tooltips: {
enabled: false
},
responsive: true,
maintainAspectRatio: false,
scales: {
xAxes: [{
ticks: {
stepSize: 6,
unitStepSize: 5
},
type: 'time',
time: {
displayFormats: {
hour: 'h:mm a',
minute: 'h:mm a',
}
}
}],
},
}
};
Chart.defaults.global.animation.easing = "easeOutBounce";
this.chart = new Chart(this.canvas.nativeElement, this.chartSetup);
When the chart first draws it animates the new values. However, if I then remove the data, and add a new set, it does not animate to the new values.
For example, to mimic new server data, using a timer, if I do the following,
where this.times holds the X data, this.vals holds the Y..
setTimeout(() => {
let temp = this.makeDummyData(20);
//let dataToUpdate = (<any>(this.chart.data)).datasets[0].data;
//let labels = (<any>(this.chart.data)).labels;
this.times.splice(0, this.times.length);
this.vals.splice(0, this.vals.length);
for (let item of temp) {
this.times.push(item.time);
this.vals.push(item.val);
}
this.chart.update();
}, 8000)
The chart does update, but it does not have any animation, it just jumps straight to the new display. If I just date the exiting data without removing it first, does does animate to the new display.
Is there any reason the animation does not work when I replace the data as in the above example?
Thanks in advance for any suggestions

Is it possible to use disproportionate intervals in jqplot?

I am using jqplot for a bar graph that has 1 very high number around 7 million and 2 very low numbers, less than 100,000 when they are charted on a bar graph the two low numbers are only a couple of pixels tall, you can barely see them.
I am wondering if it is possible to use disproportionate intervals on the x axis for example the lower half of the chart would be say 0 - 100,000 and then the upper half would go from 100,000 - 7,000,000
I can't find it in the options anywhere, I've set custom 'ticks' but all that does is squeeze all the low numbers into a tiny space at the bottom.
Google is no help.
Here is what I have so far
function init_graph() {
var line1 = [19877, 6643895, $('#inpSalary').val()];
var tickers = ['low number', 'huge number', 'low number'];
plot1 = $.jqplot('jqplot', [line1], {
stackSeries: true,
legend: {
show: true,
location: 'ne'
},
title: 'Data per month stack by user',
seriesColors:['#fcbaac','#f5564d','#1e3045'],
seriesDefaults: {
renderer: $.jqplot.BarRenderer,
rendererOptions: {
varyBarColor: true,
// barPadding: 6,
// barMargin: 15,
barWidth: 60,
highlightMouseOver: true
}
// shadowAngle: 135
},
series: [{
label: 'User1'
}],
axes: {
xaxis: {
renderer: $.jqplot.CategoryAxisRenderer,
ticks: tickers
},
yaxis: {
ticks: [0,20000,30000,40000,50000,60000,70000,80000,90000,100000,7000000],
min: 0
}
}
});
}
You could switch to using the LogAxisRenderer, which by default with give you a log base 10 scale on the y-axis:
<snip>
axes: {
xaxis: {
renderer: $.jqplot.CategoryAxisRenderer,
ticks: tickers
},
yaxis: {
renderer: $.jqplot.LogAxisRenderer,
min: 100
}
}
<snip>

Categories