The following MVCE reproduces a problem that I am experiencing when displaying a legend in a figure that has several axes objects.
In order to plot an elevation profile of the terrain I use two axes:
ax(1)
for the sky in the backgroundax(2)
for the terrain in the foregroundDuring the plotting tasks I work with each set of axes independently and at the end of the process I link ax(1)
and ax(2)
using linkaxes(ax, 'xy')
so that they remain synchronized dimensionally.
Finally, I add a legend
in the 'southoutside'
location but only the foreground axes are automatically shrinked to allow space for the legend, the background axes stay unchanged, thus interfering with the intended layout, as shown in the following image:
The very purpose of using linkaxes
was to prevent this undesired behavior.
I would be grateful if someone could shed some light on how to solve this problem.
In response to gnovice comment:
The reason why I am using two axes is because I thought it was the only possible way to combine different colormaps in the same figure. Note that for the sky I use sky_map
and for the terrain I use the demcmap
function which sets a different colormap.
% Create figure.
figure;
% Create background axes for the sky.
ax(1) = axes;
% Sky data for background.
x_bg = [0, 0, 10, 10];
y_bg = [0, 10, 10, 0];
% Fill sky with color gradient on y-axis.
fill(x_bg, y_bg, y_bg);
% Generate custom sky colormap.
sky_map = interp1([0, 1], [135, 206, 235; 255, 255, 255]./255, linspace(0, 1, 255));
% Apply sky colormap.
colormap(ax(1), sky_map);
% Create foreground axes for the terrain.
ax(2) = axes;
% Terrain data.
x = [0, 0:10, 10];
y = [0, 5*rand(size(0:10)), 0];
% Fill terrain.
fill(x, y, y);
% Sets the colormap and color axis limits based on the elevation data
% limits.
demcmap(y);
% Link x-axis and y-axis of ax(1) and ax(2).
linkaxes(ax, 'xy');
% Hide ax(1).
set(ax(1), 'Visible', 'off');
% Transparent background for ax(2).
set(ax(2), 'Color', 'none');
% Display title.
title('Vertical Profile');
% Display legend underneath.
legend('Terrain', 'Location', 'southoutside');
linkprop
.Quoting excaza's comment:
linkaxes
only syncs axes limits. You'll need to sync properties (likePosition
) withlinkprop
Add linkprop(ax, 'Position');
before calling legend
.
Result:
As a side note, if you wish to learn more, I suggest reading "Using linkaxes vs. linkprop" from Undocumented Matlab, specially to solve the problem of linkaxes
being overriden by each other.
Code:
% Create figure.
figure;
% Create background axes for the sky.
ax(1) = axes;
% Sky data for background.
x_bg = [0, 0, 10, 10];
y_bg = [0, 10, 10, 0];
% Fill sky with color gradient on y-axis.
fill(x_bg, y_bg, y_bg);
% Generate custom sky colormap.
sky_map = interp1([0, 1], [135, 206, 235; 255, 255, 255]./255, linspace(0, 1, 255));
% Apply sky colormap.
colormap(ax(1), sky_map);
% Create foreground axes for the terrain.
ax(2) = axes;
% Terrain data.
x = [0, 0:10, 10];
y = [0, 5*rand(size(0:10)), 0];
% Fill terrain.
fill(x, y, y);
% Sets the colormap and color axis limits based on the elevation data
% limits.
demcmap(y);
% Link x-axis and y-axis limits of ax(1) and ax(2).
linkaxes(ax, 'xy');
% Link Position property of ax(1) and ax(2).
linkprop(ax, 'Position');
% Hide ax(1).
set(ax(1), 'Visible', 'off');
% Transparent background for ax(2).
set(ax(2), 'Color', 'none');
% Display title.
title('Vertical Profile');
% Display legend underneath.
legend('Terrain', 'Location', 'southoutside');
'FaceVertexCData'
Quoting gnovice's comments 1 and 2:
I have to ask... why are you using two axes? Couldn't you plot the two filled polygons on the same axes, then modify the
ZData
property of each patch to ensure they are layered properly?Having multiple objects that need their own colormaps is tricky, but you can usually get around it by defining object colors so that they are RGB colors instead of colormap indices. For example, the
fill
command you use is really just creatingpatch
objects, so for the sky background you could just make a patch object directly, paying special attention to how you define theC
input argument. The sky could be made without a colormap, so the terrain could use it.
For the sky part, replace fill
with patch
and set hold on
. And then modify the 'FaceVertexCData'
property with an array of true color RGB values (see Patch Properties).
In my code below, the true color array is called CData
and is defined by:
% True color array.
CData = [sky_map(1, :); sky_map(end, :); sky_map(end, :); sky_map(1, :)];
which looks like this:
CData =
0.5294 0.8078 0.9216
1.0000 1.0000 1.0000
1.0000 1.0000 1.0000
0.5294 0.8078 0.9216
then use set
with the patch handle h1
:
% Apply sky colormap to 'FaceVertexCData' property.
set(h1, 'FaceVertexCData', CData);
Result:
Code:
% Create figure.
figure;
% Sky data for background.
x_bg = [0, 0, 10, 10];
y_bg = [0, 10, 10, 0];
% Sky indexed colors.
c_bg = [0, 1, 1, 0];
% Patch sky using default colormap (parula).
h1 = patch(x_bg, y_bg, c_bg);
% Generate custom sky colormap.
sky_map = interp1([0, 1], [135, 206, 235; 255, 255, 255]./255, linspace(0, 1, 255));
% True color array.
CData = [sky_map(1, :); sky_map(end, :); sky_map(end, :); sky_map(1, :)];
% Apply sky colormap to 'FaceVertexCData' property.
set(h1, 'FaceVertexCData', CData);
% Retain sky patch in the current axes.
hold on;
% Terrain data.
x = [0, 0:10, 10];
y = [0, 5*rand(size(0:10)), 0];
% Fill terrain.
h2 = fill(x, y, y);
% Sets the colormap and color axis limits based on the elevation data
% limits.
demcmap(y);
% Display title.
title('Vertical Profile');
% Display legend underneath.
legend(h2, 'Terrain', 'Location', 'southoutside');
Note that the results generated with Method 1 and Method 2 are very similar but not the same:
Method 1:
Method 2: