Skip to content

Commit

Permalink
Calculate most frequent non-ref allele for multi-variant renderer (#4826
Browse files Browse the repository at this point in the history
)

* Intermediate

* Small types

* Misc

* Move files around

* Misc

* wow

* Mov

* Async

* Most frequent alt
  • Loading branch information
cmdcolin authored Feb 8, 2025
1 parent ec30a72 commit 11563e3
Show file tree
Hide file tree
Showing 48 changed files with 962 additions and 887 deletions.
6 changes: 3 additions & 3 deletions plugins/hic/src/HicRenderer/HicRenderer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ export interface RenderArgsDeserialized
export interface RenderArgsDeserializedWithFeatures
extends RenderArgsDeserialized {
features: HicFeature[]
statusCallback?: (arg: string) => void
}

export type ResultsSerialized = ServerSideResultsSerialized
Expand Down Expand Up @@ -95,10 +96,9 @@ export default class HicRenderer extends ServerSideRendererType {
.getFeatures(regions[0]!, args)
.pipe(toArray()),
)
// cast to any to avoid return-type conflict, because the
// types of features returned by our getFeatures are quite
// different from the base interface

// cast to any to avoid return-type conflict, because the types of features
// returned by our getFeatures are quite different from the base interface
return features as any
}
}
Expand Down
1 change: 0 additions & 1 deletion plugins/hic/src/HicRenderer/makeImageData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ export async function makeImageData(
} = props

const { statusCallback = () => {} } = props
// @ts-expect-error
statusCallback('Drawing Hi-C matrix')
const region = regions[0]!
const { dataAdapter } = await getAdapter(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { getContainingView } from '@jbrowse/core/util'
import { observer } from 'mobx-react'
import { makeStyles } from 'tss-react/mui'

import MultiVariantTooltip from '../../shared/MultiVariantTooltip'
import MultiVariantTooltip from '../../shared/components/MultiVariantTooltip'

import type { MultiLinearVariantDisplayModel } from '../model'
import type { LinearGenomeViewModel } from '@jbrowse/plugin-linear-genome-view'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { BaseLinearDisplayComponent } from '@jbrowse/plugin-linear-genome-view'
import { observer } from 'mobx-react'

import Crosshair from './Crosshair'
import LegendBar from '../../shared/LegendBar'
import LegendBar from '../../shared/components/LegendBar'

import type { MultiLinearVariantDisplayModel } from '../model'

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { getContainingView } from '@jbrowse/core/util'
import { when } from 'mobx'

// locals
import LegendBar from '../shared/LegendBar'
import LegendBar from '../shared/components/LegendBar'

import type { MultiLinearVariantDisplayModel } from './model'
import type {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { getContainingView } from '@jbrowse/core/util'
import { observer } from 'mobx-react'
import { makeStyles } from 'tss-react/mui'

import MultiVariantTooltip from '../../shared/MultiVariantTooltip'
import MultiVariantTooltip from '../../shared/components/MultiVariantTooltip'

import type { MultiLinearVariantMatrixDisplayModel } from '../model'
import type { LinearGenomeViewModel } from '@jbrowse/plugin-linear-genome-view'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { observer } from 'mobx-react'

import Crosshair from './Crosshair'
import LinesConnectingMatrixToGenomicPosition from './LinesConnectingMatrixToGenomicPosition'
import LegendBar from '../../shared/LegendBar'
import LegendBar from '../../shared/components/LegendBar'

import type { MultiLinearVariantMatrixDisplayModel } from '../model'

Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import { getContainingView } from '@jbrowse/core/util'
import { when } from 'mobx'

// locals
import LegendBar from '../shared/LegendBar'
import LinesConnectingMatrixToGenomicPosition from './components/LinesConnectingMatrixToGenomicPosition'
import LegendBar from '../shared/components/LegendBar'

import type { MultiLinearVariantMatrixDisplayModel } from './model'
import type {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,13 @@ export default class LinearVariantMatrixRenderer extends BoxRendererType {
...res,
features: new Map(),
simplifiedFeatures: mafs.map(
(s: Feature) =>
({ feature }: { feature: Feature }) =>
new SimpleFeature({
id: s.id(),
id: feature.id(),
data: {
start: s.get('start'),
end: s.get('end'),
refName: s.get('refName'),
start: feature.get('start'),
end: feature.get('end'),
refName: feature.get('refName'),
},
}),
),
Expand Down
140 changes: 55 additions & 85 deletions plugins/variants/src/MultiLinearVariantMatrixRenderer/makeImageData.ts
Original file line number Diff line number Diff line change
@@ -1,45 +1,15 @@
import {
getColorAlleleCount,
getColorPhased,
getColorPhasedWithPhaseSet,
} from '../shared/multiVariantColor'
import { getFeaturesThatPassMinorAlleleFrequencyFilter } from '../util'
import { updateStatus } from '@jbrowse/core/util'

import type { RenderArgsDeserializedWithFeaturesAndLayout } from './types'

const fudgeFactor = 0.6
const f2 = fudgeFactor / 2
import { f2 } from '../shared/constants'
import { drawColorAlleleCount } from '../shared/drawAlleleCount'
import { drawPhased } from '../shared/drawPhased'
import { getFeaturesThatPassMinorAlleleFrequencyFilter } from '../shared/minorAlleleFrequencyUtils'

function drawColorAlleleCount(
alleles: string[],
ctx: CanvasRenderingContext2D,
x: number,
y: number,
w: number,
h: number,
) {
ctx.fillStyle = getColorAlleleCount(alleles)
ctx.fillRect(x - f2, y - f2, w + f2, h + f2)
}
import type { RenderArgsDeserializedWithFeaturesAndLayout } from './types'

function drawPhased(
alleles: string[],
ctx: CanvasRenderingContext2D,
x: number,
y: number,
w: number,
h: number,
HP: number,
PS?: string,
) {
ctx.fillStyle =
PS !== undefined
? getColorPhasedWithPhaseSet(alleles, HP, PS)
: getColorPhased(alleles, HP)
ctx.fillRect(x - f2, y - f2, w + f2, h + f2)
}
type SampleGenotype = Record<string, string[]>

export function makeImageData({
export async function makeImageData({
ctx,
canvasWidth,
canvasHeight,
Expand All @@ -58,78 +28,78 @@ export function makeImageData({
} = renderArgs

const { statusCallback = () => {} } = renderArgs
// @ts-expect-error
statusCallback('Drawing variant matrix')
const h = canvasHeight / sources.length
const mafs = getFeaturesThatPassMinorAlleleFrequencyFilter(
features.values(),
minorAlleleFrequencyFilter,
)

const arr = [] as string[][]
const m = mafs.length
const w = canvasWidth / m
for (let i = 0; i < m; i++) {
const arr2 = [] as string[]
const f = mafs[i]!
const hasPhaseSet = (f.get('format') as string).includes('PS')
if (hasPhaseSet) {
const samp = f.get('samples') as Record<string, Record<string, string[]>>
const x = (i / mafs.length) * canvasWidth
const sln = sources.length
for (let j = 0; j < sln; j++) {
const y = (j / sln) * canvasHeight
const { name, HP } = sources[j]!
const s = samp[name]
if (s) {
const genotype = s.GT?.[0]

await updateStatus('Drawing variant matrix', statusCallback, () => {
for (let i = 0; i < m; i++) {
const arr2 = [] as string[]
const { feature, mostFrequentAlt } = mafs[i]!
const hasPhaseSet = (feature.get('format') as string).includes('PS')
if (hasPhaseSet) {
const samp = feature.get('samples') as Record<string, SampleGenotype>
const x = (i / mafs.length) * canvasWidth
const sln = sources.length
for (let j = 0; j < sln; j++) {
const y = (j / sln) * canvasHeight
const { name, HP } = sources[j]!
const s = samp[name]
if (s) {
const genotype = s.GT?.[0]
if (genotype) {
arr2.push(genotype)
const isPhased = genotype.includes('|')
if (renderingMode === 'phased') {
if (isPhased) {
const PS = s.PS?.[0]
const alleles = genotype.split('|')
drawPhased(alleles, ctx, x, y, w, h, HP!, PS)
} else {
ctx.fillStyle = 'black'
ctx.fillRect(x - f2, y - f2, w + f2, h + f2)
}
} else {
const alleles = genotype.split(/[/|]/)
drawColorAlleleCount(alleles, ctx, x, y, w, h, mostFrequentAlt)
}
}
}
}
} else {
const samp = feature.get('genotypes') as Record<string, string>
const x = (i / mafs.length) * canvasWidth
const sln = sources.length
for (let j = 0; j < sln; j++) {
const y = (j / sln) * canvasHeight
const { name, HP } = sources[j]!
const genotype = samp[name]
if (genotype) {
arr2.push(genotype)
const isPhased = genotype.includes('|')
if (renderingMode === 'phased') {
if (isPhased) {
const PS = s.PS?.[0]
const alleles = genotype.split('|')
drawPhased(alleles, ctx, x, y, w, h, HP!, PS)
drawPhased(alleles, ctx, x, y, w, h, HP!)
} else {
ctx.fillStyle = 'black'
ctx.fillRect(x - f2, y - f2, w + f2, h + f2)
}
} else {
const alleles = genotype.split(/[/|]/)
drawColorAlleleCount(alleles, ctx, x, y, w, h)
drawColorAlleleCount(alleles, ctx, x, y, w, h, mostFrequentAlt)
}
}
}
}
} else {
const samp = f.get('genotypes') as Record<string, string>
const x = (i / mafs.length) * canvasWidth
const sln = sources.length
for (let j = 0; j < sln; j++) {
const y = (j / sln) * canvasHeight
const { name, HP } = sources[j]!
const genotype = samp[name]
if (genotype) {
arr2.push(genotype)
const isPhased = genotype.includes('|')
if (renderingMode === 'phased') {
if (isPhased) {
const alleles = genotype.split('|')
drawPhased(alleles, ctx, x, y, w, h, HP!)
} else {
ctx.fillStyle = 'black'
ctx.fillRect(x - f2, y - f2, w + f2, h + f2)
}
} else {
const alleles = genotype.split(/[/|]/)
drawColorAlleleCount(alleles, ctx, x, y, w, h)
}
}
}
arr.push(arr2)
}
arr.push(arr2)
}
})
return {
mafs,
arr,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { Source } from '../types'
import type { Source } from '../shared/types'
import type { RenderArgsDeserialized as BoxRenderArgsDeserialized } from '@jbrowse/core/pluggableElementTypes/renderers/BoxRendererType'
import type { Feature } from '@jbrowse/core/util'

Expand All @@ -15,4 +15,5 @@ export interface RenderArgsDeserializedWithFeaturesAndLayout
sources: Source[]
features: Map<string, Feature>
renderingMode: string
statusCallback?: (arg: string) => void
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { PrerenderedCanvas } from '@jbrowse/core/ui'
import { observer } from 'mobx-react'
import RBush from 'rbush'

import type { Source } from '../types'
import type { Source } from '../shared/types'
import type { Feature } from '@jbrowse/core/util'
import type { Region } from '@jbrowse/core/util/types'

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import { useMemo, useRef } from 'react'

import { PrerenderedCanvas } from '@jbrowse/core/ui'
import { observer } from 'mobx-react'
import RBush from 'rbush'

import type { Source } from '../../shared/types'
import type { Feature } from '@jbrowse/core/util'
import type { Region } from '@jbrowse/core/util/types'

const MultiVariantRendering = observer(function (props: {
regions: Region[]
features: Map<string, Feature>
bpPerPx: number
width: number
height: number
sources: Source[]
scrollTop: number
totalHeight: number
rbush: RBush<{ genotype: string }>
displayModel: any
onMouseLeave?: (event: React.MouseEvent) => void
onMouseMove?: (event: React.MouseEvent, arg?: Feature) => void
onFeatureClick?: (event: React.MouseEvent, arg?: Feature) => void
}) {
const { totalHeight, scrollTop } = props
const { rbush, displayModel } = props
const ref = useRef<HTMLDivElement>(null)
const rbush2 = useMemo(
() => new RBush<{ genotype: string }>().fromJSON(rbush),
[rbush],
)

function getFeatureUnderMouse(eventClientX: number, eventClientY: number) {
let offsetX = 0
let offsetY = 0
if (ref.current) {
const r = ref.current.getBoundingClientRect()
offsetX = eventClientX - r.left
offsetY = eventClientY - r.top
}
const ret = rbush2.search({
minX: offsetX,
maxX: offsetX + 3,
minY: offsetY,
maxY: offsetY + 3,
})
return ret[0]?.genotype
}

return (
<div
ref={ref}
onMouseMove={e =>
displayModel.setHoveredGenotype?.(
getFeatureUnderMouse(e.clientX, e.clientY),
)
}
onMouseLeave={() => {
displayModel.setHoveredGenotype?.(undefined)
}}
onMouseOut={() => {
displayModel.setHoveredGenotype?.(undefined)
}}
style={{
overflow: 'visible',
position: 'relative',
height: totalHeight,
}}
>
<PrerenderedCanvas
{...props}
style={{
position: 'absolute',
left: 0,
top: scrollTop,
}}
/>
</div>
)
})

export default MultiVariantRendering
2 changes: 1 addition & 1 deletion plugins/variants/src/MultiLinearVariantRenderer/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import MultiVariantRenderer from './MultiVariantRenderer'
import ReactComponent from './MultiVariantRendering'
import ReactComponent from './components/MultiVariantRendering'
import configSchema from './configSchema'

import type PluginManager from '@jbrowse/core/PluginManager'
Expand Down
Loading

0 comments on commit 11563e3

Please sign in to comment.