crosshairs or just vertical line across linked axis plots

What I have are plots that are linked by the linkaxes() function. I would like to have a vertical line that spans all of the plots so that I can compare the peaks and valleys interactively between plots.

 Risposta accettata

This may get you started:
function vertical_cursors
set(gcf, ...
'WindowButtonDownFcn', @clickFcn, ...
'WindowButtonUpFcn', @unclickFcn);
% Set up cursor text
allLines = findobj(gcf, 'type', 'line');
hText = nan(1, length(allLines));
for id = 1:length(allLines)
hText(id) = text(NaN, NaN, '', ...
'Parent', get(allLines(id), 'Parent'), ...
'BackgroundColor', 'yellow', ...
'Color', get(allLines(id), 'Color'));
end
% Set up cursor lines
allAxes = findobj(gcf, 'Type', 'axes');
hCur = nan(1, length(allAxes));
for id = 1:length(allAxes)
hCur(id) = line([NaN NaN], ylim(allAxes(id)), ...
'Color', 'black', 'Parent', allAxes(id));
end
function clickFcn(varargin)
% Initiate cursor if clicked anywhere but the figure
if strcmpi(get(gco, 'type'), 'figure')
set(hCur, 'XData', [NaN NaN]); % <-- EDIT
set(hText, 'Position', [NaN NaN]); % <-- EDIT
else
set(gcf, 'WindowButtonMotionFcn', @dragFcn)
dragFcn()
end
end
function dragFcn(varargin)
% Get mouse location
pt = get(gca, 'CurrentPoint');
% Update cursor line position
set(hCur, 'XData', [pt(1), pt(1)]);
% Update cursor text
for idx = 1:length(allLines)
xdata = get(allLines(idx), 'XData');
ydata = get(allLines(idx), 'YData');
if pt(1) >= xdata(1) && pt(1) <= xdata(end)
y = interp1(xdata, ydata, pt(1));
set(hText(idx), 'Position', [pt(1), y], ...
'String', sprintf('(%0.2f, %0.2f)', pt(1), y));
else
set(hText(idx), 'Position', [NaN NaN]);
end
end
end
function unclickFcn(varargin)
set(gcf, 'WindowButtonMotionFcn', '');
end
end
Save the above function. Here's an example of how to use it:
subplot(221);
plot(rand(10,1));
subplot(222);
plot(rand(10,2));
subplot(223);
plot([rand(10,1), rand(10,1)+1]);
subplot(224);
plot(rand(10,1));
vertical_cursors;
EDIT: As a response to a request, I added the ability to make the cursor disappear when clicking outside the axes. See the two lines of code with the comment "<-- EDIT".

10 Commenti

1 vote, interesting function
Exactly what I wanted. Thanks!
I notice this question getting asked more often these days. Perhaps we should create a FAQ question about this.
Very nice!
How would you program it so that when they click off the axes the cursor lines disappear?
@Gina, I modified my answer above to include that capability. I simply added two lines of code to make the line "disappear" upon clicking outside the axes.
Many thanks, this function makes my life easier !!!
Very good alternative to the removed 'fullcrosshair' since 2014b.
With the additional function 'linkprop' the cursor can be synchronised with additional figures.
@Jiro, many thanks for your answer perfect! I do however get an error when dragging the cursor:
Error using interp1 (line 257) The values of X should be distinct.
Error in vertical_cursors/dragFcn (line 48) y = interp1(xdata, ydata, pt(1));
Error while evaluating figure WindowButtonMotionFcn
Also is it also possible to link a crosshair in a scatterplot (based on position in array) So my data is displayed both in multiple subplots, which are now linked by a vertical line. but could you also link a crosshair in a scatterplot?
@Giro thank you for this function.
I need to acces to the values of the cursor, how is it posible to obtain the values of different cursors (i need to calculate the time between 2 cursors) in an array for instance?
And how could i put the text under the x-axis? because the text is located at the right of the cursor which does not allow to see well the plot..
Sorry for my bad english, i hope I'll be understood, i can try an other way to express what i want to do if not.
Hi,
I tried the example given and it works without issues. Then on another figure with 4 subplots that I've linked their x-axes, I've come across this error below,
Is there a way to get around this error message?
Error using griddedInterpolant
The grid vectors must contain unique points.
Error in interp1 (line 149)
F = griddedInterpolant(X,V,method);
Error in vertical_cursors/dragFcn (line 41)
y = interp1(xdata, ydata, pt(1));
Error in vertical_cursors/clickFcn (line 28)
dragFcn()
Error while evaluating Figure WindowButtonDownFcn.
Error using griddedInterpolant
The grid vectors must contain unique points.
Error in interp1 (line 149)
F = griddedInterpolant(X,V,method);
Error in vertical_cursors/dragFcn (line 41)
y = interp1(xdata, ydata, pt(1));
Error in vertical_cursors/clickFcn (line 28)
dragFcn()
Error while evaluating Figure WindowButtonDownFcn.
Error using griddedInterpolant
The grid vectors must contain unique points.
Error in interp1 (line 149)
F = griddedInterpolant(X,V,method);
Error in vertical_cursors/dragFcn (line 41)
y = interp1(xdata, ydata, pt(1));
Error while evaluating Figure WindowButtonMotionFcn.
Error using griddedInterpolant
The grid vectors must contain unique points.
Error in interp1 (line 149)
F = griddedInterpolant(X,V,method);
Error in vertical_cursors/dragFcn (line 41)
y = interp1(xdata, ydata, pt(1));
Error while evaluating Figure WindowButtonMotionFcn.
Error using griddedInterpolant
The grid vectors must contain unique points.
Error in interp1 (line 149)
F = griddedInterpolant(X,V,method);
Error in vertical_cursors/dragFcn (line 41)
y = interp1(xdata, ydata, pt(1));
Error while evaluating Figure WindowButtonMotionFcn.
Error using griddedInterpolant
The grid vectors must contain unique points.
Error in interp1 (line 149)
F = griddedInterpolant(X,V,method);
Error in vertical_cursors/dragFcn (line 41)
y = interp1(xdata, ydata, pt(1));
Error while evaluating Figure WindowButtonMotionFcn.
Error using griddedInterpolant
The grid vectors must contain unique points.
Error in interp1 (line 149)
F = griddedInterpolant(X,V,method);
Error in vertical_cursors/dragFcn (line 41)
y = interp1(xdata, ydata, pt(1));
Error while evaluating Figure WindowButtonMotionFcn.
Error using griddedInterpolant
The grid vectors must contain unique points.
Error in interp1 (line 149)
F = griddedInterpolant(X,V,method);
Error in vertical_cursors/dragFcn (line 41)
y = interp1(xdata, ydata, pt(1));
Error while evaluating Figure WindowButtonMotionFcn.
Error using griddedInterpolant
The grid vectors must contain unique points.
Error in interp1 (line 149)
F = griddedInterpolant(X,V,method);
Error in vertical_cursors/dragFcn (line 41)
y = interp1(xdata, ydata, pt(1));
Error while evaluating Figure WindowButtonMotionFcn.
Thank you very much for the code!
I extended it for datetime x-axis compatibility:
function vertical_cursors
set(gcf, ...
'WindowButtonDownFcn', @clickFcn, ...
'WindowButtonUpFcn', @unclickFcn);
% Set up cursor text
allLines = findobj(gcf, 'type', 'line');
hText = nan(1, length(allLines));
for id = 1:length(allLines)
hText(id) = text(NaN, NaN, '', ...
'Parent', get(allLines(id), 'Parent'), ...
'BackgroundColor', 'yellow', ...
'Color', get(allLines(id), 'Color'));
end
% Set up cursor lines
allAxes = findobj(gcf, 'Type', 'axes');
hCur = nan(1, length(allAxes));
for id = 1:length(allAxes)
if isa(xlim(allAxes(id)), 'datetime') == 1
x_lims = xlim(allAxes(id));
%default_time = x_lims(1) + diff(x_lims) / 2; % Option A) use mid
default_time = NaT('TimeZone',x_lims(1).TimeZone); % Option B) don't display at start
nan_data = [default_time default_time];
else
nan_data = [NaN NaN];
end
hCur(id) = line(nan_data, ylim(allAxes(id)), ...
'Color', 'black', 'Parent', allAxes(id));
end
function clickFcn(varargin)
% Initiate cursor if clicked anywhere but the figure
if strcmpi(get(gco, 'type'), 'figure')
if isa(xlim(gca), 'datetime') == 1
x_lims = xlim(gca);
nan_time = NaT('TimeZone',x_lims(1).TimeZone);
nan_data = [nan_time nan_time];
else
nan_data = [NaN NaN];
end
set(hCur, 'XData', nan_data);
set(hText, 'Position', [NaN NaN]);
else
set(gcf, 'WindowButtonMotionFcn', @dragFcn)
dragFcn()
end
end
function dragFcn(varargin)
% Get mouse location
pt = get(gca, 'CurrentPoint');
% Update cursor line position
set(hCur, 'XData', [pt(1), pt(1)]);
% Update cursor text
for idx = 1:length(allLines)
xdata = get(allLines(idx), 'XData');
ydata = get(allLines(idx), 'YData');
if isa(xlim(gca), 'datetime') == 1
if pt(1) >= get_date_xpos(xdata(1)) && pt(1) <= get_date_xpos(xdata(end))
x_lims = xlim(gca);
x = pt(1) + x_lims(1);
data_index = dsearchn(get_date_xpos(xdata'),get_date_xpos(x));
x_nearest = xdata(data_index);
y_nearest = ydata(data_index);
set(hText(idx), 'Position', [pt(1), y_nearest], ...
'String', sprintf('(%s, %0.2f)', datestr(x_nearest), y_nearest));
% y = interp1(get_date_xpos(xdata), ydata, pt(1));
% set(hText(idx), 'Position', [pt(1), y], ...
% 'String', sprintf('(%s, %0.2f)', datestr(pt(1)), y));
else
set(hText(idx), 'Position', [NaN NaN]);
end
else
if pt(1) >= xdata(1) && pt(1) <= xdata(end)
y = interp1(xdata, ydata, pt(1));
set(hText(idx), 'Position', [pt(1), y], ...
'String', sprintf('(%0.2f, %0.2f)', pt(1), y));
else
set(hText(idx), 'Position', [NaN NaN]);
end
end
end
end
function unclickFcn(varargin)
set(gcf, 'WindowButtonMotionFcn', '');
end
function xpos = get_date_xpos(x_value)
ax1 = gca;
% dx_days = diff(ax1.XLim)/24;
x_min = ax1.XLim(1);
xpos = datenum(x_value - x_min);
end
end

Accedi per commentare.

Più risposte (3)

Does the answers from this question help?

2 Commenti

It's not exactly what I had in mind, but is close. I would be happy with a vertical line that I can control with the mouse position that spans multiple plots.
Thanks for the reference to that question, it will get me started.
How to get matrix index from this code?

Accedi per commentare.

How about just using the Pointer property on the figure?
f = figure;
subplot(2,1,1)
plot(rand(5))
subplot(2,1,2);
plot(magic(5))
set(f, 'Pointer', 'fullcrosshair')

4 Commenti

That doesn't span multiple plots.
I'm not sure what you mean. As long as the axes are arranged vertically in a figure (which isn't too unusual) it seems to do what was asked.
I see what you mean, Patrick. On the other hand, it would cover the entire figure rather than just the plots.
It seems that this solution is the easiest and cleanest when the axes are stacked vertically. I'm not entirely sure what the OP meant when he said "compare the peaks and valleys interactively between plots". Is it just visually line things up, or does he want more information like actual numbers.

Accedi per commentare.

Is it possible to display the values in a window (like in datacursormode, "window inside figure" display style)??

Community Treasure Hunt

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

Start Hunting!

Translated by