details: http://www.bx.psu.edu/hg/galaxy/rev/d7c66019de13 changeset: 2955:d7c66019de13 user: Kanwei Li <kanwei@gmail.com> date: Tue Nov 03 19:16:01 2009 -0500 description: trackster: much greater resolution for line tracks (might still need to tweak BLOCK_SIZE), fixed stacking issue in feature tracks for features sharing same name) diffstat: lib/galaxy/datatypes/converters/wiggle_to_array_tree_converter.py | 4 +- lib/galaxy/visualization/tracks/data/array_tree.py | 45 ++++++----- lib/galaxy/visualization/tracks/data/interval_index.py | 6 +- lib/galaxy/web/controllers/tracks.py | 6 +- static/scripts/trackster.js | 46 +++++++---- static/trackster.css | 1 + templates/tracks/browser.mako | 1 + 7 files changed, 65 insertions(+), 44 deletions(-) diffs (293 lines): diff -r 46791b5a653b -r d7c66019de13 lib/galaxy/datatypes/converters/wiggle_to_array_tree_converter.py --- a/lib/galaxy/datatypes/converters/wiggle_to_array_tree_converter.py Tue Nov 03 17:16:40 2009 -0500 +++ b/lib/galaxy/datatypes/converters/wiggle_to_array_tree_converter.py Tue Nov 03 19:16:01 2009 -0500 @@ -8,6 +8,8 @@ from bx.arrays.array_tree import * from bx.arrays.wiggle import IntervalReader +BLOCK_SIZE = 1000 + def main(): input_fname = sys.argv[1] @@ -16,7 +18,7 @@ reader = IntervalReader( open( input_fname ) ) # Fill array from wiggle - d = array_tree_dict_from_wiggle_reader( reader, {} ) + d = array_tree_dict_from_wiggle_reader( reader, {}, block_size = BLOCK_SIZE ) for value in d.itervalues(): value.root.build_summary() diff -r 46791b5a653b -r d7c66019de13 lib/galaxy/visualization/tracks/data/array_tree.py --- a/lib/galaxy/visualization/tracks/data/array_tree.py Tue Nov 03 17:16:40 2009 -0500 +++ b/lib/galaxy/visualization/tracks/data/array_tree.py Tue Nov 03 19:16:01 2009 -0500 @@ -31,11 +31,16 @@ f.close() return { 'max': float( max(root_summary.maxs) ), 'min': float( min(root_summary.mins) ) } - def get_data( self, chrom, start, end ): + def get_data( self, chrom, start, end, **kwargs ): start = int( start ) end = int( end ) - level = int( ceil( log( end - start, BLOCK_SIZE ) ) ) - 1 - + resolution = max(1, ceil(float(kwargs['resolution']))) + + level = int( floor( log( resolution, BLOCK_SIZE ) ) ) + level = max( level, 0 ) + stepsize = BLOCK_SIZE ** level + step1 = stepsize * BLOCK_SIZE + # Open the file f = open( self.dataset.file_name ) d = FileArrayTreeDict( f ) @@ -47,22 +52,20 @@ # Is the requested level valid? assert 0 <= level <= chrom_array_tree.levels # Calculate the actual start/range/step of the block we're getting - size = BLOCK_SIZE ** (level+1) - block_start = ( start // BLOCK_SIZE ) * BLOCK_SIZE - block_step = size // BLOCK_SIZE - indexes = range( block_start, block_start + size, block_step ) - # Return either data point or a summary depending on the level - if level > 0: - s = chrom_array_tree.get_summary( start, level ) - f.close() - if s is not None: - return zip( indexes, map( float, s.sums / s.counts ) ) + + results = [] + for block_start in range( start, end, stepsize * BLOCK_SIZE ): + # print block_start + # Return either data point or a summary depending on the level + indexes = range( block_start, block_start + stepsize * BLOCK_SIZE, stepsize ) + if level > 0: + s = chrom_array_tree.get_summary( block_start, level ) + if s is not None: + results.extend( zip( indexes, map( float, s.sums / s.counts ) ) ) else: - return None - else: - v = chrom_array_tree.get_leaf( start ) - f.close() - if v is not None: - return zip( indexes, map( float, v ) ) - else: - return None \ No newline at end of file + v = chrom_array_tree.get_leaf( block_start ) + if v is not None: + results.extend( zip( indexes, map( float, v ) ) ) + + f.close() + return results diff -r 46791b5a653b -r d7c66019de13 lib/galaxy/visualization/tracks/data/interval_index.py --- a/lib/galaxy/visualization/tracks/data/interval_index.py Tue Nov 03 17:16:40 2009 -0500 +++ b/lib/galaxy/visualization/tracks/data/interval_index.py Tue Nov 03 19:16:01 2009 -0500 @@ -11,17 +11,18 @@ self.original_dataset = original_dataset self.converted_dataset = converted_dataset - def get_data( self, chrom, start, end ): + def get_data( self, chrom, start, end, **kwargs ): start, end = int(start), int(end) chrom = str(chrom) source = open( self.original_dataset.file_name ) index = Indexes( self.converted_dataset.file_name ) results = [] + uid = 0 for start, end, offset in index.find(chrom, start, end): source.seek(offset) feature = source.readline().split() - payload = { 'start': start, 'end': end, 'name': feature[3] } + payload = { 'uid': uid, 'start': start, 'end': end, 'name': feature[3] } try: payload['strand'] = feature[5] except IndexError: @@ -41,5 +42,6 @@ pass results.append(payload) + uid += 1 return results diff -r 46791b5a653b -r d7c66019de13 lib/galaxy/web/controllers/tracks.py --- a/lib/galaxy/web/controllers/tracks.py Tue Nov 03 17:16:40 2009 -0500 +++ b/lib/galaxy/web/controllers/tracks.py Tue Nov 03 19:16:01 2009 -0500 @@ -184,7 +184,7 @@ return manifest @web.json - def data( self, trans, dataset_id, track_type, chrom, low, high, stats=False ): + def data( self, trans, dataset_id, track_type, chrom, low, high, **kwargs ): """ Called by the browser to request a block of data """ @@ -216,10 +216,10 @@ data_provider = dataset_type_to_data_provider[ converted_dataset_type ]( converted_dataset, dataset ) # Return stats if we need them - if stats: return data_provider.get_stats( chrom ) + if 'stats' in kwargs: return data_provider.get_stats( chrom ) # Get the requested chunk of data - return data_provider.get_data( chrom, low, high ) + return data_provider.get_data( chrom, low, high, **kwargs ) def __dataset_as_type( self, trans, dataset, type ): """ diff -r 46791b5a653b -r d7c66019de13 static/scripts/trackster.js --- a/static/scripts/trackster.js Tue Nov 03 17:16:40 2009 -0500 +++ b/static/scripts/trackster.js Tue Nov 03 19:16:01 2009 -0500 @@ -1,13 +1,15 @@ /* Trackster 2009, James Taylor, Kanwei Li */ +var DEBUG = false; var DENSITY = 1000, DATA_ERROR = "There was an error in indexing this dataset.", DATA_NONE = "No data for this chrom/contig.", DATA_PENDING = "Currently indexing... please wait", DATA_LOADING = "Loading data...", - CACHED_TILES = 10, + CACHED_TILES_FEATURE = 10, + CACHED_TILES_LINE = 30, CACHED_DATA = 20, CONTEXT = $("<canvas></canvas>").get(0).getContext("2d"), RIGHT_STRAND, LEFT_STRAND; @@ -104,6 +106,9 @@ this.high = Math.ceil(high); this.center = Math.round( this.low + (this.high - this.low) / 2 ); + // 10^log10(range / DENSITY) Close approximation for browser window, assuming DENSITY = window width + this.resolution = Math.pow( 10, Math.ceil( Math.log( (this.high - this.low) / DENSITY ) / Math.LN10 ) ); + // Overview $("#overview-box").css( { left: ( this.low / this.span ) * $("#overview-viewport").width(), @@ -157,18 +162,16 @@ }); var TiledTrack = function() { - this.tile_cache = new Cache(CACHED_TILES); - // this.tile_cache = {}; }; $.extend( TiledTrack.prototype, Track.prototype, { draw: function() { var low = this.view.low, high = this.view.high, - range = high - low; - - var resolution = Math.pow( 10, Math.ceil( Math.log( range / DENSITY ) / Math.log( 10 ) ) ); - resolution = Math.max( resolution, 0.1 ); - resolution = Math.min( resolution, 1000000 ); + range = high - low, + resolution = this.view.resolution; + + + if (DEBUG) { $("#debug").text(resolution); } var parent_element = $("<div style='position: relative;'></div>"); this.content_div.children( ":first" ).remove(); @@ -187,7 +190,7 @@ // console.log("cached tile " + tile_index); var tile_low = tile_index * DENSITY * resolution; cached.css( { - left: ( tile_low - this.view.low ) * w_scale + left: ( tile_low - low ) * w_scale }); // Our responsibility to move the element to the new parent parent_element.append( cached ); @@ -229,6 +232,7 @@ }); var LineTrack = function ( name, dataset_id, height ) { + this.tile_cache = new Cache(CACHED_TILES_LINE); Track.call( this, name, $("#viewport") ); TiledTrack.call( this ); @@ -236,6 +240,7 @@ this.height_px = (height ? height : 100); this.container_div.addClass( "line-track" ); this.dataset_id = dataset_id; + this.data_queue = {}; this.cache = new Cache(CACHED_DATA); // We need to cache some data because of // asynchronous calls }; @@ -282,11 +287,17 @@ low = position * DENSITY * resolution, high = ( position + 1 ) * DENSITY * resolution, key = resolution + "_" + position; - - $.getJSON( data_url, { track_type: this.track_type, chrom: this.view.chrom, low: low, high: high, dataset_id: this.dataset_id }, function ( data ) { - track.cache[key] = data; - $(document).trigger( "redraw" ); - }); + + if (!track.data_queue[key]) { + track.data_queue[key] = true; + $.getJSON( data_url, { track_type: this.track_type, chrom: this.view.chrom, + low: low, high: high, dataset_id: this.dataset_id, + resolution: this.view.resolution }, function ( data ) { + track.cache[key] = data; + delete track.data_queue[key]; + track.draw(); + }); + } }, draw_tile: function( resolution, tile_index, parent_element, w_scale ) { if (!this.vertical_range) { // We don't have the necessary information yet @@ -340,6 +351,7 @@ }); var FeatureTrack = function ( name, dataset_id, height ) { + this.tile_cache = new Cache(CACHED_TILES_FEATURE); Track.call( this, name, $("#viewport") ); TiledTrack.call( this ); @@ -409,9 +421,9 @@ if (end_ary[j] === undefined || end_ary[j] < f_start) { end_ary[j] = f_end; if (include_labels) { - this.zi_slots[feature.name] = j; + this.zi_slots[feature.uid] = j; } else { - this.zo_slots[feature.name] = j; + this.zo_slots[feature.uid] = j; } break; } @@ -466,7 +478,7 @@ if (feature.start <= tile_high && feature.end >= tile_low) { var f_start = Math.floor( Math.max(0, (feature.start - tile_low) * w_scale) ), f_end = Math.ceil( Math.min(width, (feature.end - tile_low) * w_scale) ), - y_center = this.slots[feature.name] * this.vertical_gap; + y_center = this.slots[feature.uid] * this.vertical_gap; var thickness, y_start, thick_start = null, thick_end = null; if (feature.thick_start && feature.thick_end) { diff -r 46791b5a653b -r d7c66019de13 static/trackster.css --- a/static/trackster.css Tue Nov 03 17:16:40 2009 -0500 +++ b/static/trackster.css Tue Nov 03 19:16:01 2009 -0500 @@ -25,6 +25,7 @@ font-size: 100%; } +/*canvas{ border-right: 1px solid red; } /* debugging */ #nav { padding: 0.5em 0; background:#cccccc; diff -r 46791b5a653b -r d7c66019de13 templates/tracks/browser.mako --- a/templates/tracks/browser.mako Tue Nov 03 17:16:40 2009 -0500 +++ b/templates/tracks/browser.mako Tue Nov 03 19:16:01 2009 -0500 @@ -122,6 +122,7 @@ <a href="#" onclick="javascript:view.zoom_in();view.redraw();">+</a> <a href="#" onclick="javascript:view.zoom_out();view.redraw();">-</a> </form> + <div id="debug" style="float: right"></div> </div> </div> </div>