Skip to content

Commit

Permalink
feat: Gene-wide variation landscape (#67) (#105)
Browse files Browse the repository at this point in the history
  • Loading branch information
gromdimon authored Oct 4, 2023
1 parent a2f8282 commit 2ce7de2
Show file tree
Hide file tree
Showing 6 changed files with 352 additions and 5 deletions.
4 changes: 2 additions & 2 deletions frontend/src/assets/__tests__/BRCA1ClinVar.json
Git LFS file not shown
303 changes: 303 additions & 0 deletions frontend/src/components/VariantDetails/VariationLandscape.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,303 @@
<script setup lang="ts">
import { computed } from 'vue'
import VegaPlot from '@/components/VegaPlot.vue'
export interface Props {
/** Gene information from annonars. */
clinvar: any
/** The genome release. */
genomeRelease: string
/** The gene symbol. */
geneSymbol: string
}
const props = withDefaults(defineProps<Props>(), {
clinvar: null,
genomeRelease: 'grch37',
geneSymbol: ''
})
const clinvarSignificanceMapping: Record<number, number> = {
0: 0,
1: -3,
2: -2,
3: -1,
4: 1,
5: 2
}
interface ClinvarVariant {
chrom: string
pos: string
reference: string
alternative: string
rcv: string
clinsig: number
review_status: number
}
const convertClinvarSignificance = (input: number): number => {
if (input in clinvarSignificanceMapping) {
return clinvarSignificanceMapping[input]
} else {
return -4
}
}
const vegaData = computed(() => {
let clinvarInfo = []
if (props.genomeRelease == 'grch37') {
clinvarInfo = props.clinvar.variants[0].variants
} else if (props.genomeRelease == 'grch38') {
clinvarInfo = props.clinvar.variants[1]
}
return clinvarInfo.map((variant: ClinvarVariant) => ({
pos: variant.pos,
clinsig: convertClinvarSignificance(variant.clinsig)
}))
})
const vegaEncoding = {}
const vegaLayer = [
{
description: 'gray baseline',
data: { values: [{}] },
mark: { type: 'rule', stroke: 'lightgray', size: 3 },
encoding: { y: { datum: 'Uncertain significance' } }
},
{
description: 'lollipop heads',
transform: [
{
lookup: 'clinsig',
from: {
data: {
values: [
{ clinsig: -5, clinsigLabel: 'other' },
{ clinsig: -4, clinsigLabel: 'Not provided' },
{ clinsig: -3, clinsigLabel: 'Conflicting' },
{ clinsig: -2, clinsigLabel: 'Benign' },
{ clinsig: -1, clinsigLabel: 'Likely benign' },
{ clinsig: 0, clinsigLabel: 'Uncertain significance' },
{ clinsig: 1, clinsigLabel: 'Likely pathogenic' },
{ clinsig: 2, clinsigLabel: 'Pathogenic' },
{ clinsig: 3, clinsigLabel: 'Gene' }
]
},
key: 'clinsig',
fields: ['clinsigLabel']
}
}
],
mark: { type: 'circle', opacity: 0.8 },
encoding: {
x: {
field: 'pos',
type: 'quantitative',
scale: { domain: [41190000, 41282000] },
axis: { grid: false, zindex: 1000 },
title: null
},
y: {
field: 'clinsigLabel',
type: 'nominal',
scale: {
domain: [
'Gene',
'Pathogenic',
'Likely pathogenic',
'Uncertain significance',
'Likely benign',
'Benign'
]
},
axis: { grid: false },
title: null
},
color: {
field: 'clinsigLabel',
type: 'nominal',
scale: {
domain: [
'Gene',
'Pathogenic',
'Likely pathogenic',
'Uncertain significance',
'Likely benign',
'Benign'
],
range: ['gray', 'darkred', 'orange', 'yellow', 'green', 'darkgreen']
},
legend: null
},
size: { value: 100 }
}
},
{
description: 'lollipop sticks',
transform: [
{
lookup: 'clinsig',
from: {
data: {
values: [
{
clinsig: -5,
clinsigBaseline: 'Uncertain significance',
clinsigLabel: 'other'
},
{
clinsig: -4,
clinsigBaseline: 'Uncertain significance',
clinsigLabel: 'Not provided'
},
{
clinsig: -3,
clinsigBaseline: 'Uncertain significance',
clinsigLabel: 'Conflicting'
},
{
clinsig: -2,
clinsigBaseline: 'Uncertain significance',
clinsigLabel: 'Benign'
},
{
clinsig: -1,
clinsigBaseline: 'Uncertain significance',
clinsigLabel: 'Likely benign'
},
{
clinsig: 0,
clinsigBaseline: 'Uncertain significance',
clinsigLabel: 'Uncertain significance'
},
{
clinsig: 1,
clinsigBaseline: 'Uncertain significance',
clinsigLabel: 'Likely pathogenic'
},
{
clinsig: 2,
clinsigBaseline: 'Uncertain significance',
clinsigLabel: 'Pathogenic'
},
{
clinsig: 3,
clinsigBaseline: 'Uncertain significance',
clinsigLabel: 'Gene'
}
]
},
key: 'clinsig',
fields: ['clinsigBaseline', 'clinsigLabel']
}
}
],
mark: { type: 'rule', opacity: 0.8 },
encoding: {
x: {
field: 'pos',
type: 'quantitative',
scale: { domain: [41190000, 41282000] },
axis: { grid: false },
title: null
},
y: {
field: 'clinsigLabel',
type: 'nominal',
scale: {
domain: [
'Gene',
'Pathogenic',
'Likely pathogenic',
'Uncertain significance',
'Likely benign',
'Benign'
]
},
axis: { grid: false, zindex: 1000 },
title: null
},
y2: { field: 'clinsigBaseline' },
color: {
field: 'clinsigLabel',
type: 'nominal',
scale: {
domain: [
'Gene',
'Pathogenic',
'Likely pathogenic',
'Uncertain significance',
'Likely benign',
'Benign'
],
range: ['gray', 'darkred', 'orange', 'yellow', 'green', 'darkgreen']
},
legend: null
},
size: { value: 1 }
}
},
{
description: 'gene - line',
data: { values: [{ pos: 41196312 }, { pos: 41277381 }] },
mark: { type: 'line', stroke: 'black', size: 1, opacity: 0.5 },
encoding: {
x: {
field: 'pos',
type: 'quantitative',
scale: { domain: [41190000, 41282000] },
axis: { grid: false },
title: null
},
y: { datum: 'Gene' }
}
},
{
description: 'gene - exons',
data: {
values: [
{ start: 41196312, stop: 41197819 },
{ start: 41199660, stop: 41199720 }
]
},
mark: {
type: 'rect',
stroke: 'black',
height: 10,
fill: 'black',
opacity: 0.5
},
encoding: {
x: {
field: 'start',
type: 'quantitative',
scale: { domain: [41190000, 41282000] },
axis: { grid: false },
title: null
},
x2: { field: 'stop' },
y: { datum: 'Gene' }
}
}
]
</script>

<template>
<figure class="figure border rounded pl-2 pt-2 mr-3 w-100 col">
<figcaption class="figure-caption text-center">
Variation Landscape of {{ props.geneSymbol }}
</figcaption>
<VegaPlot
:data-values="vegaData"
:encoding="vegaEncoding"
:layer="vegaLayer"
:width="1300"
:height="300"
renderer="svg"
/>
</figure>
</template>
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { describe, expect, it } from 'vitest'

import * as BRCA1Clinvar from '@/assets/__tests__/BRCA1ClinVar.json'
import VariationLandscape from '@/components/VariantDetails/VariationLandscape.vue'
import { setupMountedComponents } from '@/lib/test-utils'

describe.concurrent('VariationLandscape', async () => {
it('renders the VariationLandscape plot', async () => {
const { wrapper } = setupMountedComponents(
{ component: VariationLandscape, template: false },
{
props: {
clinvar: BRCA1Clinvar['genes']['HGNC:1100'],
genomeRelease: 'grch37',
geneSymbol: 'HGNC:1100'
}
}
)
expect(wrapper.text()).toContain('Variation Landscape')
expect(wrapper.html()).toContain('figure')
})
})
2 changes: 1 addition & 1 deletion frontend/src/stores/variantInfo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ export const useVariantInfoStore = defineStore('variantInfo', () => {
geneInfo.value = geneData['genes'][hgncId]

const geneClinvarData = await annonarsClient.fetchGeneClinvarInfo(hgncId)
if (geneClinvarData?.result === null) {
if (geneClinvarData?.genes === null) {
throw new Error('No gene clinvar data found.')
}
geneClinvar.value = geneClinvarData['genes'][hgncId]
Expand Down
14 changes: 13 additions & 1 deletion frontend/src/views/VariantDetailView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import VariantDetailsFreqs from '@/components/VariantDetails/VariantFreqs.vue'
import VariantDetailsGene from '@/components/VariantDetails/VariantGene.vue'
import VariantDetailsVariantTools from '@/components/VariantDetails/VariantTools.vue'
import VariantDetailsVariantValidator from '@/components/VariantDetails/VariantValidator.vue'
import VariationLandscape from '@/components/VariantDetails/VariationLandscape.vue'
import { StoreState } from '@/stores/misc'
import { useVariantInfoStore } from '@/stores/variantInfo'
import { type SmallVariant } from '@/stores/variantInfo'
Expand Down Expand Up @@ -64,6 +65,7 @@ watch(
const SECTIONS = [
{ id: 'gene', title: 'Gene' },
{ id: 'variation-landscape', title: 'Variation Landscape' },
{ id: 'beacon-network', title: 'Beacon Network' },
{ id: 'clinvar', title: 'ClinVar' },
{ id: 'freqs', title: 'Population Frequencies' },
Expand Down Expand Up @@ -106,7 +108,7 @@ const genomeReleaseRef = ref(props.genomeRelease)
:to="{
name: 'gene',
params: {
searchTerm: `HGNC:1100`,
searchTerm: variantInfoStore.varAnnos?.cadd?.GeneName,
genomeRelease: genomeReleaseRef
}
}"
Expand All @@ -118,6 +120,16 @@ const genomeReleaseRef = ref(props.genomeRelease)
<VariantDetailsGene :gene="variantInfoStore.geneInfo" />
</div>

<div id="variation-landscape" class="variant-item">
<h2>Gene-wide Variation landscape</h2>
<v-divider />
<VariationLandscape
:clinvar="variantInfoStore.geneClinvar"
:genome-release="genomeReleaseRef"
:gene-symbol="variantInfoStore.varAnnos?.cadd?.GeneName"
/>
</div>

<div id="beacon-network" class="variant-item">
<h2>Beacon Network</h2>
<v-divider />
Expand Down
Loading

0 comments on commit 2ce7de2

Please sign in to comment.