From 4ca761e9cc644e165ba89b804dd7a58658c1bc0a Mon Sep 17 00:00:00 2001 From: Kanwei Li Date: Thu, 7 Jan 2010 15:45:15 -0500 Subject: [PATCH] trackster: support BAM visualization with samtools, automatically enable tracks for any datatype with "get_track_type" defined --- datatypes_conf.xml.sample | 4 +- eggs.ini | 2 + lib/galaxy/datatypes/binary.py | 4 +- lib/galaxy/datatypes/coverage.py | 2 - lib/galaxy/datatypes/interval.py | 7 +- lib/galaxy/datatypes/registry.py | 8 ++- lib/galaxy/web/controllers/tracks.py | 38 +++++------ static/scripts/packed/galaxy.panels.js | 2 +- static/scripts/packed/trackster.js | 2 +- static/scripts/trackster.js | 88 ++++++++++++++++++++++---- templates/tracks/browser.mako | 2 +- templates/tracks/new_browser.mako | 4 +- 12 files changed, 117 insertions(+), 46 deletions(-) diff --git a/datatypes_conf.xml.sample b/datatypes_conf.xml.sample index 5b52dc214e2..9a5f4a649f9 100644 --- a/datatypes_conf.xml.sample +++ b/datatypes_conf.xml.sample @@ -3,7 +3,9 @@ - + + + diff --git a/eggs.ini b/eggs.ini index cd2d7df9cef..88d03681a31 100644 --- a/eggs.ini +++ b/eggs.ini @@ -19,6 +19,7 @@ MySQL_python = 1.2.2 pbs_python = 2.9.4 psycopg2 = 2.0.6 pycrypto = 2.0.1 +pysam = 0.1.1 pysqlite = 2.5.6 python_lzo = 1.08 threadframe = 0.2 @@ -74,6 +75,7 @@ MySQL_python = http://superb-west.dl.sourceforge.net/sourceforge/mysql-python/My pbs_python = http://ftp.sara.nl/pub/outgoing/pbs_python-2.9.4.tar.gz psycopg2 = http://initd.org/pub/software/psycopg/PSYCOPG-2-0/psycopg2-2.0.6.tar.gz ftp://ftp-archives.postgresql.org/pub/source/v8.2.6/postgresql-8.2.6.tar.bz2 pycrypto = http://www.amk.ca/files/python/crypto/pycrypto-2.0.1.tar.gz +pysam = http://bitbucket.org/kanwei/kanwei-pysam/get/e3c601a062fd.gz pysqlite = http://pypi.python.org/packages/source/p/pysqlite/pysqlite-2.5.6.tar.gz python_lzo = http://www.oberhumer.com/opensource/lzo/download/LZO-v1/python-lzo-1.08.tar.gz http://www.oberhumer.com/opensource/lzo/download/LZO-v1/lzo-1.08.tar.gz threadframe = http://www.majid.info/python/threadframe/threadframe-0.2.tar.gz diff --git a/lib/galaxy/datatypes/binary.py b/lib/galaxy/datatypes/binary.py index 324a291d801..bea45ee3fdd 100644 --- a/lib/galaxy/datatypes/binary.py +++ b/lib/galaxy/datatypes/binary.py @@ -132,7 +132,9 @@ class Bam( Binary ): return dataset.peek except: return "Binary bam alignments file (%s)" % ( data.nice_size( dataset.get_size() ) ) - + def get_track_type( self ): + return "ReadTrack", "bai" + class Binseq( Binary ): """Class describing a zip archive of binary sequence files""" file_ext = "binseq.zip" diff --git a/lib/galaxy/datatypes/coverage.py b/lib/galaxy/datatypes/coverage.py index c237bb5b2f3..6476ae024f0 100644 --- a/lib/galaxy/datatypes/coverage.py +++ b/lib/galaxy/datatypes/coverage.py @@ -63,6 +63,4 @@ class LastzCoverage( Tabular ): resolution = max( resolution, 1 ) return resolution - def get_track_type( self ): - return "LineTrack" diff --git a/lib/galaxy/datatypes/interval.py b/lib/galaxy/datatypes/interval.py index 7a7bf2e0b75..82502d2cac9 100644 --- a/lib/galaxy/datatypes/interval.py +++ b/lib/galaxy/datatypes/interval.py @@ -325,8 +325,6 @@ class Interval( Tabular ): def get_track_resolution( self, dataset, start, end): return None - def get_track_type( self ): - return "FeatureTrack" class Bed( Interval ): """Tab delimited data in BED format""" @@ -484,6 +482,9 @@ class Bed( Interval ): else: return False return True except: return False + + def get_track_type( self ): + return "FeatureTrack", "interval_index" class _RemoteCallMixin: def _get_remote_call_url( self, redirect_url, site_name, dataset, type, app, base_url ): @@ -904,7 +905,7 @@ class Wiggle( Tabular, _RemoteCallMixin ): resolution = max( resolution, 1 ) return resolution def get_track_type( self ): - return "LineTrack" + return "LineTrack", "array_tree" class CustomTrack ( Tabular ): """UCSC CustomTrack""" diff --git a/lib/galaxy/datatypes/registry.py b/lib/galaxy/datatypes/registry.py index f9522f913a9..f0622220409 100644 --- a/lib/galaxy/datatypes/registry.py +++ b/lib/galaxy/datatypes/registry.py @@ -18,6 +18,7 @@ class Registry( object ): self.datatype_converters = odict() self.datatype_indexers = odict() self.converters = [] + self.available_tracks = [] self.set_external_metadata_tool = None self.indexers = [] self.sniff_order = [] @@ -54,6 +55,8 @@ class Registry( object ): # Use default mime type as per datatype spec mimetype = self.datatypes_by_extension[extension].get_mime() self.mimetypes_by_extension[extension] = mimetype + if hasattr( getattr( module, datatype_class ), "get_track_type" ): + self.available_tracks.append( extension ) if display_in_upload: self.upload_file_formats.append( extension ) for converter in elem.findall( 'converter' ): @@ -206,7 +209,10 @@ class Registry( object ): if not included: self.sniff_order.append(datatype) append_to_sniff_order() - + + def get_available_tracks(self): + return self.available_tracks + def get_mimetype_by_extension(self, ext ): """Returns a mimetype based on an extension""" try: diff --git a/lib/galaxy/web/controllers/tracks.py b/lib/galaxy/web/controllers/tracks.py index 1af13f582fc..a5a1a4884ee 100644 --- a/lib/galaxy/web/controllers/tracks.py +++ b/lib/galaxy/web/controllers/tracks.py @@ -23,35 +23,26 @@ from galaxy.util.bunch import Bunch from galaxy.visualization.tracks.data.array_tree import ArrayTreeDataProvider from galaxy.visualization.tracks.data.interval_index import IntervalIndexDataProvider +from galaxy.visualization.tracks.data.bam import BamDataProvider # Message strings returned to browser messages = Bunch( PENDING = "pending", NO_DATA = "no data", NO_CHROMOSOME = "no chromosome", + NO_CONVERTER = "no converter", DATA = "data", ERROR = "error" ) -# Dataset type required for each track type. This needs to be more flexible, -# there might be multiple types of indexes that suffice for a given track type. -track_type_to_dataset_type = { - "line": "array_tree", - "feature": "interval_index" -} - # Mapping from dataset type to a class that can fetch data from a file of that # type. This also needs to be more flexible. dataset_type_to_data_provider = { "array_tree": ArrayTreeDataProvider, - "interval_index": IntervalIndexDataProvider + "interval_index": IntervalIndexDataProvider, + "bai": BamDataProvider } -# FIXME: hardcoding this for now, but it should be derived from the available -# converters -browsable_types = ( "wig", "bed" ) - - class TracksController( BaseController ): """ Controller for track browser interface. Handles building a new browser from @@ -107,11 +98,12 @@ class TracksController( BaseController ): # Find all datasets in the current history that are of that dbkey # and can be displayed datasets = {} + available_tracks = trans.app.datatypes_registry.get_available_tracks() for dataset in session.query( model.HistoryDatasetAssociation ).filter_by( deleted=False, history_id=trans.history.id ): - if dataset.metadata.dbkey == dbkey and dataset.extension in browsable_types: + if dataset.metadata.dbkey == dbkey and dataset.extension in available_tracks: datasets[dataset.id] = (dataset.extension, dataset.name) # Render the template - return trans.fill_template( "tracks/new_browser.mako", converters=browsable_types, dbkey=dbkey, dbkey_set=dbkey_set, datasets=datasets ) + return trans.fill_template( "tracks/new_browser.mako", available_tracks=available_tracks, dbkey=dbkey, dbkey_set=dbkey_set, datasets=datasets ) @web.expose def browser(self, trans, id, chrom=""): @@ -127,8 +119,10 @@ class TracksController( BaseController ): for t in vis.latest_revision.config['tracks']: dataset_id = t['dataset_id'] dataset = hda_query.get( dataset_id ) + track_type, indexer = dataset.datatype.get_track_type() tracks.append( { - "type": dataset.datatype.get_track_type(), + "type": track_type, + "indexer": indexer, "name": dataset.name, "dataset_id": dataset.id } ) @@ -184,14 +178,14 @@ class TracksController( BaseController ): return manifest @web.json - def data( self, trans, dataset_id, track_type, chrom, low, high, **kwargs ): + def data( self, trans, dataset_id, indexer, chrom, low, high, **kwargs ): """ Called by the browser to request a block of data """ # Load the requested dataset dataset = trans.sa_session.query( trans.app.model.HistoryDatasetAssociation ).get( dataset_id ) # No dataset for that id - if not dataset: + if not dataset or not chrom: return messages.NO_DATA # Dataset is in error state, can't display if dataset.state == trans.app.model.Job.states.ERROR: @@ -200,12 +194,11 @@ class TracksController( BaseController ): if dataset.state != trans.app.model.Job.states.OK: return messages.PENDING # Determine what to return based on the type of track being drawn. - converted_dataset_type = track_type_to_dataset_type[track_type] + converted_dataset_type = indexer converted_dataset = self.__dataset_as_type( trans, dataset, converted_dataset_type ) - # If at this point we still don't have an `array_tree_dataset`, there - # is no way we can display this data as an array tree if not converted_dataset: - return messages.ERROR + # No converter + return messages.NO_CONVERTER # Need to check states again for the converted version if converted_dataset.state == model.Dataset.states.ERROR: return messages.ERROR @@ -228,6 +221,7 @@ class TracksController( BaseController ): converted dataset (possibly new) is returned, if it cannot be converted, None is returned. """ + log.debug("Inside dataset as type") # Already of correct type if dataset.extension == type: return dataset diff --git a/static/scripts/packed/galaxy.panels.js b/static/scripts/packed/galaxy.panels.js index 92130937ef2..f7dd62de8f4 100644 --- a/static/scripts/packed/galaxy.panels.js +++ b/static/scripts/packed/galaxy.panels.js @@ -1 +1 @@ -function ensure_dd_helper(){if($("#DD-helper").length==0){$("
").css({background:"white",opacity:0,zIndex:9000,position:"absolute",top:0,left:0,width:"100%",height:"100%"}).appendTo("body").hide()}}function make_left_panel(h,c,e){var g=false;var f=null;var d=function(i){var j=i;if(i<0){i=0}$(h).css("width",i);$(e).css("left",j);$(c).css("left",i+7);if(document.recalc){document.recalc()}};var a=function(){if(g){$(e).removeClass("hover");$(e).animate({left:f},"fast");$(h).css("left",-f).show().animate({left:0},"fast",function(){d(f);$(e).removeClass("hidden")});g=false}else{f=$(e).position().left;$(c).css("left",$(e).innerWidth());if(document.recalc){document.recalc()}$(e).removeClass("hover");$(h).animate({left:-f},"fast");$(e).animate({left:-1},"fast",function(){$(this).addClass("hidden")});g=true}};$(e).hover(function(){$(this).addClass("hover")},function(){$(this).removeClass("hover")}).bind("dragstart",function(i){$("#DD-helper").show()}).bind("dragend",function(i){$("#DD-helper").hide()}).bind("drag",function(i){x=i.offsetX;x=Math.min(400,Math.max(100,x));if(g){$(h).css("left",0);$(e).removeClass("hidden");g=false}d(x)}).bind("dragclickonly",function(i){a()}).find("div").show();var b=function(i){if((g&&i=="show")||(!g&&i=="hide")){a()}};return{force_panel:b}}function make_right_panel(a,e,h){var j=false;var g=false;var c=null;var d=function(k){$(a).css("width",k);$(e).css("right",k+9);$(h).css("right",k).css("left","");if(document.recalc){document.recalc()}};var i=function(){if(j){$(h).removeClass("hover");$(h).animate({right:c},"fast");$(a).css("right",-c).show().animate({right:0},"fast",function(){d(c);$(h).removeClass("hidden")});j=false}else{c=$(document).width()-$(h).position().left-$(h).outerWidth();$(e).css("right",$(h).innerWidth()+1);if(document.recalc){document.recalc()}$(h).removeClass("hover");$(a).animate({right:-c},"fast");$(h).animate({right:-1},"fast",function(){$(this).addClass("hidden")});j=true}g=false};var b=function(k){var l=$(e).width()-(j?c:0);if(l").text(b).click(g));a.append(" ")});a.show()}else{a.hide()}var a=$(".dialog-box").find(".extra_buttons").html("");if(d){$.each(d,function(b,g){a.append($("
").css({position:"absolute",left:f-1}));a+=g}this.content_div.children(":first").remove();this.content_div.append(b)}});var LineTrack=function(c,b,a){this.tile_cache=new Cache(CACHED_TILES_LINE);Track.call(this,c,$("#viewport"));TiledTrack.call(this);this.track_type="line";this.height_px=(a?a:100);this.container_div.addClass("line-track");this.dataset_id=b;this.data_queue={};this.cache=new Cache(CACHED_DATA)};$.extend(LineTrack.prototype,TiledTrack.prototype,{init:function(){var a=this;a.content_div.text(DATA_LOADING);$.getJSON(data_url,{stats:true,track_type:a.track_type,chrom:a.view.chrom,low:null,high:null,dataset_id:a.dataset_id},function(c){if(!c||c=="error"){a.container_div.addClass("error");a.content_div.text(DATA_ERROR)}else{if(c=="no data"){a.container_div.addClass("nodata");a.content_div.text(DATA_NONE)}else{if(c=="pending"){a.container_div.addClass("pending");a.content_div.text(DATA_PENDING);setTimeout(function(){a.init()},5000)}else{a.content_div.text("");a.content_div.css("height",a.height_px+"px");a.min_value=c.min;a.max_value=c.max;a.vertical_range=a.max_value-a.min_value;var d=$("
"+a.min_value+"
");var b=$("
"+a.max_value+"
");b.css({position:"relative",top:"35px"});b.prependTo(a.container_div);d.css({position:"relative",top:a.height_px+32+"px"});d.prependTo(a.container_div);a.draw()}}}})},get_data:function(d,b){var c=this,a=b*DENSITY*d,f=(b+1)*DENSITY*d,e=d+"_"+b;if(!c.data_queue[e]){c.data_queue[e]=true;$.getJSON(data_url,{track_type:this.track_type,chrom:this.view.chrom,low:a,high:f,dataset_id:this.dataset_id,resolution:this.view.resolution},function(g){c.cache.set(e,g);delete c.data_queue[e];c.draw()})}},draw_tile:function(d,a,m,o){if(!this.vertical_range){return}var h=a*DENSITY*d,b=DENSITY*d,c=$(""),l=d+"_"+a;if(!this.cache.get(l)){this.get_data(d,a);return}var g=this.cache.get(l);c.css({position:"absolute",top:0,left:(h-this.view.low)*o});c.get(0).width=Math.ceil(b*o);c.get(0).height=this.height_px;var n=c.get(0).getContext("2d");var e=false;n.beginPath();for(var f=0;f").get(0).getContext("2d"),f=this.view.max_low;for(var g=0,h=b.length;gthis.show_labels_scale){if(!this.showing_labels){this.showing_labels=true}for(var B in this.data_cache.obj_cache){var p=B.split("_"),e=p[0],d=p[1];if(e<=G&&d>=c){K=this.data_cache.get(B);break}}if(!K){this.data_queue[[G,c]]=true;this.get_data(G,c);return}h=this.incremental_slots(this.view.zoom_res,K)*this.vertical_gap+15;u=this.inc_slots[this.view.zoom_res]}else{if(this.showing_labels){this.showing_labels=false}h=this.height_px;u=this.zo_slots;K=this.values}var y=Math.ceil(v*r),x=$("");x.css({position:"absolute",top:0,left:(G-this.view.low)*r-this.left_offset});x.get(0).width=y+this.left_offset;x.get(0).height=h;var z=x.get(0).getContext("2d");z.fillStyle=this.base_color;z.font=this.default_font;z.textAlign="right";var C=0;for(var D=0,E=K.length;D=G){var g=Math.floor(Math.max(0,(m.start-G)*r)),l=Math.ceil(Math.min(y,(m.end-G)*r)),f=u[m.uid]*this.vertical_gap;var a,L,b=null,s=null;if(m.thick_start&&m.thick_end){b=Math.floor(Math.max(0,(m.thick_start-G)*r));s=Math.ceil(Math.min(y,(m.thick_end-G)*r))}if(!this.showing_labels){z.fillRect(g+this.left_offset,f+5,l-g,1)}else{if(z.fillText&&m.start>G){z.fillText(m.name,g-1+this.left_offset,f+8)}var I=m.blocks;if(I){if(m.strand){if(m.strand=="+"){z.fillStyle=RIGHT_STRAND}else{if(m.strand=="-"){z.fillStyle=LEFT_STRAND}}z.fillRect(g+this.left_offset,f,l-g,10);z.fillStyle=this.base_color}for(var B=0,J=I.length;BH){continue}a=5;L=3;z.fillRect(o+this.left_offset,f+L,H-o,a);if(b&&(ob)){a=9;L=1;var w=Math.max(o,b),t=Math.min(H,s);z.fillRect(w+this.left_offset,f+L,t-w,a)}}}else{a=9;L=1;z.fillRect(g+this.left_offset,f+L,l-g,a);if(m.strand){if(m.strand=="+"){z.fillStyle=RIGHT_STRAND_INV}else{if(m.strand=="-"){z.fillStyle=LEFT_STRAND_INV}}z.fillRect(g+this.left_offset,f,l-g,10);z.fillStyle=this.base_color}}}C++}}n.append(x);return x}}); \ No newline at end of file +var DEBUG=false;var DENSITY=1000,FEATURE_LEVELS=100,DATA_ERROR="There was an error in indexing this dataset.",DATA_NONE="No data for this chrom/contig.",DATA_PENDING="Currently indexing... please wait",DATA_LOADING="Loading data...",CACHED_TILES_FEATURE=10,CACHED_TILES_LINE=30,CACHED_DATA=20,CONTEXT=$("").get(0).getContext("2d"),RIGHT_STRAND,LEFT_STRAND;var right_img=new Image();right_img.src="../images/visualization/strand_right.png";right_img.onload=function(){RIGHT_STRAND=CONTEXT.createPattern(right_img,"repeat")};var left_img=new Image();left_img.src="../images/visualization/strand_left.png";left_img.onload=function(){LEFT_STRAND=CONTEXT.createPattern(left_img,"repeat")};var right_img_inv=new Image();right_img_inv.src="../images/visualization/strand_right_inv.png";right_img_inv.onload=function(){RIGHT_STRAND_INV=CONTEXT.createPattern(right_img_inv,"repeat")};var left_img_inv=new Image();left_img_inv.src="../images/visualization/strand_left_inv.png";left_img_inv.onload=function(){LEFT_STRAND_INV=CONTEXT.createPattern(left_img_inv,"repeat")};function commatize(b){b+="";var a=/(\d+)(\d{3})/;while(a.test(b)){b=b.replace(a,"$1,$2")}return b}var Cache=function(a){this.num_elements=a;this.obj_cache={};this.key_ary=[]};$.extend(Cache.prototype,{get:function(b){var a=this.key_ary.indexOf(b);if(a!=-1){this.key_ary.splice(a,1);this.key_ary.push(b)}return this.obj_cache[b]},set:function(b,c){if(!this.obj_cache[b]){if(this.key_ary.length>=this.num_elements){var a=this.key_ary.shift();delete this.obj_cache[a]}this.key_ary.push(b)}this.obj_cache[b]=c;return c}});var View=function(b,a){this.chrom=b;this.tracks=[];this.max_low=0;this.max_high=a;this.center=(this.max_high-this.max_low)/2;this.span=this.max_high-this.max_low;this.zoom_factor=3;this.zoom_level=0};$.extend(View.prototype,{add_track:function(a){a.view=this;this.tracks.push(a);if(a.init){a.init()}},redraw:function(){var d=this.span/Math.pow(this.zoom_factor,this.zoom_level),b=this.center-(d/2),e=b+d;if(b<0){b=0;e=b+d}else{if(e>this.max_high){e=this.max_high;b=e-d}}this.low=Math.floor(b);this.high=Math.ceil(e);this.center=Math.round(this.low+(this.high-this.low)/2);this.resolution=Math.pow(10,Math.ceil(Math.log((this.high-this.low)/DENSITY)/Math.LN10));this.zoom_res=Math.max(1,Math.ceil(Math.log(this.resolution,FEATURE_LEVELS)/Math.log(FEATURE_LEVELS)));$("#overview-box").css({left:(this.low/this.span)*$("#overview-viewport").width(),width:Math.max(12,((this.high-this.low)/this.span)*$("#overview-viewport").width())}).show();$("#low").val(commatize(this.low));$("#high").val(commatize(this.high));for(var c=0,a=this.tracks.length;c").text(this.name);this.content_div=$("
");this.container_div=$("
").append(this.header_div).append(this.content_div);this.parent_element.append(this.container_div)}});var TiledTrack=function(){};$.extend(TiledTrack.prototype,Track.prototype,{draw:function(){var j=this.view.low,e=this.view.high,f=e-j,d=this.view.resolution;if(DEBUG){$("#debug").text(d+" "+this.view.zoom_res)}var l=$("
");this.content_div.children(":first").remove();this.content_div.append(l);var m=this.content_div.width()/f;var i=20;var h;var a=Math.floor(j/d/DENSITY);while((a*DENSITY*d)
");while(a"+commatize(a)+"").css({position:"absolute",left:f-1}));a+=g}this.content_div.children(":first").remove();this.content_div.append(b)}});var LineTrack=function(c,b,d,a){this.tile_cache=new Cache(CACHED_TILES_LINE);Track.call(this,c,$("#viewport"));TiledTrack.call(this);this.indexer=d;this.height_px=(a?a:100);this.container_div.addClass("line-track");this.dataset_id=b;this.data_queue={};this.cache=new Cache(CACHED_DATA)};$.extend(LineTrack.prototype,TiledTrack.prototype,{init:function(){var a=this;a.content_div.text(DATA_LOADING);$.getJSON(data_url,{stats:true,indexer:a.indexer,chrom:a.view.chrom,low:null,high:null,dataset_id:a.dataset_id},function(c){if(!c||c=="error"){a.container_div.addClass("error");a.content_div.text(DATA_ERROR)}else{if(c=="no data"){a.container_div.addClass("nodata");a.content_div.text(DATA_NONE)}else{if(c=="pending"){a.container_div.addClass("pending");a.content_div.text(DATA_PENDING);setTimeout(function(){a.init()},5000)}else{a.content_div.text("");a.content_div.css("height",a.height_px+"px");a.min_value=c.min;a.max_value=c.max;a.vertical_range=a.max_value-a.min_value;var d=$("
"+a.min_value+"
");var b=$("
"+a.max_value+"
");b.css({position:"relative",top:"35px"});b.prependTo(a.container_div);d.css({position:"relative",top:a.height_px+32+"px"});d.prependTo(a.container_div);a.draw()}}}})},get_data:function(d,b){var c=this,a=b*DENSITY*d,f=(b+1)*DENSITY*d,e=d+"_"+b;if(!c.data_queue[e]){c.data_queue[e]=true;$.getJSON(data_url,{indexer:this.indexer,chrom:this.view.chrom,low:a,high:f,dataset_id:this.dataset_id,resolution:this.view.resolution},function(g){c.cache.set(e,g);delete c.data_queue[e];c.draw()})}},draw_tile:function(d,a,m,o){if(!this.vertical_range){return}var h=a*DENSITY*d,b=DENSITY*d,c=$(""),l=d+"_"+a;if(!this.cache.get(l)){this.get_data(d,a);return}var g=this.cache.get(l);c.css({position:"absolute",top:0,left:(h-this.view.low)*o});c.get(0).width=Math.ceil(b*o);c.get(0).height=this.height_px;var n=c.get(0).getContext("2d");var e=false;n.beginPath();for(var f=0;f").get(0).getContext("2d"),f=this.view.max_low;for(var g=0,h=b.length;gthis.show_labels_scale){if(!this.showing_labels){this.showing_labels=true}for(var B in this.data_cache.obj_cache){var p=B.split("_"),e=p[0],d=p[1];if(e<=G&&d>=c){K=this.data_cache.get(B);break}}if(!K){this.data_queue[[G,c]]=true;this.get_data(G,c);return}h=this.incremental_slots(this.view.zoom_res,K)*this.vertical_gap+15;u=this.inc_slots[this.view.zoom_res]}else{if(this.showing_labels){this.showing_labels=false}h=this.height_px;u=this.zo_slots;K=this.values}var y=Math.ceil(v*r),x=$("");x.css({position:"absolute",top:0,left:(G-this.view.low)*r-this.left_offset});x.get(0).width=y+this.left_offset;x.get(0).height=h;var z=x.get(0).getContext("2d");z.fillStyle=this.base_color;z.font=this.default_font;z.textAlign="right";var C=0;for(var D=0,E=K.length;D=G){var g=Math.floor(Math.max(0,(m.start-G)*r)),l=Math.ceil(Math.min(y,(m.end-G)*r)),f=u[m.uid]*this.vertical_gap;var a,L,b=null,s=null;if(m.thick_start&&m.thick_end){b=Math.floor(Math.max(0,(m.thick_start-G)*r));s=Math.ceil(Math.min(y,(m.thick_end-G)*r))}if(!this.showing_labels){z.fillRect(g+this.left_offset,f+5,l-g,1)}else{if(z.fillText&&m.start>G){z.fillText(m.name,g-1+this.left_offset,f+8)}var I=m.blocks;if(I){if(m.strand){if(m.strand=="+"){z.fillStyle=RIGHT_STRAND}else{if(m.strand=="-"){z.fillStyle=LEFT_STRAND}}z.fillRect(g+this.left_offset,f,l-g,10);z.fillStyle=this.base_color}for(var B=0,J=I.length;BH){continue}a=5;L=3;z.fillRect(o+this.left_offset,f+L,H-o,a);if(b&&(ob)){a=9;L=1;var w=Math.max(o,b),t=Math.min(H,s);z.fillRect(w+this.left_offset,f+L,t-w,a)}}}else{a=9;L=1;z.fillRect(g+this.left_offset,f+L,l-g,a);if(m.strand){if(m.strand=="+"){z.fillStyle=RIGHT_STRAND_INV}else{if(m.strand=="-"){z.fillStyle=LEFT_STRAND_INV}}z.fillRect(g+this.left_offset,f,l-g,10);z.fillStyle=this.base_color}}}C++}}n.append(x);return x}});var ReadTrack=function(c,b,d,a){this.tile_cache=new Cache(CACHED_TILES_FEATURE);Track.call(this,c,$("#viewport"));TiledTrack.call(this);FeatureTrack.call(this,c,b,d,a);this.default_font="9px Monaco, Lucida Console, monospace"};$.extend(ReadTrack.prototype,TiledTrack.prototype,FeatureTrack.prototype,{draw_tile:function(w,A,n,o){if(!this.values){return}var B=A*DENSITY*w,f=(A+1)*DENSITY*w,r=DENSITY*w;var E,q,k;k=this.height_px;q=this.zo_slots;E=this.values;var u=Math.ceil(r*o),s=$("");var b=$("").get(0).getContext("2d");s.css({position:"absolute",top:0,left:(B-this.view.low)*o-this.left_offset});s.get(0).width=u+this.left_offset;s.get(0).height=k;var v=s.get(0).getContext("2d");v.fillStyle=this.base_color;v.font=this.default_font;v.textAlign="right";var t=v.measureText("A").width;var x=0;for(var y=0,z=E.length;y=B){var h=Math.floor(Math.max(0,(m.start-B)*o)),l=Math.ceil(Math.min(u,(m.end-B)*o)),g=q[m.uid]*this.vertical_gap;var a,F,e=null,p=null;if(o>t){for(var C=0,d=m.name.length;C"); + + var dummy_canvas = $("").get(0).getContext("2d"); + + new_canvas.css({ + position: "absolute", + top: 0, + left: ( tile_low - this.view.low ) * w_scale - this.left_offset + }); + new_canvas.get(0).width = width + this.left_offset; + new_canvas.get(0).height = required_height; + // console.log(( tile_low - this.view.low ) * w_scale, tile_index, w_scale); + var ctx = new_canvas.get(0).getContext("2d"); + ctx.fillStyle = this.base_color; + ctx.font = this.default_font; + ctx.textAlign = "right"; + var px_per_char = ctx.measureText("A").width; + + var j = 0; + for (var i = 0, len = data.length; i < len; i++) { + var feature = data[i]; + if (feature.start <= tile_high && feature.end >= tile_low) { + var f_start = Math.floor( Math.max(0, (feature.start - tile_low) * w_scale) ), + f_end = Math.ceil( Math.min(width, (feature.end - tile_low) * w_scale) ), + y_center = slots[feature.uid] * this.vertical_gap; + + var thickness, y_start, thick_start = null, thick_end = null; + if (w_scale > px_per_char) { + for (var c = 0, str_len = feature.name.length; c < str_len; c++) { + var c_start = Math.floor( Math.max(0, (feature.start + c - tile_low) * w_scale) ); + ctx.fillText(feature.name[c], c_start + this.left_offset, y_center + 8); + } + } else { + ctx.fillRect(f_start + this.left_offset, y_center + 4, f_end - f_start, 3); + } + } + } + + parent_element.append( new_canvas ); + return new_canvas; + } +}); diff --git a/templates/tracks/browser.mako b/templates/tracks/browser.mako index ef3cbe957e9..86d132718c1 100644 --- a/templates/tracks/browser.mako +++ b/templates/tracks/browser.mako @@ -20,7 +20,7 @@ ${h.js( "jquery", "jquery.event.drag", "jquery.mousewheel", "trackster" )} view.add_track( new LabelTrack( $("#nav-labeltrack" ) ) ); %for track in tracks: - view.add_track( new ${track["type"]}( "${track['name']}", ${track['dataset_id']} ) ); + view.add_track( new ${track["type"]}( "${track['name']}", ${track['dataset_id']}, "${track['indexer']}" ) ); %endfor $(document).bind( "redraw", function( e ) { diff --git a/templates/tracks/new_browser.mako b/templates/tracks/new_browser.mako index 9c55b75728f..e1573a2e5c0 100644 --- a/templates/tracks/new_browser.mako +++ b/templates/tracks/new_browser.mako @@ -11,7 +11,7 @@ $( function() { -% if not converters: +% if not available_tracks:
There are no available converters needed for visualization. Please verify that your tool_conf.xml file contains converters for datatypes (see tool_conf.xml.sample) for examples. @@ -46,7 +46,7 @@ $( function() {
- + %for dataset_id, (dataset_ext, dataset_name) in datasets.iteritems():