Hi,
I've got a figure with several subplots. These subplots are located one under the other one. Then, that I want to do is create a vertical cursor to across all subplots and when I'm going to move this appear the current values of all subplots.
How can I develop this?
thanks
regards

 Accepted Answer

Matt Fig
Matt Fig on 14 Feb 2011
This can be done with a little handle graphics. I don't have time right now to make this code perfect (error checking, special cases handling, commenting(!), etc.), but it should give you something of an idea. Run this function then hit the left and right arrow to see what happens.
function [] = data_subplots()
% Link data cursors so that they track accross subplots.
% Create the subplots.
% Alternatively, use GET to create s and L from existing plots.
x = 1:.1:10;
for ii = 1:3
s(ii) = subplot(3,1,ii);
L(ii) = plot(x,sin(x/ii)*ii);
end
% Now start the work.
for ii = 1:length(s)
S.D{ii} = get(L(ii),{'xdata','ydata'});
S.L(ii) = length(S.D{ii}{1});
S.M(ii) = floor(S.L(ii)/2);
S.DF = diff(S.D{ii}{1}(S.M(ii):S.M(ii)+1)); % uniform assum
S.I(ii) = S.M(ii);
S.T(ii) = text(S.D{ii}{1}(S.M(ii)+2),S.D{ii}{2}(S.M(ii)+2),'here');
set(S.T(ii),'string',{['X: ',sprintf('%3.3g',S.D{ii}{1}(S.M(ii)))];...
['Y: ',sprintf('%3.3g',S.D{ii}{2}(S.M(ii)))]},...
'parent',s(ii),'backgroundco',[.8 .8 0]);
set(s(ii),'nextplot','add')
S.F(ii) = plot(s(ii),S.D{ii}{1}(S.M(ii)),S.D{ii}{2}(S.M(ii)),'sk');
set(S.F(ii),'markerfacec','b')
end
set(gcf,'keypressfcn',{@fh_kpfcn,S})
function [] = fh_kpfcn(varargin)
D = varargin{2}.Key;
S = varargin{3};
if strcmp(D,'leftarrow')
for ii = 1:length(S.T)
if S.I(ii)~=1
S.I(ii) = S.I(ii)-1;
set(S.F(ii),'xdata',S.D{ii}{1}(S.I(ii)),...
'ydata',S.D{ii}{2}(S.I(ii)))
set(S.T(ii),'position',...
[S.D{ii}{1}(S.I(ii))+S.DF*2,S.D{ii}{2}(S.I(ii))],...
'string',{['X: ',sprintf('%3.3g',S.D{ii}{1}(S.I(ii)))];...
['Y: ',sprintf('%3.3g',S.D{ii}{2}(S.I(ii)))]})
end
end
elseif strcmp(D,'rightarrow')
for ii = 1:length(S.T)
if S.I(ii)~=S.L(ii)
S.I(ii) = S.I(ii)+1;
set(S.F(ii),'xdata',S.D{ii}{1}(S.I(ii)),...
'ydata',S.D{ii}{2}(S.I(ii)))
set(S.T(ii),'position',...
[S.D{ii}{1}(S.I(ii))+S.DF*2,S.D{ii}{2}(S.I(ii))],...
'string',{['X: ',sprintf('%3.3g',S.D{ii}{1}(S.I(ii)))];...
['Y: ',sprintf('%3.3g',S.D{ii}{2}(S.I(ii)))]})
end
end
end
set(gcf,'keypressfcn',{@fh_kpfcn,S}) % Update the structure.

3 Comments

Mtlb Usr
Mtlb Usr on 15 Feb 2011
Hi Matt,
It's very good!! Although I can't move the datatips that the function has created. These are fixed.
How can I move these data tips at anytime?
regards
Mtlb Usr
Mtlb Usr on 15 Feb 2011
Hi,
I hadn't realized!!! I can move the datatips with by arrow's key of pc.
I think that with this solution I will resolve my question.
Thank you so much
regards
This is awesome, thank you! I extended the function a bit and tried to make it more readable for me (see below).
For others looking for help: Also have a look the solution jiro posted here: http://www.mathworks.com/matlabcentral/answers/1758-crosshairs-or-just-vertical-line-across-linked-axis-plots
It might be more intuitive because it allows clicking instead of using the arrow keys.
Dear MATLAB team, if you read this: please add a simple and intuitive crosshair function that can be easily used and activated, e.g. also in the GUI. Something like used in Grafana: https://play.grafana.org/
Extension of Matt Fig's code:
  • compatibility with datetime x-axis
  • use "shift" + left/right to move around faster
  • variable to adjust offset of the textbox relative to data point (textbox_dx_offset, 2x)
  • created an own function to generates the text so you only need to adjust it once
function [] = data_subplots_2()
% Link data cursors so that they track accross subplots.
% Create the subplots.
% Alternatively, use GET to create s and L from existing plots.
x = 1:.1:10;
for ii = 1:3
subpl(ii) = subplot(3,1,ii);
pl(ii) = plot(x,sin(x/ii)*ii);
end
% Data tool tip
% https://de.mathworks.com/matlabcentral/answers/1308-cursor-line
textbox_dx_offset = 3;
for ii = 1:length(subpl) % for each subplot
S.data_x{ii} = get(pl(ii), 'xdata');
S.data_y{ii} = get(pl(ii), 'ydata');
S.datalength(ii) = length(S.data_x{ii});
S.startindex(ii) = floor(S.datalength(ii) / 2);
S.delta_x = diff(S.data_x{ii}(S.startindex(ii):(S.startindex(ii) + 1))); % assume uniform x-axis data difference
S.index(ii) = S.startindex(ii);
S.text(ii) = text(S.data_x{ii}(S.startindex(ii) + textbox_dx_offset), S.data_y{ii}(S.startindex(ii) + textbox_dx_offset),'here');
set(S.text(ii), 'string', get_tooltip_text(S, ii), 'parent', subpl(ii), 'BackgroundColor', [1 1 1], 'EdgeColor', [0 0 0]);
set(subpl(ii), 'nextplot', 'add')
S.tooltip_point(ii) = plot(subpl(ii), S.data_x{ii}(S.startindex(ii)), S.data_y{ii}(S.startindex(ii)), 'sk');
set(S.tooltip_point(ii), 'markerfacec', 'b')
end
set(gcf, 'keypressfcn', {@key_press_function, S})
% "on key press" function:
function [] = key_press_function(varargin)
key_id = varargin{2}.Key;
S = varargin{3};
if (size(varargin{2}.Modifier, 2) > 0) && (strcmp(varargin{2}.Modifier{1}, 'shift') == 1)
dx_fac = 12;
else
dx_fac = 1;
end
textbox_dx_offset = 3;
if strcmp(key_id, 'leftarrow')
for ii = 1:length(S.text)
if S.index(ii) ~= 1
S.index(ii) = S.index(ii) - dx_fac;
set(S.tooltip_point(ii), 'xdata', S.data_x{ii}(S.index(ii)),...
'ydata', S.data_y{ii}(S.index(ii)))
set(S.text(ii), 'position',...
[get_text_xpos(S.data_x{ii}(S.index(ii)) + S.delta_x * textbox_dx_offset), S.data_y{ii}(S.index(ii))],...
'string', get_tooltip_text(S, ii))
end
end
elseif strcmp(key_id, 'rightarrow')
for ii = 1:length(S.text)
if S.index(ii) ~= S.datalength(ii)
S.index(ii) = S.index(ii) + dx_fac;
set(S.tooltip_point(ii),'xdata',S.data_x{ii}(S.index(ii)),...
'ydata',S.data_y{ii}(S.index(ii)))
set(S.text(ii),'position',...
[get_text_xpos(S.data_x{ii}(S.index(ii)) + S.delta_x * textbox_dx_offset), S.data_y{ii}(S.index(ii))],...
'string', get_tooltip_text(S, ii))
end
end
end
set(gcf,'keypressfcn',{@key_press_function,S}) % Update the structure.
% end of "on key press" function
% "get tooltip text" function:
function text = get_tooltip_text(S, ii)
if isa(S.data_x{ii}(S.index(ii)), 'datetime') == 1
text = {...
['X: ',sprintf('%s', datestr(S.data_x{ii}(S.index(ii))))];...
['Y: ',sprintf('%3.3g', S.data_y{ii}(S.index(ii)))]...
};
else
text = {...
['X: ',sprintf('%3.3g', S.data_x{ii}(S.index(ii)))];...
['Y: ',sprintf('%3.3g', S.data_y{ii}(S.index(ii)))]...
};
end
% end of "get tooltip text" function
% "get x-position" function:
function xpos = get_text_xpos(x_value)
if isa(x_value, 'datetime') == 1
ax1 = gca;
% dx_days = diff(ax1.XLim)/24;
x_min = ax1.XLim(1);
xpos = datenum(x_value - x_min);
else
xpos = x_value;
end
% end of "get tooltip text" function

Sign in to comment.

More Answers (3)

Brett Shoelson
Brett Shoelson on 14 Feb 2011

1 vote

I would create a line object for each axes, and use LINKPROP to move them all (i.e., set their x positions) simultaneously. You might also take a look at Brandon Kuczenski's vline on the File Exchange.
Cheers,
Brett
Jiro Doke
Jiro Doke on 22 Feb 2011

1 vote

I posted a similar solution here.
Paulo Silva
Paulo Silva on 14 Feb 2011
You might find useful tips inside the ginput function
edit ginput
%this function can be found inside ginput
set(fig,'pointer','fullcrosshair'); %horizontal and vertical cursor lines

9 Comments

Mtlb Usr
Mtlb Usr on 14 Feb 2011
Hi Paulo,
I don't understand you. How shall I use ginput() to get a cursor line?
If you put an example it would be perfect.
Thanks
regards
Paulo Silva
Paulo Silva on 14 Feb 2011
for the cursor lines you just need
set(fig,'pointer','fullcrosshair');
fig is the handle for the figure, you can use gcf instead of fig (fig=figure), gcf give the handle of the current figure
for the cursor position inside the figure you can do
get(gcf,'currentpoint')
for the cursor position inside an axis you can use the ginput, it will give you x,y and the button pressed, that's why I told you to look at the ginput code.
Mtlb Usr
Mtlb Usr on 14 Feb 2011
Hi,
Now I understand you.
However, I can't see the point or coordinate that I picked by the cursor.
How could I see?
regards
Paulo Silva
Paulo Silva on 14 Feb 2011
Here's one example, it allows you to mark points of several subplots, the coordinates and button pressed is shown on the command line.
subplot(311)
hold on
subplot(312)
hold on
subplot(313)
hold on
but=1;
while ((but==1))
[x,y,but]=ginput(1)
plot(x,y,'*')
end
Paulo Silva
Paulo Silva on 14 Feb 2011
Another version, now the subplots have different and fixed limits, also the point only gets marked when you press the mouse left button.
subplot(311)
hold on
axis([0 100 -10 10])
subplot(312)
hold on
axis([-100 100 -20 10])
subplot(313)
hold on
axis([100 200 -10 20])
but=1;
while ((but==1))
[x,y,but]=ginput(1)
if but==1
plot(x,y,'*')
end
end
Paulo Silva
Paulo Silva on 14 Feb 2011
if you want to save the coordinates pressed
but=1;xv=[];yv=[];
while ((but==1))
[x,y,but]=ginput(1)
if but==1
xv=[xv x];yv=[yv y];
plot(x,y,'*')
end
end
plot(xv,yv) %you can connect the dots marked
Mtlb Usr
Mtlb Usr on 14 Feb 2011
Hi Paulo,
Your example is near that I want it,but this isn't exactly.
The effect that I need is similar to a data cursor.
Do you understand me?
regards
Paulo Silva
Paulo Silva on 14 Feb 2011
I'm finally understanding but what you want isn't easy, at least for me and my basic matlab skills, maybe someone with more experience might help you.
Here's some websites with info, maybe you can find something useful in them
http://matlab.wikia.com/wiki/MATLAB_Wiki
http://undocumentedmatlab.com
Mtlb Usr
Mtlb Usr on 15 Feb 2011
Hi,
OK, I'm going to visit these web pages.
Thank you
regards

Sign in to comment.

Categories

Asked:

on 14 Feb 2011

Commented:

on 19 Mar 2022

Community Treasure Hunt

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

Start Hunting!