This commit is contained in:
James Taylor
2012-06-21 10:22:21 -04:00
5 changed files with 391 additions and 4 deletions
+26 -1
View File
@@ -1298,7 +1298,32 @@ class ENCODEPeak( Interval ):
return False
def get_track_type( self ):
return "FeatureTrack", {"data": "tabix", "index": "summary_tree"}
return "FeatureTrack", {"data": "tabix", "index": "summary_tree"}
class ChromatinInteractions( Interval ):
'''
Chromatin interactions obtained from 3C/5C/Hi-C experiments.
'''
file_ext = "chrint"
column_names = [ 'Chrom', 'Start1', 'End1', 'Start2', 'End2', 'Value' ]
"""Add metadata elements"""
MetadataElement( name="chromCol", default=1, desc="Chrom column", param=metadata.ColumnParameter )
MetadataElement( name="start1Col", default=2, desc="Start1 column", param=metadata.ColumnParameter )
MetadataElement( name="end1Col", default=3, desc="End1 column", param=metadata.ColumnParameter )
MetadataElement( name="start2Col", default=2, desc="Start2 column", param=metadata.ColumnParameter )
MetadataElement( name="end2Col", default=3, desc="End2 column", param=metadata.ColumnParameter )
MetadataElement( name="columns", default=3, desc="Number of columns", readonly=True, visible=False )
def sniff( self, filename ):
return False
def get_track_type( self ):
return "DiagonalHeatmapTrack", {"data": "tabix", "index": "summary_tree"}
if __name__ == '__main__':
import doctest, sys
@@ -19,7 +19,7 @@ from galaxy.util.lrucache import LRUCache
from galaxy.visualization.tracks.summary import *
import galaxy_utils.sequence.vcf
from galaxy.datatypes.tabular import Vcf
from galaxy.datatypes.interval import Interval, Bed, Gff, Gtf, ENCODEPeak
from galaxy.datatypes.interval import Interval, Bed, Gff, Gtf, ENCODEPeak, ChromatinInteractions
from pysam import csamtools, ctabix
@@ -146,6 +146,9 @@ class TracksDataProvider( object ):
{ 'name' : attrs[ 'name' ], 'type' : column_types[viz_col_index], \
'index' : attrs[ 'index' ] } )
return filters
def get_default_max_vals( self ):
return 5000;
#
# -- Base mixins and providers --
@@ -1254,6 +1257,67 @@ class ENCODEPeakTabixDataProvider( TabixDataProvider, ENCODEPeakDataProvider ):
'tool_id': 'Filter1',
'tool_exp_name': 'c9' } )
return filters
#
# -- ChromatinInteraction data providers --
#
class ChromatinInteractionsDataProvider( TracksDataProvider ):
def process_data( self, iterator, start_val=0, max_vals=None, **kwargs ):
"""
Provides
"""
rval = []
message = None
for count, line in enumerate( iterator ):
if count < start_val:
continue
if max_vals and count-start_val >= max_vals:
message = ERROR_MAX_VALS % ( max_vals, "interactions" )
break
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] ),
v = float( feature[6] )
# Feature initialization.
payload = [
# GUID is just a hash of the line
hash( line ),
# Add start1, end1, chr2, start2, end2, value.
s1, e1, c, s2, e2, v
]
rval.append( payload )
return { 'data': rval, 'message': message }
def get_default_max_vals( self ):
return 50000;
class ChromatinInteractionsTabixDataProvider( TabixDataProvider, ChromatinInteractionsDataProvider ):
def get_iterator( self, chrom, start, end ):
"""
"""
# Modify start as needed to get earlier interactions with start region.
start = max( 0, int( start) - 1000000 )
def filter( iter ):
for line in iter:
feature = line.split()
s1 = int( feature[1] ),
e1 = int( feature[2] ),
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 ) ):
yield line
return filter( TabixDataProvider.get_iterator( self, chrom, start, end ) )
#
# -- Helper methods. --
@@ -1269,7 +1333,9 @@ dataset_type_name_to_data_provider = {
Gtf: GtfTabixDataProvider,
ENCODEPeak: ENCODEPeakTabixDataProvider,
Interval: IntervalTabixDataProvider,
"default" : TabixDataProvider },
ChromatinInteractions: ChromatinInteractionsTabixDataProvider,
"default" : TabixDataProvider
},
"interval_index": IntervalIndexDataProvider,
"bai": BamDataProvider,
"bam": SamDataProvider,
+5 -1
View File
@@ -345,7 +345,7 @@ class TracksController( BaseUIController, UsesVisualizationMixin, UsesHistoryDat
return { "status": messages.DATA, "valid_chroms": valid_chroms }
@web.json
def data( self, trans, hda_ldda, dataset_id, chrom, low, high, start_val=0, max_vals=5000, **kwargs ):
def data( self, trans, hda_ldda, dataset_id, chrom, low, high, start_val=0, max_vals=None, **kwargs ):
"""
Provides a block of data from a dataset.
"""
@@ -401,6 +401,10 @@ class TracksController( BaseUIController, UsesVisualizationMixin, UsesHistoryDat
deps = dataset.get_converted_dataset_deps( trans, tracks_dataset_type )
data_provider = data_provider_class( converted_dataset=converted_dataset, original_dataset=dataset, dependencies=deps )
# Allow max_vals top be data provider set if not passed
if max_vals is None:
max_vals = data_provider.get_default_max_vals()
# Get and return data from data_provider.
result = data_provider.get_data( chrom, low, high, int( start_val ), int( max_vals ), **kwargs )
result.update( { 'dataset_type': tracks_dataset_type, 'extra_info': extra_info } )
+291
View File
@@ -4247,6 +4247,84 @@ extend(LineTrack.prototype, Drawable.prototype, TiledTrack.prototype, {
},
});
var DiagonalHeatmapTrack = function (view, container, obj_dict) {
var track = this;
this.display_modes = ["Heatmap"];
this.mode = "Heatmap";
TiledTrack.call(this, view, container, obj_dict);
// This all seems to be duplicated
this.hda_ldda = obj_dict.hda_ldda;
this.dataset_id = obj_dict.dataset_id;
this.original_dataset_id = this.dataset_id;
this.left_offset = 0;
// Define track configuration
this.config = new DrawableConfig( {
track: this,
params: [
{ key: 'name', label: 'Name', type: 'text', default_value: this.name },
{ key: 'pos_color', label: 'Positive Color', type: 'color', default_value: "4169E1" },
{ key: 'negative_color', label: 'Negative Color', type: 'color', default_value: "FF8C00" },
{ key: 'min_value', label: 'Min Value', type: 'float', default_value: 0 },
{ key: 'max_value', label: 'Max Value', type: 'float', default_value: 1 },
{ key: 'mode', type: 'string', default_value: this.mode, hidden: true },
{ key: 'height', type: 'int', default_value: 500, hidden: true }
],
saved_values: obj_dict.prefs,
onchange: function() {
track.set_name(track.prefs.name);
track.vertical_range = track.prefs.max_value - track.prefs.min_value;
track.set_min_value(track.prefs.min_value);
track.set_max_value(track.prefs.max_value);
}
});
this.prefs = this.config.values;
this.visible_height_px = this.config.values.height;
this.vertical_range = this.config.values.max_value - this.config.values.min_value;
};
extend(DiagonalHeatmapTrack.prototype, Drawable.prototype, TiledTrack.prototype, {
/**
* Action to take during resize.
*/
on_resize: function() {
this.request_draw(true);
},
/**
* Set track minimum value.
*/
set_min_value: function(new_val) {
this.prefs.min_value = new_val;
this.tile_cache.clear();
this.request_draw();
},
/**
* Set track maximum value.
*/
set_max_value: function(new_val) {
this.prefs.max_value = new_val;
this.tile_cache.clear();
this.request_draw();
},
/**
* Draw LineTrack tile.
*/
draw_tile: function(result, ctx, mode, resolution, tile_index, w_scale) {
// Paint onto canvas.
var
canvas = ctx.canvas,
tile_bounds = this._get_tile_bounds(tile_index, resolution),
tile_low = tile_bounds[0],
tile_high = tile_bounds[1],
painter = new painters.DiagonalHeatmapPainter(result.data, tile_low, tile_high, this.prefs, mode);
painter.draw(ctx, canvas.width, canvas.height, w_scale);
return new Tile(this, tile_index, resolution, canvas, result.data);
}
});
var FeatureTrack = function(view, container, obj_dict) {
//
// Preinitialization: do things that need to be done before calling Track and TiledTrack
@@ -4795,6 +4873,7 @@ exports.View = View;
exports.DrawableGroup = DrawableGroup;
exports.LineTrack = LineTrack;
exports.FeatureTrack = FeatureTrack;
exports.DiagonalHeatmapTrack = DiagonalHeatmapTrack;
exports.ReadTrack = ReadTrack;
exports.VcfTrack = VcfTrack;
exports.CompositeTrack = CompositeTrack;
@@ -5963,12 +6042,224 @@ extend(ArcLinkedFeaturePainter.prototype, FeaturePainter.prototype, LinkedFeatur
}
});
// Color stuff from less.js
var Color = function (rgb, a) {
/**
* The end goal here, is to parse the arguments
* into an integer triplet, such as `128, 255, 0`
*
* This facilitates operations and conversions.
*/
if (Array.isArray(rgb)) {
this.rgb = rgb;
} else if (rgb.length == 6) {
this.rgb = rgb.match(/.{2}/g).map(function (c) {
return parseInt(c, 16);
});
} else {
this.rgb = rgb.split('').map(function (c) {
return parseInt(c + c, 16);
});
}
this.alpha = typeof(a) === 'number' ? a : 1;
};
Color.prototype = {
eval: function () { return this },
//
// If we have some transparency, the only way to represent it
// is via `rgba`. Otherwise, we use the hex representation,
// which has better compatibility with older browsers.
// Values are capped between `0` and `255`, rounded and zero-padded.
//
toCSS: function () {
if (this.alpha < 1.0) {
return "rgba(" + this.rgb.map(function (c) {
return Math.round(c);
}).concat(this.alpha).join(', ') + ")";
} else {
return '#' + this.rgb.map(function (i) {
i = Math.round(i);
i = (i > 255 ? 255 : (i < 0 ? 0 : i)).toString(16);
return i.length === 1 ? '0' + i : i;
}).join('');
}
},
toHSL: function () {
var r = this.rgb[0] / 255,
g = this.rgb[1] / 255,
b = this.rgb[2] / 255,
a = this.alpha;
var max = Math.max(r, g, b), min = Math.min(r, g, b);
var h, s, l = (max + min) / 2, d = max - min;
if (max === min) {
h = s = 0;
} else {
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
switch (max) {
case r: h = (g - b) / d + (g < b ? 6 : 0); break;
case g: h = (b - r) / d + 2; break;
case b: h = (r - g) / d + 4; break;
}
h /= 6;
}
return { h: h * 360, s: s, l: l, a: a };
},
toARGB: function () {
var argb = [Math.round(this.alpha * 255)].concat(this.rgb);
return '#' + argb.map(function (i) {
i = Math.round(i);
i = (i > 255 ? 255 : (i < 0 ? 0 : i)).toString(16);
return i.length === 1 ? '0' + i : i;
}).join('');
},
mix: function (color2, weight) {
color1 = this;
var p = weight; // .value / 100.0;
var w = p * 2 - 1;
var a = color1.toHSL().a - color2.toHSL().a;
var w1 = (((w * a == -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0;
var w2 = 1 - w1;
var rgb = [color1.rgb[0] * w1 + color2.rgb[0] * w2,
color1.rgb[1] * w1 + color2.rgb[1] * w2,
color1.rgb[2] * w1 + color2.rgb[2] * w2];
var alpha = color1.alpha * p + color2.alpha * (1 - p);
return new Color(rgb, alpha);
}
};
// End colors from less.js
var LinearRamp = function( start_color, end_color, start_value, end_value ) {
/**
* Simple linear gradient
*/
this.start_color = new Color( start_color );
this.end_color = new Color( end_color );
this.start_value = start_value;
this.end_value = end_value;
this.value_range = end_value - start_value;
}
LinearRamp.prototype.map_value = function( value ) {
value = Math.max( value, this.start_value );
value = Math.min( value, this.end_value );
value = ( value - this.start_value ) / this.value_range;
// HACK: just red for now
// return "hsl(0,100%," + (value * 100) + "%)"
return this.start_color.mix( this.end_color, 1 - value ).toCSS();
}
var SplitRamp = function( start_color, middle_color, end_color, start_value, end_value ) {
/**
* Two gradients split away from 0
*/
this.positive_ramp = new LinearRamp( middle_color, end_color, 0, end_value );
this.negative_ramp = new LinearRamp( middle_color, start_color, 0, -start_value );
this.start_value = start_value;
this.end_value = end_value;
}
SplitRamp.prototype.map_value = function( value ) {
value = Math.max( value, this.start_value );
value = Math.min( value, this.end_value );
if ( value >= 0 ) {
return this.positive_ramp.map_value( value )
} else {
return this.negative_ramp.map_value( -value )
}
}
var DiagonalHeatmapPainter = function(data, view_start, view_end, prefs, mode) {
Painter.call( this, data, view_start, view_end, prefs, mode );
if ( this.prefs.min_value === undefined ) {
var min_value = Infinity;
for (var i = 0, len = this.data.length; i < len; i++) {
min_value = Math.min( min_value, this.data[i][5] );
}
this.prefs.min_value = min_value;
}
if ( this.prefs.max_value === undefined ) {
var max_value = -Infinity;
for (var i = 0, len = this.data.length; i < len; i++) {
max_value = Math.max( max_value, this.data[i][5] );
}
this.prefs.max_value = max_value;
}
};
DiagonalHeatmapPainter.prototype.default_prefs = {
min_value: undefined,
max_value: undefined,
mode: "Heatmap",
pos_color: "4169E1",
neg_color: "FF8C00"
};
DiagonalHeatmapPainter.prototype.draw = function(ctx, width, height, w_scale) {
var
min_value = this.prefs.min_value,
max_value = this.prefs.max_value,
value_range = max_value - min_value,
height_px = height,
view_start = this.view_start,
view_range = this.view_end - this.view_start,
mode = this.mode,
data = this.data,
invsqrt2 = 1 / Math.sqrt(2);
var ramp = ( new SplitRamp( this.prefs.neg_color, "FFFFFF", this.prefs.pos_color, min_value, max_value ) );
var d, s1, e1, s2, e2, value;
var scale = function( p ) { return ( p - view_start ) * w_scale };
ctx.save();
// Draw into triangle, then rotate and scale
ctx.rotate(-45 * Math.PI / 180);
ctx.scale( invsqrt2, invsqrt2 );
// Paint track.
for (var i = 0, len = data.length; i < len; i++) {
d = data[i];
// Ensure the cell is visible
// if ( )
s1 = scale( d[1] );
e1 = scale( d[2] );
s2 = scale( d[4] );
e2 = scale( d[5] );
value = d[6];
ctx.fillStyle = ( ramp.map_value( value ) )
ctx.fillRect( s1, s2, ( e1 - s1 ), ( e2 - s2 ) );
}
ctx.restore();
};
exports.Scaler = Scaler;
exports.SummaryTreePainter = SummaryTreePainter;
exports.LinePainter = LinePainter;
exports.LinkedFeaturePainter = LinkedFeaturePainter;
exports.ReadPainter = ReadPainter;
exports.ArcLinkedFeaturePainter = ArcLinkedFeaturePainter;
exports.DiagonalHeatmapPainter = DiagonalHeatmapPainter;
// End painters_module encapsulation
};
+1
View File
@@ -61,6 +61,7 @@ var addable_objects = {
"FeatureTrack": FeatureTrack,
"VcfTrack": VcfTrack,
"ReadTrack": ReadTrack,
// "DiagonalHeatmapTrack": DiagonalHeatmapTrack,
"CompositeTrack": CompositeTrack,
"DrawableGroup": DrawableGroup };