how to merge two legend in one?

183 vues (au cours des 30 derniers jours)
Arash Hajisharifi
Arash Hajisharifi le 7 Mar 2020
Hello every one,
i have three plots and i want to have just one legend for plot p1 and p2 ,for example for the following code:
x=0:0.25:2*pi;
y=sin(x);
p1=plot(x,y);
hold on
p2=plot(x,y,'o','MarkerSize',12,'MarkerFaceColor','b','MarkerEdgeColor','b');
p3=plot(x+0.02,y+0.02,'o','MarkerSize',4,'MarkerFaceColor','w','MarkerEdgeColor','w');
as you see i have merged two markers(a big blue circle and a small white circle ) and i want two show these two circles together as same legend.is there any way to do it?
  3 commentaires
Arash Hajisharifi
Arash Hajisharifi le 8 Mar 2020
Yes, it is nice
you are right, it can be done by two line handles too.
dpb
dpb le 8 Mar 2020
I tried
hL(2)=plot(x+dxy,y+dxy,'or','MarkerSize',4);
also and thought kinda' cute...

Connectez-vous pour commenter.

Réponse acceptée

Walter Roberson
Walter Roberson le 7 Mar 2020
Yes, there is a way to do it.
[h, icons] = legend([p2, p3], {'whatever the legend should say', 'unused'});
%p2_text = icons(1);
p3_text = icons(2);
p2_line = icons(3);
%p2_marker = icons(4);
p3_line = icons(5);
p3_marker = icons(6);
p2_line.Color = p1.Color;
p2_line.LineStyle = p1.LineStyle; %it is missing the solid line of p1
p3_text.Visible = 'off'; %turn off what you do not need
p3_line.Visible = 'off';
p3_marker.XData = p2_marker.XData + 0.02; %move the small white circle
p3_marker.YData = p2_marker.YData + 0.02;
This is a bit clunky.
You have to use a backwards compatibility of legend() that will probably go away and might not work for uifigures. You must have two or more outputs on the legend() call: the backwards compatibility is triggered by that. If you only have one output on the legend call, then it does not create the objects that you need to have created.
You cannot just pass a single graphics object as the first parameter to legend and add-on the additional graphics within the legend, as you are not permitted to add additional graphics objects inside a legend. Instead you have to create as many graphics objects as you need at the start, and then turn off the legend portions of them that you do not need. But you can move the objects that are already inside the legend.
  2 commentaires
dpb
dpb le 7 Mar 2020
Modifié(e) : dpb le 7 Mar 2020
"have to use a backwards compatibility of legend() that will probably go away..."
Indeed, legend was klunky before and now, while TMW has added some features, their recent penchant for hiding needed/useful properties from the user and not only making them invisible but to actually make the object almost totally opaque is a real kick in the teeth for such user niceties.
Dunno that users have the power to change the mindset that seems to have developed that we are children that must be protected from ourselves, but it is surely disheartening.
A quick look-thru using Yair's undocumented function makes it appear that even by getting to what is exposed but hidden isn't enough to be able to get to the actual objects; TMW has appears to have completely made them inaccessible.
I guess I now recall having been thru the exercise earlier that one cannot even use those hidden properties to get the children of the axes buried in the new legend object. If TMW does choose to actually remove the backwards compatibility feature, one will be hosed entirely other than by completely reinventing the wheel.
"Ease of use and rapid development" as long as willing to live with what is deemed allowable...
Arash Hajisharifi
Arash Hajisharifi le 8 Mar 2020
Thanks @Walter Roberson
it worked perfectly, thanks for your solution.

Connectez-vous pour commenter.

Plus de réponses (2)

Nathan Miller
Nathan Miller le 19 Oct 2021
Modifié(e) : Walter Roberson le 13 Mai 2023
If you dig in deep enough you absolutely can customize the legend icons to whatever you want programatically, but it's quite messy and 'undocumented'.
I came up with my solution below after looking through Yair's https://undocumentedmatlab.com/blog_old/plot-legend-customization and thinking outside the box a bit with a colleague. The key was doing the copy of the node object to create a new Marker primitive and then updating the Parent property that loads it into the legend handle to be drawn.
% Plot OP's code
figure; hold all; % new fig, enable hold/add
x=0:0.25:2*pi;
y=sin(x);
hL(1)=plot(x,y,'o-','MarkerSize',12,'MarkerFaceColor','b','MarkerEdgeColor','b');
hL(2)=plot(x+0.02,y+0.02,'o','MarkerSize',4,'MarkerFaceColor','w','MarkerEdgeColor','w');
% Add legend for the first/main plot handle
hLegend = legend(hL(1),'location','best');
drawnow(); % have to render the internal nodes before accessing them
% Extract legend nodes/primitives
hLegendEntry = hLegend.EntryContainer.NodeChildren(1); % first/bottom row of legend
iconSet = hLegendEntry.Icon.Transform.Children.Children; % array of first/bottom row's icons (marker+line)
% Create a new icon marker to add to the icon set
newLegendIcon = copy(iconSet(1)); % copy the object (or look into making a matlab.graphics.primitive.world.Marker)
newLegendIcon.get % list all properties
newLegendIcon.Parent = iconSet(1).Parent; % set the parent, adding to the legend's icon draw set
% Mess with the new icon's properties to show how you want
newLegendIcon.FaceColorData = uint8([255;255;255;255]); % rgba uint8
newLegendIcon.VertexData(1) = 0.53; % [0-1] within the icon's boundaries (not the +0.02)
newLegendIcon.VertexData(2) = 0.65; % [0-1] within the icon's boundaries (not the +0.02)
newLegendIcon.Size = 4; % a little different from MarkerSize it seems
Output image if the auto-run figure from the above code doesn't generate correctly:
Bonus extra figure/example showing how you can separate the icon markers to show multiple of them on the same line:
  2 commentaires
Zhang Jianyu
Zhang Jianyu le 13 Mai 2023
Can you show me how you plot the legend of the last graph?
Nathan Miller
Nathan Miller le 15 Mai 2023
Here, something to this effect:
% Plot OP's code
figure; hold all; % new fig, enable hold/add
x=1:5;
y=x;
hL(1)=plot(x,y,'ob');
hL(2)=plot(x,y+1,'r-s');
% Add legend for the first/main plot handle
hLegend = legend(hL,'location','best');
drawnow(); % have to render the internal nodes before accessing them
%%% Update top row legened (blue circle)
% Extract legend nodes/primitives
hLegendEntryTop = hLegend.EntryContainer.NodeChildren(end); % top row of legend
iconSet = hLegendEntryTop.Icon.Transform.Children.Children; % array of first/bottom row's icons (marker+line)
% Move primary marker over to the edge
iconSet(1).VertexData(1) = 0.90;
% Create a new icon marker to add to the icon set
newLegendIcon1 = copy(iconSet(1)); % copy the object (or look into making a matlab.graphics.primitive.world.Marker)
newLegendIcon1.Parent = iconSet(1).Parent; % set the parent, adding to the legend's icon draw set
newLegendIcon1.Style = 'plus';
newLegendIcon1.VertexData(1) = 0.10;
% Create a new icon marker to add to the icon set
newLegendIcon2 = copy(iconSet(1)); % copy the object (or look into making a matlab.graphics.primitive.world.Marker)
newLegendIcon2.Parent = iconSet(1).Parent; % set the parent, adding to the legend's icon draw set
newLegendIcon2.Style = 'asterisk';
newLegendIcon2.VertexData(1) = 0.50;
%%% Update bottom row legend (red square + line)
% Extract legend nodes/primitives
hLegendEntryBtm = hLegend.EntryContainer.NodeChildren(1); % bottom row of legend
iconSet = hLegendEntryBtm.Icon.Transform.Children.Children; % array of first/bottom row's icons (marker+line)
% Move primary marker over
iconSet(1).VertexData(1) = 0.25;
% Create a new icon marker to add to the icon set
newLegendIcon = copy(iconSet(1)); % copy the object (or look into making a matlab.graphics.primitive.world.Marker)
newLegendIcon.Parent = iconSet(1).Parent; % set the parent, adding to the legend's icon draw set
newLegendIcon.Style = 'pentagram';
newLegendIcon.VertexData(1) = 0.75;
newLegendIcon.Size = 8;

Connectez-vous pour commenter.


Hakan Caldag
Hakan Caldag le 28 Mar 2023
Modifié(e) : Hakan Caldag le 7 Nov 2023
For anyone who is happy to keep the legend somewhere outside the axes, I ended up with another solution that looks simpler to me: Copying the axes object to have a new one right on top, having two legends for each and setting the top legend color to 'none' so it becomes transparent and both icons become visible:
x=0:0.25:2*pi;
y=sin(x);
subplot(121);
p1=plot(x,y);
hold on
p2=plot(x,y,'o','MarkerSize',12,'MarkerFaceColor','b','MarkerEdgeColor','b');
p3=plot(x+0.02,y+0.02,'o','MarkerSize',4,'MarkerFaceColor','w','MarkerEdgeColor','w');
legend('','Anything here','Position',[0.6 0.6 0.1 0.1])
axhand=gca; %axes handle
duplicateplot=copyobj(axhand,1); % the duplicate
axes(duplicateplot);
% Removing the additional labels/ticks of the second axes object
xticks([]);yticks([]);xlabel('');ylabel('');
legend('','',' ','Position',[0.52 0.6036 0.135 0.1],'Color','none','EdgeColor','none')
% Setting the colors to none is what makes the icons overlap
% Adjust the position any way you would like here, I tried to match the
% shape in the plot.
% Notice that the ' ' label is set for the plot object with the white icons

Community Treasure Hunt

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

Start Hunting!

Translated by