Legend Positioning in Tiled Chart Layout

I have created 2 by 2 tiled chart and I want to position the legend centered above the figure. For some reason, MATLAB 2019b, which I am using, has locked the 'Position' property of the legend when usend in a tiled chart.
Firstly, why would they do this, and secondly does someone know a workaround?
Thanks,
Manuel

1 comentario

Kyle Johnson
Kyle Johnson el 23 de Sept. de 2020
Editada: Kyle Johnson el 23 de Sept. de 2020
I have the same problem. I have even tried specifying the legend position to none.

Iniciar sesión para comentar.

Respuestas (2)

Adam Danz
Adam Danz el 23 de Sept. de 2020
" MATLAB 2019b ... has locked the 'Position' property of the legend when usend in a tiled chart."
Yes, that's correct.
Starting in r2020b the legend placement is more flexible and supports changes to the position propery as well as setting "location" relative to the tiled layout.
x = magic(4);
figure()
tiledlayout(2,2)
ax(1) = nexttile;
plot(x,'.','MarkerSize', 20)
ax(2) = nexttile;
bar(x,'stacked')
ax(3) = nexttile;
plot(x,'-','LineWidth', 2)
ax(4) = nexttile;
contour(x,'LineWidth',2)
ax(4).Colormap = lines(256);
lh =legend(ax(1),'Location','NorthOutside','Orientation','Horizontal');
lh.Layout.Tile = 'North'; % <----- relative to tiledlayout

6 comentarios

Kyle Johnson
Kyle Johnson el 23 de Sept. de 2020
Wish I saw this 4 minutes before I posted! Guess I need to updated to 2020rb. Thanks!
Adam Danz
Adam Danz el 23 de Sept. de 2020
Editada: Adam Danz el 23 de Sept. de 2020
@Kyle Johnson your answer is still very valuable since there are plenty of users who haven't upgraded and may not upgrade for years. There are people in my lab who still use the prehistoric r2014a! 😂
Wow, it is really nice to see that the developers of MATLAB listen to the community, because I actually filed an official technical support case regarding this issue back in March 2020. Even if this was changed indenpendently of my request, it is nice that it is changed now going forward.
My personal workaround in MATLAB 2019b was to create 'subplot' based figure, where the legend positioning is more flexible, and copy the axis objects from the tiled layout over to the new figure. An example is below:
close all
% Create original figure with tiled layout
f1=figure('Units','centimeters','Position',[5 5 12.2 8.8]);
til = tiledlayout(2,2,'TileSpacing','none','Padding','none');
ax(1) = nexttile; hold on; box on;
p(1) = plot(rand(10,1));
p(2) = plot(rand(10,1));
p(3) = plot(rand(10,1));
ax(2) = nexttile; hold on; box on;
plot(rand(10,1))
plot(rand(10,1))
plot(rand(10,1))
ax(3) = nexttile; hold on; box on;
plot(rand(10,1))
plot(rand(10,1))
plot(rand(10,1))
ax(4) = nexttile; hold on; box on;
plot(rand(10,1))
plot(rand(10,1))
plot(rand(10,1))
legend(ax(1),p,{'Name1','Name2','Name3'},...
'Orientation','horizontal','Location','NorthOutside')
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Create new figure
fn=figure;
% Set properties from old figure
set(fn,'Units','centimeters','Position',f1.Position)
% Copy the axis from the old to the new figure and pause before every step
% (I have no idea why). You may need to have a look at the children in more
% detail to see which indices correspond to which axes
pause(0.5); ax(1)=copyobj(til.Children(5),fn); pause(0.5);
pause(0.5); ax(2)=copyobj(til.Children(4),fn);
pause(0.5); ax(3)=copyobj(til.Children(3),fn);
pause(0.5); ax(4)=copyobj(til.Children(2),fn);
% close the old figure
close(f1)
% Get the lines you want legend entries for (in the right order
p = ax(1).Children([3 2 1]);
% Create new legend
leg = legend(ax(1),p,{'Name1','Name2','Name3'},...
'Orientation','horizontal');
% Set position to your preference
pos = get(leg,'Position');
set(leg,'Position',[0.19 0.92 pos(3) pos(4)]);
Before:
After
Adam Danz
Adam Danz el 23 de Sept. de 2020
Editada: Adam Danz el 23 de Sept. de 2020
Why not just use subplot() to create all 4 axes instead of tiledlayout? The legend position is changeable with subplots in older (and current) releases.
'Wow, it is really nice to see that the developers of MATLAB listen to the community,"
Agreed!
Manuel Eichenlaub
Manuel Eichenlaub el 23 de Sept. de 2020
The reason I am using the tiled layout in the first place is because it offers a nice way to efficiently fill the complete figure by the axes like I my example above and have all axes the same sizes irrespective of labels or the layout, e.g. 2x2. I find this quite important when exporting the figures as pictures, e.g. for publications.
When using subplot, it always annoyed me that it leaves a lot of white space on the borders and in between the axes by default, which I find wasteful when you have limited space for a figure on a page. As far as I know there is no way to change that without setting the sizes of each axis individually. I have tried to write scripts to do this automatically in the past, but it was always a hustle so I ended up setting the sizes of the axis by hand. But thanks to the tiled layout this is no longer necessary.
Adam Danz
Adam Danz el 23 de Sept. de 2020
In the past I've use tight_subplot from the file exchange to do what you're describing with regular subplots.

Iniciar sesión para comentar.

Kyle Johnson
Kyle Johnson el 23 de Sept. de 2020
So this is really annoying. This is the workaround I developed. You can just create a new axis over the entire tiled layout, plot some NaNs there, and then set that axis to invisible. You then have a floating legend to reposition.
I made some room for that legend by adjusting the inner position of the tiled layout.
axs is an array containing all of the axes handles. t is the handle to the tiled layout.
var1name = 'annoyance (metric ton)';
var2name = 'frustration (ounces)';
ylabel(axs(1),var1name)
ylabel(axs(2),['mean: ' var1name])
ylabel(axs(3),var2name)
ylabel(axs(4),['mean: ' var2name])
xlabel(axs(3),'coding hour (hr)');
xlabel(axs(4),'coding hour (hr)');
t.InnerPosition = [t.InnerPosition(1) .15 t.InnerPosition(3) (t.InnerPosition(4)+t.InnerPosition(1)-.15)];
tmp = axes(fh1);
plot(tmp,nan,nan,'.','color',c(1,:))
hold on
plot(tmp,nan,nan,'.','color',c(2,:))
l = legend(tmp,{'attempt1','attempt2'},'Orientation','horizontal');
l.Position(1:2) = [.5-l.Position(3)/2 .025];
l.Title.String = 'Attempts';
tmp.Visible = 'off';

2 comentarios

Daniel
Daniel el 20 de Nov. de 2020
I think this is what I'm looking for, but I get an error when using InnerPosition with the handle for the tiledlayout. Any ideas?
Adam Danz
Adam Danz el 20 de Nov. de 2020
You must be using a version of Matlab prior to r2020b as my answer explains.
This answer applies changes to InnerPosition to axes that are independent to tiledLayout.

Iniciar sesión para comentar.

Categorías

Más información sobre Axes Appearance en Centro de ayuda y File Exchange.

Preguntada:

el 15 de Feb. de 2020

Comentada:

el 20 de Nov. de 2020

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!

Translated by