Question

I have 3 objects (a photo and 2 plots) to put into subplots on one figure. It should look like this:

image

But as one can notice, the photo should not be square but rectangle. I tried to make it this way (found here Matlab: How to align the axes of subplots when one of them contains a colorbar?):

main=subplot(4,4,[5,6,7,9,10,11,13,14,15])  %photo
imagesc(im); 
axis('image')  
pion=subplot(4,4,[8,12,16]); %right plot (rotated)
view(90, 90)
plot(ypion,Ppion,'.k');
poz=subplot(4,4,1:3); %upper plot
plot(xpoz,Ppoz,'.k');

pos1=get(poz,'Position')
pos2=get(main,'Position')
pos3=get(pion,'Position')

pos1(3) = pos2(3); %width for the upper plot
set(poz,'Position',pos1)
pos3(4) = pos2(4); %height for the right plot
set(pion,'Position',pos3) 

All I get is like this: image

How can I force the upper plot to have the width as the photo itself (not as the photo subplot)? Setting the equal widths of the subplots doesn't work, as the photo doesn't fill the subplot area.

Was it helpful?

Solution

The command axis image adjust the image axis ratio. So, in principle, if you adjust the plot ratios of the two plots to the same ratio, it will do what you want.

There is one caveat; the image is inherently 3 times wider or higher than the plots, due to the fact that you've plotted it in 3x3 subplots, vs 1x3 for the top and 3x1 for the right plots. So, you'll have to divide either the x or y ratios of the plots by 3.

Some example code:

clc, clf

% generate some bogus data

ypion = rand(500,1);
Ppion = 450*rand(500,1);

xpoz  = rand(500,1);
Ppoz  = 450*rand(500,1);

% Load photo
photoSub = subplot(4,4,[5,6,7,9,10,11,13,14,15]);
load mandrill
photo = imagesc([X,X]);
colormap(map)

axis image 

photoAxs = gca;
photoAxsRatio = get(photoAxs,'PlotBoxAspectRatio')

% right plot 
subplot(4,4,[8,12,16]); 
plot(Ppion,ypion,'k.');
rightAxs = gca;
axis tight

% upper plot
subplot(4,4,[1 2 3]);
plot(xpoz,Ppoz,'k.');
topAxs = gca;
axis tight


% adjust ratios
topAxsRatio = photoAxsRatio;
topAxsRatio(2) = photoAxsRatio(2)/3.8;    % NOTE: not exactly 3...
set(topAxs,'PlotBoxAspectRatio', topAxsRatio)

rightAxsRatio = photoAxsRatio;
rightAxsRatio(1) = photoAxsRatio(1)/3.6;  % NOTE: not exactly 3...
set(rightAxs,'PlotBoxAspectRatio', rightAxsRatio)

This gives the following result:

Side by Side

Just to test, changing photo = imagesc([X,X]); to photo = imagesc([X;X]); gives this:

over-under

Note that I did not divide the ratios by 3 exactly; it only came out OK if I used factors closer to 4. I do not know why that is; AFAIK, a factor of 3 should do the trick...

Oh well, at least you have something to work with now :)

OTHER TIPS

Here's a solution that removes the guesswork in the accepted answer. This solution is adapted from the original one posted here.

% adjust ratios
photoAxsratio = photoAxs.PlotBoxAspectRatio(1)/photoAxs.PlotBoxAspectRatio(2);
topAxsratio = photoAxsratio * photoAxs.Position(4)/topAxs.Position(4);
topAxs.PlotBoxAspectRatio = [topAxsratio, 1, 1];

rightAxsratio = rightAxs.Position(3) / (photoAxs.Position(3) / photoAxsratio);
rightAxs.PlotBoxAspectRatio = [rightAxsratio, 1, 1];

Preview:

enter image description here


A bit of explanation

Some of the explanation has been posted in the original post, I'm not going to repeat them here.

The idea is to calculate the correct aspect ratio for the figures required to be resized.

We have the following equations:

Photo.width = Photo.height * Photo.ratio
TopAxis.width = TopAxis.height * TopAxis.ratio 
RightAxis.width = RightAxis.height * RightAxis.ratio 

Let

TopAxis.width = Photo.width 
RightAxis.height = Photo.height

We have

TopAxis.height * TopAxis.ratio = Photo.height * Photo.ratio
TopAixs.ratio = Photo.ratio * Photo.height / TopAxis.height

RightAxis.width / RightAxis.ratio  = Photo.width / Photo.ratio
RightAxis.ratio = RightAxis.width / (Photo.width / Photo.ratio)

Since the release of matlab R2019b you can now use: tiledlayout and nexttile.

It is now easy to do:

% Load a random scary image
I = im2gray(imread('https://unwinnable.com/wp-content/uploads/2011/10/The-Ring-well.jpg'));
% Some computation...
Sx = sum(I)/max(sum(I));
Sy = sum(I,2)/max(sum(I,2));

% We create an empty 3x3 tiled layout:
%  1 2 3
%  4 5 6
%  7 8 9
tiledlayout(3, 3);

% Starting from the tile 1, we plot a 1x2 tile:
%  1 2 x
%  x x x
%  x x x
nexttile(1,[1 2])
plot(Sx,'k.')
% Starting from the tile 4, we plot a 2x2 tile:
%  x x x
%  4 5 x
%  7 8 x
nexttile(4,[2 2])
imagesc(I)
colormap(gray(256))
% Starting from the tile 6, we plot a 2x1 tile:
%  x x x
%  x x 6
%  x x 9
nexttile(6,[2 1])
plot(Sy,1:numel(Sy),'k.')

Matlab adjust the size of the plots automatically.

And we obtain:

enter image description here

Under the hood the tiled layout looks like this:

enter image description here

For this particular case I suggest using low-level axes directly instead of high-level subplot.
Use the 'OuterPosition' properties of the three axes objects you create to place them in the right place with the appropriate size.

If you want the image to be aligned to the axes(distorted image):

change axis('image') to axis('tight') .

If you want the image aspect ratio to remain, and to align the axes to the image:

Well this is quick and dirty, but after applying axis('tight'), resize the figure to the appropriate scale...

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top