Azzera filtri
Azzera filtri

how to place a legend in best corner?

55 visualizzazioni (ultimi 30 giorni)
meghannmarie
meghannmarie il 6 Mar 2019
Commentato: dpb il 8 Mar 2019
Is there a way to place legend in the best corner of subplot. I do not like it when the legend is in middle of whitespace.

Risposta accettata

dpb
dpb il 6 Mar 2019
Modificato: dpb il 7 Mar 2019
Give this a try as a first cut...written for a one-line plot as is and takes the legend, axis handles.
Generalized, it could handle simply a figure handle and find the axes and legend from it and retrieve all the X,YData.
But, illustrates the logic idea of above; seemed to work here for a test case where drew the legend on a line and then moved the legend to not clash, but that's the extent of testing.
function flag=legendclash(hAx,hLg,x,y)
% Returns T if data in line x,y cross legend box in axes hAx
AxPosn=hAx.Position; % get axis position vector
Axbot=AxPosn(2); Axtop=Axbot+AxPosn(4); % compute bounding box
Axlft=AxPosn(1); Axrgt=Axlft+AxPosn(3); % dimensions for axis
LgPosn=hLg.Position; % and same for the legend
LGbot=LgPosn(2); LGtop=LGbot+LgPosn(4);
LGlft=LgPosn(1); LGrgt=LGlft+LgPosn(3);
if strfind(hAx.YDir,'normal')
yscaled=interp1(hAx.YLim,[Axbot Axtop],y); % compute scaled plotted values
else
yscaled=interp1(hAx.YLim,[Axtop Axbot],y); % compute scaled plotted values
end
if strfind(hAx.XDir,'normal')
xscaled=interp1(hAx.xlim,[Axlft Axrgt],x); % in the axis bounding box
else
xscaled=interp1(hAx.xlim,[Axrgt Axlft],x); % in the axis bounding box
end
% test if data intersects inside the legend bounding box
flag=any(iswithin(yscaled(:),LGbot,LGtop) & iswithin(xscaled(:),LGlft,LGrgt));
end
iswithin is my oft-used utlity function that is just "syntax sugar" but makes for simpler high-level code...
function flg=iswithin(x,lo,hi)
% returns T for values within range of input
% SYNTAX:
% [log] = iswithin(x,lo,hi)
% returns T for x between lo and hi values, inclusive
flg= (x>=lo) & (x<=hi);
NOTA BENE: ERRATUM
Corrected swapped indices in axis position calculation and inserted missing function keyword
ADDENDUM
Added logic to handle reversed axes
  12 Commenti
meghannmarie
meghannmarie il 7 Mar 2019
Just updated my code and ready to run it on big dataset!
lgd = gobjects(4,1);
for sp = 1:length(subplots)
gdem = plot(means{sp},gdem_depths,'-r','Parent',subplots(sp),'LineWidth',2);
lgd(sp) = legend([obs(sp) gdem std],'MOODS','GDEM','95% of time');
count = 1; loc = {'northeast', 'southeast','northwest','southwest','best'};
flag = true;
while flag && count <= numel(loc)
set(lgd(sp),'Location', loc{count});
flag = legendclash(subplots(sp),lgd(sp));
count = count + 1;
end %while flag && count <= numel(loc)
end %for sp = 1:length(subplots)
dpb
dpb il 8 Mar 2019
Let us know how it goes...

Accedi per commentare.

Più risposte (1)

dpb
dpb il 6 Mar 2019
Try
legend(___,'Location','best')
ML tries to avoid the most data it can if it can find a location that the legend doesn't occlude anything.
It generally is "ok", but may not be what one might choose visually; the logic certainly isn't perfect.
Other than that, it's "pick a spot and go!" in that all other named choices are fixed locations relative to the axes or you set the actual position yourself programmatically (and then the "best" logic reverts to your own devices so you can test your skills against those of TMW :) )..
  2 Commenti
meghannmarie
meghannmarie il 6 Mar 2019
Modificato: meghannmarie il 6 Mar 2019
I am using best right now, but I want the legend in the corner.
What I need to do is place the legend in a fixed corner and check for conficts with data. If there is a conflict, then move to next corner. If conflicts with all four corners, then use 'best'.
I cannot figure out how to check for the conficts with the plotted lines and the legend in the subplots.
I am about to plot 1000+ graphics, so it would be nice to get this to work. Picking a spot manually is out of the question.
dpb
dpb il 6 Mar 2019
I know of no builtin functionality, sorry.
Best I can think of is that you can retrieve the position of the legend where you choose to put it. Those are, by default, normalized dimensions within the axes object.
Then you would have to scale the plotted data to the same set of normalized position values of the axis and check if there is an intersection of values of the x,y normalized data within that area of the legend.
I have meetings in town here shortly so don't have time to try to see if can think of just what such code would look like at the moment, but I think that's the general idea.
I don't know if you can see the internals of the legend function any more or not; seems like TMW has made it almost totally opaque now so you may not be able to go look at what code for collision avoidance is inside it any longer.
Probably at least worth of a look to see if you can find anything on the FEX that somebody else may have already done...

Accedi per commentare.

Categorie

Scopri di più su Graphics Object Programming in Help Center e File Exchange

Prodotti


Release

R2017b

Community Treasure Hunt

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

Start Hunting!

Translated by