5
votes

I am using this FEX entry to plot the horizontal shaded error bars for a variable plotted on the X-axis. This variable is plotted in different regions/zones and, therefore, there are 3 shaded error bars for 3 zones. I would like to combine the legends of the error bars (shaded region) as well as the mean (solid line) of any zone into a single legend represented by a solid line (or solid line inside a patch) of the same color as the zone.

THE WAY MY CODE WORKS FOR PLOTTING: A synthetic example of the way I am plotting is shown below

fh = figure();
axesh = axes('Parent', fh);
nZones = 4;
nPts = 10;
X = nan*ones(nPts, nZones);
Y = nan*ones(nPts, nZones);
XError = nan*ones(10, 4);
clr = {'r', 'b', 'g', 'm', 'y', 'c'};
for iZone = 1:nZones
    X(:, iZone) = randi(10, nPts, 1);
    Y(:, iZone) = randi(10, nPts, 1);
    XError(:, iZone) = rand(nPts, 1);
    % Append Legend Entries/Tags
    if iZone == 1
        TagAx = {['Zone # ', num2str(iZone)]};
    else
        TagAx = [TagAx, {['Zone # ', num2str(iZone)]}];
    end
    hold(axesh, 'on')
    [hLine, hPatch] = boundedline(X(:, iZone), Y(:, iZone), XError(:, iZone),...
        strcat('-', clr{iZone}), axesh, 'transparency', 0.15,...
        'orientation', 'horiz');
    legend(TagAx);
    xlabel(axesh, 'X', 'Fontweight', 'Bold');
    ylabel(axesh, 'Y', 'Fontweight', 'Bold');
    title(axesh, 'Error bars in X', 'Fontweight', 'Bold');
end

THE WAY LEGENDS ARE SHOWING-UP CURRENTLY:

enter image description here

WHAT I HAVE TRIED: As someone suggested in the comment section of that file's FEX page to add the following code after line 314 in boundedline code.

set(get(get(hp(iln),'Annotation'),'LegendInformation'),'IconDisplayStyle','off');

However, on doing that I get this error:

The name 'Annotation' is not an accessible property for an instance of class 'root'.

EDIT: The first two answers suggested accessing the legend handles of the patch and line which are returned as output the by the function boundedline. I tried that but the problem is still not solved as the legend entries are still not consistent with the zones.

3

3 Answers

4
votes

There is not an easy way to do this, You have to go into the legend and shift things up:

The following makes a sample plot where the mean lines are not superimposed on the bound patches.

N = 10;
x = 1:N;
y = sin(x);
b = ones([N,2,1]);

[hl, hp] = boundedline(x, y, b);
lh = legend('hi','');

We get a structure g associated with the legend handle lh. If you look at the types of the children, you'll see that g2 is the mean line and g4 is the patch. So I got the vertices of the patch and used it to shift the mean line up.

g = get(lh);
g2 = get(g.Children(2));
g4 = get(g.Children(4));

v = g4.Vertices;
vx = unique(v(:,1));
vy = diff(unique(v(:,2)))/2;
vy = [vy vy] + min(v(:,2));

set(g.Children(2), 'XData', vx);
set(g.Children(2), 'YData', vy);

enter image description here

Not a simple answer, and definitely requires you do a crazy amount of formatting for a plot with more than one mean line/patch pair, especially since it will leave gaps in your legend where the last zone's mean line was.

As per your comment, if you just want the shaded error bars to mark the legend then it's pretty easy:

x = 0:0.1:10;
N = length(x);
y1 = sin(x);
y2 = cos(x);
y3 = cos(2*x);

b1 = ones([N,2,1]);
b2 = 0.5*ones([N,2,1]);
b3 = 0.1*ones([N,2,1]);

hold on
[hl, hp] = boundedline(x, y1, b1, 'r',  x, y2, b2, 'b', x, y3, b3,'c')

lh = legend('one','two','three');

enter image description here

4
votes

There is a direct general way to control legends. You can simply choose not to display line entries by fetching their 'Annotation' property and setting the 'IconDisplayStyle' to 'off'.

Also, in order to use chadsgilbert's solution, you need to collect each single patch handle from every loop iteration. I refactored your code a bit, such that it would work with both solutions.

% Refactoring you example (can be fully vectorized)
figure
axesh  = axes('next','add'); % equivalent to hold on
nZones = 4;
nPts   = 10;
clr    = {'r', 'b', 'g', 'm', 'y', 'c'};
X      = randi(10, nPts, nZones);
Y      = randi(10, nPts, nZones);
XError = rand(nPts, nZones);

% Preallocate handles 
hl = zeros(nZones,1);
hp = zeros(nZones,1);
% LOOP
for ii = 1:nZones
    [hl(ii), hp(ii)] = boundedline(X(:, ii), Y(:, ii), XError(:, ii),...
        ['-', clr{ii}], 'transparency', 0.15, 'orientation', 'horiz');
end

The simplest method as shown by chadsgilbert:

% Create legend entries as a nZones x 8 char array of the format 'Zone #01',...
TagAx  = reshape(sprintf('Zone #%02d',1:nZones),8,nZones)'
legend(hp,TagAx)

... or for general more complex manipulations, set the legend display properties of the graphic objects:

% Do not display lines in the legend
hAnn   = cell2mat(get(hl,'Annotation'));
hLegEn = cell2mat(get(hAnn,'LegendInformation'));
set(hLegEn,'IconDisplayStyle','off')

% Add legend
legend(TagAx);
1
votes

That's a nice FEX entry. You can associate a specific set of handles with a legend. And luckily, boundedline already returns the handles to the lines separately from the handles to the patches.

>> [hl,hp] = boundedline([1 2 3],[4 5 6], [1 2 1],'b',[1 2 3],-[4 5 6],[2 1 1],'r');
>> %legend(hl,{'blue line' 'red line'}); % If you want narrow lines in the legend
>> legend(hp,{'blue patch' 'red patch'});  % If you want patches in the legend.