@jbrowse/plugin-alignments 1.6.1 → 1.6.5
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/plugin-alignments.cjs.development.js +37 -43
- package/dist/plugin-alignments.cjs.development.js.map +1 -1
- package/dist/plugin-alignments.cjs.production.min.js +1 -1
- package/dist/plugin-alignments.cjs.production.min.js.map +1 -1
- package/dist/plugin-alignments.esm.js +37 -43
- package/dist/plugin-alignments.esm.js.map +1 -1
- package/package.json +5 -5
- package/src/BamAdapter/BamAdapter.ts +3 -4
- package/src/PileupRenderer/PileupRenderer.tsx +11 -10
- package/src/SNPCoverageAdapter/SNPCoverageAdapter.ts +170 -155
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jbrowse/plugin-alignments",
|
|
3
|
-
"version": "1.6.
|
|
3
|
+
"version": "1.6.5",
|
|
4
4
|
"description": "JBrowse 2 alignments adapters, tracks, etc.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"jbrowse",
|
|
@@ -35,10 +35,10 @@
|
|
|
35
35
|
"useSrc": "node ../../scripts/useSrc.js"
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@gmod/bam": "^1.1.
|
|
39
|
-
"@gmod/cram": "^1.
|
|
38
|
+
"@gmod/bam": "^1.1.12",
|
|
39
|
+
"@gmod/cram": "^1.6.1",
|
|
40
40
|
"@material-ui/icons": "^4.9.1",
|
|
41
|
-
"abortable-promise-cache": "^1.
|
|
41
|
+
"abortable-promise-cache": "^1.5.0",
|
|
42
42
|
"color": "^3.1.2",
|
|
43
43
|
"copy-to-clipboard": "^3.3.1",
|
|
44
44
|
"fast-deep-equal": "^3.1.3",
|
|
@@ -61,5 +61,5 @@
|
|
|
61
61
|
"publishConfig": {
|
|
62
62
|
"access": "public"
|
|
63
63
|
},
|
|
64
|
-
"gitHead": "
|
|
64
|
+
"gitHead": "ab41f017840ffef09f5d60b008281cedaa5abe26"
|
|
65
65
|
}
|
|
@@ -197,11 +197,10 @@ export default class BamAdapter extends BaseFeatureDataAdapter {
|
|
|
197
197
|
|
|
198
198
|
async estimateRegionsStats(regions: Region[], opts?: BaseOptions) {
|
|
199
199
|
const { bam } = await this.configure()
|
|
200
|
-
// @ts-ignore
|
|
201
|
-
const index = bam.index
|
|
202
200
|
// this is a method to avoid calling on htsget adapters
|
|
203
|
-
|
|
204
|
-
|
|
201
|
+
// @ts-ignore
|
|
202
|
+
if (bam.index.filehandle !== '?') {
|
|
203
|
+
const bytes = await bytesForRegions(regions, bam)
|
|
205
204
|
const fetchSizeLimit = readConfObject(this.config, 'fetchSizeLimit')
|
|
206
205
|
return { bytes, fetchSizeLimit }
|
|
207
206
|
} else {
|
|
@@ -716,20 +716,21 @@ export default class PileupRenderer extends BoxRendererType {
|
|
|
716
716
|
ctx.fillText(`(${mismatch.base})`, leftPx + 2, topPx + heightPx)
|
|
717
717
|
}
|
|
718
718
|
} else if (mismatch.type === 'skip') {
|
|
719
|
-
// fix to avoid bad rendering
|
|
720
|
-
//
|
|
721
|
-
// ref #1236
|
|
719
|
+
// fix to avoid bad rendering note that this was also related to chrome
|
|
720
|
+
// bug https://bugs.chromium.org/p/chromium/issues/detail?id=1131528
|
|
721
|
+
// also affected firefox ref #1236 #2750
|
|
722
722
|
if (leftPx + widthPx > 0) {
|
|
723
723
|
// make small exons more visible when zoomed far out
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
724
|
+
const adjustPx = widthPx - (bpPerPx > 10 ? 1.5 : 0)
|
|
725
|
+
ctx.clearRect(leftPx, topPx, adjustPx, heightPx)
|
|
726
|
+
ctx.fillStyle = '#333'
|
|
727
|
+
ctx.fillRect(
|
|
728
|
+
Math.max(0, leftPx),
|
|
729
|
+
topPx + heightPx / 2 - 1,
|
|
730
|
+
adjustPx + (leftPx < 0 ? leftPx : 0),
|
|
731
|
+
2,
|
|
729
732
|
)
|
|
730
733
|
}
|
|
731
|
-
ctx.fillStyle = '#333'
|
|
732
|
-
ctx.fillRect(leftPx, topPx + heightPx / 2, widthPx, 2)
|
|
733
734
|
}
|
|
734
735
|
}
|
|
735
736
|
|
|
@@ -9,7 +9,7 @@ import SerializableFilterChain from '@jbrowse/core/pluggableElementTypes/rendere
|
|
|
9
9
|
import { ObservableCreate } from '@jbrowse/core/util/rxjs'
|
|
10
10
|
import { reduce, filter, toArray } from 'rxjs/operators'
|
|
11
11
|
import { Observable } from 'rxjs'
|
|
12
|
-
import { getTagAlt } from '../util'
|
|
12
|
+
import { getTag, getTagAlt } from '../util'
|
|
13
13
|
import {
|
|
14
14
|
parseCigar,
|
|
15
15
|
getNextRefPos,
|
|
@@ -171,14 +171,6 @@ export default class SNPCoverageAdapter extends BaseFeatureDataAdapter {
|
|
|
171
171
|
// delskips are elements that don't contribute to coverage, but should be
|
|
172
172
|
// reported also (and are not interbase)
|
|
173
173
|
type BinType = { total: number; strands: { [key: string]: number } }
|
|
174
|
-
const initBins = Array.from({ length: binMax }, () => ({
|
|
175
|
-
total: 0,
|
|
176
|
-
lowqual: {} as BinType,
|
|
177
|
-
cov: {} as BinType,
|
|
178
|
-
delskips: {} as BinType,
|
|
179
|
-
noncov: {} as BinType,
|
|
180
|
-
ref: {} as BinType,
|
|
181
|
-
}))
|
|
182
174
|
|
|
183
175
|
// request an extra +1 on the end to get CpG crossing region boundary
|
|
184
176
|
let regionSeq: string | undefined
|
|
@@ -189,7 +181,7 @@ export default class SNPCoverageAdapter extends BaseFeatureDataAdapter {
|
|
|
189
181
|
refName: originalRefName || refName,
|
|
190
182
|
start,
|
|
191
183
|
end: end + 1,
|
|
192
|
-
assemblyName:
|
|
184
|
+
assemblyName: region.assemblyName,
|
|
193
185
|
})
|
|
194
186
|
.pipe(toArray())
|
|
195
187
|
.toPromise()
|
|
@@ -198,172 +190,195 @@ export default class SNPCoverageAdapter extends BaseFeatureDataAdapter {
|
|
|
198
190
|
|
|
199
191
|
const bins = await features
|
|
200
192
|
.pipe(
|
|
201
|
-
reduce(
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
for (let j = fstart; j < fend; j++) {
|
|
209
|
-
const i = j - region.start
|
|
210
|
-
if (i >= 0 && i < bins.length) {
|
|
211
|
-
const bin = bins[i]
|
|
212
|
-
bin.total++
|
|
213
|
-
inc(bin, fstrand, 'ref', 'ref')
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
if (colorBy?.type === 'modifications') {
|
|
218
|
-
const seq = feature.get('seq')
|
|
219
|
-
const mm = getTagAlt(feature, 'MM', 'Mm') || ''
|
|
193
|
+
reduce(
|
|
194
|
+
(bins, feature) => {
|
|
195
|
+
const cigar = feature.get('CIGAR')
|
|
196
|
+
const fstart = feature.get('start')
|
|
197
|
+
const fend = feature.get('end')
|
|
198
|
+
const fstrand = feature.get('strand')
|
|
199
|
+
const cigarOps = parseCigar(cigar)
|
|
220
200
|
|
|
221
|
-
|
|
222
|
-
|
|
201
|
+
for (let j = fstart; j < fend; j++) {
|
|
202
|
+
const i = j - region.start
|
|
203
|
+
if (i >= 0 && i < binMax) {
|
|
204
|
+
const bin = bins[i] || {
|
|
205
|
+
total: 0,
|
|
206
|
+
lowqual: {} as BinType,
|
|
207
|
+
cov: {} as BinType,
|
|
208
|
+
delskips: {} as BinType,
|
|
209
|
+
noncov: {} as BinType,
|
|
210
|
+
ref: {} as BinType,
|
|
211
|
+
}
|
|
212
|
+
bin.total++
|
|
213
|
+
inc(bin, fstrand, 'ref', 'ref')
|
|
214
|
+
bins[i] = bin
|
|
215
|
+
}
|
|
216
|
+
}
|
|
223
217
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
218
|
+
if (colorBy?.type === 'modifications') {
|
|
219
|
+
const seq = feature.get('seq')
|
|
220
|
+
const mm = getTagAlt(feature, 'MM', 'Mm') || ''
|
|
221
|
+
|
|
222
|
+
const ml =
|
|
223
|
+
(getTagAlt(feature, 'ML', 'Ml') as number[] | string) || []
|
|
224
|
+
|
|
225
|
+
const probabilities = ml
|
|
226
|
+
? (typeof ml === 'string'
|
|
227
|
+
? ml.split(',').map(e => +e)
|
|
228
|
+
: ml
|
|
229
|
+
).map(e => e / 255)
|
|
230
|
+
: (getTagAlt(feature, 'MP', 'Mp') as string)
|
|
231
|
+
.split('')
|
|
232
|
+
.map(s => s.charCodeAt(0) - 33)
|
|
233
|
+
.map(elt => Math.min(1, elt / 50))
|
|
234
|
+
|
|
235
|
+
let probIndex = 0
|
|
236
|
+
getModificationPositions(mm, seq, fstrand).forEach(
|
|
237
|
+
({ type, positions }) => {
|
|
238
|
+
const mod = `mod_${type}`
|
|
239
|
+
for (const pos of getNextRefPos(cigarOps, positions)) {
|
|
240
|
+
const epos = pos + fstart - region.start
|
|
241
|
+
if (
|
|
242
|
+
epos >= 0 &&
|
|
243
|
+
epos < bins.length &&
|
|
244
|
+
pos + fstart < fend
|
|
245
|
+
) {
|
|
246
|
+
const bin = bins[epos]
|
|
247
|
+
if (probabilities[probIndex] > 0.5) {
|
|
248
|
+
inc(bin, fstrand, 'cov', mod)
|
|
249
|
+
} else {
|
|
250
|
+
inc(bin, fstrand, 'lowqual', mod)
|
|
251
|
+
}
|
|
245
252
|
}
|
|
253
|
+
probIndex++
|
|
246
254
|
}
|
|
247
|
-
|
|
248
|
-
}
|
|
249
|
-
},
|
|
250
|
-
)
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
// methylation based coloring takes into account both reference
|
|
254
|
-
// sequence CpG detection and reads
|
|
255
|
-
else if (colorBy?.type === 'methylation') {
|
|
256
|
-
if (!regionSeq) {
|
|
257
|
-
throw new Error(
|
|
258
|
-
'no region sequence detected, need sequenceAdapter configuration',
|
|
255
|
+
},
|
|
259
256
|
)
|
|
260
257
|
}
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
258
|
+
|
|
259
|
+
// methylation based coloring takes into account both reference
|
|
260
|
+
// sequence CpG detection and reads
|
|
261
|
+
else if (colorBy?.type === 'methylation') {
|
|
262
|
+
if (!regionSeq) {
|
|
263
|
+
throw new Error(
|
|
264
|
+
'no region sequence detected, need sequenceAdapter configuration',
|
|
265
|
+
)
|
|
266
|
+
}
|
|
267
|
+
const seq = feature.get('seq')
|
|
268
|
+
const mm = getTagAlt(feature, 'MM', 'Mm') || ''
|
|
269
|
+
const methBins = new Array(region.end - region.start).fill(0)
|
|
270
|
+
|
|
271
|
+
getModificationPositions(mm, seq, fstrand).forEach(
|
|
272
|
+
({ type, positions }) => {
|
|
273
|
+
// we are processing methylation
|
|
274
|
+
if (type === 'm') {
|
|
275
|
+
for (const pos of getNextRefPos(cigarOps, positions)) {
|
|
276
|
+
const epos = pos + fstart - region.start
|
|
277
|
+
if (epos >= 0 && epos < methBins.length) {
|
|
278
|
+
methBins[epos] = 1
|
|
279
|
+
}
|
|
273
280
|
}
|
|
274
281
|
}
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
)
|
|
282
|
+
},
|
|
283
|
+
)
|
|
278
284
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
285
|
+
for (let j = fstart; j < fend; j++) {
|
|
286
|
+
const i = j - region.start
|
|
287
|
+
if (i >= 0 && i < bins.length - 1) {
|
|
288
|
+
const l1 = regionSeq[i].toLowerCase()
|
|
289
|
+
const l2 = regionSeq[i + 1].toLowerCase()
|
|
290
|
+
const bin = bins[i]
|
|
291
|
+
const bin1 = bins[i + 1]
|
|
292
|
+
|
|
293
|
+
// color
|
|
294
|
+
if (l1 === 'c' && l2 === 'g') {
|
|
295
|
+
if (methBins[i] || methBins[i + 1]) {
|
|
296
|
+
inc(bin, fstrand, 'cov', 'meth')
|
|
297
|
+
inc(bin1, fstrand, 'cov', 'meth')
|
|
298
|
+
dec(bin, fstrand, 'ref', 'ref')
|
|
299
|
+
dec(bin1, fstrand, 'ref', 'ref')
|
|
300
|
+
} else {
|
|
301
|
+
inc(bin, fstrand, 'cov', 'unmeth')
|
|
302
|
+
inc(bin1, fstrand, 'cov', 'unmeth')
|
|
303
|
+
dec(bin, fstrand, 'ref', 'ref')
|
|
304
|
+
dec(bin1, fstrand, 'ref', 'ref')
|
|
305
|
+
}
|
|
299
306
|
}
|
|
300
307
|
}
|
|
301
308
|
}
|
|
302
309
|
}
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
// normal SNP based coloring
|
|
306
|
-
else {
|
|
307
|
-
const mismatches = feature.get('mismatches') as
|
|
308
|
-
| Mismatch[]
|
|
309
|
-
| undefined
|
|
310
|
-
|
|
311
|
-
if (mismatches) {
|
|
312
|
-
for (let i = 0; i < mismatches.length; i++) {
|
|
313
|
-
const mismatch = mismatches[i]
|
|
314
|
-
const mstart = fstart + mismatch.start
|
|
315
|
-
for (let j = mstart; j < mstart + mismatchLen(mismatch); j++) {
|
|
316
|
-
const epos = j - region.start
|
|
317
|
-
if (epos >= 0 && epos < bins.length) {
|
|
318
|
-
const bin = bins[epos]
|
|
319
|
-
const { base, type } = mismatch
|
|
320
|
-
const interbase = isInterbase(type)
|
|
321
|
-
if (!interbase) {
|
|
322
|
-
dec(bin, fstrand, 'ref', 'ref')
|
|
323
|
-
} else {
|
|
324
|
-
inc(bin, fstrand, 'noncov', type)
|
|
325
|
-
}
|
|
326
310
|
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
311
|
+
// normal SNP based coloring
|
|
312
|
+
else {
|
|
313
|
+
const mismatches = feature.get('mismatches') as
|
|
314
|
+
| Mismatch[]
|
|
315
|
+
| undefined
|
|
316
|
+
|
|
317
|
+
if (mismatches) {
|
|
318
|
+
for (let i = 0; i < mismatches.length; i++) {
|
|
319
|
+
const mismatch = mismatches[i]
|
|
320
|
+
const mstart = fstart + mismatch.start
|
|
321
|
+
for (
|
|
322
|
+
let j = mstart;
|
|
323
|
+
j < mstart + mismatchLen(mismatch);
|
|
324
|
+
j++
|
|
325
|
+
) {
|
|
326
|
+
const epos = j - region.start
|
|
327
|
+
if (epos >= 0 && epos < bins.length) {
|
|
328
|
+
const bin = bins[epos]
|
|
329
|
+
const { base, type } = mismatch
|
|
330
|
+
const interbase = isInterbase(type)
|
|
331
|
+
if (!interbase) {
|
|
332
|
+
dec(bin, fstrand, 'ref', 'ref')
|
|
333
|
+
} else {
|
|
334
|
+
inc(bin, fstrand, 'noncov', type)
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
if (type === 'deletion' || type === 'skip') {
|
|
338
|
+
inc(bin, fstrand, 'delskips', type)
|
|
339
|
+
bin.total--
|
|
340
|
+
} else if (!interbase) {
|
|
341
|
+
inc(bin, fstrand, 'cov', base)
|
|
342
|
+
}
|
|
332
343
|
}
|
|
333
344
|
}
|
|
334
345
|
}
|
|
335
|
-
}
|
|
336
346
|
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
score: 1,
|
|
347
|
+
mismatches
|
|
348
|
+
.filter(mismatch => mismatch.type === 'skip')
|
|
349
|
+
.forEach(mismatch => {
|
|
350
|
+
const mstart = feature.get('start') + mismatch.start
|
|
351
|
+
const start = mstart
|
|
352
|
+
const end = mstart + mismatch.length
|
|
353
|
+
const strand = feature.get('strand')
|
|
354
|
+
const hash = `${start}_${end}_${strand}`
|
|
355
|
+
if (!skipmap[hash]) {
|
|
356
|
+
skipmap[hash] = {
|
|
357
|
+
feature: feature,
|
|
358
|
+
start,
|
|
359
|
+
end,
|
|
360
|
+
strand,
|
|
361
|
+
xs: getTag(feature, 'XS') || getTag(feature, 'TS'),
|
|
362
|
+
score: 1,
|
|
363
|
+
}
|
|
364
|
+
} else {
|
|
365
|
+
skipmap[hash].score++
|
|
357
366
|
}
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
}
|
|
361
|
-
})
|
|
367
|
+
})
|
|
368
|
+
}
|
|
362
369
|
}
|
|
363
|
-
}
|
|
364
370
|
|
|
365
|
-
|
|
366
|
-
|
|
371
|
+
return bins
|
|
372
|
+
},
|
|
373
|
+
[] as {
|
|
374
|
+
total: number
|
|
375
|
+
lowqual: BinType
|
|
376
|
+
cov: BinType
|
|
377
|
+
delskips: BinType
|
|
378
|
+
noncov: BinType
|
|
379
|
+
ref: BinType
|
|
380
|
+
}[],
|
|
381
|
+
),
|
|
367
382
|
)
|
|
368
383
|
.toPromise()
|
|
369
384
|
|