From 650de48f6e8d38b5938c40e99b544576f78e8a77 Mon Sep 17 00:00:00 2001 From: Mark Keller <7525285+keller-mark@users.noreply.github.com> Date: Mon, 13 Oct 2025 13:50:48 -0400 Subject: [PATCH 1/2] Support obs_points_path in SpatialDataWrapper --- docs/notebooks/spatial_data_blobs.ipynb | 35 ++++++++++++++++++++++++- pyproject.toml | 2 +- src/vitessce/file_def_utils.py | 10 +++++++ src/vitessce/widget.py | 6 ++--- src/vitessce/wrappers.py | 7 ++++- 5 files changed, 54 insertions(+), 6 deletions(-) diff --git a/docs/notebooks/spatial_data_blobs.ipynb b/docs/notebooks/spatial_data_blobs.ipynb index 717e9645..53635dfa 100644 --- a/docs/notebooks/spatial_data_blobs.ipynb +++ b/docs/notebooks/spatial_data_blobs.ipynb @@ -60,6 +60,21 @@ "sdata.write(spatialdata_filepath, overwrite=True)" ] }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Change data type of x and y columns from int64 to int32\n", + "# Temporary workaround, will be resolved by https://github.com/vitessce/vitessce/issues/2292\n", + "ddf = sdata[\"blobs_points\"]\n", + "ddf[\"x\"] = ddf[\"x\"].astype('int32')\n", + "ddf[\"y\"] = ddf[\"y\"].astype('int32')\n", + "sdata[\"blobs_points_2\"] = ddf\n", + "sdata.write_element(\"blobs_points_2\")" + ] + }, { "cell_type": "code", "execution_count": null, @@ -107,7 +122,17 @@ " \"fileUid\": \"my_unique_id\"\n", " }\n", ")\n", - "dataset = vc.add_dataset(name='Blobs').add_object(wrapper)\n", + "points_wrapper = SpatialDataWrapper(\n", + " sdata_store=spatialdata_filepath,\n", + " # The following paths are relative to the root of the SpatialData zarr store on-disk.\n", + " obs_points_path=\"points/blobs_points_2\",\n", + " coordinate_system=\"global\",\n", + " coordination_values={\n", + " \"obsType\": \"point\",\n", + " \"fileUid\": \"other_unique_id\"\n", + " }\n", + ")\n", + "dataset = vc.add_dataset(name='Blobs').add_object(wrapper).add_object(points_wrapper)\n", "\n", "# Add views (visualizations) to the configuration:\n", "spatial = vc.add_view(\"spatialBeta\", dataset=dataset)\n", @@ -148,6 +173,14 @@ " }]),\n", "}, scope_prefix=get_initial_coordination_scope_prefix(\"A\", \"obsSegmentations\"))\n", "\n", + "vc.link_views_by_dict([spatial, layer_controller], {\n", + " 'pointLayer': CL([{\n", + " \"fileUid\": \"other_unique_id\",\n", + " \"obsType\": \"point\",\n", + " \"obsHighlight\": None,\n", + " }]),\n", + "}, scope_prefix=get_initial_coordination_scope_prefix(\"A\", \"obsPoints\"))\n", + "\n", "# Layout the views\n", "vc.layout(spatial | layer_controller);" ] diff --git a/pyproject.toml b/pyproject.toml index 456427a7..26e147b4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "vitessce" -version = "3.7.4" +version = "3.7.5" authors = [ { name="Mark Keller", email="mark_keller@hms.harvard.edu" }, ] diff --git a/src/vitessce/file_def_utils.py b/src/vitessce/file_def_utils.py index b3c69a86..f0332d73 100644 --- a/src/vitessce/file_def_utils.py +++ b/src/vitessce/file_def_utils.py @@ -146,6 +146,16 @@ def gen_sdata_obs_segmentations_schema(options, path: str, table_path: str = "ta options["obsSegmentations"]['coordinateSystem'] = coordinate_system return options +def gen_sdata_obs_points_schema(options, path: str, table_path: str = "tables/table", coordinate_system: Optional[str] = None) -> dict: + if path is not None: + options["obsPoints"] = { + "path": path + } + if table_path is not None: + options["obsPoints"]['tablePath'] = table_path + if coordinate_system is not None: + options["obsPoints"]['coordinateSystem'] = coordinate_system + return options def gen_sdata_obs_spots_schema(options: dict, shapes_path: str, table_path: str = "tables/table", region: Optional[str] = None, coordinate_system: Optional[str] = None) -> dict: if shapes_path is not None: diff --git a/src/vitessce/widget.py b/src/vitessce/widget.py index 53c1072c..b1b3e066 100644 --- a/src/vitessce/widget.py +++ b/src/vitessce/widget.py @@ -651,7 +651,7 @@ class VitessceWidget(anywidget.AnyWidget): next_port = DEFAULT_PORT - js_package_version = Unicode('3.6.18').tag(sync=True) + js_package_version = Unicode('3.8.3').tag(sync=True) js_dev_mode = Bool(False).tag(sync=True) custom_js_url = Unicode('').tag(sync=True) plugin_esm = List(trait=Unicode(''), default_value=[]).tag(sync=True) @@ -664,7 +664,7 @@ class VitessceWidget(anywidget.AnyWidget): store_urls = List(trait=Unicode(''), default_value=[]).tag(sync=True) - def __init__(self, config, height=600, theme='auto', uid=None, port=None, proxy=False, js_package_version='3.6.18', 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): + def __init__(self, config, height=600, theme='auto', uid=None, port=None, proxy=False, js_package_version='3.8.3', 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): """ Construct a new Vitessce widget. Not intended to be instantiated directly; instead, use ``VitessceConfig.widget``. @@ -798,7 +798,7 @@ def _plugin_command(self, params, buffers): # Launch Vitessce using plain HTML representation (no ipywidgets) -def ipython_display(config, height=600, theme='auto', base_url=None, host_name=None, uid=None, port=None, proxy=False, js_package_version='3.6.18', js_dev_mode=False, custom_js_url='', plugins=None, remount_on_uid_change=True, page_mode=False, page_esm=None, server_host=None): +def ipython_display(config, height=600, theme='auto', base_url=None, host_name=None, uid=None, port=None, proxy=False, js_package_version='3.8.3', js_dev_mode=False, custom_js_url='', plugins=None, remount_on_uid_change=True, page_mode=False, page_esm=None, server_host=None): from IPython.display import display, HTML uid_str = "vitessce" + get_uid_str(uid) diff --git a/src/vitessce/wrappers.py b/src/vitessce/wrappers.py index 99964d4b..208d41a5 100644 --- a/src/vitessce/wrappers.py +++ b/src/vitessce/wrappers.py @@ -28,6 +28,7 @@ gen_obs_sets_schema, gen_sdata_image_schema, gen_sdata_obs_segmentations_schema, + gen_sdata_obs_points_schema, gen_sdata_obs_spots_schema, gen_sdata_obs_sets_schema, gen_sdata_obs_feature_matrix_schema, @@ -1399,7 +1400,7 @@ def auto_view_config(self, vc): class SpatialDataWrapper(AnnDataWrapper): - def __init__(self, sdata_path: Optional[str] = None, sdata_url: Optional[str] = None, sdata_store: Optional[Union[str, zarr.storage.StoreLike]] = None, sdata_artifact: Optional[ln.Artifact] = None, image_path: Optional[str] = None, region: Optional[str] = None, coordinate_system: Optional[str] = None, obs_spots_path: Optional[str] = None, obs_segmentations_path: Optional[str] = None, table_path: str = "tables/table", is_zip=None, coordination_values=None, **kwargs): + def __init__(self, sdata_path: Optional[str] = None, sdata_url: Optional[str] = None, sdata_store: Optional[Union[str, zarr.storage.StoreLike]] = None, sdata_artifact: Optional[ln.Artifact] = None, image_path: Optional[str] = None, region: Optional[str] = None, coordinate_system: Optional[str] = None, obs_spots_path: Optional[str] = None, obs_segmentations_path: Optional[str] = None, obs_points_path: Optional[str] = None, table_path: str = "tables/table", is_zip=None, coordination_values=None, **kwargs): """ Wrap a SpatialData object. @@ -1422,6 +1423,8 @@ def __init__(self, sdata_path: Optional[str] = None, sdata_url: Optional[str] = :type obs_spots_path: Optional[str] :param obs_segmentations_path: Path to a labels or shapes element (segmentation bitmask label image or segmentation polygon shapes), by default None :type obs_segmentations_path: Optional[str] + :param obs_points_path: Path to a points element, by default None + :type obs_points_path: Optional[str] """ raise_error_if_zero_or_more_than_one([ sdata_path, @@ -1451,6 +1454,7 @@ def __init__(self, sdata_path: Optional[str] = None, sdata_url: Optional[str] = self._kwargs = kwargs self._obs_spots_path = obs_spots_path self._obs_segmentations_path = obs_segmentations_path + self._obs_points_path = obs_points_path if self._adata_path is not None: self.zarr_folder = 'spatialdata.zarr' self.obs_type_label = None @@ -1538,6 +1542,7 @@ def generator(base_url): options = gen_sdata_obs_spots_schema(options, self._obs_spots_path, self._table_path, self._region, self._coordinate_system) options = gen_sdata_image_schema(options, self._image_path, self._coordinate_system) options = gen_sdata_obs_segmentations_schema(options, self._obs_segmentations_path, self._table_path, self._coordinate_system) + options = gen_sdata_obs_points_schema(options, self._obs_points_path, self._table_path, self._coordinate_system) options = gen_feature_labels_schema(self._feature_labels, options) if len(options.keys()) > 0: obj_file_def = { From 5953a0b59f6a3efe9899bfcb2df8d15d4a8959ef Mon Sep 17 00:00:00 2001 From: Mark Keller <7525285+keller-mark@users.noreply.github.com> Date: Mon, 13 Oct 2025 13:51:43 -0400 Subject: [PATCH 2/2] Lint --- src/vitessce/file_def_utils.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/vitessce/file_def_utils.py b/src/vitessce/file_def_utils.py index f0332d73..1e1eb57b 100644 --- a/src/vitessce/file_def_utils.py +++ b/src/vitessce/file_def_utils.py @@ -146,6 +146,7 @@ def gen_sdata_obs_segmentations_schema(options, path: str, table_path: str = "ta options["obsSegmentations"]['coordinateSystem'] = coordinate_system return options + def gen_sdata_obs_points_schema(options, path: str, table_path: str = "tables/table", coordinate_system: Optional[str] = None) -> dict: if path is not None: options["obsPoints"] = { @@ -157,6 +158,7 @@ def gen_sdata_obs_points_schema(options, path: str, table_path: str = "tables/ta options["obsPoints"]['coordinateSystem'] = coordinate_system return options + def gen_sdata_obs_spots_schema(options: dict, shapes_path: str, table_path: str = "tables/table", region: Optional[str] = None, coordinate_system: Optional[str] = None) -> dict: if shapes_path is not None: options['obsSpots'] = {