@jbrowse/plugin-bed 4.3.0 → 5.0.0-beta.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.
- 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 +73 -31
- package/esm/BedTabixAdapter/configSchema.js +53 -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 +52 -17
- package/esm/BigBedAdapter/configSchema.js +53 -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,103 +1,114 @@
|
|
|
1
|
-
import BED from '@gmod/bed';
|
|
2
1
|
import { TabixIndexedFile } from '@gmod/tabix';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
2
|
+
import { readConfObject } from '@jbrowse/core/configuration';
|
|
3
|
+
import { BaseFeatureDataAdapter, cachedSetup, } from '@jbrowse/core/data_adapters/BaseAdapter';
|
|
4
|
+
import { SimpleFeature, downloadStatus } from '@jbrowse/core/util';
|
|
5
|
+
import { sharedBgzfWorkerPool } from '@jbrowse/core/util/bgzfWorkerPool';
|
|
6
|
+
import { decompressedBytesBudget } from '@jbrowse/core/util/cacheBudgets';
|
|
7
|
+
import { openLocation, openTabixIndexFilehandle } from '@jbrowse/core/util/io';
|
|
6
8
|
import { ObservableCreate } from '@jbrowse/core/util/rxjs';
|
|
7
|
-
import { checkStopToken } from '@jbrowse/core/util/stopToken';
|
|
8
|
-
import {
|
|
9
|
+
import { checkStopTokenThrottled, checkStopToken, createStopTokenChecker, withStopTokenSignal, } from '@jbrowse/core/util/stopToken';
|
|
10
|
+
import { readTabixHeaderLines } from '@jbrowse/core/util/tabix';
|
|
11
|
+
import { featureData, makeParser, parseNamesFromHeader } from '../util.js';
|
|
9
12
|
export default class BedTabixAdapter extends BaseFeatureDataAdapter {
|
|
10
|
-
parser
|
|
13
|
+
// the parser needs the column names, which come from the file's header, so
|
|
14
|
+
// it cannot be built in the constructor
|
|
15
|
+
getParser = cachedSetup({
|
|
16
|
+
setup: async () => makeParser({
|
|
17
|
+
autoSql: readConfObject(this.config, 'autoSql'),
|
|
18
|
+
columnNames: await this.getNames(),
|
|
19
|
+
}),
|
|
20
|
+
});
|
|
21
|
+
bedGzLoc;
|
|
11
22
|
bed;
|
|
12
|
-
columnNames;
|
|
13
|
-
scoreColumn;
|
|
14
23
|
static capabilities = ['getFeatures', 'getRefNames'];
|
|
15
|
-
|
|
24
|
+
// `label` because the index metadata download narrates nothing itself; the
|
|
25
|
+
// helper shows it only while the first attempt is in flight, so pan/zoom
|
|
26
|
+
// re-entry (every getFeatures awaits it) doesn't re-flash "Downloading index"
|
|
27
|
+
getMetadata = cachedSetup({
|
|
28
|
+
label: 'Downloading index',
|
|
29
|
+
setup: () => this.bed.getMetadata(),
|
|
30
|
+
});
|
|
31
|
+
// Memoized: getFeatures needs the names on every query, and reading them goes
|
|
32
|
+
// to the file's leading blocks — a fetch and a decompress that was being
|
|
33
|
+
// repeated on every pan and zoom. Resolves undefined when the file declares
|
|
34
|
+
// no column names — parseNamesFromHeader's answer for a header it can't read
|
|
35
|
+
// names out of.
|
|
36
|
+
getNames = cachedSetup({ setup: () => this.readNames() });
|
|
16
37
|
constructor(config, getSubAdapter, pluginManager) {
|
|
17
38
|
super(config, getSubAdapter, pluginManager);
|
|
18
|
-
|
|
19
|
-
const type = this.
|
|
20
|
-
const loc = this.
|
|
21
|
-
const autoSql = this.getConf('autoSql');
|
|
39
|
+
this.bedGzLoc = readConfObject(this.config, 'bedGzLocation');
|
|
40
|
+
const type = readConfObject(this.config, ['index', 'indexType']);
|
|
41
|
+
const loc = readConfObject(this.config, ['index', 'location']);
|
|
22
42
|
const pm = this.pluginManager;
|
|
23
43
|
this.bed = new TabixIndexedFile({
|
|
24
|
-
filehandle: openLocation(bedGzLoc, pm),
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
44
|
+
filehandle: openLocation(this.bedGzLoc, pm),
|
|
45
|
+
...openTabixIndexFilehandle(loc, type, pm),
|
|
46
|
+
chunkCacheBudget: decompressedBytesBudget,
|
|
47
|
+
bgzfWorkerPool: sharedBgzfWorkerPool(),
|
|
28
48
|
});
|
|
29
|
-
this.columnNames = this.getConf('columnNames');
|
|
30
|
-
this.scoreColumn = this.getConf('scoreColumn');
|
|
31
|
-
this.parser = new BED({ autoSql });
|
|
32
49
|
}
|
|
33
50
|
async getRefNames(opts = {}) {
|
|
34
|
-
return this.bed.getReferenceSequenceNames(opts);
|
|
51
|
+
return downloadStatus('Downloading index', opts.statusCallback, onProgress => this.bed.getReferenceSequenceNames({ ...opts, onProgress }));
|
|
35
52
|
}
|
|
36
53
|
async getHeader(opts) {
|
|
37
54
|
return this.bed.getHeader(opts);
|
|
38
55
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
return this.setupP;
|
|
47
|
-
}
|
|
48
|
-
async getMetadataPre() {
|
|
49
|
-
const meta = await this.bed.getMetadata();
|
|
50
|
-
return { meta };
|
|
51
|
-
}
|
|
52
|
-
async getMetadata(opts) {
|
|
53
|
-
const { statusCallback = () => { } } = opts || {};
|
|
54
|
-
return updateStatus('Downloading index', statusCallback, () => this.getMetadataPre2(opts));
|
|
56
|
+
/**
|
|
57
|
+
* Estimate compressed bytes for regions straight from the tabix index — no
|
|
58
|
+
* feature download. Lets wrapping adapters (e.g. MafTabix) byte-budget a
|
|
59
|
+
* fetch before pulling the (potentially huge) per-line payload.
|
|
60
|
+
*/
|
|
61
|
+
async getRegionByteSize(regions, opts) {
|
|
62
|
+
return this.bed.bytesForRegions(regions, opts);
|
|
55
63
|
}
|
|
56
|
-
async
|
|
57
|
-
|
|
58
|
-
|
|
64
|
+
async readNames() {
|
|
65
|
+
const columnNames = readConfObject(this.config, 'columnNames');
|
|
66
|
+
if (columnNames.length) {
|
|
67
|
+
return columnNames;
|
|
59
68
|
}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
return
|
|
64
|
-
? defline
|
|
65
|
-
.slice(1)
|
|
66
|
-
.split('\t')
|
|
67
|
-
.map(f => f.trim())
|
|
68
|
-
: undefined;
|
|
69
|
+
// readTabixHeaderLines covers both ways a tabix file keeps a header: a
|
|
70
|
+
// `#`-commented block, which getHeader() returns, and a plain row skipped
|
|
71
|
+
// via `tabix -S N`, which it does not.
|
|
72
|
+
return parseNamesFromHeader((await readTabixHeaderLines(this.bed)).join('\n'));
|
|
69
73
|
}
|
|
70
74
|
getFeatures(query, opts) {
|
|
71
|
-
const { stopToken, statusCallback
|
|
75
|
+
const { stopToken, statusCallback } = opts ?? {};
|
|
72
76
|
return ObservableCreate(async (observer) => {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
const colStart = columnNumbers.start - 1;
|
|
77
|
-
const colEnd = columnNumbers.end - 1;
|
|
77
|
+
// warms the index under its own status label — getLines would otherwise
|
|
78
|
+
// download it under "Downloading features"
|
|
79
|
+
await this.getMetadata(opts);
|
|
78
80
|
const names = await this.getNames();
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
+
const parser = await this.getParser();
|
|
82
|
+
const scoreColumn = readConfObject(this.config, 'scoreColumn');
|
|
83
|
+
const disableGeneHeuristic = readConfObject(this.config, 'disableGeneHeuristic');
|
|
84
|
+
const stopTokenCheck = createStopTokenChecker(stopToken);
|
|
81
85
|
checkStopToken(stopToken);
|
|
82
|
-
await
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
86
|
+
await withStopTokenSignal(stopToken, signal => downloadStatus('Downloading features', statusCallback, onProgress =>
|
|
87
|
+
// start/end come from @gmod/tabix rather than being re-derived from
|
|
88
|
+
// the line: it located the coordinate columns to find these lines at
|
|
89
|
+
// all, and already applied the index's coordinate offset (-1 for a
|
|
90
|
+
// 1-based-closed preset) and its no-end-column convention (a single
|
|
91
|
+
// position becomes start..start+1). refName is likewise the query's
|
|
92
|
+
// — getLines only calls back for lines whose ref column matches it.
|
|
93
|
+
this.bed.getLines(query.refName, query.start, query.end, {
|
|
94
|
+
lineCallback: (line, fileOffset, start, end) => {
|
|
95
|
+
checkStopTokenThrottled(stopTokenCheck);
|
|
96
|
+
const splitLine = line.split('\t');
|
|
88
97
|
observer.next(new SimpleFeature(featureData({
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
scoreColumn
|
|
94
|
-
parser
|
|
98
|
+
splitLine,
|
|
99
|
+
refName: query.refName,
|
|
100
|
+
start,
|
|
101
|
+
end,
|
|
102
|
+
scoreColumn,
|
|
103
|
+
parser,
|
|
95
104
|
uniqueId: `${this.id}-${fileOffset}`,
|
|
96
105
|
names,
|
|
97
106
|
disableGeneHeuristic,
|
|
98
107
|
})));
|
|
99
108
|
},
|
|
100
|
-
|
|
109
|
+
onProgress,
|
|
110
|
+
signal,
|
|
111
|
+
})));
|
|
101
112
|
observer.complete();
|
|
102
113
|
}, stopToken);
|
|
103
114
|
}
|
|
@@ -1,44 +1,86 @@
|
|
|
1
|
+
import type { Instance } from '@jbrowse/mobx-state-tree';
|
|
2
|
+
export declare function normalizeSnapshot(snap: Record<string, unknown>): Record<string, unknown>;
|
|
3
|
+
/**
|
|
4
|
+
* #config BedTabixAdapter
|
|
5
|
+
* #trackType FeatureTrack
|
|
6
|
+
* #fileFormat feature | BED (tabix)
|
|
7
|
+
*
|
|
8
|
+
* #example
|
|
9
|
+
* The `uri` shorthand auto-resolves the `.tbi` index; add `csi: true` for a
|
|
10
|
+
* `.csi` index instead:
|
|
11
|
+
* ```js
|
|
12
|
+
* {
|
|
13
|
+
* type: 'BedTabixAdapter',
|
|
14
|
+
* uri: 'https://example.com/features.bed.gz',
|
|
15
|
+
* }
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
1
18
|
declare const BedTabixAdapter: import("@jbrowse/core/configuration/configurationSchema").ConfigurationSchemaType<{
|
|
2
|
-
|
|
3
|
-
type:
|
|
4
|
-
defaultValue:
|
|
5
|
-
|
|
6
|
-
|
|
19
|
+
readonly densityAdapter: {
|
|
20
|
+
readonly type: 'frozen';
|
|
21
|
+
readonly defaultValue: null;
|
|
22
|
+
readonly description: 'optional quantitative sub-adapter (e.g. a BigWigAdapter over a features-per-bin bigWig) drawn as a density band where the region is too large to fetch features; null disables it';
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* #slot
|
|
26
|
+
* location of the bgzip-compressed BED, sorted by position. Must be bgzip
|
|
27
|
+
* rather than plain gzip, which tabix cannot index.
|
|
28
|
+
*/
|
|
29
|
+
readonly bedGzLocation: {
|
|
30
|
+
readonly type: "fileLocation";
|
|
31
|
+
readonly defaultValue: {
|
|
32
|
+
readonly uri: "/path/to/my.bed.gz";
|
|
33
|
+
readonly locationType: "UriLocation";
|
|
7
34
|
};
|
|
8
35
|
};
|
|
9
|
-
index: import("@jbrowse/core/configuration/configurationSchema").ConfigurationSchemaType<{
|
|
10
|
-
indexType: {
|
|
11
|
-
model: import("@jbrowse/mobx-state-tree").ISimpleType<"
|
|
12
|
-
type:
|
|
13
|
-
defaultValue:
|
|
36
|
+
readonly index: import("@jbrowse/core/configuration/configurationSchema").ConfigurationSchemaType<{
|
|
37
|
+
readonly indexType: {
|
|
38
|
+
readonly model: import("@jbrowse/mobx-state-tree").ISimpleType<"CSI" | "TBI">;
|
|
39
|
+
readonly type: 'stringEnum';
|
|
40
|
+
readonly defaultValue: 'TBI';
|
|
41
|
+
readonly description: '`TBI` is the usual `tabix` output. `CSI` is required for a reference longer than 512 Mb, which TBI cannot address.';
|
|
14
42
|
};
|
|
15
|
-
location: {
|
|
16
|
-
type:
|
|
17
|
-
defaultValue: {
|
|
18
|
-
uri:
|
|
19
|
-
locationType:
|
|
43
|
+
readonly location: {
|
|
44
|
+
readonly type: 'fileLocation';
|
|
45
|
+
readonly defaultValue: {
|
|
46
|
+
readonly uri: '/path/to/my.gz.tbi';
|
|
47
|
+
readonly locationType: 'UriLocation';
|
|
20
48
|
};
|
|
49
|
+
readonly description: 'location of the tabix index. Only needed when it is not named `<file>.tbi` (or `.csi`), which is what the `uri` shorthand assumes.';
|
|
21
50
|
};
|
|
22
51
|
}, import("@jbrowse/core/configuration/configurationSchema").ConfigurationSchemaOptions<undefined, undefined>>;
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
52
|
+
/**
|
|
53
|
+
* #slot
|
|
54
|
+
*/
|
|
55
|
+
readonly columnNames: {
|
|
56
|
+
readonly type: "stringArray";
|
|
57
|
+
readonly description: "List of column names. A column named like a standard BED column is parsed as that column's type (chromStart numeric, blockSizes a numeric list); any other column is text";
|
|
58
|
+
readonly defaultValue: readonly [];
|
|
27
59
|
};
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
60
|
+
/**
|
|
61
|
+
* #slot
|
|
62
|
+
*/
|
|
63
|
+
readonly scoreColumn: {
|
|
64
|
+
readonly type: "string";
|
|
65
|
+
readonly description: "The column to use as a \"score\" attribute";
|
|
66
|
+
readonly defaultValue: "";
|
|
32
67
|
};
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
68
|
+
/**
|
|
69
|
+
* #slot
|
|
70
|
+
*/
|
|
71
|
+
readonly autoSql: {
|
|
72
|
+
readonly type: "string";
|
|
73
|
+
readonly description: "The autoSql definition for the data fields in the file";
|
|
74
|
+
readonly defaultValue: "";
|
|
37
75
|
};
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
76
|
+
/**
|
|
77
|
+
* #slot
|
|
78
|
+
*/
|
|
79
|
+
readonly disableGeneHeuristic: {
|
|
80
|
+
readonly type: "boolean";
|
|
81
|
+
readonly description: "Disable the heuristic that auto-detects BED12 features as gene/transcript structures. Useful for files that have BED12-like structure but are not genes (e.g. tandem duplications)";
|
|
82
|
+
readonly defaultValue: false;
|
|
42
83
|
};
|
|
43
84
|
}, import("@jbrowse/core/configuration/configurationSchema").ConfigurationSchemaOptions<undefined, undefined>>;
|
|
85
|
+
export type BedTabixAdapterConfig = Instance<typeof BedTabixAdapter>;
|
|
44
86
|
export default BedTabixAdapter;
|
|
@@ -1,7 +1,29 @@
|
|
|
1
|
-
import { ConfigurationSchema } from '@jbrowse/core/configuration';
|
|
2
|
-
import {
|
|
3
|
-
function
|
|
1
|
+
import { ConfigurationSchema, expandTabixShorthand, tabixIndexFields, } from '@jbrowse/core/configuration';
|
|
2
|
+
import { densityAdapterConfigSchemaFields } from '@jbrowse/core/data_adapters/BaseAdapter';
|
|
3
|
+
export function normalizeSnapshot(snap) {
|
|
4
|
+
return expandTabixShorthand(snap, 'bedGzLocation');
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* #config BedTabixAdapter
|
|
8
|
+
* #trackType FeatureTrack
|
|
9
|
+
* #fileFormat feature | BED (tabix)
|
|
10
|
+
*
|
|
11
|
+
* #example
|
|
12
|
+
* The `uri` shorthand auto-resolves the `.tbi` index; add `csi: true` for a
|
|
13
|
+
* `.csi` index instead:
|
|
14
|
+
* ```js
|
|
15
|
+
* {
|
|
16
|
+
* type: 'BedTabixAdapter',
|
|
17
|
+
* uri: 'https://example.com/features.bed.gz',
|
|
18
|
+
* }
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
4
21
|
const BedTabixAdapter = ConfigurationSchema('BedTabixAdapter', {
|
|
22
|
+
/**
|
|
23
|
+
* #slot
|
|
24
|
+
* location of the bgzip-compressed BED, sorted by position. Must be bgzip
|
|
25
|
+
* rather than plain gzip, which tabix cannot index.
|
|
26
|
+
*/
|
|
5
27
|
bedGzLocation: {
|
|
6
28
|
type: 'fileLocation',
|
|
7
29
|
defaultValue: {
|
|
@@ -9,58 +31,54 @@ const BedTabixAdapter = ConfigurationSchema('BedTabixAdapter', {
|
|
|
9
31
|
locationType: 'UriLocation',
|
|
10
32
|
},
|
|
11
33
|
},
|
|
12
|
-
index: ConfigurationSchema('TabixIndex', {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
defaultValue: 'TBI',
|
|
17
|
-
},
|
|
18
|
-
location: {
|
|
19
|
-
type: 'fileLocation',
|
|
20
|
-
defaultValue: {
|
|
21
|
-
uri: '/path/to/my.bed.gz.tbi',
|
|
22
|
-
locationType: 'UriLocation',
|
|
23
|
-
},
|
|
24
|
-
},
|
|
25
|
-
}),
|
|
34
|
+
index: ConfigurationSchema('TabixIndex', { ...tabixIndexFields }),
|
|
35
|
+
/**
|
|
36
|
+
* #slot
|
|
37
|
+
*/
|
|
26
38
|
columnNames: {
|
|
27
39
|
type: 'stringArray',
|
|
28
|
-
description:
|
|
40
|
+
description: "List of column names. A column named like a standard BED column is parsed as that column's type (chromStart numeric, blockSizes a numeric list); any other column is text",
|
|
29
41
|
defaultValue: [],
|
|
30
42
|
},
|
|
43
|
+
/**
|
|
44
|
+
* #slot
|
|
45
|
+
*/
|
|
31
46
|
scoreColumn: {
|
|
32
47
|
type: 'string',
|
|
33
48
|
description: 'The column to use as a "score" attribute',
|
|
34
49
|
defaultValue: '',
|
|
35
50
|
},
|
|
51
|
+
/**
|
|
52
|
+
* #slot
|
|
53
|
+
*/
|
|
36
54
|
autoSql: {
|
|
37
55
|
type: 'string',
|
|
38
56
|
description: 'The autoSql definition for the data fields in the file',
|
|
39
57
|
defaultValue: '',
|
|
40
58
|
},
|
|
59
|
+
/**
|
|
60
|
+
* #slot
|
|
61
|
+
*/
|
|
41
62
|
disableGeneHeuristic: {
|
|
42
63
|
type: 'boolean',
|
|
43
64
|
description: 'Disable the heuristic that auto-detects BED12 features as gene/transcript structures. Useful for files that have BED12-like structure but are not genes (e.g. tandem duplications)',
|
|
44
65
|
defaultValue: false,
|
|
45
66
|
},
|
|
67
|
+
...densityAdapterConfigSchemaFields,
|
|
46
68
|
}, {
|
|
47
69
|
explicitlyTyped: true,
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
},
|
|
62
|
-
}
|
|
63
|
-
: snap;
|
|
64
|
-
},
|
|
70
|
+
/**
|
|
71
|
+
* #preProcessSnapshot
|
|
72
|
+
*
|
|
73
|
+
*
|
|
74
|
+
* preprocessor to allow minimal config, assumes yourfile.bed.gz.tbi:
|
|
75
|
+
* ```json
|
|
76
|
+
* {
|
|
77
|
+
* "type": "BedTabixAdapter",
|
|
78
|
+
* "uri": "yourfile.bed.gz"
|
|
79
|
+
* }
|
|
80
|
+
* ```
|
|
81
|
+
*/
|
|
82
|
+
preProcessSnapshot: normalizeSnapshot,
|
|
65
83
|
});
|
|
66
84
|
export default BedTabixAdapter;
|
|
@@ -1,10 +1,12 @@
|
|
|
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 BedTabixAdapterF(pluginManager) {
|
|
4
4
|
pluginManager.addAdapterType(() => new AdapterType({
|
|
5
5
|
name: 'BedTabixAdapter',
|
|
6
6
|
displayName: 'BED tabix adapter',
|
|
7
7
|
configSchema,
|
|
8
|
-
getAdapterClass: () => import(
|
|
8
|
+
getAdapterClass: () => import('./BedTabixAdapter.js').then(r => r.default),
|
|
9
|
+
locationKey: 'bedGzLocation',
|
|
10
|
+
normalizeSnapshot,
|
|
9
11
|
}));
|
|
10
12
|
}
|
|
@@ -1,18 +1,12 @@
|
|
|
1
1
|
import { BaseFeatureDataAdapter } from '@jbrowse/core/data_adapters/BaseAdapter';
|
|
2
|
-
import {
|
|
2
|
+
import type { BedpeAdapterConfig } from './configSchema.ts';
|
|
3
3
|
import type { BaseOptions } from '@jbrowse/core/data_adapters/BaseAdapter';
|
|
4
|
-
import type { Feature, Region } from '@jbrowse/core/util';
|
|
5
|
-
export default class BedpeAdapter extends BaseFeatureDataAdapter {
|
|
6
|
-
|
|
7
|
-
header: string;
|
|
8
|
-
feats1: Record<string, string[]>;
|
|
9
|
-
feats2: Record<string, string[]>;
|
|
10
|
-
columnNames: string[];
|
|
11
|
-
}>;
|
|
4
|
+
import type { Feature, IntervalTree, Region } from '@jbrowse/core/util';
|
|
5
|
+
export default class BedpeAdapter extends BaseFeatureDataAdapter<BedpeAdapterConfig> {
|
|
6
|
+
private loadData;
|
|
12
7
|
protected intervalTrees: Record<string, Promise<IntervalTree<Feature> | undefined> | undefined>;
|
|
13
8
|
static capabilities: string[];
|
|
14
9
|
private loadDataP;
|
|
15
|
-
private loadData;
|
|
16
10
|
getRefNames(opts?: BaseOptions): Promise<string[]>;
|
|
17
11
|
getHeader(opts?: BaseOptions): Promise<string>;
|
|
18
12
|
getNames(): Promise<string[] | undefined>;
|
|
@@ -1,54 +1,39 @@
|
|
|
1
|
-
import { BaseFeatureDataAdapter } from '@jbrowse/core/data_adapters/BaseAdapter';
|
|
2
|
-
import {
|
|
1
|
+
import { BaseFeatureDataAdapter, cachedSetup, } from '@jbrowse/core/data_adapters/BaseAdapter';
|
|
2
|
+
import { fetchAndMaybeUnzip } from '@jbrowse/core/util';
|
|
3
3
|
import { openLocation } from '@jbrowse/core/util/io';
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
4
|
+
import { parseLineByLine } from '@jbrowse/core/util/parseLineByLine';
|
|
5
|
+
import { buildPairedIntervalTree, intervalTreeFeatures, } from '../adapterUtil.js';
|
|
6
|
+
import { resolveColumnNames } from '../util.js';
|
|
7
|
+
import { featureData } from './util.js';
|
|
6
8
|
export default class BedpeAdapter extends BaseFeatureDataAdapter {
|
|
7
|
-
|
|
9
|
+
// No `label`: `fetchAndMaybeUnzip` narrates the download from inside.
|
|
10
|
+
loadData = cachedSetup({ setup: opts => this.loadDataP(opts) });
|
|
8
11
|
intervalTrees = {};
|
|
9
12
|
static capabilities = ['getFeatures', 'getRefNames'];
|
|
10
13
|
async loadDataP(opts) {
|
|
11
|
-
const
|
|
12
|
-
const lines = data.split(/\n|\r\n|\r/).filter(f => !!f);
|
|
14
|
+
const buffer = await fetchAndMaybeUnzip(openLocation(this.getConf('bedpeLocation'), this.pluginManager), opts);
|
|
13
15
|
const headerLines = [];
|
|
14
|
-
let i = 0;
|
|
15
|
-
for (; i < lines.length && lines[i].startsWith('#'); i++) {
|
|
16
|
-
headerLines.push(lines[i]);
|
|
17
|
-
}
|
|
18
|
-
const header = headerLines.join('\n');
|
|
19
16
|
const feats1 = {};
|
|
20
17
|
const feats2 = {};
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
const r1 = cols[0];
|
|
25
|
-
const r2 = cols[3];
|
|
26
|
-
if (!feats1[r1]) {
|
|
27
|
-
feats1[r1] = [];
|
|
18
|
+
parseLineByLine(buffer, line => {
|
|
19
|
+
if (line.startsWith('#')) {
|
|
20
|
+
headerLines.push(line);
|
|
28
21
|
}
|
|
29
|
-
if (!
|
|
30
|
-
|
|
22
|
+
else if (!line.startsWith('track') && !line.startsWith('browser')) {
|
|
23
|
+
const cols = line.split('\t');
|
|
24
|
+
const r1 = cols[0];
|
|
25
|
+
const r2 = cols[3];
|
|
26
|
+
(feats1[r1] ??= []).push(line);
|
|
27
|
+
(feats2[r2] ??= []).push(line);
|
|
31
28
|
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
}
|
|
35
|
-
const columnNames = this.getConf('columnNames');
|
|
29
|
+
return true;
|
|
30
|
+
}, opts?.statusCallback);
|
|
36
31
|
return {
|
|
37
|
-
header,
|
|
32
|
+
header: headerLines.join('\n'),
|
|
38
33
|
feats1,
|
|
39
34
|
feats2,
|
|
40
|
-
columnNames,
|
|
41
35
|
};
|
|
42
36
|
}
|
|
43
|
-
async loadData(opts = {}) {
|
|
44
|
-
if (!this.bedpeFeatures) {
|
|
45
|
-
this.bedpeFeatures = this.loadDataP(opts).catch((e) => {
|
|
46
|
-
this.bedpeFeatures = undefined;
|
|
47
|
-
throw e;
|
|
48
|
-
});
|
|
49
|
-
}
|
|
50
|
-
return this.bedpeFeatures;
|
|
51
|
-
}
|
|
52
37
|
async getRefNames(opts = {}) {
|
|
53
38
|
const { feats1, feats2 } = await this.loadData(opts);
|
|
54
39
|
return [...new Set([...Object.keys(feats1), ...Object.keys(feats2)])];
|
|
@@ -58,47 +43,21 @@ export default class BedpeAdapter extends BaseFeatureDataAdapter {
|
|
|
58
43
|
return header;
|
|
59
44
|
}
|
|
60
45
|
async getNames() {
|
|
61
|
-
|
|
62
|
-
if (columnNames.length) {
|
|
63
|
-
return columnNames;
|
|
64
|
-
}
|
|
65
|
-
const defs = header.split(/\n|\r\n|\r/).filter(f => !!f);
|
|
66
|
-
const defline = defs.at(-1);
|
|
67
|
-
return defline?.includes('\t')
|
|
68
|
-
? defline
|
|
69
|
-
.slice(1)
|
|
70
|
-
.split('\t')
|
|
71
|
-
.map(field => field.trim())
|
|
72
|
-
: undefined;
|
|
46
|
+
return resolveColumnNames(this.getConf('columnNames'), async () => (await this.loadData()).header);
|
|
73
47
|
}
|
|
74
48
|
async loadFeatureTreeP(refName) {
|
|
75
49
|
const { feats1, feats2 } = await this.loadData();
|
|
76
50
|
const names = await this.getNames();
|
|
77
|
-
|
|
78
|
-
const ret1 = feats1[refName]?.map((f, i) => featureData(f, `${this.id}-${refName}-${i}-r1`, false, names)) ?? [];
|
|
79
|
-
const ret2 = feats2[refName]?.map((f, i) => featureData(f, `${this.id}-${refName}-${i}-r2`, true, names)) ?? [];
|
|
80
|
-
for (const obj of [...ret1, ...ret2]) {
|
|
81
|
-
intervalTree.insert([obj.get('start'), obj.get('end')], obj);
|
|
82
|
-
}
|
|
83
|
-
return intervalTree;
|
|
51
|
+
return buildPairedIntervalTree(feats1, feats2, refName, this.id, (line, uniqueId, flip) => featureData(line, uniqueId, flip, names));
|
|
84
52
|
}
|
|
85
53
|
async loadFeatureTree(refName) {
|
|
86
|
-
|
|
87
|
-
this.intervalTrees[refName] =
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
});
|
|
91
|
-
}
|
|
54
|
+
this.intervalTrees[refName] ??= this.loadFeatureTreeP(refName).catch((e) => {
|
|
55
|
+
this.intervalTrees[refName] = undefined;
|
|
56
|
+
throw e;
|
|
57
|
+
});
|
|
92
58
|
return this.intervalTrees[refName];
|
|
93
59
|
}
|
|
94
60
|
getFeatures(query, opts = {}) {
|
|
95
|
-
return
|
|
96
|
-
const { start, end, refName } = query;
|
|
97
|
-
const intervalTree = await this.loadFeatureTree(refName);
|
|
98
|
-
for (const f of intervalTree?.search([start, end]) || []) {
|
|
99
|
-
observer.next(f);
|
|
100
|
-
}
|
|
101
|
-
observer.complete();
|
|
102
|
-
}, opts.stopToken);
|
|
61
|
+
return intervalTreeFeatures(query, opts, refName => this.loadFeatureTree(refName));
|
|
103
62
|
}
|
|
104
63
|
}
|
|
@@ -1,16 +1,39 @@
|
|
|
1
|
+
import type { Instance } from '@jbrowse/mobx-state-tree';
|
|
2
|
+
export declare function normalizeSnapshot(snap: Record<string, unknown>): Record<string, unknown>;
|
|
3
|
+
/**
|
|
4
|
+
* #config BedpeAdapter
|
|
5
|
+
* #trackType VariantTrack
|
|
6
|
+
* #fileFormat variants | BEDPE | Paired/breakend records, e.g. SV calls or Hi-C loops
|
|
7
|
+
* intended for SVs in a single assembly
|
|
8
|
+
*
|
|
9
|
+
* #example
|
|
10
|
+
* ```js
|
|
11
|
+
* {
|
|
12
|
+
* type: 'BedpeAdapter',
|
|
13
|
+
* uri: 'https://example.com/sv.bedpe',
|
|
14
|
+
* }
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
1
17
|
declare const BedpeAdapter: import("@jbrowse/core/configuration/configurationSchema").ConfigurationSchemaType<{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
18
|
+
/**
|
|
19
|
+
* #slot
|
|
20
|
+
*/
|
|
21
|
+
readonly bedpeLocation: {
|
|
22
|
+
readonly type: "fileLocation";
|
|
23
|
+
readonly description: "can be plaintext or gzipped, not indexed so loaded into memory on startup";
|
|
24
|
+
readonly defaultValue: {
|
|
25
|
+
readonly uri: "/path/to/my.bedpe.gz";
|
|
26
|
+
readonly locationType: "UriLocation";
|
|
8
27
|
};
|
|
9
28
|
};
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
29
|
+
/**
|
|
30
|
+
* #slot
|
|
31
|
+
*/
|
|
32
|
+
readonly columnNames: {
|
|
33
|
+
readonly type: "stringArray";
|
|
34
|
+
readonly description: "List of column names";
|
|
35
|
+
readonly defaultValue: readonly [];
|
|
14
36
|
};
|
|
15
37
|
}, import("@jbrowse/core/configuration/configurationSchema").ConfigurationSchemaOptions<undefined, undefined>>;
|
|
38
|
+
export type BedpeAdapterConfig = Instance<typeof BedpeAdapter>;
|
|
16
39
|
export default BedpeAdapter;
|