@jbrowse/plugin-bed 4.3.0 → 5.0.0-beta.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/esm/BedAdapter/BedAdapter.d.ts +5 -22
- package/esm/BedAdapter/BedAdapter.js +29 -93
- package/esm/BedAdapter/configSchema.d.ts +59 -32
- package/esm/BedAdapter/configSchema.js +64 -14
- package/esm/BedAdapter/index.js +4 -2
- package/esm/BedGraphAdapter/BedGraphAdapter.d.ts +5 -12
- package/esm/BedGraphAdapter/BedGraphAdapter.js +33 -89
- package/esm/BedGraphAdapter/configSchema.d.ts +35 -11
- package/esm/BedGraphAdapter/configSchema.js +43 -13
- package/esm/BedGraphAdapter/index.js +3 -2
- package/esm/BedGraphTabixAdapter/BedGraphTabixAdapter.d.ts +5 -5
- package/esm/BedGraphTabixAdapter/BedGraphTabixAdapter.js +65 -57
- package/esm/BedGraphTabixAdapter/configSchema.d.ts +46 -22
- package/esm/BedGraphTabixAdapter/configSchema.js +50 -34
- package/esm/BedGraphTabixAdapter/index.js +3 -2
- package/esm/BedTabixAdapter/BedTabixAdapter.d.ts +34 -38
- package/esm/BedTabixAdapter/BedTabixAdapter.js +82 -72
- package/esm/BedTabixAdapter/configSchema.d.ts +55 -31
- package/esm/BedTabixAdapter/configSchema.js +58 -35
- package/esm/BedTabixAdapter/index.js +4 -2
- package/esm/BedpeAdapter/BedpeAdapter.d.ts +6 -13
- package/esm/BedpeAdapter/BedpeAdapter.js +26 -74
- package/esm/BedpeAdapter/configSchema.d.ts +31 -12
- package/esm/BedpeAdapter/configSchema.js +37 -13
- package/esm/BedpeAdapter/index.js +4 -2
- package/esm/BedpeAdapter/util.js +25 -25
- package/esm/BigBedAdapter/BigBedAdapter.d.ts +8 -7
- package/esm/BigBedAdapter/BigBedAdapter.js +99 -107
- package/esm/BigBedAdapter/configSchema.d.ts +38 -16
- package/esm/BigBedAdapter/configSchema.js +46 -13
- package/esm/BigBedAdapter/index.js +3 -2
- package/esm/BigBedTextSearchAdapter/BigBedTextSearchAdapter.d.ts +27 -0
- package/esm/BigBedTextSearchAdapter/BigBedTextSearchAdapter.js +128 -0
- package/esm/BigBedTextSearchAdapter/configSchema.d.ts +51 -0
- package/esm/BigBedTextSearchAdapter/configSchema.js +57 -0
- package/esm/BigBedTextSearchAdapter/index.d.ts +2 -0
- package/esm/BigBedTextSearchAdapter/index.js +11 -0
- package/esm/StarFusionAdapter/StarFusionAdapter.d.ts +14 -0
- package/esm/StarFusionAdapter/StarFusionAdapter.js +66 -0
- package/esm/StarFusionAdapter/configSchema.d.ts +30 -0
- package/esm/StarFusionAdapter/configSchema.js +43 -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 +43 -0
- package/esm/bedGraphUtil.d.ts +11 -0
- package/esm/bedGraphUtil.js +20 -0
- package/esm/copyExcept.d.ts +1 -0
- package/esm/copyExcept.js +11 -0
- package/esm/generateBedMethylFeature.d.ts +1 -1
- package/esm/generateBedMethylFeature.js +24 -6
- package/esm/generateRastairMethylFeature.d.ts +28 -0
- package/esm/generateRastairMethylFeature.js +38 -0
- package/esm/generateRepeatMaskerFeature.d.ts +17 -51
- package/esm/generateRepeatMaskerFeature.js +27 -37
- package/esm/generateUcscTranscript.d.ts +15 -7
- package/esm/generateUcscTranscript.js +106 -29
- package/esm/index.d.ts +2 -0
- package/esm/index.js +12 -8
- package/esm/util.d.ts +32 -27
- package/esm/util.js +139 -98
- package/package.json +10 -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,27 @@
|
|
|
1
|
+
import { BigBed } from '@gmod/bbi';
|
|
2
|
+
import Trix from '@gmod/trix';
|
|
3
|
+
import BaseResult from '@jbrowse/core/TextSearch/BaseResults';
|
|
4
|
+
import { BaseAdapter } from '@jbrowse/core/data_adapters/BaseAdapter';
|
|
5
|
+
import type { BigBedTextSearchAdapterConfig } from './configSchema.ts';
|
|
6
|
+
import type PluginManager from '@jbrowse/core/PluginManager';
|
|
7
|
+
import type { BaseTextSearchAdapter, BaseTextSearchArgs } from '@jbrowse/core/data_adapters/BaseAdapter';
|
|
8
|
+
import type { getSubAdapterType } from '@jbrowse/core/data_adapters/dataAdapterCache';
|
|
9
|
+
interface Hit {
|
|
10
|
+
label: string;
|
|
11
|
+
refName: string;
|
|
12
|
+
start: number;
|
|
13
|
+
end: number;
|
|
14
|
+
}
|
|
15
|
+
export declare function mergeTranscripts(hits: Hit[]): Hit[];
|
|
16
|
+
export default class BigBedTextSearchAdapter extends BaseAdapter<BigBedTextSearchAdapterConfig> implements BaseTextSearchAdapter {
|
|
17
|
+
bigbed: BigBed;
|
|
18
|
+
trix?: Trix;
|
|
19
|
+
constructor(config: BigBedTextSearchAdapterConfig, getSubAdapter?: getSubAdapterType, pluginManager?: PluginManager);
|
|
20
|
+
names(query: string, exactOnly: boolean, signal?: AbortSignal): Promise<{
|
|
21
|
+
exact: string[];
|
|
22
|
+
prefixed: (readonly [string, string])[];
|
|
23
|
+
}>;
|
|
24
|
+
lookUp(name: string, word: string, signal?: AbortSignal): Promise<Hit[]>;
|
|
25
|
+
searchIndex({ queryString, searchType, signal }: BaseTextSearchArgs): Promise<BaseResult[]>;
|
|
26
|
+
}
|
|
27
|
+
export {};
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { BigBed } from '@gmod/bbi';
|
|
2
|
+
import Trix from '@gmod/trix';
|
|
3
|
+
import BaseResult from '@jbrowse/core/TextSearch/BaseResults';
|
|
4
|
+
import { BaseAdapter } from '@jbrowse/core/data_adapters/BaseAdapter';
|
|
5
|
+
import { checkAbortSignal } from '@jbrowse/core/util/aborting';
|
|
6
|
+
import { openLocation } from '@jbrowse/core/util/io';
|
|
7
|
+
// each name a query resolves costs a walk of the BigBed's B+ tree, so a query
|
|
8
|
+
// resolves every name its exact word carries up to one cap, and the words it
|
|
9
|
+
// only prefixes, each whole, while their names fit the other
|
|
10
|
+
const MAX_EXACT_NAMES = 50;
|
|
11
|
+
const MAX_PREFIX_NAMES = 50;
|
|
12
|
+
const MAX_TRIX_HITS = 500;
|
|
13
|
+
function isSet(location) {
|
|
14
|
+
return 'uri' in location
|
|
15
|
+
? !!location.uri
|
|
16
|
+
: 'localPath' in location
|
|
17
|
+
? !!location.localPath
|
|
18
|
+
: true;
|
|
19
|
+
}
|
|
20
|
+
// the column that matched is the name the hit goes by: a gene symbol for a
|
|
21
|
+
// search by symbol, the accession for a search by accession
|
|
22
|
+
function hitFor({ refName, start, end, rest = '', }, word) {
|
|
23
|
+
const fields = rest.split('\t');
|
|
24
|
+
const label = fields.find(f => f.toLowerCase() === word) ?? fields[0];
|
|
25
|
+
return refName && label ? { label, refName, start, end } : undefined;
|
|
26
|
+
}
|
|
27
|
+
// Transcripts sharing the name that matched and overlapping on one sequence
|
|
28
|
+
// are one gene, the rule BigBedAdapter draws them by, so a search for a gene
|
|
29
|
+
// lands on the gene rather than asking which of its transcripts
|
|
30
|
+
export function mergeTranscripts(hits) {
|
|
31
|
+
const merged = [];
|
|
32
|
+
const sorted = hits.toSorted((a, b) => a.label.localeCompare(b.label) ||
|
|
33
|
+
a.refName.localeCompare(b.refName) ||
|
|
34
|
+
a.start - b.start);
|
|
35
|
+
for (const hit of sorted) {
|
|
36
|
+
const last = merged.at(-1);
|
|
37
|
+
if (last?.label === hit.label &&
|
|
38
|
+
last.refName === hit.refName &&
|
|
39
|
+
hit.start <= last.end) {
|
|
40
|
+
last.end = Math.max(last.end, hit.end);
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
merged.push({ ...hit });
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return merged;
|
|
47
|
+
}
|
|
48
|
+
// A word is resolved whole or not at all, since a word missing some of its
|
|
49
|
+
// names would land on part of its gene
|
|
50
|
+
function prefixedNames(byWord, word) {
|
|
51
|
+
const pairs = [];
|
|
52
|
+
for (const [term, names] of byWord) {
|
|
53
|
+
if (term !== word && term.startsWith(word)) {
|
|
54
|
+
if (pairs.length + names.length > MAX_PREFIX_NAMES) {
|
|
55
|
+
break;
|
|
56
|
+
}
|
|
57
|
+
pairs.push(...names.map(name => [term, name]));
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return pairs;
|
|
61
|
+
}
|
|
62
|
+
export default class BigBedTextSearchAdapter extends BaseAdapter {
|
|
63
|
+
bigbed;
|
|
64
|
+
trix;
|
|
65
|
+
constructor(config, getSubAdapter, pluginManager) {
|
|
66
|
+
super(config, getSubAdapter, pluginManager);
|
|
67
|
+
this.bigbed = new BigBed({
|
|
68
|
+
filehandle: openLocation(this.getConf('bigBedLocation'), pluginManager),
|
|
69
|
+
});
|
|
70
|
+
const ix = this.getConf('ixFilePath');
|
|
71
|
+
const ixx = this.getConf('ixxFilePath');
|
|
72
|
+
if (isSet(ix) && isSet(ixx)) {
|
|
73
|
+
this.trix = new Trix(openLocation(ixx, pluginManager), openLocation(ix, pluginManager), MAX_TRIX_HITS);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
// The names to look up: the query as spelled, which the extra index answers
|
|
77
|
+
// however the trix split it into words, then the names the trix finds for
|
|
78
|
+
// its word. GenArk's trix drops an accession's version, so a versioned query
|
|
79
|
+
// asks for the bare word and keeps the names spelling the query
|
|
80
|
+
async names(query, exactOnly, signal) {
|
|
81
|
+
const word = query.toLowerCase();
|
|
82
|
+
if (!this.trix) {
|
|
83
|
+
return { exact: [query], prefixed: [] };
|
|
84
|
+
}
|
|
85
|
+
const trixWord = word.replace(/\.\d+$/, '');
|
|
86
|
+
const byWord = new Map();
|
|
87
|
+
for (const [term, name] of await this.trix.search(trixWord, { signal })) {
|
|
88
|
+
const names = byWord.get(term);
|
|
89
|
+
if (names) {
|
|
90
|
+
names.push(name);
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
byWord.set(term, [name]);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
const exact = trixWord === word
|
|
97
|
+
? (byWord.get(word) ?? [])
|
|
98
|
+
: (byWord.get(trixWord) ?? []).filter(n => n.toLowerCase() === word);
|
|
99
|
+
return {
|
|
100
|
+
exact: [...new Set([query, ...exact])].slice(0, MAX_EXACT_NAMES),
|
|
101
|
+
prefixed: exactOnly ? [] : prefixedNames(byWord, word),
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
async lookUp(name, word, signal) {
|
|
105
|
+
return (await this.bigbed.searchExtraIndex(name, { signal }))
|
|
106
|
+
.map(f => hitFor(f, word))
|
|
107
|
+
.filter(h => h !== undefined);
|
|
108
|
+
}
|
|
109
|
+
async searchIndex({ queryString, searchType, signal }) {
|
|
110
|
+
const query = queryString.trim();
|
|
111
|
+
const word = query.toLowerCase();
|
|
112
|
+
const { exact, prefixed } = await this.names(query, searchType === 'exact', signal);
|
|
113
|
+
const [exactHits, prefixHits] = await Promise.all([
|
|
114
|
+
Promise.all(exact.map(name => this.lookUp(name, word, signal))),
|
|
115
|
+
Promise.all(prefixed.map(([term, name]) => this.lookUp(name, term, signal))),
|
|
116
|
+
]);
|
|
117
|
+
checkAbortSignal(signal);
|
|
118
|
+
const results = (hits, exact) => mergeTranscripts(hits).map(({ label, refName, start, end }) => new BaseResult({
|
|
119
|
+
label,
|
|
120
|
+
locString: `${refName}:${start + 1}..${end}`,
|
|
121
|
+
exact,
|
|
122
|
+
}));
|
|
123
|
+
return [
|
|
124
|
+
...results(exactHits.flat(), true),
|
|
125
|
+
...results(prefixHits.flat(), false),
|
|
126
|
+
];
|
|
127
|
+
}
|
|
128
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { Instance } from '@jbrowse/mobx-state-tree';
|
|
2
|
+
/**
|
|
3
|
+
* #config BigBedTextSearchAdapter
|
|
4
|
+
* #trackType TextSearchAdapter
|
|
5
|
+
*
|
|
6
|
+
* Finds features by the names a BigBed was built to look up, its
|
|
7
|
+
* `bedToBigBed -extraIndex` columns, which a UCSC hub declares with
|
|
8
|
+
* `searchIndex`. The hub's `searchTrix` index, when given, adds prefix and
|
|
9
|
+
* case-insensitive matching. A UCSC track hub connection configures one for
|
|
10
|
+
* its first gene track declaring a `searchIndex`, GenArk's `ncbiRefSeq`.
|
|
11
|
+
*
|
|
12
|
+
* #example
|
|
13
|
+
* ```js
|
|
14
|
+
* {
|
|
15
|
+
* type: 'BigBedTextSearchAdapter',
|
|
16
|
+
* bigBedLocation: { uri: 'bbi/ncbiRefSeq.bb' },
|
|
17
|
+
* ixFilePath: { uri: 'ixIxx/ncbiRefSeq.ix' },
|
|
18
|
+
* ixxFilePath: { uri: 'ixIxx/ncbiRefSeq.ixx' },
|
|
19
|
+
* }
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
declare const BigBedTextSearchAdapter: import("@jbrowse/core/configuration/configurationSchema").ConfigurationSchemaType<{
|
|
23
|
+
type: {
|
|
24
|
+
type: "string";
|
|
25
|
+
} & {
|
|
26
|
+
defaultValue: string;
|
|
27
|
+
};
|
|
28
|
+
readonly bigBedLocation: {
|
|
29
|
+
type: "fileLocation";
|
|
30
|
+
} & {
|
|
31
|
+
defaultValue: unknown;
|
|
32
|
+
};
|
|
33
|
+
readonly ixFilePath: {
|
|
34
|
+
type: "fileLocation";
|
|
35
|
+
} & {
|
|
36
|
+
defaultValue: unknown;
|
|
37
|
+
};
|
|
38
|
+
readonly ixxFilePath: {
|
|
39
|
+
type: "fileLocation";
|
|
40
|
+
} & {
|
|
41
|
+
defaultValue: unknown;
|
|
42
|
+
};
|
|
43
|
+
readonly assemblyNames: {
|
|
44
|
+
type: "stringArray";
|
|
45
|
+
} & {
|
|
46
|
+
defaultValue: unknown;
|
|
47
|
+
};
|
|
48
|
+
}, import("@jbrowse/core/configuration/configurationSchema").ConfigurationSchemaOptions<undefined, undefined, true, import("@jbrowse/core/configuration").ConfigurationSchemaRequirement<Partial<Record<string, string[]>>, string>, any>>;
|
|
49
|
+
export interface BigBedTextSearchAdapterConfig extends Instance<typeof BigBedTextSearchAdapter> {
|
|
50
|
+
}
|
|
51
|
+
export default BigBedTextSearchAdapter;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { ConfigurationSchema } from '@jbrowse/core/configuration';
|
|
2
|
+
/**
|
|
3
|
+
* #config BigBedTextSearchAdapter
|
|
4
|
+
* #trackType TextSearchAdapter
|
|
5
|
+
*
|
|
6
|
+
* Finds features by the names a BigBed was built to look up, its
|
|
7
|
+
* `bedToBigBed -extraIndex` columns, which a UCSC hub declares with
|
|
8
|
+
* `searchIndex`. The hub's `searchTrix` index, when given, adds prefix and
|
|
9
|
+
* case-insensitive matching. A UCSC track hub connection configures one for
|
|
10
|
+
* its first gene track declaring a `searchIndex`, GenArk's `ncbiRefSeq`.
|
|
11
|
+
*
|
|
12
|
+
* #example
|
|
13
|
+
* ```js
|
|
14
|
+
* {
|
|
15
|
+
* type: 'BigBedTextSearchAdapter',
|
|
16
|
+
* bigBedLocation: { uri: 'bbi/ncbiRefSeq.bb' },
|
|
17
|
+
* ixFilePath: { uri: 'ixIxx/ncbiRefSeq.ix' },
|
|
18
|
+
* ixxFilePath: { uri: 'ixIxx/ncbiRefSeq.ixx' },
|
|
19
|
+
* }
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
const BigBedTextSearchAdapter = ConfigurationSchema('BigBedTextSearchAdapter', {
|
|
23
|
+
/**
|
|
24
|
+
* #slot
|
|
25
|
+
* the BigBed whose extra indexes resolve a name to its features
|
|
26
|
+
*/
|
|
27
|
+
bigBedLocation: {
|
|
28
|
+
type: 'fileLocation',
|
|
29
|
+
defaultValue: { uri: '/path/to/my.bb', locationType: 'UriLocation' },
|
|
30
|
+
},
|
|
31
|
+
/**
|
|
32
|
+
* #slot
|
|
33
|
+
* a UCSC `searchTrix` `.ix`, whose records are the names the extra
|
|
34
|
+
* indexes hold. Unset, a search matches a name exactly as typed
|
|
35
|
+
*/
|
|
36
|
+
ixFilePath: {
|
|
37
|
+
type: 'fileLocation',
|
|
38
|
+
defaultValue: { uri: '', locationType: 'UriLocation' },
|
|
39
|
+
},
|
|
40
|
+
/**
|
|
41
|
+
* #slot
|
|
42
|
+
* the `.ixx` beside `ixFilePath`
|
|
43
|
+
*/
|
|
44
|
+
ixxFilePath: {
|
|
45
|
+
type: 'fileLocation',
|
|
46
|
+
defaultValue: { uri: '', locationType: 'UriLocation' },
|
|
47
|
+
},
|
|
48
|
+
/**
|
|
49
|
+
* #slot
|
|
50
|
+
*/
|
|
51
|
+
assemblyNames: {
|
|
52
|
+
type: 'stringArray',
|
|
53
|
+
defaultValue: [],
|
|
54
|
+
description: 'List of assemblies covered by text search adapter',
|
|
55
|
+
},
|
|
56
|
+
}, { explicitlyTyped: true });
|
|
57
|
+
export default BigBedTextSearchAdapter;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import TextSearchAdapterType from '@jbrowse/core/pluggableElementTypes/TextSearchAdapterType';
|
|
2
|
+
import configSchema from './configSchema.js';
|
|
3
|
+
export default function BigBedTextSearchAdapterF(pluginManager) {
|
|
4
|
+
pluginManager.addTextSearchAdapterType(() => new TextSearchAdapterType({
|
|
5
|
+
name: 'BigBedTextSearchAdapter',
|
|
6
|
+
displayName: 'BigBed text search adapter',
|
|
7
|
+
description: 'Searches a BigBed by its extra-indexed names',
|
|
8
|
+
configSchema,
|
|
9
|
+
getAdapterClass: () => import('./BigBedTextSearchAdapter.js').then(r => r.default),
|
|
10
|
+
}));
|
|
11
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
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, Region } from '@jbrowse/core/util';
|
|
5
|
+
export default class StarFusionAdapter extends BaseFeatureDataAdapter<StarFusionAdapterConfig> {
|
|
6
|
+
private loadData;
|
|
7
|
+
static capabilities: string[];
|
|
8
|
+
private loadDataP;
|
|
9
|
+
getRefNames(opts?: BaseOptions): Promise<string[]>;
|
|
10
|
+
private featureFromLine;
|
|
11
|
+
private loadFeatureTreeP;
|
|
12
|
+
private treeFeatures;
|
|
13
|
+
getFeatures(query: Region, opts?: BaseOptions): import("rxjs").Observable<Feature>;
|
|
14
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
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 { parseStarFusionBreakpoint, starFusionColumns, } from '@jbrowse/core/util/starFusion';
|
|
6
|
+
import { buildPairedIntervalTree, intervalTreeFeatures, } from '../adapterUtil.js';
|
|
7
|
+
export default class StarFusionAdapter extends BaseFeatureDataAdapter {
|
|
8
|
+
loadData = cachedSetup({ setup: opts => this.loadDataP(opts) });
|
|
9
|
+
static capabilities = ['getFeatures', 'getRefNames'];
|
|
10
|
+
async loadDataP(opts) {
|
|
11
|
+
const buffer = await fetchAndMaybeUnzip(openLocation(this.getConf('starFusionLocation'), this.pluginManager), opts);
|
|
12
|
+
let columnNames = [];
|
|
13
|
+
let leftIdx = -1;
|
|
14
|
+
let rightIdx = -1;
|
|
15
|
+
const feats1 = {};
|
|
16
|
+
const feats2 = {};
|
|
17
|
+
parseLineByLine(buffer, (line, lineIndex) => {
|
|
18
|
+
if (lineIndex === 0) {
|
|
19
|
+
columnNames = starFusionColumns(line);
|
|
20
|
+
leftIdx = columnNames.indexOf('LeftBreakpoint');
|
|
21
|
+
rightIdx = columnNames.indexOf('RightBreakpoint');
|
|
22
|
+
}
|
|
23
|
+
else if (!line.startsWith('#')) {
|
|
24
|
+
const cols = line.split('\t');
|
|
25
|
+
const left = cols[leftIdx];
|
|
26
|
+
const right = cols[rightIdx];
|
|
27
|
+
if (left && right) {
|
|
28
|
+
const leftRef = parseStarFusionBreakpoint(left, true).refName;
|
|
29
|
+
const rightRef = parseStarFusionBreakpoint(right, false).refName;
|
|
30
|
+
(feats1[leftRef] ??= []).push(line);
|
|
31
|
+
(feats2[rightRef] ??= []).push(line);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return true;
|
|
35
|
+
}, opts?.statusCallback);
|
|
36
|
+
return { columnNames, feats1, feats2 };
|
|
37
|
+
}
|
|
38
|
+
async getRefNames(opts = {}) {
|
|
39
|
+
const { feats1, feats2 } = await this.loadData(opts);
|
|
40
|
+
return [...new Set([...Object.keys(feats1), ...Object.keys(feats2)])];
|
|
41
|
+
}
|
|
42
|
+
featureFromLine(line, columnNames, uniqueId, flip) {
|
|
43
|
+
const cols = line.split('\t');
|
|
44
|
+
const row = Object.fromEntries(columnNames.map((name, i) => [name, cols[i]]));
|
|
45
|
+
const donor = parseStarFusionBreakpoint(row.LeftBreakpoint, true);
|
|
46
|
+
const acceptor = parseStarFusionBreakpoint(row.RightBreakpoint, false);
|
|
47
|
+
const [primary, mate] = flip ? [acceptor, donor] : [donor, acceptor];
|
|
48
|
+
return new SimpleFeature({
|
|
49
|
+
uniqueId,
|
|
50
|
+
...primary,
|
|
51
|
+
name: row.FusionName,
|
|
52
|
+
score: row.JunctionReadCount ? +row.JunctionReadCount : undefined,
|
|
53
|
+
type: 'fusion',
|
|
54
|
+
mate,
|
|
55
|
+
...row,
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
async loadFeatureTreeP(refName) {
|
|
59
|
+
const { columnNames, feats1, feats2 } = await this.loadData();
|
|
60
|
+
return buildPairedIntervalTree(feats1, feats2, refName, this.id, (line, uniqueId, flip) => this.featureFromLine(line, columnNames, uniqueId, flip));
|
|
61
|
+
}
|
|
62
|
+
treeFeatures = intervalTreeFeatures(opts => this.loadData(opts), refName => this.loadFeatureTreeP(refName));
|
|
63
|
+
getFeatures(query, opts) {
|
|
64
|
+
return this.treeFeatures(query, opts);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
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
|
+
type: {
|
|
19
|
+
type: "string";
|
|
20
|
+
} & {
|
|
21
|
+
defaultValue: string;
|
|
22
|
+
};
|
|
23
|
+
readonly starFusionLocation: {
|
|
24
|
+
type: "fileLocation";
|
|
25
|
+
} & {
|
|
26
|
+
defaultValue: unknown;
|
|
27
|
+
};
|
|
28
|
+
}, import("@jbrowse/core/configuration/configurationSchema").ConfigurationSchemaOptions<undefined, undefined, true, import("@jbrowse/core/configuration").ConfigurationSchemaRequirement<Partial<Record<string, string[]>>, string>, any>>;
|
|
29
|
+
export type StarFusionAdapterConfig = Instance<typeof StarFusionAdapter>;
|
|
30
|
+
export default StarFusionAdapter;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { ConfigurationSchema, expandUriShorthand, } from '@jbrowse/core/configuration';
|
|
2
|
+
export function normalizeSnapshot(snap) {
|
|
3
|
+
return expandUriShorthand(snap, 'starFusionLocation');
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* #config StarFusionAdapter
|
|
7
|
+
* #trackType VariantTrack
|
|
8
|
+
* #fileFormat variants | STAR-Fusion
|
|
9
|
+
* used to load STAR-Fusion `star-fusion.fusion_predictions.tsv` output
|
|
10
|
+
*
|
|
11
|
+
* #example
|
|
12
|
+
* ```js
|
|
13
|
+
* {
|
|
14
|
+
* type: 'StarFusionAdapter',
|
|
15
|
+
* uri: 'https://example.com/star-fusion.fusion_predictions.tsv',
|
|
16
|
+
* }
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
const StarFusionAdapter = ConfigurationSchema('StarFusionAdapter', {
|
|
20
|
+
/**
|
|
21
|
+
* #slot
|
|
22
|
+
*/
|
|
23
|
+
starFusionLocation: {
|
|
24
|
+
type: 'fileLocation',
|
|
25
|
+
description: 'STAR-Fusion TSV output file (plain text or gzipped)',
|
|
26
|
+
defaultValue: {
|
|
27
|
+
uri: '/path/to/star-fusion.fusion_predictions.tsv',
|
|
28
|
+
locationType: 'UriLocation',
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
}, {
|
|
32
|
+
explicitlyTyped: true,
|
|
33
|
+
/**
|
|
34
|
+
* #preProcessSnapshot
|
|
35
|
+
*
|
|
36
|
+
* Allows minimal config:
|
|
37
|
+
* ```json
|
|
38
|
+
* { "type": "StarFusionAdapter", "uri": "star-fusion.tsv" }
|
|
39
|
+
* ```
|
|
40
|
+
*/
|
|
41
|
+
preProcessSnapshot: normalizeSnapshot,
|
|
42
|
+
});
|
|
43
|
+
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(loadData: (opts: BaseOptions) => Promise<unknown>, buildTree: (refName: string) => Promise<IntervalTree<Feature> | undefined>): (query: Region, opts?: BaseOptions) => 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,43 @@
|
|
|
1
|
+
import { IntervalTree } from '@jbrowse/core/util';
|
|
2
|
+
import { ObservableCreate } from '@jbrowse/core/util/rxjs';
|
|
3
|
+
// `getFeatures` for an in-memory interval-tree adapter: one tree per refName,
|
|
4
|
+
// built once and rebuilt after a failure. `loadData` is awaited with each
|
|
5
|
+
// caller's opts ahead of the tree, so every fetch joins the whole-file load's
|
|
6
|
+
// progress rather than only the one that started it.
|
|
7
|
+
export function intervalTreeFeatures(loadData, buildTree) {
|
|
8
|
+
const trees = new Map();
|
|
9
|
+
const loadTree = (refName) => {
|
|
10
|
+
let tree = trees.get(refName);
|
|
11
|
+
if (tree === undefined) {
|
|
12
|
+
tree = buildTree(refName).catch((e) => {
|
|
13
|
+
trees.delete(refName);
|
|
14
|
+
throw e;
|
|
15
|
+
});
|
|
16
|
+
trees.set(refName, tree);
|
|
17
|
+
}
|
|
18
|
+
return tree;
|
|
19
|
+
};
|
|
20
|
+
return (query, opts = {}) => ObservableCreate(async (observer) => {
|
|
21
|
+
await loadData(opts);
|
|
22
|
+
const tree = await loadTree(query.refName);
|
|
23
|
+
for (const f of tree?.search([query.start, query.end]) ?? []) {
|
|
24
|
+
observer.next(f);
|
|
25
|
+
}
|
|
26
|
+
observer.complete();
|
|
27
|
+
}, opts.signal);
|
|
28
|
+
}
|
|
29
|
+
// Build one interval tree for a refName from paired-feature buckets, inserting
|
|
30
|
+
// the records that start on this ref (feats1, flip=false) and those whose mate
|
|
31
|
+
// starts here (feats2, flip=true). Shared by BedpeAdapter and StarFusionAdapter.
|
|
32
|
+
export function buildPairedIntervalTree(feats1, feats2, refName, idPrefix, makeFeature) {
|
|
33
|
+
const tree = new IntervalTree();
|
|
34
|
+
for (const [i, line] of (feats1[refName] ?? []).entries()) {
|
|
35
|
+
const f = makeFeature(line, `${idPrefix}-${refName}-${i}-r1`, false);
|
|
36
|
+
tree.insert([f.get('start'), f.get('end')], f);
|
|
37
|
+
}
|
|
38
|
+
for (const [i, line] of (feats2[refName] ?? []).entries()) {
|
|
39
|
+
const f = makeFeature(line, `${idPrefix}-${refName}-${i}-r2`, true);
|
|
40
|
+
tree.insert([f.get('start'), f.get('end')], f);
|
|
41
|
+
}
|
|
42
|
+
return tree;
|
|
43
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Feature } from '@jbrowse/core/util';
|
|
2
|
+
export declare function makeBedGraphFeature({ uniqueId, refName, start, end, names, j, value, rowSource, }: {
|
|
3
|
+
uniqueId: string;
|
|
4
|
+
refName: string;
|
|
5
|
+
start: number;
|
|
6
|
+
end: number;
|
|
7
|
+
names: string[];
|
|
8
|
+
j: number;
|
|
9
|
+
value: string;
|
|
10
|
+
rowSource?: string;
|
|
11
|
+
}): Feature | undefined;
|
|
@@ -0,0 +1,20 @@
|
|
|
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. A wide file names each value column's source in
|
|
4
|
+
// its header; a tidy one has a `source` column naming each row's instead.
|
|
5
|
+
// Shared by BedGraphAdapter and BedGraphTabixAdapter.
|
|
6
|
+
export function makeBedGraphFeature({ uniqueId, refName, start, end, names, j, value, rowSource, }) {
|
|
7
|
+
const score = +value;
|
|
8
|
+
return Number.isNaN(score)
|
|
9
|
+
? undefined
|
|
10
|
+
: new SimpleFeature({
|
|
11
|
+
id: uniqueId,
|
|
12
|
+
data: {
|
|
13
|
+
refName,
|
|
14
|
+
start,
|
|
15
|
+
end,
|
|
16
|
+
score,
|
|
17
|
+
source: rowSource ?? (names[j] || `col${j}`),
|
|
18
|
+
},
|
|
19
|
+
});
|
|
20
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function copyExcept(data: Record<string, unknown>, excluded: ReadonlySet<string>): Record<string, unknown>;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// `const { a, b, ...rest } = data` in the same key order, which measured
|
|
2
|
+
// slower on every BED line shape (benches/featureData.bench.ts)
|
|
3
|
+
export function copyExcept(data, excluded) {
|
|
4
|
+
const out = {};
|
|
5
|
+
for (const key in data) {
|
|
6
|
+
if (!excluded.has(key)) {
|
|
7
|
+
out[key] = data[key];
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
return out;
|
|
11
|
+
}
|
|
@@ -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,37 @@
|
|
|
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
|
+
if (splitLine.length < 18 ||
|
|
5
|
+
+splitLine[6] !== start ||
|
|
6
|
+
+splitLine[7] !== end) {
|
|
7
|
+
return false;
|
|
8
|
+
}
|
|
9
|
+
// columns 9-17 are the nine numeric methylation stats
|
|
10
|
+
for (let i = 9; i < 18; i++) {
|
|
11
|
+
const x = splitLine[i];
|
|
12
|
+
if (!x || Number.isNaN(+x)) {
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
return true;
|
|
5
17
|
}
|
|
6
18
|
export function generateBedMethylFeature({ splitLine, uniqueId, refName, start, end, }) {
|
|
7
|
-
|
|
19
|
+
// see
|
|
20
|
+
// https://github.com/nanoporetech/modkit?tab=readme-ov-file#description-of-bedmethyl-output
|
|
21
|
+
const code = splitLine[3];
|
|
22
|
+
const strandRaw = splitLine[5];
|
|
23
|
+
const color = splitLine[8];
|
|
24
|
+
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
25
|
return {
|
|
9
26
|
uniqueId,
|
|
10
27
|
refName,
|
|
11
28
|
start,
|
|
12
29
|
end,
|
|
13
30
|
code,
|
|
14
|
-
score: +fraction_modified
|
|
15
|
-
strand,
|
|
31
|
+
score: +fraction_modified,
|
|
32
|
+
strand: parseStrand(strandRaw),
|
|
16
33
|
color,
|
|
34
|
+
// source mirrors code so MultiQuantitativeTrack groups by modification code
|
|
17
35
|
source: code,
|
|
18
36
|
n_valid_cov,
|
|
19
37
|
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
|
+
};
|