@jbrowse/plugin-bed 3.3.0 → 3.5.0

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.
@@ -33,5 +33,4 @@ export default class BedAdapter extends BaseFeatureDataAdapter {
33
33
  private loadFeatureIntervalTreeHelper;
34
34
  loadFeatureIntervalTree(refName: string): Promise<IntervalTree<any> | undefined>;
35
35
  getFeatures(query: Region, opts?: BaseOptions): import("rxjs").Observable<Feature>;
36
- freeResources(): void;
37
36
  }
@@ -15,31 +15,41 @@ class BedAdapter extends BaseAdapter_1.BaseFeatureDataAdapter {
15
15
  super(...arguments);
16
16
  this.intervalTrees = {};
17
17
  }
18
- async loadDataP(opts = {}) {
19
- const pm = this.pluginManager;
18
+ async loadDataP(opts) {
19
+ const { statusCallback = () => { } } = opts || {};
20
20
  const bedLoc = this.getConf('bedLocation');
21
- const buffer = await (0, util_1.fetchAndMaybeUnzip)((0, io_1.openLocation)(bedLoc, pm), opts);
22
- if (buffer.length > 536870888) {
23
- throw new Error('Data exceeds maximum string length (512MB)');
24
- }
25
- const data = new TextDecoder('utf8', { fatal: true }).decode(buffer);
26
- const lines = data.split(/\n|\r\n|\r/).filter(f => !!f);
21
+ const buffer = await (0, util_1.fetchAndMaybeUnzip)((0, io_1.openLocation)(bedLoc, this.pluginManager), opts);
27
22
  const headerLines = [];
28
- let i = 0;
29
- for (; i < lines.length && lines[i].startsWith('#'); i++) {
30
- headerLines.push(lines[i]);
31
- }
32
- const header = headerLines.join('\n');
33
23
  const features = {};
34
- for (; i < lines.length; i++) {
35
- const line = lines[i];
36
- const tab = line.indexOf('\t');
37
- const refName = line.slice(0, tab);
38
- if (!features[refName]) {
39
- features[refName] = [];
24
+ let blockStart = 0;
25
+ let i = 0;
26
+ const decoder = new TextDecoder('utf8');
27
+ while (blockStart < buffer.length) {
28
+ const n = buffer.indexOf(10, blockStart);
29
+ const b = n === -1 ? buffer.subarray(blockStart) : buffer.subarray(blockStart, n);
30
+ const line = decoder.decode(b).trim();
31
+ if (line) {
32
+ if (line.startsWith('#')) {
33
+ headerLines.push(line);
34
+ }
35
+ else if (line.startsWith('>')) {
36
+ break;
37
+ }
38
+ else {
39
+ const tab = line.indexOf('\t');
40
+ const refName = line.slice(0, tab);
41
+ if (!features[refName]) {
42
+ features[refName] = [];
43
+ }
44
+ features[refName].push(line);
45
+ }
40
46
  }
41
- features[refName].push(line);
47
+ if (i++ % 10000 === 0) {
48
+ statusCallback(`Loading ${(0, util_1.getProgressDisplayStr)(blockStart, buffer.length)}`);
49
+ }
50
+ blockStart = n + 1;
42
51
  }
52
+ const header = headerLines.join('\n');
43
53
  const autoSql = this.getConf('autoSql');
44
54
  const parser = new bed_1.default({ autoSql });
45
55
  const columnNames = this.getConf('columnNames');
@@ -136,7 +146,6 @@ class BedAdapter extends BaseAdapter_1.BaseFeatureDataAdapter {
136
146
  observer.complete();
137
147
  }, opts.stopToken);
138
148
  }
139
- freeResources() { }
140
149
  }
141
150
  BedAdapter.capabilities = ['getFeatures', 'getRefNames'];
142
151
  exports.default = BedAdapter;
@@ -20,5 +20,4 @@ export default class BedGraphAdapter extends BaseFeatureDataAdapter {
20
20
  columnNames: string[];
21
21
  }>;
22
22
  getFeatures(query: Region, _opts?: BaseOptions): import("rxjs").Observable<Feature>;
23
- freeResources(): void;
24
23
  }
@@ -125,6 +125,5 @@ class BedGraphAdapter extends BaseAdapter_1.BaseFeatureDataAdapter {
125
125
  observer.complete();
126
126
  });
127
127
  }
128
- freeResources() { }
129
128
  }
130
129
  exports.default = BedGraphAdapter;
@@ -14,5 +14,4 @@ export default class BedGraphAdapter extends BaseFeatureDataAdapter {
14
14
  getRefNames(opts?: BaseOptions): Promise<string[]>;
15
15
  getHeader(): Promise<string>;
16
16
  getFeatures(query: Region, opts?: BaseOptions): import("rxjs").Observable<Feature>;
17
- freeResources(): void;
18
17
  }
@@ -103,6 +103,5 @@ class BedGraphAdapter extends BaseAdapter_1.BaseFeatureDataAdapter {
103
103
  observer.complete();
104
104
  });
105
105
  }
106
- freeResources() { }
107
106
  }
108
107
  exports.default = BedGraphAdapter;
@@ -19,5 +19,4 @@ export default class BedpeAdapter extends BaseFeatureDataAdapter {
19
19
  private loadFeatureTreeP;
20
20
  private loadFeatureTree;
21
21
  getFeatures(query: Region, opts?: BaseOptions): import("rxjs").Observable<Feature>;
22
- freeResources(): void;
23
22
  }
@@ -109,7 +109,6 @@ class BedpeAdapter extends BaseAdapter_1.BaseFeatureDataAdapter {
109
109
  observer.complete();
110
110
  }, opts.stopToken);
111
111
  }
112
- freeResources() { }
113
112
  }
114
113
  BedpeAdapter.capabilities = ['getFeatures', 'getRefNames'];
115
114
  exports.default = BedpeAdapter;
@@ -32,6 +32,9 @@ export default class BigBedAdapter extends BaseFeatureDataAdapter {
32
32
  [k: string]: string;
33
33
  };
34
34
  }>;
35
+ getMetadata(opts?: BaseOptions): Promise<{
36
+ [k: string]: string;
37
+ }>;
35
38
  getFeaturesHelper({ query, opts, observer, allowRedispatch, originalQuery, }: {
36
39
  query: Region;
37
40
  opts: BaseOptions;
@@ -40,5 +43,4 @@ export default class BigBedAdapter extends BaseFeatureDataAdapter {
40
43
  originalQuery?: Region;
41
44
  }): Promise<void>;
42
45
  getFeatures(query: Region, opts?: BaseOptions): import("rxjs").Observable<Feature>;
43
- freeResources(): void;
44
46
  }
@@ -42,9 +42,9 @@ class BigBedAdapter extends BaseAdapter_1.BaseFeatureDataAdapter {
42
42
  }
43
43
  async getRefNameAliases(opts) {
44
44
  const { header } = await this.configure(opts);
45
- const ret = await Promise.all(Object.keys(header.refsByName).map(async (r) => (await (0, rxjs_2.firstValueFrom)(this.getFeatures({
45
+ const ret = await Promise.all(Object.keys(header.refsByName).map(async (refName) => (await (0, rxjs_2.firstValueFrom)(this.getFeatures({
46
46
  assemblyName: '',
47
- refName: r,
47
+ refName,
48
48
  start: 0,
49
49
  end: 1,
50
50
  }).pipe((0, rxjs_2.toArray)())))[0]));
@@ -73,16 +73,20 @@ class BigBedAdapter extends BaseAdapter_1.BaseFeatureDataAdapter {
73
73
  async getHeader(opts) {
74
74
  const { parser, header } = await this.configure(opts);
75
75
  const { version, fileType } = header;
76
- const { fields, ...rest } = parser.autoSql;
76
+ const { fields, ...autoSql } = parser.autoSql;
77
77
  return {
78
78
  version,
79
79
  fileType,
80
- autoSql: { ...rest },
81
- fields: Object.fromEntries(fields.map(({ name, comment }) => [name, comment])),
80
+ autoSql,
81
+ fields: await this.getMetadata(opts),
82
82
  };
83
83
  }
84
+ async getMetadata(opts) {
85
+ const { parser } = await this.configure(opts);
86
+ const { fields } = parser.autoSql;
87
+ return Object.fromEntries(fields.map(({ name, comment }) => [name, comment]));
88
+ }
84
89
  async getFeaturesHelper({ query, opts, observer, allowRedispatch, originalQuery = query, }) {
85
- var _a;
86
90
  const { statusCallback = () => { } } = opts;
87
91
  const scoreColumn = this.getConf('scoreColumn');
88
92
  const aggregateField = this.getConf('aggregateField');
@@ -90,94 +94,98 @@ class BigBedAdapter extends BaseAdapter_1.BaseFeatureDataAdapter {
90
94
  const feats = await (0, util_1.updateStatus)('Downloading features', statusCallback, () => bigbed.getFeatures(query.refName, query.start, query.end, {
91
95
  basesPerSpan: query.end - query.start,
92
96
  }));
93
- const parentAggregation = {};
94
- const parentAggregationFlat = [];
95
- if (feats.some(f => f.uniqueId === undefined)) {
96
- throw new Error('found uniqueId undefined');
97
- }
98
- for (const feat of feats) {
99
- const splitLine = [
100
- query.refName,
101
- `${feat.start}`,
102
- `${feat.end}`,
103
- ...(((_a = feat.rest) === null || _a === void 0 ? void 0 : _a.split('\t')) || []),
104
- ];
105
- const data = parser.parseLine(splitLine, {
106
- uniqueId: feat.uniqueId,
107
- });
108
- const aggr = data[aggregateField];
109
- if (!parentAggregation[aggr]) {
110
- parentAggregation[aggr] = [];
97
+ await (0, util_1.updateStatus)('Processing features', statusCallback, async () => {
98
+ var _a;
99
+ const parentAggregation = {};
100
+ const parentAggregationFlat = [];
101
+ if (feats.some(f => f.uniqueId === undefined)) {
102
+ throw new Error('found uniqueId undefined');
111
103
  }
112
- const { uniqueId, type, chrom, chromStart, chromEnd, description, chromStarts: chromStarts2, blockStarts: blockStarts2, blockSizes: blockSizes2, score: score2, blockCount, thickStart, thickEnd, strand, ...rest } = data;
113
- const f = (0, util_2.featureData2)({
114
- ...rest,
115
- scoreColumn,
116
- splitLine,
117
- parser,
118
- uniqueId,
119
- start: feat.start,
120
- end: feat.end,
121
- refName: query.refName,
122
- });
123
- if (aggr) {
124
- parentAggregation[aggr].push(f);
125
- parentAggregationFlat.push(f);
126
- }
127
- else {
128
- if ((0, util_1.doesIntersect2)(f.start, f.end, originalQuery.start, originalQuery.end)) {
129
- observer.next(new util_1.SimpleFeature({
130
- id: `${this.id}-${uniqueId}`,
131
- data: f,
132
- }));
104
+ for (const feat of feats) {
105
+ const splitLine = [
106
+ query.refName,
107
+ `${feat.start}`,
108
+ `${feat.end}`,
109
+ ...(((_a = feat.rest) === null || _a === void 0 ? void 0 : _a.split('\t')) || []),
110
+ ];
111
+ const data = parser.parseLine(splitLine, {
112
+ uniqueId: feat.uniqueId,
113
+ });
114
+ const aggr = data[aggregateField];
115
+ const aggrIsNotNone = aggr && aggr !== 'none';
116
+ if (aggrIsNotNone && !parentAggregation[aggr]) {
117
+ parentAggregation[aggr] = [];
133
118
  }
134
- }
135
- }
136
- if (allowRedispatch && parentAggregationFlat.length) {
137
- let minStart = Number.POSITIVE_INFINITY;
138
- let maxEnd = Number.NEGATIVE_INFINITY;
139
- for (const feat of parentAggregationFlat) {
140
- if (feat.start < minStart) {
141
- minStart = feat.start;
119
+ const { uniqueId, type, chrom, chromStart, chromEnd, description, chromStarts: chromStarts2, blockStarts: blockStarts2, blockSizes: blockSizes2, score: score2, blockCount, thickStart, thickEnd, strand, ...rest } = data;
120
+ const f = (0, util_2.featureData2)({
121
+ ...rest,
122
+ scoreColumn,
123
+ splitLine,
124
+ parser,
125
+ uniqueId,
126
+ start: feat.start,
127
+ end: feat.end,
128
+ refName: query.refName,
129
+ });
130
+ if (aggrIsNotNone) {
131
+ parentAggregation[aggr].push(f);
132
+ parentAggregationFlat.push(f);
142
133
  }
143
- if (feat.end > maxEnd) {
144
- maxEnd = feat.end;
134
+ else {
135
+ if ((0, util_1.doesIntersect2)(f.start, f.end, originalQuery.start, originalQuery.end)) {
136
+ observer.next(new util_1.SimpleFeature({
137
+ id: `${this.id}-${uniqueId}`,
138
+ data: f,
139
+ }));
140
+ }
145
141
  }
146
142
  }
147
- if (maxEnd > query.end || minStart < query.start) {
148
- await this.getFeaturesHelper({
149
- query: {
150
- ...query,
151
- start: minStart - 500000,
152
- end: maxEnd + 500000,
153
- },
154
- opts,
155
- observer,
156
- allowRedispatch: false,
157
- originalQuery: query,
158
- });
159
- return;
160
- }
161
- }
162
- Object.entries(parentAggregation).map(([name, subfeatures]) => {
163
- var _a, _b;
164
- const s = (0, util_1.min)(subfeatures.map(f => f.start));
165
- const e = (0, util_1.max)(subfeatures.map(f => f.end));
166
- if ((0, util_1.doesIntersect2)(s, e, originalQuery.start, originalQuery.end)) {
167
- const subs = subfeatures.sort((a, b) => a.uniqueId.localeCompare(b.uniqueId));
168
- observer.next(new util_1.SimpleFeature({
169
- id: `${this.id}-${(_a = subs[0]) === null || _a === void 0 ? void 0 : _a.uniqueId}-parent`,
170
- data: {
171
- type: 'gene',
172
- subfeatures: subs,
173
- strand: ((_b = subs[0]) === null || _b === void 0 ? void 0 : _b.strand) || 1,
174
- name,
175
- start: s,
176
- end: e,
177
- refName: query.refName,
178
- },
179
- }));
143
+ if (allowRedispatch && parentAggregationFlat.length) {
144
+ let minStart = Number.POSITIVE_INFINITY;
145
+ let maxEnd = Number.NEGATIVE_INFINITY;
146
+ for (const feat of parentAggregationFlat) {
147
+ if (feat.start < minStart) {
148
+ minStart = feat.start;
149
+ }
150
+ if (feat.end > maxEnd) {
151
+ maxEnd = feat.end;
152
+ }
153
+ }
154
+ if (maxEnd > query.end || minStart < query.start) {
155
+ await this.getFeaturesHelper({
156
+ query: {
157
+ ...query,
158
+ start: minStart - 500000,
159
+ end: maxEnd + 500000,
160
+ },
161
+ opts,
162
+ observer,
163
+ allowRedispatch: false,
164
+ originalQuery: query,
165
+ });
166
+ return;
167
+ }
180
168
  }
169
+ Object.entries(parentAggregation).map(([name, subfeatures]) => {
170
+ var _a, _b;
171
+ const s = (0, util_1.min)(subfeatures.map(f => f.start));
172
+ const e = (0, util_1.max)(subfeatures.map(f => f.end));
173
+ if ((0, util_1.doesIntersect2)(s, e, originalQuery.start, originalQuery.end)) {
174
+ const subs = subfeatures.sort((a, b) => a.uniqueId.localeCompare(b.uniqueId));
175
+ observer.next(new util_1.SimpleFeature({
176
+ id: `${this.id}-${(_a = subs[0]) === null || _a === void 0 ? void 0 : _a.uniqueId}-parent`,
177
+ data: {
178
+ type: 'gene',
179
+ subfeatures: subs,
180
+ strand: ((_b = subs[0]) === null || _b === void 0 ? void 0 : _b.strand) || 1,
181
+ name,
182
+ start: s,
183
+ end: e,
184
+ refName: query.refName,
185
+ },
186
+ }));
187
+ }
188
+ });
181
189
  });
182
190
  observer.complete();
183
191
  }
@@ -200,6 +208,5 @@ class BigBedAdapter extends BaseAdapter_1.BaseFeatureDataAdapter {
200
208
  }
201
209
  }, opts.stopToken);
202
210
  }
203
- freeResources() { }
204
211
  }
205
212
  exports.default = BigBedAdapter;
@@ -1,4 +1,4 @@
1
- import type { MinimalFeature, TranscriptFeat } from './types';
1
+ import type { TranscriptFeat } from './types';
2
2
  export declare function isUcscTranscript({ thickStart, blockCount, strand, }: {
3
3
  thickStart?: number;
4
4
  blockCount?: number;
@@ -9,7 +9,12 @@ export declare function generateUcscTranscript(data: TranscriptFeat): {
9
9
  strand: number;
10
10
  type: string;
11
11
  refName: string;
12
- subfeatures: MinimalFeature[];
12
+ subfeatures: {
13
+ type: string;
14
+ start: number;
15
+ end: number;
16
+ refName: string;
17
+ }[];
13
18
  start: number;
14
19
  end: number;
15
20
  };
@@ -12,81 +12,105 @@ function generateUcscTranscript(data) {
12
12
  const feats = oldSubfeatures
13
13
  .filter(child => child.type === 'block')
14
14
  .sort((a, b) => a.start - b.start);
15
- for (const block of feats) {
16
- const start = block.start;
17
- const end = block.end;
18
- if (thickStart >= end) {
19
- subfeatures.push({
20
- type: `${strand > 0 ? 'five' : 'three'}_prime_UTR`,
21
- start,
22
- end,
23
- refName,
24
- });
25
- }
26
- else if (thickStart > start && thickStart < end && thickEnd >= end) {
27
- subfeatures.push({
28
- type: `${strand > 0 ? 'five' : 'three'}_prime_UTR`,
29
- start,
30
- end: thickStart,
31
- refName,
32
- }, {
33
- type: 'CDS',
34
- phase: 0,
35
- start: thickStart,
36
- end,
37
- refName,
38
- });
39
- }
40
- else if (thickStart <= start && thickEnd >= end) {
41
- subfeatures.push({
42
- type: 'CDS',
43
- phase: 0,
44
- start,
45
- end,
46
- refName,
47
- });
48
- }
49
- else if (thickStart > start && thickStart < end && thickEnd < end) {
50
- subfeatures.push({
51
- type: `${strand > 0 ? 'five' : 'three'}_prime_UTR`,
52
- start,
53
- end: thickStart,
54
- refName,
55
- }, {
56
- type: 'CDS',
57
- phase: 0,
58
- start: thickStart,
59
- end: thickEnd,
60
- refName,
61
- }, {
62
- type: `${strand > 0 ? 'three' : 'five'}_prime_UTR`,
63
- start: thickEnd,
64
- end,
65
- refName,
66
- });
67
- }
68
- else if (thickStart <= start && thickEnd > start && thickEnd < end) {
69
- subfeatures.push({
70
- type: 'CDS',
71
- phase: 0,
72
- start,
73
- end: thickEnd,
74
- refName,
75
- }, {
76
- type: `${strand > 0 ? 'three' : 'five'}_prime_UTR`,
77
- start: thickEnd,
78
- end,
79
- refName,
80
- });
81
- }
82
- else if (thickEnd <= start) {
83
- subfeatures.push({
84
- type: `${strand > 0 ? 'three' : 'five'}_prime_UTR`,
85
- start,
86
- end,
87
- refName,
88
- });
15
+ const { cdsEndStat, cdsStartStat } = rest2;
16
+ if (cdsStartStat === 'none' && cdsEndStat === 'none') {
17
+ return {
18
+ ...rest2,
19
+ uniqueId,
20
+ strand,
21
+ type: 'transcript',
22
+ refName,
23
+ subfeatures: feats.map(e => ({
24
+ ...e,
25
+ type: 'exon',
26
+ })),
27
+ };
28
+ }
29
+ else {
30
+ for (const block of feats) {
31
+ const start = block.start;
32
+ const end = block.end;
33
+ if (thickStart >= end) {
34
+ subfeatures.push({
35
+ type: `${strand > 0 ? 'five' : 'three'}_prime_UTR`,
36
+ start,
37
+ end,
38
+ refName,
39
+ });
40
+ }
41
+ else if (thickStart > start && thickStart < end && thickEnd >= end) {
42
+ subfeatures.push({
43
+ type: `${strand > 0 ? 'five' : 'three'}_prime_UTR`,
44
+ start,
45
+ end: thickStart,
46
+ refName,
47
+ }, {
48
+ type: 'CDS',
49
+ phase: 0,
50
+ start: thickStart,
51
+ end,
52
+ refName,
53
+ });
54
+ }
55
+ else if (thickStart <= start && thickEnd >= end) {
56
+ subfeatures.push({
57
+ type: 'CDS',
58
+ phase: 0,
59
+ start,
60
+ end,
61
+ refName,
62
+ });
63
+ }
64
+ else if (thickStart > start && thickStart < end && thickEnd < end) {
65
+ subfeatures.push({
66
+ type: `${strand > 0 ? 'five' : 'three'}_prime_UTR`,
67
+ start,
68
+ end: thickStart,
69
+ refName,
70
+ }, {
71
+ type: 'CDS',
72
+ phase: 0,
73
+ start: thickStart,
74
+ end: thickEnd,
75
+ refName,
76
+ }, {
77
+ type: `${strand > 0 ? 'three' : 'five'}_prime_UTR`,
78
+ start: thickEnd,
79
+ end,
80
+ refName,
81
+ });
82
+ }
83
+ else if (thickStart <= start && thickEnd > start && thickEnd < end) {
84
+ subfeatures.push({
85
+ type: 'CDS',
86
+ phase: 0,
87
+ start,
88
+ end: thickEnd,
89
+ refName,
90
+ }, {
91
+ type: `${strand > 0 ? 'three' : 'five'}_prime_UTR`,
92
+ start: thickEnd,
93
+ end,
94
+ refName,
95
+ });
96
+ }
97
+ else if (thickEnd <= start) {
98
+ subfeatures.push({
99
+ type: `${strand > 0 ? 'three' : 'five'}_prime_UTR`,
100
+ start,
101
+ end,
102
+ refName,
103
+ });
104
+ }
89
105
  }
106
+ return {
107
+ ...rest2,
108
+ uniqueId,
109
+ strand,
110
+ type: 'mRNA',
111
+ refName,
112
+ subfeatures,
113
+ };
90
114
  }
91
115
  return {
92
116
  ...rest2,
package/dist/util.d.ts CHANGED
@@ -89,7 +89,12 @@ export declare function featureData({ line, colRef, colStart, colEnd, scoreColum
89
89
  strand: number;
90
90
  type: string;
91
91
  refName: string;
92
- subfeatures: import("./types").MinimalFeature[];
92
+ subfeatures: {
93
+ type: string;
94
+ start: number;
95
+ end: number;
96
+ refName: string;
97
+ }[];
93
98
  start: number;
94
99
  end: number;
95
100
  } | {
@@ -182,7 +187,12 @@ export declare function featureData2({ splitLine, refName, start, end, parser, u
182
187
  strand: number;
183
188
  type: string;
184
189
  refName: string;
185
- subfeatures: import("./types").MinimalFeature[];
190
+ subfeatures: {
191
+ type: string;
192
+ start: number;
193
+ end: number;
194
+ refName: string;
195
+ }[];
186
196
  start: number;
187
197
  end: number;
188
198
  } | {
@@ -33,5 +33,4 @@ export default class BedAdapter extends BaseFeatureDataAdapter {
33
33
  private loadFeatureIntervalTreeHelper;
34
34
  loadFeatureIntervalTree(refName: string): Promise<IntervalTree<any> | undefined>;
35
35
  getFeatures(query: Region, opts?: BaseOptions): import("rxjs").Observable<Feature>;
36
- freeResources(): void;
37
36
  }
@@ -1,7 +1,7 @@
1
1
  import IntervalTree from '@flatten-js/interval-tree';
2
2
  import BED from '@gmod/bed';
3
3
  import { BaseFeatureDataAdapter } from '@jbrowse/core/data_adapters/BaseAdapter';
4
- import { SimpleFeature, fetchAndMaybeUnzip } from '@jbrowse/core/util';
4
+ import { SimpleFeature, fetchAndMaybeUnzip, getProgressDisplayStr, } from '@jbrowse/core/util';
5
5
  import { openLocation } from '@jbrowse/core/util/io';
6
6
  import { ObservableCreate } from '@jbrowse/core/util/rxjs';
7
7
  import { featureData } from '../util';
@@ -10,31 +10,41 @@ class BedAdapter extends BaseFeatureDataAdapter {
10
10
  super(...arguments);
11
11
  this.intervalTrees = {};
12
12
  }
13
- async loadDataP(opts = {}) {
14
- const pm = this.pluginManager;
13
+ async loadDataP(opts) {
14
+ const { statusCallback = () => { } } = opts || {};
15
15
  const bedLoc = this.getConf('bedLocation');
16
- const buffer = await fetchAndMaybeUnzip(openLocation(bedLoc, pm), opts);
17
- if (buffer.length > 536870888) {
18
- throw new Error('Data exceeds maximum string length (512MB)');
19
- }
20
- const data = new TextDecoder('utf8', { fatal: true }).decode(buffer);
21
- const lines = data.split(/\n|\r\n|\r/).filter(f => !!f);
16
+ const buffer = await fetchAndMaybeUnzip(openLocation(bedLoc, this.pluginManager), opts);
22
17
  const headerLines = [];
23
- let i = 0;
24
- for (; i < lines.length && lines[i].startsWith('#'); i++) {
25
- headerLines.push(lines[i]);
26
- }
27
- const header = headerLines.join('\n');
28
18
  const features = {};
29
- for (; i < lines.length; i++) {
30
- const line = lines[i];
31
- const tab = line.indexOf('\t');
32
- const refName = line.slice(0, tab);
33
- if (!features[refName]) {
34
- features[refName] = [];
19
+ let blockStart = 0;
20
+ let i = 0;
21
+ const decoder = new TextDecoder('utf8');
22
+ while (blockStart < buffer.length) {
23
+ const n = buffer.indexOf(10, blockStart);
24
+ const b = n === -1 ? buffer.subarray(blockStart) : buffer.subarray(blockStart, n);
25
+ const line = decoder.decode(b).trim();
26
+ if (line) {
27
+ if (line.startsWith('#')) {
28
+ headerLines.push(line);
29
+ }
30
+ else if (line.startsWith('>')) {
31
+ break;
32
+ }
33
+ else {
34
+ const tab = line.indexOf('\t');
35
+ const refName = line.slice(0, tab);
36
+ if (!features[refName]) {
37
+ features[refName] = [];
38
+ }
39
+ features[refName].push(line);
40
+ }
35
41
  }
36
- features[refName].push(line);
42
+ if (i++ % 10000 === 0) {
43
+ statusCallback(`Loading ${getProgressDisplayStr(blockStart, buffer.length)}`);
44
+ }
45
+ blockStart = n + 1;
37
46
  }
47
+ const header = headerLines.join('\n');
38
48
  const autoSql = this.getConf('autoSql');
39
49
  const parser = new BED({ autoSql });
40
50
  const columnNames = this.getConf('columnNames');
@@ -131,7 +141,6 @@ class BedAdapter extends BaseFeatureDataAdapter {
131
141
  observer.complete();
132
142
  }, opts.stopToken);
133
143
  }
134
- freeResources() { }
135
144
  }
136
145
  BedAdapter.capabilities = ['getFeatures', 'getRefNames'];
137
146
  export default BedAdapter;
@@ -20,5 +20,4 @@ export default class BedGraphAdapter extends BaseFeatureDataAdapter {
20
20
  columnNames: string[];
21
21
  }>;
22
22
  getFeatures(query: Region, _opts?: BaseOptions): import("rxjs").Observable<Feature>;
23
- freeResources(): void;
24
23
  }
@@ -120,5 +120,4 @@ export default class BedGraphAdapter extends BaseFeatureDataAdapter {
120
120
  observer.complete();
121
121
  });
122
122
  }
123
- freeResources() { }
124
123
  }
@@ -14,5 +14,4 @@ export default class BedGraphAdapter extends BaseFeatureDataAdapter {
14
14
  getRefNames(opts?: BaseOptions): Promise<string[]>;
15
15
  getHeader(): Promise<string>;
16
16
  getFeatures(query: Region, opts?: BaseOptions): import("rxjs").Observable<Feature>;
17
- freeResources(): void;
18
17
  }
@@ -101,5 +101,4 @@ export default class BedGraphAdapter extends BaseFeatureDataAdapter {
101
101
  observer.complete();
102
102
  });
103
103
  }
104
- freeResources() { }
105
104
  }
@@ -19,5 +19,4 @@ export default class BedpeAdapter extends BaseFeatureDataAdapter {
19
19
  private loadFeatureTreeP;
20
20
  private loadFeatureTree;
21
21
  getFeatures(query: Region, opts?: BaseOptions): import("rxjs").Observable<Feature>;
22
- freeResources(): void;
23
22
  }
@@ -104,7 +104,6 @@ class BedpeAdapter extends BaseFeatureDataAdapter {
104
104
  observer.complete();
105
105
  }, opts.stopToken);
106
106
  }
107
- freeResources() { }
108
107
  }
109
108
  BedpeAdapter.capabilities = ['getFeatures', 'getRefNames'];
110
109
  export default BedpeAdapter;
@@ -32,6 +32,9 @@ export default class BigBedAdapter extends BaseFeatureDataAdapter {
32
32
  [k: string]: string;
33
33
  };
34
34
  }>;
35
+ getMetadata(opts?: BaseOptions): Promise<{
36
+ [k: string]: string;
37
+ }>;
35
38
  getFeaturesHelper({ query, opts, observer, allowRedispatch, originalQuery, }: {
36
39
  query: Region;
37
40
  opts: BaseOptions;
@@ -40,5 +43,4 @@ export default class BigBedAdapter extends BaseFeatureDataAdapter {
40
43
  originalQuery?: Region;
41
44
  }): Promise<void>;
42
45
  getFeatures(query: Region, opts?: BaseOptions): import("rxjs").Observable<Feature>;
43
- freeResources(): void;
44
46
  }
@@ -37,9 +37,9 @@ export default class BigBedAdapter extends BaseFeatureDataAdapter {
37
37
  }
38
38
  async getRefNameAliases(opts) {
39
39
  const { header } = await this.configure(opts);
40
- const ret = await Promise.all(Object.keys(header.refsByName).map(async (r) => (await firstValueFrom(this.getFeatures({
40
+ const ret = await Promise.all(Object.keys(header.refsByName).map(async (refName) => (await firstValueFrom(this.getFeatures({
41
41
  assemblyName: '',
42
- refName: r,
42
+ refName,
43
43
  start: 0,
44
44
  end: 1,
45
45
  }).pipe(toArray())))[0]));
@@ -68,16 +68,20 @@ export default class BigBedAdapter extends BaseFeatureDataAdapter {
68
68
  async getHeader(opts) {
69
69
  const { parser, header } = await this.configure(opts);
70
70
  const { version, fileType } = header;
71
- const { fields, ...rest } = parser.autoSql;
71
+ const { fields, ...autoSql } = parser.autoSql;
72
72
  return {
73
73
  version,
74
74
  fileType,
75
- autoSql: { ...rest },
76
- fields: Object.fromEntries(fields.map(({ name, comment }) => [name, comment])),
75
+ autoSql,
76
+ fields: await this.getMetadata(opts),
77
77
  };
78
78
  }
79
+ async getMetadata(opts) {
80
+ const { parser } = await this.configure(opts);
81
+ const { fields } = parser.autoSql;
82
+ return Object.fromEntries(fields.map(({ name, comment }) => [name, comment]));
83
+ }
79
84
  async getFeaturesHelper({ query, opts, observer, allowRedispatch, originalQuery = query, }) {
80
- var _a;
81
85
  const { statusCallback = () => { } } = opts;
82
86
  const scoreColumn = this.getConf('scoreColumn');
83
87
  const aggregateField = this.getConf('aggregateField');
@@ -85,94 +89,98 @@ export default class BigBedAdapter extends BaseFeatureDataAdapter {
85
89
  const feats = await updateStatus('Downloading features', statusCallback, () => bigbed.getFeatures(query.refName, query.start, query.end, {
86
90
  basesPerSpan: query.end - query.start,
87
91
  }));
88
- const parentAggregation = {};
89
- const parentAggregationFlat = [];
90
- if (feats.some(f => f.uniqueId === undefined)) {
91
- throw new Error('found uniqueId undefined');
92
- }
93
- for (const feat of feats) {
94
- const splitLine = [
95
- query.refName,
96
- `${feat.start}`,
97
- `${feat.end}`,
98
- ...(((_a = feat.rest) === null || _a === void 0 ? void 0 : _a.split('\t')) || []),
99
- ];
100
- const data = parser.parseLine(splitLine, {
101
- uniqueId: feat.uniqueId,
102
- });
103
- const aggr = data[aggregateField];
104
- if (!parentAggregation[aggr]) {
105
- parentAggregation[aggr] = [];
92
+ await updateStatus('Processing features', statusCallback, async () => {
93
+ var _a;
94
+ const parentAggregation = {};
95
+ const parentAggregationFlat = [];
96
+ if (feats.some(f => f.uniqueId === undefined)) {
97
+ throw new Error('found uniqueId undefined');
106
98
  }
107
- const { uniqueId, type, chrom, chromStart, chromEnd, description, chromStarts: chromStarts2, blockStarts: blockStarts2, blockSizes: blockSizes2, score: score2, blockCount, thickStart, thickEnd, strand, ...rest } = data;
108
- const f = featureData2({
109
- ...rest,
110
- scoreColumn,
111
- splitLine,
112
- parser,
113
- uniqueId,
114
- start: feat.start,
115
- end: feat.end,
116
- refName: query.refName,
117
- });
118
- if (aggr) {
119
- parentAggregation[aggr].push(f);
120
- parentAggregationFlat.push(f);
121
- }
122
- else {
123
- if (doesIntersect2(f.start, f.end, originalQuery.start, originalQuery.end)) {
124
- observer.next(new SimpleFeature({
125
- id: `${this.id}-${uniqueId}`,
126
- data: f,
127
- }));
99
+ for (const feat of feats) {
100
+ const splitLine = [
101
+ query.refName,
102
+ `${feat.start}`,
103
+ `${feat.end}`,
104
+ ...(((_a = feat.rest) === null || _a === void 0 ? void 0 : _a.split('\t')) || []),
105
+ ];
106
+ const data = parser.parseLine(splitLine, {
107
+ uniqueId: feat.uniqueId,
108
+ });
109
+ const aggr = data[aggregateField];
110
+ const aggrIsNotNone = aggr && aggr !== 'none';
111
+ if (aggrIsNotNone && !parentAggregation[aggr]) {
112
+ parentAggregation[aggr] = [];
128
113
  }
129
- }
130
- }
131
- if (allowRedispatch && parentAggregationFlat.length) {
132
- let minStart = Number.POSITIVE_INFINITY;
133
- let maxEnd = Number.NEGATIVE_INFINITY;
134
- for (const feat of parentAggregationFlat) {
135
- if (feat.start < minStart) {
136
- minStart = feat.start;
114
+ const { uniqueId, type, chrom, chromStart, chromEnd, description, chromStarts: chromStarts2, blockStarts: blockStarts2, blockSizes: blockSizes2, score: score2, blockCount, thickStart, thickEnd, strand, ...rest } = data;
115
+ const f = featureData2({
116
+ ...rest,
117
+ scoreColumn,
118
+ splitLine,
119
+ parser,
120
+ uniqueId,
121
+ start: feat.start,
122
+ end: feat.end,
123
+ refName: query.refName,
124
+ });
125
+ if (aggrIsNotNone) {
126
+ parentAggregation[aggr].push(f);
127
+ parentAggregationFlat.push(f);
137
128
  }
138
- if (feat.end > maxEnd) {
139
- maxEnd = feat.end;
129
+ else {
130
+ if (doesIntersect2(f.start, f.end, originalQuery.start, originalQuery.end)) {
131
+ observer.next(new SimpleFeature({
132
+ id: `${this.id}-${uniqueId}`,
133
+ data: f,
134
+ }));
135
+ }
140
136
  }
141
137
  }
142
- if (maxEnd > query.end || minStart < query.start) {
143
- await this.getFeaturesHelper({
144
- query: {
145
- ...query,
146
- start: minStart - 500000,
147
- end: maxEnd + 500000,
148
- },
149
- opts,
150
- observer,
151
- allowRedispatch: false,
152
- originalQuery: query,
153
- });
154
- return;
155
- }
156
- }
157
- Object.entries(parentAggregation).map(([name, subfeatures]) => {
158
- var _a, _b;
159
- const s = min(subfeatures.map(f => f.start));
160
- const e = max(subfeatures.map(f => f.end));
161
- if (doesIntersect2(s, e, originalQuery.start, originalQuery.end)) {
162
- const subs = subfeatures.sort((a, b) => a.uniqueId.localeCompare(b.uniqueId));
163
- observer.next(new SimpleFeature({
164
- id: `${this.id}-${(_a = subs[0]) === null || _a === void 0 ? void 0 : _a.uniqueId}-parent`,
165
- data: {
166
- type: 'gene',
167
- subfeatures: subs,
168
- strand: ((_b = subs[0]) === null || _b === void 0 ? void 0 : _b.strand) || 1,
169
- name,
170
- start: s,
171
- end: e,
172
- refName: query.refName,
173
- },
174
- }));
138
+ if (allowRedispatch && parentAggregationFlat.length) {
139
+ let minStart = Number.POSITIVE_INFINITY;
140
+ let maxEnd = Number.NEGATIVE_INFINITY;
141
+ for (const feat of parentAggregationFlat) {
142
+ if (feat.start < minStart) {
143
+ minStart = feat.start;
144
+ }
145
+ if (feat.end > maxEnd) {
146
+ maxEnd = feat.end;
147
+ }
148
+ }
149
+ if (maxEnd > query.end || minStart < query.start) {
150
+ await this.getFeaturesHelper({
151
+ query: {
152
+ ...query,
153
+ start: minStart - 500000,
154
+ end: maxEnd + 500000,
155
+ },
156
+ opts,
157
+ observer,
158
+ allowRedispatch: false,
159
+ originalQuery: query,
160
+ });
161
+ return;
162
+ }
175
163
  }
164
+ Object.entries(parentAggregation).map(([name, subfeatures]) => {
165
+ var _a, _b;
166
+ const s = min(subfeatures.map(f => f.start));
167
+ const e = max(subfeatures.map(f => f.end));
168
+ if (doesIntersect2(s, e, originalQuery.start, originalQuery.end)) {
169
+ const subs = subfeatures.sort((a, b) => a.uniqueId.localeCompare(b.uniqueId));
170
+ observer.next(new SimpleFeature({
171
+ id: `${this.id}-${(_a = subs[0]) === null || _a === void 0 ? void 0 : _a.uniqueId}-parent`,
172
+ data: {
173
+ type: 'gene',
174
+ subfeatures: subs,
175
+ strand: ((_b = subs[0]) === null || _b === void 0 ? void 0 : _b.strand) || 1,
176
+ name,
177
+ start: s,
178
+ end: e,
179
+ refName: query.refName,
180
+ },
181
+ }));
182
+ }
183
+ });
176
184
  });
177
185
  observer.complete();
178
186
  }
@@ -195,5 +203,4 @@ export default class BigBedAdapter extends BaseFeatureDataAdapter {
195
203
  }
196
204
  }, opts.stopToken);
197
205
  }
198
- freeResources() { }
199
206
  }
@@ -1,4 +1,4 @@
1
- import type { MinimalFeature, TranscriptFeat } from './types';
1
+ import type { TranscriptFeat } from './types';
2
2
  export declare function isUcscTranscript({ thickStart, blockCount, strand, }: {
3
3
  thickStart?: number;
4
4
  blockCount?: number;
@@ -9,7 +9,12 @@ export declare function generateUcscTranscript(data: TranscriptFeat): {
9
9
  strand: number;
10
10
  type: string;
11
11
  refName: string;
12
- subfeatures: MinimalFeature[];
12
+ subfeatures: {
13
+ type: string;
14
+ start: number;
15
+ end: number;
16
+ refName: string;
17
+ }[];
13
18
  start: number;
14
19
  end: number;
15
20
  };
@@ -8,81 +8,105 @@ export function generateUcscTranscript(data) {
8
8
  const feats = oldSubfeatures
9
9
  .filter(child => child.type === 'block')
10
10
  .sort((a, b) => a.start - b.start);
11
- for (const block of feats) {
12
- const start = block.start;
13
- const end = block.end;
14
- if (thickStart >= end) {
15
- subfeatures.push({
16
- type: `${strand > 0 ? 'five' : 'three'}_prime_UTR`,
17
- start,
18
- end,
19
- refName,
20
- });
21
- }
22
- else if (thickStart > start && thickStart < end && thickEnd >= end) {
23
- subfeatures.push({
24
- type: `${strand > 0 ? 'five' : 'three'}_prime_UTR`,
25
- start,
26
- end: thickStart,
27
- refName,
28
- }, {
29
- type: 'CDS',
30
- phase: 0,
31
- start: thickStart,
32
- end,
33
- refName,
34
- });
35
- }
36
- else if (thickStart <= start && thickEnd >= end) {
37
- subfeatures.push({
38
- type: 'CDS',
39
- phase: 0,
40
- start,
41
- end,
42
- refName,
43
- });
44
- }
45
- else if (thickStart > start && thickStart < end && thickEnd < end) {
46
- subfeatures.push({
47
- type: `${strand > 0 ? 'five' : 'three'}_prime_UTR`,
48
- start,
49
- end: thickStart,
50
- refName,
51
- }, {
52
- type: 'CDS',
53
- phase: 0,
54
- start: thickStart,
55
- end: thickEnd,
56
- refName,
57
- }, {
58
- type: `${strand > 0 ? 'three' : 'five'}_prime_UTR`,
59
- start: thickEnd,
60
- end,
61
- refName,
62
- });
63
- }
64
- else if (thickStart <= start && thickEnd > start && thickEnd < end) {
65
- subfeatures.push({
66
- type: 'CDS',
67
- phase: 0,
68
- start,
69
- end: thickEnd,
70
- refName,
71
- }, {
72
- type: `${strand > 0 ? 'three' : 'five'}_prime_UTR`,
73
- start: thickEnd,
74
- end,
75
- refName,
76
- });
77
- }
78
- else if (thickEnd <= start) {
79
- subfeatures.push({
80
- type: `${strand > 0 ? 'three' : 'five'}_prime_UTR`,
81
- start,
82
- end,
83
- refName,
84
- });
11
+ const { cdsEndStat, cdsStartStat } = rest2;
12
+ if (cdsStartStat === 'none' && cdsEndStat === 'none') {
13
+ return {
14
+ ...rest2,
15
+ uniqueId,
16
+ strand,
17
+ type: 'transcript',
18
+ refName,
19
+ subfeatures: feats.map(e => ({
20
+ ...e,
21
+ type: 'exon',
22
+ })),
23
+ };
24
+ }
25
+ else {
26
+ for (const block of feats) {
27
+ const start = block.start;
28
+ const end = block.end;
29
+ if (thickStart >= end) {
30
+ subfeatures.push({
31
+ type: `${strand > 0 ? 'five' : 'three'}_prime_UTR`,
32
+ start,
33
+ end,
34
+ refName,
35
+ });
36
+ }
37
+ else if (thickStart > start && thickStart < end && thickEnd >= end) {
38
+ subfeatures.push({
39
+ type: `${strand > 0 ? 'five' : 'three'}_prime_UTR`,
40
+ start,
41
+ end: thickStart,
42
+ refName,
43
+ }, {
44
+ type: 'CDS',
45
+ phase: 0,
46
+ start: thickStart,
47
+ end,
48
+ refName,
49
+ });
50
+ }
51
+ else if (thickStart <= start && thickEnd >= end) {
52
+ subfeatures.push({
53
+ type: 'CDS',
54
+ phase: 0,
55
+ start,
56
+ end,
57
+ refName,
58
+ });
59
+ }
60
+ else if (thickStart > start && thickStart < end && thickEnd < end) {
61
+ subfeatures.push({
62
+ type: `${strand > 0 ? 'five' : 'three'}_prime_UTR`,
63
+ start,
64
+ end: thickStart,
65
+ refName,
66
+ }, {
67
+ type: 'CDS',
68
+ phase: 0,
69
+ start: thickStart,
70
+ end: thickEnd,
71
+ refName,
72
+ }, {
73
+ type: `${strand > 0 ? 'three' : 'five'}_prime_UTR`,
74
+ start: thickEnd,
75
+ end,
76
+ refName,
77
+ });
78
+ }
79
+ else if (thickStart <= start && thickEnd > start && thickEnd < end) {
80
+ subfeatures.push({
81
+ type: 'CDS',
82
+ phase: 0,
83
+ start,
84
+ end: thickEnd,
85
+ refName,
86
+ }, {
87
+ type: `${strand > 0 ? 'three' : 'five'}_prime_UTR`,
88
+ start: thickEnd,
89
+ end,
90
+ refName,
91
+ });
92
+ }
93
+ else if (thickEnd <= start) {
94
+ subfeatures.push({
95
+ type: `${strand > 0 ? 'three' : 'five'}_prime_UTR`,
96
+ start,
97
+ end,
98
+ refName,
99
+ });
100
+ }
85
101
  }
102
+ return {
103
+ ...rest2,
104
+ uniqueId,
105
+ strand,
106
+ type: 'mRNA',
107
+ refName,
108
+ subfeatures,
109
+ };
86
110
  }
87
111
  return {
88
112
  ...rest2,
package/esm/util.d.ts CHANGED
@@ -89,7 +89,12 @@ export declare function featureData({ line, colRef, colStart, colEnd, scoreColum
89
89
  strand: number;
90
90
  type: string;
91
91
  refName: string;
92
- subfeatures: import("./types").MinimalFeature[];
92
+ subfeatures: {
93
+ type: string;
94
+ start: number;
95
+ end: number;
96
+ refName: string;
97
+ }[];
93
98
  start: number;
94
99
  end: number;
95
100
  } | {
@@ -182,7 +187,12 @@ export declare function featureData2({ splitLine, refName, start, end, parser, u
182
187
  strand: number;
183
188
  type: string;
184
189
  refName: string;
185
- subfeatures: import("./types").MinimalFeature[];
190
+ subfeatures: {
191
+ type: string;
192
+ start: number;
193
+ end: number;
194
+ refName: string;
195
+ }[];
186
196
  start: number;
187
197
  end: number;
188
198
  } | {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jbrowse/plugin-bed",
3
- "version": "3.3.0",
3
+ "version": "3.5.0",
4
4
  "description": "JBrowse 2 bed adapters, tracks, etc.",
5
5
  "keywords": [
6
6
  "jbrowse",
@@ -37,11 +37,11 @@
37
37
  },
38
38
  "dependencies": {
39
39
  "@flatten-js/interval-tree": "^1.0.15",
40
- "@gmod/bbi": "^6.0.0",
40
+ "@gmod/bbi": "^7.0.0",
41
41
  "@gmod/bed": "^2.1.2",
42
- "@gmod/bgzf-filehandle": "^2.0.1",
43
- "@gmod/tabix": "^2.0.0",
44
- "@jbrowse/core": "^3.3.0",
42
+ "@gmod/bgzf-filehandle": "^4.0.0",
43
+ "@gmod/tabix": "^3.0.1",
44
+ "@jbrowse/core": "^3.5.0",
45
45
  "mobx": "^6.0.0",
46
46
  "mobx-react": "^9.0.0",
47
47
  "mobx-state-tree": "^5.0.0",
@@ -53,5 +53,5 @@
53
53
  "distModule": "esm/index.js",
54
54
  "srcModule": "src/index.ts",
55
55
  "module": "esm/index.js",
56
- "gitHead": "0bb64d8cc7ecdd167515308b31eec3d9acbc59e4"
56
+ "gitHead": "8a8aa0aab2229dece106a5715a767e649e2fe92b"
57
57
  }