Make VCF a first-class type in trackster. Added VCF data provider and VCF tracks type to trackster; new functionality is used to show reference base and alternate base for SNPs in VCF tracks. Also refactored data providers so that they inherit from a base provider.

This commit is contained in:
Jeremy Goecks
2010-10-07 21:26:21 -04:00
parent db737b3003
commit abea9f8f76
8 changed files with 130 additions and 19 deletions
@@ -7,8 +7,9 @@ pkg_resources.require( "numpy" )
pkg_resources.require( "bx-python" )
from bx.arrays.array_tree import FileArrayTreeDict
from math import floor, ceil, log, pow
from base import TracksDataProvider
class ArrayTreeDataProvider( object ):
class ArrayTreeDataProvider( TracksDataProvider ):
def __init__( self, dataset, original_dataset ):
self.dataset = dataset
+2 -1
View File
@@ -9,10 +9,11 @@ from pysam import csamtools
from math import floor, ceil, log
import logging
log = logging.getLogger(__name__)
from base import TracksDataProvider
MAX_VALS = 5000 # only display first MAX_VALS datapoints
class BamDataProvider( object ):
class BamDataProvider( TracksDataProvider ):
"""
Provides access to intervals from a sorted indexed BAM file.
"""
@@ -0,0 +1,11 @@
class TracksDataProvider( object ):
""" Base class for tracks data providers. """
def __init__( self, converted_dataset, original_dataset ):
self.converted_dataset = converted_dataset
self.original_dataset = original_dataset
def get_data( self, chrom, start, end, **kwargs ):
""" Returns data in region defined by chrom, start, and end. """
# Override.
pass
@@ -8,15 +8,11 @@ Payload format: [ uid (offset), start, end, name, strand, thick_start, thick_end
import pkg_resources; pkg_resources.require( "bx-python" )
from bx.interval_index_file import Indexes
from galaxy.datatypes.interval import Bed, Gff
from galaxy.datatypes.tabular import Vcf
from base import TracksDataProvider
MAX_VALS = 5000 # only display first MAX_VALS features
class IntervalIndexDataProvider( object ):
def __init__( self, converted_dataset, original_dataset ):
self.original_dataset = original_dataset
self.converted_dataset = converted_dataset
class IntervalIndexDataProvider( TracksDataProvider ):
def get_data( self, chrom, start, end, **kwargs ):
start, end = int(start), int(end)
source = open( self.original_dataset.file_name )
@@ -66,10 +62,7 @@ class IntervalIndexDataProvider( object ):
block_starts = [ int(n) for n in feature[11].split(',') if n != '' ]
blocks = zip(block_sizes, block_starts)
payload.append( [ (start + block[1], start + block[1] + block[0]) for block in blocks] )
elif isinstance( self.original_dataset.datatype, Vcf ):
# VCF dataset.
payload.append( feature[2] ) # name
results.append(payload)
return { 'data': results, 'message': message }
@@ -0,0 +1,53 @@
"""
VCF data provider for the Galaxy track browser.
Payload format:
[ uid (offset), start, end, ID, reference base(s), alternate base(s), quality score]
"""
import pkg_resources; pkg_resources.require( "bx-python" )
from bx.interval_index_file import Indexes
from galaxy.datatypes.tabular import Vcf
from base import TracksDataProvider
MAX_VALS = 5000 # only display first MAX_VALS features
class VCFDataProvider( TracksDataProvider ):
""" Provides data for VCF tracks. """
def get_data( self, chrom, start, end, **kwargs ):
""" Returns data in region defined by chrom, start, and end. """
start, end = int(start), int(end)
source = open( self.original_dataset.file_name )
index = Indexes( self.converted_dataset.file_name )
results = []
count = 0
message = None
# If chrom is not found in indexes, try removing the first three
# characters (e.g. 'chr') and see if that works. This enables the
# provider to handle chrome names defined as chrXXX and as XXX.
chrom = str(chrom)
if chrom not in index.indexes and chrom[3:] in index.indexes:
chrom = chrom[3:]
for start, end, offset in index.find(chrom, start, end):
if count >= MAX_VALS:
message = "Only the first %s features are being displayed." % MAX_VALS
break
count += 1
source.seek(offset)
feature = source.readline().split()
payload = [ offset, start, end, \
# ID:
feature[2], \
# reference base(s):
feature[3], \
# alternative base(s)
feature[4], \
# phred quality score
feature[5] ]
results.append(payload)
return { 'data_type' : 'vcf', 'data': results, 'message': message }
+21 -6
View File
@@ -24,6 +24,7 @@ from galaxy.visualization.tracks.data.array_tree import ArrayTreeDataProvider
from galaxy.visualization.tracks.data.interval_index import IntervalIndexDataProvider
from galaxy.visualization.tracks.data.bam import BamDataProvider
from galaxy.visualization.tracks.data.summary_tree import SummaryTreeDataProvider
from galaxy.visualization.tracks.data.vcf import VCFDataProvider
# Message strings returned to browser
messages = Bunch(
@@ -36,10 +37,11 @@ messages = Bunch(
)
# Mapping from dataset type to a class that can fetch data from a file of that
# type. This also needs to be more flexible.
# type. First key is converted dataset type; if result is another dict, second key
# is original dataset type. TODO: This needs to be more flexible.
dataset_type_to_data_provider = {
"array_tree": ArrayTreeDataProvider,
"interval_index": IntervalIndexDataProvider,
"interval_index": { "vcf": VCFDataProvider, "default" : IntervalIndexDataProvider },
"bai": BamDataProvider,
"summary_tree": SummaryTreeDataProvider
}
@@ -300,15 +302,28 @@ class TracksController( BaseController, UsesVisualization ):
frequencies, max_v, avg_v, delta = summary
return { 'dataset_type': data_sources['index'], 'data': frequencies, 'max': max_v, 'avg': avg_v, 'delta': delta }
dataset_type = data_sources['data']
data_provider = dataset_type_to_data_provider[ dataset_type ]( dataset.get_converted_dataset(trans, dataset_type), dataset )
# Get data provider.
tracks_dataset_type = data_sources['data']
value = dataset_type_to_data_provider[ tracks_dataset_type ]
if isinstance( value, dict ):
# Get converter by dataset extension; if there is no data provider,
# get the default
data_provider_class = value.get( dataset.ext, value.get( "default" ) )
else:
data_provider_class = value
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 )
message = None
if isinstance(data, dict) and 'message' in data:
message = data['message']
data = data['data']
return { 'dataset_type': dataset_type, 'extra_info': extra_info, 'data': data, 'message': message }
dataset_type = data.get( 'data_type', tracks_dataset_type )
track_data = data['data']
else:
track_data = data
return { 'dataset_type': dataset_type, 'extra_info': extra_info, 'data': track_data, 'message': message }
@web.json
def save( self, trans, **kwargs ):
File diff suppressed because one or more lines are too long
+37
View File
@@ -1348,8 +1348,45 @@ $.extend( FeatureTrack.prototype, TiledTrack.prototype, {
}
ctx.fillRect(f_start + left_offset, y_center, f_end - f_start, 10);
ctx.fillStyle = block_color;
}
}
}
} else if (result.dataset_type === 'vcf') {
if (no_detail) {
ctx.fillStyle = block_color;
ctx.fillRect(f_start + left_offset, y_center + 5, f_end - f_start, 1);
}
else { // Show blocks, labels, etc.
// Unpack.
var ref_base = feature[4], alt_base = feature[5], qual = feature[6];
// Draw block for entry.
thickness = 9;
y_start = 1;
ctx.fillRect(f_start + left_offset, y_center, f_end - f_start, thickness);
// Add label for entry.
if (mode !== "Dense" && feature_name !== undefined && feature_start > tile_low) {
// Draw label
ctx.fillStyle = label_color;
if (tile_index === 0 && f_start - ctx.measureText(feature_name).width < 0) {
ctx.textAlign = "left";
ctx.fillText(feature_name, f_end + 2 + left_offset, y_center + 8);
} else {
ctx.textAlign = "right";
ctx.fillText(feature_name, f_start - 2 + left_offset, y_center + 8);
}
ctx.fillStyle = block_color;
}
// Show additional data on block.
var vcf_label = ref_base + " / " + alt_base;
if (feature_start > tile_low && ctx.measureText(vcf_label).width < (f_end - f_start)) {
ctx.fillStyle = "white";
ctx.textAlign = "center";
ctx.fillText(vcf_label, left_offset + f_start + (f_end-f_start)/2, y_center + 8);
ctx.fillStyle = block_color;
}
}
}
j++;