Related
I'm writing a script that will create 2 suplots, and have either a single slider that scrolls the x axis of both plots, or 2 sliders that individually control each subplots x axis.
I have been using an adapted version of Steven Lords FileExchange scrolling plot demo for my slider.
Right now it will only update the most recent plot (as it is currently using gca in its callback function). I've tried just replacing gca with the axes I want (the variables first_plot or second_plot) but this doesn't seem to work.
My question is then, how should I adapt this function to either control both plots or each plot individually? Here is an example of the script I am writing:
x=0:1e-2:2*pi;
y=sin(x);
dx=2;
first_plot = subplot(2,1,1);
plot(x, y);
scrollplot(dx, x)
%Plot the respiration and probe data with scrolling bar
second_plot = subplot(2,1,2);
plot(x, y);
scrollplot(dx,x)
% dx is the width of the axis 'window'
function scrollplot(dx, x)
a=gca;
% Set appropriate axis limits and settings
set(gcf,'doublebuffer','on');
set(a,'xlim',[0 dx]);
% Generate constants for use in uicontrol initialization
pos=get(a,'position');
Newpos=[pos(1) pos(2)-0.1 pos(3) 0.05];
% This will create a slider which is just underneath the axis
% but still leaves room for the axis labels above the slider
xmax=max(x);
%S= set(gca,'xlim',(get(gcbo,'value')+[0 dx]));
S=['set(gca,''xlim'',get(gcbo,''value'')+[0 ' num2str(dx) '])'];
% Setting up callback string to modify XLim of axis (gca)
% based on the position of the slider (gcbo)
% Creating Uicontrol
h=uicontrol('style','slider',...
'units','normalized','position',Newpos,...
'callback',S,'min',0,'max',xmax-dx);
end
Thanks!
You're almost there, but there's some structural issues from when you modified the code from only one set of axes.
The key thing to do is change the callback function from a string to an actual local function. This makes handling the callback much simpler!
I've adapted your code to work with two (or more) axes. Note we only need to set up the scroll bar once! You were setting it up for each axes (the scroll bars were stacked on top of each other) and both scrollers only operated on gca. Just naming the axes isn't enough to change gca, you have to use those variables! I've assigned the axes to an array for easy manipulation.
Please see the comments for details:
x=0:1e-2:2*pi;
y=sin(x);
% dx is the width of the axis 'window'
dx=2;
% Initialise the figure once, and we only need to set the properties once
fig = figure(1); clf;
set( fig, 'doublebuffer', 'on');
% Create a placeholder for axes objects
ax = gobjects( 2, 1 );
% Create plots, storing them in the axes object
ax(1) = subplot(2,1,1);
plot(x, y);
ax(2) = subplot(2,1,2);
plot(x, y);
% Set up the scroller for the array of axes objects in 'ax'
scrollplot( dx, x, ax)
function scrollplot( dx, x, ax )
% Set appropriate axis limits
for ii = 1:numel(ax)
set( ax(ii), 'xlim', [0 dx] );
end
% Create Uicontrol slider
% The callback is another local function, this gives us more
% flexibility than a character array.
uicontrol('style','slider',...
'units', 'normalized', 'position', [0.1 0.01 0.8 0.05],...
'callback', #(slider, ~) scrollcallback( ax, dx, slider ), ...
'min', 0, 'max', max(x)-dx );
end
function scrollcallback( ax, dx, slider, varargin )
% Scroller callback loops through the axes objects and updates the xlim
val = slider.Value;
for ii = 1:numel(ax)
set( ax(ii), 'xlim', val + [0, dx] );
end
end
I can't find any info on how to do this on the Internet other than to use plotyy which only seems to work for two functions.
From Matlab documentation:
Use Right y-Axis for Two Data Sets
Plot three data sets using a graph with two y-axes. Plot one set of
data associated with the left y-axis. Plot two sets of data associated
with the right y-axis by using two-column matrices.
x = linspace(0,10);
y1 = 200*exp(-0.05*x).*sin(x);
y2 = 0.8*exp(-0.5*x).*sin(10*x);
y3 = 0.2*exp(-0.5*x).*sin(10*x);
plotyy(x,y1,[x',x'],[y2',y3']);
In my opinion, the way to do this that confers the most manual control is to create three overlapping axes with the plots you need, and only display the axis for the topmost one. You could even create 'empty' axes just so you they can serve as the only axis with defined 'limits' in the x and y axes.
Example:
ax1 = axes();
X1 = linspace(0,8*pi, 100); Y1 = sin(X1);
plot(X1, Y1, 'r', 'linewidth', 10);
ax2 = axes();
h = ezplot(#(x) x .* sin(x), [-100, 100]); set(h, 'color', 'w');
ax3 = axes();
image()
%% place them on top of each other by calling them in the order you want
axes(ax3); % bottommost
axes(ax1);
axes(ax2); % topmost
set(ax1, 'visible', 'off');
set(ax2, 'visible', 'off');
set(ax3, 'visible', 'on'); % this is the axes who's limits will show
I want to add an entry manually to a MATLAB legend. This legend can be pre-existent and contain other graphed elements' entries, but not necessarily.
I make a scatter plot, but instead of using e.g. scatter(x,y), I plot it using
for n = 1:numel(x)
text(x(n),y(n),num2str(n), ...
'HorizontalAlignment','center','color',[1 0 0])
end
This results in a scatter plot of numbers one through the number of elements in x (and y, because they are of the same size). I want to add a legend entry for these numbers.
I tried to add or edit the legend with
[h,icons,plots,s] = legend(___)
as described on the legend documentation page. I can't figure out how I can add a legend entry, without having to plot something (such as an actual scatter plot or regular plot). I want the usual line or marker symbol in the legend to be a number or character such as 'n', indicating the numbers in the graph. Is this possible and how would one achieve this?
EDIT by Erik
My answer goes below zelanix's answer, because mine is based on it.
Original answer
A fairly workable solution may be as follows:
x = rand(10, 1);
y = rand(10, 1);
figure;
text(x,y,num2str(transpose(1:numel(x))),'HorizontalAlignment','center')
% Create dummy legend entries, with white symbols.
hold on;
plot(0, 0, 'o', 'color', [1 1 1], 'visible', 'off');
plot(0, 0, 'o', 'color', [1 1 1], 'visible', 'off');
hold off;
% Create legend with placeholder entries.
[h_leg, icons] = legend('foo', 'bar');
% Create new (invisible) axes on top of the legend so that we can draw
% text on top.
ax2 = axes('position', get(h_leg, 'position'));
set(ax2, 'Color', 'none', 'Box', 'off')
set(ax2, 'xtick', [], 'ytick', []);
% Draw the numbers on the legend, positioned as per the original markers.
text(get(icons(4), 'XData'), get(icons(4), 'YData'), '1', 'HorizontalAlignment', 'center')
text(get(icons(6), 'XData'), get(icons(6), 'YData'), '2', 'HorizontalAlignment', 'center')
axes(ax1);
Output:
The trick to this is that the new axes are created in exactly the same place as the legend, and the coordinates of the elements of the icons are in normalised coordinates which can now be used inside the new axes directly. Of course you are now free to use whatever font size / colour / whatever you need.
The disadvantage is that this should only be called after your legend has been populated and positioned. Moving the legend, or adding entries will not update the custom markers.
Erik's answer
Based on zelanix's answer above. It is a work-in-progress answer, I am trying to make a quite flexible function of this. Currently, it's just a script that you'd need to adapt to your situation.
% plot some lines and some text numbers
f = figure;
plot([0 1],[0 1],[0 1],[1 0])
x = rand(25,1);
y = rand(25,1);
for n = 1:numel(x)
text(x(n),y(n),num2str(n), ...
'HorizontalAlignment','center','color',[1 0 0])
end
hold on
% scatter(x,y) % used to test the number positions
scatter(x,y,'Visible','off') % moves the legend location to best position
% create the dummy legend using some dummy plots
plot(0,0,'o','Visible','off')
[l,i] = legend('some line','some other line','some numbers','location','best');
l.Visible = 'off';
% create empty axes to mimick legend
oa = gca; % the original current axes handle
a = axes;
axis manual
a.Box = 'on';
a.XTick = [];
a.YTick = [];
% copy the legend's properties and contents to the new axes
a.Units = l.Units; % just in case
a.Position = l.Position;
i = copyobj(i,a);
% replace the marker with a red 'n'
s = findobj(i,'string','some numbers');
% m = findobj(i(i~=s),'-property','YData','marker','o');
m = findobj(i(i~=s),'-property','YData');
sy = s.Position(2);
if numel(m)>1
dy = abs(m(1).YData - sy);
for k = 2:numel(m)
h = m(k);
dy2 = abs(h.YData - sy);
if dy2<dy
kbest = k;
dy = dy2;
end
end
m = m(kbest);
end
m.Visible = 'off';
mx = m.XData;
text(mx,sy,'n','HorizontalAlignment','center','color',[1 0 0])
% reset current axes to main axes
f.CurrentAxes = oa;
The result:
I'm using Matlab to produce figures, and I'm wondering if there is a way to plot a zoomed region in a figure of the overall data?
I have scatter data plotted over one week, with the x-axis in hours, and I want to zoom into the first 3 hours, and display them within the main figure with the x-axis label of minutes.
The plotting code I have so far is as follows:
allvalsx = marabint(:,2)
allvalsy = marabint(:,5)
subvalsx = marabint(1:7,2);
subvalsy = marabint(1:7,2);
%% Plots the scatter chart.
sizemarker = 135
handle = scatter(allvalsx, allvalsy, sizemarker, '.')
figure(1)
axes('Position',[.2 .2 .2 .2])
handle2 = scatter(subvalsx, subvalsy, '.r')
title(plotTitle)
xlabel('Time since treatment (hours)')
ylabel('Contact angle (deg)')
% Axis scales x1, x2, y1, y2
axis([0, marabint(length(marabint),2) + 10, 0, 120]);
% This adds a red horizontal line indicating the untreated angle of the
% sample.
untreatedLine = line('XData', [0 marabint(length(marabint),2) + 10], 'YData', [untreatedAngle untreatedAngle], 'LineStyle', '-', ...
'LineWidth', 1, 'Color','r');
% Adding a legend to the graph
legendInfo = horzcat('Untreated angle of ', untreatedString)
hleg1 = legend(untreatedLine, legendInfo);
% This encases the plot in a box
a = gca;
% set box property to off and remove background color
set(a,'box','off','color','none')
% create new, empty axes with box but without ticks
b = axes('Position',get(a,'Position'),'box','on','xtick',[],'ytick',[]);
% set original axes as active
axes(a)
% link axes in case of zooming
linkaxes([a b])
set(gcf,'PaperUnits','inches');
set(gcf,'PaperSize', [8.267 5.25]);
set(gcf,'PaperPosition',[0 0.2625 8.267 4.75]);
set(gcf,'PaperPositionMode','Manual');
set(handle,'Marker','.');
print(gcf, '-dpdf', '-r150', horzcat('markertest4.pdf'));
This produces the following
Can anyone help me out with this?
yeah, I think I know what you need. Try this:
zoomStart = 0;
zoomStop = 3;
set(gca, 'XLim', [zoomStart zoomStop])
Let me know if that doesn't do what you need, and I'll give you a different way.
I've run into a bit of a hiccup trying to plot some data in the way I want it - any advice would be greatly appreciated.
left and right are vectors of a few hundred thousand in length, obtained elsewhere.
The code below plots left, twice - the second plot lies on top of the first, roughly towards one corner.
ax1 = axes;
plot(ax1, left, 'b');
set(ax1, 'xlim', [7.075*10^4 7.5*10^4]);
set(ax1, 'ylim', [-0.02 0.02]);
ax2 = axes('Position', get(ax1,'Position'), 'XAxisLocation', 'top', 'YAxisLocation', 'right', 'Color', 'none', 'XColor', 'k', 'YColor', 'k', 'NextPlot', 'add');
plot(ax2, left, 'b');
set(ax2, 'Units', 'normalized', 'Position', [0.6 0.60 0.25 0.25]);
What I'd like to do is have the same kind of thing for right, and then display each pair of plots as a subplot, with the two subplots side by side. I've tried adapting the way I'm doing it above to use subplot, but I'm obviously doing something wrong since I keep on nuking the contents of each subplot and ending up with two empty subplots.
Also, is it possible to prevent the smaller inset plot from having a transparent background?
Consider the following example:
%# sample data
x = 1:100;
left = randn(100,1);
right = cumsum(rand(100,1)-0.5);
%# build axes positions
hBig = [subplot(121) subplot(122)]; %# create subplots
posBig = get(hBig, 'Position'); %# record their positions
delete(hBig) %# delete them
posSmall{1} = [0.275 0.63 0.16 0.24];
posSmall{2} = [0.717 0.63 0.16 0.24];
%# create axes (big/small)
hAxB(1) = axes('Position',posBig{1});
hAxB(2) = axes('Position',posBig{2});
hAxS(1) = axes('Position',posSmall{1});
hAxS(2) = axes('Position',posSmall{2});
%# plot
plot(hAxB(1), x, left, 'b');
plot(hAxB(2), x, right, 'b');
plot(hAxS(1), x, left, 'r');
plot(hAxS(2), x, right, 'r');
%# set axes properties
set(hAxB, 'XLim',[1 100], 'YLim',[-10 10]);
set(hAxS , 'Color','none', 'XAxisLocation','top', 'YAxisLocation','right');
If you want the background color of the smaller axes to be opaque, just set their colors to white:
set(hAxS , 'Color','w')
To change the background, use (for red background)
set(ax2,'color',[1 0 0])
Regarding the subplot, if you post the code that doesn't work it will help.