Skip to content

Commit 1ef2163

Browse files
authored
Upgrade Viv@0.9.2 (#73)
* Upgrade viv to v0.9.2 * Use current href in Acquisition controller instead of relying on store * Scale initial view to fill viewport * disable text for follow-up PR
1 parent 07715ea commit 1ef2163

18 files changed

+1097
-1446
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,4 @@ out/*
2828
example/*.zarr
2929
example/.ipynb_checkpoints/*
3030
example/data/**
31+
__pycache__

example/VizarrDemo.imjoy.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
def encode_zarr_store(zobj):
3838
path_prefix = f"{zobj.path}/" if zobj.path else ""
3939

40-
def getItem(key):
40+
def getItem(key, options = None):
4141
return zobj.store[path_prefix + key]
4242

4343
def setItem(key, value):

example/create_fixture.py

+14-7
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,13 @@
55
import os
66
import json
77
from skimage import data
8-
from skimage.transform import pyramid_gaussian, pyramid_laplacian
8+
from skimage.transform import pyramid_gaussian
99

1010
# Modified from https://github.com/ome/ome-zarr-py/blob/master/tests/create_test_data.py
1111
def create_ome_zarr(zarr_directory, dtype="f4"):
1212

13-
base = np.tile(data.astronaut(), (2, 2, 1))
14-
gaussian = list(pyramid_gaussian(base, downscale=2, max_layer=4, multichannel=True))
13+
base = np.tile(data.astronaut(), (4, 4, 1))
14+
gaussian = list(pyramid_gaussian(base, downscale=2, max_layer=3, multichannel=True))
1515

1616
pyramid = []
1717
# convert each level of pyramid into 5D image (t, c, z, y, x)
@@ -21,14 +21,14 @@ def create_ome_zarr(zarr_directory, dtype="f4"):
2121
blue = pixels[:, :, 2]
2222
# wrap to make 5D: (t, c, z, y, x)
2323
pixels = np.array([np.array([red]), np.array([green]), np.array([blue])])
24-
pixels = np.array([pixels])
24+
pixels = np.array([pixels]).astype(dtype)
2525
pyramid.append(pixels)
2626

2727
store = zarr.DirectoryStore(zarr_directory)
2828
grp = zarr.group(store, overwrite=True)
2929
paths = []
3030
for path, dataset in enumerate(pyramid):
31-
grp.create_dataset(str(path), data=pyramid[path].astype(dtype))
31+
grp.create_dataset(str(path), data=pyramid[path])
3232
paths.append({"path": str(path)})
3333

3434
image_data = {
@@ -53,10 +53,17 @@ def create_ome_zarr(zarr_directory, dtype="f4"):
5353
"active": True,
5454
},
5555
],
56-
"rdefs": {"model": "color",},
56+
"rdefs": {
57+
"model": "color",
58+
},
5759
}
5860

59-
multiscales = [{"version": "0.1", "datasets": paths,}]
61+
multiscales = [
62+
{
63+
"version": "0.1",
64+
"datasets": paths,
65+
}
66+
]
6067
grp.attrs["multiscales"] = multiscales
6168
grp.attrs["omero"] = image_data
6269

example/getting_started.ipynb

+1-1
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,7 @@
217217
}
218218
],
219219
"source": [
220-
"z_arr = zarr.open('astronaut.zarr').get('1')\n",
220+
"z_arr = zarr.open('astronaut.zarr').get('2')\n",
221221
"z_arr.shape"
222222
]
223223
},

package-lock.json

+601-973
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+3-3
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,16 @@
22
"name": "@hms-dbmi/vizarr",
33
"version": "0.2.0",
44
"dependencies": {
5-
"@hms-dbmi/viv": "^0.8.1",
5+
"@hms-dbmi/viv": "^0.9.2",
66
"@material-ui/core": "^4.11.0",
77
"@material-ui/icons": "^4.9.1",
8-
"deck.gl": "^8.4.0-alpha.4",
8+
"deck.gl": "^8.4.3",
99
"imjoy-rpc": "^0.2.23",
1010
"p-map": "^4.0.0",
1111
"react": "^17.0.1",
1212
"react-dom": "^17.0.1",
1313
"recoil": "0.0.13",
14-
"zarr": "^0.3.0"
14+
"zarr": "^0.4.0"
1515
},
1616
"scripts": {
1717
"start": "snowpack dev",

src/components/LayerController/AcquisitionController.tsx

+9-5
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,26 @@ import React from 'react';
22
import { Grid, NativeSelect } from '@material-ui/core';
33
import { useRecoilValue } from 'recoil';
44
import type { ChangeEvent } from 'react';
5-
import type { Acquisition } from '../../state';
65

76
import { sourceInfoState } from '../../state';
87

98
function AcquisitionController({ layerId }: { layerId: string }): JSX.Element | null {
109
const sourceInfo = useRecoilValue(sourceInfoState);
11-
const { acquisitionId, acquisitions, source } = sourceInfo[layerId];
10+
const { acquisitionId, acquisitions } = sourceInfo[layerId];
1211

1312
if (!acquisitions) {
1413
return null;
1514
}
1615

1716
const handleSelectionChange = (event: ChangeEvent<HTMLSelectElement>) => {
1817
let value = event.target.value;
19-
let acquisition = value === '-1' ? '' : `&acquisition=${value}`;
20-
window.location.href = window.location.origin + window.location.pathname + `?source=${source}${acquisition}`;
18+
const url = new URL(window.location.href);
19+
if (value === '-1') {
20+
url.searchParams.delete('acquisition');
21+
} else {
22+
url.searchParams.set('acquisition', value);
23+
}
24+
window.location.href = decodeURIComponent(url.href);
2125
};
2226

2327
return (
@@ -28,7 +32,7 @@ function AcquisitionController({ layerId }: { layerId: string }): JSX.Element |
2832
Filter by Acquisition
2933
</option>
3034
{acquisitions.map((acq) => {
31-
acq = acq as Acquisition;
35+
acq = acq as Ome.Acquisition;
3236
return (
3337
<option value={acq.id} key={acq.id}>
3438
Acquisition: {acq.name}

src/components/LayerController/AxisSliders.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ function AxisSliders({ layerId }: { layerId: string }): JSX.Element | null {
1010

1111
const sliders = axis_labels
1212
.slice(0, -2) // ignore last two axes, [y,x]
13-
.map((name, i): [string, number, number] => [name, i, loader.base.shape[i]]) // capture the name, index, and size of non-yx dims
13+
.map((name, i): [string, number, number] => [name, i, loader[0].shape[i]]) // capture the name, index, and size of non-yx dims
1414
.filter((d) => {
1515
if (d[1] === channel_axis) return false; // ignore channel_axis (for OME-Zarr channel_axis === 1)
1616
if (d[2] > 1) return true; // keep if size > 1

src/components/LayerController/index.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ function LayerController({ layerId }: { layerId: string }): JSX.Element {
4141
setLayer(initialLayerState);
4242
}
4343
// Loader only defined once layer state is initialized.
44-
if (layerId in sourceInfo && !layer.layerProps.loader) {
44+
if (layerId in sourceInfo) {
4545
const config = sourceInfo[layerId];
4646
initLayer(config);
4747
}

src/components/Viewer.tsx

+33-12
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,50 @@
1-
import React from 'react';
1+
import React, { useRef } from 'react';
22
import { useRecoilState, useRecoilValue } from 'recoil';
33
import DeckGL from 'deck.gl';
44
import { OrthographicView } from '@deck.gl/core';
55
import type { Layer } from '@deck.gl/core';
66

7-
import { viewerViewState, layersSelector } from '../state';
8-
import type { VivLayerProps, ZarrLoader } from '@hms-dbmi/viv';
7+
import { viewerViewState, layersSelector, LayerState } from '../state';
8+
import { isInterleaved, fitBounds } from '../utils';
99

10-
function WrappedViewStateDeck({ layers }: { layers: Layer<VivLayerProps>[] }): JSX.Element {
10+
function getLayerSize(props: LayerState['layerProps']) {
11+
const { loader, rows, columns } = props;
12+
const [base, maxZoom] = Array.isArray(loader) ? [loader[0], loader.length] : [loader, 0];
13+
const interleaved = isInterleaved(base.shape);
14+
let [height, width] = base.shape.slice(interleaved ? -3 : -2);
15+
if (rows && columns) {
16+
// TODO: Don't hardcode spacer size. Probably best to inspect the deck.gl Layers rather than
17+
// the Layer Props.
18+
const spacer = 5;
19+
height = (height + spacer) * rows;
20+
width = (width + spacer) * columns;
21+
}
22+
return { height, width, maxZoom };
23+
}
24+
25+
function WrappedViewStateDeck({ layers }: { layers: Layer<any, any>[] }): JSX.Element {
1126
const [viewState, setViewState] = useRecoilState(viewerViewState);
27+
const deckRef = useRef<DeckGL>(null);
28+
const views = [new OrthographicView({ id: 'ortho', controller: true })];
1229

1330
// If viewState hasn't been updated, use the first loader to guess viewState
1431
// TODO: There is probably a better place / way to set the intital view and this is a hack.
15-
if (viewState?.default && (layers[0]?.props as VivLayerProps)?.loader?.base) {
16-
const loader = (layers[0].props as VivLayerProps).loader as ZarrLoader;
17-
const [height, width] = loader.base.shape.slice(-2);
18-
const zoom = -loader.numLevels;
19-
const target = [width / 2, height / 2, 0];
32+
if (deckRef.current && viewState?.default && layers[0]?.props?.loader) {
33+
const { deck } = deckRef.current;
34+
const { width, height, maxZoom } = getLayerSize(layers[0].props);
35+
const padding = deck.width < 400 ? 10 : deck.width < 600 ? 30 : 50; // Adjust depending on viewport width.
36+
const { zoom, target } = fitBounds([width, height], [deck.width, deck.height], maxZoom, padding);
2037
setViewState({ zoom, target });
2138
}
2239

23-
const views = [new OrthographicView({ id: 'ortho', controller: true })];
24-
2540
return (
26-
<DeckGL layers={layers} viewState={viewState} onViewStateChange={(e) => setViewState(e.viewState)} views={views} />
41+
<DeckGL
42+
ref={deckRef}
43+
layers={layers}
44+
viewState={viewState}
45+
onViewStateChange={(e) => setViewState(e.viewState)}
46+
views={views}
47+
/>
2748
);
2849
}
2950

0 commit comments

Comments
 (0)