Secondary axes get displaced when resizing figure - matlab

I am trying to plot a Matlab (R2017a) figure (filled contour + colorbar) with two X axes, on the bottom and top of the graph, with the same scale but different tick marks and labels. Following the suggestions found here and here, I have achieved it, but when I try to resize the figure window manually or to print it setting certain proportions different from the default ones, e.g.:
set(gcf,'PaperUnits','centimeters','PaperPosition',[0 0 30 15])
print(gcf,'-dpng',path,'-r300')
the new axes get displaced:
I have reproduced my issue with the peaks example data from Matlab:
contourf(peaks)
ax1=gca;
colorbar
set(ax1,'box','off','color','none') % get rid of the box in order not to have duplicated tick marks
ax1_pos = ax1.Position; % position of first axes
ax2 = axes('Position',ax1_pos,... % set the new pair of axes
'XAxisLocation','top',...
'YAxisLocation','Right',...
'Color','none');
set(ax2, 'XLim', get(ax1, 'XLim'), 'YLim', get(ax1, 'YLim')); % set same limits as for ax1
set(ax2, 'XTick', 0:14:42, 'XTickLabels', {'a','a','a','a'},... % set new tick marks and labels for the top X axis.
'YTick', get(ax1, 'YTick'), 'YTickLabels', []);
Curiously enough, if I remove the colobar command and plot just the filled contour, the figure behaves correctly:
Does anyone know why is this happening (and how could it be solved)? I'm also open to achieve the two-X-axis plot by other means.

Your problem is that one axis has a colorbar and other does not, and even if you would add a colorbar to both axis, there can be quite a lot of automatic things going on that would resize your axis differently.
However, we can add an event listener and define a function to operate make both axis the same. The listener will make sure it catches the event (resizing) and that it calls the function we define. This is the code I made for that:
%% this creates the listener for change of size in the figure
f = figure('SizeChangedFcn',#(src,evn) fixaxis(src));
%% this is your code
contourf(peaks)
ax1=gca;
colorbar
set(ax1,'box','off','color','none') % get rid of the box in order not to have duplicated tick marks
ax1_pos = ax1.Position; % position of first axes
ax2 = axes('Position',ax1_pos,... % set the new pair of axes
'XAxisLocation','top',...
'YAxisLocation','Right',...
'Color','none');
set(ax2, 'XLim', get(ax1, 'XLim'), 'YLim', get(ax1, 'YLim')); % set same limits as for ax1
set(ax2, 'XTick', 0:14:42, 'XTickLabels', {'a','a','a','a'},... % set new tick marks and labels for the top X axis.
'YTick', get(ax1, 'YTick'), 'YTickLabels', []);
%% this will resize the axis if 2 of them exist
function fixaxis(src)
ax=findall(src,'Type','Axes');
if length(ax)==2
ax(2).Position=ax(1).Position;
end
end

Try also setting the 'PaperPositionMode' to 'auto':
set(gcf,'PaperUnits','centimeters','PaperPosition', [0 0 30 15], 'PaperPositionMode', 'auto');
% then print
print(gcf, '-dpng', 'myFile', '-r300')
Above command works for me. Produces following result:

Related

Change the width of a subplot. There is no position property on the Line class

I have the following figure
that I created using the following code
%% figures
DateNumObs=datenum(table2array(ADCPCRUM2(1:1678,ColumnYear)),table2array(ADCPCRUM2(1:1678,ColumnMonth)),table2array(ADCPCRUM2(1:1678,ColumnDay)),table2array(ADCPCRUM2(1:1678,ColumnHour)),table2array(ADCPCRUM2(1:1678,ColumnMinutes)),table2array(ADCPCRUM2(1:1678,ColumnSeconds)));
Flipecart=permute(ecart(1:1677,:),[2,1]);
Flipecartreel=permute(ecartreel(1:1677,:),[2,1]);
bottomVel=min(min(min(Magnitude)),min(min(velocityModel*1000)));
topVel=max(max(max(Magnitude)),max(max(velocityModel*1000)));
bottomVer=min(min(Flipecart))
topVer=max(max(Flipecart))
figure
subplot(4,1,1);
FlipMag=permute(Magnitude,[2,1]);
[C,h] =contourf(DateNumObs,1:1:22,FlipMag);
datetick('x','dd/mm/yy','keeplimits','keepticks')
caxis manual
caxis([bottomVel topVel])
c=colorbar;
c.Label.String = 'Horizontal velocity(mm/s)';
xlabel('Date');
ylabel('Depth(m from bottom)');
set(h,'LineColor','none')
title('Observation');
subplot(4,1,2);
[C,h] =contourf(DateNumObs(1:1677),1:1:22,MagMatrixH1*1000);
datetick('x','dd/mm/yy','keeplimits','keepticks')
caxis manual
caxis([bottomVel topVel])
c=colorbar;
c.Label.String = 'Horizontal velocity(mm/s)';
xlabel('Date');
ylabel('Depth(m from bottom)');
set(h,'LineColor','none')
title('Model D1');
subplot(4,1,3)
% x0=10;
% y0=10;
% width=550;
% height=400
gcf=plot(DateNumObs(1:1677),Flipecart(10,:))
% set(gcf,'LineWidth',1,'position',[x0,y0,width,height]) % Part giving the error
datetick('x','dd/mm/yy','keeplimits','keepticks')
caxis manual
caxis([bottomVer topVer])
subplot(4,1,4)
c=colorbar;
plot(DateNumObs(1:1677),Flipecartreel(10,:))
datetick('x','dd/mm/yy','keeplimits','keepticks')
caxis manual
caxis([bottomVer topVer])
I am trying to got the normal plot to be the same size as the (blue) contourf plots by using the code which is commented in the code I posted. I got this code from https://nl.mathworks.com/matlabcentral/answers/65402-how-to-set-graph-size .
However, when I try to run it it gives me the following error:
Error using matlab.graphics.chart.primitive.Line/set
There is no position property on the Line class.
Error in StatisticsSOLA (line 315)
set(gcf,'LineWidth',1,'position',[x0,y0,width,height])
I also tried is it possible to change the height of a subplot? but I get the same error. How do I prevent this error and change the width of the bottom two figures?
You are trying to set the position of the axes and the linewidth of the line object in one go, but are not providing the correct handles. Furthermore, don't store the handles of the lines in gcf, since this is a reference to the currently active figure.
Instead you can do:
data = rand(100,200); % some data
fig = figure(1); clf;
% first subplot with colorbar
ax(1) = subplot(211);
imagesc(data)
c = colorbar;
% second subplot without colorbar
ax(2) = subplot(212);
p = plot(data(1,:))
% set height and width of second subplot
drawnow % needed to get right position value of ax(1) and ax(2)
ax_pos = [ax(2).Position(1:2) ax(1).Position(3:4)]; % keep ax(2)'s left and bottom position, and set same width and height as ax(1)
set(ax(2), 'Position', ax_pos)
Alternative
Sometimes it is easier to create colorbar in the second axes, and hide it. This way you don't have to set the positions of the axes yourself.
data = rand(100,200); % some data
fig = figure(1); clf;
% first subplot with colorbar
ax(1) = subplot(211);
imagesc(data)
c = colorbar;
% second subplot without colorbar
ax(2) = subplot(212);
p = plot(data(1,:))
c = colorbar; % draw useless colorbar,
c.Visible = 'off'; % and hide it
Figure should look the same:

How to Have Non-Zero Symbol for Incomplete Labels of XTicks in Matlab?

I run into the problem where Matlab 2015b expands the labels of new Xticks when the x-axis gets bigger by using incomplete label, zeros, in the thread No Gap Next to Axis Label in Matlab?
The dynamic expansion of incomplete labels of xticks is not possible because there is always cases of insufficient space but only one symbol is needed to mark half between two values.
The situation is problematic with zeros because I have several calibration points and several systems where the extra zeros are errorprone.
I would like to have there another symbol.
Example code how to create those incomplete labels of xticks
labels = arrayfun(#(x)sprintf('%.2g', x), xticks, 'uniform', 0);
ax2 = axes('OuterPosition', [0.51 0.5 0.5 0.5]); % anything here
xticks = get(ax2, 'xtick'); % https://stackoverflow.com/a/35776785/54964
set(ax2, 'xticklabels', labels); % here the point!
Without those incomplete labels of xticks but broader labelling which is worser
labels = arrayfun(#(x)sprintf('%.2g', x), xticks, 'uniform', 0);
ax2 = axes('OuterPosition', [0.51 0.5 0.5 0.5]);
xticks = get(ax2, 'xtick'); % https://stackoverflow.com/a/35776785/54964
set(ax2, 'xtick', xticks, 'xticklabels', labels);
Output of Suever's answer
Beautiful Small window in the original size with scientific numbering because of callback(); at the end of the code following
Medium window
Code
hFig=figure;
data=randi(513,513);
D=mat2gray(pdist(data, 'correlation'));
ax2 = axes('OuterPosition', [0.51 0.5 0.5 0.5]);
plot(D, 'Parent', ax2);
axis(ax2, 'square');
title('Corr pdist');
cbar2 = colorbar();
set(ax2, 'XLim', [0 size(D,2)]);
set(cbar2, 'Visible', 'off')
grid minor;
labelconverter = #(x)sprintf('%.2g', x); % https://stackoverflow.com/a/35780915/54964
callback = #(varargin)set(ax2, 'xticklabels', arrayfun(labelconverter, get(ax2, 'xtick'), 'uniform', 0));
set(hFig, 'SizeChangedFcn', callback);
callback(); % necessary for small window
How can you have another symbol for the incomplete labels of xticks in Matlab?
As I said in the other question, if you want the labels to be updated automatically when you resize things, you'll want to do the following.
fig = figure;
% Set large xlimits to demonstrate the issue at hand
ax2 = axes('xlim', [0 1e9]);
% Force a draw event to have the axes determine where the
labelconverter = #(x)sprintf('%.2g', x);
callback = #(varargin)set(ax2, 'xticklabels', arrayfun(labelconverter, get(ax2, 'xtick'), 'uniform', 0));
set(fig, 'SizeChangedFcn', callback);
% Be sure to execute the callback to get new labels prior to figure resize.
callback();
As you change the size of your figure, the labels will be changed automatically and the positions will be updated.
Small Window
Medium Window
Large Window
Note: Test this code in isolation to verify that it works, then adapt the idea to your solution. It seems like you're ending up with a lot of complications because your namespace is polluted (for example your examples don't even run because labels isn't defined).

No Gap Next to Axis Label in Matlab?

Matlab 2015b or Matlab 2016a.
I would like to have grid lines going across Subplot's spacing between the figures in order to evaluate better two pictures horizontally.
However, I have a small gap between the two figures at the lower right-hand-side corner because which is misaligning the figures
where the gap is because of the 10^4 at lower right-hand-side corner.
I would also like to have horizontal lines going across the spacing between the two figures, but I cannot do it before the gap problem is solved.
Code where the relative alignment is done as described in the answer here about the thread Tight subplot with colorbars and subplot's 3rd parameter in Matlab?
data=randi(513,513);
D=mat2gray(pdist(data, 'correlation'));
% Set normalized outer position (x,y,width,height)
ax1=axes('OuterPosition', [0 0.5 0.5 0.5]);
plot(D, 'Parent', ax1);
xlim([0 size(D,2)]);
set(cbar1, 'Visible', 'off')
title('Signal');
ax2=axes('OuterPosition', [0.51 0.5 0.5 0.5]);
plot(D, 'Parent', ax2);
set(ax2, 'XLim', [0, size(D,1)])
axis(ax2, 'square');
title('Corr pdist');
Output of Suever's answer
I tried unsuccessfully change two (2) in sprintf('%.2g', x) bigger and smaller
ax2 = axes('OuterPosition', [0.51 0.5 0.5 0.5]);
plot(D, 'Parent', ax2);
set(ax2, 'XLim', [0, size(D,1)])
axis(ax2, 'square');
title('Corr pdist');
cbar2 = colorbar(); % ax2 not needed here in brackets
set(ax2, 'XLim', [0 size(D,2)]);
set(cbar2, 'Visible', 'off')
grid minor;
% https://stackoverflow.com/a/35776785/54964
xticks = get(ax2, 'xtick');
labels = arrayfun(#(x)sprintf('%.2g', x), xticks, 'uniform', 0);
set(ax2, 'xticklabels', labels);
It gives
where those ticks are not XMinorTicks but simply ticks (wrongly marked in the picture).
They are zero points at some points in the x-axis. When x-axis gets larger, MATLAB automatically adds new xtick marks but without complete labels.
I think it would be better to have another symbol than zero there. How can you have some other mark than zero for incomplete labels of xticks?
How can you align the 10^4 next to the last number in the second figure?
I would get the current xtick locations, convert those to strings, and then set the xticklabels property of the axes.
xticks = get(ax2, 'xtick');
labels = arrayfun(#(x)sprintf('%.2g', x), xticks, 'uniform', 0);
set(ax2, 'xtick', xticks, 'xticklabels', labels);
If you want them to dynamically be computed as the figure changes size (and the xticks get recomputed) you can link this code to the SizeChangedFcn of the figure.
func = #(varargin)set(ax2,'xticklabels',arrayfun(#(x)sprintf('%.2g',x),get(ax2, 'xtick'),'uni',0));
set(gcf, 'SizeChangedFcn', func)

Matlab - No luck when using 'xtick' to change axis on graph

I have the following graph in Matlab:
I have tried using 'xTick' and 'yTick' to make the axis on each subplot the same, but it's not accomplishing what I would like it to. I also want the both axes of each subplot to share the same range so that I can easily compare the graphs. (i.e. ranging from 0 - 20, in y, and 0 - 400 in x).
I'm not sure how to change this.
My attempt is below. Does anyone know how to do this?
figure()
hold on
subplot (1,2,1);
% xlim([0 400]);
% ylim([0 25]);
graph_made = [num_calls_made];
plot (graph_made);
title('Number of calls made')
xlabel('ID Number of caller');
ylabel('Number of calls');
set(gca, 'XTick', [0:100:400]);
set(gca, 'YTick', [0:5:20]);
subplot (1,2,2);
graph_rec = [num_calls_received];
plot (graph_rec);
title('Number of calls received')
xlabel('ID Number of caller');
ylabel('Number of calls');
set(gca, 'XTick', [0:100:400]);
set(gca, 'YTick', [0:5:20]);
hold off
If you want the axes limits to stay linked as a user interactively zooms or pans, you can also use the linkaxes command...
subplot(1,2,1)
% your plotting code here...
ax = gca; %get the handle to the current axis
subplot(1,2,2)
% your plotting code here...
ax(end+1) = gca; %get the handle to the current axis
linkaxes(ax); %this will link both the x and y axes.
XTick and YTick only change where the labels on axes go, not the limits of the axes. To change those, you have to use axis (or xlim and ylim):
axis([0 400 0 20]) %// [xmin xmax ymin ymax]

How can I assign multiple colors to tick labels in plots in MATLAB?

Is it possible to color a single number (or a set of numbers) on one of the axes in MATLAB?
Suppose I have a plot:
plot(1:10, rand(1,10))
Now, can I e.g. make the number 3 on the x-axis red?
Single tick labels can be colored using tex markup, which is enabled for tick labels by default. It is defined in the TickLabelInterpreter property of the axis.
It provides two commands for coloring text:
\color{<name>}, where <name> is a color name like “red” or “green“, and
\color[rgb]{<R>,<G>,<B>}, where <R>, <G> and <B> are numbers between 0 and 1 and define an RGB color.
These commands can be used to color single tick labels:
plot(1:10, rand(1,10))
ax = gca;
% Simply color an XTickLabel
ax.XTickLabel{3} = ['\color{red}' ax.XTickLabel{3}];
% Use TeX symbols
ax.XTickLabel{4} = '\color{blue} \uparrow';
% Use multiple colors in one XTickLabel
ax.XTickLabel{5} = '\color[rgb]{0,1,0}green\color{orange}?';
% Color YTickLabels with colormap
nColors = numel(ax.YTickLabel);
cm = jet(nColors);
for i = 1:nColors
ax.YTickLabel{i} = sprintf('\\color[rgb]{%f,%f,%f}%s', ...
cm(i,:), ax.YTickLabel{i});
end
And this is how the result looks:
The code worked for me in MATLAB R2016b and R2017a.
Unfortunately, you cannot have multiple colors for tick labels in one axes object. However, there's a solution (inspired by this page from MathWorks support site) that achieves the same effect. It overlays the existing axes it with another axes that has only one red tick.
Here's an example:
figure
plot(1:10, rand(1,10))
ax2 = copyobj(gca, gcf); %// Create a copy the axes
set(ax2, 'XTick', 3, 'XColor', 'r', 'Color', 'none') %// Keep only one red tick
ax3 = copyobj(gca, gcf); %// Create another copy
set(ax3, 'XTick', [], 'Color', 'none') %// Keep only the gridline
The result is:
As an alternative to copying the entire axes contents, it is possible to do this also by creating additional axes objects:
ax = axes();
p = plot(1:10, rand(1,10));
myTick = 3;
% Create new axes with transparent backgrounds
ax2 = axes();
ax3 = axes();
set([ax2 ax3], 'XLim', xlim(ax));
set([ax2 ax3], 'Color', 'none');
set(ax3, 'XTick', [], 'YTick', []);
% Give one new axes a single tick mark
set(ax2, 'YTick', []);
set(ax2, 'XTick', myTick);
set(ax2, 'XColor', 'r');
% This line is necessary to use the plot toolbar functions like zoom / pan
linkaxes([ax ax2 ax3]);