Skip to content

Commit cdd00c0

Browse files
author
Sebastian Benjamin
committed
WIP
1 parent 945c5f1 commit cdd00c0

File tree

4 files changed

+112
-17
lines changed

4 files changed

+112
-17
lines changed

jbrowse/src/client/JBrowse/Browser/plugins/ExtendedVariantPlugin/ExtendedVariantDisplay/model.tsx

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,11 @@ import { getEnv, IAnyStateTreeNode, types } from '@jbrowse/mobx-state-tree';
88
import PaletteIcon from '@mui/icons-material/Palette';
99
import { LinearGenomeViewModel } from '@jbrowse/plugin-linear-genome-view';
1010
import { navigateToSearch, navigateToTable } from '../../../../utils';
11-
import { GlyphType } from '@jbrowse/core/pluggableElementTypes'
11+
import SerializableFilterChain from '@jbrowse/core/pluggableElementTypes/renderers/util/serializableFilterChain';
12+
13+
function escapeForSingleQuotedJexl(value: string) {
14+
return value.replace(/\\/g, '\\\\').replace(/'/g, "\\'")
15+
}
1216

1317
function getContainingTrackWithConfig(node: IAnyStateTreeNode): IAnyStateTreeNode & { configuration: AnyConfigurationModel } {
1418
return getContainingTrack(node) as any;
@@ -128,8 +132,40 @@ export default jbrowse => {
128132
return {
129133
...superRenderProps(),
130134
config: config,
131-
rendererConfig: config
135+
rendererConfig: config,
136+
filters: new SerializableFilterChain({
137+
filters: this.activeFilters(),
138+
}),
139+
}
140+
},
141+
142+
activeFilters() {
143+
const staticJexlFilters = (getConf(self, 'jexlFilters') || []).map((f: string) =>
144+
f?.startsWith('jexl:') ? f : `jexl:${f}`,
145+
)
146+
147+
const infoFilters = getConf(self, 'infoFilters') || []
148+
const activeSamples = getConf(self, 'activeSamples') || ''
149+
const sampleFilters = activeSamples
150+
? activeSamples
151+
.split(',')
152+
.map((s: string) => s.trim())
153+
.filter((s: string) => !!s)
154+
: []
155+
156+
const dynamicFilters: string[] = []
157+
158+
if (infoFilters.length) {
159+
const serialized = escapeForSingleQuotedJexl(JSON.stringify(infoFilters))
160+
dynamicFilters.push(`jexl:passesInfoFilters(feature,'${serialized}')`)
132161
}
162+
163+
if (sampleFilters.length) {
164+
const serialized = escapeForSingleQuotedJexl(JSON.stringify(sampleFilters))
165+
dynamicFilters.push(`jexl:passesSampleFilters(feature,'${serialized}')`)
166+
}
167+
168+
return [...staticJexlFilters, ...dynamicFilters]
133169
},
134170

135171
get rendererTypeName() {

jbrowse/src/client/JBrowse/Browser/plugins/ExtendedVariantPlugin/index.ts

Lines changed: 71 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,18 @@ import InfoFilterWidget from './InfoFilterWidget';
1414
import ColorWidget from './ColorWidget';
1515
import SampleFilterWidget from './SampleFilterWidget';
1616
import GlyphType from '@jbrowse/core/pluggableElementTypes/GlyphType';
17+
import { readConfObject } from '@jbrowse/core/configuration';
18+
import { emphasize } from '@jbrowse/core/util/color';
19+
import { deserializeFilters } from './InfoFilterWidget/filterUtil';
20+
import { passesInfoFilters, passesSampleFilters } from '../../../utils';
21+
22+
const utrHeightFraction = 0.65
23+
24+
function isUTR(feature: any) {
25+
return /(\bUTR|_UTR|untranslated[_\s]region)\b/.test(
26+
feature?.get?.('type') || '',
27+
)
28+
}
1729

1830
export default class ExtendedVariantPlugin extends Plugin {
1931
name = 'ExtendedVariantPlugin'
@@ -64,17 +76,39 @@ export default class ExtendedVariantPlugin extends Plugin {
6476
new GlyphType({
6577
name: 'SNVGlyph',
6678
displayName: 'SNV Diamond',
67-
draw: ctx => {
68-
const { ctx: context, featureLayout } = ctx
69-
const { x, y, width, height } = featureLayout
79+
draw: (ctx: any) => {
80+
const { ctx: context, featureLayout, feature, config } = ctx
81+
const selected = !!ctx?.selected
82+
const rendererConfig: any = config
83+
const { x, y, width } = featureLayout
84+
85+
let top = y
86+
let height = featureLayout.height
87+
if (isUTR(feature)) {
88+
top += ((1 - utrHeightFraction) / 2) * height
89+
height *= utrHeightFraction
90+
}
7091

7192
const centerX = x + width / 2
72-
const centerY = y + height / 2
73-
const halfWidth = Math.max(width / 2, 4)
93+
const centerY = top + height / 2
94+
const halfWidth = height / 2
7495
const halfHeight = height / 2
7596

76-
// Purple diamond fill
77-
context.fillStyle = '#800080'
97+
const color = (readConfObject as any)(
98+
rendererConfig,
99+
isUTR(feature) ? 'color3' : 'color1',
100+
{ feature },
101+
) || '#800080'
102+
const color2 = (readConfObject as any)(rendererConfig, 'color2', { feature }) || '#4B0082'
103+
104+
let emphasizedColor
105+
try {
106+
emphasizedColor = emphasize(color, 0.3)
107+
} catch (error) {
108+
emphasizedColor = color
109+
}
110+
111+
context.fillStyle = selected ? emphasizedColor : color
78112
context.beginPath()
79113
context.moveTo(centerX, centerY - halfHeight) // top
80114
context.lineTo(centerX + halfWidth, centerY) // right
@@ -83,10 +117,11 @@ export default class ExtendedVariantPlugin extends Plugin {
83117
context.closePath()
84118
context.fill()
85119

86-
// Indigo stroke
87-
context.strokeStyle = '#4B0082'
88-
context.lineWidth = 1
89-
context.stroke()
120+
if (selected) {
121+
context.strokeStyle = color2
122+
context.lineWidth = 1
123+
context.stroke()
124+
}
90125
},
91126
match: feature => feature.get('type') === 'SNV',
92127
})
@@ -163,6 +198,31 @@ export default class ExtendedVariantPlugin extends Plugin {
163198
pluginManager.jexl.addFunction('formatWithCommas', (val) => {
164199
return val ? Number(val).toLocaleString() : val
165200
})
201+
202+
pluginManager.jexl.addFunction('passesInfoFilters', (feature, serializedFilters) => {
203+
try {
204+
const filters = typeof serializedFilters === 'string'
205+
? JSON.parse(serializedFilters)
206+
: serializedFilters
207+
const expandedFilters = deserializeFilters(filters)
208+
return passesInfoFilters(feature, expandedFilters)
209+
} catch (e) {
210+
console.error(e)
211+
return true
212+
}
213+
})
214+
215+
pluginManager.jexl.addFunction('passesSampleFilters', (feature, serializedSampleFilters) => {
216+
try {
217+
const sampleFilters = typeof serializedSampleFilters === 'string'
218+
? JSON.parse(serializedSampleFilters)
219+
: serializedSampleFilters
220+
return passesSampleFilters(feature, sampleFilters)
221+
} catch (e) {
222+
console.error(e)
223+
return true
224+
}
225+
})
166226
}
167227

168228
configure(pluginManager: PluginManager) {

jbrowse/src/client/JBrowse/VariantTable/components/VariantTableWidget.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ const VariantTableWidget = observer(props => {
118118
filteredFeatures.forEach(variant => {
119119
if (!variant.get('INFO')['variableSamples']) {
120120
variant.get('INFO')['variableSamples'] = []
121-
const genotypes = variant.get('GENOTYPES')()
121+
const genotypes = variant.get('genotypes')
122122
Object.keys(genotypes).forEach(function(sampleId) {
123123
const gt = genotypes[sampleId] ? genotypes[sampleId] : null
124124
if (isVariant(gt)) {

jbrowse/src/client/JBrowse/utils.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ export function passesInfoFilters(feature, filters) {
4444
return true
4545
}
4646

47-
export function passesSampleFilters(variant : VcfFeature, sampleIDs){
47+
export function passesSampleFilters(variant: VcfFeature, sampleIDs){
4848
if (!sampleIDs || sampleIDs.length === 0) {
4949
return true
5050
}
@@ -60,8 +60,7 @@ export function passesSampleFilters(variant : VcfFeature, sampleIDs){
6060
return false
6161
}
6262

63-
64-
const genotypes = variant.get('GENOTYPES')()
63+
const genotypes = variant.get('genotypes')
6564
if (!genotypes || isEmptyObject(genotypes)) {
6665
return false
6766
}

0 commit comments

Comments
 (0)