Skip to content

Commit

Permalink
Add drawMismatches to paired read track
Browse files Browse the repository at this point in the history
  • Loading branch information
cmdcolin committed Oct 31, 2018
1 parent 16ec158 commit 0da6cb7
Show file tree
Hide file tree
Showing 3 changed files with 138 additions and 7 deletions.
8 changes: 5 additions & 3 deletions src/JBrowse/Store/SeqFeature/BAM.js
Expand Up @@ -11,16 +11,17 @@ class PairedBamRead {
id() {
return Math.min(this.f1.id(), this.f2.id())
}
children() {
}
get(field) {
if(field == 'start') {
return Math.min(this.f1.get('start'), this.f2.get('start'))
} else if(field == 'end') {
return Math.max(this.f1.get('end'), this.f2.get('end'))
} else if(field == 'name') {
return this.f1.get('name')
}
}
pairedFeature() { return true }
children() {}
}

class BamSlightlyLazyFeature {
Expand Down Expand Up @@ -249,7 +250,7 @@ return declare( [ SeqFeatureStore, DeferredStatsMixin, DeferredFeaturesMixin, In
feat = pairCache[records[i]._get('name')]
if (feat) {
feat.f2 = this._bamRecordToFeature(records[i])
pairCache[records[i]._get('name')] = undefined
delete pairCache[records[i]._get('name')]
featCallback(feat)
}
else {
Expand All @@ -267,6 +268,7 @@ return declare( [ SeqFeatureStore, DeferredStatsMixin, DeferredFeaturesMixin, In
featCallback(this._bamRecordToFeature(records[i]))
}
}
console.log(pairCache)
endCallback()
})
.catch(err => {
Expand Down
80 changes: 76 additions & 4 deletions src/JBrowse/View/FeatureGlyph/PairedAlignment.js
Expand Up @@ -2,15 +2,15 @@ define([
'dojo/_base/declare',
'dojo/_base/array',
'dojo/_base/lang',
'JBrowse/View/FeatureGlyph/Box'
'JBrowse/View/FeatureGlyph/Alignment'
],
function(
declare,
array,
lang,
Box
Alignment
) {
return declare(Box, {
return declare(Alignment, {

renderFeature( context, fRect ) {
if( this.track.displayMode != 'collapsed' )
Expand All @@ -20,6 +20,8 @@ renderFeature( context, fRect ) {
if(fRect.f.pairedFeature()) {
this.renderConnector( context, fRect );
this.renderSegments( context, fRect );
this._drawMismatches( context, fRect, fRect.f.f1, this._getMismatches( fRect.f.f1 ) );
this._drawMismatches( context, fRect, fRect.f.f2, this._getMismatches( fRect.f.f2 ) );
} else {
this.inherited(arguments)
}
Expand Down Expand Up @@ -149,8 +151,78 @@ layoutFeature(viewArgs, layout, feature) {
}

return rect;
},

_drawMismatches( context, fRect, feature, mismatches ) {
var block = fRect.viewInfo.block;
var scale = block.scale;

var charSize = this.getCharacterMeasurements( context );
context.textBaseline = 'middle'; // reset to alphabetic (the default) after loop

array.forEach( mismatches, function( mismatch ) {
var start = feature.get('start') + mismatch.start;
var end = start + mismatch.length;

var mRect = {
h: (fRect.rect||{}).h || fRect.h,
l: block.bpToX( start ),
t: fRect.rect.t
};
mRect.w = Math.max( block.bpToX( end ) - mRect.l, 1 );

if( mismatch.type == 'mismatch' || mismatch.type == 'deletion' ) {
context.fillStyle = this.track.colorForBase( mismatch.type == 'deletion' ? 'deletion' : mismatch.base );
context.fillRect( mRect.l, mRect.t, mRect.w, mRect.h );

if( mRect.w >= charSize.w && mRect.h >= charSize.h-3 ) {
context.font = this.config.style.mismatchFont;
context.fillStyle = mismatch.type == 'deletion' ? 'white' : 'black';
context.fillText( mismatch.base, mRect.l+(mRect.w-charSize.w)/2+1, mRect.t+mRect.h/2 );
}
}
else if( mismatch.type == 'insertion' ) {
context.fillStyle = 'purple';
context.fillRect( mRect.l-1, mRect.t+1, 2, mRect.h-2 );
context.fillRect( mRect.l-2, mRect.t, 4, 1 );
context.fillRect( mRect.l-2, mRect.t+mRect.h-1, 4, 1 );
if( mRect.w >= charSize.w && mRect.h >= charSize.h-3 ) {
context.font = this.config.style.mismatchFont;
context.fillText( '('+mismatch.base+')', mRect.l+2, mRect.t+mRect.h/2 );
}
}
else if( mismatch.type == 'hardclip' || mismatch.type == 'softclip' ) {
context.fillStyle = mismatch.type == 'hardclip' ? 'red' : 'blue';
context.fillRect( mRect.l-1, mRect.t+1, 2, mRect.h-2 );
context.fillRect( mRect.l-2, mRect.t, 4, 1 );
context.fillRect( mRect.l-2, mRect.t+mRect.h-1, 4, 1 );
if( mRect.w >= charSize.w && mRect.h >= charSize.h-3 ) {
context.font = this.config.style.mismatchFont;
context.fillText( '('+mismatch.base+')', mRect.l+2, mRect.t+mRect.h/2 );
}
}
else if( mismatch.type == 'skip' ) {
context.clearRect( mRect.l, mRect.t, mRect.w, mRect.h );
context.fillStyle = '#333';
context.fillRect( mRect.l, mRect.t+(mRect.h-2)/2, mRect.w, 2 );
}
},this);

context.textBaseline = 'alphabetic';
},

getCharacterMeasurements: function( context ) {
return this.charSize = this.charSize || function() {
var fpx;

try {
fpx = (this.config.style.mismatchFont.match(/(\d+)px/i)||[])[1];
} catch(e) {}

fpx = fpx || Infinity;
return { w: fpx, h: fpx };
}.call(this);
}

});
});

57 changes: 57 additions & 0 deletions src/JBrowse/View/Track/PairedReads.js
@@ -1,12 +1,14 @@
define( [
'dojo/_base/declare',
'dojo/_base/array',
'dojo/_base/lang',
'JBrowse/View/Track/CanvasFeatures',
'JBrowse/View/Track/_PairedAlignmentsMixin',
'JBrowse/Util'
],
function(
declare,
array,
lang,
CanvasFeatures,
PairedAlignmentsMixin,
Expand Down Expand Up @@ -36,6 +38,61 @@ return declare([CanvasFeatures, PairedAlignmentsMixin], {
});
}
return layout;
},

// recursively find all the stylesheets that are loaded in the
// current browsing session, traversing imports and such
_getStyleSheets: function( inSheets ) {
var outSheets = []
array.forEach(inSheets, sheet => {
try {
let rules = sheet.cssRules || sheet.rules
let includedSheets = [sheet]
array.forEach(rules, rule => {
if (rule.styleSheet)
includedSheets.push(...this._getStyleSheets([rule.styleSheet]))
})
outSheets.push(...includedSheets)
} catch(e) {
//console.warn('could not read stylesheet',sheet)
}
})

return outSheets;
},

// get the appropriate HTML color string to use for a given base
// letter. case insensitive. 'reference' gives the color to draw matches with the reference.
colorForBase: function( base ) {
// get the base colors out of CSS
this._baseStyles = this._baseStyles || function() {
var colors = {};
try {
var styleSheets = this._getStyleSheets( document.styleSheets );
array.forEach( styleSheets, function( sheet ) {
// avoid modifying cssRules for plugins which generates SecurityException on Firefox
var classes = sheet.rules || sheet.cssRules;
if( ! classes ) return;
array.forEach( classes, function( c ) {
var match = /^\.jbrowse\s+\.base_([^\s_]+)$/.exec( c.selectorText );
if( match && match[1] ) {
var base = match[1];
match = /\#[0-9a-f]{3,6}|(?:rgb|hsl)a?\([^\)]*\)/gi.exec( c.cssText );
if( match && match[0] ) {
colors[ base.toLowerCase() ] = match[0];
colors[ base.toUpperCase() ] = match[0];
}
}
});
});
} catch(e) {
console.error(e)
/* catch errors from cross-domain stylesheets */
}
return colors;
}.call(this);

return this._baseStyles[base] || '#999';
}

});
Expand Down

0 comments on commit 0da6cb7

Please sign in to comment.