Deploying JBrowse as a Catalyst App

Converting JBrowse into a Catalyst Application

This is my first post, and I apologize for the crappy formatting. I will come back and fix it.

Background

JBrowse is a genome viewer written in javascript, html, allow with the usual Bio::Graphics modules. It can be run in the catalyst framework.

This assumes you have Catalyst, Catalyst dependencies, JBrowse, and JBrowse dependencies (mostly perl/bioperl/bio::graphics things) installed.

Why would you want to do such a thing? Several reasons.

  • For fun. :)
  • Catalyst rocks. It is so flexible and perfect for bioinformatics.
  • JBrowse is still fairly early in development and has only minimal user authentication. This won't work for us because we need data access to be authenticated. A single user should only see data they are authorized to see.
  • I want some things that are linked to other things. For instance I have a user with a study of 60 genomes. I want them to log in, be able to view their data (and only their data), and also be able to view SNPs. Then I want to be click that rs# to know how often that particular snp shows up in various populations of the 1000K genomes, or perhaps in that persons individual study.

Setup

Moving files around

Mostly we want are static (css, js, additional perl libs) content in root/static, html/tt2 in root/src, and data in root/static/data.

  1. Create a git repository JBrowse and initialize it somewhere. We'll call this JBrowse. At some point all my data directories will be submodules, but not now.
  2. In the directory above JBrowse run catalyst.pl JBrowse. This gives the basic catalyst files.
  3. Download the stable jbrowse ucsc zip. (1.8 at time of writing) and put it somewhere. We'll call this jbrowse_ucsc. http://jbrowse.org/releases/hg19mini-2.tar.gz
  1. Create directories root/{static,src}, and root/static/{data,css}
  2. In the jbrowse_ucsc directory
    • Copy directories jbrowse_ucsc/{js,jslib,lib,img,jar} to JBrowse/root/static
    • Copy jbrowse_ucsc/genome.css to JBrowse/root/static/css
    • Copy jbrowse_ucsc/data/* to Jbrowse/root/static/data/ucsc
    • Copy jbrowse_ucsc/index.html to JBrowse/root/src/home/index.tt2 (We'll be adding things to it later to make it more templatey.)
    • Copy jbrowse_ucsc/*cur to JBrowse/root/static

cp path/jbrowse_ucsc/{js,jslib,lib,img,jar}  pathto/JBrowse/root/static
cp path/jbrowse_ucsc/genome.css  pathto/JBrowse/root/static/css
cp path/jbrowse_ucsc/data/*  Jbrowse/root/static/data/ucsc
cp path/jbrowse_ucsc/index.html  pathto/JBrowse/root/src/home/index.tt2 
cp path/jbrowse_ucsc/*cur  pathto/JBrowse/root/static 

Editing files

  1. Now we will edit files in our Catalyst JBrowse.
  2. In JBrowse execute command

cd pathto/JBrowse
script/jbrowse_create view HTML TT

  1. Change lib/JBrowse/View/HTML.pm to match

package JBrowse::View::HTML;
use Moose;
use namespace::autoclean;

extends 'Catalyst::View::TT';

_PACKAGE_->config( TEMPLATE_EXTENSION => '.tt2', render_die => 1, INCLUDE_PATH => [ JBrowse->path_to( 'root', 'src' ), JBrowse->path_to( 'root', 'static' ), ], );
  1. Now for some static content delivery. I did not document this part so well but for nearly every pathname or url we are adding static to it.
    • root/static/js/GenomeView.js change the lines to match the following.

GenomeView.js:        view.elem.style.cursor = "url(\"/static/openhand.cur\"), move";
GenomeView.js:  document.body.style.cursor = "url(\"/static/closedhand.cur\"), move";
GenomeView.js:  view.elem.style.cursor = "url(\"/static/closedhand.cur\"), move";

  • root/static/js/Browser.js change the lines to match the folliwng.

Browser.js:235:        + "/static/img/right_arrow.png\"/> to view)

"; Browser.js:576: moveLeft.src = browserRoot + "/static/img/slide-left.png"; Browser.js:589: moveRight.src = browserRoot + "/static/img/slide-right.png"; Browser.js:604: bigZoomOut.src = browserRoot + "/static/img/zoom-out-2.png"; Browser.js:617: zoomOut.src = browserRoot + "/static/img/zoom-out-1.png"; Browser.js:630: zoomIn.src = browserRoot + "/static/img/zoom-in-1.png"; Browser.js:643: bigZoomIn.src = browserRoot + "/static/img/zoom-in-2.png";
  • root/static/lib/ImageViewer.pm change the lines to match the following.

ImageTrackRenderer.pm:64:    my $self = { 'datadir' => "/static/data",

  • root/static/css/genome.css change the lines to match the following.

genome.css:242:.plus-feature { background-image: url('/static/img/plus-chevron3.png'); }
genome.css:244:.minus-feature { background-image: url('/static/img/minus- chevron3.png'); }
genome.css:265:.plus-feature2 { background-image: url('/static/img/plus-herringbone16.png'); }
genome.css:267:.minus-feature2 { background-image: url('/static/img/minus-herringbone16.png'); }
genome.css:288:.plus-feature3 { background-image: url('/static/img/plus-chevron.png'); }
genome.css:290:.minus-feature3 { background-image: url('/static/img/minus-chevron.png'); }
genome.css:311:.plus-feature4 { background-image: url('/static/img/plus-pacman.png'); }
genome.css:313:.minus-feature4 { background-image: url('/static/img/minus-pacman.png'); }
genome.css:334:.plus-feature5 { background-image: url('/static/img/plus-chevron2.png'); }
genome.css:336:.minus-feature5 { background-image: url('/static/img/minus-chevron2.png'); }
genome.css:393:    background-image: url('/static/img/dblhelix-red.png');
genome.css:413:    background-image: url('/static/img/helix3-green.png');
genome.css:432:    background-image: url('/static/img/loops.png');
genome.css:446:.plus-cds0 { background-image: url('/static/img/plus-cds0.png'); }
genome.css:447:.plus-cds1 { background-image: url('/static/img/plus-cds1.png'); }
genome.css:448:.plus-cds2 { background-image: url('/static/img/plus-cds2.png'); }
genome.css:449:.minus-cds0 { background-image: url('/static/img/minus-cds0.png'); }
genome.css:450:.minus-cds1 { background-image: url('/static/img/minus-cds1.png'); }
genome.css:451:.minus-cds2 { background-image: url('/static/img/minus-cds2.png'); }
genome.css:582:    background-image: url('/static/img/plus-transcript-head.png');
genome.css:598:    background-image: url('/static/img/minus-transcript-head.png');
genome.css:607:    background-image: url('/static/img/cds.png');

*** I probably missed at least a few paths somewhere, but I'll show you how to find them later.

  1. We also need to make sure our index pages are working the same way. Edit JBrowse/root/src/home/index.tt2 to match the following.
  2. We are also adding an extra variable to the Browser function, to split our data directories up. Put it in, and we'll get to it later.

<!DOCTYPE html 
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
  <head>
<title>JBrowse</title>
<link rel="stylesheet" type="text/css" href="[% c.uri_for('/static/jslib/dijit/themes/tundra/tundra.css') %]"></link>
<link rel="stylesheet" type="text/css" href="[% c.uri_for('/static/jslib/dojo/resources/dojo.css') %]"></link>
<link rel="stylesheet" type="text/css" href="[% c.uri_for('/static/css/genome.css') %]"></link>
<style type="text/css">
html, body { height: 100%; width: 100%; padding: 0; border: 0; }
</style>
<script type="text/javascript" src="[% c.uri_for('/static/jslib/dojo/dojo.js') %]" djConfig="isDebug: false"></script>
<script type="text/javascript" src="[% c.uri_for('/static/jslib/dojo/jbrowse_dojo.js') %]" ></script>
<!-- js_source_files -->
<!-- the files between the "js_source_files" comments get replaced
when the JS gets minified (see the minification rule in the Makefile) -->
<script type="text/javascript" src="[% c.uri_for('/static/js/Browser.js') %]"></script>
<script type="text/javascript" src="[% c.uri_for('/static/js/Util.js') %]"></script>
<script type="text/javascript" src="[% c.uri_for('/static/js/NCList.js') %]"></script>
<script type="text/javascript" src="[% c.uri_for('/static/js/LazyPatricia.js') %]"></script>
<script type="text/javascript" src="[% c.uri_for('/static/js/LazyArray.js') %]"></script>
<script type="text/javascript" src="[% c.uri_for('/static/js/Track.js') %]"></script>
<script type="text/javascript" src="[% c.uri_for('/static/js/SequenceTrack.js') %]"></script>
<script type="text/javascript" src="[% c.uri_for('/static/js/Layout.js') %]"></script>
<script type="text/javascript" src="[% c.uri_for('/static/js/FeatureTrack.js') %]"></script>
<script type="text/javascript" src="[% c.uri_for('/static/js/UITracks.js') %]"></script>
<script type="text/javascript" src="[% c.uri_for('/static/js/ImageTrack.js') %]"></script>
<script type="text/javascript" src="[% c.uri_for('/static/js/GenomeView.js') %]"></script>
<!-- js_source_files -->
<script type="text/javascript" src="[% c.uri_for('/static/data/track_info/refSeqs.js') %]"></script>
<script type="text/javascript" src="[% c.uri_for('/static/data/track_info/trackInfo.js') %]"></script>
<script type="text/javascript">
/* <![CDATA[ */
var queryParams = dojo.queryToObject(window.location.search.slice(1));
var bookmarkCallback = function(brwsr) {
return window.location.protocol
+ "//" + window.location.host
+ window.location.pathname
+ "?loc=" + brwsr.visibleRegion()
+ "&tracks=" + brwsr.visibleTracks();
}   
var b = new Browser({
datalibs: "ucsc,ucsc_test",
containerID: "GenomeBrowser",
refSeqs: refSeqs,
trackData: trackInfo,
defaultTracks: "DNA,gene,mRNA,noncodingRNA",
location: queryParams.loc,
tracks: queryParams.tracks,
bookmark: bookmarkCallback,
dataRoot: "/static/data/"
}); 
/* ]]> */
</script>
  </head>
  <body>
 </div id="GenomeBrowser" style="height: 100%; width: 100%; padding: 0; border: 0;>
</div>
  </body>
</html>

  1. Lets create a controller to actually display our nice new data.

cd pathto/JBrowse
script/jbrowse_create.pl controller Home

  1. Add a function jbrowse in there to display our index.tt2 file. Just put it somewhere before the _PACKAGE_->meta->make_immutable; statement.

sub jbrowse :Path('jbrowse') :Args(0) {
     my(\(self, \)c) = @_; 
    $c->stash(template => 'home/index.tt2');
}

  1. Now, I don't like the way JBrowse initially sets up its data. It expects everything in the same directory, whereas I want something like
data
-track_info
-ucsc_ref (our reference data)
-data_study1
-data_study2

Where ucsc_ref data is visible to everyone and everything else is based upon login.

  1. Create directories JBrowse/root/static/data/{track_info,ucsc_test}
  2. Copy your data from ucsc into ucsc_test as well. Its only a test to make sure it is reading from the correct directories when you are splitting up data, and you can delete it right afterwards.
  3. Move all the data from jbrowse_ucsc/data/* to JBrowse/root/static/data/ucsc
  4. Move JBrowse/root/static/data/ucsc/{trackInfo.js, refSeqs.js} to JBrowse/root/static/data
  5. Edit JBrowse/root/static/data/track_info/trackInfo.js to match the following.

trackInfo = 
[
{   
"url" : "ucsc/tracks/{refseq}/cytoBand/trackData.json",
"type" : "FeatureTrack",
"label" : "newcytoBand",
"key" : "New Chromosome Band"
},  
{   
"url" : "ucsc/tracks/{refseq}/omimGene/trackData.json",
"type" : "FeatureTrack",
"label" : "newomimGene",
"key" : "New OMIM Genes"
},  
{   
"url" : "ucsc/tracks/{refseq}/knownGene/trackData.json",
"type" : "FeatureTrack",
"label" : "newknownGene",
"key" : "New UCSC Genes"
},  
{   
"url" : "ucsc/tracks/{refseq}/cytoBandIdeo/trackData.json",
"label" : "newcytoBandIdeo",
"type" : "FeatureTrack",
"key" : "New Chromosome Band (Ideogram)"
},  
{   
"url" : "ucsc_test/tracks/{refseq}/cytoBand/trackData.json",
"type" : "FeatureTrack",
"label" : "cytoBand",
"key" : "Chromosome Band"
},  
{   
"url" : "ucsc_test/tracks/{refseq}/omimGene/trackData.json",
"type" : "FeatureTrack",
"label" : "omimGene",
"key" : "OMIM Genes"
},  
{   
"url" : "ucsc_test/tracks/{refseq}/knownGene/trackData.json",
"type" : "FeatureTrack",
"label" : "knownGene",
"key" : "ucsc_test Genes"
},  
{   
"url" : "ucsc_test/tracks/{refseq}/cytoBandIdeo/trackData.json",
"label" : "cytoBandIdeo",
"type" : "FeatureTrack",
"key" : "Chromosome Band (Ideogram)"
}   
]

  1. Now for this data libs part. Open up your JBrowse/root/static/js/Browser.js and in the Browser function (first function of the file) edit this:

//Old 
this.names = new LazyTrie(
dataRoot + "names/lazy-",
/dataRoot + "names/root.json"
);

To match this*****:


//New
for (var i = 0; i < datalibs.length; i++) {
this.names += new LazyTrie(
dataRoot + datalibs[i] + "/names/lazy-",
dataRoot + datalibs[i] + "/names/root.json"
);  
}   

**** I am not at all familiar with javascript, and I haven't tested it to my satisfaction yet. If this is not the correct way to do things and it blows up please let me know.

  1. Let's run the application!
  2. cd into your JBrowse top directory and run script/jbrowse_server.pl -r
  3. go to localhost:3000/home/jbrowse and watch the magic unfold!
  4. I would suggest also opening up your javascript debugger, (Ctrl+Shift+j for me in chromium) while looking at tracks to make sure everything comes up correctly. I'm sure I missed a couple or paths/urls, but you get the general idea.

Leave a comment

About jillian

user-pic I blog about Perl.