@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
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
import { AugmentedRegion as Region } from '@jbrowse/core/util/types'
|
|
2
|
+
import { Feature } from '@jbrowse/core/util/simpleFeature'
|
|
3
|
+
import { getTag, getTagAlt, shouldFetchReferenceSequence } from '../util'
|
|
4
|
+
import {
|
|
5
|
+
parseCigar,
|
|
6
|
+
getNextRefPos,
|
|
7
|
+
getModificationPositions,
|
|
8
|
+
Mismatch,
|
|
9
|
+
} from '../BamAdapter/MismatchParser'
|
|
10
|
+
|
|
11
|
+
function mismatchLen(mismatch: Mismatch) {
|
|
12
|
+
return !isInterbase(mismatch.type) ? mismatch.length : 1
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function isInterbase(type: string) {
|
|
16
|
+
return type === 'softclip' || type === 'hardclip' || type === 'insertion'
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
20
|
+
function inc(bin: any, strand: number, type: string, field: string) {
|
|
21
|
+
let thisBin = bin[type][field]
|
|
22
|
+
if (thisBin === undefined) {
|
|
23
|
+
thisBin = bin[type][field] = {
|
|
24
|
+
total: 0,
|
|
25
|
+
'-1': 0,
|
|
26
|
+
'0': 0,
|
|
27
|
+
'1': 0,
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
thisBin.total++
|
|
31
|
+
thisBin[strand]++
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export default async function generateCoverageBins(
|
|
35
|
+
features: Feature[],
|
|
36
|
+
region: Region,
|
|
37
|
+
opts: { bpPerPx?: number; colorBy?: { type: string; tag?: string } },
|
|
38
|
+
fetchSequence: (arg: Region) => Promise<string>,
|
|
39
|
+
) {
|
|
40
|
+
const { colorBy } = opts
|
|
41
|
+
const binMax = Math.ceil(region.end - region.start)
|
|
42
|
+
|
|
43
|
+
const skipmap = {} as {
|
|
44
|
+
[key: string]: {
|
|
45
|
+
score: number
|
|
46
|
+
feature: unknown
|
|
47
|
+
start: number
|
|
48
|
+
end: number
|
|
49
|
+
strand: number
|
|
50
|
+
xs: string
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// bins contain:
|
|
55
|
+
// - cov feature if they contribute to coverage
|
|
56
|
+
// - noncov are insertions/clip features that don't contribute to coverage
|
|
57
|
+
// - delskips deletions or introns that don't contribute to coverage
|
|
58
|
+
type BinType = { total: number; strands: { [key: string]: number } }
|
|
59
|
+
|
|
60
|
+
const regionSeq =
|
|
61
|
+
features.length && shouldFetchReferenceSequence(opts.colorBy?.type)
|
|
62
|
+
? await fetchSequence(region)
|
|
63
|
+
: undefined
|
|
64
|
+
|
|
65
|
+
const bins = [] as {
|
|
66
|
+
refbase?: string
|
|
67
|
+
total: number
|
|
68
|
+
all: number
|
|
69
|
+
ref: number
|
|
70
|
+
'-1': 0
|
|
71
|
+
'0': 0
|
|
72
|
+
'1': 0
|
|
73
|
+
lowqual: BinType
|
|
74
|
+
cov: BinType
|
|
75
|
+
delskips: BinType
|
|
76
|
+
noncov: BinType
|
|
77
|
+
}[]
|
|
78
|
+
|
|
79
|
+
for (let i = 0; i < features.length; i++) {
|
|
80
|
+
const feature = features[i]
|
|
81
|
+
const fstart = feature.get('start')
|
|
82
|
+
const fend = feature.get('end')
|
|
83
|
+
const fstrand = feature.get('strand') as -1 | 0 | 1
|
|
84
|
+
|
|
85
|
+
for (let j = fstart; j < fend + 1; j++) {
|
|
86
|
+
const i = j - region.start
|
|
87
|
+
if (i >= 0 && i < binMax) {
|
|
88
|
+
if (bins[i] === undefined) {
|
|
89
|
+
bins[i] = {
|
|
90
|
+
total: 0,
|
|
91
|
+
all: 0,
|
|
92
|
+
ref: 0,
|
|
93
|
+
'-1': 0,
|
|
94
|
+
'0': 0,
|
|
95
|
+
'1': 0,
|
|
96
|
+
lowqual: {} as BinType,
|
|
97
|
+
cov: {} as BinType,
|
|
98
|
+
delskips: {} as BinType,
|
|
99
|
+
noncov: {} as BinType,
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
if (j !== fend) {
|
|
103
|
+
bins[i].total++
|
|
104
|
+
bins[i].all++
|
|
105
|
+
bins[i].ref++
|
|
106
|
+
bins[i][fstrand]++
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (colorBy?.type === 'modifications') {
|
|
112
|
+
const seq = feature.get('seq') as string
|
|
113
|
+
const mm = (getTagAlt(feature, 'MM', 'Mm') as string) || ''
|
|
114
|
+
const ops = parseCigar(feature.get('CIGAR'))
|
|
115
|
+
const fend = feature.get('end')
|
|
116
|
+
|
|
117
|
+
getModificationPositions(mm, seq, fstrand).forEach(
|
|
118
|
+
({ type, positions }) => {
|
|
119
|
+
const mod = `mod_${type}`
|
|
120
|
+
for (const pos of getNextRefPos(ops, positions)) {
|
|
121
|
+
const epos = pos + fstart - region.start
|
|
122
|
+
if (epos >= 0 && epos < bins.length && pos + fstart < fend) {
|
|
123
|
+
const bin = bins[epos]
|
|
124
|
+
if (bin) {
|
|
125
|
+
inc(bin, fstrand, 'cov', mod)
|
|
126
|
+
} else {
|
|
127
|
+
console.warn(
|
|
128
|
+
'Undefined position in modifications snpcoverage encountered',
|
|
129
|
+
)
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
},
|
|
134
|
+
)
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// methylation based coloring takes into account both reference
|
|
138
|
+
// sequence CpG detection and reads
|
|
139
|
+
else if (colorBy?.type === 'methylation') {
|
|
140
|
+
if (!regionSeq) {
|
|
141
|
+
throw new Error(
|
|
142
|
+
'no region sequence detected, need sequenceAdapter configuration',
|
|
143
|
+
)
|
|
144
|
+
}
|
|
145
|
+
const seq = feature.get('seq')
|
|
146
|
+
const mm = getTagAlt(feature, 'MM', 'Mm') || ''
|
|
147
|
+
const methBins = new Array(region.end - region.start).fill(0)
|
|
148
|
+
const ops = parseCigar(feature.get('CIGAR'))
|
|
149
|
+
|
|
150
|
+
getModificationPositions(mm, seq, fstrand).forEach(
|
|
151
|
+
({ type, positions }) => {
|
|
152
|
+
// we are processing methylation
|
|
153
|
+
if (type === 'm') {
|
|
154
|
+
for (const pos of getNextRefPos(ops, positions)) {
|
|
155
|
+
const epos = pos + fstart - region.start
|
|
156
|
+
if (epos >= 0 && epos < methBins.length) {
|
|
157
|
+
methBins[epos] = 1
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
},
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
for (let j = fstart; j < fend; j++) {
|
|
165
|
+
const i = j - region.start
|
|
166
|
+
if (i >= 0 && i < bins.length - 1) {
|
|
167
|
+
const l1 = regionSeq[i].toLowerCase()
|
|
168
|
+
const l2 = regionSeq[i + 1].toLowerCase()
|
|
169
|
+
const bin = bins[i]
|
|
170
|
+
const bin1 = bins[i + 1]
|
|
171
|
+
|
|
172
|
+
// color
|
|
173
|
+
if (l1 === 'c' && l2 === 'g') {
|
|
174
|
+
if (methBins[i] || methBins[i + 1]) {
|
|
175
|
+
inc(bin, fstrand, 'cov', 'meth')
|
|
176
|
+
inc(bin1, fstrand, 'cov', 'meth')
|
|
177
|
+
bins[i].ref--
|
|
178
|
+
bins[i][fstrand]--
|
|
179
|
+
bins[i + 1].ref--
|
|
180
|
+
bins[i + 1][fstrand]--
|
|
181
|
+
} else {
|
|
182
|
+
inc(bin, fstrand, 'cov', 'unmeth')
|
|
183
|
+
inc(bin1, fstrand, 'cov', 'unmeth')
|
|
184
|
+
bins[i].ref--
|
|
185
|
+
bins[i][fstrand]--
|
|
186
|
+
bins[i + 1].ref--
|
|
187
|
+
bins[i + 1][fstrand]--
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// normal SNP based coloring
|
|
195
|
+
const mismatches = (feature.get('mismatches') as Mismatch[]) || []
|
|
196
|
+
const colorSNPs =
|
|
197
|
+
colorBy?.type !== 'modifications' && colorBy?.type !== 'methylation'
|
|
198
|
+
|
|
199
|
+
for (let i = 0; i < mismatches.length; i++) {
|
|
200
|
+
const mismatch = mismatches[i]
|
|
201
|
+
const mstart = fstart + mismatch.start
|
|
202
|
+
const mlen = mismatchLen(mismatch)
|
|
203
|
+
const mend = mstart + mlen
|
|
204
|
+
for (let j = mstart; j < mstart + mlen; j++) {
|
|
205
|
+
const epos = j - region.start
|
|
206
|
+
if (epos >= 0 && epos < bins.length) {
|
|
207
|
+
const bin = bins[epos]
|
|
208
|
+
const { base, type } = mismatch
|
|
209
|
+
const interbase = isInterbase(type)
|
|
210
|
+
if (!interbase) {
|
|
211
|
+
bin.ref--
|
|
212
|
+
bin[fstrand]--
|
|
213
|
+
} else {
|
|
214
|
+
inc(bin, fstrand, 'noncov', type)
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
if (type === 'deletion' || type === 'skip') {
|
|
218
|
+
inc(bin, fstrand, 'delskips', type)
|
|
219
|
+
bin.total--
|
|
220
|
+
} else if (!interbase && colorSNPs) {
|
|
221
|
+
inc(bin, fstrand, 'cov', base)
|
|
222
|
+
bin.refbase = mismatch.altbase
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
if (mismatch.type === 'skip') {
|
|
228
|
+
const hash = `${mstart}_${mend}_${fstrand}`
|
|
229
|
+
if (skipmap[hash] === undefined) {
|
|
230
|
+
skipmap[hash] = {
|
|
231
|
+
feature: feature,
|
|
232
|
+
start: mstart,
|
|
233
|
+
end: mend,
|
|
234
|
+
strand: fstrand,
|
|
235
|
+
xs: getTag(feature, 'XS') || getTag(feature, 'TS'),
|
|
236
|
+
score: 0,
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
skipmap[hash].score++
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
return { bins, skipmap }
|
|
245
|
+
}
|