From 0c33ec6a8a14744e8cf178b5fed5124097aa6833 Mon Sep 17 00:00:00 2001 From: ilan-gold Date: Mon, 3 Feb 2020 14:06:10 -0500 Subject: [PATCH 1/7] Add in geotiff functionality --- README.md | 3 ++- demo/src/App.js | 5 +--- demo/src/source-info.js | 15 ++++++++--- package-lock.json | 19 +++++++++++--- package.json | 1 + .../data-utils/index.js | 3 ++- .../data-utils/tiff-utils.js | 25 +++++++++++++++++++ .../microscopy-viewer-layer.js | 13 +++++++--- webpack.config.js | 5 ++++ 9 files changed, 72 insertions(+), 17 deletions(-) create mode 100644 src/layers/microscopy-viewer-layer/data-utils/tiff-utils.js diff --git a/README.md b/README.md index b76780a2a..6a4c0fbc3 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,7 @@ peer depenedencies from this package into your own: "@luma.gl/core": "8.0.3", "@luma.gl/shadertools": "8.0.3", "deck.gl": "^8.1.0-alpha.1", +"geotiff": "^1.0.0-beta.6", "math.gl": "^3.1.3", "nebula.gl": "^0.17.1", "zarr": "^0.1.4" @@ -44,7 +45,7 @@ This component can be used with an already existing `DeckGL` setup. ##### `getTileData` (Function) **POTENIAL FUTURE BREAKING CHANGES WITH NEW FEATURES** -`getTileData` given x, y, z indices of the tile, returns the tile data or a Promise that resolves to the tile data. Alternatively, pass in `useZarr` as true to use `zarr` and our funcionality. Soon, there will be a `useGeoTIFF` as well. Look +`getTileData` given x, y, z indices of the tile, returns the tile data or a Promise that resolves to the tile data. Alternatively, pass in `useZarr` as true to use `zarr` and our functionality. Otherwise, you can use `useTiff` to make range requests directly against a pyramid/tiled tiff. Look at [this](IMAGE_RENDERING.md) for how the zarr should be laid out. Receives arguments: diff --git a/demo/src/App.js b/demo/src/App.js index ef264c707..8774ed4a0 100644 --- a/demo/src/App.js +++ b/demo/src/App.js @@ -26,8 +26,6 @@ const OrangeSlider = withStyles({ } })(Slider) - - export default class App extends PureComponent { constructor(props){ @@ -91,6 +89,7 @@ export default class App extends PureComponent { ], } const propSettings = { + useTiff: true, imageHeight: source.height * source.tileSize, imageWidth: source.width * source.tileSize, tileSize: source.tileSize, @@ -99,9 +98,7 @@ export default class App extends PureComponent { -1 * Math.log2(Math.max(source.height * source.tileSize, source.width * source.tileSize)), ) } - const useZarr = true const props = { - useZarr, initialViewState, ...propSettings, ...this.state diff --git a/demo/src/source-info.js b/demo/src/source-info.js index 32bd11357..7eb6659fd 100644 --- a/demo/src/source-info.js +++ b/demo/src/source-info.js @@ -2,10 +2,17 @@ export const source = { height: 141, width: 206, tileSize: 256, + // channels: { + // channel_0: "https://vitessce-vanderbilt-data.storage.googleapis.com/test-data/vanderbilt-data/single_channel_pyramid/img_pyramid/channel_0", + // channel_1: "https://vitessce-vanderbilt-data.storage.googleapis.com/test-data/vanderbilt-data/single_channel_pyramid/img_pyramid/channel_1", + // channel_2: "https://vitessce-vanderbilt-data.storage.googleapis.com/test-data/vanderbilt-data/single_channel_pyramid/img_pyramid/channel_2", + // channel_3: "https://vitessce-vanderbilt-data.storage.googleapis.com/test-data/vanderbilt-data/single_channel_pyramid/img_pyramid/channel_3", + // }, channels: { - channel_0: "https://vitessce-vanderbilt-data.storage.googleapis.com/test-data/vanderbilt-data/single_channel_pyramid/img_pyramid/channel_0", - channel_1: "https://vitessce-vanderbilt-data.storage.googleapis.com/test-data/vanderbilt-data/single_channel_pyramid/img_pyramid/channel_1", - channel_2: "https://vitessce-vanderbilt-data.storage.googleapis.com/test-data/vanderbilt-data/single_channel_pyramid/img_pyramid/channel_2", - channel_3: "https://vitessce-vanderbilt-data.storage.googleapis.com/test-data/vanderbilt-data/single_channel_pyramid/img_pyramid/channel_3", + channel_0: "https://vitessce-vanderbilt-data.storage.googleapis.com/test-data/vanderbilt-data/tiff-pyramid/vanderbilt_test_0.ome.tiff", + channel_1: "https://vitessce-vanderbilt-data.storage.googleapis.com/test-data/vanderbilt-data/tiff-pyramid/vanderbilt_test_1.ome.tiff", + channel_2: "https://vitessce-vanderbilt-data.storage.googleapis.com/test-data/vanderbilt-data/tiff-pyramid/vanderbilt_test_2.ome.tiff", + channel_3: "https://vitessce-vanderbilt-data.storage.googleapis.com/test-data/vanderbilt-data/tiff-pyramid/vanderbilt_test_3.ome.tiff", } + }; diff --git a/package-lock.json b/package-lock.json index 11a699c01..e33b93137 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "@hubmap/vitessce-image-viewer", - "version": "0.0.1", + "version": "0.0.2", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -5752,6 +5752,15 @@ "integrity": "sha1-kQQGSbetObKQRkO9mtUeXobVJOM=", "dev": true }, + "geotiff": { + "version": "1.0.0-beta.6", + "resolved": "https://registry.npmjs.org/geotiff/-/geotiff-1.0.0-beta.6.tgz", + "integrity": "sha512-xdZ/MLcnrv1+6wQlQZQIs11zNJywylnV1pXqDw7Ao7bmLRpM421a39dXP5e6SG+vio0mnDUZkL2XknKbqppFzw==", + "requires": { + "pako": "^1.0.3", + "xmldom": "0.1.*" + } + }, "get-caller-file": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", @@ -7710,8 +7719,7 @@ "pako": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.10.tgz", - "integrity": "sha512-0DTvPVU3ed8+HNXOu5Bs+o//Mbdj9VNQMUOe9oKCwh8l0GNwpTDMKCWbRjgtD291AWnkAgkqA/LOnQS8AmS1tw==", - "dev": true + "integrity": "sha512-0DTvPVU3ed8+HNXOu5Bs+o//Mbdj9VNQMUOe9oKCwh8l0GNwpTDMKCWbRjgtD291AWnkAgkqA/LOnQS8AmS1tw==" }, "parallel-transform": { "version": "1.2.0", @@ -10425,6 +10433,11 @@ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", "dev": true }, + "xmldom": { + "version": "0.1.31", + "resolved": "https://registry.npmjs.org/xmldom/-/xmldom-0.1.31.tgz", + "integrity": "sha512-yS2uJflVQs6n+CyjHoaBmVSqIDevTAWrzMmjG1Gc7h1qQ7uVozNhEPJAwZXWyGQ/Gafo3fCwrcaokezLPupVyQ==" + }, "xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", diff --git a/package.json b/package.json index bc6d1e2e3..90e0c71e0 100644 --- a/package.json +++ b/package.json @@ -57,6 +57,7 @@ "babel-preset-stage-0": "^6.24.1", "css-loader": "^3.4.2", "deck.gl": "^8.1.0-alpha.1", + "geotiff": "^1.0.0-beta.6", "html-webpack-plugin": "^3.2.0", "math.gl": "^3.1.3", "mini-css-extract-plugin": "^0.9.0", diff --git a/src/layers/microscopy-viewer-layer/data-utils/index.js b/src/layers/microscopy-viewer-layer/data-utils/index.js index 3e65805a4..936d2f191 100644 --- a/src/layers/microscopy-viewer-layer/data-utils/index.js +++ b/src/layers/microscopy-viewer-layer/data-utils/index.js @@ -1,3 +1,4 @@ import loadZarr from './zarr-utils'; +import loadTiff from './tiff-utils'; -export {loadZarr} +export {loadZarr, loadTiff} diff --git a/src/layers/microscopy-viewer-layer/data-utils/tiff-utils.js b/src/layers/microscopy-viewer-layer/data-utils/tiff-utils.js new file mode 100644 index 000000000..6e307c301 --- /dev/null +++ b/src/layers/microscopy-viewer-layer/data-utils/tiff-utils.js @@ -0,0 +1,25 @@ +import {fromUrl, Pool, getDecoder } from 'geotiff/dist/geotiff.bundle.min.js'; + +async function loadTile({image, channel, x, y, pool}) { + var tile = await image.getTileOrStrip(x,y, 0, pool) + var dataObj = {} + dataObj[channel] = new Uint16Array(tile.data) + return dataObj +} + +export default function loadTiff({sourceChannels, x, y, z}){ + const pool = new Pool(); + const configListPromises = Object.keys(sourceChannels).map(async (channel) => { + var tiff = await fromUrl(sourceChannels[channel]) + var image = await tiff.getImage(z) + return loadTile({image, channel, x, y, pool}) + }) + return Promise.all(configListPromises).then((dataList) => { + const orderedList = [] + const dataObj = Object.assign({}, ...dataList) + Object.keys(dataObj).sort().forEach(function(key) { + orderedList.push(dataObj[key]); + }) + return orderedList; + }); +} diff --git a/src/layers/microscopy-viewer-layer/microscopy-viewer-layer.js b/src/layers/microscopy-viewer-layer/microscopy-viewer-layer.js index 7df3e80e8..22018a75f 100644 --- a/src/layers/microscopy-viewer-layer/microscopy-viewer-layer.js +++ b/src/layers/microscopy-viewer-layer/microscopy-viewer-layer.js @@ -5,7 +5,7 @@ import { COORDINATE_SYSTEM } from 'deck.gl'; import { XRLayer } from '../xr-layer'; import {tileToBoundingBox} from './tiling-utils'; import {getTileIndices} from './tiling-utils'; -import { loadZarr } from './data-utils'; +import { loadZarr, loadTiff } from './data-utils'; const defaultProps = Object.assign({}, BaseTileLayer.defaultProps, { id: `microscopy-tile-layer`, @@ -61,9 +61,14 @@ export class MicroscopyViewerLayer extends BaseTileLayer { x, y, z: -1 * z, ...props, }); } - const getTileData = props.useZarr - ? getZarr - : props.getTileData + const getTiff = ({ x, y, z }) => { + return loadTiff({ + x, y, z: -1 * z, ...props, + }); + } + const getTileData = (props.useZarr && getZarr) || + (props.useTiff && getTiff) || + props.getTileData const overrideValuesProps = Object.assign( {}, props, { sliderValues: orderedSliderValues, diff --git a/webpack.config.js b/webpack.config.js index a941cbcd6..1d7e0422c 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -56,6 +56,11 @@ module.exports = { } ] }, + node: { + fs: 'empty', + buffer: 'empty', + http: 'empty', + }, plugins: [ new HtmlWebPackPlugin({ hash: true, From 76abfd39e5d37cc96d8f1534ac8305d4ffdbd0f5 Mon Sep 17 00:00:00 2001 From: ilan-gold Date: Mon, 3 Feb 2020 14:34:15 -0500 Subject: [PATCH 2/7] Add metadata caching function --- demo/src/App.js | 3 ++- .../data-utils/index.js | 4 ++-- .../data-utils/tiff-utils.js | 20 ++++++++++++++++++- 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/demo/src/App.js b/demo/src/App.js index 8774ed4a0..eb6b8e3d7 100644 --- a/demo/src/App.js +++ b/demo/src/App.js @@ -96,7 +96,8 @@ export default class App extends PureComponent { sourceChannels: source.channels, minZoom: Math.floor( -1 * Math.log2(Math.max(source.height * source.tileSize, source.width * source.tileSize)), - ) + ), + maxZoom: -9 } const props = { initialViewState, diff --git a/src/layers/microscopy-viewer-layer/data-utils/index.js b/src/layers/microscopy-viewer-layer/data-utils/index.js index 936d2f191..2540ec6b7 100644 --- a/src/layers/microscopy-viewer-layer/data-utils/index.js +++ b/src/layers/microscopy-viewer-layer/data-utils/index.js @@ -1,4 +1,4 @@ import loadZarr from './zarr-utils'; -import loadTiff from './tiff-utils'; +import {loadTiff, getTiffConnections} from './tiff-utils'; -export {loadZarr, loadTiff} +export {loadZarr, loadTiff, getTiffConnections} diff --git a/src/layers/microscopy-viewer-layer/data-utils/tiff-utils.js b/src/layers/microscopy-viewer-layer/data-utils/tiff-utils.js index 6e307c301..7d3f320d6 100644 --- a/src/layers/microscopy-viewer-layer/data-utils/tiff-utils.js +++ b/src/layers/microscopy-viewer-layer/data-utils/tiff-utils.js @@ -7,7 +7,7 @@ async function loadTile({image, channel, x, y, pool}) { return dataObj } -export default function loadTiff({sourceChannels, x, y, z}){ +export function loadTiff({sourceChannels, x, y, z}){ const pool = new Pool(); const configListPromises = Object.keys(sourceChannels).map(async (channel) => { var tiff = await fromUrl(sourceChannels[channel]) @@ -23,3 +23,21 @@ export default function loadTiff({sourceChannels, x, y, z}){ return orderedList; }); } + +export async function getTiffConnections({sourceChannels, x, y, maxZoom}){ + const tiffConnections = await Object.assign({}, ...Object.keys(sourceChannels).map(async (channel) => { + var tiff = await fromUrl(sourceChannels[channel]) + var imageObj = {} + for(var i = 0; i < -maxZoom; i++) { + var image = await tiff.getImage(i) + if(!imageObj[channel]) { + imageObj[channel] = [image] + } else { + imageObj[channel].push(image) + } + + } + return imageObj + })) + return tiffConnections +} From 733218a7657f12d243d7d0d675cfd7a03e6a2375 Mon Sep 17 00:00:00 2001 From: ilan-gold Date: Mon, 3 Feb 2020 15:34:50 -0500 Subject: [PATCH 3/7] Add in caching functionality for mid-level-layers --- demo/src/App.js | 2 +- demo/src/source-info.js | 22 ++-- .../data-utils/index.js | 4 +- .../data-utils/tiff-utils.js | 15 ++- .../data-utils/zarr-utils.js | 50 ++++---- .../microscopy-viewer-layer-base.js | 97 +++++++++++++++ .../microscopy-viewer-layer.js | 114 ++++-------------- 7 files changed, 170 insertions(+), 134 deletions(-) create mode 100644 src/layers/microscopy-viewer-layer/microscopy-viewer-layer-base.js diff --git a/demo/src/App.js b/demo/src/App.js index eb6b8e3d7..b70cdf86f 100644 --- a/demo/src/App.js +++ b/demo/src/App.js @@ -89,7 +89,7 @@ export default class App extends PureComponent { ], } const propSettings = { - useTiff: true, + useZarr: true, imageHeight: source.height * source.tileSize, imageWidth: source.width * source.tileSize, tileSize: source.tileSize, diff --git a/demo/src/source-info.js b/demo/src/source-info.js index 7eb6659fd..12fe70442 100644 --- a/demo/src/source-info.js +++ b/demo/src/source-info.js @@ -2,17 +2,17 @@ export const source = { height: 141, width: 206, tileSize: 256, - // channels: { - // channel_0: "https://vitessce-vanderbilt-data.storage.googleapis.com/test-data/vanderbilt-data/single_channel_pyramid/img_pyramid/channel_0", - // channel_1: "https://vitessce-vanderbilt-data.storage.googleapis.com/test-data/vanderbilt-data/single_channel_pyramid/img_pyramid/channel_1", - // channel_2: "https://vitessce-vanderbilt-data.storage.googleapis.com/test-data/vanderbilt-data/single_channel_pyramid/img_pyramid/channel_2", - // channel_3: "https://vitessce-vanderbilt-data.storage.googleapis.com/test-data/vanderbilt-data/single_channel_pyramid/img_pyramid/channel_3", - // }, channels: { - channel_0: "https://vitessce-vanderbilt-data.storage.googleapis.com/test-data/vanderbilt-data/tiff-pyramid/vanderbilt_test_0.ome.tiff", - channel_1: "https://vitessce-vanderbilt-data.storage.googleapis.com/test-data/vanderbilt-data/tiff-pyramid/vanderbilt_test_1.ome.tiff", - channel_2: "https://vitessce-vanderbilt-data.storage.googleapis.com/test-data/vanderbilt-data/tiff-pyramid/vanderbilt_test_2.ome.tiff", - channel_3: "https://vitessce-vanderbilt-data.storage.googleapis.com/test-data/vanderbilt-data/tiff-pyramid/vanderbilt_test_3.ome.tiff", - } + channel_0: "https://vitessce-vanderbilt-data.storage.googleapis.com/test-data/vanderbilt-data/single_channel_pyramid/img_pyramid/channel_0", + channel_1: "https://vitessce-vanderbilt-data.storage.googleapis.com/test-data/vanderbilt-data/single_channel_pyramid/img_pyramid/channel_1", + channel_2: "https://vitessce-vanderbilt-data.storage.googleapis.com/test-data/vanderbilt-data/single_channel_pyramid/img_pyramid/channel_2", + channel_3: "https://vitessce-vanderbilt-data.storage.googleapis.com/test-data/vanderbilt-data/single_channel_pyramid/img_pyramid/channel_3", + }, + // channels: { + // channel_0: "https://vitessce-vanderbilt-data.storage.googleapis.com/test-data/vanderbilt-data/tiff-pyramid/vanderbilt_test_0.ome.tiff", + // channel_1: "https://vitessce-vanderbilt-data.storage.googleapis.com/test-data/vanderbilt-data/tiff-pyramid/vanderbilt_test_1.ome.tiff", + // channel_2: "https://vitessce-vanderbilt-data.storage.googleapis.com/test-data/vanderbilt-data/tiff-pyramid/vanderbilt_test_2.ome.tiff", + // channel_3: "https://vitessce-vanderbilt-data.storage.googleapis.com/test-data/vanderbilt-data/tiff-pyramid/vanderbilt_test_3.ome.tiff", + // } }; diff --git a/src/layers/microscopy-viewer-layer/data-utils/index.js b/src/layers/microscopy-viewer-layer/data-utils/index.js index 2540ec6b7..2e743bbed 100644 --- a/src/layers/microscopy-viewer-layer/data-utils/index.js +++ b/src/layers/microscopy-viewer-layer/data-utils/index.js @@ -1,4 +1,4 @@ -import loadZarr from './zarr-utils'; +import {loadZarr, getZarrConnections} from './zarr-utils'; import {loadTiff, getTiffConnections} from './tiff-utils'; -export {loadZarr, loadTiff, getTiffConnections} +export {loadZarr, loadTiff, getTiffConnections, getZarrConnections} diff --git a/src/layers/microscopy-viewer-layer/data-utils/tiff-utils.js b/src/layers/microscopy-viewer-layer/data-utils/tiff-utils.js index 7d3f320d6..d8d658686 100644 --- a/src/layers/microscopy-viewer-layer/data-utils/tiff-utils.js +++ b/src/layers/microscopy-viewer-layer/data-utils/tiff-utils.js @@ -7,11 +7,10 @@ async function loadTile({image, channel, x, y, pool}) { return dataObj } -export function loadTiff({sourceChannels, x, y, z}){ +export function loadTiff({connections, x, y, z}){ const pool = new Pool(); - const configListPromises = Object.keys(sourceChannels).map(async (channel) => { - var tiff = await fromUrl(sourceChannels[channel]) - var image = await tiff.getImage(z) + const configListPromises = Object.keys(connections).map((channel) => { + var image = connections[channel][z] return loadTile({image, channel, x, y, pool}) }) return Promise.all(configListPromises).then((dataList) => { @@ -24,8 +23,8 @@ export function loadTiff({sourceChannels, x, y, z}){ }); } -export async function getTiffConnections({sourceChannels, x, y, maxZoom}){ - const tiffConnections = await Object.assign({}, ...Object.keys(sourceChannels).map(async (channel) => { +export function getTiffConnections({sourceChannels, maxZoom}){ + const tiffConnections = Object.keys(sourceChannels).map(async (channel) => { var tiff = await fromUrl(sourceChannels[channel]) var imageObj = {} for(var i = 0; i < -maxZoom; i++) { @@ -38,6 +37,6 @@ export async function getTiffConnections({sourceChannels, x, y, maxZoom}){ } return imageObj - })) - return tiffConnections + }) + return Promise.all(tiffConnections) } diff --git a/src/layers/microscopy-viewer-layer/data-utils/zarr-utils.js b/src/layers/microscopy-viewer-layer/data-utils/zarr-utils.js index d22debdf8..8afff7fd7 100644 --- a/src/layers/microscopy-viewer-layer/data-utils/zarr-utils.js +++ b/src/layers/microscopy-viewer-layer/data-utils/zarr-utils.js @@ -1,41 +1,23 @@ import { slice, openArray } from 'zarr'; -var zarrArrays = {} - -async function getData({ config, tileSize, x, y, stride, tilingWidth }) { +async function getData({ arr, channel, tileSize, x, y, stride, tilingWidth }) { const arrSlice = slice(stride * tilingWidth * y + stride * x, stride * tilingWidth * y + stride * (x + 1)); - const zarrKey = config.zarrConfig.store + config.zarrConfig.path; - if (!zarrArrays[zarrKey]) { - zarrArrays[zarrKey] = await openArray(config.zarrConfig); - } - const arr = zarrArrays[zarrKey]; const dataSlice = await arr.get([arrSlice]); const { data } = dataSlice; - const { channelName } = config; const dataObj = {} - dataObj[channelName] = data + dataObj[channel] = data return dataObj; } -export default function loadZarr({ sourceChannels, tileSize, x, y, z, imageWidth }) { +export function loadZarr({ connections, tileSize, x, y, z, imageWidth }) { const tilingWidth = Math.ceil(imageWidth / (tileSize * (2 ** z))); - const dataNames = ['redData', 'greenData', 'blueData']; // denotes order rgb - const configList = Object.keys(sourceChannels).map((channel, i) => ({ - channelName: channel, - channelType: dataNames[i], - zarrConfig: { - store: `${sourceChannels[channel]}/`, - path: `pyramid_${z}.zarr`, - mode: 'r', - }, - })); const stride = tileSize * tileSize; - const configListPromises = configList.map((config) => { + const channelPromises = Object.keys(connections).map((channel) => { return getData({ - config, tileSize, x, y, stride, tilingWidth, + arr: connections[channel][z], channel, tileSize, x, y, stride, tilingWidth, }); }); - return Promise.all(configListPromises).then((dataList) => { + return Promise.all(channelPromises).then((dataList) => { const orderedList = [] const dataObj = Object.assign({}, ...dataList) Object.keys(dataObj).sort().forEach(function(key) { @@ -44,3 +26,23 @@ export default function loadZarr({ sourceChannels, tileSize, x, y, z, imageWidth return orderedList; }); } + +export function getZarrConnections({sourceChannels, maxZoom}) { + const zarrConnections = Object.keys(sourceChannels).map(async (channel) => { + var zarrObj = {} + for(var i = 0; i < -maxZoom; i++) { + var zarr = await openArray({ + store: `${sourceChannels[channel]}/`, + path: `pyramid_${i}.zarr`, + mode: 'r', + }) + if(!zarrObj[channel]) { + zarrObj[channel] = [zarr] + } else { + zarrObj[channel].push(zarr) + } + } + return zarrObj + }) + return Promise.all(zarrConnections) +} diff --git a/src/layers/microscopy-viewer-layer/microscopy-viewer-layer-base.js b/src/layers/microscopy-viewer-layer/microscopy-viewer-layer-base.js new file mode 100644 index 000000000..9b3e6c6be --- /dev/null +++ b/src/layers/microscopy-viewer-layer/microscopy-viewer-layer-base.js @@ -0,0 +1,97 @@ +import {BaseTileLayer} from '@deck.gl/layers'; +import {Texture2D} from '@luma.gl/webgl' +import GL from '@luma.gl/constants'; +import { COORDINATE_SYSTEM } from 'deck.gl'; +import { XRLayer } from '../xr-layer'; +import {tileToBoundingBox} from './tiling-utils'; +import {getTileIndices} from './tiling-utils'; +import { loadZarr, loadTiff } from './data-utils'; + +const defaultProps = Object.assign({}, BaseTileLayer.defaultProps, { + id: `microscopy-tile-layer`, + pickable: false, + coordinateSystem: COORDINATE_SYSTEM.CARTESIAN, + maxZoom: 0, + onViewportLoad: false, + renderSubLayers: (props) => { + const { + bbox: { + west, south, east, north, + }, + } = props.tile; + const { sliderValues, data, colorValues } = props; + const xrl = new XRLayer(props, { + id: `XR-Layer-${west}-${south}-${east}-${north}`, + pickable: false, + coordinateSystem: COORDINATE_SYSTEM.CARTESIAN, + rgbData: data, + sliderValues, + colorValues, + bounds: [west, south, east, north], + visible: true, + }); + return xrl; + }, +}); + +export class MicroscopyViewerLayerBase extends BaseTileLayer { + + constructor(props) { + const minZoom = Math.floor(-1 * Math.log2(Math.max(props.imageHeight, props.imageWidth))); + const {sliderValues, colorValues} = props + var orderedSliderValues = [] + var orderedColorValues = [] + Object.keys(sliderValues).sort().forEach(function(key) { + orderedSliderValues.push(sliderValues[key]); + }) + Object.keys(colorValues).sort().forEach(function(key) { + orderedColorValues.push(colorValues[key]); + }) + var diff = 6 - orderedSliderValues.length + for (var i = 0; i < diff; i++) { + orderedSliderValues.push(65535); + } + var diff = 6 - orderedColorValues.length + for (var j = 0; j < diff; j++) { + orderedColorValues.push([0,0,0]); + } + orderedColorValues = orderedColorValues.map(color => color.map(ch => ch / 255)) + const getZarr = ({ x, y, z }) => { + return loadZarr({ + x, y, z: -1 * z, ...props, + }); + } + const getTiff = ({ x, y, z }) => { + return loadTiff({ + x, y, z: -1 * z, ...props, + }); + } + const getTileData = (props.useZarr && getZarr) || + (props.useTiff && getTiff) || + props.getTileData + const overrideValuesProps = Object.assign( + {}, props, { + sliderValues: orderedSliderValues, + colorValues: orderedColorValues, + minZoom, + getTileData, + getTileIndices: (viewport, maxZoom, minZoom) => { + return getTileIndices({ + viewport, maxZoom, minZoom, ...props, + }); + }, + tileToBoundingBox: (x, y, z) => { + return tileToBoundingBox({ + x, y, z, ...props, + }); + }, + } + ) + const layerProps = Object.assign({}, defaultProps, overrideValuesProps) + super(layerProps) + } + +} + +MicroscopyViewerLayerBase.layerName = 'MicroscopyViewerLayerBase'; +MicroscopyViewerLayerBase.defaultProps = defaultProps; diff --git a/src/layers/microscopy-viewer-layer/microscopy-viewer-layer.js b/src/layers/microscopy-viewer-layer/microscopy-viewer-layer.js index 22018a75f..c91993d83 100644 --- a/src/layers/microscopy-viewer-layer/microscopy-viewer-layer.js +++ b/src/layers/microscopy-viewer-layer/microscopy-viewer-layer.js @@ -1,97 +1,35 @@ -import {BaseTileLayer} from '@deck.gl/layers'; -import {Texture2D} from '@luma.gl/webgl' -import GL from '@luma.gl/constants'; -import { COORDINATE_SYSTEM } from 'deck.gl'; -import { XRLayer } from '../xr-layer'; -import {tileToBoundingBox} from './tiling-utils'; -import {getTileIndices} from './tiling-utils'; -import { loadZarr, loadTiff } from './data-utils'; +import {MicroscopyViewerLayerBase} from './microscopy-viewer-layer-Base' +import {getTiffConnections,getZarrConnections} from './data-utils' +import {CompositeLayer} from '@deck.gl/core'; -const defaultProps = Object.assign({}, BaseTileLayer.defaultProps, { - id: `microscopy-tile-layer`, - pickable: false, - coordinateSystem: COORDINATE_SYSTEM.CARTESIAN, - maxZoom: 0, - onViewportLoad: false, - renderSubLayers: (props) => { - const { - bbox: { - west, south, east, north, - }, - } = props.tile; - const { sliderValues, data, colorValues } = props; - const xrl = new XRLayer(props, { - id: `XR-Layer-${west}-${south}-${east}-${north}`, - pickable: false, - coordinateSystem: COORDINATE_SYSTEM.CARTESIAN, - rgbData: data, - sliderValues, - colorValues, - bounds: [west, south, east, north], - visible: true, - }); - return xrl; - }, -}); +export class MicroscopyViewerLayer extends CompositeLayer { -export class MicroscopyViewerLayer extends BaseTileLayer { + initializeState(){ + this.state = { + connections: null + } + } + + shouldUpdateState({changeFlags}) { + return changeFlags.somethingChanged; + } - constructor(props) { - const minZoom = Math.floor(-1 * Math.log2(Math.max(props.imageHeight, props.imageWidth))); - const {sliderValues, colorValues} = props - var orderedSliderValues = [] - var orderedColorValues = [] - Object.keys(sliderValues).sort().forEach(function(key) { - orderedSliderValues.push(sliderValues[key]); + updateState(){ + this.props.useTiff + ? !this.state.connections && getTiffConnections({...this.props}).then((connections) => { + this.setState({connections}) }) - Object.keys(colorValues).sort().forEach(function(key) { - orderedColorValues.push(colorValues[key]); + : !this.state.connections && getZarrConnections({...this.props}).then((connections) => { + this.setState({connections}) }) - var diff = 6 - orderedSliderValues.length - for (var i = 0; i < diff; i++) { - orderedSliderValues.push(65535); - } - var diff = 6 - orderedColorValues.length - for (var j = 0; j < diff; j++) { - orderedColorValues.push([0,0,0]); - } - orderedColorValues = orderedColorValues.map(color => color.map(ch => ch / 255)) - const getZarr = ({ x, y, z }) => { - return loadZarr({ - x, y, z: -1 * z, ...props, - }); - } - const getTiff = ({ x, y, z }) => { - return loadTiff({ - x, y, z: -1 * z, ...props, - }); - } - const getTileData = (props.useZarr && getZarr) || - (props.useTiff && getTiff) || - props.getTileData - const overrideValuesProps = Object.assign( - {}, props, { - sliderValues: orderedSliderValues, - colorValues: orderedColorValues, - minZoom, - getTileData, - getTileIndices: (viewport, maxZoom, minZoom) => { - return getTileIndices({ - viewport, maxZoom, minZoom, ...props, - }); - }, - tileToBoundingBox: (x, y, z) => { - return tileToBoundingBox({ - x, y, z, ...props, - }); - }, - } - ) - const layerProps = Object.assign({}, defaultProps, overrideValuesProps) - super(layerProps) + } + + renderLayers() { + const layers = this.state.connections ? new MicroscopyViewerLayerBase({connections: Object.assign({},...this.state.connections), ...this.props}) : [] + return layers } } -MicroscopyViewerLayer.layerName = 'MicrsocopyViewerLayer'; -MicroscopyViewerLayer.defaultProps = defaultProps; + +MicroscopyViewerLayer.layerName = 'MicroscopyViewerLayer'; From 30b71bb6e0c3f099f9f555d30680992d314f2350 Mon Sep 17 00:00:00 2001 From: ilan-gold Date: Mon, 3 Feb 2020 15:45:57 -0500 Subject: [PATCH 4/7] Clean up tiff function to infer type --- .../microscopy-viewer-layer/data-utils/tiff-utils.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/layers/microscopy-viewer-layer/data-utils/tiff-utils.js b/src/layers/microscopy-viewer-layer/data-utils/tiff-utils.js index d8d658686..46033b8fb 100644 --- a/src/layers/microscopy-viewer-layer/data-utils/tiff-utils.js +++ b/src/layers/microscopy-viewer-layer/data-utils/tiff-utils.js @@ -3,7 +3,12 @@ import {fromUrl, Pool, getDecoder } from 'geotiff/dist/geotiff.bundle.min.js'; async function loadTile({image, channel, x, y, pool}) { var tile = await image.getTileOrStrip(x,y, 0, pool) var dataObj = {} - dataObj[channel] = new Uint16Array(tile.data) + const bits8 = image.fileDirectory.BitsPerSample[0]===8 + const bits16 = image.fileDirectory.BitsPerSample[0]===16 + const bits32 = image.fileDirectory.BitsPerSample[0]===32 + dataObj[channel] = (bits8 && new Uint8Array(tile.data)) || + (bits16 && new Uint16Array(tile.data)) || + (bits32 && new Uint32Array(tile.data)) return dataObj } From 89ece6d9ac4c4e2b22b83ebebaf2cc57d69c5adf Mon Sep 17 00:00:00 2001 From: ilan-gold Date: Mon, 3 Feb 2020 16:36:13 -0500 Subject: [PATCH 5/7] Update pool caching --- demo/src/App.js | 2 +- demo/src/source-info.js | 22 +++++++++---------- .../data-utils/tiff-utils.js | 3 +-- .../microscopy-viewer-layer.js | 8 ++++--- 4 files changed, 18 insertions(+), 17 deletions(-) diff --git a/demo/src/App.js b/demo/src/App.js index b70cdf86f..eb6b8e3d7 100644 --- a/demo/src/App.js +++ b/demo/src/App.js @@ -89,7 +89,7 @@ export default class App extends PureComponent { ], } const propSettings = { - useZarr: true, + useTiff: true, imageHeight: source.height * source.tileSize, imageWidth: source.width * source.tileSize, tileSize: source.tileSize, diff --git a/demo/src/source-info.js b/demo/src/source-info.js index 12fe70442..7eb6659fd 100644 --- a/demo/src/source-info.js +++ b/demo/src/source-info.js @@ -2,17 +2,17 @@ export const source = { height: 141, width: 206, tileSize: 256, - channels: { - channel_0: "https://vitessce-vanderbilt-data.storage.googleapis.com/test-data/vanderbilt-data/single_channel_pyramid/img_pyramid/channel_0", - channel_1: "https://vitessce-vanderbilt-data.storage.googleapis.com/test-data/vanderbilt-data/single_channel_pyramid/img_pyramid/channel_1", - channel_2: "https://vitessce-vanderbilt-data.storage.googleapis.com/test-data/vanderbilt-data/single_channel_pyramid/img_pyramid/channel_2", - channel_3: "https://vitessce-vanderbilt-data.storage.googleapis.com/test-data/vanderbilt-data/single_channel_pyramid/img_pyramid/channel_3", - }, // channels: { - // channel_0: "https://vitessce-vanderbilt-data.storage.googleapis.com/test-data/vanderbilt-data/tiff-pyramid/vanderbilt_test_0.ome.tiff", - // channel_1: "https://vitessce-vanderbilt-data.storage.googleapis.com/test-data/vanderbilt-data/tiff-pyramid/vanderbilt_test_1.ome.tiff", - // channel_2: "https://vitessce-vanderbilt-data.storage.googleapis.com/test-data/vanderbilt-data/tiff-pyramid/vanderbilt_test_2.ome.tiff", - // channel_3: "https://vitessce-vanderbilt-data.storage.googleapis.com/test-data/vanderbilt-data/tiff-pyramid/vanderbilt_test_3.ome.tiff", - // } + // channel_0: "https://vitessce-vanderbilt-data.storage.googleapis.com/test-data/vanderbilt-data/single_channel_pyramid/img_pyramid/channel_0", + // channel_1: "https://vitessce-vanderbilt-data.storage.googleapis.com/test-data/vanderbilt-data/single_channel_pyramid/img_pyramid/channel_1", + // channel_2: "https://vitessce-vanderbilt-data.storage.googleapis.com/test-data/vanderbilt-data/single_channel_pyramid/img_pyramid/channel_2", + // channel_3: "https://vitessce-vanderbilt-data.storage.googleapis.com/test-data/vanderbilt-data/single_channel_pyramid/img_pyramid/channel_3", + // }, + channels: { + channel_0: "https://vitessce-vanderbilt-data.storage.googleapis.com/test-data/vanderbilt-data/tiff-pyramid/vanderbilt_test_0.ome.tiff", + channel_1: "https://vitessce-vanderbilt-data.storage.googleapis.com/test-data/vanderbilt-data/tiff-pyramid/vanderbilt_test_1.ome.tiff", + channel_2: "https://vitessce-vanderbilt-data.storage.googleapis.com/test-data/vanderbilt-data/tiff-pyramid/vanderbilt_test_2.ome.tiff", + channel_3: "https://vitessce-vanderbilt-data.storage.googleapis.com/test-data/vanderbilt-data/tiff-pyramid/vanderbilt_test_3.ome.tiff", + } }; diff --git a/src/layers/microscopy-viewer-layer/data-utils/tiff-utils.js b/src/layers/microscopy-viewer-layer/data-utils/tiff-utils.js index 46033b8fb..7ecf94f98 100644 --- a/src/layers/microscopy-viewer-layer/data-utils/tiff-utils.js +++ b/src/layers/microscopy-viewer-layer/data-utils/tiff-utils.js @@ -12,8 +12,7 @@ async function loadTile({image, channel, x, y, pool}) { return dataObj } -export function loadTiff({connections, x, y, z}){ - const pool = new Pool(); +export function loadTiff({connections, x, y, z, pool}){ const configListPromises = Object.keys(connections).map((channel) => { var image = connections[channel][z] return loadTile({image, channel, x, y, pool}) diff --git a/src/layers/microscopy-viewer-layer/microscopy-viewer-layer.js b/src/layers/microscopy-viewer-layer/microscopy-viewer-layer.js index c91993d83..12e80dd98 100644 --- a/src/layers/microscopy-viewer-layer/microscopy-viewer-layer.js +++ b/src/layers/microscopy-viewer-layer/microscopy-viewer-layer.js @@ -1,12 +1,14 @@ import {MicroscopyViewerLayerBase} from './microscopy-viewer-layer-Base' import {getTiffConnections,getZarrConnections} from './data-utils' import {CompositeLayer} from '@deck.gl/core'; +import {fromUrl, Pool, getDecoder } from 'geotiff/dist/geotiff.bundle.min.js'; export class MicroscopyViewerLayer extends CompositeLayer { initializeState(){ this.state = { - connections: null + connections: null, + pool: null } } @@ -17,7 +19,7 @@ export class MicroscopyViewerLayer extends CompositeLayer { updateState(){ this.props.useTiff ? !this.state.connections && getTiffConnections({...this.props}).then((connections) => { - this.setState({connections}) + this.setState({connections, pool: new Pool()}) }) : !this.state.connections && getZarrConnections({...this.props}).then((connections) => { this.setState({connections}) @@ -25,7 +27,7 @@ export class MicroscopyViewerLayer extends CompositeLayer { } renderLayers() { - const layers = this.state.connections ? new MicroscopyViewerLayerBase({connections: Object.assign({},...this.state.connections), ...this.props}) : [] + const layers = this.state.connections ? new MicroscopyViewerLayerBase({connections: Object.assign({},...this.state.connections), pool: this.state.pool, ...this.props}) : [] return layers } From 80a60a6baee51910cad51c47d2ae67c8cd75638d Mon Sep 17 00:00:00 2001 From: ilan-gold Date: Tue, 4 Feb 2020 14:19:37 -0500 Subject: [PATCH 6/7] Update name for bits flag --- .../microscopy-viewer-layer/data-utils/tiff-utils.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/layers/microscopy-viewer-layer/data-utils/tiff-utils.js b/src/layers/microscopy-viewer-layer/data-utils/tiff-utils.js index 7ecf94f98..2134c288f 100644 --- a/src/layers/microscopy-viewer-layer/data-utils/tiff-utils.js +++ b/src/layers/microscopy-viewer-layer/data-utils/tiff-utils.js @@ -3,12 +3,12 @@ import {fromUrl, Pool, getDecoder } from 'geotiff/dist/geotiff.bundle.min.js'; async function loadTile({image, channel, x, y, pool}) { var tile = await image.getTileOrStrip(x,y, 0, pool) var dataObj = {} - const bits8 = image.fileDirectory.BitsPerSample[0]===8 - const bits16 = image.fileDirectory.BitsPerSample[0]===16 - const bits32 = image.fileDirectory.BitsPerSample[0]===32 - dataObj[channel] = (bits8 && new Uint8Array(tile.data)) || - (bits16 && new Uint16Array(tile.data)) || - (bits32 && new Uint32Array(tile.data)) + const is8Bits = image.fileDirectory.BitsPerSample[0]===8 + const is16Bits = image.fileDirectory.BitsPerSample[0]===16 + const is32Bits = image.fileDirectory.BitsPerSample[0]===32 + dataObj[channel] = (is8Bits && new Uint8Array(tile.data)) || + (is16Bits && new Uint16Array(tile.data)) || + (is32Bits && new Uint32Array(tile.data)) return dataObj } From 7927d497515fd4713c5985da9a25dfd6c4ffe65d Mon Sep 17 00:00:00 2001 From: ilan-gold Date: Tue, 4 Feb 2020 14:21:45 -0500 Subject: [PATCH 7/7] Use arrow syntax --- src/layers/microscopy-viewer-layer/data-utils/tiff-utils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/layers/microscopy-viewer-layer/data-utils/tiff-utils.js b/src/layers/microscopy-viewer-layer/data-utils/tiff-utils.js index 2134c288f..c35e59b03 100644 --- a/src/layers/microscopy-viewer-layer/data-utils/tiff-utils.js +++ b/src/layers/microscopy-viewer-layer/data-utils/tiff-utils.js @@ -20,7 +20,7 @@ export function loadTiff({connections, x, y, z, pool}){ return Promise.all(configListPromises).then((dataList) => { const orderedList = [] const dataObj = Object.assign({}, ...dataList) - Object.keys(dataObj).sort().forEach(function(key) { + Object.keys(dataObj).sort().forEach((key) => { orderedList.push(dataObj[key]); }) return orderedList;