trackster: Add bigWig display to trackster. Automatically converts wig to bigwig if needed (NOTE: datatypes_conf.xml.sample has been edited to add the new converter, you must update datatypes_conf.xml to use it). The converter requires that wigToBigWig be in the PATH, but no other tools are needed to view bigwig files as they are provided by bx_python.

- Fix track preferences not being applied
- Fix chroms not being selectable when a new track browser is created
- Fix ReferenceTrack not working with filters
- Fix visual analytics error when tool configuration has changed
This commit is contained in:
Kanwei Li
2010-12-10 14:59:43 -05:00
parent aa4410f46d
commit 5962f0ab2e
11 changed files with 130 additions and 39 deletions
+1
View File
@@ -121,6 +121,7 @@
</datatype>
<datatype extension="velvet" type="galaxy.datatypes.assembly:Velvet" display_in_upload="false"/>
<datatype extension="wig" type="galaxy.datatypes.interval:Wiggle" display_in_upload="true">
<converter file="wig_to_bigwig_converter.xml" target_datatype="bigwig"/>
<converter file="wiggle_to_array_tree_converter.xml" target_datatype="array_tree"/>
<converter file="wiggle_to_simple_converter.xml" target_datatype="interval"/>
<!-- <display file="gbrowse/gbrowse_wig.xml" /> -->
+2 -2
View File
@@ -12,7 +12,7 @@ repository = http://eggs.g2.bx.psu.edu/new
no_auto = pbs_python DRMAA_python
[eggs:platform]
bx_python = 0.5.0
bx_python = 0.7.0
Cheetah = 2.2.2
ctypes = 1.0.2
DRMAA_python = 0.2
@@ -65,7 +65,7 @@ Whoosh = 0.3.18
psycopg2 = _8.4.2_static
pysqlite = _3.6.17_static
MySQL_python = _5.1.41_static
bx_python = _dev_f74aec067563
bx_python = _14b6a6c95da6
GeneTrack = _dev_48da9e998f0caf01c5be731e926f4b0481f658f0
SQLAlchemy = _dev_r6498
pysam = _kanwei_90e03180969d
+2
View File
@@ -230,6 +230,8 @@ class BigWig(Binary):
return dataset.peek
except:
return "Binary UCSC %s file (%s)" % ( self._name, data.nice_size( dataset.get_size() ) )
def get_track_type( self ):
return "LineTrack", {"data_standalone": "bigwig"}
class BigBed(BigWig):
"""BigBed support from UCSC."""
@@ -0,0 +1,14 @@
<tool id="CONVERTER_wig_to_bigwig" name="wig->bigwig for Trackster. Needs 'wigToBigWig' tool in path." hidden="true">
<!-- Used internally to generate track indexes -->
<command>wigToBigWig $input $chromInfo $output</command>
<inputs>
<page>
<param format="wig" name="input" type="data" label="Choose wiggle"/>
</page>
</inputs>
<outputs>
<data format="bigwig" name="output"/>
</outputs>
<help>
</help>
</tool>
+1 -1
View File
@@ -1169,7 +1169,7 @@ class Wiggle( Tabular, _RemoteCallMixin ):
resolution = max( resolution, 1 )
return resolution
def get_track_type( self ):
return "LineTrack", {"data": "array_tree"}
return "LineTrack", {"data": "bigwig"}
class CustomTrack ( Tabular ):
"""UCSC CustomTrack"""
@@ -8,6 +8,7 @@ pkg_resources.require( "bx-python" ); pkg_resources.require( "pysam" ); pkg_reso
from galaxy.datatypes.util.gff_util import *
from bx.interval_index_file import Indexes
from bx.arrays.array_tree import FileArrayTreeDict
from bx.bbi.bigwig_file import BigWigFile
from galaxy.util.lrucache import LRUCache
from galaxy.visualization.tracks.summary import *
from galaxy.datatypes.tabular import Vcf
@@ -16,6 +17,13 @@ from pysam import csamtools
MAX_VALS = 5000 # only display first MAX_VALS features
# Return None instead of NaN to pass jQuery 1.4's strict JSON
def float_nan(n):
if n != n: # NaN != NaN
return None
else:
return float(n)
class TracksDataProvider( object ):
""" Base class for tracks data providers. """
@@ -340,6 +348,50 @@ class ArrayTreeDataProvider( TracksDataProvider ):
f.close()
return results
class BigWigDataProvider( TracksDataProvider ):
"""
BigWig data provider for the Galaxy track browser.
"""
def get_data( self, chrom, start, end, **kwargs ):
# Bigwig has the possibility of it being a standalone bigwig file, in which case we use
# original_dataset, or coming from wig->bigwig conversion in which we use converted_dataset
if self.converted_dataset is not None:
f = open( self.converted_dataset.file_name )
else:
f = open( self.original_dataset.file_name )
bw = BigWigFile(file=f)
if 'stats' in kwargs:
all_dat = bw.query(chrom, 0, 2147483647, 1)
f.close()
if all_dat is None:
return None
all_dat = all_dat[0] # only 1 summary
return { 'max': float( all_dat['max'] ), \
'min': float( all_dat['min'] ), \
'total_frequency': float( all_dat['coverage'] ) }
start = int(start)
end = int(end)
num_points = 2000
if (end - start) < num_points:
num_points = end - start
data = bw.query(chrom, start, end, num_points)
f.close()
pos = start
step_size = (end - start) / num_points
result = []
for dat_dict in data:
result.append( (pos, float_nan(dat_dict['mean']) ) )
pos += step_size
return result
class IntervalIndexDataProvider( TracksDataProvider ):
"""
@@ -448,7 +500,8 @@ dataset_type_name_to_data_provider = {
"array_tree": ArrayTreeDataProvider,
"interval_index": { "vcf": VcfDataProvider, "default" : IntervalIndexDataProvider },
"bai": BamDataProvider,
"summary_tree": SummaryTreeDataProvider
"summary_tree": SummaryTreeDataProvider,
"bigwig": BigWigDataProvider
}
dataset_type_to_data_provider = {
@@ -477,7 +530,10 @@ def get_data_provider( name=None, original_dataset=None ):
# Get data provider mapping and data provider for 'data'. If
# provider available, use it; otherwise use generic provider.
_ , data_provider_mapping = original_dataset.datatype.get_track_type()
data_provider_name = data_provider_mapping[ 'data' ]
if 'data_standalone' in data_provider_mapping:
data_provider_name = data_provider_mapping[ 'data_standalone' ]
else:
data_provider_name = data_provider_mapping[ 'data' ]
if data_provider_name:
data_provider = get_data_provider( name=data_provider_name, original_dataset=original_dataset )
else:
@@ -26,7 +26,7 @@ def get_tool_def( trans, hda ):
# Get list of tool parameters that can be interactively modified.
tool_params = []
tool_param_values = dict( [ ( p.name, p.value ) for p in job.parameters ] )
tool_param_values = tool.params_from_strings( tool_param_values, trans.app )
tool_param_values = tool.params_from_strings( tool_param_values, trans.app, ignore_errors=True )
for name, input in tool.inputs.items():
if type( input ) == IntegerToolParameter:
tool_params.append( { 'name' : name, 'label': input.label, 'type': 'int', \
+16 -8
View File
@@ -120,7 +120,6 @@ class TracksController( BaseController, UsesVisualization, UsesHistoryDatasetAss
@web.require_login()
def index( self, trans, **kwargs ):
config = {}
return trans.fill_template( "tracks/browser.mako", config=config, add_dataset=kwargs.get("dataset_id", None), \
default_dbkey=kwargs.get("default_dbkey", None) )
@@ -267,7 +266,7 @@ class TracksController( BaseController, UsesVisualization, UsesHistoryDatasetAss
return msg
# Get converted datasets.
data_sources, message = self._get_converted_datasets( trans, dataset )
data_sources, message = self._get_datasource_info( trans, dataset )
if not data_sources:
return message
@@ -286,9 +285,14 @@ class TracksController( BaseController, UsesVisualization, UsesHistoryDatasetAss
return { 'dataset_type': data_sources['index'], 'data': frequencies, 'max': max_v, 'avg': avg_v, 'delta': delta }
# Get data provider.
tracks_dataset_type = data_sources['data']
data_provider_class = get_data_provider( name=tracks_dataset_type, original_dataset=dataset )
data_provider = data_provider_class( dataset.get_converted_dataset(trans, tracks_dataset_type), dataset )
if "data_standalone" in data_sources:
tracks_dataset_type = data_sources['data_standalone']
data_provider_class = get_data_provider( name=tracks_dataset_type, original_dataset=dataset )
data_provider = data_provider_class( original_dataset=dataset )
else:
tracks_dataset_type = data_sources['data']
data_provider_class = get_data_provider( name=tracks_dataset_type, original_dataset=dataset )
data_provider = data_provider_class( dataset.get_converted_dataset(trans, tracks_dataset_type), dataset )
# Get and return data from data_provider.
data = data_provider.get_data( chrom, low, high, **kwargs )
@@ -502,13 +506,17 @@ class TracksController( BaseController, UsesVisualization, UsesHistoryDatasetAss
return messages.PENDING
return None
def _get_converted_datasets( self, trans, dataset ):
def _get_datasource_info( self, trans, dataset ):
"""
Returns (a) converted datasets for a dataset and (b) dictionary of
Returns (a) datasource info for a dataset and (b) dictionary of
any messages based on or derived from the conversion.
"""
track_type, data_sources = dataset.datatype.get_track_type()
for source_type, data_source in data_sources.iteritems():
if source_type == "data_standalone":
break
try:
converted_dataset = dataset.get_converted_dataset(trans, data_source)
except ValueError:
@@ -523,4 +531,4 @@ class TracksController( BaseController, UsesVisualization, UsesHistoryDatasetAss
if not converted_dataset or converted_dataset.state != model.Dataset.states.OK:
return None, messages.PENDING
return data_sources, None
return data_sources, None
File diff suppressed because one or more lines are too long
+32 -22
View File
@@ -690,8 +690,8 @@ $.extend( Track.prototype, {
track.content_div.text(DATA_ERROR);
if (result.message) {
var track_id = track.view.tracks.indexOf(track);
var error_link = $("<a href='javascript:void(0);'></a>").attr("id", track_id + "_error");
error_link.text("Click to view error");
var error_link = $(" <a href='javascript:void(0);'></a>").attr("id", track_id + "_error");
error_link.text("View error");
$("#" + track_id + "_error").live("click", function() {
show_modal( "Trackster Error", "<pre>" + result.message + "</pre>", { "Close" : hide_modal } );
});
@@ -719,16 +719,29 @@ $.extend( Track.prototype, {
track.container_div.addClass("nodata");
track.content_div.text(DATA_NONE);
}
},
restore_prefs: function(prefs) {
var that = this;
$.each(prefs, function(pref, val) {
if (val !== undefined) {
that.prefs[pref] = val;
}
});
}
});
var TiledTrack = function(filters, tool, parent_track) {
var track = this,
view = track.view;
// Attribute init.
this.filters = (filters !== undefined ? get_filters_from_dict( filters ) : []);
this.tool = (tool !== undefined ? get_tool_from_dict( tool ) : undefined);
this.parent_track = parent_track;
this.child_tracks = [];
if (track.hidden) { return; }
//
// Init HTML elements. for tool, filters.
//
@@ -753,7 +766,6 @@ var TiledTrack = function(filters, tool, parent_track) {
e.stopPropagation();
});
var filters_table = $("<table class='filters'>").appendTo(this.filtering_div);
var track = this;
$.each(this.filters, function(index, filter) {
var table_row = $("<tr>").appendTo(filters_table);
var filter_th = $("<th class='filter-info'>").appendTo(table_row);
@@ -844,14 +856,6 @@ var TiledTrack = function(filters, tool, parent_track) {
});
}
//
// Functional init.
//
var track = this,
view = track.view;
if (track.hidden) { return; }
if (track.display_modes !== undefined) {
if (track.mode_div === undefined) {
track.mode_div = $("<div class='right-float menubutton popup' />").appendTo(track.header_div);
@@ -882,16 +886,6 @@ var TiledTrack = function(filters, tool, parent_track) {
// Track dropdown menu.
//
var track_dropdown = {};
track_dropdown["Set as overview"] = function() {
view.overview_viewport.find("canvas").remove();
track.is_overview = true;
track.set_overview();
for (var track_id in view.tracks) {
if (view.tracks[track_id] !== track) {
view.tracks[track_id].is_overview = false;
}
}
};
track_dropdown["Edit configuration"] = function() {
var cancel_fn = function() { hide_modal(); $(window).unbind("keypress.check_enter_esc"); },
ok_fn = function() { track.update_options(track.track_id); hide_modal(); $(window).unbind("keypress.check_enter_esc"); },
@@ -909,6 +903,18 @@ var TiledTrack = function(filters, tool, parent_track) {
"OK": ok_fn
});
};
track_dropdown["Set as overview"] = function() {
view.overview_viewport.find("canvas").remove();
track.is_overview = true;
track.set_overview();
for (var track_id in view.tracks) {
if (view.tracks[track_id] !== track) {
view.tracks[track_id].is_overview = false;
}
}
};
if (track.filters.length > 0) {
// Show/hide filters menu item.
track_dropdown["Show filters"] = function() {
@@ -1061,7 +1067,9 @@ $.extend( TiledTrack.prototype, Track.prototype, {
parent_element.append( tile_element );
track.max_height = Math.max( track.max_height, tile_element.height() );
track.content_div.css("height", track.max_height + "px");
if (track.hidden) { return; }
// Show/hide filters based on whether tile is filterable.
if ( tile_element.hasClass(FILTERABLE_CLASS) ) {
show_hide_popupmenu_options(track.popup_menu, "(Show|Hide) filters");
@@ -1234,6 +1242,7 @@ var LineTrack = function ( name, view, dataset_id, prefs ) {
this.data_cache = new Cache(CACHED_DATA);
this.tile_cache = new Cache(CACHED_TILES_LINE);
this.prefs = { 'color': 'black', 'min_value': undefined, 'max_value': undefined, 'mode': this.mode };
this.restore_prefs(prefs);
};
$.extend( LineTrack.prototype, TiledTrack.prototype, {
init: function() {
@@ -1454,6 +1463,7 @@ var FeatureTrack = function (name, view, dataset_id, prefs, filters, tool, paren
this.left_offset = 200;
this.prefs = { 'block_color': '#444', 'label_color': 'black', 'show_counts': true };
this.restore_prefs(prefs);
};
$.extend( FeatureTrack.prototype, TiledTrack.prototype, {
init: function() {
+2 -2
View File
@@ -15,7 +15,7 @@ ${parent.stylesheets()}
${h.css( "history", "autocomplete_tagging", "trackster", "overcast/jquery-ui-1.8.5.custom" )}
<style type="text/css">
#center, #browser-container {
#browser-container {
overflow: none;
}
ul#sortable-ul {
@@ -120,7 +120,7 @@ ${h.js( "galaxy.base", "galaxy.panels", "json2", "jquery", "jquery.event.drag",
init();
%else:
var continue_fn = function() {
view = new View( $("#center"), $("#new-title").val(), undefined, $("#new-dbkey").val() );
view = new View( $("#browser-container"), $("#new-title").val(), undefined, $("#new-dbkey").val() );
view.editor = true;
init();
hide_modal();