MATLAB: Figure Legends

As some point in the last few years, MATLAB fucked up its ‘legend’ command, specifically to piss me off I think. No, seriously, they did it just to mess with me. I’m not sure which version screwed it up for me (I went from 2012a to 2015a in one fell jump), but now that I am on 2015a, it has totally hosed all of my legacy code. The most frustrating aspect about it is that MATLAB deliberately chooses not to obey how its own code is supposed to work. Apparently, there is now officially a minimum size for legends, no matter what you specify for the 4×1 ‘Position’ vector. For anyone anally retentive particular about their figures (read: me), this essentially means you cannot make good looking figures for publication to scale should they require a legend in said figure. This might comprise only about 96% of all figures, so don’t expect a big impact, MATLAB.

\sarcasm

Below is a figure from a publication from a few years ago, when the legend function allowed for a tight box around the entries. The updated legend function doesn’t allow for legend boxes this small anymore.

examplefigure

Objectives for LegendMatrix Function

Rather than just complain about it, I decided to take the more constructive path and fix MATLAB’s problem. (Actually, I’m fibbing because I did complain to MATLAB tech support who were basically like you’re SOL, deal with it. I even went as far as searching every matlab *.m file for 0.1165 and 0.1536, which are the minimum height and width for legends, respectively, in normalized units. No such luck there either.)

http://www.mathworks.com/matlabcentral/answers/164413-legend-line-spacing-in-r2014b

But since I decided to build my own legend command, I decided to fix a few other aspects that bothered me. My thought process for building my own legend command is as follows:

  • Scalability: I needed the legend to be scalable. As I found out (and fixed) with my previous codes for figures, I need them to be adaptable depending on whether it is for a class or for a publication. Since I draw everything to scale, I decided to make the ‘FontSize’ one of the main drivers for the legend. Basically, the spacing for the legend should be driven by the size of the font and the individual entries can then be spaced accordingly.
  • Position Flexibility: Since this was the main driver for to write this function, it better be able to position the legend anywhere with any size! One of the things that is a bit wonky and I cannot think of a better way to address it is the interplay between the legend entries. Right now, a traditional 4×1 ‘Position’ vector is specified to position and size the legend, assuming units of centimeters. However, the legend items and text start in the upper left corner of the legend. This means that you may need to redraw the legend a few times, making small alterations to the position vector to get the size and position correct.
  • Multi-Columns: One of the aspects that annoyed me about the default legend command is the lack of flexibility for multiple columns. The only previous options were to have either 1 column or 1 row. However, there were plenty of times when a 2×2 or 3×3 legend arrangement would have made for better use of space. So now I’ve added this ability in this function.
  • Line Length/Position: The last thing that always irked me about the default legend command is that that basic lines are sooooooo long. In figures where space is a premium, that is certainly wasted space in my book. For this function, I have shortened the lines and reduced the white space between the text and line. Given that I almost exclusively only use ‘-‘ or ‘–‘ linestyles, this is reasonable. I’m not sure how well ‘:’ or ‘.-‘ work with this function. Lines with Markers should work as well.

LegendMatrix Function

The LegendMatrix function takes four input variables: ‘labels’ which is a cell array of strings for the legend labels, ‘pos’ which is a standard ‘Position’ matrix ([w0 h0 w h]) in units of centimeters, ‘fs’ which is the desired font size, and ‘col’ which is the number of columns you want to have. The cell array of strings is formatted as: [{‘Label1’},{‘Label2’},…,{‘Label3’}] where the labels are the text you want placed near the legend lines/markers. The inputs ‘pos’ and ‘fs’ should be self-explanatory, especially if you are using some of my other functions. For ‘col’, the usage is pretty simple. Just specify the number of columns you want. Then, the entries are populated top-to-bottom and left-to-right. So if you have 5 entries and 2 columns, then the left column will be entries 1-3 and the right column will be entries 4&5. If you wanted those 5 entries in 3 columns, then you’ll end up with 1&2 in the left column, 3&4 in the middle column, and 5 in the right column. The other thing to mention is that the ‘pos’ width and ‘cols’ are linked. The first column is lined up on the left and then subsequent columns are split evenly. This means you might need to play around with the size to make it work. (Once again, this is another reason why I suggest you make script files to do you plotting, one figure at a time, one figure per cell, so you can quickly rerun the figure generation if you need to adjust sizes.)

Like most of my other functions, these must be described or the function craps out. Also, remember, this function is meant to be used with the function FigureSetup. WordPress doesn’t like *.m-files so I have included a *.txt LegendMatrix file or you can copy the function below.

function LegendMatrix(labels,POS,fs,col)

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Function ‘LegendMatrix’:
%
% Builds a custom legend box based on an existing legend. Recently, MATLAB
% changed the ‘legend’ command such that it has a minimum size which cannot
% be changed. This circumvents the existing legend command and adds several
% enhancements.
%
% Input Variables:
%
% labels: Cell array of strings for the legend labels. This should
% be formatted [{‘Label1’},{‘Label2’},… ]
% POS: standard 1×4 matrix for position in units of centimeters
% [x0 y0 w h]
% fs: font size for the legend entries labels.
% col: number of columns for legend entries to be split into
%
% Usage Notes:
% ‘LegendMatrix’ creates a new legend with the ability to change the size
% of the legend box outside of the allowable bounds by Matlab.
% Additionally, this function gives you the option of making legend entries
% with multiple columns. The size and position of the legend
% box is specified with a standard 1×4 matrix in units of centimeters. The
% position starts in the lower left corner and the width/height of the
% legend are specified. When specifying multiple columns, the colums are
% divided at equal intervals and aligned left based on the width of the
% legend box. The position command is defined in centimeters and can be
% placed anywhere inside (or outside) the figure box. The size of the lines
% and spacing is tied to the fontsize of the text specified.
%
% Note: when passing through linespec handles with markers, you
% must specify at least ‘Marker’ and ‘MarkerEdgeColor’ for this function to
% work properly.
%
% Written by Jonathan D. Ellis, PhD
% Updated September 25, 2015
% Version: 1.00
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

delete( findobj(‘Tag’,’LegendMatrixTag’) )

labels = cellstr(labels);

allAxes = findall(gcf,’type’,’axes’);

h = allAxes(1).Children;

for i = 2:length(allAxes)
h = [h ; allAxes(i).Children];
end
h = flipud( h );
M = ceil(length(h)/col);

AX = axes;
AX.Box = ‘on’;
AX.Units = ‘centimeters’;
AX.Position = POS;
AX.XTick = [];
AX.YTick = [];
AX.Units = ‘points’;
AX.LineWidth = 0.25;
A = AX.Position;
AX.XLim = [0 A(3)];
AX.YLim = [0 A(4)];
AX.Tag = ‘LegendMatrixTag’;

W = A(3) / col;
H = A(4) / M;

Top = A(4);
j = 0;
N = 0;

for i = 1:length(h)
if isequal(get(h(i),’LineStyle’),’–‘)==1
l1 = line( [3+j*W 6+j*W] , [Top-H/2-(N)*H+1 Top-H/2-(N)*H+1] );
l1.Color = get(h(i),’Color’);
l1.LineWidth = get(h(i),’LineWidth’)*1.5;

l2 = line( [8+j*W 11+j*W] , [Top-H/2-(N)*H Top-H/2-(N)*H] );
l2.Color = get(h(i),’Color’);
l2.LineWidth = get(h(i),’LineWidth’)*1.5;
else
l = line( [3+j*W 11+j*W] , [Top-H/2-(N)*H+1 Top-H/2-(N)*H+1] );
l.Color = get(h(i),’Color’);
l.LineWidth = get(h(i),’LineWidth’)*1.5;
l.LineStyle = get(h(i),’LineStyle’);
end

m = line( [4.5+j*W+1] , [Top-H/2-(N)*H+1] );
m.Marker = get( h(i) , ‘Marker’ );
m.MarkerFaceColor = get(h(i),’MarkerFaceColor’);
m.MarkerEdgeColor = get(h(i),’MarkerEdgeColor’);
m.LineStyle = ‘none’;

t = text( 13+j*W , Top-H/2-(N)*H , labels(i) );
t.FontSize = fs;
t.FontName = ‘Times New Roman’;
t.VerticalAlignment = ‘middle’;
t.LineStyle = ‘none’;

N = N+1;

if N == M
j = j + 1;
N = 0;
end

end; % Function Ends


Simple LegendMatrix Usage

First, start out by creating some fake data to plot. Let’s use a simple 5 Hz cosine function plotted over 1 second.

>> t = linspace(0,1,1000)’;

>> A = cos(2*pi*5*t);

Next, generate your base figure box using FigureSetup, generate a generic axis with a handle (critical step!), and plot your data into that axis handle.

>> FigureSetup(1);

>> AX=axes;

>> plot( AX , t , [A A+1 A-1 A+2 A-2])

Now here, we’ve copied the same function just so we have five different lines to plot. After that, you have your basic figure generated. Now, at this point, we’re missing some axis labels so let’s add those.

>> xlabel(‘Time [s]’);

>> ylabel(‘Signal [arb.]’);

For a plot like this, it is probably handy to have the matrix outside of the figure. But since we can make arbitrary columns, let’s make the legend 2×3. First, let’s finish setting up the axes correctly. We will make the axes slightly larger than what we need, set out tick marks, then run AxisSetup to properly configure the axes. Note, the height of the figure is only 2.5 cm. This is to allow for room between the

>> axis([0 1 -3.1 3.1])

>> set( AX , ‘YTick’ ,-3:3 )

>> AxisSetup( AX , [1.1 1.1 6.8 2.5] , 9 )

At this point, your figure should look like this, especially if you used PrintRoutine to print your figure to a 600 DPI *.png file. Basically, this sets up the figure for all the simple things to do. The only thing left is to add the legend.

LegendMatrix1

Let’s compare to how the existing ‘legend’ command works. We’ll just label the signals with an arbitrary designation.

>> leg = legend(‘Sig_1′,’Sig_2′,’Sig_3′,’Sig_4′,’Sig_5’);

If we do that, we are left with:

LegendMatrix2

The legend is pretty huge, uses a ton of white space, and those base lines are super long. Even if we do some normal things like changing the fonts, font sizes, and orient it into a single horizontal line, the legend still looks like crap.

>> leg.FontSize = 7;

>> leg.FontName =  ‘Times New Roman’;

>> leg.Units = ‘centimeters’;

>> leg.Orientation = ‘horizontal’;

>> leg.Position = [3 3.8 2 .2];

LegendMatrix3

Even though we specify a small legend (2 cm wide by 0.2 cm high), MATLAB totally ignores this. @)%Q^!!%)!!!!! (my students tell me I should curse less…). Also, those lines are unnecessarily long for most figures.

For this example, let us use LegendMatrix with 3 columns and 8 pt font size. The first thing we need to do is delete the old legend (manually). Next, we need to setup the cell array of strings for our legend labels. Then, we can use LegendMatrix to generate the legend with the appropriate size and columns.

>> LegTxt = [{‘Sig_1’},{‘Sig_2’},{‘Sig_3’},{‘Sig_4’},{‘Sig_5’}];

>> LegendMatrix( LegTxt, [1.1 3.7 6.8 0.6] , 8 , 3 );

If you correctly formatted the cell array of strings, then you should get:LegendMatrix4

The length of the lines have been shortened considerably and the opportunity for reducing the whitespace is there. Also, if you do not have the position correct, just re-run LegendMatrix with changes to the 4×1 ‘Position’ command and it should overwrite the existing legend that we just made. For example, we may look at the figure above and see that the labels are actually short, so why not make this a 5 column legend and have all of the labels on a single line. If we do that (and play around with the position command a little), the figure can look like:

LegendMatrix5

With this LegendMatrix command, we can cut down on the wasted white space and make the legend look more professional. Now that we’ve fixed MATLAB’s legend command and made some enhancements along the way, we can start working on more advanced figure plotting, like multi-axis figures. Look for subsequent tutorials in the future. Once again, here’s the link to the LegendMatrix file in *.txt format.