mirror of
https://github.com/galaxyproject/galaxy.git
synced 2026-09-24 16:30:27 +08:00
trackster: Supports display of reference sequence in twobit format. Mapping of dbkey to twobit file is done in tool-data/twobit.loc. Reference track will automatically be shown when zoomed in if the twobit file exists for that dbkey. Also fixes a drawing issue with Reference/Read tracks.
This commit is contained in:
@@ -13,9 +13,10 @@ Problems
|
||||
need to support that, but need to make user defined build support better)
|
||||
"""
|
||||
|
||||
import math, re, logging, glob
|
||||
log = logging.getLogger(__name__)
|
||||
import math, re, logging, glob, pkg_resources
|
||||
pkg_resources.require( "bx-python" )
|
||||
|
||||
from bx.seq.twobit import TwoBitFile
|
||||
from galaxy import model
|
||||
from galaxy.util.json import to_json_string, from_json_string
|
||||
from galaxy.web.base.controller import *
|
||||
@@ -71,11 +72,9 @@ class DatasetSelectionGrid( grids.Grid ):
|
||||
DbKeyColumn( "Dbkey", key="dbkey", model_class=model.HistoryDatasetAssociation, visible=False )
|
||||
]
|
||||
columns.append(
|
||||
grids.MulticolFilterColumn(
|
||||
"Search",
|
||||
cols_to_filter=[ columns[0], columns[1] ],
|
||||
grids.MulticolFilterColumn( "Search", cols_to_filter=[ columns[0], columns[1] ],
|
||||
key="free-text-search", visible=False, filterable="standard" )
|
||||
)
|
||||
)
|
||||
|
||||
def build_initial_query( self, trans, **kwargs ):
|
||||
return trans.sa_session.query( self.model_class ).join( model.History.table).join( model.Dataset.table )
|
||||
@@ -96,6 +95,15 @@ class TracksController( BaseController, UsesVisualization ):
|
||||
"""
|
||||
|
||||
available_tracks = None
|
||||
available_genomes = None
|
||||
|
||||
def _init_references(self, trans):
|
||||
avail_genomes = {}
|
||||
for line in open( os.path.join( trans.app.config.tool_data_path, "twobit.loc" ) ):
|
||||
if line.startswith("#"): continue
|
||||
key, path = line.split()
|
||||
avail_genomes[key] = path
|
||||
self.available_genomes = avail_genomes
|
||||
|
||||
@web.expose
|
||||
@web.require_login()
|
||||
@@ -170,12 +178,16 @@ class TracksController( BaseController, UsesVisualization ):
|
||||
# No vis_id, so visualization is new. User is current user, dbkey must be given.
|
||||
vis_user = trans.user
|
||||
vis_dbkey = dbkey
|
||||
|
||||
|
||||
# Get chroms data.
|
||||
chroms = self._chroms( trans, vis_user, vis_dbkey )
|
||||
|
||||
# Check for reference chrom
|
||||
if self.available_genomes is None: self._init_references(trans)
|
||||
|
||||
to_sort = [{ 'chrom': chrom, 'len': length } for chrom, length in chroms.iteritems()]
|
||||
to_sort.sort(lambda a,b: cmp( split_by_number(a['chrom']), split_by_number(b['chrom']) ))
|
||||
return to_sort
|
||||
return { 'reference': vis_dbkey in self.available_genomes, 'chrom_info': to_sort }
|
||||
|
||||
def _chroms( self, trans, user, dbkey ):
|
||||
"""
|
||||
@@ -204,7 +216,24 @@ class TracksController( BaseController, UsesVisualization ):
|
||||
fields = line.split("\t")
|
||||
manifest[fields[0]] = int(fields[1])
|
||||
return manifest
|
||||
|
||||
@web.json
|
||||
def reference( self, trans, dbkey, chrom, low, high, **kwargs ):
|
||||
if self.available_genomes is None: self._init_references(trans)
|
||||
|
||||
if dbkey not in self.available_genomes:
|
||||
return None
|
||||
|
||||
try:
|
||||
twobit = TwoBitFile( open(self.available_genomes[dbkey]) )
|
||||
except IOError:
|
||||
return None
|
||||
|
||||
if chrom in twobit:
|
||||
return twobit[chrom].get(int(low), int(high))
|
||||
|
||||
return None
|
||||
|
||||
@web.json
|
||||
def data( self, trans, dataset_id, chrom, low, high, **kwargs ):
|
||||
"""
|
||||
|
||||
File diff suppressed because one or more lines are too long
+87
-18
@@ -13,6 +13,7 @@ var DENSITY = 200,
|
||||
CACHED_TILES_LINE = 30,
|
||||
CACHED_DATA = 5,
|
||||
CONTEXT = $("<canvas></canvas>").get(0).getContext("2d"),
|
||||
PX_PER_CHAR = CONTEXT.measureText("A").width,
|
||||
RIGHT_STRAND, LEFT_STRAND;
|
||||
|
||||
var right_img = new Image();
|
||||
@@ -206,7 +207,7 @@ $.extend( Track.prototype, {
|
||||
track.data_queue = {};
|
||||
track.tile_cache.clear();
|
||||
track.data_cache.clear();
|
||||
track.content_div.css( "height", "30px" );
|
||||
// track.content_div.css( "height", "30px" );
|
||||
if (!track.content_div.text()) {
|
||||
track.content_div.text(DATA_LOADING);
|
||||
}
|
||||
@@ -252,6 +253,7 @@ $.extend( Track.prototype, {
|
||||
});
|
||||
|
||||
var TiledTrack = function() {
|
||||
this.left_offset = 200;
|
||||
};
|
||||
$.extend( TiledTrack.prototype, Track.prototype, {
|
||||
draw: function() {
|
||||
@@ -333,6 +335,75 @@ $.extend( LabelTrack.prototype, Track.prototype, {
|
||||
}
|
||||
});
|
||||
|
||||
var ReferenceTrack = function () {
|
||||
this.track_type = "ReferenceTrack";
|
||||
Track.call( this, null, $("#top-labeltrack") );
|
||||
TiledTrack.call( this );
|
||||
|
||||
this.hidden = true;
|
||||
this.height_px = 12;
|
||||
this.container_div.addClass( "reference-track" );
|
||||
this.dummy_canvas = $("<canvas></canvas>").get(0).getContext("2d");
|
||||
this.data_queue = {};
|
||||
this.data_cache = new Cache(CACHED_DATA);
|
||||
this.tile_cache = new Cache(CACHED_TILES_LINE);
|
||||
};
|
||||
$.extend( ReferenceTrack.prototype, TiledTrack.prototype, {
|
||||
get_data: function(resolution, position) {
|
||||
var track = this,
|
||||
low = position * DENSITY * resolution,
|
||||
high = ( position + 1 ) * DENSITY * resolution,
|
||||
key = resolution + "_" + position;
|
||||
|
||||
if (!track.data_queue[key]) {
|
||||
track.data_queue[key] = true;
|
||||
$.ajax({ 'url': reference_url, 'dataType': 'json', 'data': { "chrom": this.view.chrom,
|
||||
"low": low, "high": high, "dbkey": this.view.dbkey },
|
||||
success: function (seq) {
|
||||
track.data_cache.set(key, seq);
|
||||
delete track.data_queue[key];
|
||||
track.draw();
|
||||
}, error: function(r, t, e) {
|
||||
console.log(r, t, e);
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
draw_tile: function( resolution, tile_index, parent_element, w_scale ) {
|
||||
var tile_low = tile_index * DENSITY * resolution,
|
||||
tile_length = DENSITY * resolution,
|
||||
canvas = $("<canvas class='tile'></canvas>"),
|
||||
ctx = canvas.get(0).getContext("2d"),
|
||||
key = resolution + "_" + tile_index;
|
||||
|
||||
if (w_scale > PX_PER_CHAR) {
|
||||
if (this.data_cache.get(key) === undefined) {
|
||||
this.get_data( resolution, tile_index );
|
||||
return;
|
||||
}
|
||||
|
||||
var seq = this.data_cache.get(key);
|
||||
if (seq === null) { return; }
|
||||
|
||||
canvas.get(0).width = Math.ceil( tile_length * w_scale + this.left_offset);
|
||||
canvas.get(0).height = this.height_px;
|
||||
|
||||
canvas.css( {
|
||||
position: "absolute",
|
||||
top: 0,
|
||||
left: ( tile_low - this.view.low ) * w_scale + this.left_offset
|
||||
});
|
||||
|
||||
for (var c = 0, str_len = seq.length; c < str_len; c++) {
|
||||
var c_start = Math.round(c * w_scale);
|
||||
ctx.fillText(seq[c], c_start + this.left_offset, 10);
|
||||
}
|
||||
parent_element.append( canvas );
|
||||
return canvas;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
var LineTrack = function ( name, dataset_id, prefs ) {
|
||||
this.track_type = "LineTrack";
|
||||
Track.call( this, name, $("#viewport") );
|
||||
@@ -400,14 +471,14 @@ $.extend( LineTrack.prototype, TiledTrack.prototype, {
|
||||
$.ajax({ 'url': data_url, 'dataType': 'json', 'data': { "chrom": this.view.chrom,
|
||||
"low": low, "high": high, "dataset_id": this.dataset_id,
|
||||
"resolution": this.view.resolution },
|
||||
success: function (result) {
|
||||
data = result.data;
|
||||
track.data_cache.set(key, data);
|
||||
delete track.data_queue[key];
|
||||
track.draw();
|
||||
}, error: function(r, t, e) {
|
||||
console.log(r, t, e);
|
||||
}
|
||||
success: function (result) {
|
||||
data = result.data;
|
||||
track.data_cache.set(key, data);
|
||||
delete track.data_queue[key];
|
||||
track.draw();
|
||||
}, error: function(r, t, e) {
|
||||
console.log(r, t, e);
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
@@ -435,7 +506,7 @@ $.extend( LineTrack.prototype, TiledTrack.prototype, {
|
||||
left: ( tile_low - this.view.low ) * w_scale
|
||||
});
|
||||
|
||||
canvas.get(0).width = Math.ceil( tile_length * w_scale );
|
||||
canvas.get(0).width = Math.ceil( tile_length * w_scale + this.left_offset);
|
||||
canvas.get(0).height = this.height_px;
|
||||
var ctx = canvas.get(0).getContext("2d"),
|
||||
in_path = false,
|
||||
@@ -567,7 +638,6 @@ var FeatureTrack = function ( name, dataset_id, prefs ) {
|
||||
this.vertical_detail_px = 10;
|
||||
this.vertical_nodetail_px = 3;
|
||||
this.default_font = "9px Monaco, Lucida Console, monospace";
|
||||
this.left_offset = 200;
|
||||
this.inc_slots = {};
|
||||
this.data_queue = {};
|
||||
this.s_e_by_tile = {};
|
||||
@@ -718,10 +788,10 @@ $.extend( FeatureTrack.prototype, TiledTrack.prototype, {
|
||||
return highest_slot;
|
||||
|
||||
},
|
||||
rect_or_text: function( ctx, w_scale, px_per_char, tile_low, tile_high, feature_start, name, x, x_len, y_center ) {
|
||||
rect_or_text: function( ctx, w_scale, tile_low, tile_high, feature_start, name, x, x_len, y_center ) {
|
||||
ctx.textAlign = "center";
|
||||
var gap = Math.round(w_scale / 2);
|
||||
if ( (this.mode === "Pack" || this.mode === "Auto") && name !== undefined && w_scale > px_per_char) {
|
||||
if ( (this.mode === "Pack" || this.mode === "Auto") && name !== undefined && w_scale > PX_PER_CHAR) {
|
||||
ctx.fillStyle = this.prefs.block_color;
|
||||
ctx.fillRect(x, y_center + 1, x_len, 9);
|
||||
ctx.fillStyle = "#eee";
|
||||
@@ -790,8 +860,7 @@ $.extend( FeatureTrack.prototype, TiledTrack.prototype, {
|
||||
new_canvas.get(0).height = required_height;
|
||||
parent_element.parent().css("height", Math.max(this.height_px, required_height) + "px");
|
||||
// console.log(( tile_low - this.view.low ) * w_scale, tile_index, w_scale);
|
||||
var ctx = new_canvas.get(0).getContext("2d"),
|
||||
px_per_char = ctx.measureText("A").width;
|
||||
var ctx = new_canvas.get(0).getContext("2d");
|
||||
ctx.fillStyle = block_color;
|
||||
ctx.font = this.default_font;
|
||||
ctx.textAlign = "right";
|
||||
@@ -857,10 +926,10 @@ $.extend( FeatureTrack.prototype, TiledTrack.prototype, {
|
||||
b2_end = Math.ceil( Math.min(width, Math.max(0, (feature[5][1] - tile_low) * w_scale)) );
|
||||
|
||||
if (feature[4][1] >= tile_low && feature[4][0] <= tile_high) {
|
||||
this.rect_or_text(ctx, w_scale, px_per_char, tile_low, tile_high, feature[4][0], feature[4][2], b1_start + left_offset, b1_end - b1_start, y_center);
|
||||
this.rect_or_text(ctx, w_scale, tile_low, tile_high, feature[4][0], feature[4][2], b1_start + left_offset, b1_end - b1_start, y_center);
|
||||
}
|
||||
if (feature[5][1] >= tile_low && feature[5][0] <= tile_high) {
|
||||
this.rect_or_text(ctx, w_scale, px_per_char, tile_low, tile_high, feature[5][0], feature[5][2], b2_start + left_offset, b2_end - b2_start, y_center);
|
||||
this.rect_or_text(ctx, w_scale, tile_low, tile_high, feature[5][0], feature[5][2], b2_start + left_offset, b2_end - b2_start, y_center);
|
||||
}
|
||||
if (b2_start > b1_end) {
|
||||
ctx.fillStyle = "#999";
|
||||
@@ -868,7 +937,7 @@ $.extend( FeatureTrack.prototype, TiledTrack.prototype, {
|
||||
}
|
||||
} else {
|
||||
ctx.fillStyle = block_color;
|
||||
this.rect_or_text(ctx, w_scale, px_per_char, tile_low, tile_high, feature_start, feature_name, f_start + left_offset, f_end - f_start, y_center);
|
||||
this.rect_or_text(ctx, w_scale, tile_low, tile_high, feature_start, feature_name, f_start + left_offset, f_end - f_start, y_center);
|
||||
}
|
||||
if (mode !== "Dense" && !no_detail && feature_start > tile_low) {
|
||||
// Draw label
|
||||
|
||||
@@ -30,7 +30,8 @@ ${h.css( "history", "autocomplete_tagging" )}
|
||||
|
||||
<%def name="center_panel()">
|
||||
<div class="unified-panel-header" unselectable="on">
|
||||
<div class="unified-panel-header-inner" id="title">
|
||||
<div class="unified-panel-header-inner">
|
||||
<div style="float:left;" id="title"></div>
|
||||
<a id="save-button" class="panel-header-button right-float" href="javascript:void(0);">Save</a>
|
||||
<a id="refresh-button" class="panel-header-button right-float" href="javascript:void(0);" onclick="view.update_options();return false;">Refresh</a>
|
||||
</div>
|
||||
@@ -90,12 +91,12 @@ ${h.js( 'galaxy.base', 'galaxy.panels', "json2", "jquery", "jquery.event.drag",
|
||||
<script type="text/javascript">
|
||||
|
||||
var data_url = "${h.url_for( action='data' )}";
|
||||
var reference_url = "${h.url_for( action='reference' )}";
|
||||
var view;
|
||||
|
||||
$(function() {
|
||||
|
||||
%if config:
|
||||
$("#title").text("${config.get('title') | h}");
|
||||
view = new View( "${config.get('chrom')}", "${config.get('title') | h}", "${config.get('vis_id')}", "${config.get('dbkey')}" );
|
||||
%for track in config.get('tracks'):
|
||||
view.add_track(
|
||||
@@ -106,7 +107,6 @@ ${h.js( 'galaxy.base', 'galaxy.panels', "json2", "jquery", "jquery.event.drag",
|
||||
%else:
|
||||
continue_fn = function() {
|
||||
view = new View( undefined, $("#new-title").val(), undefined, $("#new-dbkey").val() );
|
||||
$("#title").text($("#new-title").val());
|
||||
init();
|
||||
hide_modal();
|
||||
};
|
||||
@@ -133,6 +133,7 @@ ${h.js( 'galaxy.base', 'galaxy.panels', "json2", "jquery", "jquery.event.drag",
|
||||
|
||||
// Execute this when everything is ready
|
||||
function init() {
|
||||
$("#title").text(view.title + " (" + view.dbkey + ")");
|
||||
$("ul#sortable-ul").sortable({
|
||||
update: function(event, ui) {
|
||||
for (var track_id in view.tracks) {
|
||||
@@ -327,11 +328,14 @@ ${h.js( 'galaxy.base', 'galaxy.panels', "json2", "jquery", "jquery.event.drag",
|
||||
data: { dbkey: view.dbkey },
|
||||
%endif
|
||||
dataType: "json",
|
||||
success: function ( data ) {
|
||||
view.chrom_data = data;
|
||||
success: function ( result ) {
|
||||
if (result['reference']) {
|
||||
view.add_label_track( new ReferenceTrack() );
|
||||
}
|
||||
view.chrom_data = result['chrom_info'];
|
||||
var chrom_options = '<option value="">Select Chrom/Contig</option>';
|
||||
for (i in data) {
|
||||
var chrom = data[i]['chrom'];
|
||||
for (i in view.chrom_data) {
|
||||
var chrom = view.chrom_data[i]['chrom'];
|
||||
chrom_options += '<option value="' + chrom + '">' + chrom + '</option>';
|
||||
}
|
||||
$("#chrom").html(chrom_options);
|
||||
|
||||
Reference in New Issue
Block a user