@jbrowse/plugin-alignments 1.7.6 → 1.7.9
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 +2 -1
- 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 +107 -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 +2 -1
- package/dist/LinearPileupDisplay/model.js +1 -1
- package/dist/LinearSNPCoverageDisplay/components/Tooltip.js +2 -3
- 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 +15 -2
- package/dist/PileupRenderer/PileupRenderer.js +127 -164
- package/dist/PileupRenderer/configSchema.js +2 -2
- package/dist/SNPCoverageAdapter/SNPCoverageAdapter.d.ts +4 -6
- package/dist/SNPCoverageAdapter/SNPCoverageAdapter.js +92 -96
- package/dist/SNPCoverageRenderer/configSchema.d.ts +1 -1
- package/dist/util.d.ts +1 -1
- package/package.json +3 -3
- package/src/AlignmentsFeatureDetail/AlignmentsFeatureDetail.tsx +2 -1
- package/src/BamAdapter/BamAdapter.ts +3 -3
- package/src/BamAdapter/MismatchParser.test.ts +5 -7
- package/src/BamAdapter/MismatchParser.ts +71 -59
- package/src/CramAdapter/CramAdapter.ts +14 -10
- package/src/CramAdapter/CramSlightlyLazyFeature.ts +84 -91
- package/src/LinearPileupDisplay/model.ts +1 -1
- package/src/LinearSNPCoverageDisplay/components/Tooltip.tsx +32 -25
- package/src/LinearSNPCoverageDisplay/models/model.ts +1 -1
- package/src/PileupRenderer/PileupLayoutSession.ts +6 -1
- package/src/PileupRenderer/PileupRenderer.tsx +72 -71
- package/src/PileupRenderer/configSchema.ts +2 -2
- package/src/SNPCoverageAdapter/SNPCoverageAdapter.ts +84 -76
|
@@ -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,43 @@ 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
|
+
ref: 0,
|
|
392
|
+
'-1': 0,
|
|
393
|
+
'0': 0,
|
|
394
|
+
'1': 0,
|
|
395
|
+
lowqual: {},
|
|
396
|
+
cov: {},
|
|
397
|
+
delskips: {},
|
|
398
|
+
noncov: {}
|
|
399
|
+
};
|
|
400
|
+
}
|
|
416
401
|
|
|
417
402
|
if (j !== fend) {
|
|
418
|
-
|
|
419
|
-
|
|
403
|
+
bins[_i].total++;
|
|
404
|
+
bins[_i].ref++;
|
|
405
|
+
bins[_i][fstrand]++;
|
|
420
406
|
}
|
|
421
|
-
|
|
422
|
-
bins[_i] = bin;
|
|
423
407
|
}
|
|
424
408
|
}
|
|
425
409
|
|
|
426
410
|
if ((colorBy === null || colorBy === void 0 ? void 0 : colorBy.type) === 'modifications') {
|
|
427
411
|
var seq = feature.get('seq');
|
|
428
412
|
var mm = (0, _util.getTagAlt)(feature, 'MM', 'Mm') || '';
|
|
413
|
+
var ops = (0, _MismatchParser.parseCigar)(feature.get('CIGAR'));
|
|
414
|
+
|
|
415
|
+
var _fend = feature.get('end');
|
|
416
|
+
|
|
429
417
|
(0, _MismatchParser.getModificationPositions)(mm, seq, fstrand).forEach(function (_ref4) {
|
|
430
418
|
var type = _ref4.type,
|
|
431
419
|
positions = _ref4.positions;
|
|
@@ -439,9 +427,14 @@ var SNPCoverageAdapter = /*#__PURE__*/function (_BaseFeatureDataAdapt) {
|
|
|
439
427
|
var pos = _step.value;
|
|
440
428
|
var epos = pos + fstart - region.start;
|
|
441
429
|
|
|
442
|
-
if (epos >= 0 && epos < bins.length && pos + fstart <
|
|
443
|
-
var
|
|
444
|
-
|
|
430
|
+
if (epos >= 0 && epos < bins.length && pos + fstart < _fend) {
|
|
431
|
+
var bin = bins[epos];
|
|
432
|
+
|
|
433
|
+
if (bin) {
|
|
434
|
+
inc(bin, fstrand, 'cov', mod);
|
|
435
|
+
} else {
|
|
436
|
+
console.warn('Undefined position in modifications snpcoverage encountered');
|
|
437
|
+
}
|
|
445
438
|
}
|
|
446
439
|
}
|
|
447
440
|
} catch (err) {
|
|
@@ -462,13 +455,16 @@ var SNPCoverageAdapter = /*#__PURE__*/function (_BaseFeatureDataAdapt) {
|
|
|
462
455
|
var _mm = (0, _util.getTagAlt)(feature, 'MM', 'Mm') || '';
|
|
463
456
|
|
|
464
457
|
var methBins = new Array(region.end - region.start).fill(0);
|
|
458
|
+
|
|
459
|
+
var _ops = (0, _MismatchParser.parseCigar)(feature.get('CIGAR'));
|
|
460
|
+
|
|
465
461
|
(0, _MismatchParser.getModificationPositions)(_mm, _seq, fstrand).forEach(function (_ref5) {
|
|
466
462
|
var type = _ref5.type,
|
|
467
463
|
positions = _ref5.positions;
|
|
468
464
|
|
|
469
465
|
// we are processing methylation
|
|
470
466
|
if (type === 'm') {
|
|
471
|
-
var _iterator2 = _createForOfIteratorHelper((0, _MismatchParser.getNextRefPos)(
|
|
467
|
+
var _iterator2 = _createForOfIteratorHelper((0, _MismatchParser.getNextRefPos)(_ops, positions)),
|
|
472
468
|
_step2;
|
|
473
469
|
|
|
474
470
|
try {
|
|
@@ -496,80 +492,80 @@ var SNPCoverageAdapter = /*#__PURE__*/function (_BaseFeatureDataAdapt) {
|
|
|
496
492
|
|
|
497
493
|
var l2 = regionSeq[_i2 + 1].toLowerCase();
|
|
498
494
|
|
|
499
|
-
var
|
|
495
|
+
var bin = bins[_i2];
|
|
500
496
|
var bin1 = bins[_i2 + 1]; // color
|
|
501
497
|
|
|
502
498
|
if (l1 === 'c' && l2 === 'g') {
|
|
503
499
|
if (methBins[_i2] || methBins[_i2 + 1]) {
|
|
504
|
-
inc(
|
|
500
|
+
inc(bin, fstrand, 'cov', 'meth');
|
|
505
501
|
inc(bin1, fstrand, 'cov', 'meth');
|
|
506
|
-
|
|
507
|
-
|
|
502
|
+
bins[_i2].ref--;
|
|
503
|
+
bins[_i2][fstrand]--;
|
|
504
|
+
bins[_i2 + 1].ref--;
|
|
505
|
+
bins[_i2 + 1][fstrand]--;
|
|
508
506
|
} else {
|
|
509
|
-
inc(
|
|
507
|
+
inc(bin, fstrand, 'cov', 'unmeth');
|
|
510
508
|
inc(bin1, fstrand, 'cov', 'unmeth');
|
|
511
|
-
|
|
512
|
-
|
|
509
|
+
bins[_i2].ref--;
|
|
510
|
+
bins[_i2][fstrand]--;
|
|
511
|
+
bins[_i2 + 1].ref--;
|
|
512
|
+
bins[_i2 + 1][fstrand]--;
|
|
513
513
|
}
|
|
514
514
|
}
|
|
515
515
|
}
|
|
516
516
|
}
|
|
517
517
|
} // normal SNP based coloring
|
|
518
|
-
else {
|
|
519
|
-
var mismatches = feature.get('mismatches');
|
|
520
518
|
|
|
521
|
-
if (mismatches) {
|
|
522
|
-
for (var _i3 = 0; _i3 < mismatches.length; _i3++) {
|
|
523
|
-
var mismatch = mismatches[_i3];
|
|
524
|
-
var mstart = fstart + mismatch.start;
|
|
525
519
|
|
|
526
|
-
|
|
527
|
-
|
|
520
|
+
var mismatches = feature.get('mismatches') || [];
|
|
521
|
+
var colorSNPs = (colorBy === null || colorBy === void 0 ? void 0 : colorBy.type) !== 'modifications' && (colorBy === null || colorBy === void 0 ? void 0 : colorBy.type) !== 'methylation';
|
|
528
522
|
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
523
|
+
for (var _i3 = 0; _i3 < mismatches.length; _i3++) {
|
|
524
|
+
var mismatch = mismatches[_i3];
|
|
525
|
+
var mstart = fstart + mismatch.start;
|
|
526
|
+
var mlen = mismatchLen(mismatch);
|
|
527
|
+
var mend = mstart + mlen;
|
|
534
528
|
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
} else {
|
|
538
|
-
inc(_bin3, fstrand, 'noncov', type);
|
|
539
|
-
}
|
|
529
|
+
for (var _j2 = mstart; _j2 < mstart + mlen; _j2++) {
|
|
530
|
+
var epos = _j2 - region.start;
|
|
540
531
|
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
}
|
|
547
|
-
}
|
|
548
|
-
}
|
|
549
|
-
}
|
|
532
|
+
if (epos >= 0 && epos < bins.length) {
|
|
533
|
+
var _bin = bins[epos];
|
|
534
|
+
var base = mismatch.base,
|
|
535
|
+
type = mismatch.type;
|
|
536
|
+
var interbase = isInterbase(type);
|
|
550
537
|
|
|
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
|
-
};
|
|
538
|
+
if (!interbase) {
|
|
539
|
+
_bin.ref--;
|
|
540
|
+
_bin[fstrand]--;
|
|
569
541
|
} else {
|
|
570
|
-
|
|
542
|
+
inc(_bin, fstrand, 'noncov', type);
|
|
571
543
|
}
|
|
572
|
-
|
|
544
|
+
|
|
545
|
+
if (type === 'deletion' || type === 'skip') {
|
|
546
|
+
inc(_bin, fstrand, 'delskips', type);
|
|
547
|
+
_bin.total--;
|
|
548
|
+
} else if (!interbase && colorSNPs) {
|
|
549
|
+
inc(_bin, fstrand, 'cov', base);
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
if (mismatch.type === 'skip') {
|
|
555
|
+
var hash = "".concat(mstart, "_").concat(mend, "_").concat(fstrand);
|
|
556
|
+
|
|
557
|
+
if (skipmap[hash] === undefined) {
|
|
558
|
+
skipmap[hash] = {
|
|
559
|
+
feature: feature,
|
|
560
|
+
start: mstart,
|
|
561
|
+
end: mend,
|
|
562
|
+
strand: fstrand,
|
|
563
|
+
xs: (0, _util.getTag)(feature, 'XS') || (0, _util.getTag)(feature, 'TS'),
|
|
564
|
+
score: 0
|
|
565
|
+
};
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
skipmap[hash].score++;
|
|
573
569
|
}
|
|
574
570
|
}
|
|
575
571
|
};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
declare
|
|
1
|
+
declare const _default: import("@jbrowse/core/configuration").AnyConfigurationSchemaType;
|
|
2
2
|
export default _default;
|
package/dist/util.d.ts
CHANGED
|
@@ -14,6 +14,6 @@ export declare const orientationTypes: {
|
|
|
14
14
|
[key: string]: string;
|
|
15
15
|
};
|
|
16
16
|
};
|
|
17
|
-
export declare function getColorWGBS(strand: number, base: string): "#
|
|
17
|
+
export declare function getColorWGBS(strand: number, base: string): "#f00" | "#00f" | "#888";
|
|
18
18
|
export declare function fetchSequence(region: AugmentedRegion, adapter: BaseFeatureDataAdapter): Promise<any>;
|
|
19
19
|
export declare function shouldFetchReferenceSequence(type?: string): boolean;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jbrowse/plugin-alignments",
|
|
3
|
-
"version": "1.7.
|
|
3
|
+
"version": "1.7.9",
|
|
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": "a6504c385d703ce6e755d05652ef659ffe28c864"
|
|
61
61
|
}
|
|
@@ -134,7 +134,8 @@ function SupplementaryAlignments(props: { tag: string; model: any }) {
|
|
|
134
134
|
return (
|
|
135
135
|
<li key={`${locString}-${index}`}>
|
|
136
136
|
<Link
|
|
137
|
-
onClick={
|
|
137
|
+
onClick={event => {
|
|
138
|
+
event.preventDefault()
|
|
138
139
|
const { view } = model
|
|
139
140
|
try {
|
|
140
141
|
if (view) {
|
|
@@ -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
|
}
|
|
@@ -232,26 +233,32 @@ export function getMismatches(
|
|
|
232
233
|
// get relative reference sequence positions for positions given relative to
|
|
233
234
|
// the read sequence
|
|
234
235
|
export function* getNextRefPos(cigarOps: string[], positions: number[]) {
|
|
235
|
-
let cigarIdx = 0
|
|
236
236
|
let readPos = 0
|
|
237
237
|
let refPos = 0
|
|
238
|
+
let currPos = 0
|
|
238
239
|
|
|
239
|
-
for (let i = 0; i < positions.length; i
|
|
240
|
-
const
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
240
|
+
for (let i = 0; i < cigarOps.length && currPos < positions.length; i += 2) {
|
|
241
|
+
const len = +cigarOps[i]
|
|
242
|
+
const op = cigarOps[i + 1]
|
|
243
|
+
if (op === 'S' || op === 'I') {
|
|
244
|
+
for (let i = 0; i < len && currPos < positions.length; i++) {
|
|
245
|
+
if (positions[currPos] === readPos + i) {
|
|
246
|
+
currPos++
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
readPos += len
|
|
250
|
+
} else if (op === 'D' || op === 'N') {
|
|
251
|
+
refPos += len
|
|
252
|
+
} else if (op === 'M' || op === 'X' || op === '=') {
|
|
253
|
+
for (let i = 0; i < len && currPos < positions.length; i++) {
|
|
254
|
+
if (positions[currPos] === readPos + i) {
|
|
255
|
+
yield refPos + i
|
|
256
|
+
currPos++
|
|
257
|
+
}
|
|
251
258
|
}
|
|
259
|
+
readPos += len
|
|
260
|
+
refPos += len
|
|
252
261
|
}
|
|
253
|
-
|
|
254
|
-
yield positions[i] - readPos + refPos
|
|
255
262
|
}
|
|
256
263
|
}
|
|
257
264
|
export function getModificationPositions(
|
|
@@ -260,54 +267,59 @@ export function getModificationPositions(
|
|
|
260
267
|
fstrand: number,
|
|
261
268
|
) {
|
|
262
269
|
const seq = fstrand === -1 ? revcom(fseq) : fseq
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
270
|
+
const mods = mm.split(';').filter(mod => !!mod)
|
|
271
|
+
const result = []
|
|
272
|
+
for (let i = 0; i < mods.length; i++) {
|
|
273
|
+
const mod = mods[i]
|
|
274
|
+
const [basemod, ...skips] = mod.split(',')
|
|
268
275
|
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
276
|
+
// regexes based on parse_mm.pl from hts-specs
|
|
277
|
+
const matches = basemod.match(modificationRegex)
|
|
278
|
+
if (!matches) {
|
|
279
|
+
throw new Error('bad format for MM tag')
|
|
280
|
+
}
|
|
281
|
+
const [, base, strand, typestr] = matches
|
|
275
282
|
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
283
|
+
// can be a multi e.g. C+mh for both meth (m) and hydroxymeth (h) so
|
|
284
|
+
// split, and they can also be chemical codes (ChEBI) e.g. C+16061
|
|
285
|
+
const types = typestr.split(/(\d+|.)/).filter(f => !!f)
|
|
279
286
|
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
287
|
+
if (strand === '-') {
|
|
288
|
+
console.warn('unsupported negative strand modifications')
|
|
289
|
+
// make sure to return a somewhat matching type even in this case
|
|
290
|
+
result.push({ type: 'unsupported', positions: [] as number[] })
|
|
291
|
+
}
|
|
285
292
|
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
293
|
+
// this logic also based on parse_mm.pl from hts-specs is that in the
|
|
294
|
+
// sequence of the read, if we have a modification type e.g. C+m;2 and a
|
|
295
|
+
// sequence ACGTACGTAC we skip the two instances of C and go to the last
|
|
296
|
+
// C
|
|
297
|
+
for (let j = 0; j < types.length; j++) {
|
|
298
|
+
const type = types[j]
|
|
299
|
+
let i = 0
|
|
300
|
+
const positions = []
|
|
301
|
+
for (let k = 0; k < skips.length; k++) {
|
|
302
|
+
let delta = +skips[k]
|
|
303
|
+
do {
|
|
304
|
+
if (base === 'N' || base === seq[i]) {
|
|
305
|
+
delta--
|
|
306
|
+
}
|
|
307
|
+
i++
|
|
308
|
+
} while (delta >= 0 && i < seq.length)
|
|
309
|
+
|
|
310
|
+
const temp = i - 1
|
|
311
|
+
positions.push(fstrand === -1 ? seq.length - 1 - temp : temp)
|
|
312
|
+
}
|
|
313
|
+
if (fstrand === -1) {
|
|
314
|
+
positions.sort((a, b) => a - b)
|
|
315
|
+
}
|
|
316
|
+
result.push({
|
|
317
|
+
type,
|
|
318
|
+
positions,
|
|
308
319
|
})
|
|
309
|
-
}
|
|
310
|
-
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
return result
|
|
311
323
|
}
|
|
312
324
|
|
|
313
325
|
export function getModificationTypes(mm: string) {
|
|
@@ -317,7 +329,7 @@ export function getModificationTypes(mm: string) {
|
|
|
317
329
|
.map(mod => {
|
|
318
330
|
const [basemod] = mod.split(',')
|
|
319
331
|
|
|
320
|
-
const matches = basemod.match(
|
|
332
|
+
const matches = basemod.match(modificationRegex)
|
|
321
333
|
if (!matches) {
|
|
322
334
|
throw new Error('bad format for MM tag')
|
|
323
335
|
}
|
|
@@ -20,6 +20,13 @@ interface Header {
|
|
|
20
20
|
readGroups?: number[]
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
+
interface FilterBy {
|
|
24
|
+
flagInclude: number
|
|
25
|
+
flagExclude: number
|
|
26
|
+
tagFilter: { tag: string; value: unknown }
|
|
27
|
+
readName: string
|
|
28
|
+
}
|
|
29
|
+
|
|
23
30
|
export default class CramAdapter extends BaseFeatureDataAdapter {
|
|
24
31
|
samHeader: Header = {}
|
|
25
32
|
|
|
@@ -206,12 +213,7 @@ export default class CramAdapter extends BaseFeatureDataAdapter {
|
|
|
206
213
|
getFeatures(
|
|
207
214
|
region: Region & { originalRefName?: string },
|
|
208
215
|
opts?: BaseOptions & {
|
|
209
|
-
filterBy:
|
|
210
|
-
flagInclude: number
|
|
211
|
-
flagExclude: number
|
|
212
|
-
tagFilter: { tag: string; value: unknown }
|
|
213
|
-
name: string
|
|
214
|
-
}
|
|
216
|
+
filterBy: FilterBy
|
|
215
217
|
},
|
|
216
218
|
) {
|
|
217
219
|
const { signal, filterBy, statusCallback = () => {} } = opts || {}
|
|
@@ -234,7 +236,7 @@ export default class CramAdapter extends BaseFeatureDataAdapter {
|
|
|
234
236
|
flagInclude = 0,
|
|
235
237
|
flagExclude = 0,
|
|
236
238
|
tagFilter,
|
|
237
|
-
|
|
239
|
+
readName,
|
|
238
240
|
} = filterBy || {}
|
|
239
241
|
|
|
240
242
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
@@ -251,9 +253,11 @@ export default class CramAdapter extends BaseFeatureDataAdapter {
|
|
|
251
253
|
})
|
|
252
254
|
}
|
|
253
255
|
|
|
254
|
-
if (
|
|
255
|
-
|
|
256
|
-
|
|
256
|
+
if (readName) {
|
|
257
|
+
filtered = filtered.filter(
|
|
258
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
259
|
+
(record: any) => record.readName === readName,
|
|
260
|
+
)
|
|
257
261
|
}
|
|
258
262
|
|
|
259
263
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|