@jbrowse/plugin-bed 4.3.0 → 5.0.0-beta.1
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/esm/BedAdapter/BedAdapter.d.ts +4 -19
- package/esm/BedAdapter/BedAdapter.js +31 -88
- package/esm/BedAdapter/configSchema.d.ts +89 -40
- package/esm/BedAdapter/configSchema.js +65 -13
- package/esm/BedAdapter/index.js +4 -2
- package/esm/BedGraphAdapter/BedGraphAdapter.d.ts +4 -9
- package/esm/BedGraphAdapter/BedGraphAdapter.js +31 -85
- package/esm/BedGraphAdapter/configSchema.d.ts +35 -9
- package/esm/BedGraphAdapter/configSchema.js +47 -12
- package/esm/BedGraphAdapter/index.js +3 -2
- package/esm/BedGraphTabixAdapter/BedGraphTabixAdapter.d.ts +5 -5
- package/esm/BedGraphTabixAdapter/BedGraphTabixAdapter.js +62 -57
- package/esm/BedGraphTabixAdapter/configSchema.d.ts +48 -19
- package/esm/BedGraphTabixAdapter/configSchema.js +42 -34
- package/esm/BedGraphTabixAdapter/index.js +3 -2
- package/esm/BedTabixAdapter/BedTabixAdapter.d.ts +34 -38
- package/esm/BedTabixAdapter/BedTabixAdapter.js +82 -71
- package/esm/BedTabixAdapter/configSchema.d.ts +68 -31
- package/esm/BedTabixAdapter/configSchema.js +51 -35
- package/esm/BedTabixAdapter/index.js +4 -2
- package/esm/BedpeAdapter/BedpeAdapter.d.ts +4 -10
- package/esm/BedpeAdapter/BedpeAdapter.js +28 -69
- package/esm/BedpeAdapter/configSchema.d.ts +33 -10
- package/esm/BedpeAdapter/configSchema.js +45 -12
- package/esm/BedpeAdapter/index.js +4 -2
- package/esm/BedpeAdapter/util.js +22 -23
- package/esm/BigBedAdapter/BigBedAdapter.d.ts +8 -7
- package/esm/BigBedAdapter/BigBedAdapter.js +100 -107
- package/esm/BigBedAdapter/configSchema.d.ts +47 -17
- package/esm/BigBedAdapter/configSchema.js +51 -12
- package/esm/BigBedAdapter/index.js +3 -2
- package/esm/StarFusionAdapter/StarFusionAdapter.d.ts +15 -0
- package/esm/StarFusionAdapter/StarFusionAdapter.js +121 -0
- package/esm/StarFusionAdapter/configSchema.d.ts +31 -0
- package/esm/StarFusionAdapter/configSchema.js +52 -0
- package/esm/StarFusionAdapter/index.d.ts +2 -0
- package/esm/StarFusionAdapter/index.js +12 -0
- package/esm/adapterUtil.d.ts +5 -0
- package/esm/adapterUtil.js +28 -0
- package/esm/bedGraphUtil.d.ts +10 -0
- package/esm/bedGraphUtil.js +18 -0
- package/esm/generateBedMethylFeature.d.ts +1 -1
- package/esm/generateBedMethylFeature.js +19 -6
- package/esm/generateRastairMethylFeature.d.ts +28 -0
- package/esm/generateRastairMethylFeature.js +38 -0
- package/esm/generateRepeatMaskerFeature.d.ts +4 -51
- package/esm/generateRepeatMaskerFeature.js +28 -41
- package/esm/generateUcscTranscript.d.ts +15 -7
- package/esm/generateUcscTranscript.js +58 -28
- package/esm/index.d.ts +2 -0
- package/esm/index.js +10 -8
- package/esm/util.d.ts +32 -27
- package/esm/util.js +127 -97
- package/package.json +9 -13
- package/esm/GuessAdapter/index.d.ts +0 -2
- package/esm/GuessAdapter/index.js +0 -71
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import AdapterType from '@jbrowse/core/pluggableElementTypes/AdapterType';
|
|
2
|
-
import configSchema from
|
|
2
|
+
import configSchema, { normalizeSnapshot } from './configSchema.js';
|
|
3
3
|
export default function BigBedAdapterF(pluginManager) {
|
|
4
4
|
pluginManager.addAdapterType(() => new AdapterType({
|
|
5
5
|
name: 'BigBedAdapter',
|
|
6
6
|
displayName: 'BigBed adapter',
|
|
7
|
+
normalizeSnapshot,
|
|
7
8
|
configSchema,
|
|
8
|
-
getAdapterClass: () => import(
|
|
9
|
+
getAdapterClass: () => import('./BigBedAdapter.js').then(r => r.default),
|
|
9
10
|
}));
|
|
10
11
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { BaseFeatureDataAdapter } from '@jbrowse/core/data_adapters/BaseAdapter';
|
|
2
|
+
import type { StarFusionAdapterConfig } from './configSchema.ts';
|
|
3
|
+
import type { BaseOptions } from '@jbrowse/core/data_adapters/BaseAdapter';
|
|
4
|
+
import type { Feature, IntervalTree, Region } from '@jbrowse/core/util';
|
|
5
|
+
export default class StarFusionAdapter extends BaseFeatureDataAdapter<StarFusionAdapterConfig> {
|
|
6
|
+
private loadData;
|
|
7
|
+
protected intervalTrees: Record<string, Promise<IntervalTree<Feature> | undefined> | undefined>;
|
|
8
|
+
static capabilities: string[];
|
|
9
|
+
private loadDataP;
|
|
10
|
+
getRefNames(opts?: BaseOptions): Promise<string[]>;
|
|
11
|
+
private featureFromLine;
|
|
12
|
+
private loadFeatureTreeP;
|
|
13
|
+
private loadFeatureTree;
|
|
14
|
+
getFeatures(query: Region, opts?: BaseOptions): import("rxjs").Observable<Feature>;
|
|
15
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { BaseFeatureDataAdapter, cachedSetup, } from '@jbrowse/core/data_adapters/BaseAdapter';
|
|
2
|
+
import { SimpleFeature, fetchAndMaybeUnzip } from '@jbrowse/core/util';
|
|
3
|
+
import { openLocation } from '@jbrowse/core/util/io';
|
|
4
|
+
import { parseLineByLine } from '@jbrowse/core/util/parseLineByLine';
|
|
5
|
+
import { buildPairedIntervalTree, intervalTreeFeatures, } from '../adapterUtil.js';
|
|
6
|
+
/**
|
|
7
|
+
* A STAR-Fusion `refName:pos:strand` breakpoint, read from the RIGHT: a refName
|
|
8
|
+
* may contain colons. GRCh38's full analysis set names its HLA contigs
|
|
9
|
+
* `HLA-A*01:01:01:01`, so a breakpoint on one arrives as
|
|
10
|
+
* `HLA-A*01:01:01:01:5000:+` and splitting at the first colon read the contig as
|
|
11
|
+
* `HLA-A*01`, the position as 1 and the strand as absent. Same rule
|
|
12
|
+
* `parseSvAlt` applies to a BND mate locstring, one field further along.
|
|
13
|
+
*
|
|
14
|
+
* A string with fewer than three fields keeps what it always parsed to, so a
|
|
15
|
+
* truncated or strandless row is no worse off than before.
|
|
16
|
+
*/
|
|
17
|
+
function parseBreakpoint(str) {
|
|
18
|
+
const parts = str.split(':');
|
|
19
|
+
const strandStr = parts.length >= 3 ? parts.pop() : undefined;
|
|
20
|
+
const pos = parts.length >= 2 ? +parts.pop() : Number.NaN;
|
|
21
|
+
return {
|
|
22
|
+
refName: parts.join(':'),
|
|
23
|
+
start: pos,
|
|
24
|
+
end: pos + 1,
|
|
25
|
+
strand: strandStr === '+' ? 1 : strandStr === '-' ? -1 : undefined,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Which way the sequence a breakpoint keeps runs from it, as the paired-arc
|
|
30
|
+
* display's mate-direction tick (1 = right, -1 = left, 0 = unknown). A
|
|
31
|
+
* breakpoint's strand in STAR-Fusion is its gene's transcription strand, and the
|
|
32
|
+
* fusion transcript keeps the donor's sequence 5' of the junction and the
|
|
33
|
+
* acceptor's 3' of it: so on a + strand donor the retained side is at lower
|
|
34
|
+
* coordinates and on a + strand acceptor it is at higher ones.
|
|
35
|
+
*/
|
|
36
|
+
function tickDirection(strand, isDonor) {
|
|
37
|
+
return strand === undefined ? 0 : isDonor ? -strand : strand;
|
|
38
|
+
}
|
|
39
|
+
export default class StarFusionAdapter extends BaseFeatureDataAdapter {
|
|
40
|
+
// No `label`: `fetchAndMaybeUnzip` narrates the download from inside.
|
|
41
|
+
loadData = cachedSetup({ setup: opts => this.loadDataP(opts) });
|
|
42
|
+
intervalTrees = {};
|
|
43
|
+
static capabilities = ['getFeatures', 'getRefNames'];
|
|
44
|
+
async loadDataP(opts) {
|
|
45
|
+
const buffer = await fetchAndMaybeUnzip(openLocation(this.getConf('starFusionLocation'), this.pluginManager), opts);
|
|
46
|
+
let columnNames = [];
|
|
47
|
+
let leftIdx = -1;
|
|
48
|
+
let rightIdx = -1;
|
|
49
|
+
const feats1 = {};
|
|
50
|
+
const feats2 = {};
|
|
51
|
+
parseLineByLine(buffer, line => {
|
|
52
|
+
if (line.startsWith('#')) {
|
|
53
|
+
columnNames = line.slice(1).split('\t');
|
|
54
|
+
leftIdx = columnNames.indexOf('LeftBreakpoint');
|
|
55
|
+
rightIdx = columnNames.indexOf('RightBreakpoint');
|
|
56
|
+
}
|
|
57
|
+
else if (leftIdx >= 0 && rightIdx >= 0) {
|
|
58
|
+
const cols = line.split('\t');
|
|
59
|
+
const left = cols[leftIdx];
|
|
60
|
+
const right = cols[rightIdx];
|
|
61
|
+
// through parseBreakpoint, so the bucket a row is filed under is the
|
|
62
|
+
// refName its feature will report -- a colon-bearing contig split two
|
|
63
|
+
// ways puts the row in a bucket no query can reach
|
|
64
|
+
const leftRef = left ? parseBreakpoint(left).refName : undefined;
|
|
65
|
+
const rightRef = right ? parseBreakpoint(right).refName : undefined;
|
|
66
|
+
if (leftRef && rightRef) {
|
|
67
|
+
;
|
|
68
|
+
(feats1[leftRef] ??= []).push(line);
|
|
69
|
+
(feats2[rightRef] ??= []).push(line);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return true;
|
|
73
|
+
}, opts?.statusCallback);
|
|
74
|
+
return { columnNames, feats1, feats2 };
|
|
75
|
+
}
|
|
76
|
+
async getRefNames(opts = {}) {
|
|
77
|
+
const { feats1, feats2 } = await this.loadData(opts);
|
|
78
|
+
return [...new Set([...Object.keys(feats1), ...Object.keys(feats2)])];
|
|
79
|
+
}
|
|
80
|
+
featureFromLine(line, columnNames, uniqueId, flip) {
|
|
81
|
+
const cols = line.split('\t');
|
|
82
|
+
const row = Object.fromEntries(columnNames.map((name, i) => [name, cols[i]]));
|
|
83
|
+
const primaryBp = parseBreakpoint((flip ? row.RightBreakpoint : row.LeftBreakpoint));
|
|
84
|
+
const mateBp = parseBreakpoint((flip ? row.LeftBreakpoint : row.RightBreakpoint));
|
|
85
|
+
// the LeftBreakpoint is the donor, so this feature is the donor end of the
|
|
86
|
+
// junction whenever it is not flipped, and the mate is the other end
|
|
87
|
+
return new SimpleFeature({
|
|
88
|
+
uniqueId,
|
|
89
|
+
refName: primaryBp.refName,
|
|
90
|
+
start: primaryBp.start,
|
|
91
|
+
end: primaryBp.end,
|
|
92
|
+
strand: primaryBp.strand,
|
|
93
|
+
mateDirection: tickDirection(primaryBp.strand, !flip),
|
|
94
|
+
name: row.FusionName,
|
|
95
|
+
score: row.JunctionReadCount ? +row.JunctionReadCount : undefined,
|
|
96
|
+
type: 'fusion',
|
|
97
|
+
mate: {
|
|
98
|
+
refName: mateBp.refName,
|
|
99
|
+
start: mateBp.start,
|
|
100
|
+
end: mateBp.end,
|
|
101
|
+
strand: mateBp.strand,
|
|
102
|
+
mateDirection: tickDirection(mateBp.strand, flip),
|
|
103
|
+
},
|
|
104
|
+
...row,
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
async loadFeatureTreeP(refName) {
|
|
108
|
+
const { columnNames, feats1, feats2 } = await this.loadData();
|
|
109
|
+
return buildPairedIntervalTree(feats1, feats2, refName, this.id, (line, uniqueId, flip) => this.featureFromLine(line, columnNames, uniqueId, flip));
|
|
110
|
+
}
|
|
111
|
+
async loadFeatureTree(refName) {
|
|
112
|
+
this.intervalTrees[refName] ??= this.loadFeatureTreeP(refName).catch((e) => {
|
|
113
|
+
this.intervalTrees[refName] = undefined;
|
|
114
|
+
throw e;
|
|
115
|
+
});
|
|
116
|
+
return this.intervalTrees[refName];
|
|
117
|
+
}
|
|
118
|
+
getFeatures(query, opts = {}) {
|
|
119
|
+
return intervalTreeFeatures(query, opts, refName => this.loadFeatureTree(refName));
|
|
120
|
+
}
|
|
121
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { Instance } from '@jbrowse/mobx-state-tree';
|
|
2
|
+
export declare function normalizeSnapshot(snap: Record<string, unknown>): Record<string, unknown>;
|
|
3
|
+
/**
|
|
4
|
+
* #config StarFusionAdapter
|
|
5
|
+
* #trackType VariantTrack
|
|
6
|
+
* #fileFormat variants | STAR-Fusion
|
|
7
|
+
* used to load STAR-Fusion `star-fusion.fusion_predictions.tsv` output
|
|
8
|
+
*
|
|
9
|
+
* #example
|
|
10
|
+
* ```js
|
|
11
|
+
* {
|
|
12
|
+
* type: 'StarFusionAdapter',
|
|
13
|
+
* uri: 'https://example.com/star-fusion.fusion_predictions.tsv',
|
|
14
|
+
* }
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
declare const StarFusionAdapter: import("@jbrowse/core/configuration/configurationSchema").ConfigurationSchemaType<{
|
|
18
|
+
/**
|
|
19
|
+
* #slot
|
|
20
|
+
*/
|
|
21
|
+
readonly starFusionLocation: {
|
|
22
|
+
readonly type: "fileLocation";
|
|
23
|
+
readonly description: "STAR-Fusion TSV output file (plain text or gzipped)";
|
|
24
|
+
readonly defaultValue: {
|
|
25
|
+
readonly uri: "/path/to/star-fusion.fusion_predictions.tsv";
|
|
26
|
+
readonly locationType: "UriLocation";
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
}, import("@jbrowse/core/configuration/configurationSchema").ConfigurationSchemaOptions<undefined, undefined>>;
|
|
30
|
+
export type StarFusionAdapterConfig = Instance<typeof StarFusionAdapter>;
|
|
31
|
+
export default StarFusionAdapter;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { ConfigurationSchema } from '@jbrowse/core/configuration';
|
|
2
|
+
export function normalizeSnapshot(snap) {
|
|
3
|
+
return snap.uri
|
|
4
|
+
? {
|
|
5
|
+
...snap,
|
|
6
|
+
starFusionLocation: {
|
|
7
|
+
uri: snap.uri,
|
|
8
|
+
baseUri: snap.baseUri,
|
|
9
|
+
locationType: 'UriLocation',
|
|
10
|
+
},
|
|
11
|
+
}
|
|
12
|
+
: snap;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* #config StarFusionAdapter
|
|
16
|
+
* #trackType VariantTrack
|
|
17
|
+
* #fileFormat variants | STAR-Fusion
|
|
18
|
+
* used to load STAR-Fusion `star-fusion.fusion_predictions.tsv` output
|
|
19
|
+
*
|
|
20
|
+
* #example
|
|
21
|
+
* ```js
|
|
22
|
+
* {
|
|
23
|
+
* type: 'StarFusionAdapter',
|
|
24
|
+
* uri: 'https://example.com/star-fusion.fusion_predictions.tsv',
|
|
25
|
+
* }
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
const StarFusionAdapter = ConfigurationSchema('StarFusionAdapter', {
|
|
29
|
+
/**
|
|
30
|
+
* #slot
|
|
31
|
+
*/
|
|
32
|
+
starFusionLocation: {
|
|
33
|
+
type: 'fileLocation',
|
|
34
|
+
description: 'STAR-Fusion TSV output file (plain text or gzipped)',
|
|
35
|
+
defaultValue: {
|
|
36
|
+
uri: '/path/to/star-fusion.fusion_predictions.tsv',
|
|
37
|
+
locationType: 'UriLocation',
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
}, {
|
|
41
|
+
explicitlyTyped: true,
|
|
42
|
+
/**
|
|
43
|
+
* #preProcessSnapshot
|
|
44
|
+
*
|
|
45
|
+
* Allows minimal config:
|
|
46
|
+
* ```json
|
|
47
|
+
* { "type": "StarFusionAdapter", "uri": "star-fusion.tsv" }
|
|
48
|
+
* ```
|
|
49
|
+
*/
|
|
50
|
+
preProcessSnapshot: normalizeSnapshot,
|
|
51
|
+
});
|
|
52
|
+
export default StarFusionAdapter;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import AdapterType from '@jbrowse/core/pluggableElementTypes/AdapterType';
|
|
2
|
+
import configSchema, { normalizeSnapshot } from './configSchema.js';
|
|
3
|
+
export default function StarFusionAdapterF(pluginManager) {
|
|
4
|
+
pluginManager.addAdapterType(() => new AdapterType({
|
|
5
|
+
name: 'StarFusionAdapter',
|
|
6
|
+
displayName: 'STAR-Fusion adapter',
|
|
7
|
+
configSchema,
|
|
8
|
+
getAdapterClass: () => import('./StarFusionAdapter.js').then(r => r.default),
|
|
9
|
+
locationKey: 'starFusionLocation',
|
|
10
|
+
normalizeSnapshot,
|
|
11
|
+
}));
|
|
12
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { IntervalTree } from '@jbrowse/core/util';
|
|
2
|
+
import type { BaseOptions } from '@jbrowse/core/data_adapters/BaseAdapter';
|
|
3
|
+
import type { Feature, Region } from '@jbrowse/core/util';
|
|
4
|
+
export declare function intervalTreeFeatures(query: Region, opts: BaseOptions, loadTree: (refName: string) => Promise<IntervalTree<Feature> | undefined>): import("rxjs").Observable<Feature>;
|
|
5
|
+
export declare function buildPairedIntervalTree(feats1: Record<string, string[]>, feats2: Record<string, string[]>, refName: string, idPrefix: string, makeFeature: (line: string, uniqueId: string, flip: boolean) => Feature): IntervalTree<Feature>;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { IntervalTree } from '@jbrowse/core/util';
|
|
2
|
+
import { ObservableCreate } from '@jbrowse/core/util/rxjs';
|
|
3
|
+
// Emit the features of a per-refName interval tree that intersect the query.
|
|
4
|
+
// Shared by the in-memory interval-tree adapters.
|
|
5
|
+
export function intervalTreeFeatures(query, opts, loadTree) {
|
|
6
|
+
return ObservableCreate(async (observer) => {
|
|
7
|
+
const tree = await loadTree(query.refName);
|
|
8
|
+
for (const f of tree?.search([query.start, query.end]) ?? []) {
|
|
9
|
+
observer.next(f);
|
|
10
|
+
}
|
|
11
|
+
observer.complete();
|
|
12
|
+
}, opts.stopToken);
|
|
13
|
+
}
|
|
14
|
+
// Build one interval tree for a refName from paired-feature buckets, inserting
|
|
15
|
+
// the records that start on this ref (feats1, flip=false) and those whose mate
|
|
16
|
+
// starts here (feats2, flip=true). Shared by BedpeAdapter and StarFusionAdapter.
|
|
17
|
+
export function buildPairedIntervalTree(feats1, feats2, refName, idPrefix, makeFeature) {
|
|
18
|
+
const tree = new IntervalTree();
|
|
19
|
+
for (const [i, line] of (feats1[refName] ?? []).entries()) {
|
|
20
|
+
const f = makeFeature(line, `${idPrefix}-${refName}-${i}-r1`, false);
|
|
21
|
+
tree.insert([f.get('start'), f.get('end')], f);
|
|
22
|
+
}
|
|
23
|
+
for (const [i, line] of (feats2[refName] ?? []).entries()) {
|
|
24
|
+
const f = makeFeature(line, `${idPrefix}-${refName}-${i}-r2`, true);
|
|
25
|
+
tree.insert([f.get('start'), f.get('end')], f);
|
|
26
|
+
}
|
|
27
|
+
return tree;
|
|
28
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Feature } from '@jbrowse/core/util';
|
|
2
|
+
export declare function makeBedGraphFeature({ uniqueId, refName, start, end, names, j, value, }: {
|
|
3
|
+
uniqueId: string;
|
|
4
|
+
refName: string;
|
|
5
|
+
start: number;
|
|
6
|
+
end: number;
|
|
7
|
+
names: string[];
|
|
8
|
+
j: number;
|
|
9
|
+
value: string;
|
|
10
|
+
}): Feature | undefined;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { SimpleFeature } from '@jbrowse/core/util';
|
|
2
|
+
// Build a single-score bedGraph feature for value column j, or undefined when
|
|
3
|
+
// the value is not numeric. Shared by BedGraphAdapter and BedGraphTabixAdapter.
|
|
4
|
+
export function makeBedGraphFeature({ uniqueId, refName, start, end, names, j, value, }) {
|
|
5
|
+
const score = +value;
|
|
6
|
+
return Number.isNaN(score)
|
|
7
|
+
? undefined
|
|
8
|
+
: new SimpleFeature({
|
|
9
|
+
id: uniqueId,
|
|
10
|
+
data: {
|
|
11
|
+
refName,
|
|
12
|
+
start,
|
|
13
|
+
end,
|
|
14
|
+
score,
|
|
15
|
+
source: names[j] || `col${j}`,
|
|
16
|
+
},
|
|
17
|
+
});
|
|
18
|
+
}
|
|
@@ -16,7 +16,7 @@ export declare function generateBedMethylFeature({ splitLine, uniqueId, refName,
|
|
|
16
16
|
end: number;
|
|
17
17
|
code: string | undefined;
|
|
18
18
|
score: number;
|
|
19
|
-
strand:
|
|
19
|
+
strand: number;
|
|
20
20
|
color: string | undefined;
|
|
21
21
|
source: string | undefined;
|
|
22
22
|
n_valid_cov: string | undefined;
|
|
@@ -1,19 +1,32 @@
|
|
|
1
|
+
import { parseStrand } from './util.js';
|
|
2
|
+
// this uses modkit bedMethyl. unclear how to reliably detect minimal 9+2 bedMethyl
|
|
1
3
|
export function isBedMethylFeature({ splitLine, start, end, }) {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
4
|
+
// columns 9-17 are the nine numeric methylation stats
|
|
5
|
+
const nums = splitLine.slice(9, 18);
|
|
6
|
+
return (splitLine[6] !== undefined &&
|
|
7
|
+
+splitLine[6] === start &&
|
|
8
|
+
splitLine[7] !== undefined &&
|
|
9
|
+
+splitLine[7] === end &&
|
|
10
|
+
nums.length === 9 &&
|
|
11
|
+
nums.every(x => x && !Number.isNaN(+x)));
|
|
5
12
|
}
|
|
6
13
|
export function generateBedMethylFeature({ splitLine, uniqueId, refName, start, end, }) {
|
|
7
|
-
|
|
14
|
+
// see
|
|
15
|
+
// https://github.com/nanoporetech/modkit?tab=readme-ov-file#description-of-bedmethyl-output
|
|
16
|
+
const code = splitLine[3];
|
|
17
|
+
const strandRaw = splitLine[5];
|
|
18
|
+
const color = splitLine[8];
|
|
19
|
+
const [n_valid_cov, fraction_modified, n_mod, n_canonical, n_other_mod, n_delete, n_fail, n_diff, n_nocall,] = splitLine.slice(9);
|
|
8
20
|
return {
|
|
9
21
|
uniqueId,
|
|
10
22
|
refName,
|
|
11
23
|
start,
|
|
12
24
|
end,
|
|
13
25
|
code,
|
|
14
|
-
score: +fraction_modified
|
|
15
|
-
strand,
|
|
26
|
+
score: +fraction_modified,
|
|
27
|
+
strand: parseStrand(strandRaw),
|
|
16
28
|
color,
|
|
29
|
+
// source mirrors code so MultiQuantitativeTrack groups by modification code
|
|
17
30
|
source: code,
|
|
18
31
|
n_valid_cov,
|
|
19
32
|
fraction_modified,
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export declare function isRastairMethylFeature(names: string[]): boolean;
|
|
2
|
+
export declare function generateRastairMethylFeature({ splitLine, names, uniqueId, refName, start, end, }: {
|
|
3
|
+
splitLine: string[];
|
|
4
|
+
names: string[];
|
|
5
|
+
uniqueId: string;
|
|
6
|
+
refName: string;
|
|
7
|
+
start: number;
|
|
8
|
+
end: number;
|
|
9
|
+
}): {
|
|
10
|
+
uniqueId: string;
|
|
11
|
+
refName: string;
|
|
12
|
+
start: number;
|
|
13
|
+
end: number;
|
|
14
|
+
code: string;
|
|
15
|
+
source: string;
|
|
16
|
+
score: number;
|
|
17
|
+
fraction_modified: number;
|
|
18
|
+
strand: number;
|
|
19
|
+
n_valid_cov: string | undefined;
|
|
20
|
+
n_mod: string | undefined;
|
|
21
|
+
n_canonical: string | undefined;
|
|
22
|
+
beta_est: string | undefined;
|
|
23
|
+
no_snp: string | undefined;
|
|
24
|
+
snp: string | undefined;
|
|
25
|
+
coverage: string | undefined;
|
|
26
|
+
genotype: string | undefined;
|
|
27
|
+
gt_conf_score: string | undefined;
|
|
28
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { parseStrand } from './util.js';
|
|
2
|
+
// Rastair (https://www.rastair.com) emits a headered BED/TSV of per-CpG
|
|
3
|
+
// methylation calls from TAPS / mod-C→T data. Its layout differs from modkit
|
|
4
|
+
// bedMethyl, so it's identified by the header column names rather than
|
|
5
|
+
// positionally: #chr start end name beta_est strand unmod mod no_snp snp
|
|
6
|
+
// coverage genotype gt_p_score gt_conf_score
|
|
7
|
+
const RASTAIR_COLUMNS = ['beta_est', 'unmod', 'mod', 'coverage'];
|
|
8
|
+
export function isRastairMethylFeature(names) {
|
|
9
|
+
return RASTAIR_COLUMNS.every(col => names.includes(col));
|
|
10
|
+
}
|
|
11
|
+
export function generateRastairMethylFeature({ splitLine, names, uniqueId, refName, start, end, }) {
|
|
12
|
+
const col = (name) => splitLine[names.indexOf(name)];
|
|
13
|
+
// beta_est is a 0-1 fraction; the shared bedMethyl display path expects a
|
|
14
|
+
// 0-100 percentage (modkit fraction_modified), so scale to match
|
|
15
|
+
const fraction_modified = Number(col('beta_est')) * 100;
|
|
16
|
+
return {
|
|
17
|
+
uniqueId,
|
|
18
|
+
refName,
|
|
19
|
+
start,
|
|
20
|
+
end,
|
|
21
|
+
// rastair reports modified C (5mC); use modkit's 'm' code so it groups and
|
|
22
|
+
// colors as 5mC methylation in the shared bedMethyl display path
|
|
23
|
+
code: 'm',
|
|
24
|
+
source: 'm',
|
|
25
|
+
score: fraction_modified,
|
|
26
|
+
fraction_modified,
|
|
27
|
+
strand: parseStrand(col('strand')),
|
|
28
|
+
n_valid_cov: col('coverage'),
|
|
29
|
+
n_mod: col('mod'),
|
|
30
|
+
n_canonical: col('unmod'),
|
|
31
|
+
beta_est: col('beta_est'),
|
|
32
|
+
no_snp: col('no_snp'),
|
|
33
|
+
snp: col('snp'),
|
|
34
|
+
coverage: col('coverage'),
|
|
35
|
+
genotype: col('genotype'),
|
|
36
|
+
gt_conf_score: col('gt_conf_score'),
|
|
37
|
+
};
|
|
38
|
+
}
|
|
@@ -1,51 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
export
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
start: number;
|
|
6
|
-
end: number;
|
|
7
|
-
description: string;
|
|
8
|
-
[key: string]: unknown;
|
|
9
|
-
}): {
|
|
10
|
-
uniqueId: string;
|
|
11
|
-
refName: string;
|
|
12
|
-
start: number;
|
|
13
|
-
end: number;
|
|
14
|
-
bitsw_score: string | undefined;
|
|
15
|
-
percent_div: string | undefined;
|
|
16
|
-
percent_del: string | undefined;
|
|
17
|
-
percent_ins: string | undefined;
|
|
18
|
-
query_chr: string | undefined;
|
|
19
|
-
query_begin: string | undefined;
|
|
20
|
-
query_end: string | undefined;
|
|
21
|
-
query_remaining: string | undefined;
|
|
22
|
-
orientation: string | undefined;
|
|
23
|
-
matching_repeat_name: string | undefined;
|
|
24
|
-
matching_repeat_class: string | undefined;
|
|
25
|
-
matching_repeat_begin: string | undefined;
|
|
26
|
-
matching_repeat_end: string | undefined;
|
|
27
|
-
matching_repeat_remaining: string | undefined;
|
|
28
|
-
repeat_id: string | undefined;
|
|
29
|
-
description?: undefined;
|
|
30
|
-
} | {
|
|
31
|
-
uniqueId: string;
|
|
32
|
-
refName: string;
|
|
33
|
-
start: number;
|
|
34
|
-
end: number;
|
|
35
|
-
description: undefined;
|
|
36
|
-
bitsw_score?: undefined;
|
|
37
|
-
percent_div?: undefined;
|
|
38
|
-
percent_del?: undefined;
|
|
39
|
-
percent_ins?: undefined;
|
|
40
|
-
query_chr?: undefined;
|
|
41
|
-
query_begin?: undefined;
|
|
42
|
-
query_end?: undefined;
|
|
43
|
-
query_remaining?: undefined;
|
|
44
|
-
orientation?: undefined;
|
|
45
|
-
matching_repeat_name?: undefined;
|
|
46
|
-
matching_repeat_class?: undefined;
|
|
47
|
-
matching_repeat_begin?: undefined;
|
|
48
|
-
matching_repeat_end?: undefined;
|
|
49
|
-
matching_repeat_remaining?: undefined;
|
|
50
|
-
repeat_id?: undefined;
|
|
51
|
-
};
|
|
1
|
+
declare const FIELDS: readonly ['bitsw_score', 'percent_div', 'percent_del', 'percent_ins', 'query_chr', 'query_begin', 'query_end', 'query_remaining', 'orientation', 'matching_repeat_name', 'matching_repeat_class', 'matching_repeat_begin', 'matching_repeat_end', 'matching_repeat_remaining', 'repeat_id'];
|
|
2
|
+
export type RepeatMaskerFields = Record<(typeof FIELDS)[number], string>;
|
|
3
|
+
export declare function parseRepeatMaskerDescription(desc: unknown): RepeatMaskerFields | undefined;
|
|
4
|
+
export {};
|
|
@@ -1,43 +1,30 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
const FIELDS = [
|
|
2
|
+
'bitsw_score',
|
|
3
|
+
'percent_div',
|
|
4
|
+
'percent_del',
|
|
5
|
+
'percent_ins',
|
|
6
|
+
'query_chr',
|
|
7
|
+
'query_begin',
|
|
8
|
+
'query_end',
|
|
9
|
+
'query_remaining',
|
|
10
|
+
'orientation',
|
|
11
|
+
'matching_repeat_name',
|
|
12
|
+
'matching_repeat_class',
|
|
13
|
+
'matching_repeat_begin',
|
|
14
|
+
'matching_repeat_end',
|
|
15
|
+
'matching_repeat_remaining',
|
|
16
|
+
'repeat_id',
|
|
17
|
+
];
|
|
18
|
+
// RepeatMasker .out-derived description: 15 space-separated fields with numeric
|
|
19
|
+
// values at positions 0,1,2,3,5,6. Returns parsed fields or undefined if the
|
|
20
|
+
// description doesn't match the format.
|
|
21
|
+
export function parseRepeatMaskerDescription(desc) {
|
|
22
|
+
if (typeof desc === 'string') {
|
|
23
|
+
const parts = desc.trim().split(' ');
|
|
24
|
+
const looksRight = [0, 1, 2, 3, 5, 6].every(i => parts[i] !== undefined && !Number.isNaN(+parts[i]));
|
|
25
|
+
if (looksRight) {
|
|
26
|
+
return Object.fromEntries(FIELDS.map((f, i) => [f, parts[i] ?? '']));
|
|
27
|
+
}
|
|
4
28
|
}
|
|
5
|
-
|
|
6
|
-
const ret = desc.trim().split(' ');
|
|
7
|
-
return [0, 1, 2, 3, 5, 6].every(s => ret[s] !== undefined ? !Number.isNaN(+ret[s]) : false);
|
|
8
|
-
}
|
|
9
|
-
}
|
|
10
|
-
function makeRepeatTrackDescription(description) {
|
|
11
|
-
if (isRepeatMaskerDescriptionField(description)) {
|
|
12
|
-
const [bitsw_score, percent_div, percent_del, percent_ins, query_chr, query_begin, query_end, query_remaining, orientation, matching_repeat_name, matching_repeat_class, matching_repeat_begin, matching_repeat_end, matching_repeat_remaining, repeat_id,] = description.trim().split(' ');
|
|
13
|
-
return {
|
|
14
|
-
bitsw_score,
|
|
15
|
-
percent_div,
|
|
16
|
-
percent_del,
|
|
17
|
-
percent_ins,
|
|
18
|
-
query_chr,
|
|
19
|
-
query_begin,
|
|
20
|
-
query_end,
|
|
21
|
-
query_remaining,
|
|
22
|
-
orientation,
|
|
23
|
-
matching_repeat_name,
|
|
24
|
-
matching_repeat_class,
|
|
25
|
-
matching_repeat_begin,
|
|
26
|
-
matching_repeat_end,
|
|
27
|
-
matching_repeat_remaining,
|
|
28
|
-
repeat_id,
|
|
29
|
-
};
|
|
30
|
-
}
|
|
31
|
-
return { description };
|
|
32
|
-
}
|
|
33
|
-
export function generateRepeatMaskerFeature({ uniqueId, refName, start, end, description, ...rest }) {
|
|
34
|
-
const { subfeatures, ...rest2 } = rest;
|
|
35
|
-
return {
|
|
36
|
-
...rest2,
|
|
37
|
-
...makeRepeatTrackDescription(description),
|
|
38
|
-
uniqueId,
|
|
39
|
-
refName,
|
|
40
|
-
start,
|
|
41
|
-
end,
|
|
42
|
-
};
|
|
29
|
+
return undefined;
|
|
43
30
|
}
|
|
@@ -1,12 +1,20 @@
|
|
|
1
1
|
import type { MinimalFeature } from './types.ts';
|
|
2
|
-
|
|
3
|
-
thickStart?:
|
|
4
|
-
|
|
2
|
+
interface UcscTranscriptCheck {
|
|
3
|
+
thickStart?: number;
|
|
4
|
+
thickEnd?: number;
|
|
5
|
+
blockCount?: number;
|
|
5
6
|
strand?: number;
|
|
6
|
-
}
|
|
7
|
+
}
|
|
8
|
+
interface ValidUcscTranscriptCheck {
|
|
9
|
+
thickStart: number;
|
|
10
|
+
thickEnd: number;
|
|
11
|
+
blockCount: number;
|
|
12
|
+
strand: number;
|
|
13
|
+
}
|
|
14
|
+
export declare function isUcscTranscript(input: UcscTranscriptCheck): input is ValidUcscTranscriptCheck;
|
|
7
15
|
interface UcscTranscriptInput {
|
|
8
16
|
uniqueId: string;
|
|
9
|
-
strand
|
|
17
|
+
strand: number;
|
|
10
18
|
thickStart: number;
|
|
11
19
|
thickEnd: number;
|
|
12
20
|
refName: string;
|
|
@@ -15,10 +23,10 @@ interface UcscTranscriptInput {
|
|
|
15
23
|
subfeatures: MinimalFeature[];
|
|
16
24
|
[key: string]: unknown;
|
|
17
25
|
}
|
|
18
|
-
interface UcscTranscriptOutput {
|
|
26
|
+
interface UcscTranscriptOutput extends MinimalFeature {
|
|
19
27
|
uniqueId: string;
|
|
20
28
|
strand: number;
|
|
21
|
-
type:
|
|
29
|
+
type: 'mRNA' | 'transcript';
|
|
22
30
|
refName: string;
|
|
23
31
|
start: number;
|
|
24
32
|
end: number;
|