Skip to content

Commit efd07ac

Browse files
committed
Merge
2 parents 888dfde + 9f31d6e commit efd07ac

11 files changed

Lines changed: 602 additions & 242 deletions

File tree

.coveragerc_omit

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,5 @@ omit =
1414
src/vitessce/data_utils/multivec.py
1515
src/vitessce/data_utils/spatialdata_points_zorder.py
1616
src/vitessce/widget_plugins/demo_plugin.py
17-
src/vitessce/widget_plugins/spatial_query.py
17+
src/vitessce/widget_plugins/spatial_query.py
18+
tests/*.py

.github/workflows/deploy.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ jobs:
3838
path: ./docs-dist/html
3939
- name: Deploy package to PyPI
4040
continue-on-error: true
41-
uses: pypa/gh-action-pypi-publish@v1.4.1
41+
uses: pypa/gh-action-pypi-publish@release/v1.14
4242
with:
4343
user: __token__
4444
password: ${{ secrets.PYPI_API_TOKEN }}

README.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,18 @@ For a development installation (requires NodeJS and NPM),
3131
$ uv sync --extra dev --extra docs --extra all
3232

3333

34+
Alternatively, use conda:
35+
36+
$ conda create -n vitessce-dev python=3.12
37+
$ conda activate vitessce-dev
38+
$ pip install -e ".[dev,docs,all]"
39+
40+
Troubleshooting SpatialQuery installation on macOS:
41+
42+
$ # Use the macOS clang instead of from homebrew.
43+
$ CC=/usr/bin/clang CXX=/usr/bin/clang++ pip install -e ".[dev,docs,all,sq]"
44+
45+
3446
## Linting and testing
3547

3648
```sh

docs/notebooks/spatial_data_blobs.ipynb

Lines changed: 33 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,9 @@
3030
"outputs": [],
3131
"source": [
3232
"import spatialdata\n",
33-
"from os.path import join"
33+
"from os.path import join\n",
34+
"import pandas as pd\n",
35+
"from anndata import AnnData"
3436
]
3537
},
3638
{
@@ -39,7 +41,8 @@
3941
"metadata": {},
4042
"outputs": [],
4143
"source": [
42-
"sdata = spatialdata.datasets.blobs()"
44+
"sdata = spatialdata.datasets.blobs()\n",
45+
"sdata"
4346
]
4447
},
4548
{
@@ -48,7 +51,13 @@
4851
"metadata": {},
4952
"outputs": [],
5053
"source": [
51-
"spatialdata_filepath = join(\"data\", \"blobs.spatialdata.zarr\")"
54+
"# This blobs dataset only contains a table with a var.index corresponding to the shapes features.\n",
55+
"# We need to construct a table with a var.index corresponding to the points features:\n",
56+
"ddf = sdata.points[\"blobs_points\"]\n",
57+
"unique_gene_ids = ddf[\"genes\"].unique().compute().tolist()\n",
58+
"points_var_df = pd.DataFrame(index=unique_gene_ids, data=[], columns=[])\n",
59+
"points_table = AnnData(var=points_var_df, obs=None, X=None)\n",
60+
"sdata.tables['table_points'] = points_table"
5261
]
5362
},
5463
{
@@ -57,7 +66,7 @@
5766
"metadata": {},
5867
"outputs": [],
5968
"source": [
60-
"sdata.write(spatialdata_filepath, overwrite=True)"
69+
"spatialdata_filepath = join(\"data\", \"blobs.spatialdata.zarr\")"
6170
]
6271
},
6372
{
@@ -66,13 +75,7 @@
6675
"metadata": {},
6776
"outputs": [],
6877
"source": [
69-
"# Change data type of x and y columns from int64 to int32\n",
70-
"# Temporary workaround, will be resolved by https://github.com/vitessce/vitessce/issues/2292\n",
71-
"ddf = sdata[\"blobs_points\"]\n",
72-
"ddf[\"x\"] = ddf[\"x\"].astype('int32')\n",
73-
"ddf[\"y\"] = ddf[\"y\"].astype('int32')\n",
74-
"sdata[\"blobs_points_2\"] = ddf\n",
75-
"sdata.write_element(\"blobs_points_2\")"
78+
"sdata.write(spatialdata_filepath, overwrite=True)"
7679
]
7780
},
7881
{
@@ -112,23 +115,28 @@
112115
")\n",
113116
"# Add data to the configuration:\n",
114117
"wrapper = SpatialDataWrapper(\n",
115-
" sdata_store=spatialdata_filepath,\n",
118+
" sdata_path=spatialdata_filepath,\n",
116119
" # The following paths are relative to the root of the SpatialData zarr store on-disk.\n",
117120
" image_path=\"images/blobs_image\",\n",
118121
" obs_segmentations_path=\"labels/blobs_labels\",\n",
122+
" obs_embedding_paths=[\"tables/table/obsm/X_umap\"],\n",
123+
" obs_feature_matrix_path=\"tables/table/X\",\n",
119124
" coordinate_system=\"global\",\n",
120125
" coordination_values={\n",
121126
" \"obsType\": \"blob\",\n",
127+
" \"featureType\": \"channel\",\n",
122128
" \"fileUid\": \"my_unique_id\"\n",
123129
" }\n",
124130
")\n",
125131
"points_wrapper = SpatialDataWrapper(\n",
126-
" sdata_store=spatialdata_filepath,\n",
132+
" sdata_path=spatialdata_filepath,\n",
127133
" # The following paths are relative to the root of the SpatialData zarr store on-disk.\n",
128-
" obs_points_path=\"points/blobs_points_2\",\n",
134+
" obs_points_path=\"points/blobs_points\",\n",
135+
" obs_feature_matrix_path=\"tables/table_points/X\", # TODO\n",
129136
" coordinate_system=\"global\",\n",
130137
" coordination_values={\n",
131138
" \"obsType\": \"point\",\n",
139+
" \"featureType\": \"gene\",\n",
132140
" \"fileUid\": \"other_unique_id\"\n",
133141
" }\n",
134142
")\n",
@@ -169,6 +177,7 @@
169177
" 'segmentationChannel': CL([{\n",
170178
" 'spatialChannelVisible': True,\n",
171179
" 'obsType': 'blob',\n",
180+
" \"featureType\": \"channel\",\n",
172181
" }]),\n",
173182
" }]),\n",
174183
"}, scope_prefix=get_initial_coordination_scope_prefix(\"A\", \"obsSegmentations\"))\n",
@@ -177,7 +186,10 @@
177186
" 'pointLayer': CL([{\n",
178187
" \"fileUid\": \"other_unique_id\",\n",
179188
" \"obsType\": \"point\",\n",
189+
" \"featureType\": \"gene\",\n",
180190
" \"obsHighlight\": None,\n",
191+
" \"obsColorEncoding\": \"randomByFeature\",\n",
192+
" \"spatialLayerOpacity\": 1.0,\n",
181193
" }]),\n",
182194
"}, scope_prefix=get_initial_coordination_scope_prefix(\"A\", \"obsPoints\"))\n",
183195
"\n",
@@ -202,6 +214,13 @@
202214
"vw"
203215
]
204216
},
217+
{
218+
"cell_type": "code",
219+
"execution_count": null,
220+
"metadata": {},
221+
"outputs": [],
222+
"source": []
223+
},
205224
{
206225
"cell_type": "code",
207226
"execution_count": null,

docs/notebooks/widget_plugin_spatial-query.ipynb

Lines changed: 58 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -19,20 +19,6 @@
1919
{
2020
"cell_type": "code",
2121
"execution_count": null,
22-
"metadata": {
23-
"tags": []
24-
},
25-
"outputs": [],
26-
"source": [
27-
"#!pip install \"vitessce[all]==3.3.0\" esbuild_py anndata\n",
28-
"!pip install \"mlxtend~=0.23.0\"\n",
29-
"#!pip install -i \"https://test.pypi.org/simple/\" SpatialQuery\n",
30-
"!pip install \"SpatialQuery @ git+https://github.com/ShaokunAn/Spatial-Query@main\""
31-
]
32-
},
33-
{
34-
"cell_type": "code",
35-
"execution_count": 1,
3622
"metadata": {},
3723
"outputs": [],
3824
"source": [
@@ -44,41 +30,81 @@
4430
" ViewType as vt,\n",
4531
" CoordinationType as ct,\n",
4632
" CoordinationLevel as CL,\n",
33+
" hconcat,\n",
34+
" vconcat,\n",
4735
")\n",
4836
"from vitessce.widget_plugins import SpatialQueryPlugin"
4937
]
5038
},
39+
{
40+
"cell_type": "markdown",
41+
"metadata": {},
42+
"source": [
43+
"## Download example dataset\n",
44+
"\n",
45+
"Download the `secondary_analysis.h5ad` file for sample `HBM838.LDFP.578` from the HuBMAP Portal at https://portal.hubmapconsortium.org/browse/dataset/5bf1e7b295343c4537206beda25aa4ca"
46+
]
47+
},
5148
{
5249
"cell_type": "code",
53-
"execution_count": 2,
50+
"execution_count": null,
5451
"metadata": {},
5552
"outputs": [],
5653
"source": [
57-
"adata = read_h5ad(join(\"data\", \"HBM987_KWLK_254\", \"secondary_analysis.h5ad\"))\n",
54+
"import os\n",
55+
"from os.path import join, isfile, isdir\n",
56+
"from urllib.request import urlretrieve\n",
57+
"\n",
58+
"adata_path = join(\"data\", \"HBM987_KWLK_254\", \"secondary_analysis.h5ad\")\n",
5859
"zarr_path = join(\"data\", \"HBM987_KWLK_254\", \"secondary_analysis.h5ad.zarr\")\n",
60+
"\n",
61+
"if not isdir(join(\"data\")):\n",
62+
" os.makedirs(join(\"data\"), exist_ok=True)\n",
63+
" if not isdir(join(\"data\", \"HBM987_KWLK_254\")):\n",
64+
" os.makedirs(join(\"data\", \"HBM987_KWLK_254\"), exist_ok=True)\n",
65+
" if not isfile(adata_path):\n",
66+
" urlretrieve('https://assets.hubmapconsortium.org/0a21f3fa27109790483f2a0729be53de/secondary_analysis.h5ad', adata_path)"
67+
]
68+
},
69+
{
70+
"cell_type": "code",
71+
"execution_count": null,
72+
"metadata": {},
73+
"outputs": [],
74+
"source": [
75+
"adata = read_h5ad(adata_path)\n",
5976
"adata.write_zarr(zarr_path)"
6077
]
6178
},
6279
{
6380
"cell_type": "code",
64-
"execution_count": 3,
81+
"execution_count": null,
82+
"metadata": {},
83+
"outputs": [],
84+
"source": [
85+
"adata"
86+
]
87+
},
88+
{
89+
"cell_type": "code",
90+
"execution_count": null,
6591
"metadata": {},
6692
"outputs": [],
6793
"source": [
68-
"plugin = SpatialQueryPlugin(adata)"
94+
"plugin = SpatialQueryPlugin(adata, label_key=\"predicted_label\", spatial_key=\"X_spatial\", feature_name=\"hugo_symbol\")"
6995
]
7096
},
7197
{
7298
"cell_type": "code",
73-
"execution_count": 12,
99+
"execution_count": null,
74100
"metadata": {},
75101
"outputs": [],
76102
"source": [
77103
"vc = VitessceConfig(schema_version=\"1.0.16\", name=\"Spatial-Query\")\n",
78104
"dataset = vc.add_dataset(\"Query results\").add_object(AnnDataWrapper(\n",
79105
" adata_path=zarr_path,\n",
80106
" obs_feature_matrix_path=\"X\",\n",
81-
" obs_set_paths=[\"obs/predicted.ASCT.celltype\"],\n",
107+
" obs_set_paths=[\"obs/predicted_label\"],\n",
82108
" obs_set_names=[\"Cell Type\"],\n",
83109
" obs_spots_path=\"obsm/X_spatial\",\n",
84110
" feature_labels_path=\"var/hugo_symbol\",\n",
@@ -92,6 +118,8 @@
92118
"sets_view = vc.add_view(\"obsSets\", dataset=dataset)\n",
93119
"features_view = vc.add_view(\"featureList\", dataset=dataset)\n",
94120
"sq_view = vc.add_view(\"spatialQuery\", dataset=dataset)\n",
121+
"sq_heatmap = vc.add_view(\"spatialQueryHeatmap\", dataset=dataset)\n",
122+
"\n",
95123
"\n",
96124
"obs_set_selection_scope, = vc.add_coordination(\"obsSetSelection\",)\n",
97125
"obs_set_selection_scope.set_value(None)\n",
@@ -114,18 +142,25 @@
114142
" ])\n",
115143
"})\n",
116144
"\n",
117-
"vc.layout((spatial_view | (lc_view / features_view)) / (sets_view | sq_view));"
145+
"vc.layout((spatial_view | (lc_view / features_view)) / hconcat(sets_view, sq_heatmap, sq_view, split=[1, 1, 1]));"
118146
]
119147
},
120148
{
121149
"cell_type": "code",
122-
"execution_count": 13,
150+
"execution_count": null,
123151
"metadata": {},
124152
"outputs": [],
125153
"source": [
126154
"vw = vc.widget(height=900, plugins=[plugin], remount_on_uid_change=False)\n",
127155
"vw"
128156
]
157+
},
158+
{
159+
"cell_type": "code",
160+
"execution_count": null,
161+
"metadata": {},
162+
"outputs": [],
163+
"source": []
129164
}
130165
],
131166
"metadata": {
@@ -144,7 +179,7 @@
144179
"name": "python",
145180
"nbconvert_exporter": "python",
146181
"pygments_lexer": "ipython3",
147-
"version": "3.10.14"
182+
"version": "3.12.13"
148183
}
149184
},
150185
"nbformat": 4,

pyproject.toml

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
44

55
[project]
66
name = "vitessce"
7-
version = "3.7.9"
7+
version = "3.8.3"
88
authors = [
99
{ name="Mark Keller", email="mark_keller@hms.harvard.edu" },
1010
]
@@ -99,6 +99,13 @@ demos = [
9999
"snakemake",
100100
"pyyaml",
101101
]
102+
# SpatialQuery
103+
sq = [
104+
# When installing with `uv`, may need --no-build-isolation-package SpatialQuery
105+
"pybind11",
106+
# As of writing, `main` reflects SpatialQuery commit 9dde563 (April 4, 2026).
107+
"SpatialQuery==0.0.4"
108+
]
102109

103110
[dependency-groups]
104111
dev = [
@@ -125,6 +132,9 @@ override-dependencies = []
125132
[tool.hatch.build]
126133
exclude = [".github", "docs", "demos", "binder"]
127134

135+
[tool.hatch.metadata]
136+
allow-direct-references = true
137+
128138
[project.urls]
129139
repository = "https://github.com/vitessce/vitessce-python"
130140

src/vitessce/file_def_utils.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ def gen_sdata_obs_segmentations_schema(options, path: str, table_path: str = "ta
147147
return options
148148

149149

150-
def gen_sdata_obs_points_schema(options, path: str, table_path: str = "tables/table", coordinate_system: Optional[str] = None) -> dict:
150+
def gen_sdata_obs_points_schema(options, path: str, table_path: str = "tables/table", coordinate_system: Optional[str] = None, feature_index_column: Optional[str] = None, morton_code_column: Optional[str] = None) -> dict:
151151
if path is not None:
152152
options["obsPoints"] = {
153153
"path": path
@@ -156,6 +156,10 @@ def gen_sdata_obs_points_schema(options, path: str, table_path: str = "tables/ta
156156
options["obsPoints"]['tablePath'] = table_path
157157
if coordinate_system is not None:
158158
options["obsPoints"]['coordinateSystem'] = coordinate_system
159+
if feature_index_column is not None:
160+
options["obsPoints"]['featureIndexColumn'] = feature_index_column
161+
if morton_code_column is not None:
162+
options["obsPoints"]['mortonCodeColumn'] = morton_code_column
159163
return options
160164

161165

0 commit comments

Comments
 (0)