Data Cursors on Line Plot With Spectrogram Subplot

10 views (last 30 days)
There seems to be a bug in the behavior of data cursors when both a line plot and spectrogram plot are used in the same figure window.
After creating a subplot containing a spectrogram, the data cursors for the line plot will show the x value, a time value, and the colorbar value.
clear
close all
clc
fs = 10e9;
t = (0:1/fs:250e-9);
y = sin(2*pi*500e6*t + 20*cos(2*pi*10e6*t));
subplot(2,1,1)
plot(t,y)
subplot(2,1,2)
spectrogram(y,128,64,128,fs)
I believe this bug also applies to all versions that use the new figure window styles.

Accepted Answer

Adam Danz
Adam Danz on 20 Aug 2019
Edited: Adam Danz on 21 Aug 2019
Source of the problem
The following sequence of functions are called when producing the spectrogram() plot.
spectrogram() > pspectrogram() > displayspectrogram() > plotTFR()
Within plotTFR(), the datacursormode (DCM) is set for the entire figure and it contains an UpdateFcn function that returns the frequence, time, and power for each (x,y) coordinate. By default the UpdateFcn is empty. Since the DCM is set for a figure as opposed to axes, the same UpdateFcn is invoked when interacting with the subplot that does not contain time-frequency data.
Swiching the order that the two subplots are created does not have an effect.
Solution 1
After the figure is plotted, you can override the DCM UpdateFcn so that all data tips appear as they normally would by default . To do that, add these 3 lines to the end of the code after the plotting is complete. Now the top subplot will show the (X,Y) coordinates and the bottom subplot will show the (X,Y), index, and RGB values like any other imagesc() plot.
fh = gcf();
DCM = datacursormode(fh);
DCM.UpdateFcn = []; %default
Solution 2
If you want the datatips to appear as (x,y) in the top plot and maintain the Frequency, Time, Power datatips in the bottom plot, you can add a WindowButtonMotionFcn function that determines where the mouse is within the figure. When the mouse is within the upper plot, switch the DCM to (X,Y) mode. When the mouse is in the lower subplot, switch the DCM to Freq,Time,Power mode. One drawback of this method is that it requires you to select the data tip icon within the figure due to the WindowButtonMotionFcn. Below is a fully functional example.
% Create data
fs = 10e9;
t = (0:1/fs:250e-9);
y = sin(2*pi*500e6*t + 20*cos(2*pi*10e6*t));
% Create figure
fh = figure(); % Store figure handle
h(1) = subplot(2,1,1); % Store axis1 handle
plot(t,y);
DCM = datacursormode(fh); % Store the data cursor update function for subplot 1
DCM1Func = DCM.UpdateFcn; % ^ ^ ^
h(2) = subplot(2,1,2); % Store axis2 handle
spectrogram(y,128,64,128,fs);
DCM = datacursormode(fh); % Store the data cursor update function for subplot 2
DCM2Func = DCM.UpdateFcn; % ^ ^ ^
% Assign a callback function that responds to the mouse entering
% the figure area, pass the subplot handles and update functions.
fh.WindowButtonMotionFcn = {@switchDCMFcn,h,{DCM1Func,DCM2Func}};
% Define WindowButtonMotionFcn
function switchDCMFcn(figHand, ~, subHand, DCMFunc)
% This function is invoked when the mouse enters the figure area.
% The first part determines which subplot the mouse is in. If the
% mouse is not within a subplot, the function ends. If the mouse
% is within a subplot it determines which subplot and assigns
% the corresponding data cursor update function to the entire
% figure.
% INPUTS
% figHand: handle to figure
% subHand: cell array of subplot handles. This function is
% only designed to work with 2 subplots.
% DCMFunc: a cell array of data cursor update functions that
% correspond to each subHand.
% Get position of subplots within figure (matching units)
cp = figHand.CurrentPoint; %mouse's current position within fig
figUnits = figHand.Units; %figure units
subUnits = get(subHand,'Units'); % original units for each subplot
set(subHand,'Units',figUnits); %temporarily override subplot units to match fig's units
subplotPos = cell2mat(get(subHand,'Position')); %subplot position within fig (same units as fig)
set(subHand,{'Units'},subUnits); %return original units to subplots
% Which subplot is the mouse 'in' (if any)?
mouseIN = cp(1) >= subplotPos(:,1) & cp(1) <= sum(subplotPos(:,[1,3]),2) & ... %mouse is within the horizontal subplot range
cp(2) >= subplotPos(:,2) & cp(2) <= sum(subplotPos(:,[2,4]),2); % mouse is within the vertical subplot range
if ~any(mouseIN)
% If mouse is not within a subplot, quit now
return
else
% If mouse is within a subplot reassign the corresponding DCM
% fprintf('In %d\n',find(mouseIN)) for troubleshooting
% Get current DCM of figure
currDCM = datacursormode(figHand);
% re-assign DCM
currDCM.UpdateFcn = DCMFunc{mouseIN};
end
end % <-- needed when embedded in a script
  2 Comments
Kyle Young
Kyle Young on 21 Aug 2019
Thank you.
Solution 1 is by far the quickest way to get the cursors to show the correct information.
Solution 2 will be very useful if I get to the point where I would like to create custom datatips.
Adam Danz
Adam Danz on 21 Aug 2019
Agreed. Solution 3 would be to just write your own UpdateFcn which would probably be easier than solution 2.

Sign in to comment.

More Answers (0)

Products


Release

R2019a

Community Treasure Hunt

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

Start Hunting!