mirror of
https://github.com/galaxyproject/galaxy.git
synced 2026-09-24 16:30:27 +08:00
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:
@@ -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
|
||||
|
||||
|
||||
@@ -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 }
|
||||
@@ -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
@@ -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++;
|
||||
|
||||
Reference in New Issue
Block a user