@jbrowse/plugin-alignments 2.2.0 → 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 (210) hide show
  1. package/dist/AlignmentsFeatureDetail/AlignmentsFeatureDetail.js +12 -13
  2. package/dist/AlignmentsFeatureDetail/AlignmentsFeatureDetail.js.map +1 -1
  3. package/dist/BamAdapter/BamAdapter.d.ts +1 -1
  4. package/dist/BamAdapter/BamAdapter.js +1 -0
  5. package/dist/BamAdapter/BamAdapter.js.map +1 -1
  6. package/dist/BamAdapter/BamSlightlyLazyFeature.js +1 -0
  7. package/dist/BamAdapter/BamSlightlyLazyFeature.js.map +1 -1
  8. package/dist/BamAdapter/MismatchParser.d.ts +2 -2
  9. package/dist/BamAdapter/MismatchParser.js +4 -7
  10. package/dist/BamAdapter/MismatchParser.js.map +1 -1
  11. package/dist/BamAdapter/configSchema.js +1 -1
  12. package/dist/BamAdapter/configSchema.js.map +1 -1
  13. package/dist/BamAdapter/index.js +7 -5
  14. package/dist/BamAdapter/index.js.map +1 -1
  15. package/dist/CramAdapter/CramSlightlyLazyFeature.d.ts +1 -10
  16. package/dist/CramAdapter/CramSlightlyLazyFeature.js +5 -218
  17. package/dist/CramAdapter/CramSlightlyLazyFeature.js.map +1 -1
  18. package/dist/CramAdapter/CramTestAdapters.d.ts +1 -1
  19. package/dist/CramAdapter/CramTestAdapters.js +3 -2
  20. package/dist/CramAdapter/CramTestAdapters.js.map +1 -1
  21. package/dist/CramAdapter/configSchema.js +2 -2
  22. package/dist/CramAdapter/configSchema.js.map +1 -1
  23. package/dist/CramAdapter/index.js +7 -5
  24. package/dist/CramAdapter/index.js.map +1 -1
  25. package/dist/CramAdapter/util.d.ts +18 -0
  26. package/dist/CramAdapter/util.js +241 -0
  27. package/dist/CramAdapter/util.js.map +1 -0
  28. package/dist/LinearAlignmentsDisplay/components/AlignmentsDisplay.js +1 -1
  29. package/dist/LinearAlignmentsDisplay/components/AlignmentsDisplay.js.map +1 -1
  30. package/dist/LinearAlignmentsDisplay/models/configSchema.d.ts +1 -1
  31. package/dist/LinearAlignmentsDisplay/models/model.d.ts +5 -5
  32. package/dist/LinearAlignmentsDisplay/models/model.js +7 -7
  33. package/dist/LinearAlignmentsDisplay/models/model.js.map +1 -1
  34. package/dist/LinearPileupDisplay/components/ColorByModifications.d.ts +3 -1
  35. package/dist/LinearPileupDisplay/components/ColorByModifications.js +7 -22
  36. package/dist/LinearPileupDisplay/components/ColorByModifications.js.map +1 -1
  37. package/dist/LinearPileupDisplay/components/ColorByTag.js +2 -22
  38. package/dist/LinearPileupDisplay/components/ColorByTag.js.map +1 -1
  39. package/dist/LinearPileupDisplay/components/FilterByTag.js +2 -15
  40. package/dist/LinearPileupDisplay/components/FilterByTag.js.map +1 -1
  41. package/dist/LinearPileupDisplay/components/SetFeatureHeight.js +2 -19
  42. package/dist/LinearPileupDisplay/components/SetFeatureHeight.js.map +1 -1
  43. package/dist/LinearPileupDisplay/components/SetMaxHeight.js +2 -18
  44. package/dist/LinearPileupDisplay/components/SetMaxHeight.js.map +1 -1
  45. package/dist/LinearPileupDisplay/components/SortByTag.js +2 -23
  46. package/dist/LinearPileupDisplay/components/SortByTag.js.map +1 -1
  47. package/dist/LinearPileupDisplay/configSchema.d.ts +4 -4
  48. package/dist/LinearPileupDisplay/configSchema.js +2 -2
  49. package/dist/LinearPileupDisplay/configSchema.js.map +1 -1
  50. package/dist/LinearPileupDisplay/index.d.ts +3 -0
  51. package/dist/LinearPileupDisplay/index.js +3 -0
  52. package/dist/LinearPileupDisplay/index.js.map +1 -1
  53. package/dist/LinearPileupDisplay/model.d.ts +19 -9
  54. package/dist/LinearPileupDisplay/model.js +25 -30
  55. package/dist/LinearPileupDisplay/model.js.map +1 -1
  56. package/dist/LinearSNPCoverageDisplay/components/Tooltip.d.ts +1 -1
  57. package/dist/LinearSNPCoverageDisplay/models/model.d.ts +14 -10
  58. package/dist/LinearSNPCoverageDisplay/models/model.js +8 -6
  59. package/dist/LinearSNPCoverageDisplay/models/model.js.map +1 -1
  60. package/dist/PileupRPC/rpcMethods.d.ts +1 -1
  61. package/dist/PileupRPC/rpcMethods.js +12 -7
  62. package/dist/PileupRPC/rpcMethods.js.map +1 -1
  63. package/dist/PileupRenderer/PileupLayoutSession.d.ts +1 -1
  64. package/dist/PileupRenderer/PileupRenderer.js +32 -28
  65. package/dist/PileupRenderer/PileupRenderer.js.map +1 -1
  66. package/dist/PileupRenderer/configSchema.js +1 -1
  67. package/dist/PileupRenderer/configSchema.js.map +1 -1
  68. package/dist/PileupRenderer/index.js +8 -6
  69. package/dist/PileupRenderer/index.js.map +1 -1
  70. package/dist/SNPCoverageAdapter/SNPCoverageAdapter.d.ts +0 -51
  71. package/dist/SNPCoverageAdapter/SNPCoverageAdapter.js +3 -182
  72. package/dist/SNPCoverageAdapter/SNPCoverageAdapter.js.map +1 -1
  73. package/dist/SNPCoverageAdapter/configSchema.d.ts +2 -3
  74. package/dist/SNPCoverageAdapter/configSchema.js +6 -3
  75. package/dist/SNPCoverageAdapter/configSchema.js.map +1 -1
  76. package/dist/SNPCoverageAdapter/generateCoverageBins.d.ts +53 -0
  77. package/dist/SNPCoverageAdapter/generateCoverageBins.js +185 -0
  78. package/dist/SNPCoverageAdapter/generateCoverageBins.js.map +1 -0
  79. package/dist/SNPCoverageAdapter/index.d.ts +1 -2
  80. package/dist/SNPCoverageAdapter/index.js +17 -14
  81. package/dist/SNPCoverageAdapter/index.js.map +1 -1
  82. package/dist/index.d.ts +3 -2
  83. package/dist/index.js +4 -2
  84. package/dist/index.js.map +1 -1
  85. package/esm/AlignmentsFeatureDetail/AlignmentsFeatureDetail.js +12 -13
  86. package/esm/AlignmentsFeatureDetail/AlignmentsFeatureDetail.js.map +1 -1
  87. package/esm/BamAdapter/BamAdapter.d.ts +1 -1
  88. package/esm/BamAdapter/BamAdapter.js +1 -0
  89. package/esm/BamAdapter/BamAdapter.js.map +1 -1
  90. package/esm/BamAdapter/BamSlightlyLazyFeature.js +1 -0
  91. package/esm/BamAdapter/BamSlightlyLazyFeature.js.map +1 -1
  92. package/esm/BamAdapter/MismatchParser.d.ts +2 -2
  93. package/esm/BamAdapter/MismatchParser.js +4 -7
  94. package/esm/BamAdapter/MismatchParser.js.map +1 -1
  95. package/esm/BamAdapter/configSchema.js +1 -1
  96. package/esm/BamAdapter/configSchema.js.map +1 -1
  97. package/esm/BamAdapter/index.js +7 -5
  98. package/esm/BamAdapter/index.js.map +1 -1
  99. package/esm/CramAdapter/CramSlightlyLazyFeature.d.ts +1 -10
  100. package/esm/CramAdapter/CramSlightlyLazyFeature.js +5 -218
  101. package/esm/CramAdapter/CramSlightlyLazyFeature.js.map +1 -1
  102. package/esm/CramAdapter/CramTestAdapters.d.ts +1 -1
  103. package/esm/CramAdapter/CramTestAdapters.js +3 -2
  104. package/esm/CramAdapter/CramTestAdapters.js.map +1 -1
  105. package/esm/CramAdapter/configSchema.js +2 -2
  106. package/esm/CramAdapter/configSchema.js.map +1 -1
  107. package/esm/CramAdapter/index.js +7 -5
  108. package/esm/CramAdapter/index.js.map +1 -1
  109. package/esm/CramAdapter/util.d.ts +18 -0
  110. package/esm/CramAdapter/util.js +236 -0
  111. package/esm/CramAdapter/util.js.map +1 -0
  112. package/esm/LinearAlignmentsDisplay/components/AlignmentsDisplay.js +1 -1
  113. package/esm/LinearAlignmentsDisplay/components/AlignmentsDisplay.js.map +1 -1
  114. package/esm/LinearAlignmentsDisplay/models/configSchema.d.ts +1 -1
  115. package/esm/LinearAlignmentsDisplay/models/model.d.ts +5 -5
  116. package/esm/LinearAlignmentsDisplay/models/model.js +7 -7
  117. package/esm/LinearAlignmentsDisplay/models/model.js.map +1 -1
  118. package/esm/LinearPileupDisplay/components/ColorByModifications.d.ts +3 -1
  119. package/esm/LinearPileupDisplay/components/ColorByModifications.js +8 -23
  120. package/esm/LinearPileupDisplay/components/ColorByModifications.js.map +1 -1
  121. package/esm/LinearPileupDisplay/components/ColorByTag.js +3 -20
  122. package/esm/LinearPileupDisplay/components/ColorByTag.js.map +1 -1
  123. package/esm/LinearPileupDisplay/components/FilterByTag.js +3 -13
  124. package/esm/LinearPileupDisplay/components/FilterByTag.js.map +1 -1
  125. package/esm/LinearPileupDisplay/components/SetFeatureHeight.js +3 -17
  126. package/esm/LinearPileupDisplay/components/SetFeatureHeight.js.map +1 -1
  127. package/esm/LinearPileupDisplay/components/SetMaxHeight.js +3 -16
  128. package/esm/LinearPileupDisplay/components/SetMaxHeight.js.map +1 -1
  129. package/esm/LinearPileupDisplay/components/SortByTag.js +3 -21
  130. package/esm/LinearPileupDisplay/components/SortByTag.js.map +1 -1
  131. package/esm/LinearPileupDisplay/configSchema.d.ts +4 -4
  132. package/esm/LinearPileupDisplay/configSchema.js +2 -2
  133. package/esm/LinearPileupDisplay/configSchema.js.map +1 -1
  134. package/esm/LinearPileupDisplay/index.d.ts +3 -0
  135. package/esm/LinearPileupDisplay/index.js +1 -0
  136. package/esm/LinearPileupDisplay/index.js.map +1 -1
  137. package/esm/LinearPileupDisplay/model.d.ts +19 -9
  138. package/esm/LinearPileupDisplay/model.js +25 -30
  139. package/esm/LinearPileupDisplay/model.js.map +1 -1
  140. package/esm/LinearSNPCoverageDisplay/components/Tooltip.d.ts +1 -1
  141. package/esm/LinearSNPCoverageDisplay/models/model.d.ts +14 -10
  142. package/esm/LinearSNPCoverageDisplay/models/model.js +9 -7
  143. package/esm/LinearSNPCoverageDisplay/models/model.js.map +1 -1
  144. package/esm/PileupRPC/rpcMethods.d.ts +1 -1
  145. package/esm/PileupRPC/rpcMethods.js +12 -7
  146. package/esm/PileupRPC/rpcMethods.js.map +1 -1
  147. package/esm/PileupRenderer/PileupLayoutSession.d.ts +1 -1
  148. package/esm/PileupRenderer/PileupRenderer.js +32 -28
  149. package/esm/PileupRenderer/PileupRenderer.js.map +1 -1
  150. package/esm/PileupRenderer/configSchema.js +1 -1
  151. package/esm/PileupRenderer/configSchema.js.map +1 -1
  152. package/esm/PileupRenderer/index.js +8 -6
  153. package/esm/PileupRenderer/index.js.map +1 -1
  154. package/esm/SNPCoverageAdapter/SNPCoverageAdapter.d.ts +0 -51
  155. package/esm/SNPCoverageAdapter/SNPCoverageAdapter.js +4 -183
  156. package/esm/SNPCoverageAdapter/SNPCoverageAdapter.js.map +1 -1
  157. package/esm/SNPCoverageAdapter/configSchema.d.ts +2 -3
  158. package/esm/SNPCoverageAdapter/configSchema.js +6 -3
  159. package/esm/SNPCoverageAdapter/configSchema.js.map +1 -1
  160. package/esm/SNPCoverageAdapter/generateCoverageBins.d.ts +53 -0
  161. package/esm/SNPCoverageAdapter/generateCoverageBins.js +182 -0
  162. package/esm/SNPCoverageAdapter/generateCoverageBins.js.map +1 -0
  163. package/esm/SNPCoverageAdapter/index.d.ts +1 -2
  164. package/esm/SNPCoverageAdapter/index.js +17 -15
  165. package/esm/SNPCoverageAdapter/index.js.map +1 -1
  166. package/esm/index.d.ts +3 -2
  167. package/esm/index.js +2 -2
  168. package/esm/index.js.map +1 -1
  169. package/package.json +4 -3
  170. package/src/AlignmentsFeatureDetail/AlignmentsFeatureDetail.tsx +17 -16
  171. package/src/AlignmentsFeatureDetail/__snapshots__/index.test.js.snap +1 -473
  172. package/src/AlignmentsFeatureDetail/index.test.js +6 -4
  173. package/src/BamAdapter/BamAdapter.ts +3 -2
  174. package/src/BamAdapter/BamSlightlyLazyFeature.ts +2 -1
  175. package/src/BamAdapter/MismatchParser.test.ts +21 -12
  176. package/src/BamAdapter/MismatchParser.ts +7 -10
  177. package/src/BamAdapter/__snapshots__/BamAdapter.test.ts.snap +135 -135
  178. package/src/BamAdapter/configSchema.ts +1 -1
  179. package/src/BamAdapter/index.ts +7 -8
  180. package/src/CombinationTest.test.ts +107 -0
  181. package/src/CramAdapter/CramSlightlyLazyFeature.ts +10 -219
  182. package/src/CramAdapter/CramTestAdapters.ts +2 -1
  183. package/src/CramAdapter/__snapshots__/CramAdapter.test.ts.snap +31 -31
  184. package/src/CramAdapter/__snapshots__/util.test.ts.snap +14 -0
  185. package/src/CramAdapter/configSchema.ts +2 -3
  186. package/src/CramAdapter/index.ts +7 -8
  187. package/src/CramAdapter/util.test.ts +26 -0
  188. package/src/CramAdapter/util.ts +251 -0
  189. package/src/LinearAlignmentsDisplay/components/AlignmentsDisplay.tsx +3 -1
  190. package/src/LinearAlignmentsDisplay/models/model.tsx +8 -7
  191. package/src/LinearPileupDisplay/components/ColorByModifications.tsx +5 -34
  192. package/src/LinearPileupDisplay/components/ColorByTag.tsx +2 -29
  193. package/src/LinearPileupDisplay/components/FilterByTag.tsx +2 -22
  194. package/src/LinearPileupDisplay/components/SetFeatureHeight.tsx +2 -22
  195. package/src/LinearPileupDisplay/components/SetMaxHeight.tsx +2 -24
  196. package/src/LinearPileupDisplay/components/SortByTag.tsx +2 -31
  197. package/src/LinearPileupDisplay/configSchema.ts +3 -5
  198. package/src/LinearPileupDisplay/index.ts +5 -0
  199. package/src/LinearPileupDisplay/model.ts +31 -30
  200. package/src/LinearSNPCoverageDisplay/models/model.ts +12 -8
  201. package/src/PileupRPC/rpcMethods.ts +15 -20
  202. package/src/PileupRenderer/{PileupRenderer.tsx → PileupRenderer.ts} +48 -54
  203. package/src/PileupRenderer/configSchema.ts +1 -1
  204. package/src/PileupRenderer/index.ts +8 -9
  205. package/src/SNPCoverageAdapter/SNPCoverageAdapter.ts +5 -249
  206. package/src/SNPCoverageAdapter/configSchema.ts +14 -13
  207. package/src/SNPCoverageAdapter/generateCoverageBins.ts +245 -0
  208. package/src/SNPCoverageAdapter/index.ts +17 -18
  209. package/src/__snapshots__/index.test.ts.snap +1 -1
  210. package/src/index.ts +11 -4
@@ -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
- getTag,
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 = this.getConf(['subadapter', 'sequenceAdapter'])
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 this.generateCoverageBins(
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
@@ -1,22 +1,23 @@
1
1
  import { ConfigurationSchema } from '@jbrowse/core/configuration'
2
- import PluginManager from '@jbrowse/core/PluginManager'
3
2
 
4
3
  /**
5
4
  * #config SNPCoverageAdapter
6
5
  */
7
6
  function x() {} // eslint-disable-line @typescript-eslint/no-unused-vars
8
7
 
9
- const SNPCoverageAdapter = (pluginManager: PluginManager) =>
10
- ConfigurationSchema(
11
- 'SNPCoverageAdapter',
12
- {
13
- /**
14
- * #slot
15
- * normally refers to a BAM or CRAM adapter
16
- */
17
- subadapter: pluginManager.pluggableConfigSchemaType('adapter'),
8
+ const configSchema = ConfigurationSchema(
9
+ 'SNPCoverageAdapter',
10
+ {
11
+ /**
12
+ * #slot
13
+ * normally refers to a BAM or CRAM adapter
14
+ */
15
+ subadapter: {
16
+ type: 'frozen',
17
+ defaultValue: null,
18
18
  },
19
- { explicitlyTyped: true },
20
- )
19
+ },
20
+ { explicitlyTyped: true },
21
+ )
21
22
 
22
- export default SNPCoverageAdapter
23
+ export default configSchema
@@ -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
+ }
@@ -1,23 +1,22 @@
1
1
  import PluginManager from '@jbrowse/core/PluginManager'
2
2
  import AdapterType from '@jbrowse/core/pluggableElementTypes/AdapterType'
3
- import configSchemaFactory from './configSchema'
3
+ import configSchema from './configSchema'
4
4
  import { capabilities } from './SNPCoverageAdapter'
5
5
 
6
- export default (pluginManager: PluginManager) => {
7
- pluginManager.addAdapterType(
8
- () =>
9
- new AdapterType({
10
- name: 'SNPCoverageAdapter',
11
- adapterMetadata: {
12
- category: null,
13
- displayName: null,
14
- hiddenFromGUI: true,
15
- description: null,
16
- },
17
- getAdapterClass: () =>
18
- import('./SNPCoverageAdapter').then(r => r.default),
19
- configSchema: configSchemaFactory(pluginManager),
20
- adapterCapabilities: capabilities,
21
- }),
22
- )
6
+ export default function (pluginManager: PluginManager) {
7
+ pluginManager.addAdapterType(() => {
8
+ return new AdapterType({
9
+ name: 'SNPCoverageAdapter',
10
+ adapterMetadata: {
11
+ category: null,
12
+ displayName: null,
13
+ hiddenFromGUI: true,
14
+ description: null,
15
+ },
16
+ getAdapterClass: () =>
17
+ import('./SNPCoverageAdapter').then(r => r.default),
18
+ configSchema,
19
+ adapterCapabilities: capabilities,
20
+ })
21
+ })
23
22
  }
@@ -1,3 +1,3 @@
1
1
  // Jest Snapshot v1, https://goo.gl/fbAQLP
2
2
 
3
- exports[`plugin in a stock JBrowse 1`] = `Object {}`;
3
+ exports[`plugin in a stock JBrowse 1`] = `{}`;
package/src/index.ts CHANGED
@@ -12,7 +12,10 @@ import SNPCoverageRendererF from './SNPCoverageRenderer'
12
12
  import PileupRendererF from './PileupRenderer'
13
13
  import LinearAlignmentsDisplayF from './LinearAlignmentsDisplay'
14
14
  import LinearSNPCoverageDisplayF from './LinearSNPCoverageDisplay'
15
- import LinearPileupDisplayF from './LinearPileupDisplay'
15
+ import LinearPileupDisplayF, {
16
+ linearPileupDisplayStateModelFactory,
17
+ linearPileupDisplayConfigSchemaFactory,
18
+ } from './LinearPileupDisplay'
16
19
  import AlignmentsTrackF from './AlignmentsTrack'
17
20
  import AlignmentsFeatureWidgetF from './AlignmentsFeatureDetail'
18
21
 
@@ -28,9 +31,6 @@ import {
28
31
  TrackTypeGuesser,
29
32
  } from '@jbrowse/core/util/tracks'
30
33
 
31
- export { MismatchParser }
32
- export type { LinearPileupDisplayModel }
33
-
34
34
  export default class AlignmentsPlugin extends Plugin {
35
35
  name = 'AlignmentsPlugin'
36
36
 
@@ -125,3 +125,10 @@ export default class AlignmentsPlugin extends Plugin {
125
125
  )
126
126
  }
127
127
  }
128
+
129
+ export {
130
+ linearPileupDisplayConfigSchemaFactory,
131
+ linearPileupDisplayStateModelFactory,
132
+ MismatchParser,
133
+ }
134
+ export type { LinearPileupDisplayModel }