Search code examples
matlabmatlab-figurelegend

Setting a title for a legend in Matlab


The following code is the most Minimal, Complete, and Verifiable example that I could find. My true case is much more complicated:

x = 1:0.1:10;
y = sin(x);

subplot 211
plot(x,y)
[leg,att] = legend('show');
title(leg,'my title')
leg.Title.Visible = 'on';

subplot 212
plot(x,y)
leg = legend('show');
title(leg,'my title')

This results in:

legend title

As you can clearly see, something is wrong with the upper legend title. Somehow, asking for the att output of the legend interfere with its' title. First, for some reason, it makes it invisible, but that is already solved in the code above.

The main problem is with its position - it doesn't seem to have such a property, so once set I can't move it.

I read this post in Undocumented Matlab and I can think of some similar hacks by myself (like using text with the position of the legend), but my situation is very complicated, and I already configure the legend a lot and have several axes in every figure. Thus, I prefer a simple and working solution that rely on the original functionality of the legend title.

I use Matlab 2016a.


Solution

  • Caution! This answer uses undocumented features, the result was tested on versions 2016a and 2017a, and may differ in other versions.
    Let me know in the comments if this also works for other versions of Matlab.


    I found the answer after some research of the undocumented properties of the legend. The most direct answer to the problem in the question is to set the position of the legend title back to its place. This can be done with the property Position, that is part of the hidden property NodeChildren, of the legend title.

    The legend box is effectively just a small axes object, so the position units are normalized relative to the axes box. If we look at it just after creation of the legend we get:

    [hleg,att] = legend('show');
    title(hleg,'my title')
    hleg.Title.NodeChildren.Position
    
    ans =
         0     0     0
    

    Which is where we see the legend in the question. Now we can move it back to its correct place with:

    hleg.Title.NodeChildren.Position = [0.5 1.5 0];
    

    Here I placed it at the center of the horizontal axis (x = 0.5), above the vertical axis (y = 1.5), and the depth axis is irrelevant so it's zero (z = 0):

    correct place

    If you are not bothered by the option that the legend will interfere with the data, (e.g. your legend is outside the data axes) then you can stop here. If not, continue to read...


    We could set the background color to white:

    hleg.Title.NodeChildren.BackgroundColor = 'w';
    

    But it doesn't look good because the background is only for the text box, and is not aligned with the legend box. So I prefer to enlarge the legend box to include the title and move the legend attributes down to make some space. It takes more effort (because we need to calculate the right position for all elements), but it can be done in the following way:

    x = 1:0.1:10;
    plot(x,sin(x),x,cos(x))
    [hleg,icons,plots] = legend('show');
    title(hleg,'my title')
    hleg.Title.Visible = 'on';
    % the addition in height needed for the title:
    title_hight = hleg.Position(4)/numel(plots);
    hleg.Position([2 4]) = [hleg.Position(2)-title_hight hleg.Position(4)+title_hight];
    % calculate new position for the elements in the legeng:
    new_pos = fliplr(0.5/(numel(plots)+1):1/(numel(plots)+1):1);
    hleg.Title.NodeChildren.Position = [0.5 new_pos(1) 0];
    % set the text to the right position:
    leg_txt = findobj(icons,'Type','Text');
    txt_pos = cell2mat({leg_txt.Position}.');
    txt_pos(:,2) = new_pos(2:end);
    set(leg_txt,{'Position'},mat2cell(txt_pos,[1 1],3));
    % set the icons to the right position:
    leg_att = findobj(icons,'Type','Line');
    set(leg_att,{'YData'},mat2cell(repmat(repelem(new_pos(2:end).',...
        numel(plots)),1,2),ones(numel(plots)*2,1),2))
    

    The code above is also generalized for more the one data series, although I guess that there are cases where this will fail to give the right solution and will need some fine tunings.

    large box