Chart's Y-axis does not re-render properly in Billboard.js - javascript

There is a chart generated in Billboard.js which has the possibility to introduce custom values as minimum and maximum for the Y-axis.
The problem that I encounter is that even the axis is changing when the values are modified, they are not very close to the introduced value.
For example this screenshot:
On the maximum value I don't think it is a great problem. But on the minimum one it is. The default value was 0, after there was introduced 300 I would expect to start from a closer value to it, not 0 as before. The Y-axis just moved few pixels after that change.
The html code:
<input type="number" ng-change="$ctrl.updateY('max')" ng-model="$ctrl.max">
The JS code:
updateY(val) {
if(val=== 'max') {
if(this.max || this.max === 0) {
this.maxValue = this.max;
}
} else {
if(this.min || this.min === 0) {
this.minValue = this.min;
}
}
const maxNumber = Number(this.maxValue);
const minNumber = Number(this.minValue);
this.lineView.chart.axis.range({max: {y: maxNumber}, min: {y: minNumber}});
this.resetY = false;
}
This is the code for max, same one for min.
I think that maybe the problem is from range, it doesn't generate the Y-axis with the values introduced.
Any suggestions?

It is rendering much closer to the introduced values if it is added a padding in bb.generate:
const chart = bb.generate({
...
"axis": {
...
"y": {
...
"padding" : {
top: 0,
bottom: 10
}
...

Related

Highcharts yAxis.max manage tick intervals dynamically

I'm trying to set the max and min of y value dynamically based on the data but it is having some troubles for some values. I think the highchart couldn't set values that doesn't have a proper tick.
For eg: min: 3 and max 10. Then, the highchart won't set these values instead of that it will set a value ranging from 0...14. This is just an example value, sometimes I get 36000.14 as min and 454533.18 as max.
So my logic isn't working in that case.
This is the part of code which I currently have:
data = [3.43, 3.58, 4.86, 8.55, 8.77, 4.44, 9.67]
maximum_val = Math.ceil(maximum_val) eg: 10
minimum_val = Math.floor(minimum_val) eg: 3
evolution_options.yAxis[$('#div_count_'+div).find('#y_axis_lbl').text()] = {
min: minimum_val,
max: maximum_val,
title: false,
labels: {
style: {
color: 'black'
}
}
}
Update
Fiddle : http://jsfiddle.net/e3fy1vu9/
Further Brainstorming
I think it is because the min value isn't a multiple of the max value so Highchart couldn't calculate how to split the tick. If there was any way so I could set the max value as the multiple of the min value.
Here is my advice how to calculate the max and min for yAxis from the defined data.
Demo: https://jsfiddle.net/BlackLabel/0qwt1kx7/
yAxis: {
min: Math.floor(Math.min(...data)),
max: Math.round(Math.max(...data)),
tickAmount: Math.round(Math.max(...data)) - Math.floor(Math.min(...data)) + 1 //1 count the min or max as itself
},
Please test it and let me know if it fits to your requirements. Because the tickAmount value depends on how many values is included in the data array and probably some modifications are needed for larger data.
EDIT:
Try to use this custom solution to calculate the yAxis positions:
Demo: http://jsfiddle.net/BlackLabel/ztaj6Lkd/
var positions = [],
tickNumbers = 5,
tick = Math.floor(Math.min(...data));
for(let i = 0; i < tickNumbers; i++) {
positions.push((Math.round(tick * 100) / 100));
tick += Math.round(Math.max(...data)) / tickNumbers
}
//add the max
positions.push(Math.round(Math.max(...data)))
Hi I have fixed it by setting this alignTicks:false option.
evolution_options.yAxis[$('#div_count_'+div).find('#y_axis_lbl').text()] = {
min: minimum_val,
max: maximum_val,
title: false,
labels: {
style: {
color: 'black'
}
},
alignTicks: false
}

D3 Elastic easing transition of circle radius causes negative radius to be set

I have a very weird problem whilst using the ease("elastic", a, p) function in d3js. I am transitioning the radius of circles and I'm getting a flood of errors in the console, which are significantly slowing down my webpage. The errors state that I'm setting a negative radius — after some debugging (from console logging everything to diffs with previous code) I found out that's caused by the elastic easing. Here's my code:
function redraw() {
var day = d3.select('input[name="day"]:checked').node().value;
var time = d3.select('input[type="range"]').node().value.toString();
var direction = d3.select('input[name="direction"]:checked').node().value;
// fix bug in which data for single digit hours is not displayed
if (time.length == 1) {time = "0" + time;}
if (circles !== undefined) {
circles.transition().duration(900).ease('elastic', 1, 0.75).attr({
fill: function(d) {
return shadeColor("#ff0000", (-1 * getCircleSize(d.data, day, time, direction)));
},
r: function(d) {
var size = getCircleSize(d.data, day, time, direction);
if (size <= 0) {
return 0;
} else if (size > 0 && size < 2) {
return size + 2;
} else if (size > 50) {
return size*0.1 + 50;
} else {
return size;
}
}
});
}
}
And here is an example of the error I'm getting:
Error: Invalid negative value for <circle> attribute r="-0.04404809159933931"
(anonymous function)
# d3.v3.min.js:5l
# d3.v3.min.js:3Lt
# d3.v3.min.js:1qt
# d3.v3.min.js:1
If I change the line:
circles.transition().duration(900).ease('elastic', 1, 0.75).attr({...});
to:
circles.transition().duration(900).ease('quad').attr({...});
...it doesn't spit out any errors in the console.
Here's a screenshot as well:
It would be great if you can give me some guidance on how to solve this problem. Thanks in advance!
Check the spec on d3.ease():
elastic(a, p) - simulates an elastic band; may extend slightly beyond 0 and 1.
Because the elastic easing overshoots the range [0,1] a little, it is not useful for lengths which must not be negative like radius, width etc. As you already noticed there are other easing functions which are guaranteed to yield positive values only. Have a look at this overview on easing function to find a function which will stay within the range of [0,1].

How to set the dynamic or static tick size in a Rickshaw.js plot?

I am creating a Rickshaw.js-powered graph much like in this example: http://code.shutterstock.com/rickshaw/tutorial/example_07.html based on my own data that is returned via an AJAX call. The data is either measured in bytes (typical values range in a few gigabytes or hundreds of MBs) or seconds (anywhere between 10s and 50 minutes). I tried using a Rickshaw.Fixtures.Number.formatBase1024KMGTP formatter for the bytes and wrote my own for the seconds, which does its part well. The problem is that I need to position the tick lines in a smart way - preferably dynamically, but even static settings (e.g. place a tick every 1024*1024*1024=1 GB or every 60 s) would be fine.
I tried setting the tickSize to 1024^3 like so:
var y_axis = new Rickshaw.Graph.Axis.Y({
graph: graph,
tickSize: 1073741824 // 1 GB
});
y_axis.render();
but I ended up seeing no ticks at all. What am I doing wrong and what would be the right way?
Basically, you need to adapt the tickOffsets() function of the Axis.X, Axis.Y and Axis.Time classes in Rickshaw.
tickSize will not help you with that as - like #Old Pro stated correctly - it indicates the size of the bold tick lines in pixels. It has nothing to do with spacing.
For Time-based Axes
My solution essentially consists of replacing the standard tickOffsets() function in those files
this.tickOffsets = function() {
var domain = this.graph.x.domain();
var unit = this.fixedTimeUnit || this.appropriateTimeUnit();
var count = Math.ceil((domain[1] - domain[0]) / unit.seconds);
var runningTick = domain[0];
var offsets = [];
for (var i = 0; i < count; i++) {
var tickValue = time.ceil(runningTick, unit);
runningTick = tickValue + unit.seconds / 2;
offsets.push( { value: tickValue, unit: unit } );
}
return offsets;
};
by a custom routine. This is gonna do the trick:
this.tickOffsets = function() {
var domain = this.graph.x.domain();
var unit = this.fixedTimeUnit || this.appropriateTimeUnit();
var tickSpacing = args.tickSpacing || unit.seconds;
var count = Math.ceil((domain[1] - domain[0]) / tickSpacing);
var runningTick = domain[0];
var offsets = [];
for (var i = 0; i < count; i++) {
var tickValue = time.ceil(runningTick, unit);
runningTick = tickValue + tickSpacing;
offsets.push( { value: tickValue, unit: unit } );
}
return offsets;
};
With that in place, you can write something like
var time = new Rickshaw.Fixtures.Time();
var timeUnit = time.unit('year');
var x_axis = new Rickshaw.Graph.Axis.ExtendedTime(
{
graph: graph,
tickSpacing: 60*60*24*365*13, // 13 years
timeUnit: timeUnit
} );
to have ticks spaced out evenly at every 13 years.
For Value-based Axes
For value-based Axes, you would need to extend the render() function to include a facility that "manually" sets the ticks for the axis. I did it like this:
this.render = function() {
if (this.graph.height !== this._renderHeight) this.setSize({ auto: true });
var axis = d3.svg.axis().scale(this.graph.y).orient(this.orientation);
if (this.tickSpacing) {
var tickValues = [];
var min = Math.ceil(axis.scale().domain()[0]/this.tickSpacing);
var max = Math.floor(axis.scale().domain()[1]/this.tickSpacing);
for (i = min * this.tickSpacing; i < max; i += 1) {
console.log(i);
tickValues.push(i * this.tickSpacing);
}
axis.tickValues(tickValues);
}
axis.tickFormat( args.tickFormat || function(y) { return y } );
if (this.orientation == 'left') {
var berth = this.height * berthRate;
var transform = 'translate(' + this.width + ', ' + berth + ')';
}
if (this.element) {
this.vis.selectAll('*').remove();
}
this.vis
.append("svg:g")
.attr("class", ["y_ticks", this.ticksTreatment].join(" "))
.attr("transform", transform)
.call(axis.ticks(this.ticks).tickSubdivide(0).tickSize(this.tickSize));
var gridSize = (this.orientation == 'right' ? 1 : -1) * this.graph.width;
this.graph.vis
.append("svg:g")
.attr("class", "y_grid")
.call(axis.ticks(this.ticks).tickSubdivide(0).tickSize(gridSize));
this._renderHeight = this.graph.height;
};
The important part here are the statements in the if (this.tickSpacing) clause. They compute ticks given by the tickSpacing variable in the config array, and assign them to the axis in the axis.tickValues(tickValues) statement. Note that this.tickValues is assigned in the this.tickSpacing = args.tickSpacing statement in the initialize() function, not stated above.
Try it yourself
Have a look at this jsfiddle, where the complete code is available. This will certainly give you some pointers. If you want, you can create your own jsfiddle with your values and tell me if you need anything else.
tickSize is the size of the ticks in pixels. Not what you want to be setting to a huge number.
Set ticks to the number of ticks you want on the graph and Rickshaw (actually d3) will do some magic to give you pretty values of ticks that generate about that number of ticks on the graph.
If you want further control you're going to have to dig into d3, where you will be able to explicitly set the tick values using axis.tickValues(). I'd probably copy the existing Rickshaw.Graph.Axis.Y code and create my own Y axis class that includes access to tickValues or the ability to use my own scale. It's a little unclean in that Rickshaw creates the Y scale in the graph.render() function, so you can't easily override the Y scale, but the Y scale Rickshaw creates does have the range set from the graph data, which is information you will want when creating your own tick values.

how to display y-axis value to integers only in jqplot

How do i make the value of y-axis into integer?
i currently have this value 0.0 1.0 1.5 2.0 2.5 3.0. i want to change it to some thing like this 0 1 2 3 etc....
thank you! cheers!
if i understand what you want, is to display in y axis integer values.
Try this,
axesDefaults:
{
min: 0,
tickInterval: 1,
tickOptions: {
formatString: '%d'
}
}
Override createTicks function and introduce new axis bool property - integersOnly.
// jqplot adding integersOnly option for an axis
var oldCreateTicks = $.jqplot.LinearAxisRenderer.prototype.createTicks;
$.jqplot.LinearAxisRenderer.prototype.createTicks = function (plot) {
if (this.integersOnly == true) {
var db = this._dataBounds;
var min = ((this.min != null) ? this.min : db.min);
var max = ((this.max != null) ? this.max : db.max);
var range = max - min;
if (range < 3) {
if (this.min == null) {
this.min = 0;
}
this.tickInterval = 1;
}
}
return oldCreateTicks.apply(this, plot);
}
Just to build on the top answer.
axes: {
yaxis: {
min: 0,
tickInterval: 1,
tickOptions: {
formatString: '%d'
}
}
}
Would just apply this to the yaxis. Helpful, if you have a bar chart or some other chart and you want to isolate the axis.
Try parseInt(y_axis)
(filler text, answer too short)

plotting negative numbers with flot / jquery

I know Im missing something retarded here... All Im trying to do is graph f(x) = 2500 for a range of x from -75 to 75. This should make a horizontal line. Right now, I think its a misunderstanding on my part of some specifics of arrays. It starts at 0 and goes to 75 fine, it does not graph lower than 0. (I get half the line)
for(x = -75; x<75; x++)
{
a_const[x] = [x, 2250];
}
Im about certain the problem is there. Heres my .plot function, just to be sure.
$.plot(
$("#mydiv"),
[
//{label : "f(x) = x^2", data : a_exp},
//{label : "f(x) = sqrt(x)", data : a_sqroot},
//{label : "f(x) = 3root(x)", data : a_cuberoot}
{label: "constant", data : a_const}
],
{
//yaxis: {min:-5000},
xaxis: {min:-75},
yaxis: {min:-1000},
yaxis: {max:4000},
grid: {hoverable:true, clickable:true },
series: { points: {show:true}, lines:{show:true}}
}
);
You can't have negative array subscripts. Just do
for (x = -75, x < 75; x++ )
{
a_const.push( [x,2250] );
}
This will end up with elements with indices from 0 to 149, but containing pairs from [-75,2250] to [75,2250].
NM, figured it out. a[-75] a[-74] (etc...) is not seen by flot do to it being negative. Solution :
for(x = -75; x<75; x++)
{
a_const[x+75] = [x, 2250];
}
Would be nice to find / quote the official rule on negative indices.

Categories

Resources