Decrease the number of visible labels under Ext JS 4 chart - javascript

I'm writing Ext JS 4 line chart component. It works all fine, but when I display labels under axis they are just too dense. I won't the number of visible labels to decrease. How to do that? Here's my code for the axis:
{
type: 'Category',
position: 'bottom',
fields: ['date'],
grid: true,
label: {
field: 'label',
rotate: { degrees:315 },
renderer: function(item) {
var date = new Date(item);
/* parseIntToStringWithZeros is a custom method somewhere else */
var day = parseIntToStringWithZeros(date.getDate());
var month = parseIntToStringWithZeros(date.getMonth());
result = day + '-' + month;
return result;
}
}
}

I think you might need to use a "Numeric" axis instead of a "Category" axis a start (not sure what your data looks like, but you might need to convert times to values to get it to work).
With the numeric axis you are supposed to be able to set the number of major tick values (where the grid lines and labels appear), by setting the steps property in the axis config; however this doesn't always work. A more surefire way is to override the applyData function which isn't documented so you need to search through the dev code to see what it's doing.
Also, to simply not render a particular label you can just return the empty string in the label renderer function. e.g. if you only want an individual "month" to show up once in the above code you could do something like this..
label: {
....
renderer: (function(){
var lastRenderedMonth = '';
return function(item){
... //your code above without the return..
if(month == lastRenderedMonth)
return "";
lastRenderedMonth = month;
return result;
};
})(),
...

Related

Highcharts JS- add third variable to tooltip for two series

I've already figured out how to make a chart using highcharts where there are three variables- one on the X axis, one on the Y axis, and one on the tooltip. The way to do this is to add the following to the tooltip:
tooltip: {
formatter () {
// this.point.x is the timestamp in my original chartData array
const pointData = chartData.find(row => row.timestamp === this.point.x)
return pointData.somethingElse
}
}
See this fiddle for the full code:
https://jsfiddle.net/m9e6thwn/
I would simply like to do the same, but with two series instead of one. I can't get it to work. I tried this:
tooltip: {
formatter () {
// this.point.x is the timestamp in my original chartData array
const pointData = chartData1.find(row => row.timestamp === this.point.x)
return pointData.somethingElse
const pointData2 = chartData2.find(row => row.timestamp === this.point.x)
return pointData2.somethingElse
}
}
Here is the fiddle of the above: https://jsfiddle.net/hdeg9x02/ As you can see, the third variable only appears on one of the two series. What am I getting wrong?
There are some issues with the way you are using the formatter now. For one, you cannot have two returns in the same function without any if clauses. That will mean that only the first return will be used.
Anyway, here are some improvements I suggest you do for your code.
Add the extra information for each point to highcharts, that makes it a lot easier to access this information through highcharts. E.g. in a tooltip. You can set the data like this:
chartData1.map(function(row) {
return {
x: row.timestamp,
y: row.value,
somethingElse: row.somethingElse
}
})
If you do that, then returning the correct tooltip for each series is a simple matter of doing this:
tooltip: {
formatter () {
// this.point.x is the timestamp in my original chartData array
return this.point.somethingElse
}
}
Working JSFiddle example: https://jsfiddle.net/ewolden/dq7L64jg/6/
If you wanted more info in the tooltip you could then do:
tooltip: {
formatter () {
// this.point.x is the timestamp in my original chartData array
return this.point.somethingElse + ", time: " + str(this.x) + ", value: " + str(this.y)
}
}
Addtionally, you need to ensure that xAxis elements, i.e. your timestamps are sorted. This is a requirement for highcharts to function properly. As it is, your example is reporting
Highcharts error #15: www.highcharts.com/errors/15
in console, because chartData2 is in reverse order. It looks okay for this example, but more complicated examples can lead to the chart not looking as you expect it to.
For this example using reverse is easy enough: data: chartData2.reverse().map(function(row) {return {x: row.timestamp, y: row.value, somethingElse: row.somethingElse}})
Working JSFiddle example: https://jsfiddle.net/ewolden/dq7L64jg/7/

Chart.js Bar Chart - grouping and mapping data

I am trying to use Chart.js Bar Chart to display a set of data.
My data is weekly based so to my method I send the year and week and get the data back in 3 columns; Product, Area and Amount.
What I want is to have to Products horizontaly and in each Product I want different bars for each Area and offcourse the Amount verticaly. (Bonus: If an Area nothing in that product it should not be shown in that particular Product)
The problem is that the number of Products and the number of Areas can vary from each week. And I can't seem to find a good way to loop through the data and create the datasets the way chart.js wants.
Also tried using Underscore.js to group it but the fact that the each Area doesn't always have an amount for a spesific product seems to be causing some issues.
So I guess you have to loop through the data and map that data to another predefined array for each Area so it can match this structure somehow??
Also open for other Chart plugins, but really liked how Chart.js animates the data. And if I get this working I can probably figgure out an update method for when you change week.
To get the labels i can f.ex do this:
$.ajax({
....
success: function (d) {
var a = _.groupBy(d.data, function (d) { return d.Product });
var labels = [];
$.each(a, function (i, value) {
labels.push(i);
});
}
});
With data in this format
var myJSONData = [
{
Product: 'P1',
Area: 'A1',
Value: 12
},
...
]
You can use this function to convert it into the format Chart.js requires
var data = {
labels: [],
datasets: []
}
var colors = ['Red','Blue','Green', ...] // add as many colors as there will be areas (maximum)
myJSONData.forEach(function (e) {
// create labels
var labelIndex = data.labels.indexOf(e.Product)
if (labelIndex === -1) {
labelIndex = data.labels.length;
data.labels.push(e.Product);
// dummy entries for each dataset for the label
data.datasets.forEach(function (dataset) {
dataset.data.push(0)
})
}
// get the area dataset
var area = data.datasets.filter(function(area){
return (area.label === e.Area);
})[0]
// otherwise create it
if (area === undefined) {
area = {
label: e.Area,
// create a dummy array with an entry for each of the existing labels
data: data.labels.map(function () {
return 0;
}),
fillColor: colors[data.datasets.length]
};
data.datasets.push(area)
}
// set the value
area.data[labelIndex] = e.Value;
})
and use that to display the chart.
Fiddle - http://jsfiddle.net/jt4Lqkn3/
(Bonus: If an Area nothing in that product it should not be shown in
that particular Product)
You can't change any configuration to do this - there will be a space left for each series.
However you might want to set the strokeColor to a transparent value (e.g. strokeColor: "rgba(0, 0, 0, 0)", just below the fillColor line) and set the barStrokeWidth option to 0, so that 0 values don't show up at all on the chart (otherwise there will be thin line shown)
new Chart(ctx).Bar(data, {
barStrokeWidth: 0,
});

Limit labels number on Chart.js line chart

I want to display all of the points on my chart from the data I get, but I don't want to display all the labels for them, because then the chart is not very readable. I was looking for it in the docs, but couldn't find any parameter that would limit this.
I don't want to take only three labels for example, because then the chart is also limited to three points. Is it possible?
I have something like that right now:
If I could just leave every third-fourth label, it would be great. But I found absolutely nothing about labels options.
Try adding the options.scales.xAxes.ticks.maxTicksLimit option:
xAxes: [{
type: 'time',
ticks: {
autoSkip: true,
maxTicksLimit: 20
}
}]
For concreteness, let's say your original list of labels looks like:
["0", "1", "2", "3", "4", "5", "6", "7", "8"]
If you only want to display every 4th label, filter your list of labels so that every 4th label is filled in, and all others are the empty string (e.g. ["0", "", "", "", "4", "", "", "", "8"]).
For anyone looking to achieve this on Chart JS V2 the following will work:
var options = {
scales: {
xAxes: [{
afterTickToLabelConversion: function(data){
var xLabels = data.ticks;
xLabels.forEach(function (labels, i) {
if (i % 2 == 1){
xLabels[i] = '';
}
});
}
}]
}
}
Then pass the options variable as usual into a:
myLineChart = new Chart(ctx, {
type: 'line',
data: data,
options: options
});`
UPDATE:
I'v updated my fork with the latest pull (as of Jan 27, 2014) from NNick's Chart.js master branch.
https://github.com/hay-wire/Chart.js/tree/showXLabels
ORIGINAL ANSWER:
For those still facing this issue, I forked Chart.js a while back to solve the same problem. You can check it out on:
https://github.com/hay-wire/Chart.js/tree/skip-xlabels => Older branch! Check showXLabels branch for latest pull.
How to use:
Applicable to bar chart and line chart.
User can now pass a { showXLabels: 10 } to display only 10 labels (actual displayed labels count might be a bit different depending on the number of total labels present on x axis, but it will still remain close to 10 however)
Helps a lot when there is a very large amount of data. Earlier, the graph used to look devastated due to x axis labels drawn over each other in the cramped space. With showXLabels, user now has the control to reduce the number of labels to whatever number of labels fit good into the space available to him.
See the attached images for a comparison.
Without showXLabels option:
With { showXLabels: 10 } passed into option:
Here's some discussion on it:
https://github.com/nnnick/Chart.js/pull/521#issuecomment-60469304
For Chart.js 3.3.2, you can use #Nikita Ag's approach with a few changes. You can check the documentation. Put ticks in xAxis in scales. Example:
...
options: {
scales: {
xAxis: {
ticks: {
maxTicksLimit: 10
}
}
}
}
...
for axis rotation
use this:
scales: {
xAxes: [
{
// aqui controlas la cantidad de elementos en el eje horizontal con autoSkip
ticks: {
autoSkip: true,
maxRotation: 0,
minRotation: 0
}
}
]
}
In Chart.js 3.2.0:
options: {
scales: {
x: {
ticks: {
maxTicksLimit: 10
}
}
}
}
According to the chart.js github issue #12. Current solutions include:
Use 2.0 alpha (not production)
Hide x-axis at all when it becames too crowd (cannot accept at all)
manually control label skip of x-axis (not in responsive page)
However, after a few minutes, I thinks there's a better solution.
The following snippet will hide labels automatically. By modify xLabels with empty string before invoke draw() and restore them after then. Even more, re-rotating x labels can be applied as there's more space after hiding.
var axisFixedDrawFn = function() {
var self = this
var widthPerXLabel = (self.width - self.xScalePaddingLeft - self.xScalePaddingRight) / self.xLabels.length
var xLabelPerFontSize = self.fontSize / widthPerXLabel
var xLabelStep = Math.ceil(xLabelPerFontSize)
var xLabelRotationOld = null
var xLabelsOld = null
if (xLabelStep > 1) {
var widthPerSkipedXLabel = (self.width - self.xScalePaddingLeft - self.xScalePaddingRight) / (self.xLabels.length / xLabelStep)
xLabelRotationOld = self.xLabelRotation
xLabelsOld = clone(self.xLabels)
self.xLabelRotation = Math.asin(self.fontSize / widthPerSkipedXLabel) / Math.PI * 180
for (var i = 0; i < self.xLabels.length; ++i) {
if (i % xLabelStep != 0) {
self.xLabels[i] = ''
}
}
}
Chart.Scale.prototype.draw.apply(self, arguments);
if (xLabelRotationOld != null) {
self.xLabelRotation = xLabelRotationOld
}
if (xLabelsOld != null) {
self.xLabels = xLabelsOld
}
};
Chart.types.Bar.extend({
name : "AxisFixedBar",
initialize : function(data) {
Chart.types.Bar.prototype.initialize.apply(this, arguments);
this.scale.draw = axisFixedDrawFn;
}
});
Chart.types.Line.extend({
name : "AxisFixedLine",
initialize : function(data) {
Chart.types.Line.prototype.initialize.apply(this, arguments);
this.scale.draw = axisFixedDrawFn;
}
});
Please notice that clone is an external dependency.
i had a similar type of issue, and was given a nice solution to my specific issue show label in tooltip but not in x axis for chartjs line chart. See if this helps you
you can limit at as
scales: {
x: {
ticks: {
// For a category axis, the val is the index so the lookup via getLabelForValue is needed
callback: function(val, index) {
// Hide the label of every 2nd dataset
return index % 5 === 0 ? this.getLabelForValue(val) : '';
},
}
}
}
this will skip 4 labels and set the 5th one only.
you can use the following code:
xAxes: [{
ticks: {
autoSkip: true,
maxRotation: 90
}
}]
You may well not need anything with this new built-in feature.
A built-in label auto-skip feature detects would-be overlapping ticks and labels and removes every nth label to keep things displaying normally. https://www.chartjs.org/docs/latest/axes/
To set a custom number of ticks regardless of your chartsjs version:
yAxes: [{
ticks: {
stepSize: Math.round((Math.max.apply(Math, myListOfyValues) / 10)/5)*5,
beginAtZero: true,
precision: 0
}
}]
10 = the number of ticks
5 = rounds tick values to the nearest 5. All your y values will have the same step size.
Similar will work for xAxes too.
This answer works like a charm.
If you are wondering about the clone function, try this one:
var clone = function(el){ return el.slice(0); }
In the Chart.js file, you should find (on line 884 for me)
var Line = function(...
...
function drawScale(){
...
ctx.fillText(data.labels[i], 0,0);
...
If you just wrap that one line call to fillText with if ( i % config.xFreq === 0){ ... }
and then in chart.Line.defaults add something line xFreq : 1 you should be able to start using xFreq in your options when you call new Chart(ctx).Line(data, options).
Mind you this is pretty hacky.

Is it possible to change properties of xAxis.labels individually in HighCharts?

I would like to set different properties for my labels on the xAxis (Here is my problem).
I know how to do this for dataLabels : API.
It is similar to this.
But I don't find anything to do the same thing for xAxis.labels.
Does it exist a way to do this ?
Solution :
It changes only y property of xAxis labels.
Here is a jsFiddle.
chart: {
renderTo: 'container',
type: 'column',
events: {
load: function () {
var xAxis = this.xAxis[0];
var serie = this.series[0];
for (var current_tick in xAxis.ticks) {
var tick = xAxis.ticks[current_tick];
if(serie.data[current_tick]){
if (serie.data[current_tick].y > 0) {
tick.label.attr({
y: tick.label.y + 18
});
}
}
}
}
}
}
I am not aware of a way to do that.
What I would probably do in this situation is create to x axes, one for the positive values, and one for the negative.
{{edit - although, it seems the axis offset plugin only works (at least from what I am seeing) if you have opposite: true set for the axis.
SO, you have to offset the labels on one of them. example:
http://jsfiddle.net/jlbriggs/S48eX/

Kendo chart remove attribute using javascript

I have a kendo column chart, the datasource gets refreshed when different criterias are set via date pickers.
I want to stop the chart from displaying the value axis as a decimal and the only way I have found so far is to either set the format, which results in duplicating numbers, or the major unit, which if my data changes, then this results in overlapping value axis labels. Neither are suitable.
The only way I can now think to do it is to dynamically set the major unit based on whether my maximum value is less than 10, if it is then the major unit is set to 1, if not no major unit isn't set.
This should work fine but, if the major unit gets set to 1, and the data changes I now need to find a way to clear the major unit if my new maximum value is greater than 10.
In javascript I have:
if (highest <= 10) {
chart.data("kendoChart").options.valueAxis.majorUnit = 1;
} else {
chart.data("kendoChart").options.valueAxis.majorUnit.remove(); // This does not work
}
I can't just set majorUnit to null or 0, it doesn't like that and there is no documentation on the correct syntax for removing this kind of attribute.
This is what worked for me:
chart.data("kendoChart").options.valueAxis.majorUnit = undefined;
Don't forget to call
chart.data("kendoChart").refresh();
Do you have an example to show?
Does it help you if you just hide the value axis lines?
http://docs.kendoui.com/api/dataviz/chart#configuration-categoryAxis.line.visible
chart.data('kendoChart').options.valueAxis.line.visible = false;
If you are using datasource, you can put this code into requestEnd function, so no chart refresh is required:
$("#chart").kendoChart({
dataSource: {
transport: {
read: {
url: url,
dataType: "json",
type: 'POST'
}
},
// This is to set a step that looks appropriate for the size of the graph
requestEnd: function(e){
var max = 1;
// Iterate over your own data as you need, here my data returns "Category" and "Count" in each row.
e.response.forEach(function(row){
if(row.Count > max)
max = row.Count;
});
if(max <= 10)
$("#chart").data('kendoChart').options.valueAxis.majorUnit = 2;
else
$("#chart").data('kendoChart').options.valueAxis.majorUnit = undefined;
},
},

Categories

Resources