Adding a global legend to a tiledlayout

376 visualizaciones (últimos 30 días)
Christian Schröder
Christian Schröder el 4 de Jun. de 2020
Comentada: Rik el 14 de Ag. de 2025
Good morning,
I'm using MATLAB R2020a Update 2. I have a tiledlayout of five (three by two) area plots and would like to use the sixth, currently empty, tile to add a global legend. I've already found this question, and understand that there's no official, built-in way of doing this, but perhaps it's possible to get creative.
What I've tried is adding a new, empty area plot to the sixth tile, using NaNs as the shares, then adding a legend to that and setting the axis object for that tile to invisible, as follows:
% ...
ax = nexttile;
area([NaN NaN], NaN(2, 4));
leg = legend({'tar', 'sta', 'ext', 'dmp');
leg.Location = 'none';
leg.Interpreter = 'latex';
leg.FontSize = 16;
ax.Visible = false;
This works in principle, but leaves a horizontal line where the X axis would be:
This only happens when I use area(), not e.g. plot(). Perhaps it's a bug, perhaps it's intentional and due to the way area() works. In any case it's not what I want, but I can't get rid of it (without breaking other things in the process).
I had the idea of making the children of this axis, i.e. the areas of the area plot, invisible as well, like so:
for i = 1:length(ax.Children)
ax.Children(i).Visible = false;
end
But while this gets rid of the horizontal line it also grays out the legend entries:
This behavior in turn is known and expected and apparently cannot be changed. The workaround suggested by a Mathworks staffer in the linked question is to plot NaNs; but that's what I'm already doing and what's leaving the horizontal line, due to area()'s quirks.
Can anyone help?
I'm not hung up on specifically creating an invisible area(), this is merely the best (first, only) idea I had for fudging a global legend. If anyone can make this approach work, that'd be wonderful. If anyone has another idea of how to achieve a similar effect, that'd be wonderful as well.
Thank you very much!

Respuesta aceptada

Rik
Rik el 4 de Jun. de 2020
I can't get the position quite right, maybe you have more luck with your tinkering. This at least gets rid of the line. (I guess this is what you meant with boiler plate code in your answer here)
The new tools (like tiledlayout) have some advantages over using subplot, but this hasn't convinced me so far.
rng(4);
figure(1),clf(1)
tiledlayout(3,2);
for n=1:5
nexttile
Y=rand(30,4);Y=Y./sum(Y,2);
area(Y)
end
ax = nexttile;
p_ax=ax.Position;
area([NaN NaN], NaN(2, 4));
leg = legend({'tar', 'sta', 'ext', 'dmp'});
p_leg=leg.Position;
delete(ax)
ax=axes('Position',[p_ax(1:2) 0 0]);
area([NaN NaN], NaN(2, 4));
leg = legend({'tar', 'sta', 'ext', 'dmp'});
leg.Location = 'none';
leg.Interpreter = 'latex';
leg.FontSize = 16;
ax.Visible = false;
leg.Position=p_leg;
  1 comentario
Christian Schröder
Christian Schröder el 4 de Jun. de 2020
Excellent, that's great, thank you so much! I was able to put the legend more or less in the right place as follows:
fake_ax = nexttile;
ax_pos = fake_ax.Position;
area([NaN NaN], NaN(2, 4));
fake_leg = legend({'tar', 'sta', 'ext', 'dmp'});
fake_leg.Interpreter = 'latex';
fake_leg.FontSize = gp.axis_font_size;
leg_pos = fake_leg.Position;
delete(fake_ax);
ax = axes('Position', [ax_pos(1:2) 0 0]);
area([NaN NaN], NaN(2, 4));
leg = legend({'tar', 'sta', 'ext', 'dmp'});
leg.Location = 'none';
leg.Interpreter = 'latex';
leg.FontSize = gp.axis_font_size;
leg.Position = [(0.75 - leg_pos(3) / 2) (0.2 - leg_pos(4) / 2) leg_pos(3:4)];
ax.Visible = false;
This way the fake legend has the same size as the real one, and it's roughly centered.
And yes, this is one example of what I meant by "boilerplate code". (A while ago I also ran into trouble when trying to control the placement and formatting of tick labels; fortunately I was able to find what I needed on MATLAB Answers.)

Iniciar sesión para comentar.

Más respuestas (3)

Adam Danz
Adam Danz el 29 de Sept. de 2020
Editada: Adam Danz el 30 de En. de 2023
Update
As of Matlab r2020b, legend location can be specified relative to axes created within an TiledLayout. This demo below produces 1x4 tiles of axes and the adds a legend to the East of the layout.
% DEMO
fig = figure();
fig.Position(4) = 225;
colors = [winter(3);summer(3);autumn(3);copper(3)];
subPlotNames = 'ABCD';
h = gobjects(3,4);
tiledlayout(1,5)
for i = 1:4
ax = nexttile;
h(:,i) = plot([0,1],[.66;.5;.33].*[1,1],'LineWidth',4);
text([.1,.1,.1],[.66;.5;.33],{'1' '2' '3'},'VerticalAlignment', 'Bottom')
set(h(:,i), {'DisplayName'}, compose('%s %d',subPlotNames(i),1:3)')
title(subPlotNames(i))
end
set(h(:), {'Color'}, mat2cell(colors,ones(12,1),3))
lg = legend(h(:));
lg.Layout.Tile = 'East'; % <-- place legend east of tiles
  1 comentario
Christian Schröder
Christian Schröder el 30 de Sept. de 2020
Very cool! MATLAB just keeps getting better with each new release. Thank you very much!

Iniciar sesión para comentar.


Alexander
Alexander el 10 de Jul. de 2024
When a tiledlayout is created, it automatically creates 4 extra tiles ('north','east','south','west') around the layout you initially declare. You can create one legend after you are finished plotting, and place it in one of these locations like this.
data1 = randn(100,4);
data2 = randn(100,4);
tiledlayout(2,2);
for tile = 1:4
nexttile()
plot(data1(:,tile),'DisplayName','Data1')
hold on
plot(data2(:,tile),'DisplayName','Data2')
end
lgd = legend;
lgd.Layout.Tile = 'east';
This code will plot 4 graphs in a 2x2 layout. In my loop I increment by one tile and plot Data1 in blue and Data2 in red. After finishing the loop, I create the legend and put the legend in the eastern tile.
  5 comentarios
RAJEEV
RAJEEV el 14 de Ag. de 2025
What if : instead of only two data you have something like:
Reference Data: Data0
then Data1,Data2,Data3 and Data4.
We are comparing Data0 with Data1,Data2,Data3 and Data4.
Rik
Rik el 14 de Ag. de 2025
@RAJEEV then you will have a list of the names and you can index that list inside the loop. You should not have numbered variable names if you have more than two or three, because you should be using arrays. If you don't, you will either have a lot of work copy-pasting, or will resort to terrible ideas like using eval (which is inefficient and unsafe).

Iniciar sesión para comentar.


Rubens R. Fernandes
Rubens R. Fernandes el 15 de Jun. de 2022
Editada: Rubens R. Fernandes el 15 de Jun. de 2022
I think I got a solution for this!
In my case, I am using a tiled layout of (2x2), and the legend is defined on the data plotted for the first layout.
What I got is:
figure
%Defines the variables
x=linspace(1,10,10);
y1=x;
y2 = 2.*x;
y3 = 3.* x;
%Initiates the figure
tiledlayout(2,2)
%Plots the data in tile 1
nexttile(1)
plot(x, y1, 'DisplayName', 'y1');
hold on
plot(x, y2, 'DisplayName', 'y2');
hold on
plot(x, y3, 'DisplayName', 'y3');
%Plots the data in tile 2
nexttile(2)
plot(x, y1.^2, 'HandleVisibility', 'Off');
hold on
plot(x, y2.^2, 'HandleVisibility', 'Off');
hold on
plot(x, y3.^2, 'HandleVisibility', 'Off');
%Plots the data in tile 3
nexttile(3)
plot(x, y1.^3, 'HandleVisibility', 'Off');
hold on
plot(x, y2.^3, 'HandleVisibility', 'Off');
hold on
plot(x, y3.^3, 'HandleVisibility', 'Off');
%Plots the legend in tile 4
ax = nexttile(1);
lg = legend('Orientation','Vertical','NumColumns',2, 'Interpreter', 'Latex');
lg.Layout.Tile = 4; % THIS IS THE COMMAND THAT WILL SET THE TILE WHERE YOU WANT TO PLOT YOUR LEGEND
Hope it helps!
  2 comentarios
Philipp Richard
Philipp Richard el 29 de En. de 2023
Awesome ! Thx !
Christian Schröder
Christian Schröder el 5 de Feb. de 2023
This is also very cool, and for the record you can specify "south", "north", "east" and "west" instead of a tile number to place your legend outside of the grid of tiles in the tiledlayout as well.

Iniciar sesión para comentar.

Categorías

Más información sobre Legend en Help Center y File Exchange.

Etiquetas

Productos


Versión

R2020a

Community Treasure Hunt

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

Start Hunting!

Translated by