Labeling plots such that label is aligned with the ylabel outside the axes - matlab

Please see the following code which creates a 2 by 2 subplot with some plots:
x = linspace(0,2*pi);
y = sin(x);
hfig = figure('Position',[1317 474 760 729]);
subplot(2,2,1)
plot(x,y)
ylabel('plot1');
subplot(2,2,2)
plot(x,y.^2)
ylabel('plot2');
subplot(2,2,3)
plot(x,y.^3)
ylabel('plot3');
subplot(2,2,4)
plot(x,abs(y))
ylabel('plot4');
in each one, I have added labels by hand in Tools: Edit plot (a) (b) (c) (d) producing this figure:
The problem is, if I resize the plot they are no longer aligned with the ylabel text:
Is there a way to add these labels programmatically and have them automatically align to the ylabel text? I am surprised MATLAB does not have something like this built in already.
Thanks

This is not something that is easy to do without attaching a listener to the figure resize event (see example), and doing some computations related to aspect ratios.
It's not entirely clear what sort of objects your labels are (text or annotation), so I'll just show how to do this programmatically using the text command, which creates labels in axes coordinates (as opposed to figure coordinates). This doesn't solve the problem entirely, but it looks better, possibly to an acceptable degree:
function q56624258
x = linspace(0,2*pi);
y = sin(x);
hF = figure('Position',[-1500 174 760 729]);
%% Create plots
[hAx,hYL] = deal(gobjects(4,1));
for ind1 = 1:3
hAx(ind1) = subplot(2,2,ind1, 'Parent' , hF);
plot(hAx(ind1), x,y.^ind1);
hYL(ind1) = ylabel("plot" + ind1);
end
hAx(4) = subplot(2,2,4);
plot(hAx(4), x,abs(y));
hYL(4) = ylabel('plot4');
%% Add texts (in data coordinates; x-position is copied from the y-label)
for ind1 = 1:4
text(hAx(ind1), hYL(ind1).Position(1), 1.1, ['(' char('a'+ind1-1) ')'], ...
'HorizontalAlignment', 'center');
end
Note several modifications to your code:
The handles returned by some functions that create graphical elements are now stored (mainly: hAx, hYL).
All functions that create graphical elements (subplot, plot, ylabel) now have the target (i.e. parent or container) specified.
I changed the 'Position' of the figure so that it works in my setup (you might want to change it back).

Related

How to subplot + imagesc with a Position in Matlab?

Situation: change Position of a single subplot with imagesc
%% Matlab recommends this structure if axes(); in loop
a1 = subplot(1,2,1);
a2 = subplot(1,2,2);
while 1    
plot(a1, rand(3))    
plot(a2, rand(3))    
drawnow
end
%% Test code
unitsPerInches=[0 0 15 15];
figure('Units', 'inches');
a1 = subplot(1,2,1);
a2 = subplot(1,2,2);
while 1    
set(a1, 'Position', unitsPerInches); % TODO how to affect a1's Position here only?
imagesc(a1, rand(3))    
imagesc(a2, rand(3))    
drawnow
end
Open
What is imagesc corresponding structure to plot(a1,rand(3))?
How to change Position of figure inside the loop?
Forward in Q1 - almost done
%% Extension to imagesc
figure
a1=subplot(1,2,1);
a2=subplot(1,2,2);
for counter=1:2;
imagesc(a1,rand(3))
imagesc(a2,rand(3))
drawnow
end
Fig. 1 Output of Docs example, Fig. 2 Output of Imagesc, Fig. 3 about Q2 where Position affects both subplots
Q1 is almost done; I have just forgotten how to get corresponding plot in imagesc; x-values should be put there but pseudocode imagesc(a1,XDATA,rand(3)) is unsuccessfuly.
Backward in Q2
Code
%% Extension to imagesc
unitsPerInches=[0 0 15 15];
figure
a1=subplot(1,2,1);
a2=subplot(1,2,2);
for counter=1:2;
set(a1, 'Position', unitsPerInches); % TODO how to affect a1's Position here only?
imagesc(a1,rand(3))
imagesc(a1,rand(3))
drawnow
end
Output: position affects both images in Fig. 3.
I think I have misunderstood the meaning of Position here because so strange output.
Testing EBH's proposal for Q2
The implicit assignments cause problems when having two figures where subplots
unitsPerInches=[0 0 15 15];
aFig=figure();
a1=subplot(1,2,1);
a2=subplot(1,2,2);
bFig=figure();
b1=subplot(1,2,1);
b2=subplot(1,2,2);
for counter=1:2;
if counter==1
set(a1, 'Position', unitsPerInches); % affect only position of a1
end
subplot(1,2,counter);
imagesc(rand(3));
drawnow
subplot(1,2,counter);
imagesc(rand(3));
drawnow
end
Output: second figure of subplots fails.
System: Linux Ubuntu 16.04 64 bit
Linux kernel 4.6
Matlab: 2016a
Related threads: Matlab7 'bug' when using "subplot" + "imagesc"?
I'm not 100% sure what you're asking, but I think it's about combining multiple imagesc statements while in a loop. I'd do something more direct -- use gca and put the subplot inside the loop. Quite often, if you want to programmatically address multiple images, it makes sense to put them in some sort of structure other than creating lots of differently named variables. Note also that while 1 is probably not really what you want -- it will hammer your graphics device drivers -- and that pause can take an argument to act as a wait function, for some fraction of a second if required.
testImages{1}=double(imread('coins.png'));
testImages{2}=double(imread('cameraman.tif'));
h=figure;
set(h,'color','w'); %This handle refers to the background window
for ix=1:2
subplot(1,2,ix);
imagesc(testImages{ix});
axis equal;
colormap gray;
%Change, for example, axis position
curPoss=get(gca,'Position'); %gca stands for 'get current axis'
set(gca,'Position',curPoss+1e-2*ix^2); %Move one image up a bit
end
Does that help?
If you want to jump between figures, make an array of them, and use it within a loop:
unitsPerInches = [0.1 0.1 0.15 0.15];
figs = [figure(1) figure(2)];
for f = 1:numel(figs)
figure(figs(f));
for counter = 1:2;
subplot(1,2,counter);
imagesc(rand(3));
drawnow
end
figs(f).Children(1).Position = unitsPerInches;
figs(f).Children(2).Position = unitsPerInches+0.3;
end
Your original values for unitsPerInches was wrong, since the 'Position' property of an axes takes values between 0 to 1 by default. You can change this using the 'Units' property, like:
figs(f).Children(1).Units = 'inches';
The output from this example is two figures that looks like this:
Where there is a small axes on the down-left and a big one on the top-right.
So, back to your original questions:
What is imagesc corresponding structure to plot(a1,rand(3))?
Instead of passing the axes to imagesc, set the focus on the relevant figure, and subplot with:
figure(h)
subplot(x,y,c)
imagesc(data)
where h is a handle to the relevant figure, c is the place of the subplot within h where you want to plot the image (a number between 1 to x*y), and after these two line you call imagesc.
How to change 'Position' of figure inside the loop?
In this question it is not clear if you want to change the 'Position' of the figure or the axes, they have different units and meaning, but both are accessible in the same way:
h.Position = [left bottom width height]; % for the position of the figure
h.Children(c).Position = [left bottom width height]; % for the position of the axes
where h is as before, but c may be numbered differently, so subplot(x,y,c) may not refer to the same axes as h.Children(c). However, you can always use gca to get the current axes handle:
ax = gca;
ax.Position = [left bottom width height];
Hope it's all clear now, and if there are further questions, let me know ;)

Matlab: how do I retrieve the title, the xlabel, the ylabel and font of a plotyy?

I have some figures, for which I want to change:
The title
The xlabel and the ylabel (both font size and content)
The ticks size.
This is how I usually do it:
title('new title ');
xlhand = get(gca,'xlabel');
set(xlhand,'string','xlabel','fontsize',13);
ylhand = get(gca,'ylabel');
set(ylhand,'string','ylabel','fontsize',13);
set(gca,'FontSize',13);
It usually works fine. However, it doesn't work when I want to edit a plot made with the ploty function. The only effect is that my right ylabel changes.
I'm aware that since I have two ylabels now, changing them won't be as easy as for a normal plot. However, I'm surprised that I can't change the xlabels and the title. Why is that?
What's wrong with the above code in relation to the plotxy function? How can I make it work? The documentation doesn't contain any relevant information.
I'm using Matlab R2015a.
Edit: Yes, I meant plotyy (there was a typo in my question). Here is sample code from the documentation:
x = 0:0.01:20;
y1 = 200*exp(-0.05*x).*sin(x);
y2 = 0.8*exp(-0.5*x).*sin(10*x);
figure % new figure
plotyy(x,y1,x,y2)
If you look at the documentation, you can see that plotyy can return the axis handles. So first, you that option to obtain the handles:
Ax = plotyy(x,y1,x,y2);
Now, Ax(1) is the handle to the left axes and Ax(2) is the right one. So you can change the attributes of each of them, for example
set(Ax(1),'FontSize',13);
For reasons I don't fully understand, setting the label is done using
set(get(Ax(1),'YLabel'),'String','Whatever you want...');
EDIT:
If you already plotted the data, you can retrieve the handles using
Ax = findobj(gcf,'type','axes')
From documentation without using get, set :
x = 0:0.01:20;
y1 = 200*exp(-0.05*x).*sin(x);
y2 = 0.8*exp(-0.5*x).*sin(10*x);
figure % new figure
hAx = plotyy(x,y1,x,y2);
title('Multiple Decay Rates')
xlabel('Time (\musec)')
ylabel(hAx(1),'Slow Decay') % left y-axis
ylabel(hAx(2),'Fast Decay') % right y-axis
Then change values by
xlabel('New label','fontsize',10)
title('New Title','fontsize',10)
ylabel(hAx(2),'Fast Decay','fontsize',20)
hAx(1).FontSize=5

Include axes labels when saving plot from MATLAB GUI

I have written the following code to try and retrieve ONLY the axes and its plot from my MATLAB GUI.
F = getframe(gca);
figure();
image(F.cdata);
saveas(gcf,'PlotPic','png');
close(gcf);
I noticed, however, that this method does not include ANY of my axis labels or title. Is there any way which I can get the getframe function to include the axis labels and title?
I tried the following code but it did exactly the same
pl = plot(x,y);
xlabel('x')
ylabel('y')
ftmp = figure;
atmp = axes;
copyobj(pl,atmp);
saveas(ftmp,'PlotPic.png');
delete(ftmp);
I would do it using the rect option of the getframe function.
Basically you can provide a 2nd input argument to getframe, which then captures the content of the rectangle specified as argument. The nice thing is that you can use the handles to an axes, so it does not capture your whole GUI figure but rather a specific axes.
For example, using this line:
F = getframe(gca,RectanglePosition);
Concretely, you could set the coordinates of the rectangle such that they span both axis labels and the title as well. Here is a sample code. The pushbutton callback executes getframe and opens a new figure with the content of F.cdata:
function GUI_GetFrame
clc
clear
close all
%// Create GUI components
hFigure = figure('Position',[100 100 500 500],'Units','Pixels');
handles.axes1 = axes('Units','Pixels','Position',[60,90,400,300]);
handles.Button = uicontrol('Style','Push','Position',[200 470 60 20],'String','Get frame','Callback',#(s,e) GetFrameCallback);
%// Just create a dummy plot to illustrate
handles.Period = 2*pi;
handles.Frequency = 1/handles.Period;
handles.x = 0:pi/10:2*pi;
handles.y = rand(1)*sin(handles.Period.*handles.x);
plot(handles.x,handles.y,'Parent',handles.axes1)
title('This is a nice title','FontSize',18);
guidata(hFigure,handles); %// Save handles structure of GUI.
function GetFrameCallback(~,~)
handles = guidata(hFigure);
%// Get the position of the axes you are interested in. The 3rd and
%// 4th coordinates are useful (width and height).
AxesPos = get(handles.axes1,'Position');
%// Call getframe with a custom rectangle size.You might need to change this.
F = getframe(gca,[-30 -30 AxesPos(3)+50 AxesPos(4)+80]);
%// Just to display the result
figure()
imshow(F.cdata)
end
end
The GUI looks like this:
And once I press the pushbutton, this is the figure that pops up:
So the only trouble you have is to figure out the dimensions of the rectangle you need to select to capture the axis labels and the title.
Hope that solves your problem!

Moving MATLAB axis ticks by a half step

I'm trying to position MATLAB's ticks to line up with my grid, but I can't find a good way to offset the labels.
Also, if I run set(gca,'XTickLabel',1:10), my x tick labels end up ranging from 1 to 5. What gives?
You need to move the ticks, but get the labels before and write them back after moving:
f = figure(1)
X = randi(10,10,10);
surf(X)
view(0,90)
ax = gca;
XTick = get(ax, 'XTick')
XTickLabel = get(ax, 'XTickLabel')
set(ax,'XTick',XTick+0.5)
set(ax,'XTickLabel',XTickLabel)
YTick = get(ax, 'YTick')
YTickLabel = get(ax, 'YTickLabel')
set(ax,'YTick',YTick+0.5)
set(ax,'YTickLabel',YTickLabel)
Or if you know everything before, do it manually from the beginning:
[N,M] = size(X)
set(ax,'XTick',0.5+1:N)
set(ax,'XTickLabel',1:N)
set(ax,'YTick',0.5+1:M)
set(ax,'YTickLabel',1:M)
The marked answer works with a surf or mesh plot, however, I needed a solution which worked for a 2d plot.
This can be done by creating two axes, one to display the grid and the other to display the labels as follows
xlabels=1:1:10; %define where we want to see the labels
xgrid=0.5:1:10.5; %define where we want to see the grid
plot(xlabels,xlabels.^2); %plot a parabola as an example
set(gca,'xlim',[min(xgrid) max(xgrid)]); %set axis limits so we can see all the grid lines
set(gca,'XTickLabel',xlabels); %print the labels on this axis
axis2=copyobj(gca,gcf); %make an identical copy of the current axis and add it to the current figure
set(axis2,'Color','none'); %make the new axis transparent so we can see the plot
set(axis2,'xtick',xgrid,'XTickLabel',''); %set the tick marks to the grid, turning off labels
grid(axis2,'on'); %turn on the grid
This script displays the following figure :

Creating Horizontal and Vertical lines with varying axes values? (MatLab)

I am trying to plot both horizontal and vertical lines on a histogram that will be accurate to changing limits on both the x and y axes. I was using the line(X,Y) function, but cannot find a useful way to get the lines to be set depending on the parameters of the graph window.
I'm not entirely clear on what you want but here's the simplest answer to what I think you want:
Makes a sample histogram
y = randn(100,1);
hist(y,10)
Get the current limits of the x and y axes
xlimits = get(gca, 'XLim');
ylimits = get(gca, 'YLim');
Computes a single numeric value to plot a horizontal line.You'll want to replace this with your specific function of the axes limits
halfpt = ((ylimits(2)-ylimits(1))/2) + ylimits(1);
line(xlimits, [halfpt halfpt])
I'm not sure, but from your comment I'm suspecting that you aren't changing your axes programmatically, with say set(gca,'Xlim', [0 10]) but that you want to be able to drag the axes of your your figure with the mouse, say by using that hand/pointer button in the figure editor. In which case, one solution is to make your figure a GUI and write a callback function that handles line plotting that is a function of the xlim and ylim. Here's an example that always keeps the line in the middle of the axes regardless of how they are dragged:
function myGUI
figure('WindowButtonMotionFcn',#myCallback)
y = randn(100,1);
hist(y,10)
function myCallback(src,eventdata)
xlimits = get(gca, 'XLim');
ylimits = get(gca, 'YLim');
halfpt = ((ylimits(2)-ylimits(1))/2) + ylimits(1);
lh = findall(gcf,'Type','Line');
delete(lh);
myline = line(xlimits, [halfpt halfpt])
end
end