mirror of
https://github.com/galaxyproject/galaxy.git
synced 2026-09-21 13:50:20 +08:00
Visualization framework: replace summary_tree with bigwig datatype for all feature track coverage data, yielding better performance and a standardized approach for working with coverage data. Update Trackster to work with bigwig coverage data.
This commit is contained in:
@@ -9,12 +9,7 @@
|
||||
<datatype extension="fli" type="galaxy.datatypes.tabular:FeatureLocationIndex" display_in_upload="false"/>
|
||||
<datatype extension="bam" type="galaxy.datatypes.binary:Bam" mimetype="application/octet-stream" display_in_upload="true">
|
||||
<converter file="bam_to_bai.xml" target_datatype="bai"/>
|
||||
<converter file="bam_to_summary_tree_converter.xml" target_datatype="summary_tree"/>
|
||||
<!--
|
||||
Caution: (a) this converter requires bedtools to be installed and (b) it is very memory intensive and
|
||||
is not recommended for most laptops/desktops.
|
||||
<converter file="bam_to_bigwig_converter.xml" target_datatype="bigwig"/>
|
||||
-->
|
||||
<converter file="bam_to_bigwig_converter.xml" target_datatype="bigwig"/>
|
||||
<display file="ucsc/bam.xml" />
|
||||
<display file="ensembl/ensembl_bam.xml" />
|
||||
<display file="igv/bam.xml" />
|
||||
@@ -25,7 +20,7 @@
|
||||
<converter file="interval_to_coverage.xml" target_datatype="coverage"/>
|
||||
<converter file="bed_to_bgzip_converter.xml" target_datatype="bgzip"/>
|
||||
<converter file="bed_to_tabix_converter.xml" target_datatype="tabix" depends_on="bgzip"/>
|
||||
<converter file="bed_to_summary_tree_converter.xml" target_datatype="summary_tree"/>
|
||||
<converter file="bed_gff_or_vcf_to_bigwig_converter.xml" target_datatype="bigwig"/>
|
||||
<converter file="bed_to_fli_converter.xml" target_datatype="fli"/>
|
||||
<!-- <display file="ucsc/interval_as_bed.xml" /> -->
|
||||
<display file="igb/bed.xml" />
|
||||
@@ -51,7 +46,7 @@
|
||||
<datatype extension="chrint" type="galaxy.datatypes.interval:ChromatinInteractions" display_in_upload="True">
|
||||
<converter file="interval_to_bgzip_converter.xml" target_datatype="bgzip"/>
|
||||
<converter file="interval_to_tabix_converter.xml" target_datatype="tabix" depends_on="bgzip"/>
|
||||
<converter file="interval_to_summary_tree_converter.xml" target_datatype="summary_tree"/>
|
||||
<converter file="bed_gff_or_vcf_to_bigwig_converter.xml" target_datatype="bigwig"/>
|
||||
</datatype>
|
||||
<!-- MSI added Datatypes -->
|
||||
<datatype extension="csv" type="galaxy.datatypes.tabular:Tabular" subclass="True" display_in_upload="true" /> <!-- FIXME: csv is 'tabular'ized data, but not 'tab-delimited'; the class used here is intended for 'tab-delimited' -->
|
||||
@@ -93,7 +88,7 @@
|
||||
<datatype extension="gff" type="galaxy.datatypes.interval:Gff" display_in_upload="true">
|
||||
<converter file="gff_to_bed_converter.xml" target_datatype="bed"/>
|
||||
<converter file="gff_to_interval_index_converter.xml" target_datatype="interval_index"/>
|
||||
<converter file="gff_to_summary_tree_converter.xml" target_datatype="summary_tree"/>
|
||||
<converter file="bed_gff_or_vcf_to_bigwig_converter.xml" target_datatype="bigwig"/>
|
||||
<converter file="gff_to_fli_converter.xml" target_datatype="fli"/>
|
||||
<display file="ensembl/ensembl_gff.xml" inherit="True"/>
|
||||
<!-- <display file="gbrowse/gbrowse_gff.xml" inherit="True" /> -->
|
||||
@@ -103,7 +98,7 @@
|
||||
<datatype extension="gmaj.zip" type="galaxy.datatypes.images:Gmaj" mimetype="application/zip"/>
|
||||
<datatype extension="gtf" type="galaxy.datatypes.interval:Gtf" display_in_upload="true">
|
||||
<converter file="gff_to_interval_index_converter.xml" target_datatype="interval_index"/>
|
||||
<converter file="gff_to_summary_tree_converter.xml" target_datatype="summary_tree"/>
|
||||
<converter file="bed_gff_or_vcf_to_bigwig_converter.xml" target_datatype="bigwig"/>
|
||||
</datatype>
|
||||
<datatype extension="toolshed.gz" type="galaxy.datatypes.binary:Binary" mimetype="multipart/x-gzip" subclass="True" />
|
||||
<datatype extension="h5" type="galaxy.datatypes.binary:Binary" mimetype="application/octet-stream" subclass="True" />
|
||||
@@ -115,7 +110,7 @@
|
||||
<converter file="interval_to_bed12_converter.xml" target_datatype="bed12"/>
|
||||
<converter file="interval_to_bgzip_converter.xml" target_datatype="bgzip"/>
|
||||
<converter file="interval_to_tabix_converter.xml" target_datatype="tabix" depends_on="bgzip"/>
|
||||
<converter file="interval_to_summary_tree_converter.xml" target_datatype="summary_tree"/>
|
||||
<converter file="interval_to_bigwig_converter.xml" target_datatype="bigwig"/>
|
||||
<!-- <display file="ucsc/interval_as_bed.xml" inherit="True" /> -->
|
||||
<display file="ensembl/ensembl_interval_as_bed.xml" inherit="True"/>
|
||||
<display file="gbrowse/gbrowse_interval_as_bed.xml" inherit="True"/>
|
||||
@@ -156,7 +151,7 @@
|
||||
<datatype extension="encodepeak" type="galaxy.datatypes.interval:ENCODEPeak" display_in_upload="True">
|
||||
<converter file="encodepeak_to_tabix_converter.xml" target_datatype="tabix" depends_on="bgzip"/>
|
||||
<converter file="encodepeak_to_bgzip_converter.xml" target_datatype="bgzip"/>
|
||||
<converter file="encodepeak_to_summary_tree_converter.xml" target_datatype="summary_tree"/>
|
||||
<converter file="bed_gff_or_vcf_to_bigwig_converter.xml" target_datatype="bigwig"/>
|
||||
</datatype>
|
||||
<datatype extension="pdf" type="galaxy.datatypes.images:Pdf" mimetype="application/pdf"/>
|
||||
<datatype extension="pileup" type="galaxy.datatypes.tabular:Pileup" display_in_upload="true">
|
||||
@@ -172,7 +167,7 @@
|
||||
<datatype extension="Roadmaps" type="galaxy.datatypes.assembly:Roadmaps" display_in_upload="false"/>
|
||||
<datatype extension="sam" type="galaxy.datatypes.tabular:Sam" display_in_upload="true">
|
||||
<converter file="sam_to_bam.xml" target_datatype="bam"/>
|
||||
<converter file="sam_to_summary_tree_converter.xml" target_datatype="summary_tree"/>
|
||||
<converter file="sam_to_bigwig_converter.xml" target_datatype="bigwig"/>
|
||||
</datatype>
|
||||
<datatype extension="scf" type="galaxy.datatypes.binary:Scf" mimetype="application/octet-stream" display_in_upload="true"/>
|
||||
<datatype extension="Sequences" type="galaxy.datatypes.assembly:Sequences" display_in_upload="false"/>
|
||||
@@ -190,7 +185,7 @@
|
||||
<converter file="vcf_to_bgzip_converter.xml" target_datatype="bgzip"/>
|
||||
<converter file="vcf_to_vcf_bgzip_converter.xml" target_datatype="vcf_bgzip"/>
|
||||
<converter file="vcf_to_tabix_converter.xml" target_datatype="tabix" depends_on="bgzip"/>
|
||||
<converter file="vcf_to_summary_tree_converter.xml" target_datatype="summary_tree"/>
|
||||
<converter file="bed_gff_or_vcf_to_bigwig_converter.xml" target_datatype="bigwig"/>
|
||||
<display file="ucsc/vcf.xml" />
|
||||
<display file="igv/vcf.xml" />
|
||||
<display file="rviewer/vcf.xml" inherit="True"/>
|
||||
@@ -203,7 +198,6 @@
|
||||
<!-- <display file="gbrowse/gbrowse_wig.xml" /> -->
|
||||
<display file="igb/wig.xml" />
|
||||
</datatype>
|
||||
<datatype extension="summary_tree" type="galaxy.datatypes.binary:Binary" subclass="True" />
|
||||
<datatype extension="interval_index" type="galaxy.datatypes.binary:Binary" subclass="True" />
|
||||
<datatype extension="tabix" type="galaxy.datatypes.binary:Binary" subclass="True" />
|
||||
<datatype extension="bgzip" type="galaxy.datatypes.binary:Binary" subclass="True" />
|
||||
|
||||
@@ -103,7 +103,7 @@ class Bam( Binary ):
|
||||
"""Class describing a BAM binary file"""
|
||||
file_ext = "bam"
|
||||
track_type = "ReadTrack"
|
||||
data_sources = { "data": "bai", "index": [ "bigwig", "summary_tree" ] }
|
||||
data_sources = { "data": "bai", "index": "bigwig" }
|
||||
|
||||
MetadataElement( name="bam_index", desc="BAM Index File", param=metadata.FileParameter, file_ext="bai", readonly=True, no_value=None, visible=False, optional=True )
|
||||
|
||||
|
||||
@@ -1,7 +1,14 @@
|
||||
<tool id="CONVERTER_bam_to_bigwig_0" name="Convert BAM to BigWig" version="1.0.0" hidden="true">
|
||||
<!-- <description>__NOT_USED_CURRENTLY_FOR_CONVERTERS__</description> -->
|
||||
<command>
|
||||
bedtools genomecov -bg -split -ibam $input -g $chromInfo | wigToBigWig stdin $chromInfo $output
|
||||
bedtools genomecov -bg -split -ibam $input -g $chromInfo
|
||||
|
||||
## Streaming the bedgraph file to wigToBigWig is fast but very memory intensive; hence, this
|
||||
## should only be used on systems with large RAM.
|
||||
## | wigToBigWig stdin $chromInfo $output
|
||||
|
||||
## This can be used anywhere.
|
||||
> temp.bg ; bedGraphToBigWig temp.bg $chromInfo $output
|
||||
</command>
|
||||
<inputs>
|
||||
<param format="bam" name="input" type="data" label="Choose BAM file"/>
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
<tool id="CONVERTER_bam_to_summary_tree_0" name="Convert BAM to Summary Tree" version="1.0.0" hidden="true">
|
||||
<!-- <description>__NOT_USED_CURRENTLY_FOR_CONVERTERS__</description> -->
|
||||
<command interpreter="python">
|
||||
sam_or_bam_to_summary_tree_converter.py --bam $input1 $input1.metadata.bam_index $output1
|
||||
</command>
|
||||
<inputs>
|
||||
<param format="bam" name="input1" type="data" label="Choose BAM file"/>
|
||||
</inputs>
|
||||
<outputs>
|
||||
<data format="summary_tree" name="output1"/>
|
||||
</outputs>
|
||||
<help>
|
||||
</help>
|
||||
</tool>
|
||||
@@ -0,0 +1,25 @@
|
||||
<tool id="CONVERTER_bed_gff_or_vcf_to_bigwig_0" name="Convert BED, GFF, or VCF to BigWig" version="1.0.0" hidden="true">
|
||||
<!-- <description>__NOT_USED_CURRENTLY_FOR_CONVERTERS__</description> -->
|
||||
<command>
|
||||
## Remove comments and sort by chromosome.
|
||||
grep -v '^#' $input | sort -k1,1 |
|
||||
|
||||
## Generate coverage bedgraph.
|
||||
bedtools genomecov -bg -split -i stdin -g $chromInfo
|
||||
|
||||
## Streaming the bedgraph file to wigToBigWig is fast but very memory intensive; hence, this
|
||||
## should only be used on systems with large RAM.
|
||||
## | wigToBigWig stdin $chromInfo $output
|
||||
|
||||
## This can be used anywhere.
|
||||
> temp.bg ; bedGraphToBigWig temp.bg $chromInfo $output
|
||||
</command>
|
||||
<inputs>
|
||||
<param format="bed,gff,vcf" name="input" type="data" label="Choose input file"/>
|
||||
</inputs>
|
||||
<outputs>
|
||||
<data format="bigwig" name="output"/>
|
||||
</outputs>
|
||||
<help>
|
||||
</help>
|
||||
</tool>
|
||||
@@ -1,14 +0,0 @@
|
||||
<tool id="CONVERTER_bed_to_summary_tree_0" name="Convert BED to Summary Tree" version="1.0.0" hidden="true">
|
||||
<!-- <description>__NOT_USED_CURRENTLY_FOR_CONVERTERS__</description> -->
|
||||
<command interpreter="python">interval_to_summary_tree_converter.py $input1 $output1</command>
|
||||
<inputs>
|
||||
<page>
|
||||
<param format="bed" name="input1" type="data" label="Choose BED file"/>
|
||||
</page>
|
||||
</inputs>
|
||||
<outputs>
|
||||
<data format="summary_tree" name="output1"/>
|
||||
</outputs>
|
||||
<help>
|
||||
</help>
|
||||
</tool>
|
||||
@@ -1,20 +0,0 @@
|
||||
<tool id="CONVERTER_encodepeak_to_summary_tree_0" name="Convert ENCODEPeak to Summary Tree" version="1.0.0" hidden="true">
|
||||
<!-- <description>__NOT_USED_CURRENTLY_FOR_CONVERTERS__</description> -->
|
||||
<command interpreter="python">interval_to_summary_tree_converter.py
|
||||
-c ${input1.metadata.chromCol}
|
||||
-s ${input1.metadata.startCol}
|
||||
-e ${input1.metadata.endCol}
|
||||
$input1 $output1
|
||||
</command>
|
||||
|
||||
<inputs>
|
||||
<page>
|
||||
<param format="ENCODEPeak" name="input1" type="data" label="Choose ENCODEPeak file"/>
|
||||
</page>
|
||||
</inputs>
|
||||
<outputs>
|
||||
<data format="summary_tree" name="output1"/>
|
||||
</outputs>
|
||||
<help>
|
||||
</help>
|
||||
</tool>
|
||||
@@ -1,14 +0,0 @@
|
||||
<tool id="CONVERTER_gff_to_summary_tree_0" name="Convert GFF to Summary Tree" version="1.0.0" hidden="true">
|
||||
<!-- <description>__NOT_USED_CURRENTLY_FOR_CONVERTERS__</description> -->
|
||||
<command interpreter="python">interval_to_summary_tree_converter.py $input1 $output1 --gff</command>
|
||||
<inputs>
|
||||
<page>
|
||||
<param format="gff" name="input1" type="data" label="Choose GFF file"/>
|
||||
</page>
|
||||
</inputs>
|
||||
<outputs>
|
||||
<data format="summary_tree" name="output1"/>
|
||||
</outputs>
|
||||
<help>
|
||||
</help>
|
||||
</tool>
|
||||
@@ -1,63 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
Convert from interval file to summary tree file. Default input file format is BED (0-based, half-open intervals).
|
||||
|
||||
usage: %prog <options> in_file out_file
|
||||
-c, --chr-col: chromosome column, default=1
|
||||
-s, --start-col: start column, default=2
|
||||
-e, --end-col: end column, default=3
|
||||
-t, --strand-col: strand column, default=6
|
||||
-G, --gff: input is GFF format, meaning start and end coordinates are 1-based, closed interval
|
||||
"""
|
||||
from __future__ import division
|
||||
|
||||
import sys, fileinput, optparse
|
||||
from galaxy import eggs
|
||||
import pkg_resources; pkg_resources.require( "bx-python" )
|
||||
from galaxy.visualization.tracks.summary import *
|
||||
from bx.intervals.io import *
|
||||
from galaxy.datatypes.util.gff_util import *
|
||||
|
||||
def main():
|
||||
# Read options, args.
|
||||
parser = optparse.OptionParser()
|
||||
parser.add_option( '-c', '--chr-col', type='int', dest='chrom_col', default=1 )
|
||||
parser.add_option( '-s', '--start-col', type='int', dest='start_col', default=2 )
|
||||
parser.add_option( '-e', '--end-col', type='int', dest='end_col', default=3 )
|
||||
parser.add_option( '-t', '--strand-col', type='int', dest='strand_col', default=6 )
|
||||
parser.add_option( '-G', '--gff', dest="gff_format", action="store_true" )
|
||||
(options, args) = parser.parse_args()
|
||||
input_fname, output_fname = args
|
||||
|
||||
# Convert column indices to 0-based.
|
||||
options.chrom_col -= 1
|
||||
options.start_col -= 1
|
||||
options.end_col -= 1
|
||||
options.strand_col -= 1
|
||||
|
||||
# Do conversion.
|
||||
if options.gff_format:
|
||||
reader_wrapper_class = GFFReaderWrapper
|
||||
chr_col, start_col, end_col, strand_col = ( 0, 3, 4, 6 )
|
||||
else:
|
||||
reader_wrapper_class = NiceReaderWrapper
|
||||
chr_col, start_col, end_col, strand_col = ( options.chrom_col, options.start_col, options.end_col, options.strand_col )
|
||||
reader_wrapper = reader_wrapper_class( fileinput.FileInput( input_fname ),
|
||||
chrom_col=chr_col,
|
||||
start_col=start_col,
|
||||
end_col=end_col,
|
||||
strand_col=strand_col,
|
||||
fix_strand=True )
|
||||
st = SummaryTree()
|
||||
for feature in list( reader_wrapper ):
|
||||
if isinstance( feature, GenomicInterval ):
|
||||
# Tree expects BED coordinates.
|
||||
if type( feature ) is GFFFeature:
|
||||
convert_gff_coords_to_bed( feature )
|
||||
st.insert_range( feature.chrom, long( feature.start ), long( feature.end ) )
|
||||
|
||||
st.write( output_fname )
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,20 +0,0 @@
|
||||
<tool id="CONVERTER_interval_to_summary_tree_0" name="Convert Interval to Summary Tree" version="1.0.0" hidden="true">
|
||||
<!-- <description>__NOT_USED_CURRENTLY_FOR_CONVERTERS__</description> -->
|
||||
<command interpreter="python">interval_to_summary_tree_converter.py
|
||||
-c ${input1.metadata.chromCol}
|
||||
-s ${input1.metadata.startCol}
|
||||
-e ${input1.metadata.endCol}
|
||||
$input1 $output1
|
||||
</command>
|
||||
|
||||
<inputs>
|
||||
<page>
|
||||
<param format="interval" name="input1" type="data" label="Choose Interval file"/>
|
||||
</page>
|
||||
</inputs>
|
||||
<outputs>
|
||||
<data format="summary_tree" name="output1"/>
|
||||
</outputs>
|
||||
<help>
|
||||
</help>
|
||||
</tool>
|
||||
@@ -1,42 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
from __future__ import division
|
||||
|
||||
import sys, os, optparse
|
||||
sys.stderr = open(os.devnull, 'w') # suppress stderr as cython produces warning on some systems:
|
||||
# csamtools.so:6: RuntimeWarning: __builtin__.file size changed
|
||||
|
||||
from galaxy import eggs
|
||||
import pkg_resources
|
||||
|
||||
if sys.version_info[:2] == (2, 4):
|
||||
pkg_resources.require( "ctypes" )
|
||||
pkg_resources.require( "pysam" )
|
||||
|
||||
from pysam import csamtools
|
||||
from galaxy.visualization.tracks.summary import *
|
||||
|
||||
def main():
|
||||
parser = optparse.OptionParser()
|
||||
parser.add_option( '-S', '--sam', action="store_true", dest="is_sam" )
|
||||
parser.add_option( '-B', '--bam', action="store_true", dest="is_bam" )
|
||||
options, args = parser.parse_args()
|
||||
|
||||
if options.is_bam:
|
||||
input_fname = args[0]
|
||||
index_fname = args[1]
|
||||
out_fname = args[2]
|
||||
samfile = csamtools.Samfile( filename=input_fname, mode='rb', index_filename=index_fname )
|
||||
elif options.is_sam:
|
||||
input_fname = args[0]
|
||||
out_fname = args[1]
|
||||
samfile = csamtools.Samfile( filename=input_fname, mode='r' )
|
||||
|
||||
st = SummaryTree()
|
||||
for read in samfile.fetch():
|
||||
st.insert_range( samfile.getrname( read.rname ), read.pos, read.pos + read.rlen )
|
||||
|
||||
st.write(out_fname)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,14 +0,0 @@
|
||||
<tool id="CONVERTER_sam_to_summary_tree_0" name="Convert SAM to Summary Tree" version="1.0.0" hidden="true">
|
||||
<!-- <description>__NOT_USED_CURRENTLY_FOR_CONVERTERS__</description> -->
|
||||
<command interpreter="python">sam_or_bam_to_summary_tree_converter.py --sam $input1 $output1</command>
|
||||
<inputs>
|
||||
<page>
|
||||
<param format="sam" name="input1" type="data" label="Choose sam file"/>
|
||||
</page>
|
||||
</inputs>
|
||||
<outputs>
|
||||
<data format="summary_tree" name="output1"/>
|
||||
</outputs>
|
||||
<help>
|
||||
</help>
|
||||
</tool>
|
||||
@@ -1,30 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
Convert from VCF file to summary tree file.
|
||||
|
||||
usage: %prog in_file out_file
|
||||
"""
|
||||
from __future__ import division
|
||||
|
||||
import optparse
|
||||
import galaxy_utils.sequence.vcf
|
||||
from galaxy.visualization.tracks.summary import SummaryTree
|
||||
|
||||
def main():
|
||||
# Read options, args.
|
||||
parser = optparse.OptionParser()
|
||||
(options, args) = parser.parse_args()
|
||||
in_file, out_file = args
|
||||
|
||||
# Do conversion.
|
||||
st = SummaryTree()
|
||||
for line in list( galaxy_utils.sequence.vcf.Reader( open( in_file ) ) ):
|
||||
# VCF format provides a chrom and 1-based position for each variant.
|
||||
# SummaryTree expects 0-based coordinates.
|
||||
st.insert_range( line.chrom, long( line.pos-1 ), long( line.pos ) )
|
||||
|
||||
st.write(out_file)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,14 +0,0 @@
|
||||
<tool id="CONVERTER_vcf_to_summary_tree_0" name="Convert VCF to Summary Tree" version="1.0.0" hidden="true">
|
||||
<!-- <description>__NOT_USED_CURRENTLY_FOR_CONVERTERS__</description> -->
|
||||
<command interpreter="python">vcf_to_summary_tree_converter.py $input1 $output1</command>
|
||||
<inputs>
|
||||
<page>
|
||||
<param format="vcf" name="input1" type="data" label="Choose VCF file"/>
|
||||
</page>
|
||||
</inputs>
|
||||
<outputs>
|
||||
<data format="summary_tree" name="output1"/>
|
||||
</outputs>
|
||||
<help>
|
||||
</help>
|
||||
</tool>
|
||||
@@ -47,7 +47,7 @@ class Interval( Tabular ):
|
||||
file_ext = "interval"
|
||||
line_class = "region"
|
||||
track_type = "FeatureTrack"
|
||||
data_sources = { "data": "tabix", "index": "summary_tree" }
|
||||
data_sources = { "data": "tabix", "index": "bigwig" }
|
||||
|
||||
"""Add metadata elements"""
|
||||
MetadataElement( name="chromCol", default=1, desc="Chrom column", param=metadata.ColumnParameter )
|
||||
@@ -354,7 +354,7 @@ class BedGraph( Interval ):
|
||||
class Bed( Interval ):
|
||||
"""Tab delimited data in BED format"""
|
||||
file_ext = "bed"
|
||||
data_sources = { "data": "tabix", "index": "summary_tree", "feature_search": "fli" }
|
||||
data_sources = { "data": "tabix", "index": "bigwig", "feature_search": "fli" }
|
||||
track_type = Interval.track_type
|
||||
|
||||
"""Add metadata elements"""
|
||||
@@ -569,7 +569,7 @@ class Gff( Tabular, _RemoteCallMixin ):
|
||||
"""Tab delimited data in Gff format"""
|
||||
file_ext = "gff"
|
||||
column_names = [ 'Seqname', 'Source', 'Feature', 'Start', 'End', 'Score', 'Strand', 'Frame', 'Group' ]
|
||||
data_sources = { "data": "interval_index", "index": "summary_tree", "feature_search": "fli" }
|
||||
data_sources = { "data": "interval_index", "index": "bigwig", "feature_search": "fli" }
|
||||
track_type = Interval.track_type
|
||||
|
||||
"""Add metadata elements"""
|
||||
@@ -1288,7 +1288,7 @@ class ENCODEPeak( Interval ):
|
||||
|
||||
file_ext = "encodepeak"
|
||||
column_names = [ 'Chrom', 'Start', 'End', 'Name', 'Score', 'Strand', 'SignalValue', 'pValue', 'qValue', 'Peak' ]
|
||||
data_sources = { "data": "tabix", "index": "summary_tree" }
|
||||
data_sources = { "data": "tabix", "index": "bigwig" }
|
||||
|
||||
"""Add metadata elements"""
|
||||
MetadataElement( name="chromCol", default=1, desc="Chrom column", param=metadata.ColumnParameter )
|
||||
@@ -1307,7 +1307,7 @@ class ChromatinInteractions( Interval ):
|
||||
|
||||
file_ext = "chrint"
|
||||
track_type = "DiagonalHeatmapTrack"
|
||||
data_sources = { "data": "tabix", "index": "summary_tree" }
|
||||
data_sources = { "data": "tabix", "index": "bigwig" }
|
||||
|
||||
column_names = [ 'Chrom1', 'Start1', 'End1', 'Chrom2', 'Start2', 'End2', 'Value' ]
|
||||
|
||||
|
||||
@@ -359,7 +359,7 @@ class Taxonomy( Tabular ):
|
||||
class Sam( Tabular ):
|
||||
file_ext = 'sam'
|
||||
track_type = "ReadTrack"
|
||||
data_sources = { "data": "bam", "index": "summary_tree" }
|
||||
data_sources = { "data": "bam", "index": "bigwig" }
|
||||
|
||||
def __init__(self, **kwd):
|
||||
"""Initialize taxonomy datatype"""
|
||||
@@ -537,7 +537,7 @@ class ElandMulti( Tabular ):
|
||||
class Vcf( Tabular ):
|
||||
""" Variant Call Format for describing SNPs and other simple genome variations. """
|
||||
track_type = "VariantTrack"
|
||||
data_sources = { "data": "tabix", "index": "summary_tree" }
|
||||
data_sources = { "data": "tabix", "index": "bigwig" }
|
||||
|
||||
file_ext = 'vcf'
|
||||
column_names = [ 'Chrom', 'Pos', 'ID', 'Ref', 'Alt', 'Qual', 'Filter', 'Info', 'Format', 'data' ]
|
||||
|
||||
@@ -14,7 +14,6 @@ from galaxy.util.json import from_json_string
|
||||
from bx.interval_index_file import Indexes
|
||||
from bx.bbi.bigwig_file import BigWigFile
|
||||
from galaxy.util.lrucache import LRUCache
|
||||
from galaxy.visualization.tracks.summary import summary_tree_from_file
|
||||
from galaxy.visualization.data_providers.basic import BaseDataProvider
|
||||
from galaxy.visualization.data_providers.cigar import get_ref_based_read_seq_and_cigar
|
||||
from galaxy.datatypes.interval import Bed, Gff, Gtf
|
||||
@@ -800,79 +799,6 @@ class RawVcfDataProvider( VcfDataProvider ):
|
||||
|
||||
return line_filter_iter()
|
||||
|
||||
class SummaryTreeDataProvider( GenomeDataProvider ):
|
||||
"""
|
||||
Summary tree data provider for the Galaxy track browser.
|
||||
"""
|
||||
|
||||
dataset_type = 'summary_tree'
|
||||
|
||||
CACHE = LRUCache( 20 ) # Store 20 recently accessed indices for performance
|
||||
|
||||
def valid_chroms( self ):
|
||||
st = summary_tree_from_file( self.converted_dataset.file_name )
|
||||
return st.chrom_blocks.keys()
|
||||
|
||||
def get_data( self, chrom, start, end, level=None, resolution=None, detail_cutoff=None, draw_cutoff=None, **kwargs ):
|
||||
"""
|
||||
Returns summary tree data for a given genomic region.
|
||||
"""
|
||||
filename = self.converted_dataset.file_name
|
||||
st = self.CACHE[filename]
|
||||
if st is None:
|
||||
st = summary_tree_from_file( self.converted_dataset.file_name )
|
||||
self.CACHE[filename] = st
|
||||
|
||||
# Look for chrom in tree using both naming conventions.
|
||||
if chrom not in st.chrom_blocks:
|
||||
chrom = _convert_between_ucsc_and_ensemble_naming( chrom )
|
||||
if chrom not in st.chrom_blocks:
|
||||
return None
|
||||
|
||||
# Get or compute level.
|
||||
if level:
|
||||
level = int( level )
|
||||
elif resolution:
|
||||
resolution = max( 1, ceil( float( resolution ) ) )
|
||||
level = ceil( log( resolution, st.block_size ) ) - 1
|
||||
level = int( max( level, 0 ) )
|
||||
else:
|
||||
# Either level or resolution is required.
|
||||
return None
|
||||
|
||||
if level <= 1:
|
||||
return "detail"
|
||||
|
||||
# Use level to get results.
|
||||
stats = st.chrom_stats[ chrom ]
|
||||
results = st.query( chrom, int(start), int(end), level, detail_cutoff=detail_cutoff, draw_cutoff=draw_cutoff )
|
||||
if results == "detail" or results == "draw":
|
||||
return results
|
||||
else:
|
||||
return {
|
||||
'dataset_type': self.dataset_type,
|
||||
'data': results,
|
||||
'max': stats[ level ][ "max" ],
|
||||
'avg': stats[ level ][ "avg" ],
|
||||
'delta': stats[ level ][ "delta" ],
|
||||
'level': level
|
||||
}
|
||||
|
||||
def has_data( self, chrom ):
|
||||
"""
|
||||
Returns true if dataset has data for this chrom
|
||||
"""
|
||||
|
||||
# Get summary tree.
|
||||
filename = self.converted_dataset.file_name
|
||||
st = self.CACHE[filename]
|
||||
if st is None:
|
||||
st = summary_tree_from_file( self.converted_dataset.file_name )
|
||||
self.CACHE[filename] = st
|
||||
|
||||
# Check for data.
|
||||
return st.chrom_blocks.get(chrom, None) or st.chrom_blocks.get(_convert_between_ucsc_and_ensemble_naming(chrom), None)
|
||||
|
||||
class BamDataProvider( GenomeDataProvider, FilterableMixin ):
|
||||
"""
|
||||
Provides access to intervals from a sorted indexed BAM file. Coordinate
|
||||
|
||||
@@ -29,7 +29,6 @@ class DataProviderRegistry( object ):
|
||||
"interval_index": genome.IntervalIndexDataProvider,
|
||||
"bai": genome.BamDataProvider,
|
||||
"bam": genome.SamDataProvider,
|
||||
"summary_tree": genome.SummaryTreeDataProvider,
|
||||
"bigwig": genome.BigWigDataProvider,
|
||||
"bigbed": genome.BigBedDataProvider
|
||||
}
|
||||
|
||||
@@ -1,117 +0,0 @@
|
||||
'''
|
||||
This module cannot be moved due to the use of pickling.
|
||||
'''
|
||||
|
||||
import sys, os
|
||||
import cPickle
|
||||
|
||||
# TODO: What are the performance implications of setting min level to 1? Data
|
||||
# structure size and/or query speed? It would be nice to have level 1 data
|
||||
# so that client does not have to compute it.
|
||||
MIN_LEVEL = 2
|
||||
|
||||
class SummaryTree:
|
||||
'''
|
||||
Summary tree data structure for feature aggregation across large genomic regions.
|
||||
'''
|
||||
def __init__( self, block_size=25, levels=6, draw_cutoff=150, detail_cutoff=30 ):
|
||||
self.chrom_blocks = {}
|
||||
self.levels = levels
|
||||
self.draw_cutoff = draw_cutoff
|
||||
self.detail_cutoff = detail_cutoff
|
||||
self.block_size = block_size
|
||||
self.chrom_stats = {}
|
||||
|
||||
def find_block( self, num, level ):
|
||||
""" Returns block that num is in for level. """
|
||||
return ( num / self.block_size ** level )
|
||||
|
||||
def insert_range( self, chrom, start, end ):
|
||||
""" Inserts a feature at chrom:start-end into the tree. """
|
||||
|
||||
# Get or set up chrom blocks.
|
||||
if chrom in self.chrom_blocks:
|
||||
blocks = self.chrom_blocks[ chrom ]
|
||||
else:
|
||||
blocks = self.chrom_blocks[ chrom ] = {}
|
||||
self.chrom_stats[ chrom ] = {}
|
||||
for level in range( MIN_LEVEL, self.levels + 1 ):
|
||||
blocks[ level ] = {}
|
||||
|
||||
# Insert feature into all matching blocks at all levels.
|
||||
for level in range( MIN_LEVEL, self.levels + 1 ):
|
||||
block_level = blocks[ level ]
|
||||
starting_block = self.find_block( start, level )
|
||||
ending_block = self.find_block( end, level )
|
||||
for block in range( starting_block, ending_block + 1 ):
|
||||
if block in block_level:
|
||||
block_level[ block ] += 1
|
||||
else:
|
||||
block_level[ block ] = 1
|
||||
|
||||
def finish( self ):
|
||||
""" Compute stats for levels. """
|
||||
|
||||
for chrom, blocks in self.chrom_blocks.iteritems():
|
||||
for level in range( self.levels, MIN_LEVEL - 1, -1 ):
|
||||
# Set level's stats.
|
||||
max_val = max( blocks[ level ].values() )
|
||||
self.chrom_stats[ chrom ][ level ] = {}
|
||||
self.chrom_stats[ chrom ][ level ][ "delta" ] = self.block_size ** level
|
||||
self.chrom_stats[ chrom ][ level ][ "max" ] = max_val
|
||||
self.chrom_stats[ chrom ][ level ][ "avg" ] = float( max_val ) / len( blocks[ level ] )
|
||||
|
||||
self.chrom_blocks[ chrom ] = dict( [ ( key, value ) for key, value in blocks.iteritems() ] )
|
||||
|
||||
def query( self, chrom, start, end, level, draw_cutoff=None, detail_cutoff=None ):
|
||||
""" Queries tree for data. """
|
||||
|
||||
# Set cutoffs to self's attributes if not defined.
|
||||
if draw_cutoff != 0:
|
||||
draw_cutoff = self.draw_cutoff
|
||||
if detail_cutoff != 0:
|
||||
detail_cutoff = self.detail_cutoff
|
||||
|
||||
# Get data.
|
||||
if chrom in self.chrom_blocks:
|
||||
stats = self.chrom_stats[ chrom ]
|
||||
|
||||
# For backwards compatibility:
|
||||
if "detail_level" in stats and level <= stats[ "detail_level" ]:
|
||||
return "detail"
|
||||
elif "draw_level" in stats and level <= stats[ "draw_level" ]:
|
||||
return "draw"
|
||||
|
||||
# If below draw, detail level, return string to denote this.
|
||||
max = stats[ level ][ "max" ]
|
||||
if max < detail_cutoff:
|
||||
return "detail"
|
||||
if max < draw_cutoff:
|
||||
return "draw"
|
||||
|
||||
# Return block data.
|
||||
blocks = self.chrom_blocks[ chrom ]
|
||||
results = []
|
||||
multiplier = self.block_size ** level
|
||||
starting_block = self.find_block( start, level )
|
||||
ending_block = self.find_block( end, level )
|
||||
for block in range( starting_block, ending_block + 1 ):
|
||||
val = 0
|
||||
if block in blocks[ level ]:
|
||||
val = blocks[ level ][ block ]
|
||||
results.append( ( block * multiplier, val ) )
|
||||
return results
|
||||
|
||||
return None
|
||||
|
||||
def write( self, filename ):
|
||||
""" Writes tree to file. """
|
||||
self.finish()
|
||||
cPickle.dump( self, open( filename, 'wb' ), 2 )
|
||||
|
||||
def summary_tree_from_file( filename ):
|
||||
st_file = open( filename, "rb" )
|
||||
st = cPickle.load( st_file )
|
||||
st_file.close()
|
||||
return st
|
||||
|
||||
@@ -145,32 +145,41 @@ class DatasetsController( BaseAPIController, UsesVisualizationMixin, UsesHistory
|
||||
|
||||
extra_info = None
|
||||
mode = kwargs.get( "mode", "Auto" )
|
||||
# Handle histogram mode uniquely for now:
|
||||
data_provider_registry = trans.app.data_provider_registry
|
||||
|
||||
# Coverage mode uses index data.
|
||||
if mode == "Coverage":
|
||||
# Get summary using minimal cutoffs.
|
||||
indexer = data_provider_registry.get_data_provider( trans, original_dataset=dataset, source='index' )
|
||||
summary = indexer.get_data( chrom, low, high, detail_cutoff=0, draw_cutoff=0, **kwargs )
|
||||
if summary == "detail":
|
||||
# Use maximum level of detail--2--to get summary data no matter the resolution.
|
||||
summary = indexer.get_data( chrom, low, high, resolution=kwargs[ 'resolution' ],
|
||||
level=2, detail_cutoff=0, draw_cutoff=0 )
|
||||
return summary
|
||||
return indexer.get_data( chrom, low, high, **kwargs )
|
||||
|
||||
if 'index' in data_sources and data_sources['index']['name'] == "summary_tree" and mode == "Auto":
|
||||
# Only check for summary_tree if it's Auto mode (which is the default)
|
||||
#
|
||||
# Have to choose between indexer and data provider
|
||||
# TODO:
|
||||
# (1) add logic back in for no_detail
|
||||
# (2) handle scenario where mode is Squish/Pack but data requested is large, so reduced data needed to be returned.
|
||||
|
||||
# If mode is Auto, need to determine what type of data to return.
|
||||
if mode == "Auto":
|
||||
# Get stats from indexer.
|
||||
indexer = data_provider_registry.get_data_provider( trans, original_dataset=dataset, source='index' )
|
||||
summary = indexer.get_data( chrom, low, high, resolution=kwargs[ 'resolution' ] )
|
||||
if summary is None:
|
||||
return { 'dataset_type': indexer.dataset_type, 'data': None }
|
||||
|
||||
if summary == "draw":
|
||||
kwargs["no_detail"] = True # meh
|
||||
extra_info = "no_detail"
|
||||
elif summary != "detail":
|
||||
return summary
|
||||
stats = indexer.get_data( chrom, low, high, stats=True )
|
||||
|
||||
# If stats were requested, return them.
|
||||
if 'stats' in kwargs:
|
||||
if stats[ 'data' ][ 'max' ] == 0:
|
||||
return { 'dataset_type': indexer.dataset_type, 'data': None }
|
||||
else:
|
||||
return stats
|
||||
|
||||
# Use heuristic based on max depth and region size to determine whether to
|
||||
# return coverage data. When zoomed out and region is large, max depth
|
||||
# is determining factor. However, when sufficiently zoomed in and region is
|
||||
# small, coverage data is no longer provided.
|
||||
if int( high ) - int( low ) > 50000 and stats[ 'data' ][ 'max' ] > 1000:
|
||||
return indexer.get_data( chrom, low, high )
|
||||
|
||||
#
|
||||
# Provide individual data points.
|
||||
#
|
||||
|
||||
# Get data provider.
|
||||
data_provider = data_provider_registry.get_data_provider( trans, original_dataset=dataset, source='data' )
|
||||
|
||||
@@ -58,7 +58,6 @@ var ConfigSetting = Backbone.Model.extend({
|
||||
{ key: 'mode', type: 'string', default_value: this.mode, hidden: true },
|
||||
{ key: 'reverse_strand_color', label: 'Antisense strand color', type: 'color', default_value: null },
|
||||
{ key: 'show_differences', label: 'Show differences only', type: 'bool', default_value: true },
|
||||
{ key: 'histogram_max', label: 'Histogram maximum', type: 'float', default_value: null, help: 'Clear value to set automatically' },
|
||||
{ key: 'mode', type: 'string', default_value: this.mode, hidden: true }
|
||||
]
|
||||
});
|
||||
|
||||
@@ -133,49 +133,6 @@ Painter.prototype.default_prefs = {};
|
||||
*/
|
||||
Painter.prototype.draw = function(ctx, width, height, w_scale) {};
|
||||
|
||||
/**
|
||||
* SummaryTreePainter, a histogram showing number of intervals in a region
|
||||
*/
|
||||
var SummaryTreePainter = function(data, view_start, view_end, prefs, mode) {
|
||||
Painter.call(this, data, view_start, view_end, prefs, mode);
|
||||
};
|
||||
|
||||
SummaryTreePainter.prototype.default_prefs = { show_counts: false };
|
||||
|
||||
SummaryTreePainter.prototype.draw = function(ctx, width, height, w_scale) {
|
||||
var view_start = this.view_start,
|
||||
points = this.data.data,
|
||||
max = (this.prefs.histogram_max ? this.prefs.histogram_max : this.data.max),
|
||||
// Set base Y so that max label and data do not overlap. Base Y is where rectangle bases
|
||||
// start. However, height of each rectangle is relative to required_height; hence, the
|
||||
// max rectangle is required_height.
|
||||
base_y = height,
|
||||
delta_x_px = Math.ceil(this.data.delta * w_scale);
|
||||
ctx.save();
|
||||
|
||||
for (var i = 0, len = points.length; i < len; i++) {
|
||||
var x = Math.floor( (points[i][0] - view_start) * w_scale );
|
||||
var y = points[i][1];
|
||||
|
||||
if (!y) { continue; }
|
||||
var y_px = y / max * height;
|
||||
if (y !== 0 && y_px < 1) { y_px = 1; }
|
||||
|
||||
ctx.fillStyle = this.prefs.block_color;
|
||||
ctx.fillRect( x, base_y - y_px, delta_x_px, y_px );
|
||||
|
||||
// Draw number count if it can fit the number with some padding, otherwise things clump up
|
||||
var text_padding_req_x = 4;
|
||||
if (this.prefs.show_counts && (ctx.measureText(y).width + text_padding_req_x) < delta_x_px) {
|
||||
ctx.fillStyle = this.prefs.label_color;
|
||||
ctx.textAlign = "center";
|
||||
ctx.fillText(y, x + (delta_x_px/2), 10);
|
||||
}
|
||||
}
|
||||
|
||||
ctx.restore();
|
||||
};
|
||||
|
||||
var LinePainter = function(data, view_start, view_end, prefs, mode) {
|
||||
Painter.call( this, data, view_start, view_end, prefs, mode );
|
||||
var i, len;
|
||||
@@ -1692,7 +1649,6 @@ extend(VariantPainter.prototype, Painter.prototype, {
|
||||
|
||||
return {
|
||||
Scaler: Scaler,
|
||||
SummaryTreePainter: SummaryTreePainter,
|
||||
LinePainter: LinePainter,
|
||||
LinkedFeaturePainter: LinkedFeaturePainter,
|
||||
ReadPainter: ReadPainter,
|
||||
|
||||
@@ -2076,11 +2076,10 @@ var Tile = function(track, region, resolution, canvas, data) {
|
||||
*/
|
||||
Tile.prototype.predisplay_actions = function() {};
|
||||
|
||||
var SummaryTreeTile = function(track, region, resolution, canvas, data, max_val) {
|
||||
var LineTrackTile = function(track, region, resolution, canvas, data) {
|
||||
Tile.call(this, track, region, resolution, canvas, data);
|
||||
this.max_val = max_val;
|
||||
};
|
||||
extend(SummaryTreeTile.prototype, Tile.prototype);
|
||||
LineTrackTile.prototype.predisplay_actions = function() {};
|
||||
|
||||
var FeatureTrackTile = function(track, region, resolution, canvas, data, w_scale, mode, message, all_slotted, feature_mapper) {
|
||||
// Attribute init.
|
||||
@@ -2601,7 +2600,7 @@ extend(Track.prototype, Drawable.prototype, {
|
||||
track.enabled = false;
|
||||
track.tile_cache.clear();
|
||||
track.data_manager.clear();
|
||||
track.content_div.css("height", "auto");
|
||||
track.tiles_div.css("height", "auto");
|
||||
/*
|
||||
if (!track.content_div.text()) {
|
||||
track.content_div.text(DATA_LOADING);
|
||||
@@ -2669,7 +2668,7 @@ extend(Track.prototype, Drawable.prototype, {
|
||||
track.tiles_div.css( "height", track.visible_height_px + "px" );
|
||||
track.enabled = true;
|
||||
// predraw_init may be asynchronous, wait for it and then draw
|
||||
$.when(track.predraw_init()).done(function() {
|
||||
$.when.apply($, track.predraw_init()).done(function() {
|
||||
init_deferred.resolve();
|
||||
track.container_div.removeClass("nodata error pending");
|
||||
track.request_draw();
|
||||
@@ -2688,7 +2687,29 @@ extend(Track.prototype, Drawable.prototype, {
|
||||
/**
|
||||
* Additional initialization required before drawing track for the first time.
|
||||
*/
|
||||
predraw_init: function() {},
|
||||
predraw_init: function() {
|
||||
var track = this;
|
||||
return $.getJSON( track.dataset.url(),
|
||||
{ data_type: 'data', stats: true, chrom: track.view.chrom, low: 0,
|
||||
high: track.view.max_high, hda_ldda: track.dataset.get('hda_ldda') }, function(result) {
|
||||
track.container_div.addClass( "line-track" );
|
||||
var data = result.data;
|
||||
|
||||
// Tracks may not have stat data either because there is no data or data is not yet ready.
|
||||
if (data && data.min && data.max) {
|
||||
// Compute default minimum and maximum values
|
||||
var min_value = data.min,
|
||||
max_value = data.max;
|
||||
// If mean and sd are present, use them to compute a ~95% window
|
||||
// but only if it would shrink the range on one side
|
||||
min_value = Math.floor( Math.min( 0, Math.max( min_value, data.mean - 2 * data.sd ) ) );
|
||||
max_value = Math.ceil( Math.max( 0, Math.min( max_value, data.mean + 2 * data.sd ) ) );
|
||||
// Update the prefs
|
||||
track.prefs.min_value = min_value;
|
||||
track.prefs.max_value = max_value;
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns all drawables in this drawable.
|
||||
@@ -2800,6 +2821,32 @@ extend(TiledTrack.prototype, Drawable.prototype, Track.prototype, {
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Set track bounds for current chromosome.
|
||||
*/
|
||||
set_min_max: function() {
|
||||
var track = this;
|
||||
|
||||
return $.getJSON( track.dataset.url(),
|
||||
{ data_type: 'data', stats: true, chrom: track.view.chrom, low: 0,
|
||||
high: track.view.max_high, hda_ldda: track.dataset.get('hda_ldda') },
|
||||
function(result) {
|
||||
var data = result.data;
|
||||
if ( isNaN(parseFloat(track.prefs.min_value)) || isNaN(parseFloat(track.prefs.max_value)) ) {
|
||||
// Compute default minimum and maximum values
|
||||
var min_value = data.min,
|
||||
max_value = data.max;
|
||||
// If mean and sd are present, use them to compute a ~95% window
|
||||
// but only if it would shrink the range on one side
|
||||
min_value = Math.floor( Math.min( 0, Math.max( min_value, data.mean - 2 * data.sd ) ) );
|
||||
max_value = Math.ceil( Math.max( 0, Math.min( max_value, data.mean + 2 * data.sd ) ) );
|
||||
// Update the prefs
|
||||
track.prefs.min_value = min_value;
|
||||
track.prefs.max_value = max_value;
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Change track's mode.
|
||||
*/
|
||||
@@ -2808,6 +2855,10 @@ extend(TiledTrack.prototype, Drawable.prototype, Track.prototype, {
|
||||
// TODO: is it necessary to store the mode in two places (.mode and track_config)?
|
||||
track.mode = new_mode;
|
||||
track.config.values['mode'] = new_mode;
|
||||
// FIXME: find a better way to get Auto data w/o clearing cache; using mode in the
|
||||
// data manager would work if Auto data were checked for compatibility when a specific
|
||||
// mode is chosen.
|
||||
if (new_mode === 'Auto') { this.data_manager.clear(); }
|
||||
track.request_draw(true);
|
||||
this.action_icons.mode_icon.attr("title", "Set display mode (now: " + track.mode + ")");
|
||||
return track;
|
||||
@@ -2954,10 +3005,11 @@ extend(TiledTrack.prototype, Drawable.prototype, Track.prototype, {
|
||||
/**
|
||||
* Add a maximum/minimum label to track.
|
||||
*/
|
||||
_add_yaxis_label: function(type, val, pref_name, on_change) {
|
||||
_add_yaxis_label: function(type, on_change) {
|
||||
var track = this,
|
||||
css_class = (type === 'max' ? 'top' : 'bottom'),
|
||||
text = (type === 'max' ? 'max' : 'min'),
|
||||
pref_name = (type === 'max' ? 'max_value' : 'min_value'),
|
||||
// Default action for on_change is to redraw track.
|
||||
on_change = on_change || function() {
|
||||
track.request_draw(true);
|
||||
@@ -2966,11 +3018,11 @@ extend(TiledTrack.prototype, Drawable.prototype, Track.prototype, {
|
||||
|
||||
if (label.length !== 0) {
|
||||
// Label already exists, so update value.
|
||||
label.text(val);
|
||||
label.text(track.prefs[pref_name]);
|
||||
}
|
||||
else {
|
||||
// Add label.
|
||||
label = $("<div/>").text(val).make_text_editable({
|
||||
label = $("<div/>").text(track.prefs[pref_name]).make_text_editable({
|
||||
num_cols: 12,
|
||||
on_finish: function(new_val) {
|
||||
$(".bs-tooltip").remove();
|
||||
@@ -2989,6 +3041,29 @@ extend(TiledTrack.prototype, Drawable.prototype, Track.prototype, {
|
||||
* drawn/fetched and shown.
|
||||
*/
|
||||
postdraw_actions: function(tiles, width, w_scale, clear_after) {
|
||||
//
|
||||
// If some tiles have line track tiles (which denote coverage data), redraw all tiles using coverage data.
|
||||
//
|
||||
var non_line_track_tiles = _.filter(tiles, function(tile) {
|
||||
return !(tile instanceof LineTrackTile);
|
||||
});
|
||||
|
||||
if (tiles.length !== non_line_track_tiles.length) {
|
||||
// Clear because this is set when drawing.
|
||||
this.max_height_px = 0;
|
||||
var track = this;
|
||||
_.each(non_line_track_tiles, function(tile) {
|
||||
tile.html_elt.remove();
|
||||
track.draw_helper(true, tile.region, tile.resolution, track.tiles_div, w_scale, { mode: 'Coverage' });
|
||||
});
|
||||
|
||||
track._add_yaxis_label('max');
|
||||
}
|
||||
else {
|
||||
// Remove Y-axis labels because there are no line track tiles.
|
||||
this.container_div.find('.yaxislabel').remove();
|
||||
}
|
||||
|
||||
//
|
||||
// If some tiles have icons, set padding of tiles without icons so features and rows align.
|
||||
//
|
||||
@@ -3004,16 +3079,6 @@ extend(TiledTrack.prototype, Drawable.prototype, Track.prototype, {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
//
|
||||
// If using SummaryTree tiles, show max.
|
||||
//
|
||||
var track = this,
|
||||
first_tile = tiles[0];
|
||||
if (first_tile instanceof SummaryTreeTile) {
|
||||
var max_val = (this.prefs.histogram_max ? this.prefs.histogram_max : first_tile.max_val);
|
||||
this._add_yaxis_label('max', max_val, 'histogram_max');
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -3021,12 +3086,13 @@ extend(TiledTrack.prototype, Drawable.prototype, Track.prototype, {
|
||||
* jQuery.Deferred object that is fulfilled when tile can be drawn again.
|
||||
*/
|
||||
draw_helper: function(force, region, resolution, parent_element, w_scale, kwargs) {
|
||||
var track = this,
|
||||
key = this._gen_tile_cache_key(w_scale, region),
|
||||
is_tile = function(o) { return (o && 'track' in o); };
|
||||
|
||||
// Init kwargs if necessary to avoid having to check if kwargs defined.
|
||||
if (!kwargs) { kwargs = {}; }
|
||||
|
||||
var track = this,
|
||||
key = this._gen_tile_cache_key(w_scale, region),
|
||||
is_tile = function(o) { return (o && 'track' in o); },
|
||||
mode = kwargs.mode || track.mode;
|
||||
|
||||
// Check tile cache, if found show existing tile in correct position
|
||||
var tile = (force ? undefined : track.tile_cache.get_elt(key));
|
||||
@@ -3041,7 +3107,7 @@ extend(TiledTrack.prototype, Drawable.prototype, Track.prototype, {
|
||||
var can_draw_now = true;
|
||||
|
||||
// Get the track data, maybe a deferred
|
||||
var tile_data = track.data_manager.get_data(region, track.mode, resolution, track.data_url_extra_params);
|
||||
var tile_data = track.data_manager.get_data(region, mode, resolution, track.data_url_extra_params);
|
||||
if ( is_deferred( tile_data ) ) {
|
||||
can_draw_now = false;
|
||||
}
|
||||
@@ -3049,7 +3115,7 @@ extend(TiledTrack.prototype, Drawable.prototype, Track.prototype, {
|
||||
// Get reference data if needed, maybe a deferred
|
||||
var seq_data;
|
||||
if ( view.reference_track ) {
|
||||
seq_data = view.reference_track.data_manager.get_data(region, track.mode, resolution, view.reference_track.data_url_extra_params);
|
||||
seq_data = view.reference_track.data_manager.get_data(region, mode, resolution, view.reference_track.data_url_extra_params);
|
||||
if ( is_deferred( seq_data ) ) {
|
||||
can_draw_now = false;
|
||||
}
|
||||
@@ -3066,7 +3132,6 @@ extend(TiledTrack.prototype, Drawable.prototype, Track.prototype, {
|
||||
|
||||
// HACK: this is FeatureTrack-specific.
|
||||
// If track mode is Auto, determine mode and update.
|
||||
var mode = track.mode;
|
||||
if (mode === "Auto" && track.get_mode) {
|
||||
mode = track.get_mode(tile_data);
|
||||
track.update_auto_mode(mode);
|
||||
@@ -3079,7 +3144,6 @@ extend(TiledTrack.prototype, Drawable.prototype, Track.prototype, {
|
||||
tile_high = region.get('end'),
|
||||
width = Math.ceil( (tile_high - tile_low) * w_scale ) + track.left_offset,
|
||||
height = track.get_canvas_height(tile_data, mode, w_scale, width);
|
||||
|
||||
canvas.width = width;
|
||||
canvas.height = height;
|
||||
var ctx = canvas.getContext('2d');
|
||||
@@ -3115,12 +3179,14 @@ extend(TiledTrack.prototype, Drawable.prototype, Track.prototype, {
|
||||
},
|
||||
|
||||
/**
|
||||
* Draw a track tile for summary tree data.
|
||||
* Draw line (bigwig) data onto tile.
|
||||
*/
|
||||
_draw_summary_tree_tile: function(result, ctx, region, resolution, w_scale) {
|
||||
var painter = new painters.SummaryTreePainter(result, region.get('start'), region.get('end'), this.prefs);
|
||||
painter.draw(ctx, ctx.canvas.width, ctx.canvas.height, w_scale);
|
||||
return new SummaryTreeTile(this, region, resolution, ctx.canvas, result.data, result.max);
|
||||
_draw_line_track_tile: function(result, ctx, mode, resolution, region, w_scale) {
|
||||
var canvas = ctx.canvas,
|
||||
painter = new painters.LinePainter(result.data, region.get('start'), region.get('end'), this.prefs, mode);
|
||||
painter.draw(ctx, canvas.width, canvas.height, w_scale);
|
||||
|
||||
return new LineTrackTile(this, region, resolution, canvas, result.data);
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -3277,10 +3343,17 @@ var LabelTrack = function (view, container) {
|
||||
};
|
||||
extend(LabelTrack.prototype, Track.prototype, {
|
||||
build_header_div: function() {},
|
||||
|
||||
init: function() {
|
||||
// Enable by default because there should always be data when drawing track.
|
||||
this.enabled = true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Additional initialization required before drawing track for the first time.
|
||||
*/
|
||||
predraw_init: function() {},
|
||||
|
||||
_draw: function() {
|
||||
var view = this.view,
|
||||
range = view.high - view.low,
|
||||
@@ -3623,8 +3696,8 @@ extend(CompositeTrack.prototype, TiledTrack.prototype, {
|
||||
t = function() { track.update_all_min_max(); };
|
||||
|
||||
// Add min, max labels.
|
||||
this._add_yaxis_label('min', this.drawables[0].prefs.min_value, 'min_value', t);
|
||||
this._add_yaxis_label('max', this.drawables[0].prefs.max_value, 'max_value', t);
|
||||
this._add_yaxis_label('min', t);
|
||||
this._add_yaxis_label('max', t);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -3655,6 +3728,11 @@ extend(ReferenceTrack.prototype, Drawable.prototype, TiledTrack.prototype, {
|
||||
this.enabled = true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Additional initialization required before drawing track for the first time.
|
||||
*/
|
||||
predraw_init: function() {},
|
||||
|
||||
can_draw: Drawable.prototype.can_draw,
|
||||
|
||||
/**
|
||||
@@ -3716,42 +3794,15 @@ var LineTrack = function (view, container, obj_dict) {
|
||||
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.request_redraw(true);
|
||||
}
|
||||
});
|
||||
|
||||
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(LineTrack.prototype, Drawable.prototype, TiledTrack.prototype, {
|
||||
|
||||
predraw_init: function() {
|
||||
var track = this;
|
||||
track.vertical_range = undefined;
|
||||
return $.getJSON( track.dataset.url(),
|
||||
{ data_type: 'data', stats: true, chrom: track.view.chrom, low: 0,
|
||||
high: track.view.max_high, hda_ldda: track.dataset.get('hda_ldda') }, function(result) {
|
||||
track.container_div.addClass( "line-track" );
|
||||
var data = result.data;
|
||||
if ( isNaN(parseFloat(track.prefs.min_value)) || isNaN(parseFloat(track.prefs.max_value)) ) {
|
||||
// Compute default minimum and maximum values
|
||||
var min_value = data.min,
|
||||
max_value = data.max;
|
||||
// If mean and sd are present, use them to compute a ~95% window
|
||||
// but only if it would shrink the range on one side
|
||||
min_value = Math.floor( Math.min( 0, Math.max( min_value, data.mean - 2 * data.sd ) ) );
|
||||
max_value = Math.ceil( Math.max( 0, Math.min( max_value, data.mean + 2 * data.sd ) ) );
|
||||
// Update the prefs
|
||||
track.prefs.min_value = min_value;
|
||||
track.prefs.max_value = max_value;
|
||||
}
|
||||
track.vertical_range = track.prefs.max_value - track.prefs.min_value;
|
||||
track.total_frequency = data.total_frequency;
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Actions to be taken before drawing.
|
||||
*/
|
||||
@@ -3759,18 +3810,10 @@ extend(LineTrack.prototype, Drawable.prototype, TiledTrack.prototype, {
|
||||
before_draw: function() {},
|
||||
|
||||
/**
|
||||
* Draw LineTrack tile.
|
||||
* Draw track tile.
|
||||
*/
|
||||
draw_tile: function(result, ctx, mode, resolution, region, w_scale) {
|
||||
// Paint onto canvas.
|
||||
var
|
||||
canvas = ctx.canvas,
|
||||
tile_low = region.get('start'),
|
||||
tile_high = region.get('end'),
|
||||
painter = new painters.LinePainter(result.data, tile_low, tile_high, this.prefs, mode);
|
||||
painter.draw(ctx, canvas.width, canvas.height, w_scale);
|
||||
|
||||
return new Tile(this, region, resolution, canvas, result.data);
|
||||
return this._draw_line_track_tile(result, ctx, mode, resolution, region, w_scale);
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -3811,14 +3854,12 @@ var DiagonalHeatmapTrack = function (view, container, obj_dict) {
|
||||
saved_values: obj_dict.prefs,
|
||||
onchange: function() {
|
||||
track.set_name(track.prefs.name);
|
||||
track.vertical_range = track.prefs.max_value - track.prefs.min_value;
|
||||
this.request_redraw(true);
|
||||
}
|
||||
});
|
||||
|
||||
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, {
|
||||
/**
|
||||
@@ -3863,7 +3904,8 @@ var FeatureTrack = function(view, container, obj_dict) {
|
||||
{ key: 'label_color', label: 'Label color', type: 'color', default_value: 'black' },
|
||||
{ key: 'show_counts', label: 'Show summary counts', type: 'bool', default_value: true,
|
||||
help: 'Show the number of items in each bin when drawing summary histogram' },
|
||||
{ key: 'histogram_max', label: 'Histogram maximum', type: 'float', default_value: null, help: 'clear value to set automatically' },
|
||||
{ key: 'min_value', label: 'Histogram minimum', type: 'float', default_value: null, help: 'clear value to set automatically' },
|
||||
{ key: 'max_value', label: 'Histogram maximum', type: 'float', default_value: null, help: 'clear value to set automatically' },
|
||||
{ key: 'connector_style', label: 'Connector style', type: 'select', default_value: 'fishbones',
|
||||
options: [ { label: 'Line with arrows', value: 'fishbone' }, { label: 'Arcs', value: 'arcs' } ] },
|
||||
{ key: 'mode', type: 'string', default_value: this.mode, hidden: true },
|
||||
@@ -3905,12 +3947,14 @@ extend(FeatureTrack.prototype, Drawable.prototype, TiledTrack.prototype, {
|
||||
* drawn/fetched and shown.
|
||||
*/
|
||||
postdraw_actions: function(tiles, width, w_scale, clear_after) {
|
||||
TiledTrack.prototype.postdraw_actions.call(this, tiles, clear_after);
|
||||
TiledTrack.prototype.postdraw_actions.call(this, tiles, width, w_scale, clear_after);
|
||||
|
||||
var track = this,
|
||||
i;
|
||||
|
||||
// If mode is Coverage and tiles do not share max, redraw tiles as necessary using new max.
|
||||
/*
|
||||
This code isn't used right now because Coverage mode uses predefined max in preferences.
|
||||
if (track.mode === "Coverage") {
|
||||
// Get global max.
|
||||
var global_max = -1;
|
||||
@@ -3928,7 +3972,8 @@ extend(FeatureTrack.prototype, Drawable.prototype, TiledTrack.prototype, {
|
||||
track.draw_helper(true, tile.index, tile.resolution, tile.html_elt.parent(), w_scale, { more_tile_data: { max: global_max } } );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
//
|
||||
// Update filter attributes, UI.
|
||||
@@ -3992,17 +4037,17 @@ extend(FeatureTrack.prototype, Drawable.prototype, TiledTrack.prototype, {
|
||||
this.action_icons.show_more_rows_icon.hide();
|
||||
}
|
||||
},
|
||||
|
||||
update_auto_mode: function( mode ) {
|
||||
var mode;
|
||||
if ( this.mode === "Auto" ) {
|
||||
if ( mode === "no_detail" ) {
|
||||
mode = "feature spans";
|
||||
} else if ( mode === "summary_tree" ) {
|
||||
mode = "coverage histogram";
|
||||
}
|
||||
this.action_icons.mode_icon.attr("title", "Set display mode (now: Auto/" + mode + ")");
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Place features in slots for drawing (i.e. pack features).
|
||||
* this.slotters[level] is created in this method. this.slotters[level]
|
||||
@@ -4022,15 +4067,13 @@ extend(FeatureTrack.prototype, Drawable.prototype, TiledTrack.prototype, {
|
||||
|
||||
return slotter.slot_features( features );
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns appropriate display mode based on data.
|
||||
*/
|
||||
get_mode: function(data) {
|
||||
if (data.dataset_type === "summary_tree") {
|
||||
mode = "summary_tree";
|
||||
}
|
||||
// HACK: use no_detail mode track is in overview to prevent overview from being too large.
|
||||
else if (data.extra_info === "no_detail" || this.is_overview) {
|
||||
if (data.extra_info === "no_detail" || this.is_overview) {
|
||||
mode = "no_detail";
|
||||
}
|
||||
else {
|
||||
@@ -4054,12 +4097,13 @@ extend(FeatureTrack.prototype, Drawable.prototype, TiledTrack.prototype, {
|
||||
}
|
||||
return mode;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns canvas height needed to display data; return value is an integer that denotes the
|
||||
* number of pixels required.
|
||||
*/
|
||||
get_canvas_height: function(result, mode, w_scale, canvas_width) {
|
||||
if (mode === "summary_tree" || mode === "Coverage") {
|
||||
if (mode === "Coverage" || result.dataset_type === 'bigwig') {
|
||||
return this.summary_draw_height;
|
||||
}
|
||||
else {
|
||||
@@ -4071,6 +4115,7 @@ extend(FeatureTrack.prototype, Drawable.prototype, TiledTrack.prototype, {
|
||||
return Math.max(MIN_TRACK_HEIGHT, dummy_painter.get_required_height(rows_required, canvas_width) );
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Draw FeatureTrack tile.
|
||||
* @param result result from server
|
||||
@@ -4087,10 +4132,10 @@ extend(FeatureTrack.prototype, Drawable.prototype, TiledTrack.prototype, {
|
||||
tile_low = region.get('start'),
|
||||
tile_high = region.get('end'),
|
||||
left_offset = this.left_offset;
|
||||
|
||||
// Drawing the summary tree.
|
||||
if (mode === "summary_tree" || mode === "Coverage") {
|
||||
return this._draw_summary_tree_tile(result, ctx, region, resolution, w_scale);
|
||||
|
||||
// If data is line track data, draw line track tile.
|
||||
if (result.dataset_type === 'bigwig') {
|
||||
return this._draw_line_track_tile(result, ctx, 'Histogram', resolution, region, w_scale);
|
||||
}
|
||||
|
||||
// Handle row-by-row tracks
|
||||
@@ -4147,6 +4192,7 @@ extend(FeatureTrack.prototype, Drawable.prototype, TiledTrack.prototype, {
|
||||
|
||||
return new FeatureTrackTile(track, region, resolution, canvas, result.data, w_scale, mode, result.message, all_slotted, feature_mapper);
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns true if data is compatible with a given mode.
|
||||
*/
|
||||
@@ -4155,24 +4201,25 @@ extend(FeatureTrack.prototype, Drawable.prototype, TiledTrack.prototype, {
|
||||
if (mode === "Auto") {
|
||||
return true;
|
||||
}
|
||||
// Histogram mode requires summary_tree data.
|
||||
// Histogram mode requires bigwig data.
|
||||
else if (mode === "Coverage") {
|
||||
return data.dataset_type === "summary_tree";
|
||||
return data.dataset_type === "bigwig";
|
||||
}
|
||||
// All other modes--Dense, Squish, Pack--require data + details.
|
||||
else if (data.extra_info === "no_detail" || data.dataset_type === "summary_tree") {
|
||||
else if (data.extra_info === "no_detail") {
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
return true;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns true if data can be subsetted.
|
||||
*/
|
||||
can_subset: function(data) {
|
||||
// Do not subset summary tree data, entries with a message, or data with no detail.
|
||||
if (data.dataset_type === "summary_tree" || data.message || data.extra_info === "no_detail") {
|
||||
// Do not subset entries with a message or data with no detail.
|
||||
if (data.dataset_type === 'bigwig' || data.message || data.extra_info === "no_detail") {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -4217,9 +4264,9 @@ extend(VariantTrack.prototype, Drawable.prototype, TiledTrack.prototype, {
|
||||
* Draw tile.
|
||||
*/
|
||||
draw_tile: function(result, ctx, mode, resolution, region, w_scale) {
|
||||
// Data could be summary tree data or variant data.
|
||||
if (result.dataset_type === 'summary_tree') {
|
||||
return this._draw_summary_tree_tile(result, ctx, region, resolution, w_scale);
|
||||
// Data could be coverage data or variant data.
|
||||
if (result.dataset_type === 'bigwig') {
|
||||
return this._draw_line_track_tile(result, ctx, "Histogram", region, resolution, w_scale);
|
||||
}
|
||||
else { // result.dataset_type === 'variant'
|
||||
var view = this.view,
|
||||
@@ -4235,7 +4282,7 @@ extend(VariantTrack.prototype, Drawable.prototype, TiledTrack.prototype, {
|
||||
* number of pixels required.
|
||||
*/
|
||||
get_canvas_height: function(result, mode, w_scale, canvas_width) {
|
||||
if (result.dataset_type === 'summary_tree') {
|
||||
if (result.dataset_type === 'bigwig') {
|
||||
return this.summary_draw_height;
|
||||
}
|
||||
else {
|
||||
@@ -4264,9 +4311,11 @@ extend(VariantTrack.prototype, Drawable.prototype, TiledTrack.prototype, {
|
||||
* Additional initialization required before drawing track for the first time.
|
||||
*/
|
||||
predraw_init: function() {
|
||||
var deferreds = [ Track.prototype.predraw_init.call(this) ];
|
||||
if (!this.dataset.get_metadata('sample_names')) {
|
||||
return this.dataset.fetch();
|
||||
deferreds.push(this.dataset.fetch());
|
||||
}
|
||||
return deferreds;
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -4277,7 +4326,7 @@ extend(VariantTrack.prototype, Drawable.prototype, TiledTrack.prototype, {
|
||||
TiledTrack.prototype.postdraw_actions.call(this, tiles, width, w_scale, clear_after);
|
||||
|
||||
// Add summary/sample labels if needed and not already included.
|
||||
if ( !(tiles[0] instanceof SummaryTreeTile) && this.prefs.show_labels) {
|
||||
if ( !(tiles[0] instanceof LineTrackTile) && this.prefs.show_labels) {
|
||||
var font_size;
|
||||
|
||||
// Add and/or style labels.
|
||||
@@ -4343,7 +4392,6 @@ var ReadTrack = function (view, container, obj_dict) {
|
||||
{ key: 'show_insertions', label: 'Show insertions', type: 'bool', default_value: false },
|
||||
{ key: 'show_differences', label: 'Show differences only', type: 'bool', default_value: true },
|
||||
{ key: 'show_counts', label: 'Show summary counts', type: 'bool', default_value: true },
|
||||
{ key: 'histogram_max', label: 'Histogram maximum', type: 'float', default_value: null, help: 'Clear value to set automatically' },
|
||||
{ key: 'mode', type: 'string', default_value: this.mode, hidden: true }
|
||||
],
|
||||
saved_values: obj_dict.prefs,
|
||||
|
||||
@@ -505,9 +505,6 @@ var GenomeDataManager = Cache.extend({
|
||||
// FIXME: constant should go somewhere.
|
||||
extra_params.num_samples = 1000 * detail_multiplier;
|
||||
}
|
||||
else if (cur_data.dataset_type === 'summary_tree') {
|
||||
extra_params.level = Math.min(cur_data.level - 1, 2);
|
||||
}
|
||||
|
||||
return this.load_data(region, mode, resolution, extra_params);
|
||||
},
|
||||
@@ -579,7 +576,7 @@ var GenomeDataManager = Cache.extend({
|
||||
data_point[0] <= subregion.get('end');
|
||||
});
|
||||
},
|
||||
'refseq': function(data, subregion) {
|
||||
refseq: function(data, subregion) {
|
||||
var seq_start = subregion.get('start') - entry.region.get('start'),
|
||||
seq_end = entry.data.length - ( entry.region.get('end') - subregion.get('end') );
|
||||
return entry.data.slice(seq_start, seq_end);
|
||||
|
||||
Reference in New Issue
Block a user