How to make chartJs stacked bar always rounded when data is 0? - javascript

I came up with this Fiddle : https://jsfiddle.net/2s09hqLu/ which has stacked rounded chart as I wanted.But problem is when a value is 0 in data array.It doesnt make it rounded.I always want it to be rounded how can I do that?
data: [20, 5, 0, 15, 12, 13]
As you can see here on the yellow its flat not rounded.How can I solve this problem?

I have updated your Fiddle only line number 118:
https://jsfiddle.net/r7hvn2ox/
From:
if (rounded){
To:
if (rounded || this._chart.data.datasets[1].data[this._index] == 0) {
I know its fix for 2 datasets: [{}, {}] bars, but it will work perfectly in any of the values of datasets and also for this example.

Thanks everyone for answers.I tried below and worked perfectly for any amount of datasets.
var lastVisible;
var datasetsLength = this._chart.data.datasets.length;
this._chart.data.datasets.map((e,index)=>{
lastVisible=datasetsLength-1;
//to find the depth of datasets and get non-zero value
for(var i=lastVisible;i>0;i--){
if(!this._chart.getDatasetMeta(i).hidden){
if(this._chart.data.datasets[i].data[this._index] != 0){
lastVisible = i;
break;
}
}
}
})

You could add an additional expression to rounded variable if its rendering specific item. This is not the best approach.
var rounded = this._datasetIndex === lastVisible || this._index === 2;
Alternatively you could compare values in each dataset
var cond = false;
if(this._datasetIndex === 0) {
var datasets = this._chart.data.datasets;
var dataset = datasets[this._datasetIndex];
var currentData = dataset.data[this._index];
var nextData = datasets[this._datasetIndex + 1].data[this._index]
cond = currentData !== 0 && nextData === 0;
}
var rounded = this._datasetIndex === lastVisible || cond;

Related

How to set value in array depending on previous value of array

I've got a simple problem, but I'm struggling to find the easiest solution without transforming the array a hundred times.
I want to do a simple stacked graph in google sheets, with weeks on X and values on Y. I got the values for each week, but only for weeks, that have a value.
The values are all calculations I've done with google apps script/ js.
person1 = [[2019/37,2], [2019/42,3]] and so on, for multiple persons and for 80 weeks in total.
The num value is the total value after each week. So I want the array to be filled up with the missing weeks. Therefore I mapped this to another array, where I have all the weeks but no values, giving these weeks the value 0:
person1= [[2019/37,2],[2019/38,0],[2019/39,0],...,[2019/42,3],[2019/43,0],[2019/44,0],...]
This of course does not fit to see a progress in the graph.
So I need something to set the weeks, which were filled up, to the previous value, resulting in
person1= [[2019/37,2],[2019/38,2],[2019/39,2],...,[2019/42,3],[2019/43,3],[2019/44,3],...]
Looping through this and setting the values with something like person[i][1] == person[i-1][1] seems not to be a good practice of course.
So, what would be the best way to achieve this? I'm kind of stuck with this now, I feel like I don't see the forest for the trees.
Thanks in advance!
code:
let valueArray = [[2019/37,2], [2019/42,3]]
let weeksArray = [2019/38,2019/39,2019/40,2019/41...]
//find missing weeks
let notFound = weeksArray.filter(el => valueArray.includes(el) == false).map(x => [x,0]);
//concat and sort
let outArray = arr.concat(notFound).sort((a,b)=> a[0].localeCompare(b[0]));
//output:
//[[2019/37,2],[2019/38,0],[2019/39,0],...,[2019/42,3],[2019/43,0],[2019/44,0],...]
Solution:
Since you already have the expanded array, you can use map on the whole array and use a function to replace the values:
var weeks = [[2019/37,2],[2019/38,0],[2019/39,0],[2019/40,3],[2019/41,0],[2019/42,4],[2019/43,0],[2019/44,0]];
weeks.map((a,b)=>{weeks[b][1] = (a[1] == 0 && b > 0) ? weeks[b-1][1] : weeks[b][1]});
To make it more readable, this is the same as:
weeks.forEach(function missing(item,index,arr) {
if (item[1] == 0 && index > 0) {
arr[index][1] = arr[index-1][1];
}
}
);
Console log:
References:
Arrow Functions
Conditional Operator
Array.prototype.map()
function fixArray() {
var array = [["2019/1", "1"], ["2019/10", "2"], ["2019/20", "3"], ["2019/30", "4"], ["2019/40", "5"]];
var oA = [];
array.forEach(function (r, i) {
oA.push(r);
let t1 = r[0].split('/');
let diff;
if (i + 1 < array.length) {
let inc = 1;
let t2 = array[i + 1][0].split('/');
if (t1[0] == t2[0] && t2[1] - t1[1] > 1) {
do {
let t3 = ['', ''];
t3[0] = t1[0] + '/' + Number(parseInt(t1[1]) + inc);
t3[1] = r[1];
diff = t2[1] - t1[1] - inc;
oA.push(t3);
inc++;
} while (diff > 1);
}
}
});
let end = "is near";
console.log(JSON.stringify(oA));
}
console.log:
[["2019/1","1"],["2019/2","1"],["2019/3","1"],["2019/4","1"],["2019/5","1"],["2019/6","1"],["2019/7","1"],["2019/8","1"],["2019/9","1"],["2019/10","2"],["2019/11","2"],["2019/12","2"],["2019/13","2"],["2019/14","2"],["2019/15","2"],["2019/16","2"],["2019/17","2"],["2019/18","2"],["2019/19","2"],["2019/20","3"],["2019/21","3"],["2019/22","3"],["2019/23","3"],["2019/24","3"],["2019/25","3"],["2019/26","3"],["2019/27","3"],["2019/28","3"],["2019/29","3"],["2019/30","4"],["2019/31","4"],["2019/32","4"],["2019/33","4"],["2019/34","4"],["2019/35","4"],["2019/36","4"],["2019/37","4"],["2019/38","4"],["2019/39","4"],["2019/40","5"]]

Highcharts + R: Show tooltip values in million/billion etc. automatically

Highcharter (R wrapper for displaying Highcharts plots in R) does automatically adjust axis labels depending on the values, e.g. 405 will show as 405 whereas 3'000'000 will show as 3M.
I want to reproduce this for the tooltips shown.
This article outlines how to do this in Highcharts directly. However, I am not able to reproduce this using Highcharter.
Related:
highcharts tooltip format millions billions
JS Fiddle: http://jsfiddle.net/BlackLabel/ynCKW/104/
What am I missing?
chart_data <- tibble(
date_var = c(seq(from = as.Date('2019-09-10'), to = as.Date('2019-09-15'), by = 1))
, value = c(2304, 50000, 678900, 98457, 124684, 249547)
)
hchart(chart_data, type = 'line', hcaes(x = date_var, y = value)) %>%
hc_tooltip(formatter = JS(
"function() {
var axis = this.series.yAxis;
return axis.defaultLabelFormatter.call({
axis: axis,
value: this.y
});
}"
))
IMPORTANTLY: It seems to work for some values but not all of them. For instance, with above data I get correct tooltip labels for the 2nd and the 3rd date but all else show the original number.
In the related SO question you provided a link to, the assumption was to short numbers that have zeros (0) at the end. Just use the previous code from #Paweł Fus' answer:
formatter: function() {
var ret = '',
multi,
axis = this.series.yAxis,
numericSymbols = ['k', 'M', 'G', 'T', 'P', 'E'],
i = numericSymbols.length;
while (i-- && ret === '') {
multi = Math.pow(1000, i + 1);
if (axis.tickInterval >= multi && numericSymbols[i] !== null) {
ret = Highcharts.numberFormat(this.y / multi, -1) + numericSymbols[i];
}
}
return ret;
}
jsFiddle: https://jsfiddle.net/BlackLabel/yrb7gzap
Best regards!

Javascript: Find corner positions of rectangle in 2d matrix (array)

I have an array like below...
var my_array = [
'00000000000000000000000',
'00000011111111000000000',
'00000011111111000000000',
'00000011111111000000000',
'00000011111111000000000',
'00000011111111000000000',
'00000000000000000000000',
]
... and I'd like to get a function that is able to calculate the bounds (4 corner positions) of the 1-characters in the array above.
So my result should be something like this:
var result= {
'top-left' : [6,1],
'bottom-left' : [6,5],
'top-right' : [13,1],
'bottom-right' : [13,5]
}
Also the function should be able to work on rotated arrays like the example array below:
var rotated_array = [
"00000000000000000000000",
"00000000001100000000000",
"00000000111110000000000",
"00000001111111000000000",
"00000111111111100000000",
"00000111111111000000000",
"00000011111100000000000",
"00000001110000000000000",
"00000000100000000000000",
"00000000000000000000000",
"00000000000000000000000"
]
// array ----> result should be something like this:
var result= {
'top-left' : [5,5],
'bottom-left' : [8,8],
'top-right' : [11,1],
'bottom-right' : [14,4]
}
I've developed a function that is able to return an array of the '1'-characters-positions (x/y-positions) so I will get an result array (from my_array) like this:
var positions = [[7,2],[7,1],[6,1],[6,2],[6,3],[6,4],[6,5],[7,5],[7,4],[7,3],[8,3],[8,2],[8,1],[9,1],[9,2],[9,3],[9,4],[9,5],[8,5],[8,4],[10,5],[10,4],[10,3],[10,2],[10,1],[11,1],[11,2],[11,3],[11,4],[11,5],[12,5],[12,4],[12,3],[12,2],[12,1],[13,1],[13,2],[13,3],[13,4],[13,5]]
Because the arraylist above includes all positions of my rectangle it also should includes the 4 corner positions... But how to get the right coordinates?
My idea was to filter the arrays biggest xValue, biggest yValue, smallest xValue and smallest yValue to get the 4 corners. Sound good, but there are also multiple possibilities for my corners:
var positions = [[7,2],[7,1],[6,1],[6,2],[6,3],[6,4],[6,5],[7,5],[7,4],[7,3],[8,3],[8,2],[8,1],[9,1],[9,2],[9,3],[9,4],[9,5],[8,5],[8,4],[10,5],[10,4],[10,3],[10,2],[10,1],[11,1],[11,2],[11,3],[11,4],[11,5],[12,5],[12,4],[12,3],[12,2],[12,1],[13,1],[13,2],[13,3],[13,4],[13,5]]
var corners = {
'maxX': positions.filter(e => e[0] === Math.max(...positions.map(e => e[0]))),
'minX': positions.filter(e => e[0] === Math.min(...positions.map(e => e[0]))),
'maxY': positions.filter(e => e[1] === Math.max(...positions.map(e => e[1]))),
'minY': positions.filter(e => e[1] === Math.min(...positions.map(e => e[1])))
}
console.log(JSON.stringify(corners))
So the code above returns something like this:
{
"maxX":[[13,1],[13,2],[13,3],[13,4],[13,5]], // x values are all 13 (max)
"minX":[[6,1],[6,2],[6,3],[6,4],[6,5]], // x values are all 6 (min)
"maxY":[[6,5],[7,5],[9,5],[8,5],[10,5],[11,5],[12,5],[13,5]], // y values are all 5 (max)
"minY":[[7,1],[6,1],[8,1],[9,1],[10,1],[11,1],[12,1],[13,1]] // y values are all 1 (min)
}
But how to get the right coordinates for each corner from the result above??
I am absolutely clueless how to calculate the right ones, so I hope somebody can help me with my function..
Thanks in advance, jonas
This is an extension of the comment on the question. Convert to vector and find the "pixel" closest to the corners.
If you can identify the "1" that borders the square (detect change from left to right and from top to bottom) and
Identify which "1" who belongs to which side of the square you can calculate the vector of each of the 4 borders using "minimum distance".
Afterwards you can calculate the intersection between the lines and you have the corners.
Minimum distance and intersection of lines are standard geometric operation, you can google. I cant remember them right know.
function findExtremes( array ){
let upRight = array[0],
downRight = array[0],
downLeft = array[0],
upLeft = array[0];
for(const [x,y] of array){
if(x < downRight[0] || x === downRight[0] && y < downRight[1]) downRight = [x,y];
if(x > upRight[0] || x === upRight[0] && y > upRight[1]) upRight = [x,y];
if(y > upLeft[1] || y === upLeft[1] && x < upLeft[0]) upLeft = [x,y];
if(y < downLeft[1] || y === downLeft[1] && x > downLeft[0]) downLeft = [x,y];
}
return {downRight, downLeft, upRight, upLeft};
}
The code works like this:
x < downRight[0]
This first part insures that we take the smallest x value. OR if
x === downRight[0]
weve got two minimal x values, we take
y < downRight[1]
the one with the smaller y.

AmMaps - heatmap for images instead of areas

AmMaps has cool and easy feature to create heatmap for areas:
https://www.amcharts.com/demos/us-heat-map/
Is it possible to use it also for points (images) - to calculate image color basing on value, colorSteps etc.?
Technically, heat map features are not applied to images. However, it's pretty easy to apply the same principle using a very basic plugin:
AmCharts.addInitHandler(function(map) {
// check if `colorSolid` is set for `imagesSettings`
if (map.imagesSettings.colorSolid === undefined)
return;
// calculate minimum and maximum value
var minValue,
maxValue;
if (map.minValue === undefined || map.maxValue === undefined) {
for (var i = 0; i < map.dataProvider.images.length; i++) {
var image = map.dataProvider.images[i];
if (image.value !== undefined) {
if (minValue === undefined || (image.value < minValue))
minValue = image.value;
if (maxValue === undefined || (image.value > maxValue))
maxValue = image.value;
}
}
}
// use map overrides if set
if (map.minValue !== undefined)
minValue = map.minValue;
if (map.maxValue !== undefined)
maxValue = map.maxValue;
// set colors for each area
for (var i = 0; i < map.dataProvider.images.length; i++) {
var image = map.dataProvider.images[i];
if (image.color === undefined && image.value !== undefined) {
// we set colors for those images that don't have color set explicitly
var percent = (image.value - minValue) / (maxValue - minValue);
image.color = AmCharts.getColorFade(
map.imagesSettings.color,
map.imagesSettings.colorSolid,
percent);
}
}
}, ["map"]);
Add it somewhere before the map code. Also make sure both color and colorSolid is set in imagesSettings.
Here's a complete working example.

jqplot--how to have 2 lines with very different values to scale for good values in one chart?

i'm using jqplot to draw charts, in one chart it will have 2 line series, but the values are quite different, one is about 2000, and the other might be more than 1,000,000,
how can i specify it to have 2 lines with 2 y axis in a way that can be displayed in one chart fine?
currently i just saw one line that's just a plain line in the bottom? my codes are something like below
$.ajax({url: myurl,
success:function(json){
$.jqplot.config.enablePlugins = true;
var data = eval(json);
var ssList = data[0];
var sdList = data[1];
var dataOption = [ssList];
if(sdList.length > 0){
dataOption[1] = sdList;
}
var seriesOption = {lineWidth:2, markerOptions:{style:'square'}};
if(sdList.length > 0){
seriesOption[1] = {renderer:$.jqplot.OHLCRenderer, rendererOptions:{candleStick:true}};
}
var axisOption ={};
axisOption.xaxis = {
renderer:$.jqplot.DateAxisRenderer,
tickOptions:{formatString:'%Y-%m-%d'}
};
axisOption.yaxis = {autoscale:true,label: "time"};
if(sdList.length > 0){
axisOption.y2axis = {autoscale:true,label: "index"};
}
var plot2 = $.jqplot('chart2', dataOption, {
title:'水木社区股票版人气指数',
axes:axisOption,
series:seriesOption,
highlighter:{
showMarker:false,
tooltipAxes: 'xy',
yvalues: 1,
formatString:'<table class="jqplot-highlighter"><tr><td>日期:%s</td></tr><tr><td>人气指数:%s</td></tr></table>'
}
});
}});
You can create plots with more 'y' axes. Second example here http://www.jqplot.com/tests/zooming.php
Don't use autoscale, try to set max and min values for each one and you will have your plot more readable.

Categories

Resources