1
votes

I want to add an entry manually to a MATLAB legend. This legend can be pre-existent and contain other graphed elements' entries, but not necessarily.

I make a scatter plot, but instead of using e.g. scatter(x,y), I plot it using

for n = 1:numel(x)
    text(x(n),y(n),num2str(n), ...
    'HorizontalAlignment','center','color',[1 0 0])
end

This results in a scatter plot of numbers one through the number of elements in x (and y, because they are of the same size). I want to add a legend entry for these numbers.

I tried to add or edit the legend with

[h,icons,plots,s] = legend(___)

as described on the legend documentation page. I can't figure out how I can add a legend entry, without having to plot something (such as an actual scatter plot or regular plot). I want the usual line or marker symbol in the legend to be a number or character such as 'n', indicating the numbers in the graph. Is this possible and how would one achieve this?

1
Could you fake it with an annotation? - Steve
@Steve - Yes, but then it wouldn't work if the graph already had a legend for any other graphed elements. In that case I would want to add my custom entry to that legend. - Erik
Perhaps an option could be to create hidden (white) points (using code similar to this answer) then add the number to the legend text instead, for example 1: foo, 2: bar. It might also be possible to add a placeholder to the legend in this way, then fake it by adding the text as an annotation on top (although the legend might hide it). - zelanix
@zelanix - thanks for the link and suggestions. I thought of the following: I could make the default legend line or marker invisible (can be done with icons, the legend output in the question). Then I could alter the legend text position (also in icons). The text string then could become 'n␣␣␣(as spaces as needed to look OK)␣␣␣legend entry text'. This would then be all black text, which is OK for black numbers. However, if I wanted to add another scatter of numbers with a different colour, the 'n' couldn't be in that colour. - Erik
Aren't MATLAB's legends just axes, only wrapped in some of the more modern graphics engine wrappers and quite hard-coded and inaccessible, compared to regular axes? I could create my own function that instead of adding the normal legend, it adds axes that look exactly like the real legend, incl. any current entries in the legend. This would require quite an amount of programming, however, for which I don't have the time. - Erik

1 Answers

1
votes

EDIT by Erik

My answer goes below zelanix's answer, because mine is based on it.


Original answer

A fairly workable solution may be as follows:

x = rand(10, 1);
y = rand(10, 1);

figure;

text(x,y,num2str(transpose(1:numel(x))),'HorizontalAlignment','center')

% Create dummy legend entries, with white symbols.
hold on;
plot(0, 0, 'o', 'color', [1 1 1], 'visible', 'off');
plot(0, 0, 'o', 'color', [1 1 1], 'visible', 'off');
hold off;

% Create legend with placeholder entries.
[h_leg, icons] = legend('foo', 'bar');

% Create new (invisible) axes on top of the legend so that we can draw
% text on top.
ax2 = axes('position', get(h_leg, 'position'));
set(ax2, 'Color', 'none', 'Box', 'off')
set(ax2, 'xtick', [], 'ytick', []);

% Draw the numbers on the legend, positioned as per the original markers.
text(get(icons(4), 'XData'), get(icons(4), 'YData'), '1', 'HorizontalAlignment', 'center')
text(get(icons(6), 'XData'), get(icons(6), 'YData'), '2', 'HorizontalAlignment', 'center')

axes(ax1);

Output:

enter image description here

The trick to this is that the new axes are created in exactly the same place as the legend, and the coordinates of the elements of the icons are in normalised coordinates which can now be used inside the new axes directly. Of course you are now free to use whatever font size / colour / whatever you need.

The disadvantage is that this should only be called after your legend has been populated and positioned. Moving the legend, or adding entries will not update the custom markers.


Erik's answer

Based on zelanix's answer above. It is a work-in-progress answer, I am trying to make a quite flexible function of this. Currently, it's just a script that you'd need to adapt to your situation.

% plot some lines and some text numbers
f = figure;
plot([0 1],[0 1],[0 1],[1 0])
x = rand(25,1);
y = rand(25,1);
for n = 1:numel(x)
    text(x(n),y(n),num2str(n), ...
    'HorizontalAlignment','center','color',[1 0 0])
end
hold on
% scatter(x,y) % used to test the number positions
scatter(x,y,'Visible','off') % moves the legend location to best position

% create the dummy legend using some dummy plots
plot(0,0,'o','Visible','off')
[l,i] = legend('some line','some other line','some numbers','location','best');
l.Visible = 'off';

% create empty axes to mimick legend
oa = gca; % the original current axes handle
a = axes;
axis manual
a.Box = 'on';
a.XTick = [];
a.YTick = [];

% copy the legend's properties and contents to the new axes
a.Units = l.Units; % just in case
a.Position = l.Position;
i = copyobj(i,a);

% replace the marker with a red 'n'
s = findobj(i,'string','some numbers');
% m = findobj(i(i~=s),'-property','YData','marker','o');
m = findobj(i(i~=s),'-property','YData');
sy = s.Position(2);
if numel(m)>1
    dy = abs(m(1).YData - sy);
    for k = 2:numel(m)
        h = m(k);
        dy2 = abs(h.YData - sy);
        if dy2<dy
            kbest = k;
            dy = dy2;
        end
    end
    m = m(kbest);
end
m.Visible = 'off';
mx = m.XData;
text(mx,sy,'n','HorizontalAlignment','center','color',[1 0 0])

% reset current axes to main axes
f.CurrentAxes = oa;

The result:

enter image description here