Circster: use chords to denote chromosome interactions data.

This commit is contained in:
Jeremy Goecks
2012-10-23 11:06:27 -04:00
parent c5bfb57be9
commit f41b3d3883
7 changed files with 165 additions and 48 deletions
@@ -347,11 +347,18 @@ class TabixDataProvider( FilterableMixin, GenomeDataProvider ):
tabix = ctabix.Tabixfile(bgzip_fname, index_filename=self.converted_dataset.file_name)
# If chrom not in data, try alternative.
if chrom not in tabix.contigs:
# Get iterator using either naming scheme.
iterator = iter( [] )
if chrom in tabix.contigs:
iterator = tabix.fetch(reference=chrom, start=start, end=end)
else:
# Try alternative naming scheme.
chrom = _convert_between_ucsc_and_ensemble_naming( chrom )
return tabix.fetch(reference=chrom, start=start, end=end)
if chrom in tabix.contigs:
iterator = tabix.fetch(reference=chrom, start=start, end=end)
return iterator
def write_data_to_file( self, regions, filename ):
out = open( filename, "w" )
@@ -1457,11 +1464,11 @@ class ChromatinInteractionsDataProvider( GenomeDataProvider ):
feature = line.split()
length = len( feature )
s1 = int( feature[1] ),
e1 = int( feature[2] ),
c = feature[3],
s2 = int( feature[4] ),
e2 = int( feature[5] ),
s1 = int( feature[1] )
e1 = int( feature[2] )
c = feature[3]
s2 = int( feature[4] )
e2 = int( feature[5] )
v = float( feature[6] )
# Feature initialization.
@@ -1480,7 +1487,7 @@ class ChromatinInteractionsDataProvider( GenomeDataProvider ):
return 50000;
class ChromatinInteractionsTabixDataProvider( TabixDataProvider, ChromatinInteractionsDataProvider ):
def get_iterator( self, chrom, start, end ):
def get_iterator( self, chrom, start=0, end=sys.maxint ):
"""
"""
# Modify start as needed to get earlier interactions with start region.
@@ -1493,7 +1500,7 @@ class ChromatinInteractionsTabixDataProvider( TabixDataProvider, ChromatinIntera
c = feature[3]
s2 = int( feature[4] )
e2 = int( feature[5] )
if ( ( c == chrom ) and ( s1 < end and e1 > start ) and ( s2 < end and e2 > start ) ):
if ( s1 <= end and e1 >= start ) and ( s2 <= end and e2 >= start ):
yield line
return filter( TabixDataProvider.get_iterator( self, chrom, start, end ) )
+3 -3
View File
@@ -602,7 +602,7 @@ class UsesVisualizationMixin( UsesHistoryDatasetAssociationMixin,
return visualization
def _get_genome_data( self, trans, dataset, dbkey=None ):
def _get_genome_data( self, trans, dataset, dbkey=None, source='index' ):
"""
Returns genome-wide data for dataset if available; if not, message is returned.
"""
@@ -615,7 +615,7 @@ class UsesVisualizationMixin( UsesHistoryDatasetAssociationMixin,
query_dbkey = dbkey
chroms_info = self.app.genomes.chroms( trans, dbkey=query_dbkey )
# If there are no messages (messages indicate data is not ready/available), preload data.
# If there are no messages (messages indicate data is not ready/available), get data.
messages_list = [ data_source_dict[ 'message' ] for data_source_dict in data_sources.values() ]
message = get_highest_priority_msg( messages_list )
if message:
@@ -623,7 +623,7 @@ class UsesVisualizationMixin( UsesHistoryDatasetAssociationMixin,
else:
data_provider = trans.app.data_provider_registry.get_data_provider( trans,
original_dataset=dataset,
source='index' )
source=source )
# HACK: pass in additional params, which are only used for summary tree data, not BBI data.
rval = data_provider.get_genome_data( chroms_info, level=4, detail_cutoff=0, draw_cutoff=0 )
@@ -8,6 +8,7 @@ from galaxy.util.sanitize_html import sanitize_html
from galaxy.visualization.genomes import decode_dbkey
from galaxy.visualization.genome.visual_analytics import get_dataset_job
from galaxy.visualization.data_providers.phyloviz import PhylovizDataProvider
from galaxy.datatypes.interval import ChromatinInteractions
from .library import LibraryListGrid
@@ -748,11 +749,16 @@ class VisualizationController( BaseUIController, SharableMixin, UsesAnnotations,
chroms_info = self.app.genomes.chroms( trans, dbkey=dbkey )
genome = { 'dbkey': dbkey, 'chroms_info': chroms_info }
# Add genome-wide summary tree data to each track in viz.
# Add genome-wide data to each track in viz.
tracks = viz_config.get( 'tracks', [] )
for track in tracks:
dataset = self.get_hda_or_ldda( trans, track[ 'hda_ldda'], track[ 'dataset_id' ] )
genome_data = self._get_genome_data( trans, dataset, dbkey )
# HACK: chromatin interactions tracks use data as source.
source = 'index'
if isinstance( dataset.datatype, ChromatinInteractions ):
source = 'data'
genome_data = self._get_genome_data( trans, dataset, dbkey, source=source )
if not isinstance( genome_data, str ):
track[ 'preloaded_data' ] = genome_data
+132 -25
View File
@@ -48,23 +48,46 @@ var CircsterView = Backbone.View.extend({
this.track_gap = 5;
this.label_arc_height = 20;
this.scale = 1;
this.track_views = null;
this.circular_views = null;
this.chords_views = null;
// When tracks added to/removed from model, update view.
this.model.get('tracks').on('add', this.add_track, this);
this.model.get('tracks').on('remove', this.remove_track, this);
this.get_circular_tracks();
},
// HACKs: using track_type for circular/chord distinction in the functions below for now.
/**
* Returns tracks to be rendered using circular view.
*/
get_circular_tracks: function() {
return this.model.get('tracks').filter(function(track) {
return track.get('track_type') !== 'DiagonalHeatmapTrack';
});
},
/**
* Returns tracks to be rendered using chords view.
*/
get_chord_tracks: function() {
return this.model.get('tracks').filter(function(track) {
return track.get('track_type') === 'DiagonalHeatmapTrack';
});
},
/**
* Returns a list of tracks' radius bounds.
*/
get_tracks_bounds: function() {
var dataset_arc_height = this.dataset_arc_height,
var circular_tracks = this.get_circular_tracks();
dataset_arc_height = this.dataset_arc_height,
min_dimension = Math.min(this.$el.width(), this.$el.height()),
// Compute radius start based on model, will be centered
// and fit entirely inside element by default.
radius_start = min_dimension / 2 -
this.model.get('tracks').length * (this.dataset_arc_height + this.track_gap) -
circular_tracks.length * (this.dataset_arc_height + this.track_gap) -
(this.label_arc_height + this.track_gap),
// Compute range of track starting radii.
@@ -77,12 +100,16 @@ var CircsterView = Backbone.View.extend({
});
},
/**
* Renders circular tracks, chord tracks, and label tracks.
*/
render: function() {
var self = this,
dataset_arc_height = this.dataset_arc_height,
width = self.$el.width(),
height = self.$el.height(),
tracks = this.model.get('tracks'),
circular_tracks = this.get_circular_tracks(),
chords_tracks = this.get_chord_tracks(),
tracks_bounds = this.get_tracks_bounds(),
// Set up SVG element.
@@ -108,7 +135,7 @@ var CircsterView = Backbone.View.extend({
}
self.zoom_drag_timeout = setTimeout(function() {
// Render more detail in tracks' visible elements.
_.each(self.track_views, function(view) {
_.each(self.circular_views, function(view) {
view.update_scale(scale);
});
}, 400);
@@ -117,32 +144,46 @@ var CircsterView = Backbone.View.extend({
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")")
.append('svg:g').attr('class', 'tracks');
// -- Render each dataset in the visualization. --
// -- Render circular tracks. --
// Create a view for each track in the visualiation and render.
this.track_views = tracks.map(function(track, index) {
track_view_class = (track.get('track_type') === 'LineTrack' ?
this.circular_views = circular_tracks.map(function(track, index) {
var track_view_class = (track.get('track_type') === 'LineTrack' ?
CircsterBigWigTrackView :
CircsterSummaryTreeTrackView );
CircsterSummaryTreeTrackView ),
view = new track_view_class({
el: svg.append('g')[0],
track: track,
radius_bounds: tracks_bounds[index],
genome: self.genome,
total_gap: self.total_gap
});
return new track_view_class({
view.render();
return view;
});
// -- Render chords tracks. --
this.chords_views = chords_tracks.map(function(track) {
var view = new CircsterChromInteractionsTrackView({
el: svg.append('g')[0],
track: track,
radius_bounds: tracks_bounds[index],
radius_bounds: tracks_bounds[0],
genome: self.genome,
total_gap: self.total_gap
});
});
_.each(this.track_views, function(view) {
view.render();
return view;
});
// -- Render chromosome labels. --
// -- Render label tracks. --
// Set radius start = end for track bounds.
var track_bounds = tracks_bounds[tracks.length];
var track_bounds = tracks_bounds[circular_tracks.length];
track_bounds[1] = track_bounds[0];
this.label_track_view = new CircsterLabelTrackView({
el: svg.append('g')[0],
@@ -161,13 +202,12 @@ var CircsterView = Backbone.View.extend({
add_track: function(new_track) {
// Recompute and update track bounds.
var new_track_bounds = this.get_tracks_bounds();
_.each(this.track_views, function(track_view, i) {
//console.log(self.get_tracks_bounds(), i);
_.each(this.circular_views, function(track_view, i) {
track_view.update_radius_bounds(new_track_bounds[i]);
});
// Render new track.
var track_index = this.track_views.length,
var track_index = this.circular_views.length,
track_view_class = (new_track.get('track_type') === 'LineTrack' ?
CircsterBigWigTrackView :
CircsterSummaryTreeTrackView ),
@@ -179,7 +219,7 @@ var CircsterView = Backbone.View.extend({
total_gap: this.total_gap
});
track_view.render();
this.track_views.push(track_view);
this.circular_views.push(track_view);
// Update label track.
var track_bounds = new_track_bounds[ new_track_bounds.length-1 ];
@@ -192,14 +232,13 @@ var CircsterView = Backbone.View.extend({
*/
remove_track: function(track, tracks, options) {
// -- Remove track from view. --
var track_view = this.track_views[options.index];
this.track_views.splice(options.index, 1);
var track_view = this.circular_views[options.index];
this.circular_views.splice(options.index, 1);
track_view.$el.remove();
// Recompute and update track bounds.
var new_track_bounds = this.get_tracks_bounds();
_.each(this.track_views, function(track_view, i) {
//console.log(self.get_tracks_bounds(), i);
_.each(this.circular_views, function(track_view, i) {
track_view.update_radius_bounds(new_track_bounds[i]);
});
}
@@ -581,7 +620,7 @@ var CircsterSummaryTreeTrackView = CircsterQuantitativeTrackView.extend({
});
/**
* Bigwig track view in Circster
* Bigwig track view in Circster.
*/
var CircsterBigWigTrackView = CircsterQuantitativeTrackView.extend({
@@ -604,6 +643,74 @@ var CircsterBigWigTrackView = CircsterQuantitativeTrackView.extend({
}
});
/**
* Chromosome interactions track view in Circster.
*/
var CircsterChromInteractionsTrackView = CircsterTrackView.extend({
render: function() {
var self = this;
// When data is ready, render track.
$.when(self.track.get('data_manager').data_is_ready()).then(function() {
// Convert genome-wide data in chord data.
$.when(self.track.get('data_manager').get_genome_wide_data(self.genome)).then(function(genome_wide_data) {
var chord_data = [],
chroms_info = self.genome.get_chroms_info();
// Convert chromosome data into chord data.
_.each(genome_wide_data, function(chrom_data, index) {
// Map each interaction into chord data.
var cur_chrom = chroms_info[index].chrom;
var chrom_chord_data = _.map(chrom_data.data, function(datum) {
// Each datum is an interaction/chord.
var source_angle = self._get_region_angle(cur_chrom, datum[1]),
target_angle = self._get_region_angle(datum[3], datum[4]);
return {
source: {
startAngle: source_angle,
endAngle: source_angle + 0.01
},
target: {
startAngle: target_angle,
endAngle: target_angle + 0.01
}
};
});
chord_data = chord_data.concat(chrom_chord_data);
});
self.parent_elt.append("g")
.attr("class", "chord")
.selectAll("path")
.data(chord_data)
.enter().append("path")
.style("fill", '000')
.attr("d", d3.svg.chord().radius(self.radius_bounds[0]))
.style("opacity", 1);
});
});
},
/**
* Returns radians for a genomic position.
*/
_get_region_angle: function(chrom, position) {
// Find chrom angle data
var chrom_angle_data = _.find(this.chroms_layout, function(chrom_layout) {
return chrom_layout.data.chrom === chrom;
});
// Return angle at position.
return chrom_angle_data.endAngle -
(
(chrom_angle_data.endAngle - chrom_angle_data.startAngle) *
(chrom_angle_data.data.len - position) / chrom_angle_data.data.len
);
}
});
// Module exports.
return {
CircsterView: CircsterView
+1 -1
View File
@@ -905,7 +905,7 @@ var TracksterView = Backbone.View.extend({
// Introduction div shown when there are no tracks.
this.intro_div = $("<div/>").addClass("intro").appendTo(this.viewport_container).hide();
var add_tracks_button = $("<div/>").text("Add Datasets to Visualization").addClass("action-button").appendTo(this.intro_div).click(function () {
visualization.select_datasets(select_datasets_url, add_track_async_url, view.dbkey, function(tracks) {
visualization.select_datasets(select_datasets_url, add_track_async_url, { 'f-dbkey': view.dbkey }, function(tracks) {
_.each(tracks, function(track) {
view.add_drawable( object_from_template(track, view, view) );
});
+1 -1
View File
@@ -20,7 +20,7 @@ var TracksterUI = base.Base.extend({
var self = this,
menu = create_icon_buttons_menu([
{ icon_class: 'plus-button', title: 'Add tracks', on_click: function() {
visualization.select_datasets(select_datasets_url, add_track_async_url, view.dbkey, function(tracks) {
visualization.select_datasets(select_datasets_url, add_track_async_url, { 'f-dbkey': view.dbkey }, function(tracks) {
_.each(tracks, function(track) {
view.add_drawable( object_from_template(track, view, view) );
});
+2 -5
View File
@@ -3,8 +3,6 @@ define( ["libs/underscore", "mvc/data", "viz/trackster/util", "utils/config"],
/**
* Model, view, and controller objects for Galaxy visualization framework.
*
* Required libraries: Backbone, jQuery
*
* Models have no references to views, instead using events to indicate state
* changes; this is advantageous because multiple views can use the same object
@@ -16,11 +14,10 @@ define( ["libs/underscore", "mvc/data", "viz/trackster/util", "utils/config"],
* track definitions are obtained from the server and the success_fn is called with the list of
* definitions for selected datasets.
*/
var select_datasets = function(dataset_url, add_track_async_url, dbkey, success_fn) {
var select_datasets = function(dataset_url, add_track_async_url, filters, success_fn) {
$.ajax({
url: dataset_url,
// Filter by dbkey if available.
data: ( dbkey ? { 'f-dbkey': dbkey } : {} ),
data: filters,
error: function() { alert( "Grid failed" ); },
success: function(table_html) {
show_modal(