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