@gmod/bbi 7.0.5 → 8.0.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.
- package/CHANGELOG.md +8 -0
- package/README.md +59 -0
- package/dist/bbi.d.ts +13 -3
- package/dist/bbi.js +81 -18
- package/dist/bbi.js.map +1 -1
- package/dist/bigbed.d.ts +14 -2
- package/dist/bigbed.js +127 -86
- package/dist/bigbed.js.map +1 -1
- package/dist/bigwig.js +1 -2
- package/dist/bigwig.js.map +1 -1
- package/dist/block-view.d.ts +13 -4
- package/dist/block-view.js +332 -150
- package/dist/block-view.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/parse-bigwig.d.ts +3 -0
- package/dist/parse-bigwig.js +15 -0
- package/dist/parse-bigwig.js.map +1 -0
- package/dist/range.js +12 -24
- package/dist/range.js.map +1 -1
- package/dist/types.d.ts +14 -2
- package/dist/unzip.d.ts +18 -1
- package/dist/unzip.js +33 -4
- package/dist/unzip.js.map +1 -1
- package/dist/util.d.ts +2 -4
- package/dist/util.js +7 -5
- package/dist/util.js.map +1 -1
- package/dist/wasm/inflate-wasm-inlined.d.ts +19 -0
- package/dist/wasm/inflate-wasm-inlined.js +117 -0
- package/dist/wasm/inflate-wasm-inlined.js.map +1 -0
- package/dist/wasm/inflate_wasm.d.ts +1 -0
- package/dist/wasm/inflate_wasm.js +43 -0
- package/dist/wasm/inflate_wasm.js.map +1 -0
- package/dist/wasm/inflate_wasm_bg.d.ts +68 -0
- package/dist/wasm/inflate_wasm_bg.js +307 -0
- package/dist/wasm/inflate_wasm_bg.js.map +1 -0
- package/esm/bbi.d.ts +13 -3
- package/esm/bbi.js +81 -18
- package/esm/bbi.js.map +1 -1
- package/esm/bigbed.d.ts +14 -2
- package/esm/bigbed.js +127 -86
- package/esm/bigbed.js.map +1 -1
- package/esm/bigwig.js +1 -2
- package/esm/bigwig.js.map +1 -1
- package/esm/block-view.d.ts +13 -4
- package/esm/block-view.js +334 -152
- package/esm/block-view.js.map +1 -1
- package/esm/index.d.ts +2 -1
- package/esm/index.js +1 -0
- package/esm/index.js.map +1 -1
- package/esm/parse-bigwig.d.ts +3 -0
- package/esm/parse-bigwig.js +12 -0
- package/esm/parse-bigwig.js.map +1 -0
- package/esm/range.js +12 -24
- package/esm/range.js.map +1 -1
- package/esm/types.d.ts +14 -2
- package/esm/unzip.d.ts +18 -1
- package/esm/unzip.js +30 -3
- package/esm/unzip.js.map +1 -1
- package/esm/util.d.ts +2 -4
- package/esm/util.js +7 -5
- package/esm/util.js.map +1 -1
- package/esm/wasm/inflate-wasm-inlined.d.ts +19 -0
- package/esm/wasm/inflate-wasm-inlined.js +111 -0
- package/esm/wasm/inflate-wasm-inlined.js.map +1 -0
- package/esm/wasm/inflate_wasm.d.ts +1 -0
- package/esm/wasm/inflate_wasm.js +5 -0
- package/esm/wasm/inflate_wasm.js.map +1 -0
- package/esm/wasm/inflate_wasm_bg.d.ts +68 -0
- package/esm/wasm/inflate_wasm_bg.js +296 -0
- package/esm/wasm/inflate_wasm_bg.js.map +1 -0
- package/package.json +21 -17
- package/src/bbi.ts +101 -21
- package/src/bigbed.ts +165 -83
- package/src/bigwig.ts +1 -2
- package/src/block-view.ts +415 -158
- package/src/index.ts +8 -1
- package/src/parse-bigwig.ts +19 -0
- package/src/range.ts +13 -21
- package/src/types.ts +19 -2
- package/src/unzip.ts +88 -3
- package/src/util.ts +9 -10
- package/src/wasm/inflate-wasm-inlined.d.ts +49 -0
- package/src/wasm/inflate-wasm-inlined.js +1 -0
- package/src/wasm/inflate_wasm.d.ts +35 -0
- package/src/wasm/inflate_wasm.js +4 -0
- package/src/wasm/inflate_wasm_bg.js +309 -0
- package/src/wasm/inflate_wasm_bg.wasm +0 -0
- package/src/wasm/inflate_wasm_bg.wasm.d.ts +13 -0
package/dist/block-view.js
CHANGED
|
@@ -9,7 +9,7 @@ const quick_lru_1 = __importDefault(require("quick-lru"));
|
|
|
9
9
|
const range_ts_1 = __importDefault(require("./range.js"));
|
|
10
10
|
const unzip_ts_1 = require("./unzip.js");
|
|
11
11
|
const util_ts_1 = require("./util.js");
|
|
12
|
-
const decoder =
|
|
12
|
+
const decoder = new TextDecoder('utf8');
|
|
13
13
|
function coordFilter(s1, e1, s2, e2) {
|
|
14
14
|
return s1 < e2 && e1 >= s2;
|
|
15
15
|
}
|
|
@@ -20,18 +20,30 @@ function coordFilter(s1, e1, s2, e2) {
|
|
|
20
20
|
* Genome Explorer by Thomas Down.
|
|
21
21
|
*/
|
|
22
22
|
class BlockView {
|
|
23
|
-
|
|
23
|
+
bbi;
|
|
24
|
+
refsByName;
|
|
25
|
+
rTreeOffset;
|
|
26
|
+
uncompressBufSize;
|
|
27
|
+
blockType;
|
|
28
|
+
// R-tree index header cache - R-trees are spatial data structures used to
|
|
29
|
+
// efficiently query genomic intervals by chromosome and position
|
|
30
|
+
rTreePromise;
|
|
31
|
+
featureCache = new abortable_promise_cache_1.default({
|
|
32
|
+
cache: new quick_lru_1.default({ maxSize: 1000 }),
|
|
33
|
+
fill: async ({ length, offset }, signal) => this.bbi.read(length, offset, { signal }),
|
|
34
|
+
});
|
|
35
|
+
constructor(bbi, refsByName,
|
|
36
|
+
// Offset to the R-tree index in the file - this is part of the "cirTree"
|
|
37
|
+
// (combined ID R-tree), which combines a B+ tree for chromosome names
|
|
38
|
+
// with an R-tree for efficient spatial queries
|
|
39
|
+
rTreeOffset, uncompressBufSize, blockType) {
|
|
24
40
|
this.bbi = bbi;
|
|
25
41
|
this.refsByName = refsByName;
|
|
26
|
-
this.
|
|
27
|
-
this.
|
|
42
|
+
this.rTreeOffset = rTreeOffset;
|
|
43
|
+
this.uncompressBufSize = uncompressBufSize;
|
|
28
44
|
this.blockType = blockType;
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
fill: async ({ length, offset }, signal) => this.bbi.read(length, offset, { signal }),
|
|
32
|
-
});
|
|
33
|
-
if (!(cirTreeOffset >= 0)) {
|
|
34
|
-
throw new Error('invalid cirTreeOffset!');
|
|
45
|
+
if (!(rTreeOffset >= 0)) {
|
|
46
|
+
throw new Error('invalid rTreeOffset!');
|
|
35
47
|
}
|
|
36
48
|
}
|
|
37
49
|
async readWigData(chrName, start, end, observer, opts) {
|
|
@@ -39,105 +51,98 @@ class BlockView {
|
|
|
39
51
|
const chrId = this.refsByName[chrName];
|
|
40
52
|
if (chrId === undefined) {
|
|
41
53
|
observer.complete();
|
|
54
|
+
return;
|
|
42
55
|
}
|
|
43
56
|
const request = { chrId, start, end };
|
|
44
|
-
if (!this.
|
|
45
|
-
this.
|
|
57
|
+
if (!this.rTreePromise) {
|
|
58
|
+
this.rTreePromise = this.bbi.read(48, this.rTreeOffset, opts);
|
|
46
59
|
}
|
|
47
|
-
const buffer = await this.
|
|
48
|
-
const dataView = new DataView(buffer.buffer);
|
|
49
|
-
|
|
50
|
-
|
|
60
|
+
const buffer = await this.rTreePromise;
|
|
61
|
+
const dataView = new DataView(buffer.buffer, buffer.byteOffset, buffer.length);
|
|
62
|
+
// Maximum number of children per R-tree node - used to calculate memory bounds
|
|
63
|
+
const rTreeBlockSize = dataView.getUint32(4, true);
|
|
64
|
+
const blocksToFetch = [];
|
|
51
65
|
let outstanding = 0;
|
|
52
|
-
|
|
66
|
+
// R-tree leaf nodes contain the actual data blocks to fetch
|
|
67
|
+
const processLeafNode = (dataView, startOffset, count) => {
|
|
68
|
+
let offset = startOffset;
|
|
69
|
+
for (let i = 0; i < count; i++) {
|
|
70
|
+
const startChrom = dataView.getUint32(offset, true);
|
|
71
|
+
offset += 4;
|
|
72
|
+
const startBase = dataView.getUint32(offset, true);
|
|
73
|
+
offset += 4;
|
|
74
|
+
const endChrom = dataView.getUint32(offset, true);
|
|
75
|
+
offset += 4;
|
|
76
|
+
const endBase = dataView.getUint32(offset, true);
|
|
77
|
+
offset += 4;
|
|
78
|
+
const blockOffset = Number(dataView.getBigUint64(offset, true));
|
|
79
|
+
offset += 8;
|
|
80
|
+
const blockSize = Number(dataView.getBigUint64(offset, true));
|
|
81
|
+
offset += 8;
|
|
82
|
+
if (blockIntersectsQuery({ startChrom, startBase, endBase, endChrom })) {
|
|
83
|
+
blocksToFetch.push({
|
|
84
|
+
offset: blockOffset,
|
|
85
|
+
length: blockSize,
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
// R-tree non-leaf nodes contain pointers to child nodes
|
|
91
|
+
const processNonLeafNode = (dataView, startOffset, count, level) => {
|
|
92
|
+
const recurOffsets = [];
|
|
93
|
+
let offset = startOffset;
|
|
94
|
+
for (let i = 0; i < count; i++) {
|
|
95
|
+
const startChrom = dataView.getUint32(offset, true);
|
|
96
|
+
offset += 4;
|
|
97
|
+
const startBase = dataView.getUint32(offset, true);
|
|
98
|
+
offset += 4;
|
|
99
|
+
const endChrom = dataView.getUint32(offset, true);
|
|
100
|
+
offset += 4;
|
|
101
|
+
const endBase = dataView.getUint32(offset, true);
|
|
102
|
+
offset += 4;
|
|
103
|
+
const blockOffset = Number(dataView.getBigUint64(offset, true));
|
|
104
|
+
offset += 8;
|
|
105
|
+
if (blockIntersectsQuery({ startChrom, startBase, endChrom, endBase })) {
|
|
106
|
+
recurOffsets.push(blockOffset);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
if (recurOffsets.length > 0) {
|
|
110
|
+
traverseRTree(recurOffsets, level + 1);
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
const processRTreeNode = (rTreeBlockData, offset2, level) => {
|
|
53
114
|
try {
|
|
54
|
-
const data =
|
|
55
|
-
const
|
|
56
|
-
const dataView = new DataView(b.buffer, b.byteOffset, b.length);
|
|
115
|
+
const data = rTreeBlockData.subarray(offset2);
|
|
116
|
+
const dataView = new DataView(data.buffer, data.byteOffset, data.length);
|
|
57
117
|
let offset = 0;
|
|
58
118
|
const isLeaf = dataView.getUint8(offset);
|
|
59
|
-
offset += 2; // 1 skip
|
|
60
|
-
const
|
|
119
|
+
offset += 2; // 1 skip for reserved byte
|
|
120
|
+
const count = dataView.getUint16(offset, true);
|
|
61
121
|
offset += 2;
|
|
62
122
|
if (isLeaf === 1) {
|
|
63
|
-
|
|
64
|
-
for (let i = 0; i < cnt; i++) {
|
|
65
|
-
const startChrom = dataView.getUint32(offset, true);
|
|
66
|
-
offset += 4;
|
|
67
|
-
const startBase = dataView.getUint32(offset, true);
|
|
68
|
-
offset += 4;
|
|
69
|
-
const endChrom = dataView.getUint32(offset, true);
|
|
70
|
-
offset += 4;
|
|
71
|
-
const endBase = dataView.getUint32(offset, true);
|
|
72
|
-
offset += 4;
|
|
73
|
-
const blockOffset = Number(dataView.getBigUint64(offset, true));
|
|
74
|
-
offset += 8;
|
|
75
|
-
const blockSize = Number(dataView.getBigUint64(offset, true));
|
|
76
|
-
offset += 8;
|
|
77
|
-
blocksToFetch2.push({
|
|
78
|
-
startChrom,
|
|
79
|
-
startBase,
|
|
80
|
-
endBase,
|
|
81
|
-
endChrom,
|
|
82
|
-
blockOffset,
|
|
83
|
-
blockSize,
|
|
84
|
-
offset,
|
|
85
|
-
});
|
|
86
|
-
}
|
|
87
|
-
blocksToFetch = blocksToFetch.concat(blocksToFetch2
|
|
88
|
-
.filter(f => filterFeats(f))
|
|
89
|
-
.map(l => ({
|
|
90
|
-
offset: l.blockOffset,
|
|
91
|
-
length: l.blockSize,
|
|
92
|
-
})));
|
|
123
|
+
processLeafNode(dataView, offset, count);
|
|
93
124
|
}
|
|
94
125
|
else if (isLeaf === 0) {
|
|
95
|
-
|
|
96
|
-
for (let i = 0; i < cnt; i++) {
|
|
97
|
-
const startChrom = dataView.getUint32(offset, true);
|
|
98
|
-
offset += 4;
|
|
99
|
-
const startBase = dataView.getUint32(offset, true);
|
|
100
|
-
offset += 4;
|
|
101
|
-
const endChrom = dataView.getUint32(offset, true);
|
|
102
|
-
offset += 4;
|
|
103
|
-
const endBase = dataView.getUint32(offset, true);
|
|
104
|
-
offset += 4;
|
|
105
|
-
const blockOffset = Number(dataView.getBigUint64(offset, true));
|
|
106
|
-
offset += 8;
|
|
107
|
-
recurOffsets.push({
|
|
108
|
-
startChrom,
|
|
109
|
-
startBase,
|
|
110
|
-
endChrom,
|
|
111
|
-
endBase,
|
|
112
|
-
blockOffset,
|
|
113
|
-
offset,
|
|
114
|
-
});
|
|
115
|
-
}
|
|
116
|
-
const recurOffsets2 = recurOffsets
|
|
117
|
-
.filter(f => filterFeats(f))
|
|
118
|
-
.map(l => l.blockOffset);
|
|
119
|
-
if (recurOffsets2.length > 0) {
|
|
120
|
-
cirFobRecur(recurOffsets2, level + 1);
|
|
121
|
-
}
|
|
126
|
+
processNonLeafNode(dataView, offset, count, level);
|
|
122
127
|
}
|
|
123
128
|
}
|
|
124
129
|
catch (e) {
|
|
125
130
|
observer.error(e);
|
|
126
131
|
}
|
|
127
132
|
};
|
|
128
|
-
const
|
|
133
|
+
const blockIntersectsQuery = (b) => {
|
|
129
134
|
const { startChrom, startBase, endChrom, endBase } = b;
|
|
130
135
|
return ((startChrom < chrId || (startChrom === chrId && startBase <= end)) &&
|
|
131
136
|
(endChrom > chrId || (endChrom === chrId && endBase >= start)));
|
|
132
137
|
};
|
|
133
|
-
const
|
|
138
|
+
const fetchAndProcessRTreeBlocks = async (offsets, range, level) => {
|
|
134
139
|
try {
|
|
135
|
-
const length =
|
|
136
|
-
const offset =
|
|
140
|
+
const length = range.max - range.min;
|
|
141
|
+
const offset = range.min;
|
|
137
142
|
const resultBuffer = await this.featureCache.get(`${length}_${offset}`, { length, offset }, opts?.signal);
|
|
138
|
-
for (const element of
|
|
139
|
-
if (
|
|
140
|
-
|
|
143
|
+
for (const element of offsets) {
|
|
144
|
+
if (range.contains(element)) {
|
|
145
|
+
processRTreeNode(resultBuffer, element - offset, level);
|
|
141
146
|
outstanding -= 1;
|
|
142
147
|
if (outstanding === 0) {
|
|
143
148
|
this.readFeatures(observer, blocksToFetch, {
|
|
@@ -154,34 +159,37 @@ class BlockView {
|
|
|
154
159
|
observer.error(e);
|
|
155
160
|
}
|
|
156
161
|
};
|
|
157
|
-
const
|
|
162
|
+
const traverseRTree = (offsets, level) => {
|
|
158
163
|
try {
|
|
159
|
-
outstanding +=
|
|
164
|
+
outstanding += offsets.length;
|
|
160
165
|
// Upper bound on size, based on a completely full leaf node.
|
|
161
|
-
const
|
|
166
|
+
const maxRTreeBlockSpan = 4 + rTreeBlockSize * 32;
|
|
162
167
|
let spans = new range_ts_1.default([
|
|
163
168
|
{
|
|
164
|
-
min:
|
|
165
|
-
max:
|
|
169
|
+
min: offsets[0],
|
|
170
|
+
max: offsets[0] + maxRTreeBlockSpan,
|
|
166
171
|
},
|
|
167
172
|
]);
|
|
168
|
-
for (let i = 1; i <
|
|
173
|
+
for (let i = 1; i < offsets.length; i += 1) {
|
|
169
174
|
const blockSpan = new range_ts_1.default([
|
|
170
175
|
{
|
|
171
|
-
min:
|
|
172
|
-
max:
|
|
176
|
+
min: offsets[i],
|
|
177
|
+
max: offsets[i] + maxRTreeBlockSpan,
|
|
173
178
|
},
|
|
174
179
|
]);
|
|
175
180
|
spans = spans.union(blockSpan);
|
|
176
181
|
}
|
|
177
|
-
|
|
178
|
-
|
|
182
|
+
spans.getRanges().forEach(range => {
|
|
183
|
+
fetchAndProcessRTreeBlocks(offsets, range, level).catch((e) => {
|
|
184
|
+
observer.error(e);
|
|
185
|
+
});
|
|
186
|
+
});
|
|
179
187
|
}
|
|
180
188
|
catch (e) {
|
|
181
189
|
observer.error(e);
|
|
182
190
|
}
|
|
183
191
|
};
|
|
184
|
-
|
|
192
|
+
traverseRTree([this.rTreeOffset + 48], 1);
|
|
185
193
|
return;
|
|
186
194
|
}
|
|
187
195
|
catch (e) {
|
|
@@ -193,8 +201,6 @@ class BlockView {
|
|
|
193
201
|
let offset = startOffset;
|
|
194
202
|
const dataView = new DataView(b.buffer, b.byteOffset, b.length);
|
|
195
203
|
while (offset < b.byteLength) {
|
|
196
|
-
// this was extracted from looking at the runtime code generated by
|
|
197
|
-
// binary-parser
|
|
198
204
|
const chromId = dataView.getUint32(offset, true);
|
|
199
205
|
offset += 4;
|
|
200
206
|
const start = dataView.getUint32(offset, true);
|
|
@@ -208,14 +214,10 @@ class BlockView {
|
|
|
208
214
|
const maxScore = dataView.getFloat32(offset, true);
|
|
209
215
|
offset += 4;
|
|
210
216
|
const sumData = dataView.getFloat32(offset, true);
|
|
211
|
-
offset +=
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
if (request
|
|
216
|
-
? chromId === request.chrId &&
|
|
217
|
-
coordFilter(start, end, request.start, request.end)
|
|
218
|
-
: true) {
|
|
217
|
+
offset += 8;
|
|
218
|
+
if (!request ||
|
|
219
|
+
(chromId === request.chrId &&
|
|
220
|
+
coordFilter(start, end, request.start, request.end))) {
|
|
219
221
|
features.push({
|
|
220
222
|
start,
|
|
221
223
|
end,
|
|
@@ -231,8 +233,7 @@ class BlockView {
|
|
|
231
233
|
parseBigBedBlock(data, startOffset, offset, request) {
|
|
232
234
|
const items = [];
|
|
233
235
|
let currOffset = startOffset;
|
|
234
|
-
const
|
|
235
|
-
const dataView = new DataView(b.buffer, b.byteOffset, b.length);
|
|
236
|
+
const dataView = new DataView(data.buffer, data.byteOffset, data.length);
|
|
236
237
|
while (currOffset < data.byteLength) {
|
|
237
238
|
const c2 = currOffset;
|
|
238
239
|
const chromId = dataView.getUint32(currOffset, true);
|
|
@@ -248,19 +249,20 @@ class BlockView {
|
|
|
248
249
|
}
|
|
249
250
|
}
|
|
250
251
|
const b = data.subarray(currOffset, i);
|
|
251
|
-
const rest = decoder
|
|
252
|
+
const rest = decoder.decode(b);
|
|
252
253
|
currOffset = i + 1;
|
|
253
|
-
|
|
254
|
-
chromId
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
254
|
+
if (!request ||
|
|
255
|
+
(chromId === request.chrId &&
|
|
256
|
+
coordFilter(start, end, request.start, request.end))) {
|
|
257
|
+
items.push({
|
|
258
|
+
start,
|
|
259
|
+
end,
|
|
260
|
+
rest,
|
|
261
|
+
uniqueId: `bb-${offset + c2}`,
|
|
262
|
+
});
|
|
263
|
+
}
|
|
260
264
|
}
|
|
261
|
-
return
|
|
262
|
-
? items.filter((f) => coordFilter(f.start, f.end, request.start, request.end))
|
|
263
|
-
: items;
|
|
265
|
+
return items;
|
|
264
266
|
}
|
|
265
267
|
parseBigWigBlock(buffer, startOffset, req) {
|
|
266
268
|
const b = buffer.subarray(startOffset);
|
|
@@ -277,7 +279,7 @@ class BlockView {
|
|
|
277
279
|
offset += 2;
|
|
278
280
|
const itemCount = dataView.getUint16(offset, true);
|
|
279
281
|
offset += 2;
|
|
280
|
-
const items =
|
|
282
|
+
const items = [];
|
|
281
283
|
switch (blockType) {
|
|
282
284
|
case 1: {
|
|
283
285
|
for (let i = 0; i < itemCount; i++) {
|
|
@@ -287,11 +289,13 @@ class BlockView {
|
|
|
287
289
|
offset += 4;
|
|
288
290
|
const score = dataView.getFloat32(offset, true);
|
|
289
291
|
offset += 4;
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
292
|
+
if (!req || coordFilter(start, end, req.start, req.end)) {
|
|
293
|
+
items.push({
|
|
294
|
+
start,
|
|
295
|
+
end,
|
|
296
|
+
score,
|
|
297
|
+
});
|
|
298
|
+
}
|
|
295
299
|
}
|
|
296
300
|
break;
|
|
297
301
|
}
|
|
@@ -301,11 +305,14 @@ class BlockView {
|
|
|
301
305
|
offset += 4;
|
|
302
306
|
const score = dataView.getFloat32(offset, true);
|
|
303
307
|
offset += 4;
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
308
|
+
const end = start + itemSpan;
|
|
309
|
+
if (!req || coordFilter(start, end, req.start, req.end)) {
|
|
310
|
+
items.push({
|
|
311
|
+
score,
|
|
312
|
+
start,
|
|
313
|
+
end,
|
|
314
|
+
});
|
|
315
|
+
}
|
|
309
316
|
}
|
|
310
317
|
break;
|
|
311
318
|
}
|
|
@@ -314,36 +321,49 @@ class BlockView {
|
|
|
314
321
|
const score = dataView.getFloat32(offset, true);
|
|
315
322
|
offset += 4;
|
|
316
323
|
const start = blockStart + i * itemStep;
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
324
|
+
const end = start + itemSpan;
|
|
325
|
+
if (!req || coordFilter(start, end, req.start, req.end)) {
|
|
326
|
+
items.push({
|
|
327
|
+
score,
|
|
328
|
+
start,
|
|
329
|
+
end,
|
|
330
|
+
});
|
|
331
|
+
}
|
|
322
332
|
}
|
|
323
333
|
break;
|
|
324
334
|
}
|
|
325
335
|
}
|
|
326
|
-
return
|
|
327
|
-
? items.filter(f => coordFilter(f.start, f.end, req.start, req.end))
|
|
328
|
-
: items;
|
|
336
|
+
return items;
|
|
329
337
|
}
|
|
330
338
|
async readFeatures(observer, blocks, opts = {}) {
|
|
331
339
|
try {
|
|
332
|
-
const { blockType,
|
|
340
|
+
const { blockType, uncompressBufSize } = this;
|
|
333
341
|
const { signal, request } = opts;
|
|
334
342
|
const blockGroupsToFetch = (0, util_ts_1.groupBlocks)(blocks);
|
|
335
|
-
(0, util_ts_1.checkAbortSignal)(signal);
|
|
336
343
|
await Promise.all(blockGroupsToFetch.map(async (blockGroup) => {
|
|
337
|
-
(0, util_ts_1.checkAbortSignal)(signal);
|
|
338
344
|
const { length, offset } = blockGroup;
|
|
339
345
|
const data = await this.featureCache.get(`${length}_${offset}`, blockGroup, signal);
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
346
|
+
const localBlocks = blockGroup.blocks.map(block => ({
|
|
347
|
+
offset: block.offset - blockGroup.offset,
|
|
348
|
+
length: block.length,
|
|
349
|
+
}));
|
|
350
|
+
let decompressedData;
|
|
351
|
+
let decompressedOffsets;
|
|
352
|
+
if (uncompressBufSize > 0) {
|
|
353
|
+
const result = await (0, unzip_ts_1.unzipBatch)(data, localBlocks, uncompressBufSize);
|
|
354
|
+
decompressedData = result.data;
|
|
355
|
+
decompressedOffsets = result.offsets;
|
|
356
|
+
}
|
|
357
|
+
else {
|
|
358
|
+
decompressedData = data;
|
|
359
|
+
decompressedOffsets = localBlocks.map(b => b.offset);
|
|
360
|
+
decompressedOffsets.push(data.length);
|
|
361
|
+
}
|
|
362
|
+
for (let i = 0; i < blockGroup.blocks.length; i++) {
|
|
363
|
+
const block = blockGroup.blocks[i];
|
|
364
|
+
const start = decompressedOffsets[i];
|
|
365
|
+
const end = decompressedOffsets[i + 1];
|
|
366
|
+
const resultData = decompressedData.subarray(start, end);
|
|
347
367
|
switch (blockType) {
|
|
348
368
|
case 'summary': {
|
|
349
369
|
observer.next(this.parseSummaryBlock(resultData, 0, request));
|
|
@@ -354,7 +374,7 @@ class BlockView {
|
|
|
354
374
|
break;
|
|
355
375
|
}
|
|
356
376
|
case 'bigbed': {
|
|
357
|
-
observer.next(this.parseBigBedBlock(resultData, 0,
|
|
377
|
+
observer.next(this.parseBigBedBlock(resultData, 0, block.offset * (1 << 8), request));
|
|
358
378
|
break;
|
|
359
379
|
}
|
|
360
380
|
default: {
|
|
@@ -369,6 +389,168 @@ class BlockView {
|
|
|
369
389
|
observer.error(e);
|
|
370
390
|
}
|
|
371
391
|
}
|
|
392
|
+
async readBigWigFeaturesAsArrays(blocks, opts = {}) {
|
|
393
|
+
const { uncompressBufSize } = this;
|
|
394
|
+
const { signal, request } = opts;
|
|
395
|
+
const blockGroupsToFetch = (0, util_ts_1.groupBlocks)(blocks);
|
|
396
|
+
const allStarts = [];
|
|
397
|
+
const allEnds = [];
|
|
398
|
+
const allScores = [];
|
|
399
|
+
let totalCount = 0;
|
|
400
|
+
await Promise.all(blockGroupsToFetch.map(async (blockGroup) => {
|
|
401
|
+
const { length, offset } = blockGroup;
|
|
402
|
+
const data = await this.featureCache.get(`${length}_${offset}`, blockGroup, signal);
|
|
403
|
+
const localBlocks = blockGroup.blocks.map(block => ({
|
|
404
|
+
offset: block.offset - blockGroup.offset,
|
|
405
|
+
length: block.length,
|
|
406
|
+
}));
|
|
407
|
+
if (uncompressBufSize > 0) {
|
|
408
|
+
const result = await (0, unzip_ts_1.decompressAndParseBigWigBlocks)(data, localBlocks, uncompressBufSize, request?.start ?? 0, request?.end ?? 0);
|
|
409
|
+
if (result.starts.length > 0) {
|
|
410
|
+
allStarts.push(result.starts);
|
|
411
|
+
allEnds.push(result.ends);
|
|
412
|
+
allScores.push(result.scores);
|
|
413
|
+
totalCount += result.starts.length;
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
else {
|
|
417
|
+
for (const block of localBlocks) {
|
|
418
|
+
const blockData = data.subarray(block.offset, block.offset + block.length);
|
|
419
|
+
const features = this.parseBigWigBlock(blockData, 0, request);
|
|
420
|
+
if (features.length > 0) {
|
|
421
|
+
const starts = new Int32Array(features.length);
|
|
422
|
+
const ends = new Int32Array(features.length);
|
|
423
|
+
const scores = new Float32Array(features.length);
|
|
424
|
+
for (let i = 0; i < features.length; i++) {
|
|
425
|
+
const f = features[i];
|
|
426
|
+
starts[i] = f.start;
|
|
427
|
+
ends[i] = f.end;
|
|
428
|
+
scores[i] = f.score;
|
|
429
|
+
}
|
|
430
|
+
allStarts.push(starts);
|
|
431
|
+
allEnds.push(ends);
|
|
432
|
+
allScores.push(scores);
|
|
433
|
+
totalCount += features.length;
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
}));
|
|
438
|
+
if (allStarts.length === 0) {
|
|
439
|
+
return {
|
|
440
|
+
starts: new Int32Array(0),
|
|
441
|
+
ends: new Int32Array(0),
|
|
442
|
+
scores: new Float32Array(0),
|
|
443
|
+
};
|
|
444
|
+
}
|
|
445
|
+
if (allStarts.length === 1) {
|
|
446
|
+
return {
|
|
447
|
+
starts: allStarts[0],
|
|
448
|
+
ends: allEnds[0],
|
|
449
|
+
scores: allScores[0],
|
|
450
|
+
};
|
|
451
|
+
}
|
|
452
|
+
const starts = new Int32Array(totalCount);
|
|
453
|
+
const ends = new Int32Array(totalCount);
|
|
454
|
+
const scores = new Float32Array(totalCount);
|
|
455
|
+
let offset = 0;
|
|
456
|
+
for (let i = 0; i < allStarts.length; i++) {
|
|
457
|
+
starts.set(allStarts[i], offset);
|
|
458
|
+
ends.set(allEnds[i], offset);
|
|
459
|
+
scores.set(allScores[i], offset);
|
|
460
|
+
offset += allStarts[i].length;
|
|
461
|
+
}
|
|
462
|
+
return { starts, ends, scores };
|
|
463
|
+
}
|
|
464
|
+
async readSummaryFeaturesAsArrays(blocks, opts = {}) {
|
|
465
|
+
const { uncompressBufSize } = this;
|
|
466
|
+
const { signal, request } = opts;
|
|
467
|
+
const blockGroupsToFetch = (0, util_ts_1.groupBlocks)(blocks);
|
|
468
|
+
const allStarts = [];
|
|
469
|
+
const allEnds = [];
|
|
470
|
+
const allScores = [];
|
|
471
|
+
const allMinScores = [];
|
|
472
|
+
const allMaxScores = [];
|
|
473
|
+
let totalCount = 0;
|
|
474
|
+
await Promise.all(blockGroupsToFetch.map(async (blockGroup) => {
|
|
475
|
+
const { length, offset } = blockGroup;
|
|
476
|
+
const data = await this.featureCache.get(`${length}_${offset}`, blockGroup, signal);
|
|
477
|
+
const localBlocks = blockGroup.blocks.map(block => ({
|
|
478
|
+
offset: block.offset - blockGroup.offset,
|
|
479
|
+
length: block.length,
|
|
480
|
+
}));
|
|
481
|
+
if (uncompressBufSize > 0) {
|
|
482
|
+
const result = await (0, unzip_ts_1.decompressAndParseSummaryBlocks)(data, localBlocks, uncompressBufSize, request?.chrId ?? 0, request?.start ?? 0, request?.end ?? 0);
|
|
483
|
+
if (result.starts.length > 0) {
|
|
484
|
+
allStarts.push(result.starts);
|
|
485
|
+
allEnds.push(result.ends);
|
|
486
|
+
allScores.push(result.scores);
|
|
487
|
+
allMinScores.push(result.minScores);
|
|
488
|
+
allMaxScores.push(result.maxScores);
|
|
489
|
+
totalCount += result.starts.length;
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
else {
|
|
493
|
+
for (const block of localBlocks) {
|
|
494
|
+
const blockData = data.subarray(block.offset, block.offset + block.length);
|
|
495
|
+
const features = this.parseSummaryBlock(blockData, 0, request);
|
|
496
|
+
if (features.length > 0) {
|
|
497
|
+
const starts = new Int32Array(features.length);
|
|
498
|
+
const ends = new Int32Array(features.length);
|
|
499
|
+
const scores = new Float32Array(features.length);
|
|
500
|
+
const minScores = new Float32Array(features.length);
|
|
501
|
+
const maxScores = new Float32Array(features.length);
|
|
502
|
+
for (let i = 0; i < features.length; i++) {
|
|
503
|
+
const f = features[i];
|
|
504
|
+
starts[i] = f.start;
|
|
505
|
+
ends[i] = f.end;
|
|
506
|
+
scores[i] = f.score ?? 0;
|
|
507
|
+
minScores[i] = f.minScore ?? 0;
|
|
508
|
+
maxScores[i] = f.maxScore ?? 0;
|
|
509
|
+
}
|
|
510
|
+
allStarts.push(starts);
|
|
511
|
+
allEnds.push(ends);
|
|
512
|
+
allScores.push(scores);
|
|
513
|
+
allMinScores.push(minScores);
|
|
514
|
+
allMaxScores.push(maxScores);
|
|
515
|
+
totalCount += features.length;
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
}));
|
|
520
|
+
if (allStarts.length === 0) {
|
|
521
|
+
return {
|
|
522
|
+
starts: new Int32Array(0),
|
|
523
|
+
ends: new Int32Array(0),
|
|
524
|
+
scores: new Float32Array(0),
|
|
525
|
+
minScores: new Float32Array(0),
|
|
526
|
+
maxScores: new Float32Array(0),
|
|
527
|
+
};
|
|
528
|
+
}
|
|
529
|
+
if (allStarts.length === 1) {
|
|
530
|
+
return {
|
|
531
|
+
starts: allStarts[0],
|
|
532
|
+
ends: allEnds[0],
|
|
533
|
+
scores: allScores[0],
|
|
534
|
+
minScores: allMinScores[0],
|
|
535
|
+
maxScores: allMaxScores[0],
|
|
536
|
+
};
|
|
537
|
+
}
|
|
538
|
+
const starts = new Int32Array(totalCount);
|
|
539
|
+
const ends = new Int32Array(totalCount);
|
|
540
|
+
const scores = new Float32Array(totalCount);
|
|
541
|
+
const minScores = new Float32Array(totalCount);
|
|
542
|
+
const maxScores = new Float32Array(totalCount);
|
|
543
|
+
let offset = 0;
|
|
544
|
+
for (let i = 0; i < allStarts.length; i++) {
|
|
545
|
+
starts.set(allStarts[i], offset);
|
|
546
|
+
ends.set(allEnds[i], offset);
|
|
547
|
+
scores.set(allScores[i], offset);
|
|
548
|
+
minScores.set(allMinScores[i], offset);
|
|
549
|
+
maxScores.set(allMaxScores[i], offset);
|
|
550
|
+
offset += allStarts[i].length;
|
|
551
|
+
}
|
|
552
|
+
return { starts, ends, scores, minScores, maxScores };
|
|
553
|
+
}
|
|
372
554
|
}
|
|
373
555
|
exports.BlockView = BlockView;
|
|
374
556
|
//# sourceMappingURL=block-view.js.map
|