Skip to content

Commit

Permalink
Fix query->target synteny navigation
Browse files Browse the repository at this point in the history
  • Loading branch information
cmdcolin committed Dec 11, 2022
1 parent 2869340 commit 6a6f6d4
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 41 deletions.
50 changes: 49 additions & 1 deletion plugins/alignments/src/BamAdapter/MismatchParser.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { revcom } from '@jbrowse/core/util'

export interface Mismatch {
qual?: number
start: number
Expand All @@ -11,9 +12,12 @@ export interface Mismatch {
}
const mdRegex = new RegExp(/(\d+|\^[a-z]+|[a-z])/gi)
const modificationRegex = new RegExp(/([A-Z])([-+])([^,.?]+)([.?])?/)
const cigarRegex = new RegExp(/([MIDNSHPX=])/)

export function parseCigar(cigar = '') {
return cigar.split(/([MIDNSHPX=])/).slice(0, -1)
return cigar.split(cigarRegex).slice(0, -1)
}

export function cigarToMismatches(
ops: string[],
seq?: string,
Expand Down Expand Up @@ -339,3 +343,47 @@ export function getModificationTypes(mm: string) {
})
.flat()
}

export function getOrientedCigar(flip: boolean, cigar: string[]) {
if (flip) {
const ret = []
for (let i = 0; i < cigar.length; i += 2) {
const len = cigar[i]
let op = cigar[i + 1]
if (op === 'D') {
op = 'I'
} else if (op === 'I') {
op = 'D'
}
ret.push(len)
ret.push(op)
}
return ret
} else {
return cigar
}
}

export function getOrientedMismatches(flip: boolean, cigar: string) {
const mismatches = getMismatches(cigar)
if (flip) {
let startReadjuster = 0
return mismatches.map(m => {
if (m.type === 'insertion') {
m.type = 'deletion'
m.length = +m.base
m.start += startReadjuster
startReadjuster += m.length
} else if (m.type === 'deletion') {
const len = m.length
m.type = 'insertion'
m.base = `${len}`
m.length = 0
m.start += startReadjuster
startReadjuster -= len
}
return m
})
}
return mismatches
}
36 changes: 6 additions & 30 deletions plugins/comparative-adapters/src/PAFAdapter/PAFAdapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { MismatchParser } from '@jbrowse/plugin-alignments'

// locals
import { zip, isGzip } from '../util'
const { getMismatches } = MismatchParser
const { getOrientedMismatches } = MismatchParser

export interface PAFRecord {
qname: string
Expand Down Expand Up @@ -131,30 +131,6 @@ function weightedMean(tuples: [number, number][]) {
return valueSum / weightSum
}

function getOrientedMismatches(flip: boolean, cigar: string) {
const mismatches = getMismatches(cigar)
if (flip) {
let startReadjuster = 0
return mismatches.map(m => {
if (m.type === 'insertion') {
m.type = 'deletion'
m.length = +m.base
m.start += startReadjuster
startReadjuster += m.length
} else if (m.type === 'deletion') {
const len = m.length
m.type = 'insertion'
m.base = `${len}`
m.length = 0
m.start += startReadjuster
startReadjuster -= len
}
return m
})
}
return mismatches
}

class SyntenyFeature extends SimpleFeature {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
get(arg: string): any {
Expand Down Expand Up @@ -332,13 +308,13 @@ export default class PAFAdapter extends BaseFeatureDataAdapter {
end,
type: 'match',
refName,
strand, // : !flip ? strand * -1 : strand,
strand,

// this is a special property of how to interpret CIGAR on PAF,
// intrinsic to the data format. the CIGAR is read backwards
// for features aligning to the negative strand of the target,
// which is actually different than how it works in e.g.
// BAM/SAM (which is visible during alignments track read vs ref)
// intrinsic to the data format. the CIGAR is read backwards for
// features aligning to the negative strand of the target, which
// is actually different than how it works in e.g. BAM/SAM (which
// is visible during alignments track read vs ref)
revCigar: true,

// depending on whether the query or target is queried, the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,13 @@ import { LinearSyntenyViewModel } from '../LinearSyntenyView/model'

type LSV = LinearSyntenyViewModel

const { parseCigar } = MismatchParser
const { parseCigar, getOrientedCigar } = MismatchParser

function findPosInCigar(cigar: string[], x: number) {
function findPosInCigar(inCigar: string[], flip: boolean, x: number) {
let featX = 0
let mateX = 0
for (let i = 0; i < cigar.length; i += 2) {
const cigar = getOrientedCigar(flip, inCigar)
for (let i = 0; i < cigar.length; i++) {
const len = +cigar[i]
const op = cigar[i + 1]
const min = Math.min(len, x - featX)
Expand Down Expand Up @@ -57,6 +58,7 @@ async function navToSynteny(feature: Feature, self: any) {
const featStart = feature.get('start')
const featEnd = feature.get('end')
const mate = feature.get('mate')
const flip = feature.get('flipInsDel')
const mateStart = mate.start
const mateEnd = mate.end
const mateAsm = mate.assemblyName
Expand All @@ -69,16 +71,16 @@ async function navToSynteny(feature: Feature, self: any) {
let rFeatStart = featStart
let rFeatEnd = featStart

if (cigar.length) {
const [featStartX, mateStartX] = findPosInCigar(cigar, regStart - featStart)
const [featEndX, mateEndX] = findPosInCigar(cigar, regEnd - featStart)
if (cigar) {
const [fStartX, mStartX] = findPosInCigar(cigar, flip, regStart - featStart)
const [fEndX, mEndX] = findPosInCigar(cigar, flip, regEnd - featStart)

// avoid multiply by 0 with strand undefined
const flipper = strand === -1 ? -1 : 1
rFeatStart = featStart + featStartX
rFeatEnd = featStart + featEndX
rMateStart = mateStart + mateStartX * flipper
rMateEnd = mateStart + mateEndX * flipper
rFeatStart = featStart + fStartX
rFeatEnd = featStart + fEndX
rMateStart = mateStart + mStartX * flipper
rMateEnd = mateStart + mEndX * flipper
} else {
rFeatEnd = featEnd
rMateEnd = mateEnd
Expand Down

0 comments on commit 6a6f6d4

Please sign in to comment.