@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.
Files changed (64) hide show
  1. package/dist/CramAdapter/CramTestAdapters.js +2 -1
  2. package/dist/CramAdapter/CramTestAdapters.js.map +1 -1
  3. package/dist/LinearPileupDisplay/components/ColorByModifications.d.ts +3 -1
  4. package/dist/LinearPileupDisplay/components/ColorByModifications.js +7 -22
  5. package/dist/LinearPileupDisplay/components/ColorByModifications.js.map +1 -1
  6. package/dist/LinearPileupDisplay/components/ColorByTag.js +2 -22
  7. package/dist/LinearPileupDisplay/components/ColorByTag.js.map +1 -1
  8. package/dist/LinearPileupDisplay/components/FilterByTag.js +2 -15
  9. package/dist/LinearPileupDisplay/components/FilterByTag.js.map +1 -1
  10. package/dist/LinearPileupDisplay/components/SetFeatureHeight.js +2 -19
  11. package/dist/LinearPileupDisplay/components/SetFeatureHeight.js.map +1 -1
  12. package/dist/LinearPileupDisplay/components/SetMaxHeight.js +2 -18
  13. package/dist/LinearPileupDisplay/components/SetMaxHeight.js.map +1 -1
  14. package/dist/LinearPileupDisplay/components/SortByTag.js +2 -23
  15. package/dist/LinearPileupDisplay/components/SortByTag.js.map +1 -1
  16. package/dist/LinearSNPCoverageDisplay/models/model.d.ts +13 -9
  17. package/dist/LinearSNPCoverageDisplay/models/model.js +8 -6
  18. package/dist/LinearSNPCoverageDisplay/models/model.js.map +1 -1
  19. package/dist/PileupRenderer/configSchema.js +1 -1
  20. package/dist/PileupRenderer/configSchema.js.map +1 -1
  21. package/dist/SNPCoverageAdapter/SNPCoverageAdapter.d.ts +0 -51
  22. package/dist/SNPCoverageAdapter/SNPCoverageAdapter.js +3 -182
  23. package/dist/SNPCoverageAdapter/SNPCoverageAdapter.js.map +1 -1
  24. package/dist/SNPCoverageAdapter/generateCoverageBins.d.ts +53 -0
  25. package/dist/SNPCoverageAdapter/generateCoverageBins.js +185 -0
  26. package/dist/SNPCoverageAdapter/generateCoverageBins.js.map +1 -0
  27. package/esm/CramAdapter/CramTestAdapters.js +2 -1
  28. package/esm/CramAdapter/CramTestAdapters.js.map +1 -1
  29. package/esm/LinearPileupDisplay/components/ColorByModifications.d.ts +3 -1
  30. package/esm/LinearPileupDisplay/components/ColorByModifications.js +8 -23
  31. package/esm/LinearPileupDisplay/components/ColorByModifications.js.map +1 -1
  32. package/esm/LinearPileupDisplay/components/ColorByTag.js +3 -20
  33. package/esm/LinearPileupDisplay/components/ColorByTag.js.map +1 -1
  34. package/esm/LinearPileupDisplay/components/FilterByTag.js +3 -13
  35. package/esm/LinearPileupDisplay/components/FilterByTag.js.map +1 -1
  36. package/esm/LinearPileupDisplay/components/SetFeatureHeight.js +3 -17
  37. package/esm/LinearPileupDisplay/components/SetFeatureHeight.js.map +1 -1
  38. package/esm/LinearPileupDisplay/components/SetMaxHeight.js +3 -16
  39. package/esm/LinearPileupDisplay/components/SetMaxHeight.js.map +1 -1
  40. package/esm/LinearPileupDisplay/components/SortByTag.js +3 -21
  41. package/esm/LinearPileupDisplay/components/SortByTag.js.map +1 -1
  42. package/esm/LinearSNPCoverageDisplay/models/model.d.ts +13 -9
  43. package/esm/LinearSNPCoverageDisplay/models/model.js +9 -7
  44. package/esm/LinearSNPCoverageDisplay/models/model.js.map +1 -1
  45. package/esm/PileupRenderer/configSchema.js +1 -1
  46. package/esm/PileupRenderer/configSchema.js.map +1 -1
  47. package/esm/SNPCoverageAdapter/SNPCoverageAdapter.d.ts +0 -51
  48. package/esm/SNPCoverageAdapter/SNPCoverageAdapter.js +4 -183
  49. package/esm/SNPCoverageAdapter/SNPCoverageAdapter.js.map +1 -1
  50. package/esm/SNPCoverageAdapter/generateCoverageBins.d.ts +53 -0
  51. package/esm/SNPCoverageAdapter/generateCoverageBins.js +182 -0
  52. package/esm/SNPCoverageAdapter/generateCoverageBins.js.map +1 -0
  53. package/package.json +3 -3
  54. package/src/CramAdapter/CramTestAdapters.ts +1 -0
  55. package/src/LinearPileupDisplay/components/ColorByModifications.tsx +5 -34
  56. package/src/LinearPileupDisplay/components/ColorByTag.tsx +2 -29
  57. package/src/LinearPileupDisplay/components/FilterByTag.tsx +2 -22
  58. package/src/LinearPileupDisplay/components/SetFeatureHeight.tsx +2 -22
  59. package/src/LinearPileupDisplay/components/SetMaxHeight.tsx +2 -24
  60. package/src/LinearPileupDisplay/components/SortByTag.tsx +2 -31
  61. package/src/LinearSNPCoverageDisplay/models/model.ts +12 -8
  62. package/src/PileupRenderer/configSchema.ts +1 -1
  63. package/src/SNPCoverageAdapter/SNPCoverageAdapter.ts +5 -249
  64. 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
+ }