How to get index and color matrix from Data cursor

a = 1 : 10
b = 1.001 : 10.001
c = 1.002 : 10.002
a = [a;b;c];
a = a (:)';
cg = clustergram(a, 'Cluster', 'row', 'DisplayRange', ceil(max(abs(a))), 'ColumnPDist', 'chebychev', 'Linkage', 'complete', 'Colormap',colormap('jet'), 'DisplayRatio', 1/9)
plot(cg)
Then, I click the Data Cursor and select a point.
Is there a way to get the index and color matrix to the workspace?
thanks
Ananiah

 Accepted Answer

"Is there a way to get the index and color matrix to the workspace?"
Here are 5 methods.
Method 1: use getDataTip
getDataTip is a function on the file exchange that extracts the datatip content and handles from a figure, axis, or an array of figures and axes in Matlab r2014 and later. See example file in the link.
Method 2: use getCursorInfo()
Matlab's getCurosrInfo() returns the target and coordinates of each data tip in the figure but not the full text within the data tip.
datacursormode on
dcmObj = datacursormode(gcf);
dcmStruc = getCursorInfo(dcmObj);
Method 3: Use the data tip content menu
If you right-click a data tip and select Export Cursor Data to Workspace to return the target and coordinates of the selected data tip but not the full text within the data tip.
Method 4: Use the script below
This script is a section of getDataTip but only works in recent releases of Matlab that contain a DataTip object.
Unless the Data Tip content are loaded into an output variable, you shouldn't assign variables to the another workspace outside of a function.
Here's a simple script you can run any time from the command window or from a favorite command to capture the content of a Data Tip in the current figure. If the current figure does not have a Data Tip, a message will indicate so. If the current figure has more than one data tip, content from all data tips will be displayed.
% Get *all* datatip handles in figure
dth = findobj(gcf,'Type','DataTip');
if isempty(dth)
fprintf('There are no data tips in current figure.\n')
else
dtArray = [{newline};reshape([{dth.Content}',repelem({newline},numel(dth),1)]',[],1)];
disp(char(vertcat(dtArray{:})))
end
Method 5: Use a ButtonDownFcn and reference the cdata
Datatips are not supported for standalone graphics such as clustergram, heatmap, etc. The datatip can be applied to the underlying image object (see this answer).
Here's a workaround that doesn't rely on datatip.
In short, it assigns a ButtonDownFcn to the main axes and produces a marker and text object when you click on the axes. The text shows the underlying RGB color values and they are also printed to the command window. Right-click the main axes to delete all pseudo data tips.
% Create clustergram
a = 1 : 10;
b = 1.001 : 10.001;
c = 1.002 : 10.002;
a = [a;b;c];
a = a (:)';
cg = clustergram(a, 'Cluster', 'row', ...
'DisplayRange', ceil(max(abs(a))), ...
'ColumnPDist', 'chebychev', ...
'Linkage', 'complete',...
'Colormap','jet',
'DisplayRatio', 1/9);
% Get fig & axis handles
cgfig = findall(0,'type','figure', 'Tag', 'Clustergram');
cgax = findobj(cgfig, 'type','axes','tag','HeatMapAxes');
% Get underlying image handle
cgImg = findall(cgax,'Type','Image','Tag','HeatMapImage');
% Confirm that datatip() is not supported
% datatip(cgax, .333, .5) % not supported as of r2020b
% Create an invisibly axes that lays on top of the main axes
axOverlay = copyobj(cgax, cgfig);
delete(axOverlay.Children)
linkaxes([cgax, axOverlay])
linkprop([cgax, axOverlay], {'Parent','Units',...
'InnerPosition','OuterPosition','Position','View'}); % add more as needed
set(axOverlay,'HandleVisibility','on','Visible','on',...
'HitTest','on','PickableParts','visible',...
'Color','none','Tag','ClustergramOverlay',...
'XTick',[],'YTick',[],'xlabel',[],'ylabel',[],'title',[])
% Set ButtonDownFcn for figure
axOverlay.ButtonDownFcn = {@pseudoDataTip, cg, cgImg, axOverlay};
function pseudoDataTip(~,event,cg, cgImg, axOverlay)
% pseudoDataTip triggered by clicking the invisible axis on top of the clustergram heatmap.
% Add text similar to a data tip to clustergram axis including underlying RGB value.
% * event: Hit event data
% * cg is the clustergram obj
% * cgImg is the handle to the heatmap image within the clustergram obj
% * axOverlay is the invisible axes on top of the clustergram
% Only respond to left-mouse-clicks
if event.Button ~= 1
return
end
% Get mouse coordinate
xy = axOverlay.CurrentPoint(1,1:2);
% Find nearest coordinate to mouse click
xl = xlim(axOverlay);
yl = ylim(axOverlay);
xNorm = round((xy(1)-xl(1))/range(xl) * (size(cgImg.CData,2)-1) + 1);
yNorm = round((xy(2)-yl(1))/range(yl) * (size(cgImg.CData,1)-1) + 1);
% Get RGB value
cdata = cgImg.CData(yNorm,xNorm);
cmap = cg.Colormap;
clim = cgImg.Parent.CLim;
rgbIdx = round((cdata - clim(1))/range(clim) * (size(cmap,1)-1) + 1);
RGB = axOverlay.Colormap(rgbIdx,:);
% add marker and text box to clicked coordinate
hold(cgImg.Parent, 'on')
rgbstr = sprintf('RGB: [%.4g, %.4g, %.4g]', RGB);
fprintf([rgbstr,'\n']) % Print to command window
th = text(cgImg.Parent, xy(1), xy(2)-range(yl)*.03, rgbstr, ...
'VerticalAlignment','Top','HorizontalAlignment','center',...
'BackgroundColor', [.996,.984,.730],'LineStyle','-', ...
'tag','RGBMarker');
plot(cgImg.Parent, xy(1), xy(2), 'ko','Markersize', 8, ...
'MarkerFaceColor',RGB,'tag','RGBMarker')
% Assign cx menu to invisible axes if it wasn't already created
if isempty(axOverlay.ContextMenu)
fig = ancestor(cgImg,'figure');
cm = uicontextmenu(fig);
uimenu(cm, 'Text','Delete all RGB markers',...
'MenuSelectedFcn', @(~,~)delete(findall(cgImg.Parent,'Tag','RGBMarker')));
axOverlay.ContextMenu = cm;
end
end

19 Comments

Thank you for your answer!
When I attempt to use that code (and walk through it with breakpoints) I get "There are no data tips in current figure." all the time.
Looking forward to your reply!
Ananiah
a = 1 : 10
b = 1.001 : 10.001
c = 1.002 : 10.002
a = [a;b;c];
a = a (:)';
cg = clustergram(a, 'Cluster', 'row', 'DisplayRange', ceil(max(abs(a))), 'ColumnPDist', 'chebychev', 'Linkage', 'complete', 'Colormap',colormap('jet'), 'DisplayRatio', 1/9)
f = plot(cg);
%pause and select one point
% Get *all* datatip handles in figure
dth = findobj(gcf,'Type','DataTip');
if isempty(dth)
fprintf('There are no data tips in current figure.\n')
else
dtArray = [{newline};reshape([{dth.Content}',repelem({newline},numel(dth),1)]',[],1)];
disp(char(vertcat(dtArray{:})))
end
That's the message created by fprintf in my answer and it appears when there are no data tips in the figure.
You have to set a data tip before running that script.
Does a data tip exist when you get this message? If so, is the figure containing the data tip the active figure? What release of Matlab are you using?
Yes, I set a data tip before running this script.
And my Matlab version is R2018a.
Since I do not have r2018a running right now, please do the following and report back.
  1. Set a data tip in the figure
  2. Close all figures except the one with the data tip
  3. Show me the result of findall(gcf), you can copy-paste the results from the command window.
Thank you for your patience!
There are my steps about the Data Cursor.
then, I close the Clustergram 1 and select one point.
continue,
And the result of findall(gcf)
By the way, is there a way to avoid manual operation to select points if I want to get indexs and color matrixs of every point's Data Cursor in a Figure into an output variable to the workspace?
Thank you again for your help!
Ananiah
Dear Adam,
I almost find out why I didn't get the information about Data Cursor.
In the result of findobj of my Figure f, there are no type of 'DataTip', also in result of findall. But there is a type named 'PointDataTip'. And in this type, I find a attribute named 'String' which have all the information I need!
However, I can not find the PointDataTip by using findobj even if I using code:set(0,'ShowHiddenHandles','on'). Until I find this: How stupid i am! o(≧口≦)o
Now, there is another question: is there a way to avoid manual operation to select points if I want to get indexs and color matrixs of every point's Data Cursor in a Figure into an output variable to the workspace
I will continue to seek the way to solute this problem. Looking forward to your response!
Ananiah
"findobj does not return graphics objects that have the HandleVisibility property set to 'off'"
Use findall(gcf, 'type', ___) instead of findobj(___).
If that doesn't work, please copy-paste the text (not a screen-shot) of the result for
findall(fig)
It looks like you have 204 objects on the fig and I'd like to see that list.
If do not using code:set(0,'ShowHiddenHandles','on'), the result of findobj like this:
findobj
ans =
6×1 graphics array:
Root
Figure (1)
Axes (HeatMapTitleAxes)
Axes (HeatMapAxes)
Text
Image (HeatMapImage)
Using code:findall(gcf, 'type', 'hggroup') can get the PointDataTip information.
However, I don't know why your codes have different result in my Matlab.
% Get *all* datatip handles in figure
dth = findobj(gcf,'Type','DataTip');
if isempty(dth)
fprintf('There are no data tips in current figure.\n')
else
dtArray = [{newline};reshape([{dth.Content}',repelem({newline},numel(dth),1)]',[],1)];
disp(char(vertcat(dtArray{:})))
end
I worked on this today and will update this comment with an improved solution that works with any release of Matlab after r2014a.
I just gotta test is a bit more tomorrow. Hold tight....
In this way, I can obtain the information I need.
clear;
clc;
a = 1 : 10;
b = 1.001 : 10.001;
c = 1.002 : 10.002;
a = [a;b;c];
a = a (:)';
cg = clustergram(a, 'Cluster', 'row', 'DisplayRange', ceil(max(abs(a))), 'ColumnPDist', 'chebychev', 'Linkage', 'complete', 'Colormap',colormap('jet'), 'DisplayRatio', 1/9)
close all;
f = plot(cg);
%pause and select one point, and I want to make improvements in this area later.
%%
set(0,'ShowHiddenHandles','on')
dth = findobj(gcf,'Type','hggroup');%the type of PointDataTip is hggroup
S = dth.String(1, :);
str2double(regexp(S,'\d*\.?\d*','match'))
S = dth.String(2, :);
str2double(regexp(S,'\d*\.?\d*','match'))
S = dth.String(3, :);
str2double(regexp(S,'\d*\.?\d*','match'))
Now, there is another question bothers me: is there a way to avoid manual operation to select points in this Figure? Looking forward to your idea!
Ananiah
  1. Avoid changing ShowHiddenHandles whenever possible. Those handles are usually hidden for good reason.
  2. After r2018a data tips were changed from including only a PointDataTip object to including that object and a DataTip object. The hggroup type grabs the handle to the PointDataTip obj while the DataTip type grabs the handle to the DataTip obj.
I've updated my answer to include the new function getDataTip from the file exchange. It works with Matlab r2014b to the current release. The function extracts data tip content and handles. See the example file in the link.
Demo:
Index is an nx1 vector of Index values for n-datatips in the current figure.
RGB is an nx3 matrix of RGB values for n-datatips in the current figure.
% Create plot
a = 1 : 10;
b = 1.001 : 10.001;
c = 1.002 : 10.002;
a = [a;b;c];
a = a (:)';
cg = clustergram(a, 'Cluster', 'row', 'DisplayRange', ceil(max(abs(a))), 'ColumnPDist', 'chebychev', 'Linkage', 'complete', 'Colormap',colormap('jet'), 'DisplayRatio', 1/9)
close all;
f = plot(cg);
% MANUALLY PLACE 1 OR MORE DATA TIPS
% Get datatip text and handles
[content, dtHandles] = getDataTips(gcf);
% Extract 2nd row of text in each datatip (the index values)
partialText = cellfun(@(c){c{2}},[content{:}]);
IDXcell = regexp(partialText,'\d+(\.\d+)?','match');
Index = cell2mat(cellfun(@str2double, IDXcell, 'UniformOutput', false))
% Extract 3rd row of text in each datatip (the color values)
partialText = cellfun(@(c){c{3}},[content{:}]);
RGBcell = regexp(partialText,'\d+(\.\d+)?','match');
RGB = cell2mat(cellfun(@str2double, RGBcell, 'UniformOutput', false))
Index =
10.001
1.001
RGB =
0.49804 0 0
0.70196 1 0.29412
"is there a way to avoid manual operation to select points?'
Yes, by using datatip() but it's only supported in r2019b and later. There are undocumented approaches you could search for when working with older releases of Matlab.
I found this:https://www.mathworks.com/matlabcentral/fileexchange/19877-makedatatip, but it does not currently work in my object.
I worked on this today and will update my matlab to version r2019b.
Besides, I looked up the datatip(). I'm afraid that it will not work as I wish because the figure made from codes: plot(cg) is different from plot figure.
I just added Method #5 to my answer and it solves the problem.
Clustergrams use heatmaps and heatmaps are very difficult to modify. They do not support datatips (as of r2020b). Method #5 creates psudo-datatips and prints the RGB values to a text box and the command window.
Dear Adam,
I just read your Method #5, it helped me a lot and gave me a inspiration.
I add a double loop to create coordinates automatically.
a = 1 : 10;
b = 1.001 : 10.001;
c = 1.002 : 10.002;
a = [a;b;c];
a = a (:)';
cg = clustergram(a, 'Cluster', 'row', ...
'DisplayRange', ceil(max(abs(a))), ...
'ColumnPDist', 'chebychev', ...
'Linkage', 'complete',...
'Colormap','jet',...
'DisplayRatio', 1/9);
% Get fig & axis handles
cgfig = findall(0,'type','figure', 'Tag', 'Clustergram');
cgax = findobj(cgfig, 'type','axes','tag','HeatMapAxes');
% Get underlying image handle
cgImg = findall(cgax,'Type','Image','Tag','HeatMapImage');
% Confirm that datatip() is not supported
% datatip(cgax, .333, .5) % not supported as of r2020b
% Create an invisibly axes that lays on top of the main axes
axOverlay = copyobj(cgax, cgfig);
delete(axOverlay.Children)
linkaxes([cgax, axOverlay])
linkprop([cgax, axOverlay], {'Parent','Units',...
'InnerPosition','OuterPosition','Position','View'}); % add more as needed
set(axOverlay,'HandleVisibility','on','Visible','on',...
'HitTest','on','PickableParts','visible',...
'Color','none','Tag','ClustergramOverlay',...
'XTick',[],'YTick',[],'xlabel',[],'ylabel',[],'title',[])
% Set ButtonDownFcn for figure
xl = xlim(axOverlay);
yl = ylim(axOverlay);
for y = round(yl(1)) : 1 : yl(2)-yl(1)
for x = round(xl(1)) : 1 : xl(2)-xl(1)
xy = [x, y];
xNorm = round((xy(1)-xl(1))/range(xl) * (size(cgImg.CData,2)-1) + 1);
yNorm = round((xy(2)-yl(1))/range(yl) * (size(cgImg.CData,1)-1) + 1);
% Get RGB value
cdata = cgImg.CData(yNorm,xNorm);
cmap = cg.Colormap;
clim = cgImg.Parent.CLim;
rgbIdx = round((cdata - clim(1))/range(clim) * (size(cmap,1)-1) + 1);
RGB = axOverlay.Colormap(rgbIdx,:);
hold(cgImg.Parent, 'on')
rgbstr = sprintf('RGB: [%.4g, %.4g, %.4g]', RGB);
fprintf([rgbstr,'\n']) % Print to command window
end
end
However, there are some aspects that need to be improved, such as poor accuracy. I will try to modify it later.
These outputs are different with the display of datatips which I add manually.
Preliminarily considering, I think the reason comes from this part of the code:
rgbIdx = round((cdata - clim(1))/range(clim) * (size(cmap,1)-1) + 1);
RGB = axOverlay.Colormap(rgbIdx,:);
It gets the RGB from Colormap by the ratio of cdata to clim, and the rgbIdx must be a positive integer. So, the RGB is certainly one row of colormap.
Assuming that the A is a 1-by-100 matrix or more, but the colormap is only a 64-by-3 matrix. It is obviously inappropriate to express matrix A with 64-by-3 matrix.
The demo in my answer adds markers with their own colors that can be compared to the background to visually confirm the color selection.
The section of code you mentioned normalizes the CData to match the values to an rgb value which must be an integer row number of the colormap.
The examples you shared that are underlined in yellow and blue look like you're cutting off decimal places. You can include more decimal places by increasing the values in fprintf to something like %.5f
The red underlined example has some other error and I'm not sure how the two different rgb vectors were created.
'The demo in my answer adds markers with their own colors that can be compared to the background to visually confirm the color selection.'
I don't quite understand about that. Could you say something more?
Thanks!
Here are two lines from my answer (Method #5).
RGB = axOverlay.Colormap(rgbIdx,:);
% ...
plot(cgImg.Parent, xy(1), xy(2), 'ko','Markersize', 8, ...
'MarkerFaceColor',RGB,'tag','RGBMarker')
The first line is the 1x3 RGB vector under the area where you clicked.
In the second line, that same 1x3 RGB vector is used to set the color of the pseudo datatip marker.
If you look at the GIF image in Method 5, the circular markers contain a black edge and a colored face that matches the color of the background.

Sign in to comment.

More Answers (0)

Categories

Products

Release

R2018a

Asked:

on 27 Oct 2020

Edited:

on 2 Jan 2025

Community Treasure Hunt

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

Start Hunting!