@jbrowse/plugin-alignments 2.2.1 → 2.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/CramAdapter/CramTestAdapters.js +2 -1
- package/dist/CramAdapter/CramTestAdapters.js.map +1 -1
- package/dist/LinearPileupDisplay/components/ColorByModifications.d.ts +3 -1
- package/dist/LinearPileupDisplay/components/ColorByModifications.js +7 -22
- package/dist/LinearPileupDisplay/components/ColorByModifications.js.map +1 -1
- package/dist/LinearPileupDisplay/components/ColorByTag.js +2 -22
- package/dist/LinearPileupDisplay/components/ColorByTag.js.map +1 -1
- package/dist/LinearPileupDisplay/components/FilterByTag.js +2 -15
- package/dist/LinearPileupDisplay/components/FilterByTag.js.map +1 -1
- package/dist/LinearPileupDisplay/components/SetFeatureHeight.js +2 -19
- package/dist/LinearPileupDisplay/components/SetFeatureHeight.js.map +1 -1
- package/dist/LinearPileupDisplay/components/SetMaxHeight.js +2 -18
- package/dist/LinearPileupDisplay/components/SetMaxHeight.js.map +1 -1
- package/dist/LinearPileupDisplay/components/SortByTag.js +2 -23
- package/dist/LinearPileupDisplay/components/SortByTag.js.map +1 -1
- package/dist/LinearSNPCoverageDisplay/models/model.d.ts +13 -9
- package/dist/LinearSNPCoverageDisplay/models/model.js +8 -6
- package/dist/LinearSNPCoverageDisplay/models/model.js.map +1 -1
- package/dist/PileupRenderer/configSchema.js +1 -1
- package/dist/PileupRenderer/configSchema.js.map +1 -1
- package/dist/SNPCoverageAdapter/SNPCoverageAdapter.d.ts +0 -51
- package/dist/SNPCoverageAdapter/SNPCoverageAdapter.js +3 -182
- package/dist/SNPCoverageAdapter/SNPCoverageAdapter.js.map +1 -1
- package/dist/SNPCoverageAdapter/generateCoverageBins.d.ts +53 -0
- package/dist/SNPCoverageAdapter/generateCoverageBins.js +185 -0
- package/dist/SNPCoverageAdapter/generateCoverageBins.js.map +1 -0
- package/esm/CramAdapter/CramTestAdapters.js +2 -1
- package/esm/CramAdapter/CramTestAdapters.js.map +1 -1
- package/esm/LinearPileupDisplay/components/ColorByModifications.d.ts +3 -1
- package/esm/LinearPileupDisplay/components/ColorByModifications.js +8 -23
- package/esm/LinearPileupDisplay/components/ColorByModifications.js.map +1 -1
- package/esm/LinearPileupDisplay/components/ColorByTag.js +3 -20
- package/esm/LinearPileupDisplay/components/ColorByTag.js.map +1 -1
- package/esm/LinearPileupDisplay/components/FilterByTag.js +3 -13
- package/esm/LinearPileupDisplay/components/FilterByTag.js.map +1 -1
- package/esm/LinearPileupDisplay/components/SetFeatureHeight.js +3 -17
- package/esm/LinearPileupDisplay/components/SetFeatureHeight.js.map +1 -1
- package/esm/LinearPileupDisplay/components/SetMaxHeight.js +3 -16
- package/esm/LinearPileupDisplay/components/SetMaxHeight.js.map +1 -1
- package/esm/LinearPileupDisplay/components/SortByTag.js +3 -21
- package/esm/LinearPileupDisplay/components/SortByTag.js.map +1 -1
- package/esm/LinearSNPCoverageDisplay/models/model.d.ts +13 -9
- package/esm/LinearSNPCoverageDisplay/models/model.js +9 -7
- package/esm/LinearSNPCoverageDisplay/models/model.js.map +1 -1
- package/esm/PileupRenderer/configSchema.js +1 -1
- package/esm/PileupRenderer/configSchema.js.map +1 -1
- package/esm/SNPCoverageAdapter/SNPCoverageAdapter.d.ts +0 -51
- package/esm/SNPCoverageAdapter/SNPCoverageAdapter.js +4 -183
- package/esm/SNPCoverageAdapter/SNPCoverageAdapter.js.map +1 -1
- package/esm/SNPCoverageAdapter/generateCoverageBins.d.ts +53 -0
- package/esm/SNPCoverageAdapter/generateCoverageBins.js +182 -0
- package/esm/SNPCoverageAdapter/generateCoverageBins.js.map +1 -0
- package/package.json +3 -3
- package/src/CramAdapter/CramTestAdapters.ts +1 -0
- package/src/LinearPileupDisplay/components/ColorByModifications.tsx +5 -34
- package/src/LinearPileupDisplay/components/ColorByTag.tsx +2 -29
- package/src/LinearPileupDisplay/components/FilterByTag.tsx +2 -22
- package/src/LinearPileupDisplay/components/SetFeatureHeight.tsx +2 -22
- package/src/LinearPileupDisplay/components/SetMaxHeight.tsx +2 -24
- package/src/LinearPileupDisplay/components/SortByTag.tsx +2 -31
- package/src/LinearSNPCoverageDisplay/models/model.ts +12 -8
- package/src/PileupRenderer/configSchema.ts +1 -1
- package/src/SNPCoverageAdapter/SNPCoverageAdapter.ts +5 -249
- package/src/SNPCoverageAdapter/generateCoverageBins.ts +245 -0
|
@@ -2,50 +2,23 @@ import React, { useState } from 'react'
|
|
|
2
2
|
import { observer } from 'mobx-react'
|
|
3
3
|
import {
|
|
4
4
|
Button,
|
|
5
|
-
Dialog,
|
|
6
5
|
DialogContent,
|
|
7
6
|
DialogActions,
|
|
8
|
-
DialogTitle,
|
|
9
|
-
IconButton,
|
|
10
7
|
TextField,
|
|
11
8
|
Typography,
|
|
12
9
|
} from '@mui/material'
|
|
13
|
-
import {
|
|
14
|
-
import CloseIcon from '@mui/icons-material/Close'
|
|
15
|
-
|
|
16
|
-
const useStyles = makeStyles()(theme => ({
|
|
17
|
-
root: {
|
|
18
|
-
width: 300,
|
|
19
|
-
},
|
|
20
|
-
closeButton: {
|
|
21
|
-
position: 'absolute',
|
|
22
|
-
right: theme.spacing(1),
|
|
23
|
-
top: theme.spacing(1),
|
|
24
|
-
color: theme.palette.grey[500],
|
|
25
|
-
},
|
|
26
|
-
}))
|
|
10
|
+
import { Dialog } from '@jbrowse/core/ui'
|
|
27
11
|
|
|
28
12
|
function ColorByTagDlg(props: {
|
|
29
13
|
model: { setColorScheme: Function }
|
|
30
14
|
handleClose: () => void
|
|
31
15
|
}) {
|
|
32
|
-
const { classes } = useStyles()
|
|
33
16
|
const { model, handleClose } = props
|
|
34
17
|
const [tag, setTag] = useState('')
|
|
35
18
|
const validTag = tag.match(/^[A-Za-z][A-Za-z0-9]$/)
|
|
36
19
|
|
|
37
20
|
return (
|
|
38
|
-
<Dialog open onClose={handleClose}>
|
|
39
|
-
<DialogTitle>
|
|
40
|
-
Color by tag
|
|
41
|
-
<IconButton
|
|
42
|
-
aria-label="close"
|
|
43
|
-
className={classes.closeButton}
|
|
44
|
-
onClick={handleClose}
|
|
45
|
-
>
|
|
46
|
-
<CloseIcon />
|
|
47
|
-
</IconButton>
|
|
48
|
-
</DialogTitle>
|
|
21
|
+
<Dialog open onClose={handleClose} title="Color by tag">
|
|
49
22
|
<DialogContent style={{ overflowX: 'hidden' }}>
|
|
50
23
|
<Typography>Enter tag to color by: </Typography>
|
|
51
24
|
<Typography color="textSecondary">
|
|
@@ -2,31 +2,21 @@ import React, { useState } from 'react'
|
|
|
2
2
|
import { observer } from 'mobx-react'
|
|
3
3
|
import {
|
|
4
4
|
Button,
|
|
5
|
-
Dialog,
|
|
6
5
|
DialogActions,
|
|
7
6
|
DialogContent,
|
|
8
|
-
DialogTitle,
|
|
9
|
-
IconButton,
|
|
10
7
|
Link,
|
|
11
8
|
Paper,
|
|
12
9
|
TextField,
|
|
13
10
|
Typography,
|
|
14
11
|
} from '@mui/material'
|
|
12
|
+
import { Dialog } from '@jbrowse/core/ui'
|
|
15
13
|
import { makeStyles } from 'tss-react/mui'
|
|
16
14
|
|
|
17
|
-
import CloseIcon from '@mui/icons-material/Close'
|
|
18
|
-
|
|
19
15
|
const useStyles = makeStyles()(theme => ({
|
|
20
16
|
paper: {
|
|
21
17
|
padding: theme.spacing(2),
|
|
22
18
|
margin: theme.spacing(2),
|
|
23
19
|
},
|
|
24
|
-
closeButton: {
|
|
25
|
-
position: 'absolute',
|
|
26
|
-
right: theme.spacing(1),
|
|
27
|
-
top: theme.spacing(1),
|
|
28
|
-
color: theme.palette.grey[500],
|
|
29
|
-
},
|
|
30
20
|
field: {
|
|
31
21
|
margin: theme.spacing(2),
|
|
32
22
|
},
|
|
@@ -105,17 +95,7 @@ function FilterByTagDlg(props: {
|
|
|
105
95
|
const site = 'https://broadinstitute.github.io/picard/explain-flags.html'
|
|
106
96
|
|
|
107
97
|
return (
|
|
108
|
-
<Dialog open onClose={handleClose}>
|
|
109
|
-
<DialogTitle>
|
|
110
|
-
Filter options
|
|
111
|
-
<IconButton
|
|
112
|
-
aria-label="close"
|
|
113
|
-
className={classes.closeButton}
|
|
114
|
-
onClick={handleClose}
|
|
115
|
-
>
|
|
116
|
-
<CloseIcon />
|
|
117
|
-
</IconButton>
|
|
118
|
-
</DialogTitle>
|
|
98
|
+
<Dialog open onClose={handleClose} title="Filter options">
|
|
119
99
|
<DialogContent>
|
|
120
100
|
<Typography>
|
|
121
101
|
Set filter bitmask options. Refer to <Link href={site}>{site}</Link>{' '}
|
|
@@ -3,26 +3,13 @@ import { observer } from 'mobx-react'
|
|
|
3
3
|
import {
|
|
4
4
|
Button,
|
|
5
5
|
Checkbox,
|
|
6
|
-
Dialog,
|
|
7
6
|
DialogActions,
|
|
8
7
|
DialogContent,
|
|
9
|
-
DialogTitle,
|
|
10
8
|
FormControlLabel,
|
|
11
|
-
IconButton,
|
|
12
9
|
TextField,
|
|
13
10
|
Typography,
|
|
14
11
|
} from '@mui/material'
|
|
15
|
-
import {
|
|
16
|
-
import CloseIcon from '@mui/icons-material/Close'
|
|
17
|
-
|
|
18
|
-
const useStyles = makeStyles()(theme => ({
|
|
19
|
-
closeButton: {
|
|
20
|
-
position: 'absolute',
|
|
21
|
-
right: theme.spacing(1),
|
|
22
|
-
top: theme.spacing(1),
|
|
23
|
-
color: theme.palette.grey[500],
|
|
24
|
-
},
|
|
25
|
-
}))
|
|
12
|
+
import { Dialog } from '@jbrowse/core/ui'
|
|
26
13
|
|
|
27
14
|
function SetFeatureHeightDlg(props: {
|
|
28
15
|
model: {
|
|
@@ -33,7 +20,6 @@ function SetFeatureHeightDlg(props: {
|
|
|
33
20
|
}
|
|
34
21
|
handleClose: () => void
|
|
35
22
|
}) {
|
|
36
|
-
const { classes } = useStyles()
|
|
37
23
|
const { model, handleClose } = props
|
|
38
24
|
const { featureHeightSetting, noSpacing: noSpacingSetting } = model
|
|
39
25
|
const [height, setHeight] = useState(`${featureHeightSetting}`)
|
|
@@ -42,13 +28,7 @@ function SetFeatureHeightDlg(props: {
|
|
|
42
28
|
const ok = height !== '' && !Number.isNaN(+height)
|
|
43
29
|
|
|
44
30
|
return (
|
|
45
|
-
<Dialog open onClose={handleClose}>
|
|
46
|
-
<DialogTitle>
|
|
47
|
-
Set feature height
|
|
48
|
-
<IconButton className={classes.closeButton} onClick={handleClose}>
|
|
49
|
-
<CloseIcon />
|
|
50
|
-
</IconButton>
|
|
51
|
-
</DialogTitle>
|
|
31
|
+
<Dialog open onClose={handleClose} title={'Set feature height'}>
|
|
52
32
|
<DialogContent>
|
|
53
33
|
<Typography>
|
|
54
34
|
Adjust the feature height and whether there is any spacing between
|
|
@@ -2,30 +2,18 @@ import React, { useState } from 'react'
|
|
|
2
2
|
import { observer } from 'mobx-react'
|
|
3
3
|
import {
|
|
4
4
|
Button,
|
|
5
|
-
Dialog,
|
|
6
5
|
DialogActions,
|
|
7
6
|
DialogContent,
|
|
8
|
-
DialogTitle,
|
|
9
|
-
IconButton,
|
|
10
7
|
TextField,
|
|
11
8
|
Typography,
|
|
12
9
|
} from '@mui/material'
|
|
10
|
+
import { Dialog } from '@jbrowse/core/ui'
|
|
13
11
|
import { makeStyles } from 'tss-react/mui'
|
|
14
|
-
import CloseIcon from '@mui/icons-material/Close'
|
|
15
12
|
|
|
16
13
|
const useStyles = makeStyles()(theme => ({
|
|
17
14
|
root: {
|
|
18
15
|
width: 500,
|
|
19
16
|
},
|
|
20
|
-
closeButton: {
|
|
21
|
-
position: 'absolute',
|
|
22
|
-
right: theme.spacing(1),
|
|
23
|
-
top: theme.spacing(1),
|
|
24
|
-
color: theme.palette.grey[500],
|
|
25
|
-
},
|
|
26
|
-
field: {
|
|
27
|
-
margin: theme.spacing(2),
|
|
28
|
-
},
|
|
29
17
|
}))
|
|
30
18
|
|
|
31
19
|
function SetMaxHeightDlg(props: {
|
|
@@ -41,17 +29,7 @@ function SetMaxHeightDlg(props: {
|
|
|
41
29
|
const [max, setMax] = useState(`${maxHeight}`)
|
|
42
30
|
|
|
43
31
|
return (
|
|
44
|
-
<Dialog open onClose={handleClose}>
|
|
45
|
-
<DialogTitle>
|
|
46
|
-
Filter options
|
|
47
|
-
<IconButton
|
|
48
|
-
aria-label="close"
|
|
49
|
-
className={classes.closeButton}
|
|
50
|
-
onClick={handleClose}
|
|
51
|
-
>
|
|
52
|
-
<CloseIcon />
|
|
53
|
-
</IconButton>
|
|
54
|
-
</DialogTitle>
|
|
32
|
+
<Dialog open onClose={handleClose} title="Filter options">
|
|
55
33
|
<DialogContent className={classes.root}>
|
|
56
34
|
<Typography>
|
|
57
35
|
Set max height for the track. For example, you can increase this if
|
|
@@ -2,51 +2,22 @@ import React, { useState } from 'react'
|
|
|
2
2
|
import { observer } from 'mobx-react'
|
|
3
3
|
import {
|
|
4
4
|
Button,
|
|
5
|
-
Dialog,
|
|
6
5
|
DialogActions,
|
|
7
6
|
DialogContent,
|
|
8
|
-
DialogTitle,
|
|
9
|
-
IconButton,
|
|
10
7
|
TextField,
|
|
11
8
|
Typography,
|
|
12
9
|
} from '@mui/material'
|
|
13
|
-
import {
|
|
14
|
-
|
|
15
|
-
import CloseIcon from '@mui/icons-material/Close'
|
|
16
|
-
|
|
17
|
-
const useStyles = makeStyles()(theme => ({
|
|
18
|
-
root: {
|
|
19
|
-
margin: 0,
|
|
20
|
-
padding: theme.spacing(2),
|
|
21
|
-
},
|
|
22
|
-
closeButton: {
|
|
23
|
-
position: 'absolute',
|
|
24
|
-
right: theme.spacing(1),
|
|
25
|
-
top: theme.spacing(1),
|
|
26
|
-
color: theme.palette.grey[500],
|
|
27
|
-
},
|
|
28
|
-
}))
|
|
10
|
+
import { Dialog } from '@jbrowse/core/ui'
|
|
29
11
|
|
|
30
12
|
function SortByTagDlg(props: {
|
|
31
13
|
model: { setSortedBy: Function }
|
|
32
14
|
handleClose: () => void
|
|
33
15
|
}) {
|
|
34
|
-
const { classes } = useStyles()
|
|
35
16
|
const { model, handleClose } = props
|
|
36
17
|
const [tag, setTag] = useState('')
|
|
37
18
|
const validTag = tag.match(/^[A-Za-z][A-Za-z0-9]$/)
|
|
38
19
|
return (
|
|
39
|
-
<Dialog open onClose={handleClose}>
|
|
40
|
-
<DialogTitle>
|
|
41
|
-
Sort by tag
|
|
42
|
-
<IconButton
|
|
43
|
-
aria-label="close"
|
|
44
|
-
className={classes.closeButton}
|
|
45
|
-
onClick={handleClose}
|
|
46
|
-
>
|
|
47
|
-
<CloseIcon />
|
|
48
|
-
</IconButton>
|
|
49
|
-
</DialogTitle>
|
|
20
|
+
<Dialog open onClose={handleClose} title="Sort by tag">
|
|
50
21
|
<DialogContent>
|
|
51
22
|
<Typography>Set the tag to sort by</Typography>
|
|
52
23
|
<Typography color="textSecondary">
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import { addDisposer, types, cast, getEnv, getSnapshot } from 'mobx-state-tree'
|
|
2
|
-
import
|
|
2
|
+
import clone from 'clone'
|
|
3
|
+
import { autorun } from 'mobx'
|
|
4
|
+
|
|
5
|
+
// jbrowse
|
|
6
|
+
import PluginManager from '@jbrowse/core/PluginManager'
|
|
3
7
|
import {
|
|
4
8
|
getConf,
|
|
5
9
|
readConfObject,
|
|
@@ -8,7 +12,6 @@ import {
|
|
|
8
12
|
} from '@jbrowse/core/configuration'
|
|
9
13
|
import { linearWiggleDisplayModelFactory } from '@jbrowse/plugin-wiggle'
|
|
10
14
|
import { LinearGenomeViewModel } from '@jbrowse/plugin-linear-genome-view'
|
|
11
|
-
import PluginManager from '@jbrowse/core/PluginManager'
|
|
12
15
|
import { getContainingView } from '@jbrowse/core/util'
|
|
13
16
|
|
|
14
17
|
// locals
|
|
@@ -75,7 +78,7 @@ function stateModelFactory(
|
|
|
75
78
|
}),
|
|
76
79
|
)
|
|
77
80
|
.volatile(() => ({
|
|
78
|
-
modificationTagMap:
|
|
81
|
+
modificationTagMap: undefined as Record<string, string> | undefined,
|
|
79
82
|
}))
|
|
80
83
|
.actions(self => ({
|
|
81
84
|
/**
|
|
@@ -109,12 +112,14 @@ function stateModelFactory(
|
|
|
109
112
|
const colorPalette = ['red', 'blue', 'green', 'orange', 'purple']
|
|
110
113
|
let i = 0
|
|
111
114
|
|
|
115
|
+
const newMap = clone(self.modificationTagMap) || {}
|
|
112
116
|
uniqueModifications.forEach(value => {
|
|
113
|
-
if (!
|
|
117
|
+
if (!newMap[value]) {
|
|
114
118
|
const newColor = colorPalette[i++]
|
|
115
|
-
|
|
119
|
+
newMap[value] = newColor
|
|
116
120
|
}
|
|
117
121
|
})
|
|
122
|
+
self.modificationTagMap = newMap
|
|
118
123
|
},
|
|
119
124
|
}))
|
|
120
125
|
.views(self => {
|
|
@@ -170,8 +175,7 @@ function stateModelFactory(
|
|
|
170
175
|
*/
|
|
171
176
|
get modificationsReady() {
|
|
172
177
|
return self.colorBy?.type === 'modifications'
|
|
173
|
-
?
|
|
174
|
-
.length > 0
|
|
178
|
+
? self.modificationTagMap !== undefined
|
|
175
179
|
: true
|
|
176
180
|
},
|
|
177
181
|
|
|
@@ -184,7 +188,7 @@ function stateModelFactory(
|
|
|
184
188
|
return {
|
|
185
189
|
...superProps,
|
|
186
190
|
notReady: superProps.notReady || !this.modificationsReady,
|
|
187
|
-
modificationTagMap:
|
|
191
|
+
modificationTagMap: modificationTagMap,
|
|
188
192
|
|
|
189
193
|
// must use getSnapshot because otherwise changes to e.g. just the
|
|
190
194
|
// colorBy.type are not read
|
|
@@ -28,7 +28,7 @@ const PileupRenderer = ConfigurationSchema(
|
|
|
28
28
|
model: types.enumeration('orientationType', ['fr', 'rf', 'ff']),
|
|
29
29
|
defaultValue: 'fr',
|
|
30
30
|
description:
|
|
31
|
-
'read sequencer
|
|
31
|
+
'read sequencer orientation. fr is normal "reads pointing at each other ---> <--- while some other sequencers can use other options',
|
|
32
32
|
},
|
|
33
33
|
/**
|
|
34
34
|
* #slot
|
|
@@ -6,46 +6,13 @@ import { AugmentedRegion as Region } from '@jbrowse/core/util/types'
|
|
|
6
6
|
import SimpleFeature, { Feature } from '@jbrowse/core/util/simpleFeature'
|
|
7
7
|
import { ObservableCreate } from '@jbrowse/core/util/rxjs'
|
|
8
8
|
import { toArray } from 'rxjs/operators'
|
|
9
|
-
import
|
|
10
|
-
|
|
11
|
-
getTagAlt,
|
|
12
|
-
fetchSequence,
|
|
13
|
-
shouldFetchReferenceSequence,
|
|
14
|
-
} from '../util'
|
|
15
|
-
import {
|
|
16
|
-
parseCigar,
|
|
17
|
-
getNextRefPos,
|
|
18
|
-
getModificationPositions,
|
|
19
|
-
Mismatch,
|
|
20
|
-
} from '../BamAdapter/MismatchParser'
|
|
21
|
-
|
|
22
|
-
function mismatchLen(mismatch: Mismatch) {
|
|
23
|
-
return !isInterbase(mismatch.type) ? mismatch.length : 1
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
function isInterbase(type: string) {
|
|
27
|
-
return type === 'softclip' || type === 'hardclip' || type === 'insertion'
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
31
|
-
function inc(bin: any, strand: number, type: string, field: string) {
|
|
32
|
-
let thisBin = bin[type][field]
|
|
33
|
-
if (thisBin === undefined) {
|
|
34
|
-
thisBin = bin[type][field] = {
|
|
35
|
-
total: 0,
|
|
36
|
-
'-1': 0,
|
|
37
|
-
'0': 0,
|
|
38
|
-
'1': 0,
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
thisBin.total++
|
|
42
|
-
thisBin[strand]++
|
|
43
|
-
}
|
|
9
|
+
import generateCoverageBins from './generateCoverageBins'
|
|
10
|
+
import { fetchSequence } from '../util'
|
|
44
11
|
|
|
45
12
|
export default class SNPCoverageAdapter extends BaseFeatureDataAdapter {
|
|
46
13
|
protected async configure() {
|
|
47
14
|
const subadapterConfig = this.getConf('subadapter')
|
|
48
|
-
const sequenceConf =
|
|
15
|
+
const sequenceConf = subadapterConfig.sequenceAdapter
|
|
49
16
|
const dataAdapter = await this.getSubAdapter?.(subadapterConfig)
|
|
50
17
|
|
|
51
18
|
const sequenceAdapter = sequenceConf
|
|
@@ -81,10 +48,11 @@ export default class SNPCoverageAdapter extends BaseFeatureDataAdapter {
|
|
|
81
48
|
.pipe(toArray())
|
|
82
49
|
.toPromise()
|
|
83
50
|
|
|
84
|
-
const { bins, skipmap } = await
|
|
51
|
+
const { bins, skipmap } = await generateCoverageBins(
|
|
85
52
|
feats,
|
|
86
53
|
region,
|
|
87
54
|
opts,
|
|
55
|
+
arg => this.fetchSequence(arg),
|
|
88
56
|
)
|
|
89
57
|
|
|
90
58
|
bins.forEach((bin, index) => {
|
|
@@ -135,218 +103,6 @@ export default class SNPCoverageAdapter extends BaseFeatureDataAdapter {
|
|
|
135
103
|
}
|
|
136
104
|
|
|
137
105
|
freeResources(/* { region } */): void {}
|
|
138
|
-
|
|
139
|
-
async generateCoverageBins(
|
|
140
|
-
features: Feature[],
|
|
141
|
-
region: Region,
|
|
142
|
-
opts: { bpPerPx?: number; colorBy?: { type: string; tag?: string } },
|
|
143
|
-
) {
|
|
144
|
-
const { colorBy } = opts
|
|
145
|
-
const binMax = Math.ceil(region.end - region.start)
|
|
146
|
-
|
|
147
|
-
const skipmap = {} as {
|
|
148
|
-
[key: string]: {
|
|
149
|
-
score: number
|
|
150
|
-
feature: unknown
|
|
151
|
-
start: number
|
|
152
|
-
end: number
|
|
153
|
-
strand: number
|
|
154
|
-
xs: string
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
// bins contain:
|
|
159
|
-
// - cov feature if they contribute to coverage
|
|
160
|
-
// - noncov are insertions/clip features that don't contribute to coverage
|
|
161
|
-
// - delskips deletions or introns that don't contribute to coverage
|
|
162
|
-
type BinType = { total: number; strands: { [key: string]: number } }
|
|
163
|
-
|
|
164
|
-
const regionSeq =
|
|
165
|
-
features.length && shouldFetchReferenceSequence(opts.colorBy?.type)
|
|
166
|
-
? await this.fetchSequence(region)
|
|
167
|
-
: undefined
|
|
168
|
-
|
|
169
|
-
const bins = [] as {
|
|
170
|
-
refbase?: string
|
|
171
|
-
total: number
|
|
172
|
-
all: number
|
|
173
|
-
ref: number
|
|
174
|
-
'-1': 0
|
|
175
|
-
'0': 0
|
|
176
|
-
'1': 0
|
|
177
|
-
lowqual: BinType
|
|
178
|
-
cov: BinType
|
|
179
|
-
delskips: BinType
|
|
180
|
-
noncov: BinType
|
|
181
|
-
}[]
|
|
182
|
-
|
|
183
|
-
for (let i = 0; i < features.length; i++) {
|
|
184
|
-
const feature = features[i]
|
|
185
|
-
const fstart = feature.get('start')
|
|
186
|
-
const fend = feature.get('end')
|
|
187
|
-
const fstrand = feature.get('strand') as -1 | 0 | 1
|
|
188
|
-
|
|
189
|
-
for (let j = fstart; j < fend + 1; j++) {
|
|
190
|
-
const i = j - region.start
|
|
191
|
-
if (i >= 0 && i < binMax) {
|
|
192
|
-
if (bins[i] === undefined) {
|
|
193
|
-
bins[i] = {
|
|
194
|
-
total: 0,
|
|
195
|
-
all: 0,
|
|
196
|
-
ref: 0,
|
|
197
|
-
'-1': 0,
|
|
198
|
-
'0': 0,
|
|
199
|
-
'1': 0,
|
|
200
|
-
lowqual: {} as BinType,
|
|
201
|
-
cov: {} as BinType,
|
|
202
|
-
delskips: {} as BinType,
|
|
203
|
-
noncov: {} as BinType,
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
if (j !== fend) {
|
|
207
|
-
bins[i].total++
|
|
208
|
-
bins[i].all++
|
|
209
|
-
bins[i].ref++
|
|
210
|
-
bins[i][fstrand]++
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
if (colorBy?.type === 'modifications') {
|
|
216
|
-
const seq = feature.get('seq') as string
|
|
217
|
-
const mm = (getTagAlt(feature, 'MM', 'Mm') as string) || ''
|
|
218
|
-
const ops = parseCigar(feature.get('CIGAR'))
|
|
219
|
-
const fend = feature.get('end')
|
|
220
|
-
|
|
221
|
-
getModificationPositions(mm, seq, fstrand).forEach(
|
|
222
|
-
({ type, positions }) => {
|
|
223
|
-
const mod = `mod_${type}`
|
|
224
|
-
for (const pos of getNextRefPos(ops, positions)) {
|
|
225
|
-
const epos = pos + fstart - region.start
|
|
226
|
-
if (epos >= 0 && epos < bins.length && pos + fstart < fend) {
|
|
227
|
-
const bin = bins[epos]
|
|
228
|
-
if (bin) {
|
|
229
|
-
inc(bin, fstrand, 'cov', mod)
|
|
230
|
-
} else {
|
|
231
|
-
console.warn(
|
|
232
|
-
'Undefined position in modifications snpcoverage encountered',
|
|
233
|
-
)
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
},
|
|
238
|
-
)
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
// methylation based coloring takes into account both reference
|
|
242
|
-
// sequence CpG detection and reads
|
|
243
|
-
else if (colorBy?.type === 'methylation') {
|
|
244
|
-
if (!regionSeq) {
|
|
245
|
-
throw new Error(
|
|
246
|
-
'no region sequence detected, need sequenceAdapter configuration',
|
|
247
|
-
)
|
|
248
|
-
}
|
|
249
|
-
const seq = feature.get('seq')
|
|
250
|
-
const mm = getTagAlt(feature, 'MM', 'Mm') || ''
|
|
251
|
-
const methBins = new Array(region.end - region.start).fill(0)
|
|
252
|
-
const ops = parseCigar(feature.get('CIGAR'))
|
|
253
|
-
|
|
254
|
-
getModificationPositions(mm, seq, fstrand).forEach(
|
|
255
|
-
({ type, positions }) => {
|
|
256
|
-
// we are processing methylation
|
|
257
|
-
if (type === 'm') {
|
|
258
|
-
for (const pos of getNextRefPos(ops, positions)) {
|
|
259
|
-
const epos = pos + fstart - region.start
|
|
260
|
-
if (epos >= 0 && epos < methBins.length) {
|
|
261
|
-
methBins[epos] = 1
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
},
|
|
266
|
-
)
|
|
267
|
-
|
|
268
|
-
for (let j = fstart; j < fend; j++) {
|
|
269
|
-
const i = j - region.start
|
|
270
|
-
if (i >= 0 && i < bins.length - 1) {
|
|
271
|
-
const l1 = regionSeq[i].toLowerCase()
|
|
272
|
-
const l2 = regionSeq[i + 1].toLowerCase()
|
|
273
|
-
const bin = bins[i]
|
|
274
|
-
const bin1 = bins[i + 1]
|
|
275
|
-
|
|
276
|
-
// color
|
|
277
|
-
if (l1 === 'c' && l2 === 'g') {
|
|
278
|
-
if (methBins[i] || methBins[i + 1]) {
|
|
279
|
-
inc(bin, fstrand, 'cov', 'meth')
|
|
280
|
-
inc(bin1, fstrand, 'cov', 'meth')
|
|
281
|
-
bins[i].ref--
|
|
282
|
-
bins[i][fstrand]--
|
|
283
|
-
bins[i + 1].ref--
|
|
284
|
-
bins[i + 1][fstrand]--
|
|
285
|
-
} else {
|
|
286
|
-
inc(bin, fstrand, 'cov', 'unmeth')
|
|
287
|
-
inc(bin1, fstrand, 'cov', 'unmeth')
|
|
288
|
-
bins[i].ref--
|
|
289
|
-
bins[i][fstrand]--
|
|
290
|
-
bins[i + 1].ref--
|
|
291
|
-
bins[i + 1][fstrand]--
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
// normal SNP based coloring
|
|
299
|
-
const mismatches = (feature.get('mismatches') as Mismatch[]) || []
|
|
300
|
-
const colorSNPs =
|
|
301
|
-
colorBy?.type !== 'modifications' && colorBy?.type !== 'methylation'
|
|
302
|
-
|
|
303
|
-
for (let i = 0; i < mismatches.length; i++) {
|
|
304
|
-
const mismatch = mismatches[i]
|
|
305
|
-
const mstart = fstart + mismatch.start
|
|
306
|
-
const mlen = mismatchLen(mismatch)
|
|
307
|
-
const mend = mstart + mlen
|
|
308
|
-
for (let j = mstart; j < mstart + mlen; j++) {
|
|
309
|
-
const epos = j - region.start
|
|
310
|
-
if (epos >= 0 && epos < bins.length) {
|
|
311
|
-
const bin = bins[epos]
|
|
312
|
-
const { base, type } = mismatch
|
|
313
|
-
const interbase = isInterbase(type)
|
|
314
|
-
if (!interbase) {
|
|
315
|
-
bin.ref--
|
|
316
|
-
bin[fstrand]--
|
|
317
|
-
} else {
|
|
318
|
-
inc(bin, fstrand, 'noncov', type)
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
if (type === 'deletion' || type === 'skip') {
|
|
322
|
-
inc(bin, fstrand, 'delskips', type)
|
|
323
|
-
bin.total--
|
|
324
|
-
} else if (!interbase && colorSNPs) {
|
|
325
|
-
inc(bin, fstrand, 'cov', base)
|
|
326
|
-
bin.refbase = mismatch.altbase
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
if (mismatch.type === 'skip') {
|
|
332
|
-
const hash = `${mstart}_${mend}_${fstrand}`
|
|
333
|
-
if (skipmap[hash] === undefined) {
|
|
334
|
-
skipmap[hash] = {
|
|
335
|
-
feature: feature,
|
|
336
|
-
start: mstart,
|
|
337
|
-
end: mend,
|
|
338
|
-
strand: fstrand,
|
|
339
|
-
xs: getTag(feature, 'XS') || getTag(feature, 'TS'),
|
|
340
|
-
score: 0,
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
skipmap[hash].score++
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
return { bins, skipmap }
|
|
349
|
-
}
|
|
350
106
|
}
|
|
351
107
|
|
|
352
108
|
const { capabilities } = SNPCoverageAdapter
|