mirror of
https://github.com/galaxyproject/galaxy.git
synced 2026-10-24 12:13:33 +08:00
Circster: use chords to denote chromosome interactions data.
This commit is contained in:
@@ -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 ) )
|
||||
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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) );
|
||||
});
|
||||
|
||||
@@ -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) );
|
||||
});
|
||||
|
||||
@@ -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(
|
||||
|
||||
Reference in New Issue
Block a user