With Plotly.js I'm getting a top-margin added sometimes (not consistently), where the total height of the graph is 300px, but the graph itself is only 150px high. The SVG container then is stretched and the actual graph is smaller. What can I do to prevent this white-space, and why does it only show up selectively?
Plotly Matlab syntax that results in 300px div instead of a 300px graph:
`% PLOT MEAN MOVEMENT
data = {...
struct(...
'x', nScan, ...
'y',fastmotion, ...
'type', 'scatter')...
};
if max(fastmotion) < 0.3
yminval = 0.3;
else
yminval = round(max(fastmotion) + 1);
end
layout = struct(...
'yaxis', struct(...
'title', 'Movement (mm)', ...
'range', [0, yminval]));
header{3} = 'Absolute Movement';
layout.width = 800;
layout.height = 300;
p = plotlyfig;
p.data = data;
p.layout = layout;
p.PlotOptions.FileName = 'plot_5';
html_file = plotlyoffline(p);
html_file;`
You can check this answer on a different question, and here is the fiddle.
The quick code you can use:
var layout = {
margin: {
l: 20,
r: 20,
b: 20,
t: 20,
pad: 5
}, title: false
};
As mfedoten says on his answer: But be careful if you have tick labels, if you set margins to zero the labels will be cropped
So apparently line 68 in plotlyfig.m caused the issue:
obj.PlotlyDefaults.MinTitleMargin = 80;
Even though I had no title, it sometimes added 80 px on top of the graph, maxing out at the specified graph height. Setting this value to 0 solves the problem.
Probably line 68 in plotlyfig.m just set default margins.
You can set the margins manually like that:
p.layout.margin = struct('b', 15,'l', 30, 'r', 0, 't', 0);
You can find documentation on margins here.
But be careful if you have tick labels, if you set margins to zero the labels will be cropped.
Related
I'm having a hard time making the labels in the y-axis responsive.I want the labels to move to multiple lines and have responsive font sizes when the space is not enough.I'm using the chart.js datalabels library for labelling on top of horizontalBar Graph.Also the labels are getting hidden due to the outer chart container.
var chart = new Chart('ctx', {
type: 'horizontalBar',
data: {
labels: ["Something something something", "blah blah..", "blah blah..","Something something something"],
datasets: [{
data: [6, 87, 56,25,100,65],
backgroundColor: "#4082c4"
}]
},
options:{
responsive: true,
maintainAspectRatio: false,
plugins: {
datalabels: {
color: 'black',
anchor: "end",
align: "right",
offset: 20,
display: function (context) {
return context.dataset.data[context.dataIndex];
},
font: {
weight: 'bold',
size: 26
},
formatter: Math.round
}
},
legend: {
"display": false
},
tooltips: {
"enabled": false
},
scales: {
yAxes: [{
barPercentage: 1.0,
gridLines: {
display: false
},
ticks: {
fontSize: 20,
beginAtZero: true,
}
}],
xAxes: [{
gridLines: {
display: false
},
ticks: {
min: 0,
max: 100,
stepSize: 20
}
}]
}
}
})
The numbers in the right side of the bar also gets clipped of.I want the chart to be at the center horizontally.In the browser the chart looks like this-
Link to the fiddle:-https://jsfiddle.net/24wdpfxL/
You can do this, but it's a bit hack-y.
First the data labels. In the datalabels config section, you can try something like:
/* Adjust data label font size according to chart size */
font: function(context) {
var width = context.chart.width;
var size = Math.round(width / 32);
return {
weight: 'bold',
size: size
};
}
Change the size calculation as necessary.
For the y-axis labels, there's an answer here, however apparently since Chart.js 2.7.0, the line:
c.scales['y-axis-0'].options.ticks.fontSize
..should be changed to:
c.scales['y-axis-0'].options.ticks.minor.fontSize
(ref)
So to scale the y-axis labels font size according to chart height, it might look like:
plugins: [{
/* Adjust axis labelling font size according to chart size */
beforeDraw: function(c) {
var chartHeight = c.chart.height;
var size = chartHeight * 5 / 100;
c.scales['y-axis-0'].options.ticks.minor.fontSize = size;
}
}]
Note: This requires "maintainAspectRatio:" to be set to "true".
There's still one problem however, and that's that the part of the chart containing the y-axis labels will remain at the same pixel width even when resized.
We need to also resize this area to keep it at a constant % of the overall chart width, e.g. 40%, instead of a fixed pixel width (added to yAxes config section):
/* Keep y-axis width proportional to overall chart width */
afterFit: function(scale) {
var chartWidth = scale.chart.width;
var new_width=chartWidth*0.4;
scale.width = new_width;
}
(You might not notice this as a problem with your original example, since there is a oversized line that seems to cause the y-axis width to keep expanding when the window is enlarged. But when the labels don't overflow, then the width stays constant unless the above is used.)
Complete jsFiddle: https://jsfiddle.net/0kxt25v3/2/
(fullscreen)
I'm not sure about wrapping labels on to the next line, you might just need to pre-process the labels to limit the maximum number of characters per label.
I also haven't attempted to scale the x-axis label font sizes, but it should be easy enough to add it in to the "beforeDraw:" section.
If you're using chartjs-plugin-datalabels; here's how i was able to make the labels responsive. Considering the chart may have variable width and height, we can get the average of both (height and width) and calculate the font size. I am also setting the max font size limit to 12.
datalabels: {
font: function (context) {
var avgSize = Math.round((context.chart.height + context.chart.width) / 2);
var size = Math.round(avgSize / 32);
size = size > 12 ? 12 : size; // setting max limit to 12
return {
size: size,
weight: 'bold'
};
},
}
I ran into the clipping problem myself recently and fixed this by setting a suggestedMax value that was wider than the largest value in my dataset.
I want to maintain the same size of the bars. no matter how many there are values.
here is the size I want to have all bars.
when added four values the bars become smaller.
I want all the bars are the same size. no matter that I have to scroll to see the other bars (this is what I want).
this is my code:
http://jsfiddle.net/Laxfsbtb/
$('#container').highcharts({
chart: {
type: 'bar'
},
series: [{
name: 'text1',
data: [1000, 950,920,880,850,234],
color: "#FF0000"
}, {
name: 'text2',
data: [800,770,750,740,730,4324],
color: "#000000"
}, {
name: 'text3',
data: [600,540,535,500,400,324],
color: "#00FF00"
}]
});
});
This can be done with the pointWidth parameter like:
plotOptions: {
series: {
pointWidth: 20 //width of the bar/column/point.
}
},
To allow scrollbar you should probably upgrade to highstock but that only works horizontally. Or you could set up a div that allows the chart to be "larger" on the inside and scroll through that div window.
Not sure precisely what you're looking for, but this might help.
I put together an example a while ago to adjust the height of the chart according to the number of bars, using some preset parameters:
var barCount = chartData.length;
var pointWidth = 20;
var marginTop = 60;
var marginRight = 10;
var marginBottom = 50;
var marginLeft = 100;
var pointPadding = 0.3;
var chartHeight = marginTop
+ marginBottom
+ ((pointWidth * barCount) * (1 + groupPadding + pointPadding));
So, tell it what size you want the bars, how much padding between them, how much margin at top and bottom of chart, and how many data points you have.
Bars will stay the same size and spacing while the chart itself grows to accommodate them.
example:
http://jsfiddle.net/jlbriggs/kpu5d1qf/
To see it work, change the 12 in this line:
var chartData = randomData(12, true);
To whatever number you want.
((Edit
Since you are working with grouped data, you'll have to change the math a little bit.you'll need to account for the number of groups, and multiply hat by the groupPadding, added to the number of points * the pointPadding.
You'll also have to make getting your count of data points slightly more complex.
I have a ton of data associated with time. I want to plot a, hopefully, simple line graph. A simplified example:
var data = [{ time: 13:30, size: 100 }, { time: 13:37, size: 500}, { time: 13: 42, size: 300 }
{ time: 13:51, size: 150 }, { time: 13:56, size: 175 }, { time: 15:59, size: 75 }
{ time: 16:11, size: 75 }, { time: 16:37, size: 125 }, { time: 15:27, size: 200 } [...]
{ time: 20:36, size: 500 }]
Many time values, and corresponding size values. Of course, I do not want to plot every single time value on this graph. Instead I want to take the lowest value, 13:30, then plot the time on the axes at 30 minute intervals (i.e. 13:30, 14:00, 14:30 ... 20:30) until I hit the latest time in my array of data. At the same time, I want to plot every size value. So, the graph will only display a tick for 13:30 and 14:00, but the values that correspond with 13.37, 13.42, 13.51, 13.56 will be plotted on the line.
How would I go about doing this?
My first attempt was something like this:
var graph = d3.select('.graph'),
WIDTH = 790,
HEIGHT = 250,
MARGINS = {
top: 20,
right: 20,
bottom: 20,
left: 50
}
xScale = d3.scale.linear().range([MARGINS.left, WIDTH - MARGINS.right]),
yScale = d3.scale.linear().range([HEIGHT - MARGINS.top, MARGINS.bottom]).domain([smallest, largest]),
xAxis = d3.svg.axis().scale(xScale),
yAxis = d3.svg.axis().scale(yScale).orient("left");
NB the "smallest" and "largest" variables are the smallest and largest size values in my data array, calculated elsewhere. Obviously this is wrong because this would plot every single time value on the axes (as explained above, I do not want this) and even if I did it errors and glitches horribly.
How would I go about achieving what I want to achieve?
Thx
-- Gaweyne
Solved it. d3.time.scale not d3.scale.linear()
xScale = d3.time.scale(range([MARGINS.left, WIDTH - MARGINS.right]).domain(new Date([//Earliest Date//]), new Date([//Latest Date//]))
My charts output dynamic data based on input filters. These can have 1 to 30 bars and they resize on screen resize/device. Just an example, currently where there's < 5 bars and the charts width is 1138px (max), then I have pretty big bars in width.
Q. How is this problem dealth with?
var options = {
xaxis: { ticks: ticks},
grid: { clickable: false, hoverable: true },
series: { stack: true, bars: {show: true,align: 'center',barWidth: 0.5,lineWidth: 2,fillColor: {colors: [{opacity: 0.9}, {opacity: 0.9}]}} },
legend: {container: ".widget-legend"}
};
Assuming you want to give your bars the same pixel width whether there are 1 or 30 bars, you can calculate the value for barWidth from the width of the chart (in x axis units and pixels, correcting for axes and margins) using cross-multiplication. A reasonable pixel width for up to 30 bars on 1100 pixels would be around 20 pixels per bar.
var chartWidthInPixels = 1100 * 0.95; // or $('#chart').width() * 0.95
var barWidthInPixels = 20; // or chartWidthInPixels / 50
var chartWidthInAxisUnits = (max(ticks) - min(ticks)) + 1; // this will be 1 for one bar
var barWidthInAxisUnits = barWidthInPixels * (chartWidthInAxisUnits / chartWidthInPixels);
For one bar with the above example values this gives around 0.019 for the barWidth. By variation of the constants in the formulas you can modify the calculation.
I have several points that I try to represent using a graphael line chart. Almost all of them are below 0.03. However, the chart sets 1 as the maximum and all points are shown to be very down at the chart. When I have even one point over 0.03, graphael sets the maximum value something more than 0.03 and therefore the chart displays correctly. What can I do for the rest cases where all my points are below 0.03? It is really very ungly, and not helpful at all.
Is there a way to set the max value of the y axis automatically?
If you cannot help me, can you please run it, just to tell me if he can run it normally? It wouldn't take much time...
This is what I mean
I noticed that it also might produce very wrong y axis labels...
My code is shown below
var lines = r.linechart(30, 30, 600, 440,[[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71]],[[0.02046,0.00635,0.02813,0.02808,0.02385,0.00729,0.02145,0.01978,0.02683,0.03006,0.03105,0.01211,0.0083,0.0022,0.02172,0.01541,0.01181,0.02418,0.00552,0.02708,0.01011,0.0105,0.0294,0.00132,0.00615,0.02303,0.00065,0.00967,0.00381,0.02167,0.01357,0.0221,0.02512,0.00918,0.01072,0.01083,0.00598,0.00195,0.01716,0.00347,0.03058,0.01815,0.02899,0.01537,0.0241,0.0241,0.00451,0.00088,0.01574,0.00688,0.0143,0.0024,0.03047,0.02876,0.00528,0.00996,0.00865,0.02681,0.02194,0.00082,0.00664,0.01178,0.01596,0.00516,0.0165,0.00895,0.01869,0.03082,0.02318,0.01089,0.00943,0.00986]], {axisxstep : 12,axisystep : 5,nostroke: false, axis: "0 0 1 1", symbol: "circle", smooth: true }).hoverColumn(function () {
this.tags = r.set();
for (var i = 0, ii = this.y.length; i < ii; i++) {
this.tags.push(r.tag(this.x, this.y[i], this.values[i], 160, 10).insertBefore(this).attr([{ fill: "#fff" }, { fill: this.symbols[i].attr("fill") }]));
}
}, function () {
this.tags && this.tags.remove();
});