UNPKG

taxonium-component

Version:

React component for exploring large phylogenetic trees in the browser

316 lines (315 loc) 10.1 kB
import { L as x } from "./browser-BpRiKmO-.js"; import { s as y, a1 as k } from "./JBrowsePanel-BNE3gNW1.js"; import { B as I } from "./index-CpJXUZUB.js"; import { r as z } from "./rxjs-L4bS73F7.js"; import { S as _ } from "./simpleFeature-DimH8SQV.js"; const S = BigInt(32); function U(r, e, n) { const t = +!!n, o = +!n; return BigInt(r.getInt32(e, n) * o + r.getInt32(e + 4, n) * t) << S | BigInt(r.getUint32(e, n) * t + r.getUint32(e + 4, n) * o); } function q(r, e, n) { const t = r.getUint32(e, n), o = r.getUint32(e + 4, n), s = +!!n, i = +!n; return BigInt(t * i + o * s) << S | BigInt(t * s + o * i); } "getBigInt64" in DataView || (DataView.prototype.getBigInt64 = function(r, e) { return U(this, r, e); }); "getBigUint64" in DataView || (DataView.prototype.getBigUint64 = function(r, e) { return q(this, r, e); }); const P = 440477507, g = ["T", "C", "A", "G"], m = []; for (let r = 0; r < 256; r++) m.push(g[r >> 6 & 3] + g[r >> 4 & 3] + g[r >> 2 & 3] + g[r & 3]); const O = m.map((r) => r.toLowerCase()); class C { /** * @param {object} args * @param {string} [args.path] filesystem path for the .2bit file to open * @param {Filehandle} [args.filehandle] node fs.promises-like filehandle for the .2bit file. * Only needs to support `filehandle.read(buffer, offset, length, position)` */ constructor({ filehandle: e, path: n }) { if (e) this.filehandle = e; else if (n) this.filehandle = new x(n); else throw new Error("must supply path or filehandle"); } async _detectEndianness() { const e = await this.filehandle.read(8, 0), n = new DataView(e.buffer); if (n.getInt32(0, !0) === P) this.version = n.getInt32(0, !0); else throw new Error("not a 2bit file"); } getHeader() { return this.headerP || (this.headerP = this._getHeader().catch((e) => { throw this.headerP = void 0, e; })), this.headerP; } async _getHeader() { await this._detectEndianness(); const e = await this.filehandle.read(16, 0), n = !0, t = new DataView(e.buffer, e.byteOffset, e.length); let o = 0; const s = t.getInt32(o, n); if (o += 4, s !== 440477507) throw new Error(`Wrong magic number ${s}`); const i = t.getInt32(o, n); o += 4; const c = t.getUint32(o, n); o += 4; const a = t.getUint32(o, n); return { version: i, magic: s, sequenceCount: c, reserved: a }; } getIndex() { return this.indexP || (this.indexP = this._getIndex().catch((e) => { throw this.indexP = void 0, e; })), this.indexP; } async _getIndex() { const e = await this.getHeader(), n = 8 + e.sequenceCount * (257 + (this.version === 1 ? 8 : 4)), t = await this.filehandle.read(n, 8), o = !0, s = new DataView(t.buffer, t.byteOffset, t.length); let i = 0; const c = s.getUint32(i, o); i += 4, i += 4; const a = [], h = new TextDecoder("utf8"); for (let l = 0; l < c; l++) { const u = s.getUint8(i); i += 1; const f = h.decode(t.subarray(i, i + u)); if (i += u, e.version === 1) { const d = Number(s.getBigUint64(i, o)); i += 8, a.push({ offset: d, name: f }); } else { const d = s.getUint32(i, o); i += 4, a.push({ offset: d, name: f }); } } return Object.fromEntries(a.map(({ name: l, offset: u }) => [l, u])); } /** * @returns for an array of string sequence names that are found in the file */ async getSequenceNames() { const e = await this.getIndex(); return Object.keys(e); } /** * @returns object listing the lengths of all sequences like `{seqName: * length, ...}`. * * note: this is a relatively slow operation especially if there are many * refseqs in the file, if you can get this information from a different file * e.g. a chrom.sizes file, it will be much faster */ async getSequenceSizes() { const e = await this.getIndex(), n = Object.keys(e), t = await Promise.all(Object.values(e).map((s) => this._getSequenceSize(s))), o = {}; for (const [s, i] of n.entries()) o[i] = t[s]; return o; } /** * @param seqName name of the sequence * * @returns sequence length, or undefined if it is not in the file */ async getSequenceSize(e) { const t = (await this.getIndex())[e]; return t ? this._getSequenceSize(t) : void 0; } async _getSequenceSize(e) { return this._record1(e).then((n) => n.dnaSize); } async _record1(e, n = 8) { const t = await this.filehandle.read(n, e), o = !0; let s = 0; const i = new DataView(t.buffer, t.byteOffset, t.length), c = i.getUint32(s, o); s += 4; const a = i.getUint32(s, o); return s += 4, { dnaSize: c, nBlockCount: a }; } async _record2(e, n) { const t = await this.filehandle.read(n, e), o = !0; let s = 0; const i = new DataView(t.buffer, t.byteOffset, t.length), c = i.getUint32(s, o); s += 4; const a = []; for (let u = 0; u < c; u++) { const f = i.getUint32(s, o); s += 4, a.push(f); } const h = []; for (let u = 0; u < c; u++) { const f = i.getUint32(s, o); s += 4, h.push(f); } return { maskBlockCount: i.getUint32(s, o), nBlockSizes: h, nBlockStarts: a }; } async _record3(e, n) { const t = await this.filehandle.read(n, e), o = !0; let s = 0; const i = new DataView(t.buffer, t.byteOffset, t.length), c = i.getUint32(s, o); s += 4; const a = []; for (let u = 0; u < c; u++) { const f = i.getUint32(s, o); s += 4, a.push(f); } const h = []; for (let u = 0; u < c; u++) { const f = i.getUint32(s, o); s += 4, h.push(f); } const l = i.getInt32(s, o); return { maskBlockCount: c, maskBlockSizes: h, maskBlockStarts: a, reserved: l }; } async _getSequenceRecord(e) { const n = await this._record1(e), t = n.nBlockCount * 8 + 8, o = await this._record2(e + 4, t), s = o.maskBlockCount * 8 + 8, i = await this._record3(e + 4 + t - 4, s); return { dnaSize: n.dnaSize, nBlocks: { starts: o.nBlockStarts, sizes: o.nBlockSizes }, maskBlocks: { starts: i.maskBlockStarts, sizes: i.maskBlockSizes }, dnaPosition: e + 4 + t - 4 + s }; } /** * @param seqName name of the sequence you want * * @param [regionStart] optional 0-based half-open start of the sequence * region to fetch. * * @param [regionEnd] optional 0-based half-open end of the sequence region * to fetch. defaults to end of the sequence * * @returns for a string of sequence bases */ async getSequence(e, n = 0, t = Number.POSITIVE_INFINITY) { const s = (await this.getIndex())[e]; if (!s) return; const i = await this._getSequenceRecord(s); if (n < 0) throw new TypeError("regionStart cannot be less than 0"); t > i.dnaSize && (t = i.dnaSize); const c = this._getOverlappingBlocks(n, t, i.nBlocks.starts, i.nBlocks.sizes), a = this._getOverlappingBlocks(n, t, i.maskBlocks.starts, i.maskBlocks.sizes), h = Math.ceil((t - n) / 4) + 1, l = Math.floor(n / 4), u = await this.filehandle.read(h, i.dnaPosition + l); let f = ""; for (let d = n; d < t; d += 1) { for (; a.length > 0 && a[0].end <= d; ) a.shift(); const p = a[0] && a[0].start <= d && a[0].end > d; if (c[0] && d >= c[0].start && d < c[0].end) { const w = c.shift(); for (; d < w.end && d < t; d += 1) f += p ? "n" : "N"; d -= 1; } else { const w = Math.floor(d / 4) - l, B = d % 4, b = u[w]; f += p ? O[b][B] : m[b][B]; } } return f; } _getOverlappingBlocks(e, n, t, o) { let s, i; for (const [a, h] of t.entries()) { const l = o[a]; if (e >= h + l || n <= h) { if (s !== void 0) { i = a; break; } } else s === void 0 && (s = a); } if (s === void 0) return []; i === void 0 && (i = t.length); const c = new Array(i - s); for (let a = s; a < i; a += 1) c[a - s] = { start: t[a], end: t[a] + o[a], size: o[a] }; return c; } } class N extends I.BaseSequenceAdapter { async initChromSizes() { const e = y.readConfObject(this.config, "chromSizesLocation"); if (e.uri !== "/path/to/default.chrom.sizes" && e.uri !== "") { const t = await k.openLocation(e, this.pluginManager).readFile("utf8"); return Object.fromEntries(t.split(/\n|\r\n|\r/).filter((o) => !!o.trim()).map((o) => { const [s, i] = o.split(" "); return [s, +i]; })); } } async setupPre() { return { twobit: new C({ filehandle: k.openLocation(this.getConf("twoBitLocation"), this.pluginManager) }), chromSizesData: await this.initChromSizes() }; } async setup() { return this.setupP || (this.setupP = this.setupPre().catch((e) => { throw this.setupP = void 0, e; })), this.setupP; } async getRefNames() { const { chromSizesData: e, twobit: n } = await this.setup(); return e ? Object.keys(e) : n.getSequenceNames(); } async getRegions() { const { chromSizesData: e, twobit: n } = await this.setup(); if (e) return Object.keys(e).map((t) => ({ refName: t, start: 0, end: e[t] })); { const t = await n.getSequenceSizes(); return Object.keys(t).map((o) => ({ refName: o, start: 0, end: t[o] })); } } getFeatures({ refName: e, start: n, end: t }) { return z.ObservableCreate(async (o) => { const { twobit: s } = await this.setup(), i = await s.getSequenceSize(e), c = i !== void 0 ? Math.min(i, t) : t, a = await s.getSequence(e, n, c); a && o.next(new _({ id: `${e} ${n}-${c}`, data: { refName: e, start: n, end: c, seq: a } })), o.complete(); }); } } export { N as default }; //# sourceMappingURL=TwoBitAdapter-BSeVsbr5.js.map