Skip to content

Commit 1ecfa79

Browse files
committed
Add getRange support to widget
1 parent 32c7197 commit 1ecfa79

2 files changed

Lines changed: 65 additions & 28 deletions

File tree

docs/notebooks/spatial_data_blobs.ipynb

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@
115115
")\n",
116116
"# Add data to the configuration:\n",
117117
"wrapper = SpatialDataWrapper(\n",
118-
" sdata_path=spatialdata_filepath,\n",
118+
" sdata_store=spatialdata_filepath,\n",
119119
" # The following paths are relative to the root of the SpatialData zarr store on-disk.\n",
120120
" image_path=\"images/blobs_image\",\n",
121121
" obs_segmentations_path=\"labels/blobs_labels\",\n",
@@ -129,7 +129,7 @@
129129
" }\n",
130130
")\n",
131131
"points_wrapper = SpatialDataWrapper(\n",
132-
" sdata_path=spatialdata_filepath,\n",
132+
" sdata_store=spatialdata_filepath,\n",
133133
" # The following paths are relative to the root of the SpatialData zarr store on-disk.\n",
134134
" obs_points_path=\"points/blobs_points\",\n",
135135
" obs_feature_matrix_path=\"tables/table_points/X\", # TODO\n",
@@ -214,6 +214,13 @@
214214
"vw"
215215
]
216216
},
217+
{
218+
"cell_type": "code",
219+
"execution_count": null,
220+
"metadata": {},
221+
"outputs": [],
222+
"source": []
223+
},
217224
{
218225
"cell_type": "code",
219226
"execution_count": null,

src/vitessce/widget.py

Lines changed: 56 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -393,17 +393,15 @@ def get_uid_str(uid):
393393
394394
if (!data.success) {
395395
resolve(undefined);
396+
return;
396397
}
397398
398-
if (key.includes("spatialdata_attrs") && key.endsWith("0") && !ArrayBuffer.isView(bufferData.buffer)) {
399-
// For some reason, the Zarrita.js UnicodeStringArray does not seem to work with
400-
// ArrayBuffers (throws a TypeError), so here we convert to Uint8Array if needed.
401-
// This error is occurring specifically for the arr.getChunk call within the AnnDataSource._loadString function.
402-
// TODO: figure out a more long-term solution.
403-
resolve(new Uint8Array(bufferData.buffer));
399+
if (ArrayBuffer.isView(bufferData)) {
400+
resolve(new Uint8Array(bufferData.buffer, bufferData.byteOffset, bufferData.byteLength));
401+
return;
404402
}
405-
406-
resolve(bufferData.buffer);
403+
resolve(new Uint8Array(bufferData.buffer));
404+
return;
407405
});
408406
}
409407
@@ -435,15 +433,26 @@ def get_uid_str(uid):
435433
});
436434
if (!data.success) return undefined;
437435
438-
if (key.includes("spatialdata_attrs") && key.endsWith("0") && !ArrayBuffer.isView(buffers[0].buffer)) {
439-
// For some reason, the Zarrita.js UnicodeStringArray does not seem to work with
440-
// ArrayBuffers (throws a TypeError), so here we convert to Uint8Array if needed.
441-
// This error is occurring specifically for the arr.getChunk call within the AnnDataSource._loadString function.
442-
// TODO: figure out a more long-term solution.
443-
return new Uint8Array(buffers[0].buffer);
436+
if (ArrayBuffer.isView(buffers[0])) {
437+
return new Uint8Array(buffers[0].buffer, buffers[0].byteOffset, buffers[0].byteLength);
444438
}
439+
return new Uint8Array(buffers[0].buffer);
440+
}
441+
},
442+
async getRange(key, rangeQuery) {
443+
if (invokeBatched) {
444+
return enqueue([storeUrl, key, rangeQuery]);
445+
} else {
446+
// Do not submit zarr gets in batches. Instead, submit individually.
447+
const [data, buffers] = await view.experimental.invoke("_zarr_get_range", [storeUrl, key, rangeQuery], {
448+
signal: AbortSignal.timeout(invokeTimeout),
449+
});
450+
if (!data.success) return undefined;
445451
446-
return buffers[0].buffer;
452+
if (ArrayBuffer.isView(buffers[0])) {
453+
return new Uint8Array(buffers[0].buffer, buffers[0].byteOffset, buffers[0].byteLength);
454+
}
455+
return new Uint8Array(buffers[0].buffer);
447456
}
448457
},
449458
}
@@ -729,7 +738,7 @@ class VitessceWidget(anywidget.AnyWidget):
729738

730739
next_port = DEFAULT_PORT
731740

732-
js_package_version = Unicode('3.9.2').tag(sync=True)
741+
js_package_version = Unicode('3.9.4').tag(sync=True)
733742
js_dev_mode = Bool(False).tag(sync=True)
734743
custom_js_url = Unicode('').tag(sync=True)
735744
plugin_esm = List(trait=Unicode(''), default_value=[]).tag(sync=True)
@@ -742,7 +751,7 @@ class VitessceWidget(anywidget.AnyWidget):
742751

743752
store_urls = List(trait=Unicode(''), default_value=[]).tag(sync=True)
744753

745-
def __init__(self, config, height=600, theme='auto', uid=None, port=None, proxy=False, js_package_version='3.9.2', js_dev_mode=False, custom_js_url='', plugins=None, remount_on_uid_change=True, prefer_local=True, invoke_timeout=300000, invoke_batched=True, page_mode=False, page_esm=None, prevent_scroll=True, server_host=None):
754+
def __init__(self, config, height=600, theme='auto', uid=None, port=None, proxy=False, js_package_version='3.9.4', js_dev_mode=False, custom_js_url='', plugins=None, remount_on_uid_change=True, prefer_local=True, invoke_timeout=300000, invoke_batched=True, page_mode=False, page_esm=None, prevent_scroll=True, server_host=None):
746755
"""
747756
Construct a new Vitessce widget. Not intended to be instantiated directly; instead, use ``VitessceConfig.widget``.
748757
@@ -850,21 +859,42 @@ def _zarr_get(self, params, buffers):
850859
except KeyError:
851860
buffers = []
852861
return {"success": len(buffers) == 1}, buffers
862+
863+
@anywidget.experimental.command
864+
def _zarr_get_range(self, params, buffers):
865+
[store_url, key, range_query] = params
866+
store = self._stores[store_url]
867+
try:
868+
full_value = store[key.lstrip("/")]
869+
# Reference: https://github.com/manzt/zarrita.js/blob/f63a2521e2b46b22aa26af4146822e4d827dff83/packages/%40zarrita-storage/src/types.ts#L3
870+
if "suffixLength" in range_query:
871+
suffix_length = range_query["suffixLength"]
872+
buffers = [full_value[-suffix_length:]]
873+
elif "offset" in range_query and "length" in range_query:
874+
offset = range_query["offset"]
875+
length = range_query["length"]
876+
buffers = [full_value[offset:offset+length]]
877+
except KeyError:
878+
buffers = []
879+
return {"success": len(buffers) == 1}, buffers
853880

854881
@anywidget.experimental.command
855882
def _zarr_get_multi(self, params_arr, buffers):
856-
# This variant of _zarr_get supports batching.
883+
# This variant of _zarr_get and _zarr_get_range supports batching.
857884
result_dicts = []
858885
result_buffers = []
859886
for params in params_arr:
860-
[store_url, key] = params
861-
store = self._stores[store_url]
862-
try:
863-
result_buffers.append(store[key.lstrip("/")])
864-
result_dicts.append({"success": True})
865-
except KeyError:
866-
result_buffers.append(b'')
887+
if len(params) == 2:
888+
result_dict, result_buffer_arr = self._zarr_get(params, buffers)
889+
elif len(params) == 3:
890+
result_dict, result_buffer_arr = self._zarr_get_range(params, buffers)
891+
892+
if result_dict["success"] and len(result_buffer_arr) == 1:
893+
result_dicts.append(result_dict)
894+
result_buffers.append(result_buffer_arr[0])
895+
else:
867896
result_dicts.append({"success": False})
897+
result_buffers.append(b'')
868898
return result_dicts, result_buffers
869899

870900
@anywidget.experimental.command
@@ -876,7 +906,7 @@ def _plugin_command(self, params, buffers):
876906
# Launch Vitessce using plain HTML representation (no ipywidgets)
877907

878908

879-
def ipython_display(config, height=600, theme='auto', base_url=None, host_name=None, uid=None, port=None, proxy=False, js_package_version='3.9.2', js_dev_mode=False, custom_js_url='', plugins=None, remount_on_uid_change=True, page_mode=False, page_esm=None, server_host=None):
909+
def ipython_display(config, height=600, theme='auto', base_url=None, host_name=None, uid=None, port=None, proxy=False, js_package_version='3.9.4', js_dev_mode=False, custom_js_url='', plugins=None, remount_on_uid_change=True, page_mode=False, page_esm=None, server_host=None):
880910
from IPython.display import display, HTML
881911
uid_str = "vitessce" + get_uid_str(uid)
882912

0 commit comments

Comments
 (0)