Why does legend('Position') overwrite strings?

6 views (last 30 days)
When I try to reset the position of a legend with a legend('Position',...) command, the legend string changes to "Position". In contrast, legend('Location',...) works as I expect (but does not give the same level of control). Here is a minimal working example:
x=0:0.02:1;
y=x.^2;
z=x.^3;
f=figure;
hold on;
plot(x,y,'displayname','x^2');
plot(x,z,'displayname','x^3');
legend('show')
legend('Location','Best'); % This works as expected.
% But if I use the next line instead of setting Location,
% I just get the word 'Position' in the legend
% (and the legend seems to be located at 0,0).
% legend('Position',[0.5 0.5 0.2 0.2]);
It appears that a work-around is to use:
hl=legend('show');
legend(hl.String,'Position',[0.5 0.5 0.2 0.2]);
but I am curious why the simpler version fails. What am I doing wrong with legend('Position',...) ?
Thanks.

Accepted Answer

dpb
dpb on 23 Sep 2017
Edited: dpb on 23 Sep 2017
"What am I doing wrong with legend('Position',...)?"
Assuming that the text string 'Position' is going to be interpreted as a the named property when it is in the location reserved for the label strings. Per the documentation, the syntax is one of (excluding objects, handles, etc., etc.)
...
legend(label1,...,labelN)
legend(labels)
...
legend(___,Name,Value)
...
I'll grant it is a little hard to necessarily infer what that last one means, but the TMW convention is that the underscore placeholder means you must have the preceding position-specific argument(s) enumerated in the list of alternate forms prior to that entry; your form doesn't include it.
Hence the reason 'Position' shows up as the label, it is the label and the remaining data are "eaten". It's a weakness in the error-checking on input it doesn't warn or error on that in my view; probably worth an enhancement request to improve the error checking. ADDENDUM/ERRATUM On further testing here with HG1, even though 'Position' in the label location isn't read, the parsing logic still finds the 4-vector if present and does use it.
I don't have HG2 so can't test it here; it looks like if the 'Location' case works as you say it does then the function tests specifically for it (and since it's documented, probably 'Orientation' as well) but not the other properties specifically. If it is still an m-file that would be easy enough to check in the source code.
If you have an existing legend or to set properties other than on creation, return the lgd object handle and set the properties or use the dot notation.
  3 Comments
dpb
dpb on 23 Sep 2017
Edited: dpb on 23 Sep 2017
I agree it is inconsistent, but so what else is new in Matlab? :)
It "just growed" and all sorts of these quirky things are extant that were introduced at some point in time and once there then maintaining compatibility becomes an issue, too.
As say, if it's still an m-file you can look at the code and see how the inputs are parsed. I did just look at HG1 implementation and only the two strings 'Location' and 'Orientation' are explicitly searched for in the beginning parsing. 'Position' isn't looked for specifically altho the 4-vector is found and assumed to be for position either with/without the explicit string. But, as you discovered in HG2, whatever text is int the leading label parameter location will be used for it with the exception only of the two specific option strings 'Location' and 'Orientation'. In your use case the 'Position' text is in that location and thus becomes the label.
But, contrarily, but apparently same logic as in HG2, if use 'Location' but no string one gets a default label of 'Data1' and the location parameter is used correctly with the named property. The difference is that, as stated above, it is one of the two explicitly searched for and so its name/value pair are processed correctly even in the absence of a label string.
I'd say it's worth an enhancement request to TMW to clean up the input processing and error/warning logic.
dpb
dpb on 23 Sep 2017
The input parsing of interest in HG1 looks like--
...
while n <= nargs
if ischar(argin{n})
if strcmpi(argin{n},'orientation')
...
% found 'Orientation',ORIENT
...
elseif strcmpi(argin{n},'location')
...
% found 'Location',POS
...
elseif foundAllStrings && (n < nargs)
..
else
% found a string for legend entry
...
end
elseif isnumeric(argin{n}) && length(argin{n})==1 && ...
...
elseif isnumeric(argin{n}) && length(argin{n})==4 && ...
(n > 1 || ~all(ishandle(argin{n})))
% to use position vector either it must not be the first argument,
% or if it is, then the values must not all be handles
...
The comments go on to note the last is an undocumented API for backwards compatibility with some of the old basic fitting stuff.
But, the key in the given behavior is that the two specifically-documented named properties are searched for first and the others are only found by later processing so they do get special treatment.
As this is pretty complex code, not too surprising the basic logic was just picked up and not totally rewritten for HG2; particularly owing to the internal need for the backward compatibility. Illustrates the difficulties and "what tangled webs we weave" when trying to both progress with enhancements yet maintain compatibility with what was first done 30 yr ago, perhaps.

Sign in to comment.

More Answers (0)

Community Treasure Hunt

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

Start Hunting!