Cursor "z" value from a regular xy plot including f(z)

I often have data sets that involve x, y, and z data but not necessarily on any kind of grid or square array. Hence, I don't use image plots, and only occasionally contour plots. Instead, I plot y vs. x, set mode to "markers", color the points using f(z), and include a colorscale. See the attached image for a simple example.

How can I quickly see the exact "z" value from a given point when using this format? I can put the cursor on a point, and the info bar shows x and y info but nothing about the z wave I've assigned in f(z). Currently I have to show a table with the Z wave and scroll to the point indicated by the cursor. Ideally I'd like a way to do this just using the GUI, but I'd be open to some procedure especially if I don't have to run it manually every time. Thanks.
You can append the w_y vs w_x trace a second time, but this time around you can use the numerical wave w_z instead of a marker.
You can also offset the position of the text-markers (appended trace) by some number depending on the scale. (ModifyGraph offset(w_y)={4,0}) and ModifyGraph muloffset(w_y)={1.2,1.2} are commands you can take a look).

Attached I have put an example. The example offsets in the bottom axis only.


best,
_sk
graph_offset.png
I'd go for a dedicated panel with the information controlled by a window hook function.
Might be a little work though. Do you need this information for a single window or several windows simultaneously (one global panel or one panel per graph) ?

HJ
It would be good if the info panel could show you Z values from an f(z) wave. I will see if it can be added to Igor 8. Don't hold your breath, though.

In the meantime, use this function in a tag:
#include <Graph Utility Procs>

Function/S fofztagZvalue(string tracename)

    String zcolorinfo = WMGetRECREATIONInfoByKey("zColor(x)", traceinfo("", "junk", 0))
    String wavepath = StringFromList(0, zcolorinfo, ",")
    wavepath = wavepath[1,inf]
    Wave zwave = $wavepath
    return num2str(zwave[tagval(0)])
end

This function needs to be called from dynamic text in a tag attached to the trace in question. Here is tag text I used to get a very simply printout of the Z value:
\{fofztagZvalue("junk")}

That just shows the number in the tag. You may wish to enhance it somewhat. Also, the given function always returns 6-digit precision. You might want to use sprintf to give you more control over the precision and formatting of the returned text.

You can reposition the tag on any data point by holding the option (alt) key down while dragging the tag onto a new point.

John Weeks
WaveMetrics, Inc.
support@wavemetrics.com
No, I won't. I can't figure out a way to get the trace name inside the tag attached to a trace. So the only way to go is to know that when you put the tag on the trace. Here is a function that will put a tag on a given trace at a given point, generating the call to my function inside the tag text:
Function putTagOnTrace(string graphname, string tracename, variable pnum)

    if (strlen(graphname) == 0)
        graphname = WinName(0,1)
    endif
   
    string tagname = UniqueName("fofzTag", 14, 0, graphname)
    Tag/C/N=$tagname/L=1 $tracename, pnum, "\\{fofztagZvalue(\"" + tracename + "\")}"
end


John Weeks
WaveMetrics, Inc.
support@wavemetrics.com
_sk wrote:
You can append the w_y vs w_x trace a second time


Although I don't want all the values displayed on the graph (too messy for most of my graphs), this gave me an idea for a workaround. I can append the Z wave vs. the X wave and hide it. Then I can put both the A and B cursors on the point interest, then switch the B cursor to the hidden Z wave such that its value at least displays in the info box. Kind of a pain, but it's at least a temporary solution.

John, I couldn't get your procedures to work. It seemed that the line WMGetRECREATIONInfoByKey("zColor(y)",traceinfo("","junk",0)) was returning an empty string, and I'm not sure how to dig into that particular function.

A dedicated panel for each graph would be fine for my purpose, especially if you could create or hide it at will. Usually my graphs only contain one trace, if that would simplify anything. Thanks for the help so far.
Let's try again, a little more carefully...
#include <Graph Utility Procs>
 
Function/S fofztagZvalue(string tracename)
 
    String zcolorinfo = WMGetRECREATIONInfoByKey("zColor(x)", traceinfo("", tracename, 0))
    String wavepath = StringFromList(0, zcolorinfo, ",")
    wavepath = wavepath[1,inf]
    Wave zwave = $wavepath
    return num2str(zwave[tagval(0)])
end

And the text for the tag would be
\{fofztagZvalue("replace with your actual trace name")}

My second function works correctly as long as you use the actual trace name, and not "junk". My testing worked, of course, because I didn't change the trace name between copying info from the command line and using that info to compose my function.

John "Red Faced" Weeks
WaveMetrics, Inc.
support@wavemetrics.com
I was bored so I wrote a simple panel solution. I'd say it's pre-beta...
The main function is
InstallDataPanelHook(WindowName, MaxCursors, w_x, w_y, w_d)
with
WindowName: The name of the graph or "" for the topmost one
MaxCursors: The intended maximum number of used cursors
w_x, w_y, w_z: Wave references to the three waves containing the x, y, data triplets

No warranty!

Have fun
HJ

PS: If there are at least two cursors used, the differences between the properties of cursor A and cursor B are displayed as well
PPS: If you need a version that works on 2D waves, just leave a message
DataValue.ipf
Thanks HJDrescher! That panel is a great start. I modified it a bit so that's it's easier for a user to call on the top graph without having to provide WindowName, w_x, w_y, or w_d. I incorporated some of John's code to extract the data wave name. Now the function is launched using just InstallDataPanelHook(MaxCursors) or invoked from the Macros menu. (Also your version didn't work with "" used for WindowName because of the data folder creation under Packages). See the attached ipf if you like.
DataValueUser.ipf
Glad you like it.
Without knowledge of the detailed data structure, such things might happen. However, "" as a value for WindowName was working on my sample experiment if it had a valid top most graph in it... ('valid' is probably the crucial point)
Cheers,
HJ

PS: Found the mistake: "" as an alias for windowname is in the wrong function... --> pre-beta :-)
ajleenheer wrote:
_sk wrote:
You can append the w_y vs w_x trace a second time


Although I don't want all the values displayed on the graph (too messy for most of my graphs), this gave me an idea for a workaround. I can append the Z wave vs. the X wave and hide it. Then I can put both the A and B cursors on the point interest, then switch the B cursor to the hidden Z wave such that its value at least displays in the info box. Kind of a pain, but it's at least a temporary solution.

John, I couldn't get your procedures to work. It seemed that the line WMGetRECREATIONInfoByKey("zColor(y)",traceinfo("","junk",0)) was returning an empty string, and I'm not sure how to dig into that particular function.

A dedicated panel for each graph would be fine for my purpose, especially if you could create or hide it at will. Usually my graphs only contain one trace, if that would simplify anything. Thanks for the help so far.


If you want to make it real nice this is another story all together, but I understood you wanted something that will do the job fast.

There are so many ways of doing what you want, but the best way would have to be made by you.

best,
_sk

This is an example. You start by issuing thunk_make_data() in the command window. Then you hover over the point you want to find the value of and you press the V key on your keyboard. Modify as you wish.

function thunk_make_data()
    string s_winname = "thunkdata"
   
    dowindow $s_winname
    if (v_flag)
        dowindow/k $s_winname
    endif
   
    make/o/n=10 w_a = gnoise(10)
    make/o/n=10 w_b = gnoise(10)
    make/o/n=10 w_c = gnoise(10)
    display/n=$s_winname/k=1 w_a vs w_b
    ModifyGraph/w=$s_winname mode=3,marker=16,msize=5,useMrkStrokeRGB=1;DelayUpdate
    ModifyGraph/w=$s_winname zColor(w_a)={w_c,*,*,Grays,0};DelayUpdate
    ModifyGraph/w=$s_winname width={Aspect,1},height={Aspect,1};DelayUpdate
    ModifyGraph/w=$s_winname mirror=2
   
    setwindow $s_winname, hook(modified)=thunk_hook
end

function thunk_hook(s)
    struct WMWinHookStruct& s
   
    //print s.eventname
    //print s.keycode
    strswitch (s.eventname)
        case "keyboard":
            switch (s.keycode)
                case 118:
                    // pressing the key V
                    string s_traceinfo = TraceFromPixel(s.mouseloc.h, s.mouseloc.v, "WINDOW:"+s.winname+";")
                    variable v_pt = str2num(stringbykey("HITPOINT", s_traceinfo))
                    wave w_c = root:w_c
                    wave w_a = root:w_a
                    wave w_b = root:w_b
                   
                    //print s_traceinfo
                   
                    if (numtype(v_pt) != 2)
                        print "f(z): ", w_c[v_pt]
                        tag/c/n=t1/b=3/f=2/s=3/v=1/X=10/Y=10 w_a, v_pt, num2str(w_c[v_pt])
                    endif
                    break
                case 107:
                    tag/n=t1/k
                    break
               
            endswitch
            break
        case "kill":
            setwindow $(s.winname), hook(modified)=$""
            break
    endswitch
   
end
Here is another version of the hook function. Clicking on a datapoint activates the tag, clicking on empty space kills it:

function thunk_hook(s)
    struct WMWinHookStruct& s
   
    strswitch (s.eventname)
        case "mouseup":
            wave w_c = root:w_c
            wave w_a = root:w_a
            wave w_b = root:w_b
   
            string s_traceinfo = TraceFromPixel(s.mouseloc.h, s.mouseloc.v, "WINDOW:"+s.winname+";")
            variable v_pt = str2num(stringbykey("HITPOINT", s_traceinfo))
                   
            if (numtype(v_pt) != 2)
                //print "f(z): ", w_c[v_pt]
                tag/c/n=t1/b=3/f=2/s=3/v=1/X=10/Y=10 w_a, v_pt, num2str(w_c[v_pt])
            else
                tag/n=t1/k
            endif
            break

        case "kill":
            setwindow $(s.winname), hook(modified)=$""
            break
    endswitch
   
end


best,
_sk
_sk wrote:
Then you hover over the point you want to find the value of and you press the V key on your keyboard...
...
...Here is another version of the hook function. Clicking on a datapoint activates the tag, clicking on empty space kills it


Thanks for the suggestions, sk. I implemented them within HJDrescher's framework, and it all works together now. I like the simplicity of your solution but it doesn't seem to fit within Igor's GUI: the mouseup (click) functionality somewhat interferes with the ability to double-click on a trace to modify its appearance; any click creates/moves the tag. Is there a way to distinguish a double-click from a single click? And on the keyboard hook version to press "V", it works once, but Igor interprets the key press as the start of typing in the command window. Can that shift of window focus be overridden in the hook function?

I appreciate all the help. I'm pretty new at generating custom UI elements in Igor.
ajleenheer,

For me the double click works when the hook is on the mouseup event.
And the keyboard hook works without focus stealing to the command window.
(Win7, IP6.37)

But above all, I am providing these as simple examples which you would have to accommodate to your work case.

Good luck with using Igor ;-)

best,
_sk

Thanks for this function _sk.  This was a good introduction to hooks for me.  It might be useful to note that this works even if you haven't set the f(z) color option.  It just pulls the z-value from a third specified wave.  I did have to change the w_y wave variable into a string variable to get it to work, but this could be due to me using an older version of Igor.

I couldn't get it to work with a set of 2D data that I had plotted up because each curve has the same trace name.  Is there some way around this?