@gmod/bbi 8.1.2 → 9.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 +2 -0
- package/README.md +0 -24
- package/dist/array-feature-view.js.map +1 -1
- package/dist/bbi.d.ts +5 -25
- package/dist/bbi.js +31 -133
- package/dist/bbi.js.map +1 -1
- package/dist/bigbed.d.ts +0 -1
- package/dist/bigbed.js +19 -51
- package/dist/bigbed.js.map +1 -1
- package/dist/bigwig.d.ts +1 -2
- package/dist/bigwig.js +1 -2
- package/dist/bigwig.js.map +1 -1
- package/dist/block-view.d.ts +7 -16
- package/dist/block-view.js +369 -443
- package/dist/block-view.js.map +1 -1
- package/dist/parse-bigwig.d.ts +2 -2
- package/dist/parse-bigwig.js +5 -5
- package/dist/parse-bigwig.js.map +1 -1
- package/dist/range.d.ts +1 -15
- package/dist/range.js +16 -49
- package/dist/range.js.map +1 -1
- package/dist/util.d.ts +0 -22
- package/dist/util.js +0 -46
- package/dist/util.js.map +1 -1
- package/dist/wasm/inflate-wasm-inlined.js +1 -1
- package/dist/wasm/inflate-wasm-inlined.js.map +1 -1
- package/esm/array-feature-view.js.map +1 -1
- package/esm/bbi.d.ts +5 -25
- package/esm/bbi.js +31 -133
- package/esm/bbi.js.map +1 -1
- package/esm/bigbed.d.ts +0 -1
- package/esm/bigbed.js +19 -50
- package/esm/bigbed.js.map +1 -1
- package/esm/bigwig.d.ts +1 -2
- package/esm/bigwig.js +1 -2
- package/esm/bigwig.js.map +1 -1
- package/esm/block-view.d.ts +7 -16
- package/esm/block-view.js +369 -443
- package/esm/block-view.js.map +1 -1
- package/esm/parse-bigwig.d.ts +2 -2
- package/esm/parse-bigwig.js +5 -5
- package/esm/parse-bigwig.js.map +1 -1
- package/esm/range.d.ts +1 -15
- package/esm/range.js +15 -48
- package/esm/range.js.map +1 -1
- package/esm/util.d.ts +0 -22
- package/esm/util.js +0 -42
- package/esm/util.js.map +1 -1
- package/esm/wasm/inflate-wasm-inlined.js +1 -1
- package/esm/wasm/inflate-wasm-inlined.js.map +1 -1
- package/package.json +3 -6
- package/src/array-feature-view.ts +4 -1
- package/src/bbi.ts +50 -153
- package/src/bigbed.ts +22 -55
- package/src/bigwig.ts +1 -3
- package/src/block-view.ts +525 -639
- package/src/parse-bigwig.ts +7 -9
- package/src/range.ts +19 -58
- package/src/util.ts +0 -46
- package/src/wasm/inflate-wasm-inlined.js +1 -1
- package/src/wasm/inflate_wasm_bg.wasm +0 -0
package/src/block-view.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import AbortablePromiseCache from '@gmod/abortable-promise-cache'
|
|
2
2
|
import QuickLRU from '@jbrowse/quick-lru'
|
|
3
3
|
|
|
4
|
-
import
|
|
4
|
+
import { mergeRanges } from './range.ts'
|
|
5
5
|
import {
|
|
6
6
|
decompressAndParseBigWigBlocks,
|
|
7
7
|
decompressAndParseSummaryBlocks,
|
|
@@ -12,7 +12,6 @@ import { groupBlocks } from './util.ts'
|
|
|
12
12
|
import type { Feature } from './types.ts'
|
|
13
13
|
import type { BigWigFeatureArrays, SummaryFeatureArrays } from './unzip.ts'
|
|
14
14
|
import type { GenericFilehandle } from 'generic-filehandle2'
|
|
15
|
-
import type { Observer } from 'rxjs'
|
|
16
15
|
|
|
17
16
|
const decoder = new TextDecoder('utf8')
|
|
18
17
|
const CIR_TREE_MAGIC = 0x2468ace0
|
|
@@ -37,6 +36,268 @@ function coordFilter(s1: number, e1: number, s2: number, e2: number): boolean {
|
|
|
37
36
|
return s1 < e2 && e1 >= s2
|
|
38
37
|
}
|
|
39
38
|
|
|
39
|
+
function parseSummaryBlock(
|
|
40
|
+
b: Uint8Array,
|
|
41
|
+
startOffset: number,
|
|
42
|
+
request?: CoordRequest,
|
|
43
|
+
) {
|
|
44
|
+
const features: Feature[] = []
|
|
45
|
+
let offset = startOffset
|
|
46
|
+
const dataView = new DataView(b.buffer, b.byteOffset, b.length)
|
|
47
|
+
while (offset < b.byteLength) {
|
|
48
|
+
const chromId = dataView.getUint32(offset, true)
|
|
49
|
+
offset += 4
|
|
50
|
+
const start = dataView.getUint32(offset, true)
|
|
51
|
+
offset += 4
|
|
52
|
+
const end = dataView.getUint32(offset, true)
|
|
53
|
+
offset += 4
|
|
54
|
+
const validCnt = dataView.getUint32(offset, true)
|
|
55
|
+
offset += 4
|
|
56
|
+
const minScore = dataView.getFloat32(offset, true)
|
|
57
|
+
offset += 4
|
|
58
|
+
const maxScore = dataView.getFloat32(offset, true)
|
|
59
|
+
offset += 4
|
|
60
|
+
const sumData = dataView.getFloat32(offset, true)
|
|
61
|
+
offset += 8
|
|
62
|
+
|
|
63
|
+
if (
|
|
64
|
+
!request ||
|
|
65
|
+
(chromId === request.chrId &&
|
|
66
|
+
coordFilter(start, end, request.start, request.end))
|
|
67
|
+
) {
|
|
68
|
+
features.push({
|
|
69
|
+
start,
|
|
70
|
+
end,
|
|
71
|
+
maxScore,
|
|
72
|
+
minScore,
|
|
73
|
+
summary: true,
|
|
74
|
+
score: sumData / (validCnt || 1),
|
|
75
|
+
})
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return features
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function parseBigBedBlock(
|
|
82
|
+
data: Uint8Array,
|
|
83
|
+
startOffset: number,
|
|
84
|
+
offset: number,
|
|
85
|
+
request?: CoordRequest,
|
|
86
|
+
) {
|
|
87
|
+
const items: Feature[] = []
|
|
88
|
+
let currOffset = startOffset
|
|
89
|
+
const dataView = new DataView(data.buffer, data.byteOffset, data.length)
|
|
90
|
+
while (currOffset < data.byteLength) {
|
|
91
|
+
const c2 = currOffset
|
|
92
|
+
const chromId = dataView.getUint32(currOffset, true)
|
|
93
|
+
currOffset += 4
|
|
94
|
+
const start = dataView.getInt32(currOffset, true)
|
|
95
|
+
currOffset += 4
|
|
96
|
+
const end = dataView.getInt32(currOffset, true)
|
|
97
|
+
currOffset += 4
|
|
98
|
+
let i = currOffset
|
|
99
|
+
for (; i < data.length; i++) {
|
|
100
|
+
if (data[i] === 0) {
|
|
101
|
+
break
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
const b = data.subarray(currOffset, i)
|
|
105
|
+
const rest = decoder.decode(b)
|
|
106
|
+
currOffset = i + 1
|
|
107
|
+
if (
|
|
108
|
+
!request ||
|
|
109
|
+
(chromId === request.chrId &&
|
|
110
|
+
coordFilter(start, end, request.start, request.end))
|
|
111
|
+
) {
|
|
112
|
+
items.push({
|
|
113
|
+
start,
|
|
114
|
+
end,
|
|
115
|
+
rest,
|
|
116
|
+
uniqueId: `bb-${offset + c2}`,
|
|
117
|
+
})
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return items
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function parseBigWigBlock(
|
|
124
|
+
buffer: Uint8Array,
|
|
125
|
+
startOffset: number,
|
|
126
|
+
req?: CoordRequest,
|
|
127
|
+
) {
|
|
128
|
+
const b = buffer.subarray(startOffset)
|
|
129
|
+
const dataView = new DataView(b.buffer, b.byteOffset, b.length)
|
|
130
|
+
const blockStart = dataView.getInt32(4, true)
|
|
131
|
+
const itemStep = dataView.getUint32(12, true)
|
|
132
|
+
const itemSpan = dataView.getUint32(16, true)
|
|
133
|
+
const blockType = dataView.getUint8(20)
|
|
134
|
+
const itemCount = dataView.getUint16(22, true)
|
|
135
|
+
let offset = 24
|
|
136
|
+
const items: Feature[] = []
|
|
137
|
+
switch (blockType) {
|
|
138
|
+
case 1: {
|
|
139
|
+
for (let i = 0; i < itemCount; i++) {
|
|
140
|
+
const start = dataView.getInt32(offset, true)
|
|
141
|
+
offset += 4
|
|
142
|
+
const end = dataView.getInt32(offset, true)
|
|
143
|
+
offset += 4
|
|
144
|
+
const score = dataView.getFloat32(offset, true)
|
|
145
|
+
offset += 4
|
|
146
|
+
if (!req || coordFilter(start, end, req.start, req.end)) {
|
|
147
|
+
items.push({ start, end, score })
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
break
|
|
151
|
+
}
|
|
152
|
+
case 2: {
|
|
153
|
+
for (let i = 0; i < itemCount; i++) {
|
|
154
|
+
const start = dataView.getInt32(offset, true)
|
|
155
|
+
offset += 4
|
|
156
|
+
const score = dataView.getFloat32(offset, true)
|
|
157
|
+
offset += 4
|
|
158
|
+
const end = start + itemSpan
|
|
159
|
+
if (!req || coordFilter(start, end, req.start, req.end)) {
|
|
160
|
+
items.push({ score, start, end })
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
break
|
|
164
|
+
}
|
|
165
|
+
case 3: {
|
|
166
|
+
for (let i = 0; i < itemCount; i++) {
|
|
167
|
+
const score = dataView.getFloat32(offset, true)
|
|
168
|
+
offset += 4
|
|
169
|
+
const start = blockStart + i * itemStep
|
|
170
|
+
const end = start + itemSpan
|
|
171
|
+
if (!req || coordFilter(start, end, req.start, req.end)) {
|
|
172
|
+
items.push({ score, start, end })
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
break
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
return items
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function parseBigWigBlockAsArrays(
|
|
182
|
+
buffer: Uint8Array,
|
|
183
|
+
startOffset: number,
|
|
184
|
+
req?: CoordRequest,
|
|
185
|
+
): { starts: Int32Array; ends: Int32Array; scores: Float32Array } {
|
|
186
|
+
const dataView = new DataView(
|
|
187
|
+
buffer.buffer,
|
|
188
|
+
buffer.byteOffset + startOffset,
|
|
189
|
+
buffer.length - startOffset,
|
|
190
|
+
)
|
|
191
|
+
const blockStart = dataView.getInt32(4, true)
|
|
192
|
+
const itemStep = dataView.getUint32(12, true)
|
|
193
|
+
const itemSpan = dataView.getUint32(16, true)
|
|
194
|
+
const blockType = dataView.getUint8(20)
|
|
195
|
+
const itemCount = dataView.getUint16(22, true)
|
|
196
|
+
|
|
197
|
+
const starts = new Int32Array(itemCount)
|
|
198
|
+
const ends = new Int32Array(itemCount)
|
|
199
|
+
const scores = new Float32Array(itemCount)
|
|
200
|
+
|
|
201
|
+
if (!req) {
|
|
202
|
+
switch (blockType) {
|
|
203
|
+
case 1: {
|
|
204
|
+
let offset = 24
|
|
205
|
+
for (let i = 0; i < itemCount; i++) {
|
|
206
|
+
starts[i] = dataView.getInt32(offset, true)
|
|
207
|
+
ends[i] = dataView.getInt32(offset + 4, true)
|
|
208
|
+
scores[i] = dataView.getFloat32(offset + 8, true)
|
|
209
|
+
offset += 12
|
|
210
|
+
}
|
|
211
|
+
return { starts, ends, scores }
|
|
212
|
+
}
|
|
213
|
+
case 2: {
|
|
214
|
+
let offset = 24
|
|
215
|
+
for (let i = 0; i < itemCount; i++) {
|
|
216
|
+
const start = dataView.getInt32(offset, true)
|
|
217
|
+
starts[i] = start
|
|
218
|
+
ends[i] = start + itemSpan
|
|
219
|
+
scores[i] = dataView.getFloat32(offset + 4, true)
|
|
220
|
+
offset += 8
|
|
221
|
+
}
|
|
222
|
+
return { starts, ends, scores }
|
|
223
|
+
}
|
|
224
|
+
case 3: {
|
|
225
|
+
let offset = 24
|
|
226
|
+
for (let i = 0; i < itemCount; i++) {
|
|
227
|
+
const start = blockStart + i * itemStep
|
|
228
|
+
starts[i] = start
|
|
229
|
+
ends[i] = start + itemSpan
|
|
230
|
+
scores[i] = dataView.getFloat32(offset, true)
|
|
231
|
+
offset += 4
|
|
232
|
+
}
|
|
233
|
+
return { starts, ends, scores }
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
return { starts, ends, scores }
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const reqStart = req.start
|
|
240
|
+
const reqEnd = req.end
|
|
241
|
+
let idx = 0
|
|
242
|
+
|
|
243
|
+
switch (blockType) {
|
|
244
|
+
case 1: {
|
|
245
|
+
let offset = 24
|
|
246
|
+
for (let i = 0; i < itemCount; i++) {
|
|
247
|
+
const start = dataView.getInt32(offset, true)
|
|
248
|
+
const end = dataView.getInt32(offset + 4, true)
|
|
249
|
+
if (start < reqEnd && end >= reqStart) {
|
|
250
|
+
starts[idx] = start
|
|
251
|
+
ends[idx] = end
|
|
252
|
+
scores[idx] = dataView.getFloat32(offset + 8, true)
|
|
253
|
+
idx++
|
|
254
|
+
}
|
|
255
|
+
offset += 12
|
|
256
|
+
}
|
|
257
|
+
break
|
|
258
|
+
}
|
|
259
|
+
case 2: {
|
|
260
|
+
let offset = 24
|
|
261
|
+
for (let i = 0; i < itemCount; i++) {
|
|
262
|
+
const start = dataView.getInt32(offset, true)
|
|
263
|
+
const end = start + itemSpan
|
|
264
|
+
if (start < reqEnd && end >= reqStart) {
|
|
265
|
+
starts[idx] = start
|
|
266
|
+
ends[idx] = end
|
|
267
|
+
scores[idx] = dataView.getFloat32(offset + 4, true)
|
|
268
|
+
idx++
|
|
269
|
+
}
|
|
270
|
+
offset += 8
|
|
271
|
+
}
|
|
272
|
+
break
|
|
273
|
+
}
|
|
274
|
+
case 3: {
|
|
275
|
+
let offset = 24
|
|
276
|
+
for (let i = 0; i < itemCount; i++) {
|
|
277
|
+
const start = blockStart + i * itemStep
|
|
278
|
+
const end = start + itemSpan
|
|
279
|
+
if (start < reqEnd && end >= reqStart) {
|
|
280
|
+
starts[idx] = start
|
|
281
|
+
ends[idx] = end
|
|
282
|
+
scores[idx] = dataView.getFloat32(offset, true)
|
|
283
|
+
idx++
|
|
284
|
+
}
|
|
285
|
+
offset += 4
|
|
286
|
+
}
|
|
287
|
+
break
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
if (idx < itemCount) {
|
|
292
|
+
return {
|
|
293
|
+
starts: starts.subarray(0, idx),
|
|
294
|
+
ends: ends.subarray(0, idx),
|
|
295
|
+
scores: scores.subarray(0, idx),
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
return { starts, ends, scores }
|
|
299
|
+
}
|
|
300
|
+
|
|
40
301
|
/**
|
|
41
302
|
* View into a subset of the data in a BigWig file.
|
|
42
303
|
*
|
|
@@ -71,579 +332,216 @@ export class BlockView {
|
|
|
71
332
|
}
|
|
72
333
|
}
|
|
73
334
|
|
|
74
|
-
|
|
335
|
+
private async _collectBlocks(
|
|
75
336
|
chrName: string,
|
|
76
337
|
start: number,
|
|
77
338
|
end: number,
|
|
78
|
-
observer: Observer<Feature[]>,
|
|
79
339
|
opts?: Options,
|
|
80
|
-
) {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
340
|
+
): Promise<{ blocks: ReadData[]; chrId: number } | undefined> {
|
|
341
|
+
const chrId = this.refsByName[chrName]
|
|
342
|
+
if (chrId === undefined) {
|
|
343
|
+
return undefined
|
|
344
|
+
}
|
|
345
|
+
if (!this.rTreePromise) {
|
|
346
|
+
this.rTreePromise = this.bbi.read(48, this.rTreeOffset, opts)
|
|
347
|
+
}
|
|
348
|
+
const buffer = await this.rTreePromise
|
|
349
|
+
const dataView = new DataView(
|
|
350
|
+
buffer.buffer,
|
|
351
|
+
buffer.byteOffset,
|
|
352
|
+
buffer.length,
|
|
353
|
+
)
|
|
354
|
+
const magic = dataView.getUint32(0, true)
|
|
355
|
+
if (magic !== CIR_TREE_MAGIC) {
|
|
356
|
+
throw new Error(
|
|
357
|
+
`invalid cirTree magic: 0x${magic.toString(16)} (expected 0x${CIR_TREE_MAGIC.toString(16)}) at offset ${this.rTreeOffset}, file may be corrupt or unsupported`,
|
|
96
358
|
)
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
offset
|
|
125
|
-
|
|
126
|
-
offset += 8
|
|
127
|
-
if (
|
|
128
|
-
blockIntersectsQuery({ startChrom, startBase, endBase, endChrom })
|
|
129
|
-
) {
|
|
130
|
-
blocksToFetch.push({
|
|
131
|
-
offset: blockOffset,
|
|
132
|
-
length: blockSize,
|
|
133
|
-
})
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
// R-tree non-leaf nodes contain pointers to child nodes
|
|
139
|
-
const processNonLeafNode = (
|
|
140
|
-
dataView: DataView,
|
|
141
|
-
startOffset: number,
|
|
142
|
-
count: number,
|
|
143
|
-
level: number,
|
|
144
|
-
) => {
|
|
145
|
-
const recurOffsets = []
|
|
146
|
-
let offset = startOffset
|
|
147
|
-
for (let i = 0; i < count; i++) {
|
|
148
|
-
const startChrom = dataView.getUint32(offset, true)
|
|
149
|
-
offset += 4
|
|
150
|
-
const startBase = dataView.getUint32(offset, true)
|
|
151
|
-
offset += 4
|
|
152
|
-
const endChrom = dataView.getUint32(offset, true)
|
|
153
|
-
offset += 4
|
|
154
|
-
const endBase = dataView.getUint32(offset, true)
|
|
155
|
-
offset += 4
|
|
156
|
-
const blockOffset = Number(dataView.getBigUint64(offset, true))
|
|
157
|
-
offset += 8
|
|
158
|
-
if (
|
|
159
|
-
blockIntersectsQuery({ startChrom, startBase, endChrom, endBase })
|
|
160
|
-
) {
|
|
161
|
-
recurOffsets.push(blockOffset)
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
if (recurOffsets.length > 0) {
|
|
165
|
-
traverseRTree(recurOffsets, level + 1)
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
const processRTreeNode = (
|
|
170
|
-
rTreeBlockData: Uint8Array,
|
|
171
|
-
offset2: number,
|
|
172
|
-
level: number,
|
|
173
|
-
) => {
|
|
174
|
-
try {
|
|
175
|
-
const data = rTreeBlockData.subarray(offset2)
|
|
176
|
-
const dataView = new DataView(
|
|
177
|
-
data.buffer,
|
|
178
|
-
data.byteOffset,
|
|
179
|
-
data.length,
|
|
180
|
-
)
|
|
181
|
-
let offset = 0
|
|
182
|
-
|
|
183
|
-
const isLeaf = dataView.getUint8(offset)
|
|
184
|
-
offset += 2 // 1 skip for reserved byte
|
|
185
|
-
const count = dataView.getUint16(offset, true)
|
|
186
|
-
offset += 2
|
|
187
|
-
|
|
188
|
-
if (isLeaf === 1) {
|
|
189
|
-
processLeafNode(dataView, offset, count)
|
|
190
|
-
} else if (isLeaf === 0) {
|
|
191
|
-
processNonLeafNode(dataView, offset, count, level)
|
|
192
|
-
}
|
|
193
|
-
} catch (e) {
|
|
194
|
-
observer.error(e)
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
const blockIntersectsQuery = (b: {
|
|
199
|
-
startChrom: number
|
|
200
|
-
startBase: number
|
|
201
|
-
endChrom: number
|
|
202
|
-
endBase: number
|
|
203
|
-
}) => {
|
|
204
|
-
const { startChrom, startBase, endChrom, endBase } = b
|
|
205
|
-
return (
|
|
206
|
-
(startChrom < chrId || (startChrom === chrId && startBase <= end)) &&
|
|
207
|
-
(endChrom > chrId || (endChrom === chrId && endBase >= start))
|
|
359
|
+
}
|
|
360
|
+
const rTreeBlockSize = dataView.getUint32(4, true)
|
|
361
|
+
// Upper bound on size, based on a completely full leaf node.
|
|
362
|
+
const maxRTreeBlockSpan = 4 + rTreeBlockSize * 32
|
|
363
|
+
|
|
364
|
+
const blockIntersectsQuery = (
|
|
365
|
+
startChrom: number,
|
|
366
|
+
startBase: number,
|
|
367
|
+
endChrom: number,
|
|
368
|
+
endBase: number,
|
|
369
|
+
) =>
|
|
370
|
+
(startChrom < chrId || (startChrom === chrId && startBase <= end)) &&
|
|
371
|
+
(endChrom > chrId || (endChrom === chrId && endBase >= start))
|
|
372
|
+
|
|
373
|
+
const blocks: ReadData[] = []
|
|
374
|
+
let currentOffsets = [this.rTreeOffset + 48]
|
|
375
|
+
|
|
376
|
+
while (currentOffsets.length > 0) {
|
|
377
|
+
const spans = mergeRanges(
|
|
378
|
+
currentOffsets.map(o => ({ min: o, max: o + maxRTreeBlockSpan })),
|
|
379
|
+
)
|
|
380
|
+
const nextOffsets: number[] = []
|
|
381
|
+
for (const { min, max } of spans) {
|
|
382
|
+
const length = max - min
|
|
383
|
+
const offset = min
|
|
384
|
+
const resultBuffer = await this.featureCache.get(
|
|
385
|
+
`${length}_${offset}`,
|
|
386
|
+
{ length, offset },
|
|
387
|
+
opts?.signal,
|
|
208
388
|
)
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
389
|
+
for (const element of currentOffsets) {
|
|
390
|
+
if (min <= element && element <= max) {
|
|
391
|
+
const data = resultBuffer.subarray(element - offset)
|
|
392
|
+
const dv = new DataView(data.buffer, data.byteOffset, data.length)
|
|
393
|
+
const isLeaf = dv.getUint8(0)
|
|
394
|
+
const count = dv.getUint16(2, true)
|
|
395
|
+
let nodeOffset = 4
|
|
396
|
+
if (isLeaf === 1) {
|
|
397
|
+
for (let i = 0; i < count; i++) {
|
|
398
|
+
const startChrom = dv.getUint32(nodeOffset, true)
|
|
399
|
+
const startBase = dv.getUint32(nodeOffset + 4, true)
|
|
400
|
+
const endChrom = dv.getUint32(nodeOffset + 8, true)
|
|
401
|
+
const endBase = dv.getUint32(nodeOffset + 12, true)
|
|
402
|
+
const blockOffset = Number(
|
|
403
|
+
dv.getBigUint64(nodeOffset + 16, true),
|
|
404
|
+
)
|
|
405
|
+
const blockSize = Number(dv.getBigUint64(nodeOffset + 24, true))
|
|
406
|
+
nodeOffset += 32
|
|
407
|
+
if (
|
|
408
|
+
blockIntersectsQuery(startChrom, startBase, endChrom, endBase)
|
|
409
|
+
) {
|
|
410
|
+
blocks.push({ offset: blockOffset, length: blockSize })
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
} else if (isLeaf === 0) {
|
|
414
|
+
for (let i = 0; i < count; i++) {
|
|
415
|
+
const startChrom = dv.getUint32(nodeOffset, true)
|
|
416
|
+
const startBase = dv.getUint32(nodeOffset + 4, true)
|
|
417
|
+
const endChrom = dv.getUint32(nodeOffset + 8, true)
|
|
418
|
+
const endBase = dv.getUint32(nodeOffset + 12, true)
|
|
419
|
+
const childOffset = Number(
|
|
420
|
+
dv.getBigUint64(nodeOffset + 16, true),
|
|
421
|
+
)
|
|
422
|
+
nodeOffset += 24
|
|
423
|
+
if (
|
|
424
|
+
blockIntersectsQuery(startChrom, startBase, endChrom, endBase)
|
|
425
|
+
) {
|
|
426
|
+
nextOffsets.push(childOffset)
|
|
427
|
+
}
|
|
235
428
|
}
|
|
236
429
|
}
|
|
237
430
|
}
|
|
238
|
-
} catch (e) {
|
|
239
|
-
observer.error(e)
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
const traverseRTree = (offsets: number[], level: number) => {
|
|
243
|
-
try {
|
|
244
|
-
outstanding += offsets.length
|
|
245
|
-
|
|
246
|
-
// Upper bound on size, based on a completely full leaf node.
|
|
247
|
-
const maxRTreeBlockSpan = 4 + rTreeBlockSize * 32
|
|
248
|
-
let spans = new Range([
|
|
249
|
-
{
|
|
250
|
-
min: offsets[0]!,
|
|
251
|
-
max: offsets[0]! + maxRTreeBlockSpan,
|
|
252
|
-
},
|
|
253
|
-
])
|
|
254
|
-
for (let i = 1; i < offsets.length; i += 1) {
|
|
255
|
-
const blockSpan = new Range([
|
|
256
|
-
{
|
|
257
|
-
min: offsets[i]!,
|
|
258
|
-
max: offsets[i]! + maxRTreeBlockSpan,
|
|
259
|
-
},
|
|
260
|
-
])
|
|
261
|
-
spans = spans.union(blockSpan)
|
|
262
|
-
}
|
|
263
|
-
spans.getRanges().forEach(range => {
|
|
264
|
-
fetchAndProcessRTreeBlocks(offsets, range, level).catch(
|
|
265
|
-
(e: unknown) => {
|
|
266
|
-
observer.error(e)
|
|
267
|
-
},
|
|
268
|
-
)
|
|
269
|
-
})
|
|
270
|
-
} catch (e) {
|
|
271
|
-
observer.error(e)
|
|
272
431
|
}
|
|
273
432
|
}
|
|
274
|
-
|
|
275
|
-
traverseRTree([this.rTreeOffset + 48], 1)
|
|
276
|
-
return
|
|
277
|
-
} catch (e) {
|
|
278
|
-
observer.error(e)
|
|
433
|
+
currentOffsets = nextOffsets
|
|
279
434
|
}
|
|
280
|
-
}
|
|
281
435
|
|
|
282
|
-
|
|
283
|
-
b: Uint8Array,
|
|
284
|
-
startOffset: number,
|
|
285
|
-
request?: CoordRequest,
|
|
286
|
-
) {
|
|
287
|
-
const features: Feature[] = []
|
|
288
|
-
let offset = startOffset
|
|
289
|
-
|
|
290
|
-
const dataView = new DataView(b.buffer, b.byteOffset, b.length)
|
|
291
|
-
while (offset < b.byteLength) {
|
|
292
|
-
const chromId = dataView.getUint32(offset, true)
|
|
293
|
-
offset += 4
|
|
294
|
-
const start = dataView.getUint32(offset, true)
|
|
295
|
-
offset += 4
|
|
296
|
-
const end = dataView.getUint32(offset, true)
|
|
297
|
-
offset += 4
|
|
298
|
-
const validCnt = dataView.getUint32(offset, true)
|
|
299
|
-
offset += 4
|
|
300
|
-
const minScore = dataView.getFloat32(offset, true)
|
|
301
|
-
offset += 4
|
|
302
|
-
const maxScore = dataView.getFloat32(offset, true)
|
|
303
|
-
offset += 4
|
|
304
|
-
const sumData = dataView.getFloat32(offset, true)
|
|
305
|
-
offset += 8
|
|
306
|
-
|
|
307
|
-
if (
|
|
308
|
-
!request ||
|
|
309
|
-
(chromId === request.chrId &&
|
|
310
|
-
coordFilter(start, end, request.start, request.end))
|
|
311
|
-
) {
|
|
312
|
-
features.push({
|
|
313
|
-
start,
|
|
314
|
-
end,
|
|
315
|
-
maxScore,
|
|
316
|
-
minScore,
|
|
317
|
-
summary: true,
|
|
318
|
-
score: sumData / (validCnt || 1),
|
|
319
|
-
})
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
return features
|
|
436
|
+
return { blocks, chrId }
|
|
324
437
|
}
|
|
325
438
|
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
) {
|
|
332
|
-
const
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
while (currOffset < data.byteLength) {
|
|
336
|
-
const c2 = currOffset
|
|
337
|
-
const chromId = dataView.getUint32(currOffset, true)
|
|
338
|
-
currOffset += 4
|
|
339
|
-
const start = dataView.getInt32(currOffset, true)
|
|
340
|
-
currOffset += 4
|
|
341
|
-
const end = dataView.getInt32(currOffset, true)
|
|
342
|
-
currOffset += 4
|
|
343
|
-
let i = currOffset
|
|
344
|
-
for (; i < data.length; i++) {
|
|
345
|
-
if (data[i] === 0) {
|
|
346
|
-
break
|
|
347
|
-
}
|
|
348
|
-
}
|
|
349
|
-
const b = data.subarray(currOffset, i)
|
|
350
|
-
const rest = decoder.decode(b)
|
|
351
|
-
currOffset = i + 1
|
|
352
|
-
if (
|
|
353
|
-
!request ||
|
|
354
|
-
(chromId === request.chrId &&
|
|
355
|
-
coordFilter(start, end, request.start, request.end))
|
|
356
|
-
) {
|
|
357
|
-
items.push({
|
|
358
|
-
start,
|
|
359
|
-
end,
|
|
360
|
-
rest,
|
|
361
|
-
uniqueId: `bb-${offset + c2}`,
|
|
362
|
-
})
|
|
363
|
-
}
|
|
439
|
+
public async readWigData(
|
|
440
|
+
chrName: string,
|
|
441
|
+
start: number,
|
|
442
|
+
end: number,
|
|
443
|
+
opts?: Options,
|
|
444
|
+
): Promise<Feature[]> {
|
|
445
|
+
const collected = await this._collectBlocks(chrName, start, end, opts)
|
|
446
|
+
if (!collected) {
|
|
447
|
+
return []
|
|
364
448
|
}
|
|
365
|
-
|
|
366
|
-
return
|
|
449
|
+
const { blocks, chrId } = collected
|
|
450
|
+
return this.readFeatures(blocks, {
|
|
451
|
+
...opts,
|
|
452
|
+
request: { chrId, start, end },
|
|
453
|
+
})
|
|
367
454
|
}
|
|
368
455
|
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
const
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
const itemSpan = dataView.getUint32(offset, true)
|
|
384
|
-
offset += 4
|
|
385
|
-
const blockType = dataView.getUint8(offset)
|
|
386
|
-
offset += 2
|
|
387
|
-
const itemCount = dataView.getUint16(offset, true)
|
|
388
|
-
offset += 2
|
|
389
|
-
const items = []
|
|
390
|
-
switch (blockType) {
|
|
391
|
-
case 1: {
|
|
392
|
-
for (let i = 0; i < itemCount; i++) {
|
|
393
|
-
const start = dataView.getInt32(offset, true)
|
|
394
|
-
offset += 4
|
|
395
|
-
const end = dataView.getInt32(offset, true)
|
|
396
|
-
offset += 4
|
|
397
|
-
const score = dataView.getFloat32(offset, true)
|
|
398
|
-
offset += 4
|
|
399
|
-
if (!req || coordFilter(start, end, req.start, req.end)) {
|
|
400
|
-
items.push({
|
|
401
|
-
start,
|
|
402
|
-
end,
|
|
403
|
-
score,
|
|
404
|
-
})
|
|
405
|
-
}
|
|
406
|
-
}
|
|
407
|
-
break
|
|
408
|
-
}
|
|
409
|
-
case 2: {
|
|
410
|
-
for (let i = 0; i < itemCount; i++) {
|
|
411
|
-
const start = dataView.getInt32(offset, true)
|
|
412
|
-
offset += 4
|
|
413
|
-
const score = dataView.getFloat32(offset, true)
|
|
414
|
-
offset += 4
|
|
415
|
-
const end = start + itemSpan
|
|
416
|
-
if (!req || coordFilter(start, end, req.start, req.end)) {
|
|
417
|
-
items.push({
|
|
418
|
-
score,
|
|
419
|
-
start,
|
|
420
|
-
end,
|
|
421
|
-
})
|
|
422
|
-
}
|
|
423
|
-
}
|
|
424
|
-
break
|
|
425
|
-
}
|
|
426
|
-
case 3: {
|
|
427
|
-
for (let i = 0; i < itemCount; i++) {
|
|
428
|
-
const score = dataView.getFloat32(offset, true)
|
|
429
|
-
offset += 4
|
|
430
|
-
const start = blockStart + i * itemStep
|
|
431
|
-
const end = start + itemSpan
|
|
432
|
-
if (!req || coordFilter(start, end, req.start, req.end)) {
|
|
433
|
-
items.push({
|
|
434
|
-
score,
|
|
435
|
-
start,
|
|
436
|
-
end,
|
|
437
|
-
})
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
break
|
|
441
|
-
}
|
|
456
|
+
public async readWigDataAsArrays(
|
|
457
|
+
chrName: string,
|
|
458
|
+
start: number,
|
|
459
|
+
end: number,
|
|
460
|
+
opts?: Options,
|
|
461
|
+
): Promise<BigWigFeatureArrays | SummaryFeatureArrays> {
|
|
462
|
+
const collected = await this._collectBlocks(chrName, start, end, opts)
|
|
463
|
+
const blocks = collected?.blocks ?? []
|
|
464
|
+
const request = collected
|
|
465
|
+
? { chrId: collected.chrId, start, end }
|
|
466
|
+
: undefined
|
|
467
|
+
const optsWithReq = { ...opts, request }
|
|
468
|
+
if (this.blockType === 'summary') {
|
|
469
|
+
return this._readSummaryFeaturesAsArrays(blocks, optsWithReq)
|
|
442
470
|
}
|
|
443
|
-
|
|
444
|
-
return items
|
|
471
|
+
return this._readBigWigFeaturesAsArrays(blocks, optsWithReq)
|
|
445
472
|
}
|
|
446
473
|
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
const
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
starts[i] = dataView.getInt32(offset, true)
|
|
473
|
-
ends[i] = dataView.getInt32(offset + 4, true)
|
|
474
|
-
scores[i] = dataView.getFloat32(offset + 8, true)
|
|
475
|
-
offset += 12
|
|
476
|
-
}
|
|
477
|
-
return { starts, ends, scores }
|
|
478
|
-
}
|
|
479
|
-
case 2: {
|
|
480
|
-
let offset = 24
|
|
481
|
-
for (let i = 0; i < itemCount; i++) {
|
|
482
|
-
const start = dataView.getInt32(offset, true)
|
|
483
|
-
starts[i] = start
|
|
484
|
-
ends[i] = start + itemSpan
|
|
485
|
-
scores[i] = dataView.getFloat32(offset + 4, true)
|
|
486
|
-
offset += 8
|
|
487
|
-
}
|
|
488
|
-
return { starts, ends, scores }
|
|
474
|
+
public async readFeatures(
|
|
475
|
+
blocks: { offset: number; length: number }[],
|
|
476
|
+
opts: Options = {},
|
|
477
|
+
): Promise<Feature[]> {
|
|
478
|
+
const { blockType, uncompressBufSize } = this
|
|
479
|
+
const { signal, request } = opts
|
|
480
|
+
const blockGroupsToFetch = groupBlocks(blocks)
|
|
481
|
+
const allFeatures: Feature[] = []
|
|
482
|
+
for (const blockGroup of blockGroupsToFetch) {
|
|
483
|
+
const data = await this.bbi.read(blockGroup.length, blockGroup.offset, {
|
|
484
|
+
signal,
|
|
485
|
+
})
|
|
486
|
+
const groupOffset = blockGroup.offset
|
|
487
|
+
const subBlocks = blockGroup.blocks
|
|
488
|
+
|
|
489
|
+
let decompressedData: Uint8Array
|
|
490
|
+
let decompressedOffsets: number[]
|
|
491
|
+
|
|
492
|
+
if (uncompressBufSize > 0) {
|
|
493
|
+
const localBlocks: { offset: number; length: number }[] = []
|
|
494
|
+
for (const block of subBlocks) {
|
|
495
|
+
localBlocks.push({
|
|
496
|
+
offset: block.offset - groupOffset,
|
|
497
|
+
length: block.length,
|
|
498
|
+
})
|
|
489
499
|
}
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
}
|
|
499
|
-
return { starts, ends, scores }
|
|
500
|
+
const result = await unzipBatch(data, localBlocks, uncompressBufSize)
|
|
501
|
+
decompressedData = result.data
|
|
502
|
+
decompressedOffsets = result.offsets
|
|
503
|
+
} else {
|
|
504
|
+
decompressedData = data
|
|
505
|
+
decompressedOffsets = []
|
|
506
|
+
for (const block of subBlocks) {
|
|
507
|
+
decompressedOffsets.push(block.offset - groupOffset)
|
|
500
508
|
}
|
|
509
|
+
decompressedOffsets.push(data.length)
|
|
501
510
|
}
|
|
502
|
-
return { starts, ends, scores }
|
|
503
|
-
}
|
|
504
|
-
|
|
505
|
-
const reqStart = req.start
|
|
506
|
-
const reqEnd = req.end
|
|
507
|
-
let idx = 0
|
|
508
511
|
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
ends[idx] = end
|
|
533
|
-
scores[idx] = dataView.getFloat32(offset + 4, true)
|
|
534
|
-
idx++
|
|
535
|
-
}
|
|
536
|
-
offset += 8
|
|
512
|
+
for (let i = 0; i < subBlocks.length; i++) {
|
|
513
|
+
const start = decompressedOffsets[i]!
|
|
514
|
+
const end = decompressedOffsets[i + 1]!
|
|
515
|
+
const resultData = decompressedData.subarray(start, end)
|
|
516
|
+
let features: Feature[]
|
|
517
|
+
switch (blockType) {
|
|
518
|
+
case 'summary':
|
|
519
|
+
features = parseSummaryBlock(resultData, 0, request)
|
|
520
|
+
break
|
|
521
|
+
case 'bigwig':
|
|
522
|
+
features = parseBigWigBlock(resultData, 0, request)
|
|
523
|
+
break
|
|
524
|
+
case 'bigbed':
|
|
525
|
+
features = parseBigBedBlock(
|
|
526
|
+
resultData,
|
|
527
|
+
0,
|
|
528
|
+
subBlocks[i]!.offset * (1 << 8),
|
|
529
|
+
request,
|
|
530
|
+
)
|
|
531
|
+
break
|
|
532
|
+
default:
|
|
533
|
+
features = []
|
|
534
|
+
console.warn(`Don't know what to do with ${blockType}`)
|
|
537
535
|
}
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
case 3: {
|
|
541
|
-
let offset = 24
|
|
542
|
-
for (let i = 0; i < itemCount; i++) {
|
|
543
|
-
const start = blockStart + i * itemStep
|
|
544
|
-
const end = start + itemSpan
|
|
545
|
-
if (start < reqEnd && end >= reqStart) {
|
|
546
|
-
starts[idx] = start
|
|
547
|
-
ends[idx] = end
|
|
548
|
-
scores[idx] = dataView.getFloat32(offset, true)
|
|
549
|
-
idx++
|
|
550
|
-
}
|
|
551
|
-
offset += 4
|
|
536
|
+
for (const f of features) {
|
|
537
|
+
allFeatures.push(f)
|
|
552
538
|
}
|
|
553
|
-
break
|
|
554
|
-
}
|
|
555
|
-
}
|
|
556
|
-
|
|
557
|
-
if (idx < itemCount) {
|
|
558
|
-
return {
|
|
559
|
-
starts: starts.subarray(0, idx),
|
|
560
|
-
ends: ends.subarray(0, idx),
|
|
561
|
-
scores: scores.subarray(0, idx),
|
|
562
539
|
}
|
|
563
540
|
}
|
|
564
|
-
return
|
|
565
|
-
}
|
|
566
|
-
|
|
567
|
-
public async readFeatures(
|
|
568
|
-
observer: Observer<Feature[]>,
|
|
569
|
-
blocks: { offset: number; length: number }[],
|
|
570
|
-
opts: Options = {},
|
|
571
|
-
) {
|
|
572
|
-
try {
|
|
573
|
-
const { blockType, uncompressBufSize } = this
|
|
574
|
-
const { signal, request } = opts
|
|
575
|
-
const blockGroupsToFetch = groupBlocks(blocks)
|
|
576
|
-
await Promise.all(
|
|
577
|
-
blockGroupsToFetch.map(async blockGroup => {
|
|
578
|
-
const { length, offset } = blockGroup
|
|
579
|
-
const data = await this.featureCache.get(
|
|
580
|
-
`${length}_${offset}`,
|
|
581
|
-
blockGroup,
|
|
582
|
-
signal,
|
|
583
|
-
)
|
|
584
|
-
|
|
585
|
-
const localBlocks = blockGroup.blocks.map(block => ({
|
|
586
|
-
offset: block.offset - blockGroup.offset,
|
|
587
|
-
length: block.length,
|
|
588
|
-
}))
|
|
589
|
-
|
|
590
|
-
let decompressedData: Uint8Array
|
|
591
|
-
let decompressedOffsets: number[]
|
|
592
|
-
|
|
593
|
-
if (uncompressBufSize > 0) {
|
|
594
|
-
const result = await unzipBatch(
|
|
595
|
-
data,
|
|
596
|
-
localBlocks,
|
|
597
|
-
uncompressBufSize,
|
|
598
|
-
)
|
|
599
|
-
decompressedData = result.data
|
|
600
|
-
decompressedOffsets = result.offsets
|
|
601
|
-
} else {
|
|
602
|
-
decompressedData = data
|
|
603
|
-
decompressedOffsets = localBlocks.map(b => b.offset)
|
|
604
|
-
decompressedOffsets.push(data.length)
|
|
605
|
-
}
|
|
606
|
-
|
|
607
|
-
for (let i = 0; i < blockGroup.blocks.length; i++) {
|
|
608
|
-
const block = blockGroup.blocks[i]!
|
|
609
|
-
const start = decompressedOffsets[i]!
|
|
610
|
-
const end = decompressedOffsets[i + 1]!
|
|
611
|
-
const resultData = decompressedData.subarray(start, end)
|
|
612
|
-
|
|
613
|
-
switch (blockType) {
|
|
614
|
-
case 'summary': {
|
|
615
|
-
observer.next(this.parseSummaryBlock(resultData, 0, request))
|
|
616
|
-
break
|
|
617
|
-
}
|
|
618
|
-
case 'bigwig': {
|
|
619
|
-
observer.next(this.parseBigWigBlock(resultData, 0, request))
|
|
620
|
-
break
|
|
621
|
-
}
|
|
622
|
-
case 'bigbed': {
|
|
623
|
-
observer.next(
|
|
624
|
-
this.parseBigBedBlock(
|
|
625
|
-
resultData,
|
|
626
|
-
0,
|
|
627
|
-
block.offset * (1 << 8),
|
|
628
|
-
request,
|
|
629
|
-
),
|
|
630
|
-
)
|
|
631
|
-
break
|
|
632
|
-
}
|
|
633
|
-
default: {
|
|
634
|
-
console.warn(`Don't know what to do with ${blockType}`)
|
|
635
|
-
}
|
|
636
|
-
}
|
|
637
|
-
}
|
|
638
|
-
}),
|
|
639
|
-
)
|
|
640
|
-
observer.complete()
|
|
641
|
-
} catch (e) {
|
|
642
|
-
observer.error(e)
|
|
643
|
-
}
|
|
541
|
+
return allFeatures
|
|
644
542
|
}
|
|
645
543
|
|
|
646
|
-
|
|
544
|
+
private async _readBigWigFeaturesAsArrays(
|
|
647
545
|
blocks: { offset: number; length: number }[],
|
|
648
546
|
opts: Options = {},
|
|
649
547
|
): Promise<BigWigFeatureArrays> {
|
|
@@ -656,51 +554,45 @@ export class BlockView {
|
|
|
656
554
|
const allScores: Float32Array[] = []
|
|
657
555
|
let totalCount = 0
|
|
658
556
|
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
557
|
+
for (const blockGroup of blockGroupsToFetch) {
|
|
558
|
+
const { length, offset } = blockGroup
|
|
559
|
+
const data = await this.bbi.read(length, offset, { signal })
|
|
560
|
+
|
|
561
|
+
const localBlocks = blockGroup.blocks.map(block => ({
|
|
562
|
+
offset: block.offset - blockGroup.offset,
|
|
563
|
+
length: block.length,
|
|
564
|
+
}))
|
|
565
|
+
|
|
566
|
+
if (uncompressBufSize > 0) {
|
|
567
|
+
const result = await decompressAndParseBigWigBlocks(
|
|
568
|
+
data,
|
|
569
|
+
localBlocks,
|
|
570
|
+
uncompressBufSize,
|
|
571
|
+
request?.start ?? 0,
|
|
572
|
+
request?.end ?? 0,
|
|
666
573
|
)
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
request?.start ?? 0,
|
|
679
|
-
request?.end ?? 0,
|
|
574
|
+
if (result.starts.length > 0) {
|
|
575
|
+
allStarts.push(result.starts)
|
|
576
|
+
allEnds.push(result.ends)
|
|
577
|
+
allScores.push(result.scores)
|
|
578
|
+
totalCount += result.starts.length
|
|
579
|
+
}
|
|
580
|
+
} else {
|
|
581
|
+
for (const block of localBlocks) {
|
|
582
|
+
const blockData = data.subarray(
|
|
583
|
+
block.offset,
|
|
584
|
+
block.offset + block.length,
|
|
680
585
|
)
|
|
586
|
+
const result = parseBigWigBlockAsArrays(blockData, 0, request)
|
|
681
587
|
if (result.starts.length > 0) {
|
|
682
588
|
allStarts.push(result.starts)
|
|
683
589
|
allEnds.push(result.ends)
|
|
684
590
|
allScores.push(result.scores)
|
|
685
591
|
totalCount += result.starts.length
|
|
686
592
|
}
|
|
687
|
-
} else {
|
|
688
|
-
for (const block of localBlocks) {
|
|
689
|
-
const blockData = data.subarray(
|
|
690
|
-
block.offset,
|
|
691
|
-
block.offset + block.length,
|
|
692
|
-
)
|
|
693
|
-
const result = this.parseBigWigBlockAsArrays(blockData, 0, request)
|
|
694
|
-
if (result.starts.length > 0) {
|
|
695
|
-
allStarts.push(result.starts)
|
|
696
|
-
allEnds.push(result.ends)
|
|
697
|
-
allScores.push(result.scores)
|
|
698
|
-
totalCount += result.starts.length
|
|
699
|
-
}
|
|
700
|
-
}
|
|
701
593
|
}
|
|
702
|
-
}
|
|
703
|
-
|
|
594
|
+
}
|
|
595
|
+
}
|
|
704
596
|
|
|
705
597
|
if (allStarts.length === 0) {
|
|
706
598
|
return {
|
|
@@ -734,7 +626,7 @@ export class BlockView {
|
|
|
734
626
|
return { starts, ends, scores, isSummary: false as const }
|
|
735
627
|
}
|
|
736
628
|
|
|
737
|
-
|
|
629
|
+
private async _readSummaryFeaturesAsArrays(
|
|
738
630
|
blocks: { offset: number; length: number }[],
|
|
739
631
|
opts: Options = {},
|
|
740
632
|
): Promise<SummaryFeatureArrays> {
|
|
@@ -749,69 +641,63 @@ export class BlockView {
|
|
|
749
641
|
const allMaxScores: Float32Array[] = []
|
|
750
642
|
let totalCount = 0
|
|
751
643
|
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
644
|
+
for (const blockGroup of blockGroupsToFetch) {
|
|
645
|
+
const { length, offset } = blockGroup
|
|
646
|
+
const data = await this.bbi.read(length, offset, { signal })
|
|
647
|
+
|
|
648
|
+
const localBlocks = blockGroup.blocks.map(block => ({
|
|
649
|
+
offset: block.offset - blockGroup.offset,
|
|
650
|
+
length: block.length,
|
|
651
|
+
}))
|
|
652
|
+
|
|
653
|
+
if (uncompressBufSize > 0) {
|
|
654
|
+
const result = await decompressAndParseSummaryBlocks(
|
|
655
|
+
data,
|
|
656
|
+
localBlocks,
|
|
657
|
+
uncompressBufSize,
|
|
658
|
+
request?.chrId ?? 0,
|
|
659
|
+
request?.start ?? 0,
|
|
660
|
+
request?.end ?? 0,
|
|
759
661
|
)
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
request?.end ?? 0,
|
|
662
|
+
if (result.starts.length > 0) {
|
|
663
|
+
allStarts.push(result.starts)
|
|
664
|
+
allEnds.push(result.ends)
|
|
665
|
+
allScores.push(result.scores)
|
|
666
|
+
allMinScores.push(result.minScores)
|
|
667
|
+
allMaxScores.push(result.maxScores)
|
|
668
|
+
totalCount += result.starts.length
|
|
669
|
+
}
|
|
670
|
+
} else {
|
|
671
|
+
for (const block of localBlocks) {
|
|
672
|
+
const blockData = data.subarray(
|
|
673
|
+
block.offset,
|
|
674
|
+
block.offset + block.length,
|
|
774
675
|
)
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
const features = this.parseSummaryBlock(blockData, 0, request)
|
|
790
|
-
if (features.length > 0) {
|
|
791
|
-
const starts = new Int32Array(features.length)
|
|
792
|
-
const ends = new Int32Array(features.length)
|
|
793
|
-
const scores = new Float32Array(features.length)
|
|
794
|
-
const minScores = new Float32Array(features.length)
|
|
795
|
-
const maxScores = new Float32Array(features.length)
|
|
796
|
-
for (let i = 0; i < features.length; i++) {
|
|
797
|
-
const f = features[i]!
|
|
798
|
-
starts[i] = f.start
|
|
799
|
-
ends[i] = f.end
|
|
800
|
-
scores[i] = f.score ?? 0
|
|
801
|
-
minScores[i] = f.minScore ?? 0
|
|
802
|
-
maxScores[i] = f.maxScore ?? 0
|
|
803
|
-
}
|
|
804
|
-
allStarts.push(starts)
|
|
805
|
-
allEnds.push(ends)
|
|
806
|
-
allScores.push(scores)
|
|
807
|
-
allMinScores.push(minScores)
|
|
808
|
-
allMaxScores.push(maxScores)
|
|
809
|
-
totalCount += features.length
|
|
676
|
+
const features = parseSummaryBlock(blockData, 0, request)
|
|
677
|
+
if (features.length > 0) {
|
|
678
|
+
const starts = new Int32Array(features.length)
|
|
679
|
+
const ends = new Int32Array(features.length)
|
|
680
|
+
const scores = new Float32Array(features.length)
|
|
681
|
+
const minScores = new Float32Array(features.length)
|
|
682
|
+
const maxScores = new Float32Array(features.length)
|
|
683
|
+
for (let i = 0; i < features.length; i++) {
|
|
684
|
+
const f = features[i]!
|
|
685
|
+
starts[i] = f.start
|
|
686
|
+
ends[i] = f.end
|
|
687
|
+
scores[i] = f.score ?? 0
|
|
688
|
+
minScores[i] = f.minScore ?? 0
|
|
689
|
+
maxScores[i] = f.maxScore ?? 0
|
|
810
690
|
}
|
|
691
|
+
allStarts.push(starts)
|
|
692
|
+
allEnds.push(ends)
|
|
693
|
+
allScores.push(scores)
|
|
694
|
+
allMinScores.push(minScores)
|
|
695
|
+
allMaxScores.push(maxScores)
|
|
696
|
+
totalCount += features.length
|
|
811
697
|
}
|
|
812
698
|
}
|
|
813
|
-
}
|
|
814
|
-
|
|
699
|
+
}
|
|
700
|
+
}
|
|
815
701
|
|
|
816
702
|
if (allStarts.length === 0) {
|
|
817
703
|
return {
|