@jbrowse/plugin-alignments 1.7.7 → 1.7.10
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/AlignmentsFeatureDetail/AlignmentsFeatureDetail.js +13 -3
- package/dist/AlignmentsFeatureDetail/index.d.ts +28 -3
- package/dist/AlignmentsFeatureDetail/index.js +6 -17
- package/dist/BamAdapter/BamAdapter.d.ts +1 -1
- package/dist/BamAdapter/BamAdapter.js +3 -3
- package/dist/BamAdapter/MismatchParser.d.ts +2 -5
- package/dist/BamAdapter/MismatchParser.js +108 -46
- package/dist/BamAdapter/MismatchParser.test.js +6 -14
- package/dist/CramAdapter/CramAdapter.d.ts +10 -9
- package/dist/CramAdapter/CramAdapter.js +6 -6
- package/dist/CramAdapter/CramSlightlyLazyFeature.js +35 -30
- package/dist/LinearPileupDisplay/model.d.ts +6 -3
- package/dist/LinearPileupDisplay/model.js +132 -51
- package/dist/LinearSNPCoverageDisplay/components/Tooltip.js +37 -17
- package/dist/LinearSNPCoverageDisplay/models/model.d.ts +2 -2
- package/dist/LinearSNPCoverageDisplay/models/model.js +1 -1
- package/dist/PileupRenderer/PileupLayoutSession.d.ts +3 -0
- package/dist/PileupRenderer/PileupLayoutSession.js +3 -1
- package/dist/PileupRenderer/PileupRenderer.d.ts +66 -9
- package/dist/PileupRenderer/PileupRenderer.js +296 -258
- package/dist/PileupRenderer/configSchema.js +2 -2
- package/dist/SNPCoverageAdapter/SNPCoverageAdapter.d.ts +6 -6
- package/dist/SNPCoverageAdapter/SNPCoverageAdapter.js +95 -96
- package/dist/SNPCoverageRenderer/configSchema.d.ts +1 -1
- package/package.json +3 -3
- package/src/AlignmentsFeatureDetail/AlignmentsFeatureDetail.tsx +14 -3
- package/src/AlignmentsFeatureDetail/index.ts +7 -17
- package/src/BamAdapter/BamAdapter.ts +3 -3
- package/src/BamAdapter/MismatchParser.test.ts +5 -7
- package/src/BamAdapter/MismatchParser.ts +72 -59
- package/src/CramAdapter/CramAdapter.ts +14 -10
- package/src/CramAdapter/CramSlightlyLazyFeature.ts +84 -91
- package/src/LinearPileupDisplay/model.ts +76 -24
- package/src/LinearSNPCoverageDisplay/components/Tooltip.tsx +41 -20
- package/src/LinearSNPCoverageDisplay/models/model.ts +1 -1
- package/src/PileupRenderer/PileupLayoutSession.ts +6 -1
- package/src/PileupRenderer/PileupRenderer.tsx +413 -225
- package/src/PileupRenderer/configSchema.ts +2 -2
- package/src/SNPCoverageAdapter/SNPCoverageAdapter.ts +89 -76
|
@@ -31,8 +31,8 @@ var _default = (0, _configuration.ConfigurationSchema)('PileupRenderer', {
|
|
|
31
31
|
},
|
|
32
32
|
minSubfeatureWidth: {
|
|
33
33
|
type: 'number',
|
|
34
|
-
description: 'the minimum width in px for a pileup mismatch feature. use for increasing mismatch marker widths when zoomed out
|
|
35
|
-
defaultValue: 0
|
|
34
|
+
description: 'the minimum width in px for a pileup mismatch feature. use for increasing/decreasing mismatch marker widths when zoomed out, e.g. 0 or 1',
|
|
35
|
+
defaultValue: 0.7
|
|
36
36
|
},
|
|
37
37
|
maxHeight: {
|
|
38
38
|
type: 'integer',
|
|
@@ -19,7 +19,13 @@ export default class SNPCoverageAdapter extends BaseFeatureDataAdapter {
|
|
|
19
19
|
};
|
|
20
20
|
}): Promise<{
|
|
21
21
|
bins: {
|
|
22
|
+
refbase?: string | undefined;
|
|
22
23
|
total: number;
|
|
24
|
+
all: number;
|
|
25
|
+
ref: number;
|
|
26
|
+
'-1': 0;
|
|
27
|
+
'0': 0;
|
|
28
|
+
'1': 0;
|
|
23
29
|
lowqual: {
|
|
24
30
|
total: number;
|
|
25
31
|
strands: {
|
|
@@ -44,12 +50,6 @@ export default class SNPCoverageAdapter extends BaseFeatureDataAdapter {
|
|
|
44
50
|
[key: string]: number;
|
|
45
51
|
};
|
|
46
52
|
};
|
|
47
|
-
ref: {
|
|
48
|
-
total: number;
|
|
49
|
-
strands: {
|
|
50
|
-
[key: string]: number;
|
|
51
|
-
};
|
|
52
|
-
};
|
|
53
53
|
}[];
|
|
54
54
|
skipmap: {
|
|
55
55
|
[key: string]: {
|
|
@@ -57,36 +57,17 @@ function isInterbase(type) {
|
|
|
57
57
|
function inc(bin, strand, type, field) {
|
|
58
58
|
var thisBin = bin[type][field];
|
|
59
59
|
|
|
60
|
-
if (
|
|
60
|
+
if (thisBin === undefined) {
|
|
61
61
|
thisBin = bin[type][field] = {
|
|
62
62
|
total: 0,
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
'1': 0
|
|
67
|
-
}
|
|
63
|
+
'-1': 0,
|
|
64
|
+
'0': 0,
|
|
65
|
+
'1': 0
|
|
68
66
|
};
|
|
69
67
|
}
|
|
70
68
|
|
|
71
69
|
thisBin.total++;
|
|
72
|
-
thisBin
|
|
73
|
-
} // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
function dec(bin, strand, type, field) {
|
|
77
|
-
if (!bin[type][field]) {
|
|
78
|
-
bin[type][field] = {
|
|
79
|
-
total: 0,
|
|
80
|
-
strands: {
|
|
81
|
-
'-1': 0,
|
|
82
|
-
'0': 0,
|
|
83
|
-
'1': 0
|
|
84
|
-
}
|
|
85
|
-
};
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
bin[type][field].total--;
|
|
89
|
-
bin[type][field].strands[strand]--;
|
|
70
|
+
thisBin[strand]++;
|
|
90
71
|
}
|
|
91
72
|
|
|
92
73
|
var SNPCoverageAdapter = /*#__PURE__*/function (_BaseFeatureDataAdapt) {
|
|
@@ -396,36 +377,45 @@ var SNPCoverageAdapter = /*#__PURE__*/function (_BaseFeatureDataAdapt) {
|
|
|
396
377
|
|
|
397
378
|
_loop = function _loop(i) {
|
|
398
379
|
var feature = features[i];
|
|
399
|
-
var ops = (0, _MismatchParser.parseCigar)(feature.get('CIGAR'));
|
|
400
380
|
var fstart = feature.get('start');
|
|
401
381
|
var fend = feature.get('end');
|
|
402
382
|
var fstrand = feature.get('strand');
|
|
403
383
|
|
|
404
|
-
for (var j = fstart; j < fend; j++) {
|
|
384
|
+
for (var j = fstart; j < fend + 1; j++) {
|
|
405
385
|
var _i = j - region.start;
|
|
406
386
|
|
|
407
387
|
if (_i >= 0 && _i < binMax) {
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
388
|
+
if (bins[_i] === undefined) {
|
|
389
|
+
bins[_i] = {
|
|
390
|
+
total: 0,
|
|
391
|
+
all: 0,
|
|
392
|
+
ref: 0,
|
|
393
|
+
'-1': 0,
|
|
394
|
+
'0': 0,
|
|
395
|
+
'1': 0,
|
|
396
|
+
lowqual: {},
|
|
397
|
+
cov: {},
|
|
398
|
+
delskips: {},
|
|
399
|
+
noncov: {}
|
|
400
|
+
};
|
|
401
|
+
}
|
|
416
402
|
|
|
417
403
|
if (j !== fend) {
|
|
418
|
-
|
|
419
|
-
|
|
404
|
+
bins[_i].total++;
|
|
405
|
+
bins[_i].all++;
|
|
406
|
+
bins[_i].ref++;
|
|
407
|
+
bins[_i][fstrand]++;
|
|
420
408
|
}
|
|
421
|
-
|
|
422
|
-
bins[_i] = bin;
|
|
423
409
|
}
|
|
424
410
|
}
|
|
425
411
|
|
|
426
412
|
if ((colorBy === null || colorBy === void 0 ? void 0 : colorBy.type) === 'modifications') {
|
|
427
413
|
var seq = feature.get('seq');
|
|
428
414
|
var mm = (0, _util.getTagAlt)(feature, 'MM', 'Mm') || '';
|
|
415
|
+
var ops = (0, _MismatchParser.parseCigar)(feature.get('CIGAR'));
|
|
416
|
+
|
|
417
|
+
var _fend = feature.get('end');
|
|
418
|
+
|
|
429
419
|
(0, _MismatchParser.getModificationPositions)(mm, seq, fstrand).forEach(function (_ref4) {
|
|
430
420
|
var type = _ref4.type,
|
|
431
421
|
positions = _ref4.positions;
|
|
@@ -439,9 +429,14 @@ var SNPCoverageAdapter = /*#__PURE__*/function (_BaseFeatureDataAdapt) {
|
|
|
439
429
|
var pos = _step.value;
|
|
440
430
|
var epos = pos + fstart - region.start;
|
|
441
431
|
|
|
442
|
-
if (epos >= 0 && epos < bins.length && pos + fstart <
|
|
443
|
-
var
|
|
444
|
-
|
|
432
|
+
if (epos >= 0 && epos < bins.length && pos + fstart < _fend) {
|
|
433
|
+
var bin = bins[epos];
|
|
434
|
+
|
|
435
|
+
if (bin) {
|
|
436
|
+
inc(bin, fstrand, 'cov', mod);
|
|
437
|
+
} else {
|
|
438
|
+
console.warn('Undefined position in modifications snpcoverage encountered');
|
|
439
|
+
}
|
|
445
440
|
}
|
|
446
441
|
}
|
|
447
442
|
} catch (err) {
|
|
@@ -462,13 +457,16 @@ var SNPCoverageAdapter = /*#__PURE__*/function (_BaseFeatureDataAdapt) {
|
|
|
462
457
|
var _mm = (0, _util.getTagAlt)(feature, 'MM', 'Mm') || '';
|
|
463
458
|
|
|
464
459
|
var methBins = new Array(region.end - region.start).fill(0);
|
|
460
|
+
|
|
461
|
+
var _ops = (0, _MismatchParser.parseCigar)(feature.get('CIGAR'));
|
|
462
|
+
|
|
465
463
|
(0, _MismatchParser.getModificationPositions)(_mm, _seq, fstrand).forEach(function (_ref5) {
|
|
466
464
|
var type = _ref5.type,
|
|
467
465
|
positions = _ref5.positions;
|
|
468
466
|
|
|
469
467
|
// we are processing methylation
|
|
470
468
|
if (type === 'm') {
|
|
471
|
-
var _iterator2 = _createForOfIteratorHelper((0, _MismatchParser.getNextRefPos)(
|
|
469
|
+
var _iterator2 = _createForOfIteratorHelper((0, _MismatchParser.getNextRefPos)(_ops, positions)),
|
|
472
470
|
_step2;
|
|
473
471
|
|
|
474
472
|
try {
|
|
@@ -496,80 +494,81 @@ var SNPCoverageAdapter = /*#__PURE__*/function (_BaseFeatureDataAdapt) {
|
|
|
496
494
|
|
|
497
495
|
var l2 = regionSeq[_i2 + 1].toLowerCase();
|
|
498
496
|
|
|
499
|
-
var
|
|
497
|
+
var bin = bins[_i2];
|
|
500
498
|
var bin1 = bins[_i2 + 1]; // color
|
|
501
499
|
|
|
502
500
|
if (l1 === 'c' && l2 === 'g') {
|
|
503
501
|
if (methBins[_i2] || methBins[_i2 + 1]) {
|
|
504
|
-
inc(
|
|
502
|
+
inc(bin, fstrand, 'cov', 'meth');
|
|
505
503
|
inc(bin1, fstrand, 'cov', 'meth');
|
|
506
|
-
|
|
507
|
-
|
|
504
|
+
bins[_i2].ref--;
|
|
505
|
+
bins[_i2][fstrand]--;
|
|
506
|
+
bins[_i2 + 1].ref--;
|
|
507
|
+
bins[_i2 + 1][fstrand]--;
|
|
508
508
|
} else {
|
|
509
|
-
inc(
|
|
509
|
+
inc(bin, fstrand, 'cov', 'unmeth');
|
|
510
510
|
inc(bin1, fstrand, 'cov', 'unmeth');
|
|
511
|
-
|
|
512
|
-
|
|
511
|
+
bins[_i2].ref--;
|
|
512
|
+
bins[_i2][fstrand]--;
|
|
513
|
+
bins[_i2 + 1].ref--;
|
|
514
|
+
bins[_i2 + 1][fstrand]--;
|
|
513
515
|
}
|
|
514
516
|
}
|
|
515
517
|
}
|
|
516
518
|
}
|
|
517
519
|
} // normal SNP based coloring
|
|
518
|
-
else {
|
|
519
|
-
var mismatches = feature.get('mismatches');
|
|
520
520
|
|
|
521
|
-
if (mismatches) {
|
|
522
|
-
for (var _i3 = 0; _i3 < mismatches.length; _i3++) {
|
|
523
|
-
var mismatch = mismatches[_i3];
|
|
524
|
-
var mstart = fstart + mismatch.start;
|
|
525
521
|
|
|
526
|
-
|
|
527
|
-
|
|
522
|
+
var mismatches = feature.get('mismatches') || [];
|
|
523
|
+
var colorSNPs = (colorBy === null || colorBy === void 0 ? void 0 : colorBy.type) !== 'modifications' && (colorBy === null || colorBy === void 0 ? void 0 : colorBy.type) !== 'methylation';
|
|
528
524
|
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
525
|
+
for (var _i3 = 0; _i3 < mismatches.length; _i3++) {
|
|
526
|
+
var mismatch = mismatches[_i3];
|
|
527
|
+
var mstart = fstart + mismatch.start;
|
|
528
|
+
var mlen = mismatchLen(mismatch);
|
|
529
|
+
var mend = mstart + mlen;
|
|
534
530
|
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
} else {
|
|
538
|
-
inc(_bin3, fstrand, 'noncov', type);
|
|
539
|
-
}
|
|
531
|
+
for (var _j2 = mstart; _j2 < mstart + mlen; _j2++) {
|
|
532
|
+
var epos = _j2 - region.start;
|
|
540
533
|
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
}
|
|
547
|
-
}
|
|
548
|
-
}
|
|
549
|
-
}
|
|
534
|
+
if (epos >= 0 && epos < bins.length) {
|
|
535
|
+
var _bin = bins[epos];
|
|
536
|
+
var base = mismatch.base,
|
|
537
|
+
type = mismatch.type;
|
|
538
|
+
var interbase = isInterbase(type);
|
|
550
539
|
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
var mstart = feature.get('start') + mismatch.start;
|
|
555
|
-
var start = mstart;
|
|
556
|
-
var end = mstart + mismatch.length;
|
|
557
|
-
var strand = feature.get('strand');
|
|
558
|
-
var hash = "".concat(start, "_").concat(end, "_").concat(strand);
|
|
559
|
-
|
|
560
|
-
if (!skipmap[hash]) {
|
|
561
|
-
skipmap[hash] = {
|
|
562
|
-
feature: feature,
|
|
563
|
-
start: start,
|
|
564
|
-
end: end,
|
|
565
|
-
strand: strand,
|
|
566
|
-
xs: (0, _util.getTag)(feature, 'XS') || (0, _util.getTag)(feature, 'TS'),
|
|
567
|
-
score: 1
|
|
568
|
-
};
|
|
540
|
+
if (!interbase) {
|
|
541
|
+
_bin.ref--;
|
|
542
|
+
_bin[fstrand]--;
|
|
569
543
|
} else {
|
|
570
|
-
|
|
544
|
+
inc(_bin, fstrand, 'noncov', type);
|
|
571
545
|
}
|
|
572
|
-
|
|
546
|
+
|
|
547
|
+
if (type === 'deletion' || type === 'skip') {
|
|
548
|
+
inc(_bin, fstrand, 'delskips', type);
|
|
549
|
+
_bin.total--;
|
|
550
|
+
} else if (!interbase && colorSNPs) {
|
|
551
|
+
inc(_bin, fstrand, 'cov', base);
|
|
552
|
+
_bin.refbase = mismatch.altbase;
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
if (mismatch.type === 'skip') {
|
|
558
|
+
var hash = "".concat(mstart, "_").concat(mend, "_").concat(fstrand);
|
|
559
|
+
|
|
560
|
+
if (skipmap[hash] === undefined) {
|
|
561
|
+
skipmap[hash] = {
|
|
562
|
+
feature: feature,
|
|
563
|
+
start: mstart,
|
|
564
|
+
end: mend,
|
|
565
|
+
strand: fstrand,
|
|
566
|
+
xs: (0, _util.getTag)(feature, 'XS') || (0, _util.getTag)(feature, 'TS'),
|
|
567
|
+
score: 0
|
|
568
|
+
};
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
skipmap[hash].score++;
|
|
573
572
|
}
|
|
574
573
|
}
|
|
575
574
|
};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
declare
|
|
1
|
+
declare const _default: import("@jbrowse/core/configuration").AnyConfigurationSchemaType;
|
|
2
2
|
export default _default;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jbrowse/plugin-alignments",
|
|
3
|
-
"version": "1.7.
|
|
3
|
+
"version": "1.7.10",
|
|
4
4
|
"description": "JBrowse 2 alignments adapters, tracks, etc.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"jbrowse",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"dependencies": {
|
|
36
36
|
"@babel/runtime": "^7.17.9",
|
|
37
37
|
"@gmod/bam": "^1.1.15",
|
|
38
|
-
"@gmod/cram": "^1.6.
|
|
38
|
+
"@gmod/cram": "^1.6.4",
|
|
39
39
|
"@material-ui/icons": "^4.9.1",
|
|
40
40
|
"color": "^3.1.2",
|
|
41
41
|
"copy-to-clipboard": "^3.3.1",
|
|
@@ -57,5 +57,5 @@
|
|
|
57
57
|
"publishConfig": {
|
|
58
58
|
"access": "public"
|
|
59
59
|
},
|
|
60
|
-
"gitHead": "
|
|
60
|
+
"gitHead": "02d8c1e88e5603ea5855faed4ccb814e28071b32"
|
|
61
61
|
}
|
|
@@ -77,12 +77,22 @@ function AlignmentFlags(props: { feature: any }) {
|
|
|
77
77
|
|
|
78
78
|
function Formatter({ value }: { value: unknown }) {
|
|
79
79
|
const [show, setShow] = useState(false)
|
|
80
|
+
const [copied, setCopied] = useState(false)
|
|
80
81
|
const display = String(value)
|
|
81
82
|
if (display.length > 100) {
|
|
82
83
|
return (
|
|
83
84
|
<>
|
|
84
|
-
<button
|
|
85
|
-
|
|
85
|
+
<button
|
|
86
|
+
type="button"
|
|
87
|
+
onClick={() => {
|
|
88
|
+
copy(display)
|
|
89
|
+
setCopied(true)
|
|
90
|
+
setTimeout(() => {
|
|
91
|
+
setCopied(false)
|
|
92
|
+
}, 700)
|
|
93
|
+
}}
|
|
94
|
+
>
|
|
95
|
+
{copied ? 'Copied to clipboard' : 'Copy'}
|
|
86
96
|
</button>
|
|
87
97
|
<button type="button" onClick={() => setShow(val => !val)}>
|
|
88
98
|
{show ? 'Show less' : 'Show more'}
|
|
@@ -134,7 +144,8 @@ function SupplementaryAlignments(props: { tag: string; model: any }) {
|
|
|
134
144
|
return (
|
|
135
145
|
<li key={`${locString}-${index}`}>
|
|
136
146
|
<Link
|
|
137
|
-
onClick={
|
|
147
|
+
onClick={event => {
|
|
148
|
+
event.preventDefault()
|
|
138
149
|
const { view } = model
|
|
139
150
|
try {
|
|
140
151
|
if (view) {
|
|
@@ -1,30 +1,20 @@
|
|
|
1
1
|
import { lazy } from 'react'
|
|
2
2
|
import PluginManager from '@jbrowse/core/PluginManager'
|
|
3
3
|
import { ConfigurationSchema } from '@jbrowse/core/configuration'
|
|
4
|
-
import { ElementId } from '@jbrowse/core/util/types/mst'
|
|
5
4
|
import { types } from 'mobx-state-tree'
|
|
6
5
|
import WidgetType from '@jbrowse/core/pluggableElementTypes/WidgetType'
|
|
6
|
+
import { stateModelFactory as baseModelFactory } from '@jbrowse/core/BaseFeatureWidget'
|
|
7
7
|
|
|
8
8
|
const configSchema = ConfigurationSchema('AlignmentsFeatureWidget', {})
|
|
9
9
|
|
|
10
10
|
export function stateModelFactory(pluginManager: PluginManager) {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
const baseModel = baseModelFactory(pluginManager)
|
|
12
|
+
return types.compose(
|
|
13
|
+
baseModel,
|
|
14
|
+
types.model('AlignmentsFeatureWidget', {
|
|
14
15
|
type: types.literal('AlignmentsFeatureWidget'),
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
pluginManager.pluggableMstType('view', 'stateModel'),
|
|
18
|
-
),
|
|
19
|
-
})
|
|
20
|
-
.actions(self => ({
|
|
21
|
-
setFeatureData(data: unknown) {
|
|
22
|
-
self.featureData = data
|
|
23
|
-
},
|
|
24
|
-
clearFeatureData() {
|
|
25
|
-
self.featureData = undefined
|
|
26
|
-
},
|
|
27
|
-
}))
|
|
16
|
+
}),
|
|
17
|
+
)
|
|
28
18
|
}
|
|
29
19
|
|
|
30
20
|
export default function register(pluginManager: PluginManager) {
|
|
@@ -171,7 +171,7 @@ export default class BamAdapter extends BaseFeatureDataAdapter {
|
|
|
171
171
|
flagInclude: number
|
|
172
172
|
flagExclude: number
|
|
173
173
|
tagFilter: { tag: string; value: unknown }
|
|
174
|
-
|
|
174
|
+
readName: string
|
|
175
175
|
}
|
|
176
176
|
},
|
|
177
177
|
) {
|
|
@@ -187,7 +187,7 @@ export default class BamAdapter extends BaseFeatureDataAdapter {
|
|
|
187
187
|
flagInclude = 0,
|
|
188
188
|
flagExclude = 0,
|
|
189
189
|
tagFilter,
|
|
190
|
-
|
|
190
|
+
readName,
|
|
191
191
|
} = filterBy || {}
|
|
192
192
|
|
|
193
193
|
for (const record of records) {
|
|
@@ -214,7 +214,7 @@ export default class BamAdapter extends BaseFeatureDataAdapter {
|
|
|
214
214
|
}
|
|
215
215
|
}
|
|
216
216
|
|
|
217
|
-
if (
|
|
217
|
+
if (readName && record.get('name') !== readName) {
|
|
218
218
|
continue
|
|
219
219
|
}
|
|
220
220
|
|
|
@@ -233,17 +233,15 @@ test('clipping', () => {
|
|
|
233
233
|
])
|
|
234
234
|
})
|
|
235
235
|
|
|
236
|
-
test('getNextRefPos
|
|
236
|
+
test('getNextRefPos test 1', () => {
|
|
237
237
|
const cigar = parseCigar('10S10M1I4M1D15M')
|
|
238
238
|
const iter = getNextRefPos(cigar, [5, 10, 15, 20, 25, 30, 35])
|
|
239
|
-
|
|
240
|
-
expect(vals).toEqual([-5, 0, 5, 10, 14, 20, 25])
|
|
239
|
+
expect([...iter]).toEqual([0, 5, 15, 20, 25])
|
|
241
240
|
})
|
|
242
|
-
test('getNextRefPos
|
|
243
|
-
const cigar = parseCigar('
|
|
241
|
+
test('getNextRefPos test 2', () => {
|
|
242
|
+
const cigar = parseCigar('10S15M')
|
|
244
243
|
const iter = getNextRefPos(cigar, [5, 10, 15])
|
|
245
|
-
|
|
246
|
-
expect(vals).toEqual([-5, 0, 5])
|
|
244
|
+
expect([...iter]).toEqual([0, 5])
|
|
247
245
|
})
|
|
248
246
|
|
|
249
247
|
test('getModificationPositions', () => {
|
|
@@ -10,6 +10,7 @@ export interface Mismatch {
|
|
|
10
10
|
cliplen?: number
|
|
11
11
|
}
|
|
12
12
|
const mdRegex = new RegExp(/(\d+|\^[a-z]+|[a-z])/gi)
|
|
13
|
+
const modificationRegex = new RegExp(/([A-Z])([-+])([^,.?]+)([.?])?/)
|
|
13
14
|
export function parseCigar(cigar: string) {
|
|
14
15
|
return (cigar || '').split(/([MIDNSHPX=])/)
|
|
15
16
|
}
|
|
@@ -38,6 +39,7 @@ export function cigarToMismatches(
|
|
|
38
39
|
start: roffset + j,
|
|
39
40
|
type: 'mismatch',
|
|
40
41
|
base: seq[soffset + j],
|
|
42
|
+
altbase: ref[roffset + j],
|
|
41
43
|
length: 1,
|
|
42
44
|
})
|
|
43
45
|
}
|
|
@@ -232,26 +234,32 @@ export function getMismatches(
|
|
|
232
234
|
// get relative reference sequence positions for positions given relative to
|
|
233
235
|
// the read sequence
|
|
234
236
|
export function* getNextRefPos(cigarOps: string[], positions: number[]) {
|
|
235
|
-
let cigarIdx = 0
|
|
236
237
|
let readPos = 0
|
|
237
238
|
let refPos = 0
|
|
239
|
+
let currPos = 0
|
|
238
240
|
|
|
239
|
-
for (let i = 0; i < positions.length; i
|
|
240
|
-
const
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
241
|
+
for (let i = 0; i < cigarOps.length && currPos < positions.length; i += 2) {
|
|
242
|
+
const len = +cigarOps[i]
|
|
243
|
+
const op = cigarOps[i + 1]
|
|
244
|
+
if (op === 'S' || op === 'I') {
|
|
245
|
+
for (let i = 0; i < len && currPos < positions.length; i++) {
|
|
246
|
+
if (positions[currPos] === readPos + i) {
|
|
247
|
+
currPos++
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
readPos += len
|
|
251
|
+
} else if (op === 'D' || op === 'N') {
|
|
252
|
+
refPos += len
|
|
253
|
+
} else if (op === 'M' || op === 'X' || op === '=') {
|
|
254
|
+
for (let i = 0; i < len && currPos < positions.length; i++) {
|
|
255
|
+
if (positions[currPos] === readPos + i) {
|
|
256
|
+
yield refPos + i
|
|
257
|
+
currPos++
|
|
258
|
+
}
|
|
251
259
|
}
|
|
260
|
+
readPos += len
|
|
261
|
+
refPos += len
|
|
252
262
|
}
|
|
253
|
-
|
|
254
|
-
yield positions[i] - readPos + refPos
|
|
255
263
|
}
|
|
256
264
|
}
|
|
257
265
|
export function getModificationPositions(
|
|
@@ -260,54 +268,59 @@ export function getModificationPositions(
|
|
|
260
268
|
fstrand: number,
|
|
261
269
|
) {
|
|
262
270
|
const seq = fstrand === -1 ? revcom(fseq) : fseq
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
271
|
+
const mods = mm.split(';').filter(mod => !!mod)
|
|
272
|
+
const result = []
|
|
273
|
+
for (let i = 0; i < mods.length; i++) {
|
|
274
|
+
const mod = mods[i]
|
|
275
|
+
const [basemod, ...skips] = mod.split(',')
|
|
268
276
|
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
277
|
+
// regexes based on parse_mm.pl from hts-specs
|
|
278
|
+
const matches = basemod.match(modificationRegex)
|
|
279
|
+
if (!matches) {
|
|
280
|
+
throw new Error('bad format for MM tag')
|
|
281
|
+
}
|
|
282
|
+
const [, base, strand, typestr] = matches
|
|
275
283
|
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
284
|
+
// can be a multi e.g. C+mh for both meth (m) and hydroxymeth (h) so
|
|
285
|
+
// split, and they can also be chemical codes (ChEBI) e.g. C+16061
|
|
286
|
+
const types = typestr.split(/(\d+|.)/).filter(f => !!f)
|
|
279
287
|
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
288
|
+
if (strand === '-') {
|
|
289
|
+
console.warn('unsupported negative strand modifications')
|
|
290
|
+
// make sure to return a somewhat matching type even in this case
|
|
291
|
+
result.push({ type: 'unsupported', positions: [] as number[] })
|
|
292
|
+
}
|
|
285
293
|
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
294
|
+
// this logic also based on parse_mm.pl from hts-specs is that in the
|
|
295
|
+
// sequence of the read, if we have a modification type e.g. C+m;2 and a
|
|
296
|
+
// sequence ACGTACGTAC we skip the two instances of C and go to the last
|
|
297
|
+
// C
|
|
298
|
+
for (let j = 0; j < types.length; j++) {
|
|
299
|
+
const type = types[j]
|
|
300
|
+
let i = 0
|
|
301
|
+
const positions = []
|
|
302
|
+
for (let k = 0; k < skips.length; k++) {
|
|
303
|
+
let delta = +skips[k]
|
|
304
|
+
do {
|
|
305
|
+
if (base === 'N' || base === seq[i]) {
|
|
306
|
+
delta--
|
|
307
|
+
}
|
|
308
|
+
i++
|
|
309
|
+
} while (delta >= 0 && i < seq.length)
|
|
310
|
+
|
|
311
|
+
const temp = i - 1
|
|
312
|
+
positions.push(fstrand === -1 ? seq.length - 1 - temp : temp)
|
|
313
|
+
}
|
|
314
|
+
if (fstrand === -1) {
|
|
315
|
+
positions.sort((a, b) => a - b)
|
|
316
|
+
}
|
|
317
|
+
result.push({
|
|
318
|
+
type,
|
|
319
|
+
positions,
|
|
308
320
|
})
|
|
309
|
-
}
|
|
310
|
-
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
return result
|
|
311
324
|
}
|
|
312
325
|
|
|
313
326
|
export function getModificationTypes(mm: string) {
|
|
@@ -317,7 +330,7 @@ export function getModificationTypes(mm: string) {
|
|
|
317
330
|
.map(mod => {
|
|
318
331
|
const [basemod] = mod.split(',')
|
|
319
332
|
|
|
320
|
-
const matches = basemod.match(
|
|
333
|
+
const matches = basemod.match(modificationRegex)
|
|
321
334
|
if (!matches) {
|
|
322
335
|
throw new Error('bad format for MM tag')
|
|
323
336
|
}
|