FontSize for the image's legend in Matlab - matlab

I have the following code:
X = 0:pi/100:0.25*pi;
Y1 = sin(X);
Y2 = cos(X);
Y3 = tan(X);
fh = figure('toolbar','none','menubar','none','Units','characters');
Pan1 = uipanel(fh,'Units','normalized','Position',[0 0 0.5 1],'title',...
'Panel1');
Pan2 = uipanel(fh,'Units','normalized','Position',[0.5 0 0.5 1],'title',...
'Panel2');
haxes = axes('Parent',Pan2,'Units', 'normalized','Position',...
[0.125 0.1 0.75 0.75]);
hplot = plot(haxes,X,Y1,X,Y2,X,Y3);
xlabel(haxes,'Time (second)');
ylabel(haxes,'Amplitude (meter)');
title(haxes,'Trigonometric functions');
Ley = {'Sine function','Cosine function','Tangent function'}; %# legend's strings values
legend(haxes,Ley,'Location','SouthOutside');
[FileName,PathName,FilterIndex] = uiputfile('*.bmp;*.png;*.jpg;*.tif','Save as');
ftmp = figure('Menu','none','Toolbar','none','Units','normalized',...
'Position',[-1000 -1000 1 1]);
set(gcf,'PaperPositionMode','auto');
set(gcf,'InvertHardcopy','off');
new_axes = copyobj(haxes, ftmp);
legend(new_axes,Ley,'Location','SouthOutside','FontSize',8);
set(new_axes,'Units','normalized','Position',[0.1 0.1 0.8 0.8]);
fmtgraf = {'-dbmp','-dpng','-djpeg','-dtiff'};
fmt = fmtgraf{FilterIndex};
print(ftmp,fmt,FileName,'-r0');
delete(ftmp);
delete(fh);
As seen in the code, the command line
legend(new_axes,Ley,'Location','SouthOutside','FontSize',8);
is run before the command line
set(new_axes,'Units','normalized','Position',[0.1 0.1 0.8 0.8]);
Because of it, the image appears cutted by its low part as seen below (independently of the existence or no existence of the property/value 'FontSize')
If the command line
legend(new_axes,Ley,'Location','SouthOutside','FontSize',8);
is run after the command line
set(new_axes,'Units','normalized','Position',[0.1 0.1 0.8 0.8]);
now the image is cutted by its low part but in this case it is not seen neither the xlabel text nor the legend box (as seen below)
If 'FontSize',8 is suppressed, all is Ok. How can I fix this if I want that the legend to have a lesser size?

It works fine for me too... You have to understand that LEGEND basically creates another axis instance inside the figure.
Now you are placing it using 'SouthOutside' location, so it will try to resize the existing axis to place itself underneath it, but if you don't leave it enough space it might not fit, especially as you are using 'normalized' units which let the axes auto-resize given the parent container size.
Try to vertically shrink the main plot axis a bit in advance, to give the legend more room...
Also the order of commands does matter. Compare this:
new_axes = copyobj(haxes, ftmp);
legend(new_axes, Ley, 'Location','SouthOutside', 'FontSize',8);
set(new_axes, 'Units','normalized', 'Position',[0.1 0.1 0.8 0.8]);
against:
new_axes = copyobj(haxes, ftmp);
set(new_axes, 'Units','normalized', 'Position',[0.1 0.1 0.8 0.8]);
legend(new_axes, Ley, 'Location','SouthOutside', 'FontSize',8);
EDIT:
As I mentioned, LEGEND creates just another axis. Therefore for ultimate control, you could manually position all the axes in the figure yourself (specify actual positions instead of relying on "outside" values for the 'Location' property exposed by the legend function).. Here is an example to illustrate:
%# create a normal plot
clf
hAx = axes();
plot(hAx, rand(10,3))
xlabel(hAx, 'xlabel'), title(hAx,'title')
%# add a legend on the inside and record the axis outerposition (in pixels)
hLgnd = legend(hAx, {'1' '2' '3'}, 'Location','South', 'FontSize',8);
set(hLgnd, 'Units','pixels')
op = get(hLgnd,'OuterPosition');
set(hLgnd, 'Units','normalized')
%# resize the plot axis vertically to make room for the legend
set(hAx, 'Units','pixels')
pos = get(hAx,'Position');
ins = get(hAx,'TightInset');
set(hAx, 'Position',[pos(1) pos(2)+op(4) pos(3) pos(4)-op(4)])
set(hAx, 'Units','normalized')
%# move the legend to the bottom in the free space
set(hLgnd, 'Units','pixels')
set(hLgnd, 'OuterPosition',[op(1) (pos(2)-ins(2))/2 op(3) op(4)])
set(hLgnd, 'Units','normalized')
Try it out for different figure sizes and rerun the code.. Note that if you want the axes to correctly adjust their sizes automatically when you resize the figure, you have to do a similar thing as the above code inside the 'ResizeFcn' event handler of the figure, ie:
set(gcf,'ResizeFcn',#myEventHandler)

It works fine for me:
I notice that our screenshots have different aspect ratios. Perhaps your monitor has a widescreen aspect ratio? The 'units' 'normalized' option that you're applying to the new axes will set its dimensions relative to the monitor it's displayed on. When you create a wider figure, perhaps MATLAB is clipping the legend from the bottom (its graphics aren't perfect).
My advice would be to perhaps try setting the axes units directly using 'units' 'pixels', with a squarer aspect ratio.
Another option might be to create the legend with 'orientation' 'horizontal', which would spread the items out with less height, or to place it inside the graph, perhaps 'SouthEast'.

Related

Resize legend box in octave [duplicate]

I am trying to create a plot in Octave (using v4.4.1 on Windows) using plotyy and putting the legend outside the plot (because the data covers all the usable space inside the graph). The following MVCE should reproduce the issue fairly well:
% Generate some random data to reproduce the issue
data = rand(1000,10);
data(:,1:8) = data(:,1:8)-0.5;
data(:,9:10) = data(:,9:10)+30;
timedate = linspace(737310,737313,size(data,1));
data_labels={'1';'2';'3';'4';'5';'6';'7';'8';'9';'10'};
% Plot the data
figure('Name','MVCE','Position',[300 200 1000 600])
[ax,h1,h2] = plotyy(timedate,data(:,1:8),timedate,data(:,9:10));
set(h2,'Visible','on');
datetick(ax(1),'x','HH:MM:SS')
datetick(ax(2),'x','HH:MM:SS')
ylim(ax(1),[-1 1])
ylim(ax(2),[20 50])
xlabel('Date & time')
ylabel(ax(1),'Something')
ylabel(ax(2),'Something else')
title('plotyy graph with legend problem')
[hl,hlo] = legend([h1;h2],data_labels,'location','eastoutside');
grid on
This the output of the code using the gnuplot graphics toolkit:
As you can see, the legend does not go outside the plot, and the second y axis is not visible (it looks like part of the plot is actually truncated).
I have tried using the qt and fltk graphics toolkits, which give issues of their own:
With qt graphics toolkit
With fltk graphics toolkit
Can anoybody suggest a fix or at least workaround? Does the same issue also happen in MATLAB or is it Octave-specific?
EDIT
Using the suggestion in Tasos' answer, I managed to almost make it work with gnuplot:
% Plot the data
figure('Name','MVCE','Position',[300 200 1000 600])
[ax,h1,h2] = plotyy(timedate,data(:,1:8),timedate,data(:,9:10));
set(h2,'Visible','on');
datetick(ax(1),'x','HH:MM:SS')
datetick(ax(2),'x','HH:MM:SS')
ylim(ax(1),[-1 1])
ylim(ax(2),[20 50])
ax1Pos = get(ax(1), 'position');
ax2Pos = get(ax(2), 'position');
ax1Pos(3) = ax1Pos(3) * 0.73;
ax2Pos(3) = ax2Pos(3) * 0.73;
set(ax(1), 'position', ax2Pos);
set(ax(2), 'position', ax2Pos);
xlabel('Date & time')
ylabel(ax(1),'Something')
ylabel(ax(2),'Something else')
title('plotyy graph with legend problem')
[hl,hlo] = legend([h1;h2],data_labels,'location','eastoutside');
pos = get(hl,'Position');
pos(1) = 0.9;
set(hl,'Position',pos)
grid on
Which produces:
Apart from the fact that the legend overlays with the second y axis label (which it doesn't on my screen, only when printing to jpg), the problem is that Octave appears to plot two legends on top of each other for some reason: one with the first set of data attached to the first set of axes, and one with the complete set of data, for both axes right on top of the first legend. This is obviously wrong, and trying to set the Visible property of hl to off deletes both legends, not just the one.
UPDATED: deals with both legend placement and OpenGL precision affecting graph.
Regarding the problem of the legend not appearing exactly in the position you want it to, you can manually adjust the position of all axes involved in a figure, to place them exactly where you want.
Regarding the problem of OpenGL being unable to deal with the precision involved when adding small numbers to a large number, plot the graph with only the small numbers involved, and then simply adjust the xticklabels to correspond to the numbers you desire.
Full code below:
% Generate some random data to reproduce the issue
data = rand(1000,10);
data(:,1:8) = data(:,1:8)-0.5;
data(:,9:10) = data(:,9:10)+30;
t_offset = 737310;
timedate = linspace(0,3,size(data,1));
data_labels={'1';'2';'3';'4';'5';'6';'7';'8';'9';'10'};
% Plot the data
figure('Name','MVCE','Position',[300 200 1000 600])
[ax,h1,h2] = plotyy(timedate,data(:,1:8),timedate,data(:,9:10));
set(h2,'Visible','on');
ylim(ax(1),[-1 1])
ylim(ax(2),[20 50])
ylabel(ax(1),'Something')
ylabel(ax(2),'Something else')
title('plotyy graph with legend problem')
[hl,hlo] = legend([h1;h2],data_labels,'location','eastoutside');
set(hl, 'position', get(hl, 'position') .* [0.975, 1, 0.975, 1] )
grid on
ax1Pos = get(ax(1), 'position'); ax2Pos = get(ax(2), 'position');
ax1Pos(3) = ax1Pos(3) * 0.95; ax2Pos(3) = ax2Pos(3) * 0.95;
set(ax(1), 'position', ax2Pos); set(ax(2), 'position', ax2Pos);
XTicks = get(ax(1), 'xtick');
set(ax(1), 'xticklabel', datestr(XTicks + t_offset, 'HH:MM:SS'))
xlabel('Date & time')
set(ax(2), 'xtick', []);
Output:

Centering xlabel position in MATLAB

How can I center the position of an xlabel such that it is in the middle of a figure? I would like the center of the xlabel to be alligned with the center of a caption when using LaTeX
The xlabel function creates a string graphics object and sets this as the XLabel property of the current axes object. You can define properties for this string objects when calling xlabel. You can adjust the position of the center of the string object by adjusting the Position property which is by defaults set to [0 0].
First you get what the position is right now (after plotting and using xlabel):
vec_pos = get(get(gca, 'XLabel'), 'Position');
Then you update the position (adjust x with -0.5 for instance):
set(get(gca, 'XLabel'), 'Position', vec_pos + [-0.5 0 0]);
This is done in the data-units by default of the x-axis as far as the documentation goes. It seems to me that the label "Time (s)" is located at 0.13s (according to your figure). Let's adjust it to the left with 0.008 seconds to 0.122s (a "guestimate").
Force it to be "data" units and adjust with 0.008:
str_defaultUnits = get(get(gca, 'XLabel'), 'Units'); % copy this
set(get(gca, 'XLabel'), 'Units', 'data'); % change it
set(get(gca, 'XLabel'), 'Position', vec_pos + [-0.008 0 0]); % adjust position
set(get(gca, 'XLabel'), 'Units', str_defaultUnits); % set it back as it was
On another note: What you trying to achieve is somewhat wrong I would say :) The label of an axis should not be aligned by force to the entire figure caption. Why do this? The figure caption is centered on the entire figure, not just the plotting area. I fear it will look weird in the end. Your choice of course.
The specifics of what you're trying to do will depend on your matlab print settings and latex options (e.g. caption raggedright or centering), but this should at least put your xlabel in the centre of the figure, rather than the centre of the axes.
fh=figure;
ah=axes;
plot(ah,[2.0:10],[2:10])
xlh=xlabel(ah,'my xlabel');
drawnow;
xlh_pos=get(xlh,'position');
ah_pos=get(ah,'position');
x_lim=xlim;
xlh_pos_fig=0.5;%put it in the middle
xlh_pos(1)=(xlh_pos_fig - ah_pos(1))*(x_lim(2)-x_lim(1))/ah_pos(3)+x_lim(1);
set(xlh,'position',xlh_pos);

How to keep the subplot sizes unchanged after putting a colorbar

Let us say we have a 1-by-2 subplot and we plot some graphics inside as follows:
subplot(1,2,1)
surf(peaks(20))
subplot(1,2,2)
surf(peaks(20))
And then we want to put a colorbar:
colorbar
I don't want the right figure squezzed as in the result. How can we put the colorbar out of the rightmost figure in a row of subplots and keep the sizes of them unchanged?
Note: Actually, I need it for plotting images where the colorbar is common and I want to put it on the right. I used this toy example for simplicity.
You could just extract the position of the first plot and use on the second. MATLAB automatically moves the colorbar to the right when rescaling.
f1=figure(1);clf;
s1=subplot(1,2,1);
surf(peaks(20));
s2=subplot(1,2,2);
surf(peaks(20));
hb = colorbar('location','eastoutside');
%% # Solution:
s1Pos = get(s1,'position');
s2Pos = get(s2,'position');
s2Pos(3:4) = [s1Pos(3:4)];
set(s2,'position',s2Pos);
%% # Alternative method. Brute force placement
set(s1,'Units','normalized', 'position', [0.1 0.2 0.3 0.6]);
set(s2,'Units','normalized', 'position', [0.5 0.2 0.3 0.6]);
set(hb,'Units','normalized', 'position', [0.9 0.2 0.05 0.6]);
This is just what I was looking for. After implementing Vidar's automatic solution I came up with a simplification. Get the position of the far right axes BEFORE adding the colorbar, and then just reset the squeezed position to the original:
f1=figure(1);clf;
s1=subplot(1,2,1);
surf(peaks(20));
s2=subplot(1,2,2);
surf(peaks(20));
s2Pos = get(s2,'position');
hb = colorbar('location','eastoutside');
set(s2,'position',s2Pos);

Plotting a subplot on top of another plot in Matlab

I need to plot several plots along a sloped line at different positions.
For example, if I:
plot(0:200,'k');
plotpts = 5:5:200;
I would like to be able to plot a smaller plot at each of my plotpts on top of the original 0:200 line.
I know you can use hold on and plot over top that way, but I need to change my origin each time. Does anyone have any suggestions? I would really like to stay in matlab. Thanks!
Here is a flexible way I usually do it:
plot(1:10, 'k')
plotpts = 2:2:8;
mainbox = get(gca, 'Position');
xlims = get(gca, 'XLim');
ylims = get(gca, 'Ylim');
for i=1:length(plotpts)
originx = mainbox(1) + (plotpts(i) - xlims(1)) * (mainbox(3)) / (xlims(2) - xlims(1));
originy = mainbox(2) + (plotpts(i) - ylims(1)) * (mainbox(4)) / (ylims(2) - ylims(1));
axes('position', [originx originy 0.1 0.1], 'Color', 'none')
% Do some plotting here...
end
It's quite a bit of work, but you probably want to use the axes command. A figure window can host any number of axes, where each axes has it's own position, data, annotations, color etc.
The most difficult thing for the application you describe is that each axis position needs to be defined in the coordinate frame of the underlying figure, which means that some math may be required to create the illusion that the axis is correctly positioned within a parent axes/
For example, if you first create a simple plot
figure(1234); clf;
plot(1:10, rand(1,10),'.k-','linewidth',5);
xlim([1 10]);
ylim([0 1]);
set(gca,'color','y'); %This just helps demonstrate the next steps
You can place another axis directly on top of the first, and then
ha = axes('position',[.2 .3 .1 .1])
plot(linspace(0,2*pi,100), sin(linspace(0,2*pi,100)), 'b-')
xlim([0 2*pi])
You can adjust the the properties of the inset axis to suit your particular needs, for example
set(ha,'color','none'); %A transparent axis
set(ha,'xtick',[],'ytick',[]); %Remove tick labels
title(ha,'This is an inset plot')
Is the command subplot not what you're looking for?

Add legend outside of axes without rescaling in MATLAB

I've got a GUI in MATLAB with a set of axes pre-placed. I'm using the location property of the legend to place it to the right hand side of the axes. However, by doing this the axes get re-scaled so that the axes+legend take up the original width of the axes. Is there any way to circumvent the re-size?
Example:
x=0:.1:10;
y=sin(x);
figure
pos=get(gca,'position');
pos(3)=.5; %#re-size axes to leave room for legend
set(gca,'position',pos)
plot(x,y)
So far I get:
Place legend:
legend('sin(x)','location','eastoutside')
...aaaaand...
MATLAB squishes it all into the original axes space. Any way around this?
EDIT
%# create three axes with custom position
x=0:.1:10;
y=sin(x);
hAx1 = axes('Position',[0.05 0.05 0.7 0.2]); plot(hAx1, x,y)
hAx2 = axes('Position',[0.05 0.4 0.7 0.2]); plot(hAx2, x,y)
hAx3 = axes('Position',[0.05 0.75 0.7 0.2]); plot(hAx3, x,y)
%# add legend to middle one
h = legend(hAx2, 'sin(x)'); pos = get(h,'position');
set(h, 'position',[0.8 0.5 pos(3:4)])