@loaders.gl/zarr
Version:
Framework-independent loaders for Zarr
214 lines (209 loc) • 6.73 kB
JavaScript
;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// dist/index.js
var dist_exports = {};
__export(dist_exports, {
ZarrPixelSource: () => zarr_pixel_source_default,
loadZarr: () => loadZarr
});
module.exports = __toCommonJS(dist_exports);
// dist/lib/utils.js
var import_zarr = require("zarr");
function normalizeStore(source) {
if (typeof source === "string") {
return new import_zarr.HTTPStore(source);
}
return source;
}
async function loadMultiscales(store, path = "") {
const grp = await (0, import_zarr.openGroup)(store, path);
const rootAttrs = await grp.attrs.asObject();
if (!Array.isArray(rootAttrs.multiscales)) {
throw new Error("Cannot find Zarr multiscales metadata.");
}
const { datasets } = rootAttrs.multiscales[0];
const promises = datasets.map((d) => grp.getItem(d.path));
return {
data: await Promise.all(promises),
rootAttrs
};
}
function getDims(labels) {
const lookup = new Map(labels.map((name, i) => [name, i]));
if (lookup.size !== labels.length) {
throw Error("Labels must be unique, found duplicated label.");
}
return (name) => {
const index = lookup.get(name);
if (index === void 0) {
throw Error("Invalid dimension.");
}
return index;
};
}
function prevPowerOf2(x) {
return 2 ** Math.floor(Math.log2(x));
}
function isInterleaved(shape) {
const lastDimSize = shape[shape.length - 1];
return lastDimSize === 3 || lastDimSize === 4;
}
function guessTileSize(arr) {
const interleaved = isInterleaved(arr.shape);
const [yChunk, xChunk] = arr.chunks.slice(interleaved ? -3 : -2);
const size = Math.min(yChunk, xChunk);
return prevPowerOf2(size);
}
function guessLabels(rootAttrs) {
if ("omero" in rootAttrs) {
return ["t", "c", "z", "y", "x"];
}
throw new Error("Could not infer dimension labels for Zarr source. Must provide dimension labels.");
}
function getIndexer(labels) {
const size = labels.length;
const dims = getDims(labels);
return (sel) => {
if (Array.isArray(sel)) {
return [...sel];
}
const selection = Array(size).fill(0);
for (const [key, value] of Object.entries(sel)) {
selection[dims(key)] = value;
}
return selection;
};
}
function getImageSize(source) {
const interleaved = isInterleaved(source.shape);
const [height, width] = source.shape.slice(interleaved ? -3 : -2);
return { height, width };
}
function validLabels(labels, shape) {
if (labels.length !== shape.length) {
throw new Error("Labels do not match Zarr array shape.");
}
const n = shape.length;
if (isInterleaved(shape)) {
return labels[n - 3] === "y" && labels[n - 2] === "x" && labels[n - 1] === "_c";
}
return labels[n - 2] === "y" && labels[n - 1] === "x";
}
// dist/lib/zarr-pixel-source.js
var import_zarr2 = require("zarr");
var DTYPE_LOOKUP = {
u1: "Uint8",
u2: "Uint16",
u4: "Uint32",
f4: "Float32",
f8: "Float64",
i1: "Int8",
i2: "Int16",
i4: "Int32"
};
var ZarrPixelSource = class {
labels;
tileSize;
_data;
_indexer;
_readChunks;
constructor(data, labels, tileSize) {
this._indexer = getIndexer(labels);
this._data = data;
const xChunkSize = data.chunks[this._xIndex];
const yChunkSize = data.chunks[this._xIndex - 1];
this._readChunks = tileSize === xChunkSize && tileSize === yChunkSize;
this.labels = labels;
this.tileSize = tileSize;
}
get shape() {
return this._data.shape;
}
get dtype() {
const suffix = this._data.dtype.slice(1);
if (!(suffix in DTYPE_LOOKUP)) {
throw Error(`Zarr dtype not supported, got ${suffix}.`);
}
return DTYPE_LOOKUP[suffix];
}
get _xIndex() {
const interleave = isInterleaved(this._data.shape);
return this._data.shape.length - (interleave ? 2 : 1);
}
_chunkIndex(selection, x, y) {
const sel = this._indexer(selection);
sel[this._xIndex] = x;
sel[this._xIndex - 1] = y;
return sel;
}
/**
* Converts x, y tile indices to zarr dimension Slices within image bounds.
*/
_getSlices(x, y) {
const { height, width } = getImageSize(this);
const [xStart, xStop] = [x * this.tileSize, Math.min((x + 1) * this.tileSize, width)];
const [yStart, yStop] = [y * this.tileSize, Math.min((y + 1) * this.tileSize, height)];
if (xStart === xStop || yStart === yStop) {
throw new import_zarr2.BoundsCheckError("Tile slice is zero-sized.");
}
return [(0, import_zarr2.slice)(xStart, xStop), (0, import_zarr2.slice)(yStart, yStop)];
}
async getRaster({ selection }) {
const sel = this._chunkIndex(selection, null, null);
const { data, shape } = await this._data.getRaw(sel);
const [height, width] = shape;
return { data, width, height };
}
async getTile(props) {
const { x, y, selection, signal } = props;
let res;
if (this._readChunks) {
const sel = this._chunkIndex(selection, x, y);
res = await this._data.getRawChunk(sel, { storeOptions: { signal } });
} else {
const [xSlice, ySlice] = this._getSlices(x, y);
const sel = this._chunkIndex(selection, xSlice, ySlice);
res = await this._data.getRaw(sel);
}
const { data, shape: [height, width] } = res;
return { data, width, height };
}
onTileError(err) {
if (!(err instanceof import_zarr2.BoundsCheckError)) {
throw err;
}
}
};
var zarr_pixel_source_default = ZarrPixelSource;
// dist/lib/load-zarr.js
async function loadZarr(root, options = {}) {
const store = normalizeStore(root);
const { data, rootAttrs } = await loadMultiscales(store);
const tileSize = guessTileSize(data[0]);
const labels = options.labels ?? guessLabels(rootAttrs);
if (!validLabels(labels, data[0].shape)) {
throw new Error("Invalid labels for Zarr array dimensions.");
}
const pyramid = data.map((arr) => new zarr_pixel_source_default(arr, labels, tileSize));
return {
data: pyramid,
metadata: rootAttrs
};
}
//# sourceMappingURL=index.cjs.map