diff --git a/lib/galaxy/visualization/tracks/data_providers.py b/lib/galaxy/visualization/tracks/data_providers.py index e150e58a8d6..5e3efcf972b 100644 --- a/lib/galaxy/visualization/tracks/data_providers.py +++ b/lib/galaxy/visualization/tracks/data_providers.py @@ -235,11 +235,13 @@ class BamDataProvider( TracksDataProvider ): else: return None # Encode reads as list of lists; each read is a list with the format - # [, , , , , , ] + # [, , , , , ] # where has the format - # [, , ??] + # [, , , ??] # and has the format - # [, , ??] + # [, , , ??] + # For single-end reads, read has format: + # [, , , , cigar, seq] # NOTE: read end and sequence data are not valid for reads outside of # requested region and should not be used. results = [] @@ -258,32 +260,38 @@ class BamDataProvider( TracksDataProvider ): if read.is_proper_pair: if qname in paired_pending: # one in dict is always first pair = paired_pending[qname] - results.append( [ qname, pair['start'], read.pos + read_len, seq, read.cigar, [pair['start'], pair['end'], pair['seq']], [read.pos, read.pos + read_len, seq] ] ) + results.append( [ "%i_%s" % ( read.pos-start, qname ), + pair['start'], + read.pos + read_len, + qname, + [ pair['start'], pair['end'], pair['cigar'], pair['seq'] ], + [ read.pos, read.pos + read_len, read.cigar, seq] + ] ) del paired_pending[qname] else: paired_pending[qname] = { 'start': read.pos, 'end': read.pos + read_len, 'seq': seq, 'mate_start': read.mpos, 'rlen': read_len, 'cigar': read.cigar } else: - results.append( [qname, read.pos, read.pos + read_len, seq, read.cigar] ) + results.append( [ "%i_%s" % ( read.pos-start, qname ), read.pos, read.pos + read_len, qname, read.cigar, read.seq] ) # Take care of reads whose mates are out of range. for qname, read in paired_pending.iteritems(): if read['mate_start'] < read['start']: # Mate is before read. - start = read['mate_start'] - end = read['end'] + read_start = read['mate_start'] + read_end = read['end'] # Make read_1 start=end so that length is 0 b/c we don't know # read length. - r1 = [read['mate_start'], read['mate_start']] - r2 = [read['start'], read['end'], read['seq']] + r1 = [ read['mate_start'], read['mate_start'] ] + r2 = [ read['start'], read['end'], read['cigar'], read['seq'] ] else: # Mate is after read. - start = read['start'] + read_start = read['start'] # Make read_2 start=end so that length is 0 b/c we don't know # read length. Hence, end of read is start of read_2. - end = read['mate_start'] - r1 = [read['start'], read['end'], read['seq']] - r2 = [read['mate_start'], read['mate_start']] + read_end = read['mate_start'] + r1 = [ read['start'], read['end'], read['cigar'], read['seq'] ] + r2 = [ read['mate_start'], read['mate_start'] ] - results.append( [ qname, start, end, read['seq'], read['cigar'], r1, r2 ] ) + results.append( [ "%i_%s" % ( read_start-start, qname ), read_start, read_end, qname, r1, r2 ] ) bamfile.close() return { 'data': results, 'message': message } @@ -437,6 +445,11 @@ class IntervalIndexDataProvider( TracksDataProvider ): if chrom not in index.indexes and chrom[3:] in index.indexes: chrom = chrom[3:] + # + # Build data to return. Payload format is: + # [ , , , , , ] + # + no_detail = ( "no_detail" in kwargs ) for start, end, offset in index.find(chrom, start, end): if count >= MAX_VALS: message = "Only the first %s features are being displayed." % MAX_VALS @@ -445,16 +458,16 @@ class IntervalIndexDataProvider( TracksDataProvider ): source.seek( offset ) # TODO: can we use column metadata to fill out payload? # TODO: use function to set payload data - if "no_detail" not in kwargs: - if isinstance( self.original_dataset.datatype, Gff ): - # GFF dataset. - reader = GFFReaderWrapper( source, fix_strand=True ) - feature = reader.next() - payload = package_gff_feature( feature ) - payload.insert( 0, offset ) - elif isinstance( self.original_dataset.datatype, Bed ): - # BED dataset. - payload = [ offset, start, end ] + if isinstance( self.original_dataset.datatype, Gff ): + # GFF dataset. + reader = GFFReaderWrapper( source, fix_strand=True ) + feature = reader.next() + payload = package_gff_feature( feature, no_detail ) + payload.insert( 0, offset ) + elif isinstance( self.original_dataset.datatype, Bed ): + # BED dataset. + payload = [ offset, start, end ] + if not no_detail: feature = source.readline().split() length = len(feature) if length >= 4: @@ -472,7 +485,7 @@ class IntervalIndexDataProvider( TracksDataProvider ): block_starts = [ int(n) for n in feature[11].split(',') if n != '' ] blocks = zip( block_sizes, block_starts ) payload.append( [ ( start + block[1], start + block[1] + block[0] ) for block in blocks ] ) - + if length >= 5: payload.append( int(feature[4]) ) # score @@ -564,9 +577,15 @@ def get_data_provider( name=None, original_dataset=None ): pass return data_provider -def package_gff_feature( feature ): +def package_gff_feature( feature, no_detail=False ): """ Package a GFF feature in an array for data providers. """ feature = convert_gff_coords_to_bed( feature ) + + # No detail means only start, end. + if no_detail: + return [ feature.start, feature.end ] + + # Return full feature. payload = [ feature.start, feature.end, feature.name(), diff --git a/static/scripts/packed/trackster.js b/static/scripts/packed/trackster.js index da71b17e905..c663a438b7e 100644 --- a/static/scripts/packed/trackster.js +++ b/static/scripts/packed/trackster.js @@ -1 +1 @@ -var DENSITY=200,FEATURE_LEVELS=10,MAX_FEATURE_DEPTH=50,DEFAULT_DATA_QUERY_WAIT=5000,CONNECTOR_COLOR="#ccc",DATA_ERROR="There was an error in indexing this dataset.",DATA_NOCONVERTER="A converter for this dataset is not installed. Please check your datatypes_conf.xml file.",DATA_NONE="No data for this chrom/contig.",DATA_PENDING="Currently indexing... please wait",DATA_LOADING="Loading data...",FILTERABLE_CLASS="filterable",CACHED_TILES_FEATURE=10,CACHED_TILES_LINE=5,CACHED_DATA=5,DUMMY_CANVAS=document.createElement("canvas"),RIGHT_STRAND,LEFT_STRAND;if(window.G_vmlCanvasManager){G_vmlCanvasManager.initElement(DUMMY_CANVAS)}CONTEXT=DUMMY_CANVAS.getContext("2d");PX_PER_CHAR=CONTEXT.measureText("A").width;var right_img=new Image();right_img.src=image_path+"/visualization/strand_right.png";right_img.onload=function(){RIGHT_STRAND=CONTEXT.createPattern(right_img,"repeat")};var left_img=new Image();left_img.src=image_path+"/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=image_path+"/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=image_path+"/visualization/strand_left_inv.png";left_img_inv.onload=function(){LEFT_STRAND_INV=CONTEXT.createPattern(left_img_inv,"repeat")};function round_1000(a){return Math.round(a*1000)/1000}var Cache=function(a){this.num_elements=a;this.clear()};$.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},clear:function(){this.obj_cache={};this.key_ary=[]}});var View=function(a,d,c,b,e){this.container=a;this.chrom=null;this.vis_id=c;this.dbkey=b;this.title=d;this.tracks=[];this.label_tracks=[];this.max_low=0;this.max_high=0;this.num_tracks=0;this.track_id_counter=0;this.zoom_factor=3;this.min_separation=30;this.has_changes=false;this.init(e);this.reset()};$.extend(View.prototype,{init:function(d){var c=this.container,a=this;this.top_container=$("
").addClass("top-container").appendTo(c);this.content_div=$("
").addClass("content").css("position","relative").appendTo(c);this.bottom_container=$("
").addClass("bottom-container").appendTo(c);this.top_labeltrack=$("
").addClass("top-labeltrack").appendTo(this.top_container);this.viewport_container=$("
").addClass("viewport-container").addClass("viewport-container").appendTo(this.content_div);this.intro_div=$("
").addClass("intro").text("Select a chrom from the dropdown below").hide();this.nav_labeltrack=$("
").addClass("nav-labeltrack").appendTo(this.bottom_container);this.nav_container=$("
").addClass("nav-container").prependTo(this.top_container);this.nav=$("
").addClass("nav").appendTo(this.nav_container);this.overview=$("
").addClass("overview").appendTo(this.bottom_container);this.overview_viewport=$("
").addClass("overview-viewport").appendTo(this.overview);this.overview_close=$("Close Overview").addClass("overview-close").hide().appendTo(this.overview_viewport);this.overview_highlight=$("
").addClass("overview-highlight").hide().appendTo(this.overview_viewport);this.overview_box_background=$("
").addClass("overview-boxback").appendTo(this.overview_viewport);this.overview_box=$("
").addClass("overview-box").appendTo(this.overview_viewport);this.default_overview_height=this.overview_box.height();this.nav_controls=$("
").addClass("nav-controls").appendTo(this.nav);this.chrom_form=$("
").attr("action",function(){}).appendTo(this.nav_controls);this.chrom_select=$("").addClass("nav-input").hide().bind("keypress focusout",b).appendTo(this.chrom_form);this.location_span=$("").addClass("location").appendTo(this.chrom_form);this.location_span.bind("click",function(){a.location_span.hide();a.chrom_select.hide();a.nav_input.css("display","inline-block");a.nav_input.select();a.nav_input.focus()});if(this.vis_id!==undefined){this.hidden_input=$("").attr("type","hidden").val(this.vis_id).appendTo(this.chrom_form)}this.zo_link=$("").click(function(){a.zoom_out();a.redraw()}).appendTo(this.chrom_form);this.zi_link=$("").click(function(){a.zoom_in();a.redraw()}).appendTo(this.chrom_form);$.ajax({url:chrom_url,data:(this.vis_id!==undefined?{vis_id:this.vis_id}:{dbkey:this.dbkey}),dataType:"json",success:function(f){if(f.reference){a.add_label_track(new ReferenceTrack(a))}a.chrom_data=f.chrom_info;var j='';for(var h=0,e=a.chrom_data.length;h'+g+""}a.chrom_select.html(j);a.intro_div.show();a.chrom_select.bind("change",function(){a.change_chrom(a.chrom_select.val())});if(d){d()}},error:function(){alert("Could not load chroms for this dbkey:",a.dbkey)}});this.content_div.bind("click",function(f){$(this).find("input").trigger("blur")});this.content_div.bind("dblclick",function(f){a.zoom_in(f.pageX,this.viewport_container)});this.overview_box.bind("dragstart",function(f){this.current_x=f.offsetX}).bind("drag",function(f){var h=f.offsetX-this.current_x;this.current_x=f.offsetX;var g=Math.round(h/a.viewport_container.width()*(a.max_high-a.max_low));a.move_delta(-g)});this.overview_close.bind("click",function(){for(var f=0,e=a.tracks.length;f").css({height:a.content_div.height()+a.top_labeltrack.height()+a.nav_labeltrack.height(),top:"0px",position:"absolute","background-color":"#ccf",opacity:0.5,"z-index":1000}).appendTo($(this))}).bind("drag",function(j){var g=Math.min(j.clientX,this.drag_origin_x)-a.container.offset().left,f=Math.max(j.clientX,this.drag_origin_x)-a.container.offset().left,i=(a.high-a.low),h=a.viewport_container.width();a.update_location(Math.round(g/h*i)+a.low,Math.round(f/h*i)+a.low);this.drag_div.css({left:g+"px",width:(f-g)+"px"})}).bind("dragend",function(k){var g=Math.min(k.clientX,this.drag_origin_x),f=Math.max(k.clientX,this.drag_origin_x),i=(a.high-a.low),h=a.viewport_container.width(),j=a.low;a.low=Math.round(g/h*i)+j;a.high=Math.round(f/h*i)+j;this.drag_div.remove();a.redraw()});this.add_label_track(new LabelTrack(this,this.top_labeltrack));this.add_label_track(new LabelTrack(this,this.nav_labeltrack));$(window).bind("resize",function(){a.resize_window()});$(document).bind("redraw",function(){a.redraw()});this.reset();$(window).trigger("resize")},update_location:function(a,b){this.location_span.text(commatize(a)+" - "+commatize(b));this.nav_input.val(this.chrom+":"+commatize(a)+"-"+commatize(b))},change_chrom:function(e,b,g){var d=this;var f=$.grep(d.chrom_data,function(j,k){return j.chrom===e})[0];if(f===undefined){return}if(e!==d.chrom){d.chrom=e;if(!d.chrom){d.intro_div.show()}else{d.intro_div.hide()}d.chrom_select.val(d.chrom);d.max_high=f.len;d.reset();d.redraw(true);for(var h=0,a=d.tracks.length;ha.max_high){a.high=a.max_high;a.low=a.max_high-b}else{a.high-=c;a.low-=c}}a.redraw()},add_track:function(a){a.view=this;a.track_id=this.track_id_counter;this.tracks.push(a);if(a.init){a.init()}a.container_div.attr("id","track_"+a.track_id);this.track_id_counter+=1;this.num_tracks+=1},add_label_track:function(a){a.view=this;this.label_tracks.push(a)},remove_track:function(a){this.has_changes=true;a.container_div.fadeOut("slow",function(){$(this).remove()});delete this.tracks[this.tracks.indexOf(a)];this.num_tracks-=1},reset:function(){this.low=this.max_low;this.high=this.max_high;this.viewport_container.find(".yaxislabel").remove()},redraw:function(h){var g=this.high-this.low,f=this.low,b=this.high;if(fthis.max_high){b=this.max_high}if(this.high!==0&&gthis.index){return true}return false},keep:function(a){if(!this.applies_to(a)){return true}return(a[this.index]>=this.low&&a[this.index]<=this.high)},update_attrs:function(b){var a=false;if(!this.applies_to(b)){return a}if(b[this.index]this.slider_max){this.slider_max=b[this.index];a=false}return a},update_ui_elt:function(){var b=this.slider.slider("option","min"),a=this.slider.slider("option","max");if(this.slider_mina){this.slider.slider("option","min",this.slider_min);this.slider.slider("option","max",this.slider_max);this.slider.slider("option","values",[this.slider_min,this.slider_max])}}});var get_filters_from_dict=function(a){var g=[];for(var d=0;d").addClass("track").css("position","relative");if(!this.hidden){this.header_div=$("
").appendTo(this.container_div);if(this.view.editor){this.drag_div=$("
").appendTo(this.header_div)}this.name_div=$("