File Exchange

image thumbnail

legendflex.m: a more flexible, customizable legend

version 1.9 (561 KB) by

Create a legend with more flexible positioning and labeling capabilities

4.78788
45 Ratings

134 Downloads

Updated

Editor's Note: This file was selected as MATLAB Central Pick of the Week

This function offers a more flexible version of the legend command. It uses a different method of positioning the legend (placing it relative to any axis, object, or figure), as well as options to:
 
- organize legend text and symbols in a grid with a specified number of rows and/or columns
- rescale the horizontal space used by each legend symbol
- create multiple legends for the same axis
- add a title to the legend within the legend box

Comments and Ratings (96)

Matilda

@Kelly
Thanks Kelly, I look forward to it !

Kelly Kearney

Kelly Kearney (view profile)

@Neil,

Can you please post some code to reproduce this problem? What type of graphics objects are you labeling? What version of Matlab, and what operating system are you working on? I would definitely like to fix this, but have been unable to reproduce the issue myself (Matlab R2016b, Mac OS X). Here's the example I used, and both the pdf and png images from all 3 export methods (print, saveas, and export_fig) include properly-rendered legends:

n = 22;
data = rand(100,n);
lbl = cellstr(num2str((1:n)'));

h = plot(data);
legendflex(h, lbl, 'ncol', 4);

saveas(gcf, 'lftest1.png');
saveas(gcf, 'lftest1.pdf');
print('lftest2', '-dpng');
print('lftest2', '-dpdf');
export_fig(gcf, 'lftest3', '-png', '-pdf');

If you have an example that demonstrates the problem, I'd really appreciate if you could open an issue on the GitHub page (https://github.com/kakearney/legendflex-pkg/issues).

Neil Tandon

columnlegend worked better for my needs.

Neil Tandon

I am generating a legend with 4 columns and 22 total items. The legend displays fine in MATLAB's figure window, but when I export the figure to pdf (or any other format I've tried), the text is all jumbled in one corner of the figure. It doesn't matter if I use export_fig or print. Others have noted similar problems, but I have not seen solutions provided. I can export other figures containing MATLAB's built-in legend without any problems.

Kelly Kearney

Kelly Kearney (view profile)

@Matilda,

Thanks for pointing out this shortfall of legendflex. The function does not currently support multi-line entries, and adding that capability will require some reworking of the code's underlying logic. I've added this as an open bug issue on the GitHub repository, and will try to add a fix when I get some time.

Matilda

Thank you for this great function !
I have been trying to use this function to create a legend where one of the legend entry is on two lines.
1) Using the default legend function of matlab, I would write :
legend('a','b',{'c1','c2'}) or legend('a','b',['c1',char(10),'c2'])
2) I tried using these in legendflex and it returns an error. I have tried to debug the error and I got to display a two lines legend entry but the legend entries and legend symbols are no longer aligned correctly.

Did anyone tried to plot a multi-line legend entry using legendflex ? I have more details on the errors if needed.

Thanks a lot !

Alan Green

Alber Filba

Great function! Finally I can make a legend as small as I want!

Excellent - spent hours manipulating a legend for a scatter plot only to find that all the changes were lost when I saved the figure as a png file. Within minutes of downloading legendflex I had created the legend exactly as I wanted it and it looks the same int he saved png file. Absolutely excellent. Thank you.

DomJung

Neil Dalchau

Can't believe I hadn't rated this function yet. It's fantastic. And Kelly is great at fixing bugs and pushing new versions. You can rely on this one...

Neil Dalchau

Heiko

Heiko (view profile)

Hi Kelly, I found a hiding issue by uiPanels when applying the function within uiPanels for recent Matlab Releases. As a solution I added a 'parent' input Parameter, i.e. hnew.leg = axes(..., 'parent', Opt.parent), whereras Opt.parent = figh in case no 'parent' is provided.

@Kelly,
Thanks for the quick reply, I have made some personal modifications to get the function to work, however they are just a crude band-aid and not a real solution to the problem.
I look forward to seeing the update.

Kelly Kearney

Kelly Kearney (view profile)

@Benjamin,

Thank you for pointing out this issue. I've traced the issue to a change in the way legend.m parses input and output arguments in the R2016a release. I've added this to the known issues for legendflex.m (https://github.com/kakearney/legendflex-pkg/issues/6), and will work on updating the function as soon as possible.

Great Tool, I've been using it for a while to overcome the inadequacies of the built in Legend function. However i have noticed that in r2016a, that the font input is no longer being respected. None of the text properties are being implemented. eg Font, Font Size Interpreter,Not sure if I'm doing something wrong because the same code works correctly in r2014b.

OlivierMegel

Maryam GH

Roman

Roman (view profile)

Applying this very useful function in 2016a - contrary to earlier ML-versions - results in:
Warning: NARGCHK will be removed in a future release. Use NARGINCHK or NARGOUTCHK instead. Warning: NARGCHK will be removed in a future release. Use NARGINCHK or NARGOUTCHK instead.

Plugging NARGINCHK in getpos.m function brings error messages too many output ...

Kesh Ikuma

Kesh Ikuma (view profile)

Oops, never mind. I somehow downloaded an older version of the file...

Kesh Ikuma

Kesh Ikuma (view profile)

Nice work. But there is a bug on the first line (l.210): ID'ing HG2 handles. The handle argument is checked with isnumeric call which is no longer appropriate. Suggest simply using

all(ishghandle(varargin{1}))

Very usefull!!!
How I could make it visilbe off and then on again?

I have suggested a possible solution/work-around at https://github.com/kakearney/legendflex-pkg/issues/5
If VerticalAlignment is set to 'middle', it is relatively robust towards changes of FontSize...

Kelly Kearney

Kelly Kearney (view profile)

@Soren,

See https://github.com/kakearney/legendflex-pkg/issues/5

Latex/Tex is an ongoing problem with this function, but unfortunately one that doesn't have an easy, all-situations fix. Some workarounds are suggested in the above link.

Very nice function!
However, I would like to use latex (and not tex) as textinterpreter. E.g. by set(groot,'defaultTextInterpreter','latex'). This is possible with the function as it is, but it results in vertical misaligment between the plot line and the tag text, as I see it. Can you help me?

Jakob

Jakob (view profile)

Ok, I found why it was not working for me, it was my fault (but not a very obvious one): I used stem with the option 'filled' plus additional name-value-pairs (color etc.). This works at first and also when adding a legend if I dont request the objects as output. But the documentation on 'stem' shows that you should not use 'filled' plus more options. Doing it seems to mess up subtle things internally...

Jakob

Jakob (view profile)

Great function. I am currently experiencing an error though, but I think it might be a problem of the matlab legend function rather than of legendflex. Maybe you can still give me a hint on how to solve it:

The problem appears with a plot that has three data sequences plottet using stem. The legendflex codeline that calls the matlab legend function produces a

Warning: Error updating Legend. Following is the chain of causes of the error:
 Property color not found in class matlab.graphics.primitive.world.Marker, or is not present in all elements of the array of class
 matlab.graphics.primitive.world.Marker.
 
> In defaulterrorcallback at 12
  In C:\Program Files\MATLAB\R2014b\toolbox\matlab\scribe\private\legendHGUsingMATLABClasses.p>find_legend_info at 382
  In C:\Program Files\MATLAB\R2014b\toolbox\matlab\scribe\private\legendHGUsingMATLABClasses.p>legendHGUsingMATLABClasses at 251
  In legend at 118
  In legendflex at 307

..., the legend-entries are not displayed and the returned h.obj is an empty double array. This creates an error later, when your code tries to get the Extent of the objects in h.obj (textExtent).
If I just use legend (not legendflex), I get the same warning if I request the legend-objects as outputs. If I dont, there is no warning and the legend-entries get displayed normally.

Do you have an idea? Thanks!

Elizabeth Jones

Great function. I haven't used all the capabilities, but I got a quick legend in three rows without any errors.

Kelly Kearney

Kelly Kearney (view profile)

The resize function bug has now been fixed. To clarify, the feval messiness is just a wrapper function to call all the callback functions in succession (though I messed up the order of inputs in my earlier implementation, hence the error).

Kelly Kearney

Kelly Kearney (view profile)

@Joerg,

Thanks for the feedback. I'll look into the resize function bug as soon as possible. Trying to rewrite this function so it's compatible with both old and new handle graphics has been more difficult than I expected, and obviously I haven't worked out all the bugs yet.

I've also added your enhancement requests to my Issues list on the GitHub page, and will hopefully be able to add both options sometime soon.

Joerg Huschke

I really appreciate this function. However, I have several issues with it, tried with R2011b:

1) I don't see it correctly handles figures already having a resize function defined. For me the function produces error:
"
Error using horzcat
The following error occurred converting from cell to function_handle:
Error using function_handle
Too many output arguments.

Error in legendflexNew (line 640)
        set(figh, 'ResizeFcn', cellfun(@(x)feval(x,h,e), [rsz @updatelegfigresize]));
"

Which is quite obvious, as rsz is forced to be a cell just a few lines above this code line. When I fix this to
        set(figh, 'ResizeFcn', cellfun(@(x)feval(x,h,e), [rsz {@updatelegfigresize}]));

I get the next error for this line, because the 'e' in the feval is undefined. I have no idea what the intention of this feval is Why should the resizefcn be set to the result of the evaluation of @updatelegfigresize or of the function stored in rsz, rather than the function itself. Totally confused… This problem did not exist in version of 2011 I had been using so far.

2) The function should support the 'location' input like mathworks original legend does. Basically translate it to the Anchor/Buffer pair as provided in the function help text. I've already done this in my private extension of the function.

3 When using the 'outside' variants of the legend location then the reference axis to which the legend shall be associated is not reduced to make room for the outside-legend, unlike for the mathworks original legend. So currently the outside positions are not really usable unless I shrink the axis myself before.

I'm really looking forward to a legend function where I don't have to make compromises, like now I have to go either for the mathworks-provided one with its extremely limited functionality or with the older legendflex (as the current version does not work for me at all, see issue 1 above) but then cannot use outside locations. Would it be possible to fix this?

Eric

Eric (view profile)

@Kelly

Thanks, that was definitely the issue. Forgot to add that new folder to my path. Great submission BTW.

Kelly Kearney

Kelly Kearney (view profile)

@Eric

Looks like you're using an old version of getpos. Check that you're using the version distributed with this package, and not a version downloaded separately (the original one, FEX entry 13927, hasn't been updated for HG2 graphics; my version has).

Eric

Eric (view profile)

Crashes at legpospx = getpos(h.leg, 'px'); (line 415) with

ll = plot(0:1:3,magic(4));
legendflex(ll,{'1','2','3','4'})

in Matlab 2015b with error message:

Undefined operator '~' for input arguments of type 'matlab.ui.Figure'.
Error in getpos (line 160)
        if ~href % HREF is the Root object (no 'Position' property)
Error in legendflex (line 415)
legpospx = getpos(h.leg, 'px');

Ken Campbell

Kelly, thanks for posting this excellent contribution. I had my own crude code for working with legends but this is much better. Your code is flexible, has lots of useful options, and is supported by good documentation.

Kelly Kearney

Kelly Kearney (view profile)

@Jonathan,

It's difficult to diagnose what's going wrong there without knowing what type of objects you're trying to label. I recently updated the code to better work with the new handle graphics. If you still see this error, can you email a full example that reproduces it?

Jonathan

Hi, Fantastic script. I have had no issues running it on R2014a. However, I upgraded to R2015a, and have since encountered a problem when I run:

lbl = {previous, current, 'raw data (limited QC)',seasonal,'+/-1std','+2std'};
[hgGroup1,now,c,mm,hgGroup2,ext]= legendflex(lbl, 'ref', gcf,...
    'anchor', {'nw','nw'}, ...
    'buffer',[140 -76], ...
    'nrow',3, ...
    'ncol',3, ...
    'fontsize',10);

The returned error is:
Undefined function or variable "idx".

Error in legendflex (line 545)
        ynorm = (xy{2}- (1-idx*rowHeightNm))./rowHeightNm;

Not clear to me what I'm doing wrong.

Thanks

Kelly Kearney

Kelly Kearney (view profile)

@Igstam,

You're right, I wrote the contour workaround pretty hastily and didn't test it for the no-handle input option. Corrected version on its way!

lgstarn

Hi, I love your script. pre-R2014b it worked great but of course old version broke with the update. So I downloaded the new version, but I see what appears to be a bug in your code. On the line:

iscont = strcmp(get(legin{1}, 'type'), 'contour');

I get the error:
Error using get
Conversion to double from cell is not possible.

Error in legendflex (line 293)
iscont = strcmp(get(legin{1}, 'type'), 'contour');

That's because I'm calling it as so:

strcell = {'U','V'};

buffer = [20 0];
legendflex(strcell,'FontSize',12,'anchor', [4 4], 'buffer',buffer);

so legin{1} is a string cell, and the get call does not work. The workaround I put in place was to surround the whole thing with a check if legin{1} is a string cell:

% Create a temporary legend to get all the objects
if (~all(cellfun(@ischar, legin{1})))
    iscont = strcmp(get(legin{1}, 'type'), 'contour');
    if any(iscont)
        % Not sure why, but 2014b needs contour labels to be listed last in a
        % legend, or everything goes haywire

        [srt, isrt] = sort(iscont);
        legin{1} = legin{1}(isrt);
        legin{2} = legin{2}(isrt);
    end
end

It's kind of messy to have legin{1} mean different things with the way you call it with the M or h syntax. I recommend refactoring legin{1} to have consistent meaning after you get done parsing the args.

Kelly Kearney

Kelly Kearney (view profile)

@Arnold,

The box around the legend content should expand as necessary to fit all content, including the title. If you have an example where this doesn't happen, could you email me?

arnold

arnold (view profile)

it would be great if the box around the legend would scale with a given title.

Secondly, it would also be handy to be able to easily append something to all entries, like a unit [m] or something.

Kelly Kearney

Kelly Kearney (view profile)

@Leo,

The disappearing legends you're seeing are due to the subplot command (which deletes any existing axes overlapping its intended space). See https://github.com/kakearney/legendflex-pkg/issues/1 for a full explanation.

Leo Simon

It's a great program, and does exactly what I need it to do. There appears to be a small problem, which can easily be avoided but it would be great if you could fix it. When using subplots, if you use the "anchor" flag *and* have a legend label that is long enough to extend outside of the plot area, then when you open a new subplot, the old legend disappears. The following example illustrates the problem. As noted, it only arises when the anchor and the longLegend flags are turned on. Obviously, one can get around this problem by increasing the 'Position' dimensions of the subplot

close all;
legFontSize = 5;
legXScale = 0.5;
longLegend = 1;
anchorOn = 1;
for ii=1:15;
    subplot(3,5,ii);
    hold on;
    h{ii}(1) = plot(1:10);
    h{ii}(2) = plot(2:11);
    if longLegend;
        currentLegend = {'This is a very very very long legend','nothing'};
    else;
        currentLegend = {'This','nothing'};
    end;
    if anchorOn;
        legendflex(h{ii},currentLegend,'anchor',{'nw','nw'},'fontSize',legFontSize,'xScale',legXScale);
    else;
        legendflex(h{ii},currentLegend,'fontSize',legFontSize,'xScale',legXScale);
    end;
    if ii==2; keyboard;end;
end;

Leo Simon

Warwick

Very useful and much appreciated.

Kelly Kearney

Kelly Kearney (view profile)

@Warwick:
The code to reproduce the sample image is now available in the example on the GitHub page (follow the kakearney/legendflex-pkg link, and scroll to the bottom of the page).

@Martin: The getpos.m function is included in the download; make sure you add the setgetpos folder to your path.

Martin

Martin (view profile)

I'm using Matlab R2013a. If I use the example code I get the following error:

figure;
b = bar(rand(10,5),'stacked'); colormap(summer); hold on
x = plot(1:10,5*rand(10,1),'marker','square','markersize',12,...
'markeredgecolor','y','markerfacecolor',[.6 0 .6],...
'linestyle','-','color','r','linewidth',2); hold off
lbl = {'Carrots','Peas','Peppers','Green Beans','Cucumbers','Eggplant'};
% Rather than covering up data or resizing the axis, let's squeeze the
% legend into the margin at the top of the figure;
legendflex([b,x], lbl, 'ref', gcf, ...
                    'anchor', {'n','n'}, ...
                    'buffer',[0 0], ...
                    'nrow',2, ...
                    'fontsize',8);

I get this error:

Undefined function 'getpos' for input arguments of type 'double'.

Error in legendflex_pre2014b (line 351)
legpospx = getpos(h.leg, 'px');

Error in legendflex (line 209)
    [tmp{:}] = legendflex_pre2014b(varargin{:});

Error in rfefe (line 11)
legendflex([b,x], lbl, 'ref', gcf, ...

What is the problem? Anybody knows?

thx

Warwick

Could you include example code for your quiver plot at Figure C? Thanks.

Kelly Kearney

Kelly Kearney (view profile)

@Chris,

Yes, I think that was due to a residual R2014b bug. I've done a bit of cleanup over the last few weeks, and your example works fine using my most recent version, which I just uploaded. Email me if you still have problems with this latest version.

Chris Garner

This works in 2013b:

clear all

xmin=1;
xmax=10;
r = 18;
c=5;
x=xmin+rand(r,c)*(xmax-xmin);

b = bar(x,'stacked');

lbl = {'1','2','3','4','5'};

legendflex(b, lbl, 'ref', gcf, ...
    'anchor', {'n','n'}, ...
    'buffer',[15 0], ...
    'nrow',1, ...
    'fontsize',10,'box','off',...
    'anchor', [6 6]);

but I get this error in 2014b:

Error using legendflex (line 228)
Legend labels must be a cell array of
strings

Error in test (line 13)
legendflex(b, lbl, 'ref', gcf, ...

Is this due to change in ML graphics?

Can your code help me enlarge legend marker sizes? I have a scatter plot with tiny dots but I want the legend to show the dots larger so one can see the colors better.
thanks!

Jose

Jose (view profile)

Just in case somebody else would like to use the latex interpreter and an horizontal legend. A quick fix is to either add $^{~}$ or $_{~}$ if one wants to adjust the top or bottom height of the word, respectively. This worked for me.

Jose

Jose (view profile)

@ Kelly

thanks for the quick reply. I guess I will have to find some quick fix for this :)

Kelly Kearney

Kelly Kearney (view profile)

@Jose,

Legendflex does not always play nicely with the Latex interpreter, because the Latex interpreter interacts unpredictably with the Extent property of text objects (which I rely on to reposition everything). I'm uploading a small text document to this function to explain this. The document offers a few workarounds if text is running into each other or overflowing the legend box, but I'm not sure I have a good suggestion for misaligned baselines, as in your case. My crystal ball suggests things will be a bit better in the 2014b release, but still not perfect.

Jose

Jose (view profile)

great work! I believe I found a bug with the current program.

When trying to use it with the latex interpreter and having an horizontal legend, apparently, all word items in the legend must have the same height, otherwise the legend becomes not properly aligned.

Here is an example code:

close all
figure,
p1 = plot(1,1,'b-','LineWidth',linewidthl)
hold on
p2 = plot(1,1,'r--','LineWidth',linewidthl)
hold on
p3 = plot(1,1,'g--','LineWidth',1.5)

legendflex([p1,p2,p3], {'P','Ap','R'}, 'ref', gcf,...
          'anchor', {'n','n'},...
          'buffer', [0 0],...
          'box','off','nrow',1,...
          'Interpreter','latex','FontSize',fontleg)

Since Ap does not have the same heigth as P and R, Ap becomes unaligned. However, if Ap is switched to Ac, then all is OK. Anybody has experienced this problem before?

arnold

arnold (view profile)

works great but it would still be good to be able to move it around like the normal legend since you never know which position might obscure your data. I'd really like that option

How can I add extra white space after the legend text? The bounding box is to close to the text on the right hand side of the box.

Adam Danz

Adam Danz (view profile)

Great! Definitely flexible. Thanks!

Kelly Kearney

Kelly Kearney (view profile)

@Werner,

You're right, this function relies on the current convention of graphics handles being numeric... I'll update it if and when HG2 is officially released (and perhaps by then legends will be flexible enough that this function won't be needed anymore).

Werner

Werner (view profile)

Unfortunately this doesn't work for HG2 x(. But great submission!

Brett

Brett (view profile)

This is what I was looking for, but I had to fix it to work well for axes embedded in complicated layouts involving uipanels, uitabs, etc. I use the excellent GUI Layout Toolbox from here on the file exchange to have lots of resizeable panels for example.

The problem is that this function assumes the legend should be placed relative to the figure. Unfortunately if a nested uipanel is used as a reference, the getpos() function does not correctly return the figure coordinates relative to the anchor. To get the global figure coordinates for a nested object, the GUI's hierarchy must be traversed, and the left and bottom coordinates summed for each parent up to the figure level. Each control's position is relative to it's parent, not to the figure.

Fortunately there's a simpler solution. I made the legend a child of the 'ref' object and fixed anywhere that temporary axes were made to the figure to be to the ref object instead. I also added a check that if this parent object is an axes, to get it's parent since the legend axes can't be a child of another axes. Finally, in the case of a plot in a densely nested GUI, the resize function should be on the parent component, not on the entire figure. This way when I drag the boundaries between uipanels to resize them, the legend and axes resize with them. I'm still experimenting with it, but this seems to solve my problems thus far.

I'll wait on permission to post my code since the author listed a copyright in the source.

Great addon to Matlab. Especially the shrinking function removing the unwanted space between legend marker and legend text. Thanks

James Ackman

Indeed, this function allows for flexible positioning of legend data without auto-resizing of your plot axes.

And as a bonus it is a great way to make a static legend-- as workaround for the often discussed problems of slow graphics performance when updating dynamic plot data with the base legend() command.

James Ackman

Kesh Ikuma

Kesh Ikuma (view profile)

Thanks for sharing a very useful function!

I have one issue though. I'm creating an EPS figure using the built-in PRINT function (in conjunction with my submitted EPS Toolbox), and the legendflex texts overlap while built-in legend gets the exact same texts properly resized. It appears to be that the print resolution is not reflected properly (96 desektop dpi to e.g., 300 print dpi) Any clue how to work around this? If you'd like to see what I described, please contact me via email. Thanks!

Henrik

Henrik (view profile)

Thank you very much Kelly, this fixed the problem

Kelly Kearney

Kelly Kearney (view profile)

@Henrik: Technically, legends are axes (both regular legends and those generated by legendflex). It looks like Convert4publication.m filters out legends based on their Tag property (on line 153). If you add the following line in the same location, it should also filter out legendflex axes:

h(arrayfun(@(a) isappdata(a, 'legflex'), h)) = [];

Henrik

Henrik (view profile)

Hi
This is exactly what I have been looking for to help make nice plots. However, I have a problem with this function in combination with convertplot4publication: http://www.mathworks.com/matlabcentral/fileexchange/34741-convertplot4publication
I'm hoping you could be able to help me with this?
For some reason it sees the legend as a subplot.. Here's an example of the problem:

y1 = randn(1000,1);
plot(y1)
legh={'leg1'};
legendflex(legh,'xscale',0.5)
ConvertPlot4Publication('testPlot')

Kelly Kearney

Kelly Kearney (view profile)

@Leo,

The warning you are receiving is one issued by the print function whenever it prints a figure with a ResizeFcn, and legendflex does add a resize function to figures. Setting your figure's 'PaperPositionMode' to 'auto' will avoid it (and save you the step of manually setting the page size). Or you could use a function like export_fig (FEX #23629), which in my opinion produces much better output without the quirks of print.

Leo Simon

Thanks very much for this program, it's very helpful. I'm having one problem, which may or may not be specific to legendflex. I'm using it to print my legend to a separate figure. I want to resize my legend to be the same size as a regular figure. I can do this on screen with

set(gcf,'Position',...);

To match on paper what I have on screen I would normally use

set(gcf,'PaperPosition',...);

But when I set 'PaperPosition' to the desired size, to preseve the larger sized legend, and then print, I get this message:

Warning: Positioning Figure for ResizeFcn. Set PaperPositionMode to 'auto' (match figure screen size) to avoid resizing and this warning.

I've never had this problem before, so wondering if it's something special about legendflex.

Any advice would be most appreciated!

Leo

Kelly Kearney

Kelly Kearney (view profile)

A workaround for the vertical positioning issues with subscripts/superscripts pointed out by William: render the legend with Interpreter set to 'none', then reset it to 'tex' afterwards. You may need to use a larger text size on the initial render as well, and reset to the correct size afterwards (e.g. if text hits box). It's not a perfect fix, and introduces extra horizontal space between columns, but I haven't found a better way to deal with the issues of produced by tex formatting in legends.

William

This is exactly what I was looking for, thank you! It worked perfectly for the first application (legends in subplots).

When I tried to use it the second time, I noticed a bug. When I used the legend with subscripts, e.g., 'e_pp', the marker positions are shifted downward. Is there anyway to correct this?

Thanks again, I appreciate your work!

Eric

Eric (view profile)

I am running into issues when using the PRINT command as follows:

print(fig,'-depsc2','-r600',fname.eps)

It creates the eps fine but the legend boxes are not in th eproper location. I can fix that be setting the figure PaperPositionMode to 'auto', but I like to have the flexibility of resizing before I print. Also, sometimes (like with a single row legends, the end of the text is clipped by the legend box.

Eric

Eric (view profile)

I am running into issues when using the PRINT command as follows:

print(fig,'-depsc2','-r600',fname.eps)

It creates the eps fine but the legend boxes are not in th eproper location. I can fix that be setting the figure PaperPositionMode to 'auto', but I like to have the flexibility of resizing before I print. Also, sometimes (like with a single row legends, the end of the text is clipped by the legend box.

alexancer

Clint Noack

Erik Johnson

Erik Johnson (view profile)

If legendflex is called like:

   [legend_h, object_h, plot_h, text_str] = legendflex(h, strs, 'ncol', ncols, ... );

then a workaround for the column offset is:

   % fix legendflex's subsequent column locations
   h = findobj(object_h, 'type', 'line');
   y = get(h, {'YData'});
   y = repmat(ans(1:ceil(length(y)/2/ncols)*2,:), ncols, 1);
   y = ans(1:length(h),:);
   set(h, {'YData'}, y);

Erik Johnson

Erik Johnson (view profile)

A VERY useful function. However, I get slight offsets in the vertical locations of the lines/markers in different columns. Have you seen this?

For example, the code:

   nrows = 1;
   ncols = 3;
   n = nrows * ncols;
   x = repmat([0;1], 1, n);
   y = [zeros(1,n); (1:n)/n];
   h = plot(x, y);
   strs = num2cell(char('`'+(1:n)), 1)';
   [legax,legh] = legendflex(h, strs, 'ncol', ncols);

   get(findobj(legh,'type','line','LineStyle','-'),{'YData'});
      cat(1,ans{:}); yline=reshape(ans(:,1),[],3)
   get(findobj(legh,'type','line','LineStyle','none'),{'YData'});
      cat(1,ans{:}); ymarker=reshape(ans(:,1),[],3)
   get(findobj(legh,'type','text'),{'Position'});
      cat(1,ans{:}); ytext=reshape(ans(:,2),[],3)

gives the results:

   yline = [9.5388 9.2710 9.0032]
   ymarker = [9.5388 9.2710 9.0032]
   ytext = [4.3091 4.3091 4.3091]

showing that the text items are set at consistent locations, but the lines and markers are slightly offset. (Change nrows to, say, 8 and the same vertical offset happens on all elements of subsequent columns.)

Any suggestions?

P.S. This is in MATLAB 7.14.0.739 (R2012a) on a Mac.

Chris

Chris (view profile)

Very nice - thanks!
I wonder if anyone knows how to go a step beyond. That is, my legends are often like tables which I currently hand make as figures within figures. The table-like legend has row and column headings with only the symbols/line types in the cells instead of a label for every symbol/line. This is because the symbol itself might change to represent one variable and whether it's filled or open might represent another so it's identity is a combination of the row and column headings in the table.

Dominic

thanks a lot for this very useful code!!!

Mukhtar Ullah

Thanks Kelly for your explanation. I am not sure what will I loose if I keep those lines commented out. At the moment, I am avoiding any figure resize when both legendflex and panel are at play.
 

Kelly Kearney

Kelly Kearney (view profile)

@Mukhtar Ullah

Regarding making legendflex play nicely with panel, I admit I never considered the possibility that a figure's resize function might be linked to an object method. I don't do object-oriented coding myself, so I have very little knowledge of the internal workings, scope, etc. of objects. So no promises to fix this, though I will look into it if I find some spare time.

Mukhtar Ullah

I just realized that it is possible to find the legendflex axes by
ax = getappdata(hfig, 'legflexchildren')
I now have another problem. When I use legendflex together with panel
http://www.mathworks.co.uk/matlabcentral/fileexchange/20003-panel
any attempt to resize the figure results in the following error
Error using panel.resizeCallback
Cannot access method 'resizeCallback' in class 'panel'.

Error in legendflex>resizefig (line 637)
                feval(Lf.oldrsz, hfig, ed);
 
Error using drawnow
Error while evaluating figure ResizeFcn

It turned out that commenting line 637 solves my problem.
Any idea of fixing this in a future release?

Thanks

Mukhtar Ullah

Thank you Kelly for clarifying that. It occurred to me because with legend, it is possible to set properties such as interpreter, string,..using set directly on the axes handle.
A minor addition to legendflex will make it easier to find its handle using findobj and findall function. For example, if you add a tag 'legendflex' to the axes, findobj(gcf, 'tag', 'legendflex') would locate the axes.

Mukhtar

Kelly Kearney

Kelly Kearney (view profile)

@Mukhtar Ullah,

I don't actually manually set the interpreter in this function, so it relies on the default. You can actually change this after the fact using set, as long as you return the object_h handles (see last syntax option); the first n indices of this array will correspond to the n text objects in the legend. This slightly obscure way of returning handles was chosen to be consistent with the original legend command.

Mukhtar Ullah

This function is the first serious fix to the long lasting problems Matlab has with legends.
The text interpreter is set none by this function and you could only change it when calling the function, and not later using SET. Is there a good reason for that?
Mukhtar

Ben

Ben (view profile)

Could you provide the example of getting the result as the screenshoot shows? I think the simple examples in the .m file is not fully demonstrate the capability of your function. Wish you could add more examples.

Philipp

Very useful indeed!

Kelly Kearney

Kelly Kearney (view profile)

Vertical alignment bug has been corrected, and I added some checks to make sure the figure resize doesn't crash when you remove a legend. Thanks to Jason for discovering these bugs.

Jason Kaeding

I guess it didn't take my first bug submission...

The vertical positioning of the patches is incorrect in the following code:

figure;
area(1:10,rand(10,10))
legendflex(gca,cellstr(num2str(column(1:10))),'ncol',5,'anchor',[6 2],'buffer',[0 -10])

Jason Kaeding

Another bug: if I delete the legend then resize my figure, I get an error...

??? Error using ==> getappdata
Invalid object handle

Error in ==> legendflex>resizefig at 621
    Lf = getappdata(ax(ia), 'legflex');
 
??? Error while evaluating figure ResizeFcn

Claudio

Well done! Very useful!

Dan

Dan (view profile)

Updates

1.9

Updated description (which was erased when the github link was added)

1.8

- Corrected some remaining R2014b bugs; all graphics handles should now be objects rather than numeric if running in R2014b or later.
- Switched source to github repository

1.7

Added note regarding legendflex with the Latex interpreter

1.6

Added parameter to modify padding between legend box and text/symbols

1.5

- updated for R2014b graphics

1.4

Now respects manual changes made by user (including legend height, width, and units) when resizing.

1.3

Fixed bug where very wide titles were not completely enclosed in legend box.

1.2

bug fixes: fixed error in function resize if you delete the legend, fixed minor vertical alignment issue

1.1

Figure resize now re-links legend and labeled objects with updated line/patch properties.

MATLAB Release
MATLAB 7.10 (R2010a)
Acknowledgements

Inspired by: SETPOS 1.2 - GETPOS 1.2

Download apps, toolboxes, and other File Exchange content using Add-On Explorer in MATLAB.

» Watch video