trackster: load block data incrementally, huge performance gains from drawing smaller tiles

This commit is contained in:
Kanwei Li
2009-11-19 18:55:35 -05:00
parent 4c7e009359
commit fb00e2b7a2
4 changed files with 42 additions and 47 deletions
@@ -18,11 +18,10 @@ class IntervalIndexDataProvider( object ):
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 = { 'uid': uid, 'start': start, 'end': end, 'name': feature[3] }
payload = { 'uid': offset, 'start': start, 'end': end, 'name': feature[3] }
try:
payload['strand'] = feature[5]
except IndexError:
@@ -44,6 +43,5 @@ class IntervalIndexDataProvider( object ):
pass
results.append(payload)
uid += 1
return results
+1 -1
View File
@@ -1 +1 @@
$.fn.makeAbsolute=function(a){return this.each(function(){var b=$(this);var c=b.position();b.css({position:"absolute",marginLeft:0,marginTop:0,top:c.top,left:c.left,right:$(window).width()-(c.left+b.width())});if(a){b.remove().appendTo("body")}})};jQuery(document).ready(function(){jQuery("a[confirm]").click(function(){return confirm(jQuery(this).attr("confirm"))});jQuery("div[popupmenu]").each(function(){var c={};$(this).find("a").each(function(){var b=$(this).attr("confirm"),d=$(this).attr("href"),e=$(this).attr("target");c[$(this).text()]=function(){if(!b||confirm(b)){var g=window;if(e=="_parent"){g=window.parent}g.location=d}}});var a=$("#"+$(this).attr("popupmenu"));make_popupmenu(a,c);$(this).remove();a.show()})});function ensure_popup_helper(){if($("#popup-helper").length==0){$("<div id='popup-helper'/>").css({background:"white",opacity:0,zIndex:15000,position:"absolute",top:0,left:0,width:"100%",height:"100%"}).appendTo("body").hide()}}function make_popupmenu(d,c){ensure_popup_helper();var a=$(d);var b=$("<ul id='"+d.attr("id")+"-menu'></ul>");$.each(c,function(g,f){if(f){$("<li/>").html(g).click(f).appendTo(b)}else{$("<li class='head'/>").html(g).appendTo(b)}});var e=$("<div class='popmenu-wrapper'>");e.append(b).append("<div class='overlay-border'>").css("position","absolute").appendTo("body").hide();attach_popupmenu(d,e)}function attach_popupmenu(b,d){var a=function(){d.unbind().hide();$("#popup-helper").unbind("click.popupmenu").hide()};var c=function(g){var h=$(b).offset();$("#popup-helper").bind("click.popupmenu",a).show();d.click(a).css({left:0,top:-1000}).show();var f=g.pageX-d.width()/2;f=Math.min(f,$(document).scrollLeft()+$(window).width()-$(d).width()-20);f=Math.max(f,$(document).scrollLeft()+20);d.css({top:g.pageY-5,left:f});return false};$(b).click(c)};
$.fn.makeAbsolute=function(a){return this.each(function(){var b=$(this);var c=b.position();b.css({position:"absolute",marginLeft:0,marginTop:0,top:c.top,left:c.left,right:$(window).width()-(c.left+b.width())});if(a){b.remove().appendTo("body")}})};jQuery(document).ready(function(){jQuery("a[confirm]").click(function(){return confirm(jQuery(this).attr("confirm"))});make_popup_menus()});function make_popup_menus(){jQuery("div[popupmenu]").each(function(){var c={};$(this).find("a").each(function(){var b=$(this).attr("confirm"),d=$(this).attr("href"),e=$(this).attr("target");c[$(this).text()]=function(){if(!b||confirm(b)){var g=window;if(e=="_parent"){g=window.parent}g.location=d}}});var a=$("#"+$(this).attr("popupmenu"));make_popupmenu(a,c);$(this).remove();a.show()})}function ensure_popup_helper(){if($("#popup-helper").length==0){$("<div id='popup-helper'/>").css({background:"white",opacity:0,zIndex:15000,position:"absolute",top:0,left:0,width:"100%",height:"100%"}).appendTo("body").hide()}}function make_popupmenu(d,c){ensure_popup_helper();var a=$(d);var b=$("<ul id='"+d.attr("id")+"-menu'></ul>");$.each(c,function(g,f){if(f){$("<li/>").html(g).click(f).appendTo(b)}else{$("<li class='head'/>").html(g).appendTo(b)}});var e=$("<div class='popmenu-wrapper'>");e.append(b).append("<div class='overlay-border'>").css("position","absolute").appendTo("body").hide();attach_popupmenu(d,e)}function attach_popupmenu(b,d){var a=function(){d.unbind().hide();$("#popup-helper").unbind("click.popupmenu").hide()};var c=function(g){var h=$(b).offset();$("#popup-helper").bind("click.popupmenu",a).show();d.click(a).css({left:0,top:-1000}).show();var f=g.pageX-d.width()/2;f=Math.min(f,$(document).scrollLeft()+$(window).width()-$(d).width()-20);f=Math.max(f,$(document).scrollLeft()+20);d.css({top:g.pageY-5,left:f});return false};$(b).click(c)}var array_length=function(a){if(a.length){return a.length}var b=0;for(element in a){b++}return b};
File diff suppressed because one or more lines are too long
+39 -42
View File
@@ -1,7 +1,7 @@
/* Trackster
2009, James Taylor, Kanwei Li
*/
var DEBUG = true;
var DEBUG = false;
var DENSITY = 1000,
FEATURE_LEVELS = 100,
@@ -109,7 +109,7 @@ $.extend( View.prototype, {
// 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 ) );
this.zoom_res = Math.max(1,Math.ceil( Math.log( (this.high - this.low) / FEATURE_LEVELS ) / Math.log(FEATURE_LEVELS) ));
this.zoom_res = Math.max(1,Math.ceil( Math.log( this.resolution, FEATURE_LEVELS ) / Math.log(FEATURE_LEVELS) ));
// Overview
$("#overview-box").css( {
@@ -404,7 +404,7 @@ $.extend( FeatureTrack.prototype, TiledTrack.prototype, {
});
},
get_data: function( low, high ) {
console.log("getting: ", low, high);
// console.log("getting: ", low, high);
var track = this,
key = low + '_' + high;
@@ -420,39 +420,24 @@ $.extend( FeatureTrack.prototype, TiledTrack.prototype, {
});
}
},
calc_slots: function( include_labels ) {
calc_slots: function() {
// console.log("num vals: " + this.values.length);
var end_ary = [],
scale = this.content_div.width() / (this.view.high - this.view.low),
labels_scale = this.show_labels_scale,
max_high = this.view.max_high,
max_low = this.view.max_low;
// console.log(scale, this.view.high, this.view.low);
if (include_labels) {
this.zi_slots = {};
}
var dummy_canvas = $("<canvas></canvas>").get(0).getContext("2d");
for (var i = 0, len = this.values.length; i < len; i++) {
var f_start, f_end, feature = this.values[i];
if (include_labels) {
f_start = Math.floor( (feature.start - max_low) * labels_scale );
f_start -= dummy_canvas.measureText(feature.name).width;
f_end = Math.ceil( (feature.end - max_low) * labels_scale );
} else {
f_start = Math.floor( (feature.start - max_low) * scale );
f_end = Math.ceil( (feature.end - max_low) * scale );
}
f_start = Math.floor( (feature.start - max_low) * scale );
f_end = Math.ceil( (feature.end - max_low) * scale );
// if (include_labels) { console.log(f_start, f_end); }
var j = 0;
while (true) {
if (end_ary[j] === undefined || end_ary[j] < f_start) {
end_ary[j] = f_end;
if (include_labels) {
this.zi_slots[feature.uid] = j;
} else {
this.zo_slots[feature.uid] = j;
}
this.zo_slots[feature.uid] = j;
break;
}
j++;
@@ -464,20 +449,23 @@ $.extend( FeatureTrack.prototype, TiledTrack.prototype, {
incremental_slots: function( level, features ) {
if (!this.inc_slots[level]) {
this.inc_slots[level] = {};
this.inc_slots[level].w_scale = 1000 / Math.pow(FEATURE_LEVELS, level);
this.inc_slots[level].w_scale = DENSITY / Math.pow(FEATURE_LEVELS, level+1);
// this.inc_slots[level].w_scale = 1000 / (this.view.high - this.view.low);
}
var slots = this.inc_slots[level];
if (slots[uid]) {
return slots[uid];
}
// console.log(level, slots.w_scale, slots);
var end_ary = [],
undone = [],
max_high = this.view.max_high,
highest_slot = 0, // To measure how big to draw canvas
dummy_canvas = $("<canvas></canvas>").get(0).getContext("2d"),
max_low = this.view.max_low;
for (var i = 0, len = features.length; i < len; i++) {
var feature = features[i];
// console.log(feature.name, feature.uid, slots[feature.uid]);
if (slots[feature.uid]) {
highest_slot = Math.max(highest_slot, slots[feature.uid]);
end_ary[ slots[feature.uid] ] = Math.ceil( (feature.end - max_low) * slots.w_scale );
} else {
undone.push(feature);
@@ -488,6 +476,19 @@ $.extend( FeatureTrack.prototype, TiledTrack.prototype, {
f_start = Math.floor( (feature.start - max_low) * slots.w_scale );
f_start -= dummy_canvas.measureText(feature.name).width;
f_end = Math.ceil( (feature.end - max_low) * slots.w_scale );
// console.log(f_start, f_end, feature.name);
var j = 0;
while (true) {
if (end_ary[j] === undefined || end_ary[j] < f_start) {
end_ary[j] = f_end;
slots[feature.uid] = j;
highest_slot = Math.max(highest_slot, j);
break;
}
j++;
}
}
return highest_slot;
},
draw_tile: function( resolution, tile_index, parent_element, w_scale ) {
@@ -499,14 +500,10 @@ $.extend( FeatureTrack.prototype, TiledTrack.prototype, {
tile_span = DENSITY * resolution;
// console.log("drawing " + tile_index);
// Once we zoom in enough, show name labels
var data;
var data, slots, required_height;
if (w_scale > this.show_labels_scale) {
if (!this.showing_labels) {
this.showing_labels = true;
if (!this.zi_slots) {
this.calc_slots(true);
}
this.slots = this.zi_slots;
}
for (var k in this.data_cache.obj_cache) {
var k_split = k.split("_"), k_low = k_split[0], k_high = k_split[1];
@@ -520,20 +517,20 @@ $.extend( FeatureTrack.prototype, TiledTrack.prototype, {
this.get_data(tile_low, tile_high);
return;
}
required_height = this.incremental_slots( this.view.zoom_res, data ) * this.vertical_gap + 15;
// console.log(required_height);
slots = this.inc_slots[this.view.zoom_res];
} else {
if (this.showing_labels) {
this.showing_labels = false;
this.slots = this.zo_slots;
}
required_height = this.height_px;
slots = this.zo_slots;
data = this.values;
}
// console.log(this.slots);
// console.log(tile_low, tile_high, tile_length, w_scale);
var width = Math.ceil( tile_span * w_scale ),
height = this.height_px,
new_canvas = $("<canvas class='tile'></canvas>");
new_canvas.css({
@@ -542,7 +539,7 @@ $.extend( FeatureTrack.prototype, TiledTrack.prototype, {
left: ( tile_low - this.view.low ) * w_scale - this.left_offset
});
new_canvas.get(0).width = width + this.left_offset;
new_canvas.get(0).height = height;
new_canvas.get(0).height = required_height;
// console.log(( tile_low - this.view.low ) * w_scale, tile_index, w_scale);
var ctx = new_canvas.get(0).getContext("2d");
ctx.fillStyle = this.base_color;
@@ -555,7 +552,7 @@ $.extend( FeatureTrack.prototype, TiledTrack.prototype, {
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.uid] * this.vertical_gap;
y_center = slots[feature.uid] * this.vertical_gap;
var thickness, y_start, thick_start = null, thick_end = null;
if (feature.thick_start && feature.thick_end) {
@@ -569,7 +566,7 @@ $.extend( FeatureTrack.prototype, TiledTrack.prototype, {
// Showing labels, blocks, details
if (ctx.fillText && feature.start > tile_low) {
ctx.fillText(feature.name, f_start - 1 + this.left_offset, y_center + 8);
// ctx.fillText(commatize(feature.start), f_start - 1, y_center + 8);
// ctx.fillText(commatize(feature.start), f_start - 1 + this.left_offset, y_center + 8);
}
var blocks = feature.blocks;
if (blocks) {