Skip to content

Commit

Permalink
Standardize phred qual scaling between BAM and CRAM and add option to…
Browse files Browse the repository at this point in the history
… make mismatches render in a lighter color when quality is low (#1720)

* Render quality score of mismatch

* Bump @gmod/bam for qualRaw

* Color by mismatch quality

* Use just 1 bp for indicators

* Update tests

* Remove checker for the left/right bounds
  • Loading branch information
cmdcolin authored Feb 22, 2021
1 parent 02df96a commit 73bdb44
Show file tree
Hide file tree
Showing 9 changed files with 172 additions and 43 deletions.
2 changes: 1 addition & 1 deletion plugins/alignments/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
"useSrc": "node ../../scripts/useSrc.js"
},
"dependencies": {
"@gmod/bam": "^1.1.5",
"@gmod/bam": "^1.1.6",
"@gmod/cram": "^1.5.7",
"@material-ui/icons": "^4.9.1",
"abortable-promise-cache": "^1.1.3",
Expand Down
16 changes: 13 additions & 3 deletions plugins/alignments/src/BamAdapter/BamSlightlyLazyFeature.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,10 @@ export default class BamSlightlyLazyFeature implements Feature {
return md
}

qualRaw(): Buffer | undefined {
return this.record.qualRaw()
}

set(): void {}

tags() {
Expand Down Expand Up @@ -179,7 +183,7 @@ export default class BamSlightlyLazyFeature implements Feature {
if (cigarString) {
cigarOps = parseCigar(cigarString)
mismatches = mismatches.concat(
cigarToMismatches(cigarOps, this.get('seq')),
cigarToMismatches(cigarOps, this.get('seq'), this.qualRaw()),
)
}
return mismatches
Expand All @@ -200,15 +204,21 @@ export default class BamSlightlyLazyFeature implements Feature {
if (cigarString) {
cigarOps = parseCigar(cigarString)
mismatches = mismatches.concat(
cigarToMismatches(cigarOps, this.get('seq')),
cigarToMismatches(cigarOps, this.get('seq'), this.qualRaw()),
)
}

// now let's look for CRAM or MD mismatches
const mdString = this.get(mdAttributeName)
if (mdString) {
mismatches = mismatches.concat(
mdToMismatches(mdString, cigarOps, mismatches, this.get('seq')),
mdToMismatches(
mdString,
cigarOps,
mismatches,
this.get('seq'),
this.qualRaw(),
),
)
}

Expand Down
26 changes: 17 additions & 9 deletions plugins/alignments/src/BamAdapter/MismatchParser.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export interface Mismatch {
qual?: number
start: number
length: number
type: string
Expand All @@ -11,7 +12,11 @@ export interface Mismatch {
export function parseCigar(cigar: string) {
return (cigar || '').split(/([MIDNSHPX=])/)
}
export function cigarToMismatches(ops: string[], seq: string): Mismatch[] {
export function cigarToMismatches(
ops: string[],
seq: string,
qual?: Buffer,
): Mismatch[] {
let currOffset = 0
let seqOffset = 0
const mismatches: Mismatch[] = []
Expand Down Expand Up @@ -45,11 +50,14 @@ export function cigarToMismatches(ops: string[], seq: string): Mismatch[] {
})
} else if (op === 'X') {
const r = seq.slice(seqOffset, seqOffset + len)
const q = qual?.slice(seqOffset, seqOffset + len) || []

for (let j = 0; j < len; j++) {
mismatches.push({
start: currOffset + j,
type: 'mismatch',
base: r[j],
qual: q[j],
length: 1,
})
}
Expand Down Expand Up @@ -89,6 +97,7 @@ export function mdToMismatches(
cigarOps: string[],
cigarMismatches: Mismatch[],
seq: string,
qual?: Buffer,
): Mismatch[] {
const mismatchRecords: Mismatch[] = []
let curr: Mismatch = { start: 0, base: '', length: 0, type: 'mismatch' }
Expand Down Expand Up @@ -165,12 +174,10 @@ export function mdToMismatches(
break
}
}
curr.base = seq
? seq.substr(
cigarOps ? getTemplateCoordLocal(curr.start) : curr.start,
1,
)
: 'X'
const s = cigarOps ? getTemplateCoordLocal(curr.start) : curr.start
curr.base = seq ? seq.substr(s, 1) : 'X'
const qualScore = qual?.slice(s, s + 1)[0]
if (qualScore) curr.qual = qualScore
curr.altbase = token
nextRecord()
}
Expand Down Expand Up @@ -201,20 +208,21 @@ export function getMismatches(
cigarString: string,
mdString: string,
seq: string,
qual?: Buffer,
): Mismatch[] {
let mismatches: Mismatch[] = []
let cigarOps: string[] = []

// parse the CIGAR tag if it has one
if (cigarString) {
cigarOps = parseCigar(cigarString)
mismatches = mismatches.concat(cigarToMismatches(cigarOps, seq))
mismatches = mismatches.concat(cigarToMismatches(cigarOps, seq, qual))
}

// now let's look for CRAM or MD mismatches
if (mdString) {
mismatches = mismatches.concat(
mdToMismatches(mdString, cigarOps, mismatches, seq),
mdToMismatches(mdString, cigarOps, mismatches, seq, qual),
)
}

Expand Down
Loading

0 comments on commit 73bdb44

Please sign in to comment.