Skip to content

Commit 73b24f4

Browse files
authored
Filter samples and allow gzip samples tsv (#4792)
1 parent cd65bea commit 73b24f4

File tree

28 files changed

+524
-286
lines changed

28 files changed

+524
-286
lines changed

packages/core/data_adapters/CytobandAdapter/CytobandAdapter.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { SimpleFeature, fetchAndMaybeUnzip } from '../../util'
1+
import { SimpleFeature, fetchAndMaybeUnzipText } from '../../util'
22
import { openLocation } from '../../util/io'
33
import { BaseAdapter } from '../BaseAdapter'
44

@@ -11,10 +11,10 @@ export default class CytobandAdapter extends BaseAdapter {
1111
return []
1212
}
1313

14-
const pm = this.pluginManager
15-
const buf = await fetchAndMaybeUnzip(openLocation(conf, pm), opts)
16-
const decoder = new TextDecoder('utf8', { fatal: true })
17-
const text = decoder.decode(buf)
14+
const text = await fetchAndMaybeUnzipText(
15+
openLocation(conf, this.pluginManager),
16+
opts,
17+
)
1818
return text
1919
.split(/\n|\r\n|\r/)
2020
.filter(f => !!f.trim())

packages/core/util/index.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1407,6 +1407,18 @@ export async function fetchAndMaybeUnzip(
14071407
: buf
14081408
}
14091409

1410+
export async function fetchAndMaybeUnzipText(
1411+
loc: GenericFilehandle,
1412+
opts?: BaseOptions,
1413+
) {
1414+
const buffer = await fetchAndMaybeUnzip(loc, opts)
1415+
// 512MB max chrome string length is 512MB
1416+
if (buffer.length > 536_870_888) {
1417+
throw new Error('Data exceeds maximum string length (512MB)')
1418+
}
1419+
return new TextDecoder('utf8', { fatal: true }).decode(buffer)
1420+
}
1421+
14101422
// MIT https://github.com/inspect-js/is-object
14111423
export function isObject(
14121424
x: unknown,

plugins/bed/src/BedpeAdapter/BedpeAdapter.ts

Lines changed: 8 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1,69 +1,14 @@
11
import IntervalTree from '@flatten-js/interval-tree'
22
import { BaseFeatureDataAdapter } from '@jbrowse/core/data_adapters/BaseAdapter'
3-
import { SimpleFeature, fetchAndMaybeUnzip } from '@jbrowse/core/util'
3+
import { fetchAndMaybeUnzipText } from '@jbrowse/core/util'
44
import { openLocation } from '@jbrowse/core/util/io'
55
import { ObservableCreate } from '@jbrowse/core/util/rxjs'
66

7+
import { featureData } from './util'
8+
79
import type { BaseOptions } from '@jbrowse/core/data_adapters/BaseAdapter'
810
import type { Feature, Region } from '@jbrowse/core/util'
911

10-
const svTypes = new Set(['DUP', 'TRA', 'INV', 'CNV', 'DEL'])
11-
12-
export function featureData(
13-
line: string,
14-
uniqueId: string,
15-
flip: boolean,
16-
names?: string[],
17-
) {
18-
const l = line.split('\t')
19-
const ref1 = l[flip ? 3 : 0]!
20-
const start1 = +l[flip ? 4 : 1]!
21-
const end1 = +l[flip ? 5 : 2]!
22-
const ref2 = l[!flip ? 3 : 0]!
23-
const start2 = +l[!flip ? 4 : 1]!
24-
const end2 = +l[!flip ? 5 : 2]!
25-
const name = l[6]!
26-
const score = +l[7]!
27-
const strand1 = parseStrand(l[8]!)
28-
const strand2 = parseStrand(l[9]!)
29-
const extra = l.slice(10)
30-
const rest = names
31-
? Object.fromEntries(names.slice(10).map((n, idx) => [n, extra[idx]]))
32-
: {}
33-
const ALT = svTypes.has(extra[0]!) ? `<${extra[0]}>` : undefined
34-
35-
return new SimpleFeature({
36-
...rest,
37-
start: start1,
38-
end: end1,
39-
type: 'paired_feature',
40-
refName: ref1,
41-
strand: strand1,
42-
name,
43-
score,
44-
uniqueId,
45-
mate: {
46-
refName: ref2,
47-
start: start2,
48-
end: end2,
49-
strand: strand2,
50-
},
51-
...(ALT ? { ALT: [ALT] } : {}), // ALT is an array in VCF
52-
})
53-
}
54-
55-
function parseStrand(strand: string) {
56-
if (strand === '+') {
57-
return 1
58-
} else if (strand === '-') {
59-
return -1
60-
} else if (strand === '.') {
61-
return 0
62-
} else {
63-
return undefined
64-
}
65-
}
66-
6712
export default class BedpeAdapter extends BaseFeatureDataAdapter {
6813
protected bedpeFeatures?: Promise<{
6914
header: string
@@ -80,15 +25,11 @@ export default class BedpeAdapter extends BaseFeatureDataAdapter {
8025
public static capabilities = ['getFeatures', 'getRefNames']
8126

8227
private async loadDataP(opts?: BaseOptions) {
83-
const pm = this.pluginManager
84-
const bedLoc = this.getConf('bedpeLocation')
85-
const loc = openLocation(bedLoc, pm)
86-
const buffer = await fetchAndMaybeUnzip(loc, opts)
87-
// 512MB max chrome string length is 512MB
88-
if (buffer.length > 536_870_888) {
89-
throw new Error('Data exceeds maximum string length (512MB)')
90-
}
91-
const data = new TextDecoder('utf8', { fatal: true }).decode(buffer)
28+
const data = await fetchAndMaybeUnzipText(
29+
openLocation(this.getConf('bedpeLocation'), this.pluginManager),
30+
opts,
31+
)
32+
9233
const lines = data.split(/\n|\r\n|\r/).filter(f => !!f)
9334
const headerLines = []
9435
let i = 0

plugins/bed/src/BedpeAdapter/util.ts

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import { SimpleFeature } from '@jbrowse/core/util'
2+
3+
const svTypes = new Set(['DUP', 'TRA', 'INV', 'CNV', 'DEL'])
4+
5+
export function featureData(
6+
line: string,
7+
uniqueId: string,
8+
flip: boolean,
9+
names?: string[],
10+
) {
11+
const l = line.split('\t')
12+
const ref1 = l[flip ? 3 : 0]!
13+
const start1 = +l[flip ? 4 : 1]!
14+
const end1 = +l[flip ? 5 : 2]!
15+
const ref2 = l[!flip ? 3 : 0]!
16+
const start2 = +l[!flip ? 4 : 1]!
17+
const end2 = +l[!flip ? 5 : 2]!
18+
const name = l[6]!
19+
const score = +l[7]!
20+
const strand1 = parseStrand(l[8]!)
21+
const strand2 = parseStrand(l[9]!)
22+
const extra = l.slice(10)
23+
const rest = names
24+
? Object.fromEntries(names.slice(10).map((n, idx) => [n, extra[idx]]))
25+
: {}
26+
const ALT = svTypes.has(extra[0]!) ? `<${extra[0]}>` : undefined
27+
28+
return new SimpleFeature({
29+
...rest,
30+
start: start1,
31+
end: end1,
32+
type: 'paired_feature',
33+
refName: ref1,
34+
strand: strand1,
35+
name,
36+
score,
37+
uniqueId,
38+
mate: {
39+
refName: ref2,
40+
start: start2,
41+
end: end2,
42+
strand: strand2,
43+
},
44+
...(ALT ? { ALT: [ALT] } : {}), // ALT is an array in VCF
45+
})
46+
}
47+
48+
function parseStrand(strand: string) {
49+
if (strand === '+') {
50+
return 1
51+
} else if (strand === '-') {
52+
return -1
53+
} else if (strand === '.') {
54+
return 0
55+
} else {
56+
return undefined
57+
}
58+
}

plugins/comparative-adapters/src/util.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { fetchAndMaybeUnzip } from '@jbrowse/core/util'
1+
import { fetchAndMaybeUnzipText } from '@jbrowse/core/util'
22

33
import type { PAFRecord } from './PAFAdapter/util'
44
import type { BaseOptions } from '@jbrowse/core/data_adapters/BaseAdapter'
@@ -27,9 +27,7 @@ export function parseBed(text: string) {
2727
}
2828

2929
export async function readFile(file: GenericFilehandle, opts?: BaseOptions) {
30-
const buf = await fetchAndMaybeUnzip(file, opts)
31-
const decoder = new TextDecoder('utf8')
32-
return decoder.decode(buf)
30+
return fetchAndMaybeUnzipText(file, opts)
3331
}
3432

3533
export function zip(a: number[], b: number[]) {
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import { SanitizedHTML } from '@jbrowse/core/ui'
2+
import BaseTooltip from '@jbrowse/core/ui/BaseTooltip'
3+
import { getContainingView } from '@jbrowse/core/util'
4+
import { observer } from 'mobx-react'
5+
import { makeStyles } from 'tss-react/mui'
6+
7+
import type { MultiLinearVariantDisplayModel } from '../model'
8+
import type { LinearGenomeViewModel } from '@jbrowse/plugin-linear-genome-view'
9+
10+
const useStyles = makeStyles()({
11+
cursor: {
12+
pointerEvents: 'none',
13+
zIndex: 1000,
14+
position: 'relative',
15+
},
16+
})
17+
18+
const Crosshair = observer(function ({
19+
mouseX,
20+
mouseY,
21+
model,
22+
}: {
23+
mouseX: number
24+
mouseY: number
25+
model: MultiLinearVariantDisplayModel
26+
}) {
27+
const { classes } = useStyles()
28+
const { height, scrollTop, rowHeight, sources } = model
29+
const { width } = getContainingView(model) as LinearGenomeViewModel
30+
const source = sources?.[Math.floor(mouseY / rowHeight)]
31+
const y = mouseY - scrollTop
32+
return source ? (
33+
<div style={{ position: 'relative' }}>
34+
<svg
35+
className={classes.cursor}
36+
width={width}
37+
height={height}
38+
style={{
39+
position: 'absolute',
40+
top: scrollTop,
41+
}}
42+
>
43+
<line x1={0} x2={width} y1={y} y2={y} stroke="black" />
44+
<line x1={mouseX} x2={mouseX} y1={0} y2={height} stroke="black" />
45+
</svg>
46+
<BaseTooltip>
47+
{source.color ? (
48+
<div
49+
style={{
50+
width: 10,
51+
height: 10,
52+
backgroundColor: source.color,
53+
}}
54+
/>
55+
) : null}
56+
<SanitizedHTML
57+
html={Object.entries(source)
58+
.filter(([key]) => key !== 'color')
59+
.map(([key, value]) => `${key}:${value}`)
60+
.join('<br/>')}
61+
/>
62+
</BaseTooltip>
63+
</div>
64+
) : null
65+
})
66+
67+
export default Crosshair

plugins/variants/src/MultiLinearVariantDisplay/components/VariantDisplayComponent.tsx

Lines changed: 4 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,20 @@
11
import { useRef, useState } from 'react'
22

3-
import { SanitizedHTML } from '@jbrowse/core/ui'
4-
import BaseTooltip from '@jbrowse/core/ui/BaseTooltip'
5-
import { getContainingView } from '@jbrowse/core/util'
63
import { BaseLinearDisplayComponent } from '@jbrowse/plugin-linear-genome-view'
74
import { observer } from 'mobx-react'
8-
import { makeStyles } from 'tss-react/mui'
95

6+
import Crosshair from './Crosshair'
107
import LegendBar from '../../shared/LegendBar'
118

129
import type { MultiLinearVariantDisplayModel } from '../model'
13-
import type { LinearGenomeViewModel } from '@jbrowse/plugin-linear-genome-view'
14-
15-
const useStyles = makeStyles()({
16-
cursor: {
17-
pointerEvents: 'none',
18-
},
19-
})
2010

2111
const MultiLinearVariantDisplayComponent = observer(function (props: {
2212
model: MultiLinearVariantDisplayModel
2313
}) {
2414
const { model } = props
25-
const { classes } = useStyles()
26-
const { height, sources, rowHeight, scrollTop } = model
2715
const ref = useRef<HTMLDivElement>(null)
2816
const [mouseY, setMouseY] = useState<number>()
2917
const [mouseX, setMouseX] = useState<number>()
30-
const { width } = getContainingView(model) as LinearGenomeViewModel
3118

3219
return (
3320
<div
@@ -46,34 +33,9 @@ const MultiLinearVariantDisplayComponent = observer(function (props: {
4633
>
4734
<BaseLinearDisplayComponent {...props} />
4835
<LegendBar model={model} />
49-
{mouseY && sources ? (
50-
<div style={{ position: 'relative' }}>
51-
<svg
52-
className={classes.cursor}
53-
width={width}
54-
height={height}
55-
style={{ position: 'absolute', top: scrollTop }}
56-
>
57-
<line
58-
x1={0}
59-
x2={width}
60-
y1={mouseY - scrollTop}
61-
y2={mouseY - scrollTop}
62-
stroke="black"
63-
/>
64-
<line x1={mouseX} x2={mouseX} y1={0} y2={height} stroke="black" />
65-
</svg>
66-
<BaseTooltip>
67-
<SanitizedHTML
68-
html={Object.entries(
69-
sources[Math.floor(mouseY / rowHeight)] || {},
70-
)
71-
.filter(([key]) => key !== 'color')
72-
.map(([key, value]) => `${key}:${value}`)
73-
.join('<br/>')}
74-
/>
75-
</BaseTooltip>
76-
</div>
36+
37+
{mouseX && mouseY ? (
38+
<Crosshair mouseX={mouseX} mouseY={mouseY} model={model} />
7739
) : null}
7840
</div>
7941
)

0 commit comments

Comments
 (0)