Skip to content

Commit

Permalink
Save time pileup renderer
Browse files Browse the repository at this point in the history
  • Loading branch information
cmdcolin committed Nov 16, 2021
1 parent e35e33c commit 541012f
Show file tree
Hide file tree
Showing 5 changed files with 61 additions and 121 deletions.
28 changes: 9 additions & 19 deletions plugins/alignments/src/BamAdapter/BamSlightlyLazyFeature.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,16 +153,12 @@ export default class BamSlightlyLazyFeature implements Feature {
}

toJSON(): SimpleFeatureSerialized {
const tags = Object.fromEntries(
this.tags()
.map(t => {
return [t, this.get(t)]
})
.filter(elt => elt[1] !== undefined),
)

return {
...tags,
...Object.fromEntries(
this.tags()
.map(t => [t, this.get(t)])
.filter(elt => elt[1] !== undefined),
),
uniqueId: this.id(),
}
}
Expand Down Expand Up @@ -201,24 +197,18 @@ export default class BamSlightlyLazyFeature implements Feature {

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

// 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'),
this.qualRaw(),
),
mdToMismatches(mdString, cigarOps, mismatches, seq, qual),
)
}

Expand Down
11 changes: 6 additions & 5 deletions plugins/alignments/src/BamAdapter/MismatchParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -150,18 +150,19 @@ export function mdToMismatches(
}

// now actually parse the MD string
const md = mdstring.match(/(\d+|\^[a-z]+|[a-z])/gi) || []
const md = mdstring.match(/(\d+|\^[A-Za-z]+|[A-Za-z])/g) || []
for (let i = 0; i < md.length; i++) {
const token = md[i]
if (token.match(/^\d/)) {
curr.start += parseInt(token, 10)
} else if (token.match(/^\^/)) {
const num = +token
if (!Number.isNaN(num)) {
curr.start += num
} else if (token.startsWith('^')) {
curr.length = token.length - 1
curr.base = '*'
curr.type = 'deletion'
curr.seq = token.substring(1)
nextRecord()
} else if (token.match(/^[a-z]/i)) {
} else {
// mismatch
for (let j = 0; j < token.length; j += 1) {
curr.length = 1
Expand Down
74 changes: 46 additions & 28 deletions plugins/alignments/src/PileupRenderer/PileupRenderer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ import { renderToAbstractCanvas } from '@jbrowse/core/util/offscreenCanvasUtils'
import { BaseLayout } from '@jbrowse/core/util/layouts/BaseLayout'
import { getAdapter } from '@jbrowse/core/data_adapters/dataAdapterCache'
import { readConfObject } from '@jbrowse/core/configuration'

// locals
import {
Mismatch,
parseCigar,
Expand All @@ -26,21 +28,12 @@ import {
} from '../BamAdapter/MismatchParser'
import { sortFeature } from './sortUtil'
import { getTagAlt, orientationTypes } from '../util'

import {
PileupLayoutSession,
PileupLayoutSessionProps,
} from './PileupLayoutSession'
import { BaseFeatureDataAdapter } from '@jbrowse/core/data_adapters/BaseAdapter'

export type {
RenderArgs,
RenderArgsSerialized,
RenderResults,
ResultsSerialized,
ResultsDeserialized,
}

function getColorBaseMap(theme: Theme) {
return {
A: theme.palette.bases.A.main,
Expand Down Expand Up @@ -94,7 +87,7 @@ const alignmentColoring: { [key: string]: string } = {
color_pair_rr: 'navy',
color_pair_rl: 'teal',
color_pair_ll: 'green',
color_nostrand: '#999',
color_nostrand: '#c8c8c8',
color_interchrom: 'orange',
color_longinsert: 'red',
color_shortinsert: 'pink',
Expand All @@ -121,21 +114,30 @@ export default class PileupRenderer extends BoxRendererType {
return { charWidth, charHeight }
}

layoutFeature(
feature: Feature,
layout: BaseLayout<Feature>,
config: AnyConfigurationModel,
bpPerPx: number,
region: Region,
showSoftClip?: boolean,
): LayoutRecord | null {
layoutFeature({
feature,
layout,
bpPerPx,
region,
showSoftClip,
heightPx,
displayMode,
}: {
feature: Feature
layout: BaseLayout<Feature>
bpPerPx: number
region: Region
showSoftClip?: boolean
heightPx: number
displayMode: string
}): LayoutRecord | null {
let expansionBefore = 0
let expansionAfter = 0
const mismatches: Mismatch[] = feature.get('mismatches')
const seq: string = feature.get('seq')

// Expand the start and end of feature when softclipping enabled
if (showSoftClip && seq) {
const mismatches: Mismatch[] = feature.get('mismatches')
for (let i = 0; i < mismatches.length; i += 1) {
const { type, start, cliplen = 0 } = mismatches[i]
if (type === 'softclip') {
Expand All @@ -151,8 +153,6 @@ export default class PileupRenderer extends BoxRendererType {
bpPerPx,
)

let heightPx = readConfObject(config, 'height', { feature })
const displayMode = readConfObject(config, 'displayMode', { feature })
if (displayMode === 'compact') {
heightPx /= 3
}
Expand Down Expand Up @@ -554,7 +554,7 @@ export default class PileupRenderer extends BoxRendererType {
// fetchValues
else {
const foundValue = colorTagMap[val]
ctx.fillStyle = foundValue || 'color_nostrand'
ctx.fillStyle = foundValue || alignmentColoring['color_nostrand']
}
break
}
Expand All @@ -563,7 +563,7 @@ export default class PileupRenderer extends BoxRendererType {

case 'normal':
default:
ctx.fillStyle = readConfObject(config, 'color', { feature })
ctx.fillStyle = '#c8c8c8'
break
}

Expand Down Expand Up @@ -598,6 +598,8 @@ export default class PileupRenderer extends BoxRendererType {
drawIndels?: boolean
minSubfeatureWidth: number
largeInsertionIndicatorScale: number
charWidth: number
charHeight: number
},
) {
const {
Expand All @@ -606,10 +608,11 @@ export default class PileupRenderer extends BoxRendererType {
mismatchAlpha,
drawSNPs = true,
drawIndels = true,
charWidth,
charHeight,
} = opts
const { bpPerPx, regions } = props
const { heightPx, topPx, feature } = feat
const { charWidth, charHeight } = this.getCharWidthHeight(ctx)
const [region] = regions
const start = feature.get('start')

Expand Down Expand Up @@ -836,6 +839,8 @@ export default class PileupRenderer extends BoxRendererType {
throw new Error('invalid layout object')
}
ctx.font = 'bold 10px Courier New,monospace'

const { charWidth, charHeight } = this.getCharWidthHeight(ctx)
// eslint-disable-next-line @typescript-eslint/no-explicit-any
layoutRecords.forEach((feat: any) => {
if (feat === null) {
Expand All @@ -844,14 +849,15 @@ export default class PileupRenderer extends BoxRendererType {

const { feature, topPx, heightPx } = feat

ctx.fillStyle = readConfObject(config, 'color', { feature })
this.drawAlignmentRect(ctx, { feature, topPx, heightPx }, props)
this.drawMismatches(ctx, feat, props, theme, colorForBase, {
mismatchAlpha,
drawSNPs: shouldDrawMismatches(colorBy?.type),
drawIndels: shouldDrawMismatches(colorBy?.type),
largeInsertionIndicatorScale: insertScale,
minSubfeatureWidth,
charWidth,
charHeight,
})
if (showSoftClip) {
this.drawSoftClipping(ctx, feat, props, config, theme)
Expand Down Expand Up @@ -884,17 +890,21 @@ export default class PileupRenderer extends BoxRendererType {
? sortFeature(features, sortedBy)
: null
const featureMap = sortedFeatures || features

const heightPx = readConfObject(config, 'height')
const displayMode = readConfObject(config, 'displayMode')
const layoutRecords = iterMap(
featureMap.values(),
feature =>
this.layoutFeature(
this.layoutFeature({
feature,
layout,
config,
bpPerPx,
region,
showSoftClip,
),
heightPx,
displayMode,
}),
featureMap.size,
)
return layoutRecords
Expand Down Expand Up @@ -971,3 +981,11 @@ export default class PileupRenderer extends BoxRendererType {
return new PileupLayoutSession(args)
}
}

export type {
RenderArgs,
RenderArgsSerialized,
RenderResults,
ResultsSerialized,
ResultsDeserialized,
}
7 changes: 0 additions & 7 deletions plugins/alignments/src/PileupRenderer/configSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,6 @@ import { types } from 'mobx-state-tree'
export default ConfigurationSchema(
'PileupRenderer',
{
color: {
type: 'color',
description: 'the color of each feature in a pileup alignment',
defaultValue: `#c8c8c8`,
contextVariable: ['feature'],
},

orientationType: {
type: 'stringEnum',
model: types.enumeration('orientationType', ['fr', 'rf', 'ff']),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -975,68 +975,6 @@ exports[`ConfigurationEditor widget renders with defaults of the PileupTrack sch
<div
class="MuiFormGroup-root"
>
<div
class="MuiPaper-root makeStyles-paper MuiPaper-elevation1 MuiPaper-rounded"
>
<div
class="makeStyles-paperContent"
>
<div
class="MuiFormControl-root MuiTextField-root MuiFormControl-fullWidth"
>
<label
class="MuiFormLabel-root MuiInputLabel-root MuiInputLabel-formControl MuiInputLabel-animated MuiInputLabel-shrink MuiFormLabel-filled"
data-shrink="true"
>
color
</label>
<div
class="MuiInputBase-root MuiInput-root MuiInput-underline MuiInputBase-fullWidth MuiInput-fullWidth MuiInputBase-formControl MuiInput-formControl"
style="color: rgb(200, 200, 200); border-right-width: 25px; border-right-style: solid; border-right-color: #c8c8c8;"
>
<input
aria-invalid="false"
class="MuiInputBase-input MuiInput-input"
type="text"
value="#c8c8c8"
/>
</div>
<p
class="MuiFormHelperText-root MuiFormHelperText-filled"
>
the color of each feature in a pileup alignment
</p>
</div>
</div>
<div
class="makeStyles-slotModeSwitch"
>
<button
class="MuiButtonBase-root MuiIconButton-root MuiIconButton-colorSecondary"
tabindex="0"
title="convert to callback"
type="button"
>
<span
class="MuiIconButton-label"
>
<svg
aria-hidden="true"
class="MuiSvgIcon-root"
focusable="false"
viewBox="0 0 24 24"
>
<path
d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8z"
/>
</svg>
</span>
<span
class="MuiTouchRipple-root"
/>
</button>
</div>
</div>
<div
class="MuiPaper-root makeStyles-paper MuiPaper-elevation1 MuiPaper-rounded"
>
Expand Down

0 comments on commit 541012f

Please sign in to comment.