From dfabeed6239d8817e8ac13c73fb6e89d956db491 Mon Sep 17 00:00:00 2001 From: noah Date: Tue, 19 Nov 2024 07:58:50 -0800 Subject: [PATCH] remove duplicate file from common/loaders --- .../src/common/loaders/ome-zarr/zarr-data.ts | 236 ------------------ .../src/data-sources/ome-zarr/planar-slice.ts | 5 +- .../src/data-sources/ome-zarr/slice-grid.ts | 5 +- 3 files changed, 4 insertions(+), 242 deletions(-) delete mode 100644 examples/src/common/loaders/ome-zarr/zarr-data.ts diff --git a/examples/src/common/loaders/ome-zarr/zarr-data.ts b/examples/src/common/loaders/ome-zarr/zarr-data.ts deleted file mode 100644 index 1c14120..0000000 --- a/examples/src/common/loaders/ome-zarr/zarr-data.ts +++ /dev/null @@ -1,236 +0,0 @@ -// lets make some easy to understand utils to access .zarr data stored in an s3 bucket somewhere -// import { HTTPStore, NestedArray, type TypedArray, openArray, openGroup, slice } from "zarr"; -import * as zarr from 'zarrita'; -import { some } from 'lodash'; -import { Box2D, type Interval, Vec2, type box2D, limit, type vec2 } from '@alleninstitute/vis-geometry'; -import type { AxisAlignedPlane } from '~/data-renderers/versa-renderer'; - -// documentation for ome-zarr datasets (from which these types are built) -// can be found here: -// https://ngff.openmicroscopy.org/latest/#multiscale-md -// -export type ZarrDataset = Awaited>; -type AxisDesc = { - name: string; // x or y or z or time or ? - type: string; // space or time or ? - unit: string; // see list of possible units: https://ngff.openmicroscopy.org/latest/#axes-md -}; - -// todo, there are other types of coordinate transforms... -type ScaleTransform = { - type: 'scale'; - scale: ReadonlyArray; -}; - -function isScaleTransform(trn: unknown): trn is ScaleTransform { - if (typeof trn === 'object' && trn !== null) { - const scaleTransform = trn as ScaleTransform; - return scaleTransform.type === 'scale' && scaleTransform.scale !== undefined; - } - return false; -} -type DatasetDesc = { - path: string; - coordinateTransformations: ReadonlyArray; -}; -type DatasetWithShape = DatasetDesc & { - shape: number[]; -}; -type ZarrAttr = { - axes: ReadonlyArray; - datasets: ReadonlyArray; -}; -type ZarrAttrs = { - multiscales: ReadonlyArray; -}; - -async function getRawInfo(store: zarr.FetchStore) { - const group = await zarr.open(store, { kind: 'group' }); - return group.attrs as ZarrAttrs; - // TODO HACK ALERT: I am once again doing the thing that I hate, in which I promise to my friend Typescript that - // the junk I just pulled out of this internet file is exactly what I expect it to be: :fingers_crossed: -} - -async function mapAsync(arr: ReadonlyArray, fn: (t: T, index: number) => Promise) { - return Promise.all(arr.map((v, i) => fn(v, i))); -} -// return the mapping from path (aka resolution group???) to the dimensional shape of the data -async function loadMetadata(url: string) { - const store = new zarr.FetchStore(url); - const root = zarr.root(store); - const attrs: ZarrAttrs = await getRawInfo(store); - const addShapeToDesc = async (d: DatasetDesc) => ({ - ...d, - shape: (await zarr.open(root.resolve(d.path), { kind: 'array' })).shape, - }); - return { - url, - multiscales: await mapAsync(attrs.multiscales, async (attr) => ({ - ...attr, - datasets: await mapAsync(attr.datasets, addShapeToDesc), - })), - }; -} - -type OmeDimension = 'x' | 'y' | 'z' | 't' | 'c'; -const uvTable = { - xy: { u: 'x', v: 'y' }, - xz: { u: 'x', v: 'z' }, - yz: { u: 'y', v: 'z' }, -} as const; -const sliceDimension = { - xy: 'z', - xz: 'y', - yz: 'x', -} as const; -export function uvForPlane(plane: AxisAlignedPlane) { - return uvTable[plane]; -} -export function sliceDimensionForPlane(plane: AxisAlignedPlane) { - return sliceDimension[plane]; -} -export type ZarrRequest = Record; -export function pickBestScale( - dataset: ZarrDataset, - plane: { - u: OmeDimension; - v: OmeDimension; - }, - relativeView: box2D, // a box in data-unit-space - // in the plane given above - displayResolution: vec2 -) { - const datasets = dataset.multiscales[0].datasets; - const axes = dataset.multiscales[0].axes; - const realSize = sizeInUnits(plane, axes, datasets[0])!; - - const vxlPitch = (size: vec2) => Vec2.div(realSize, size); - // size, in dataspace, of a pixel 1/res - const pxPitch = Vec2.div(Box2D.size(relativeView), displayResolution); - const dstToDesired = (a: vec2, goal: vec2) => { - const diff = Vec2.sub(a, goal); - if (diff[0] * diff[1] > 0) { - // the res (a) is higher than our goal - - // weight this heavily to prefer smaller than the goal - return 1000 * Vec2.length(Vec2.sub(a, goal)); - } - return Vec2.length(Vec2.sub(a, goal)); - }; - // we assume the datasets are ordered... hmmm TODO - const choice = datasets.reduce( - (bestSoFar, cur) => - dstToDesired(vxlPitch(planeSizeInVoxels(plane, axes, bestSoFar)!), pxPitch) > - dstToDesired(vxlPitch(planeSizeInVoxels(plane, axes, cur)!), pxPitch) - ? cur - : bestSoFar, - datasets[0] - ); - return choice ?? datasets[datasets.length - 1]; -} -function indexFor(dim: OmeDimension, axes: readonly AxisDesc[]) { - return axes.findIndex((axe) => axe.name === dim); -} - -export function sizeInUnits( - plane: - | AxisAlignedPlane - | { - u: OmeDimension; - v: OmeDimension; - }, - axes: readonly AxisDesc[], - dataset: DatasetWithShape -): vec2 | undefined { - const planeUV = typeof plane === 'string' ? uvForPlane(plane) : plane; - const vxls = planeSizeInVoxels(planeUV, axes, dataset); - - if (vxls === undefined) return undefined; - let size: vec2 = vxls; - // now, just apply the correct transforms, if they exist... - - dataset.coordinateTransformations.forEach((trn) => { - if (isScaleTransform(trn)) { - // try to apply it! - const uIndex = indexOfDimension(axes, planeUV.u); - const vIndex = indexOfDimension(axes, planeUV.v); - size = Vec2.mul(size, [trn.scale[uIndex], trn.scale[vIndex]]); - } - }); - return size; -} -export function sizeInVoxels(dim: OmeDimension, axes: readonly AxisDesc[], dataset: DatasetWithShape) { - const uI = indexFor(dim, axes); - if (uI === -1) return undefined; - - return dataset.shape[uI]; -} -export function planeSizeInVoxels( - plane: { - u: OmeDimension; - v: OmeDimension; - }, - axes: readonly AxisDesc[], - dataset: DatasetWithShape -): vec2 | undefined { - // first - u&v must not refer to the same dimension, - // and both should exist in the axes... - const { u, v } = plane; - if (u === v) return undefined; - const uI = indexFor(u, axes); - const vI = indexFor(v, axes); - - if (uI === -1 || vI === -1) return undefined; - - return [dataset.shape[uI], dataset.shape[vI]] as const; -} -// feel free to freak out if the request is over or under determined or whatever -function buildQuery(r: Readonly, axes: readonly AxisDesc[], shape: number[]) { - const ordered = axes.map((a) => r[a.name as OmeDimension]); - // if any are undefined, throw up - if (some(ordered, (a) => a === undefined)) { - throw new Error('request does not match expected dimensions of ome-zarr dataset!'); - } - - return ordered.map((d, i) => { - const bounds = { min: 0, max: shape[i] }; - if (d === null) { - return d; - } else if (typeof d === 'number') { - return limit(bounds, d); - } - return zarr.slice(limit(bounds, d.min), limit(bounds, d.max)); - }); -} - -export async function explain(z: ZarrDataset) { - console.dir(z); - const root = zarr.root(new zarr.FetchStore(z.url)); - for (const d of z.multiscales[0].datasets) { - zarr.open(root.resolve(d.path), { kind: 'array' }).then((arr) => { - console.dir(arr); - }); - } -} - -export function indexOfDimension(axes: readonly AxisDesc[], dim: OmeDimension) { - return axes.findIndex((ax) => ax.name === dim); -} -export async function getSlice(metadata: ZarrDataset, r: ZarrRequest, layerIndex: number) { - // put the request in native order - const root = zarr.root(new zarr.FetchStore(metadata.url)); - const scene = metadata.multiscales[0]; - const { axes } = scene; - const level = scene.datasets[layerIndex] ?? scene.datasets[scene.datasets.length - 1]; - const arr = await zarr.open(root.resolve(level.path), { kind: 'array' }); - const result = await zarr.get(arr, buildQuery(r, axes, level.shape)); - if (typeof result == 'number') { - throw new Error('oh noes, slice came back all weird'); - } - return { - shape: result.shape, - buffer: result, - }; -} -export async function load(url: string) { - return loadMetadata(url); -} diff --git a/examples/src/data-sources/ome-zarr/planar-slice.ts b/examples/src/data-sources/ome-zarr/planar-slice.ts index b6d23b1..310041a 100644 --- a/examples/src/data-sources/ome-zarr/planar-slice.ts +++ b/examples/src/data-sources/ome-zarr/planar-slice.ts @@ -1,8 +1,7 @@ -import { type ZarrDataset } from '@alleninstitute/vis-omezarr'; +import { loadMetadata, type ZarrDataset } from '@alleninstitute/vis-omezarr'; import type { AxisAlignedPlane } from '~/data-renderers/versa-renderer'; import type { ColorMapping } from '../../data-renderers/types'; import type { OptionalTransform, Simple2DTransform } from '../types'; -import { load } from '~/common/loaders/ome-zarr/zarr-data'; export type ZarrSliceConfig = { type: 'zarrSliceConfig'; url: string; @@ -33,7 +32,7 @@ function assembleZarrSlice(config: ZarrSliceConfig, dataset: ZarrDataset): AxisA } export function createZarrSlice(config: ZarrSliceConfig): Promise { const { url } = config; - return load(url).then((dataset) => { + return loadMetadata(url).then((dataset) => { return assembleZarrSlice(config, dataset); }); } diff --git a/examples/src/data-sources/ome-zarr/slice-grid.ts b/examples/src/data-sources/ome-zarr/slice-grid.ts index 867b4e0..2baf963 100644 --- a/examples/src/data-sources/ome-zarr/slice-grid.ts +++ b/examples/src/data-sources/ome-zarr/slice-grid.ts @@ -1,8 +1,7 @@ -import { type ZarrDataset } from '@alleninstitute/vis-omezarr'; +import { loadMetadata, type ZarrDataset } from '@alleninstitute/vis-omezarr'; import type { AxisAlignedPlane } from '~/data-renderers/versa-renderer'; import type { ColorMapping } from '../../data-renderers/types'; import type { OptionalTransform, Simple2DTransform } from '../types'; -import { load } from '~/common/loaders/ome-zarr/zarr-data'; export type ZarrSliceGridConfig = { type: 'ZarrSliceGridConfig'; @@ -34,7 +33,7 @@ function assembleZarrSliceGrid(config: ZarrSliceGridConfig, dataset: ZarrDataset } export function createZarrSliceGrid(config: ZarrSliceGridConfig): Promise { const { url } = config; - return load(url).then((dataset) => { + return loadMetadata(url).then((dataset) => { return assembleZarrSliceGrid(config, dataset); }); }