igvteam / igv.js

Embeddable genomic visualization component based on the Integrative Genomics Viewer
MIT License
641 stars 229 forks source link

feature request: fireEvent when the cursor guide changes #486

Closed paul-shannon closed 4 years ago

paul-shannon commented 6 years ago

We'd like to provide a simple visual way for our users to specify the TSS for a transcript. One possibility would be for our app to register an event handler which tracks any changes to the cursor guide. Perhaps this could be included in the event API which (if I remember right) will be part of igv.js version 1.1?

jrobinso commented 6 years ago

Could you expand a little on how this would work? Would you rather capture a user click on a specified track?

paul-shannon commented 6 years ago

Briefly, the context may be helpful. Our R trena package builds gene regulatory models by combining

1) DNase footprint or ATAC-seq (“regulatory region”) data 2) MotifDb motif/TF annotations 3) gene expression.

“trenaViz” runs igv.js and cytoscape.js in the browser, from R, or within a jupyter notebook. The user needs to specify the region of interest (roi), the target gene and the transcription start site for that gene (or transcript), as well as sources for the regulatory region lookup.

We typically display motif-binding sites for a generously large region, then the user zooms in on a proximal promoter and perhaps some distal enhancer regions as well. Possible transcription factor activity in these regions is evaluated for the model. (As a side note, at some point it would be very helpful to use igv region selection, either interactive or programmatic, to build up and display a list of roi’s, all of which can then be seen in igv, then queried for use in subsequent calculations.)

At present I communicate igv.js locus change events to the R or jupyter notebook hosting environment, so that the hosting environment can, via a simple function call, the coordinates of the currently displayed region.

In order to make selection of the TSS similarly easy, my thought was to also communicate the current cursor guide location (if any) to the R or python hosting environment. There may be other reasons in the future to ask igv.js for a single base location; the interactive cursor you provide seems like a good way to do that.

In the interactive R version, the code looks like this:

tbl.motifs = findMotifsInRegion(trena, roi=getRegionFromIGV(), sources=c(“wellington.brain.footprints”, “new.atacSeq”)) displayRegulatoryRegions(trena, tbl.motifs) geneModel = createGeneModel(trena, targetGene=“AQP4”, tss=getCursorLocation(), tbl.motifs, expression=“mtx.temporalCortex”)

Thus the user could either explicitly supply roi and tss, or query them from the current igv view.

Capturing a user click on a specified track seems like it might be error prone. But maybe I don’t yet grasp the possibilities you imagine.

On Nov 24, 2017, at 10:39 AM, Jim Robinson notifications@github.com wrote:

Could you expand a little on how this would work? Would you rather capture a user click on a specified track?

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub, or mute the thread.

jrobinso commented 6 years ago

Thanks, what I'm missing is how is the selection made? As the user moves the mouse around the cursor position changes, how do you know when the TSS has been selected?

paul-shannon commented 6 years ago

I picture the user moving the cursor to the right spot, then calling a function in R or python to query that current location.

The ipyWidget design uses “traitlets” - a base class for syncing up selected variables between browser and kernel. This forces a reactive programming model, which, to my mind, forces an application level design in which relevant browser state is automatically updated to the kernel. I use this technique to update the kernel’s view of roi whenever locus changes in igv in the browser.

I imagine you are thinking (I had not thought this through) that my approach would cause lots of intermediary transient events to be tracked, and data echoed to the kernel.

So better by far might be an event which fires when the cursor change operations cease rather like the “mouse up” event offered in many event-driven programming settings.

(All message passing between kernel and ipython widgets is asynchronous. This is also true for the websockets I use to connect an R session to igv in the browser. Asynchrony forces extra complexity, including global variables, to emulate a simple functional interface between R/python and the browser. Just a simple python or R “getRegion()” function call, if not synchronous, gets complicated. Which is why I use the admittedly counter-intuitive strategy “browser updates python/R with relevant state” so that local python/R function calls are easy, making use of browser state that has already arrived. Sorry if this is too much information!)

On Nov 24, 2017, at 11:24 AM, Jim Robinson notifications@github.com wrote:

Thansk, what I'm missing is how is the selection made? As the user moves the mouse around the cursor position changes, how do you know when the TSS has been selected?

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub, or mute the thread.

jrobinso commented 6 years ago

Why not just call an igv API function to give you the current cursor position whenevery you want it? There is no API function, but could be.

jrobinso commented 6 years ago

As an alternative, that is, when I get into this events might be easier.

paul-shannon commented 6 years ago

Maybe I am missing something, but as far as I see it, the intrinsically round-trip structure of a function call maps poorly onto asynchronous (websocket) communication.

in R or python:

cPos = getCursorPosition() send async message to browser, specifying callback

def callBack(cPosResponse)

how to route this delayed response back to the initiating function?

# could set a global semaphore that the initiating function waits on,
# when cPosResponse shows up, the semaphore is cleared, and cPosResponse
# is queried out of some globally visible state

It has been much simpler to send browser state to R or python whenever something significant changes.

On Nov 24, 2017, at 11:57 AM, Jim Robinson notifications@github.com wrote:

Why not just call an igv API function to give you the current cursor position whenevery you want it? There is no API function, but could be.

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub, or mute the thread.

jrobinso commented 6 years ago

So the trigger for this is the user running come code, not a UI element on the page?

To register to receive updates through events you will supply a javascript function, which gets called, I know this is obvious just confirming.

paul-shannon commented 6 years ago

Sorry that I have been confusing, probably explaining too much and too little at the same time...

The trigger would not be a user's function call, but a user UI action in the igv browser.

I imagine that the javascript event handler would be set up like this

igvBrowser.on(“cursorGuideMouseUp”, function(referenceFrame, newValue){ browserState.cursorGuideChromLoc = newValue; })

ipyWidget infrastructure would automatically notice that the javascript browserState object was modified, and propagate its new values back to the python kernel. Then, completely independently, whenever the notebook user calls the python function

getCursorGuideLocation()

the latest browserState values would already be present to the python kernel, and “cursorGuideChromLoc” field could be read out. The same logic applies in the R implementation.

On Nov 24, 2017, at 12:53 PM, Jim Robinson notifications@github.com wrote:

So the trigger for this is the user running come code, not a UI element on the page?

To register to receive updates through events you will supply a javascript function, which gets called, I know this is obvious just confirming.

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub, or mute the thread.

jrobinso commented 6 years ago

OK, go it.

jrobinso commented 4 years ago

If still an issue reopen