diff --git a/.gitignore b/.gitignore index da2f0211..ba206a33 100644 --- a/.gitignore +++ b/.gitignore @@ -22,6 +22,10 @@ __pycache__/ # docs /docs/generated/ /docs/_build/ +/docs/auto_gallery/ +/docs/sg_execution_times.rst +/docs/gallery/**/data/ +/docs/gallery/**/__pycache__/ # IDEs /.idea/ diff --git a/docs/_static/css/custom.css b/docs/_static/css/custom.css index b8c8d47f..699553df 100644 --- a/docs/_static/css/custom.css +++ b/docs/_static/css/custom.css @@ -2,3 +2,8 @@ div.cell_output table.dataframe { font-size: 0.8em; } + +/* Gallery landing: subsection links are shown in the sidebar; hide the duplicate inline list. */ +section#gallery > .toctree-wrapper { + display: none; +} diff --git a/docs/conf.py b/docs/conf.py index 3e7591a8..aaccb72d 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -56,6 +56,7 @@ "sphinx.ext.intersphinx", "sphinx.ext.autosummary", "sphinx.ext.napoleon", + "sphinx_gallery.gen_gallery", "sphinxcontrib.bibtex", "sphinxcontrib.katex", "sphinx_autodoc_typehints", @@ -118,6 +119,9 @@ "tutorials/notebooks/README.md", "tutorials/notebooks/references.md", "tutorials/notebooks/notebooks/paper_reproducibility/*", + "gallery/*", + "auto_gallery/**/*.ipynb", + "auto_gallery/**/*.py", ] @@ -157,3 +161,19 @@ # you can add an exception to this list. ("py:class", "igraph.Graph"), ] + +# -- Sphinx-Gallery configuration ------------------------------------------- + +sys.path.insert(0, str(HERE / "gallery")) + +sphinx_gallery_conf = { + "examples_dirs": ["gallery"], + "gallery_dirs": ["auto_gallery"], + "filename_pattern": r"/plot_", + "ignore_pattern": r"(__init__|_helpers)\.py", + "image_scrapers": ("matplotlib",), + "matplotlib_animations": True, + "within_subsection_order": "FileNameSortKey", + "nested_sections": True, + "download_all_examples": True, +} diff --git a/docs/gallery.rst b/docs/gallery.rst new file mode 100644 index 00000000..b187672f --- /dev/null +++ b/docs/gallery.rst @@ -0,0 +1,15 @@ +Gallery +------- + +Examples demonstrating the plotting capabilities of ``spatialdata-plot``. + +.. toctree:: + :maxdepth: 1 + + auto_gallery/basic/index + auto_gallery/customization/index + auto_gallery/overlays/index + +.. include:: auto_gallery/index.rst + :start-after: Examples demonstrating the plotting capabilities of ``spatialdata-plot``. + :end-before: .. toctree:: diff --git a/docs/gallery/README.rst b/docs/gallery/README.rst new file mode 100644 index 00000000..0eeb3ff8 --- /dev/null +++ b/docs/gallery/README.rst @@ -0,0 +1,4 @@ +Gallery +------- + +Examples demonstrating the plotting capabilities of ``spatialdata-plot``. diff --git a/docs/gallery/_helpers.py b/docs/gallery/_helpers.py new file mode 100644 index 00000000..c28194fb --- /dev/null +++ b/docs/gallery/_helpers.py @@ -0,0 +1,60 @@ +"""Shared data loaders for gallery examples.""" + +from __future__ import annotations + +import warnings + +import numpy as np +import scanpy as sc +import spatialdata as sd +from spatialdata.models import Image2DModel, ShapesModel, TableModel + + +def load_visium_breast_cancer() -> sd.SpatialData: + """Load Visium breast cancer tissue as a SpatialData object. + + Uses ``scanpy.datasets.visium_sge`` which caches the download + via :mod:`pooch`, so the data is only fetched once. + """ + with warnings.catch_warnings(): + warnings.simplefilter("ignore") + adata = sc.datasets.visium_sge( + sample_id="V1_Breast_Cancer_Block_A_Section_1", + ) + + sample = list(adata.uns["spatial"].keys())[0] + meta = adata.uns["spatial"][sample] + sf = meta["scalefactors"]["tissue_hires_scalef"] + + image = Image2DModel.parse( + np.moveaxis(meta["images"]["hires"], -1, 0), + dims=("c", "y", "x"), + ) + + radius = meta["scalefactors"]["spot_diameter_fullres"] * sf / 2 + circles = ShapesModel.parse( + adata.obsm["spatial"] * sf, + geometry=0, + radius=radius, + index=adata.obs_names, + ) + + adata.obs["region"] = "spots" + adata.obs["region"] = adata.obs["region"].astype("category") + adata.obs["instance_key"] = adata.obs_names + table = TableModel.parse( + adata, + region="spots", + region_key="region", + instance_key="instance_key", + ) + + table.var_names_make_unique() + sc.pp.normalize_total(table) + sc.pp.log1p(table) + + return sd.SpatialData( + images={"tissue": image}, + shapes={"spots": circles}, + tables={"table": table}, + ) diff --git a/docs/gallery/basic/README.rst b/docs/gallery/basic/README.rst new file mode 100644 index 00000000..8b3fbeea --- /dev/null +++ b/docs/gallery/basic/README.rst @@ -0,0 +1,4 @@ +Basic +----- + +Rendering individual spatial element types. diff --git a/docs/gallery/basic/plot_render_images.py b/docs/gallery/basic/plot_render_images.py new file mode 100644 index 00000000..6dbcdd2d --- /dev/null +++ b/docs/gallery/basic/plot_render_images.py @@ -0,0 +1,14 @@ +""" +Render tissue image +=================== + +Render an H&E tissue image from a Visium experiment. +""" + +from _helpers import load_visium_breast_cancer + +import spatialdata_plot # noqa: F401 + +sdata = load_visium_breast_cancer() + +sdata.pl.render_images("tissue").pl.show() diff --git a/docs/gallery/basic/plot_render_labels.py b/docs/gallery/basic/plot_render_labels.py new file mode 100644 index 00000000..df673097 --- /dev/null +++ b/docs/gallery/basic/plot_render_labels.py @@ -0,0 +1,14 @@ +""" +Render labels +============= + +Render a cell segmentation mask. +""" + +import spatialdata as sd + +import spatialdata_plot # noqa: F401 + +sdata = sd.datasets.blobs() + +sdata.pl.render_labels("blobs_labels").pl.show() diff --git a/docs/gallery/basic/plot_render_labels_contour.py b/docs/gallery/basic/plot_render_labels_contour.py new file mode 100644 index 00000000..5fac8cde --- /dev/null +++ b/docs/gallery/basic/plot_render_labels_contour.py @@ -0,0 +1,14 @@ +""" +Render label contours +===================== + +Render segmentation boundaries using ``contour_px``. +""" + +import spatialdata as sd + +import spatialdata_plot # noqa: F401 + +sdata = sd.datasets.blobs() + +sdata.pl.render_labels("blobs_labels", contour_px=3).pl.show() diff --git a/docs/gallery/basic/plot_render_points.py b/docs/gallery/basic/plot_render_points.py new file mode 100644 index 00000000..6090ebf4 --- /dev/null +++ b/docs/gallery/basic/plot_render_points.py @@ -0,0 +1,14 @@ +""" +Render points +============= + +Render transcript detections as points. +""" + +import spatialdata as sd + +import spatialdata_plot # noqa: F401 + +sdata = sd.datasets.blobs() + +sdata.pl.render_points("blobs_points", size=3).pl.show() diff --git a/docs/gallery/basic/plot_render_shapes.py b/docs/gallery/basic/plot_render_shapes.py new file mode 100644 index 00000000..3d356274 --- /dev/null +++ b/docs/gallery/basic/plot_render_shapes.py @@ -0,0 +1,14 @@ +""" +Render spots +============ + +Render Visium spot shapes on their own. +""" + +from _helpers import load_visium_breast_cancer + +import spatialdata_plot # noqa: F401 + +sdata = load_visium_breast_cancer() + +sdata.pl.render_shapes("spots").pl.show() diff --git a/docs/gallery/customization/README.rst b/docs/gallery/customization/README.rst new file mode 100644 index 00000000..de4b4d12 --- /dev/null +++ b/docs/gallery/customization/README.rst @@ -0,0 +1,4 @@ +Customization +------------- + +Styling options: colormaps, outlines, contours, and alpha. diff --git a/docs/gallery/customization/plot_gene_cmap.py b/docs/gallery/customization/plot_gene_cmap.py new file mode 100644 index 00000000..6cba571d --- /dev/null +++ b/docs/gallery/customization/plot_gene_cmap.py @@ -0,0 +1,14 @@ +""" +Gene expression with custom colormap +===================================== + +Color spots by gene expression using a custom colormap. +""" + +from _helpers import load_visium_breast_cancer + +import spatialdata_plot # noqa: F401 + +sdata = load_visium_breast_cancer() + +(sdata.pl.render_images("tissue").pl.render_shapes("spots", color="ERBB2", cmap="magma", fill_alpha=0.8).pl.show()) diff --git a/docs/gallery/customization/plot_multi_panel.py b/docs/gallery/customization/plot_multi_panel.py new file mode 100644 index 00000000..36b4c7ca --- /dev/null +++ b/docs/gallery/customization/plot_multi_panel.py @@ -0,0 +1,31 @@ +""" +Multi-panel layout +================== + +Display multiple coordinate systems side by side. +""" + +import numpy as np +import spatialdata as sd +from spatialdata.models import Image2DModel +from spatialdata.transformations import Identity + +import spatialdata_plot # noqa: F401 + +rng = np.random.default_rng(0) +img_a = Image2DModel.parse( + rng.random((3, 64, 64)), + dims=("c", "y", "x"), + transformations={"sample_a": Identity()}, +) +img_b = Image2DModel.parse( + rng.random((3, 64, 64)), + dims=("c", "y", "x"), + transformations={"sample_b": Identity()}, +) +sdata = sd.SpatialData(images={"img_a": img_a, "img_b": img_b}) + +sdata.pl.render_images().pl.show( + coordinate_systems=["sample_a", "sample_b"], + figsize=(8, 4), +) diff --git a/docs/gallery/customization/plot_shapes_outline.py b/docs/gallery/customization/plot_shapes_outline.py new file mode 100644 index 00000000..99d3ccf4 --- /dev/null +++ b/docs/gallery/customization/plot_shapes_outline.py @@ -0,0 +1,24 @@ +""" +Spots with outlines +=================== + +Style spots with visible outlines and translucent fill. +""" + +from _helpers import load_visium_breast_cancer + +import spatialdata_plot # noqa: F401 + +sdata = load_visium_breast_cancer() + +( + sdata.pl.render_images("tissue") + .pl.render_shapes( + "spots", + fill_alpha=0.3, + outline_width=1.5, + outline_color="black", + outline_alpha=1.0, + ) + .pl.show() +) diff --git a/docs/gallery/customization/plot_single_channel.py b/docs/gallery/customization/plot_single_channel.py new file mode 100644 index 00000000..208b5747 --- /dev/null +++ b/docs/gallery/customization/plot_single_channel.py @@ -0,0 +1,14 @@ +""" +Single channel with colormap +============================= + +Render one image channel with a colormap. +""" + +from _helpers import load_visium_breast_cancer + +import spatialdata_plot # noqa: F401 + +sdata = load_visium_breast_cancer() + +sdata.pl.render_images("tissue", channel=0, cmap="viridis").pl.show() diff --git a/docs/gallery/overlays/README.rst b/docs/gallery/overlays/README.rst new file mode 100644 index 00000000..ca97656a --- /dev/null +++ b/docs/gallery/overlays/README.rst @@ -0,0 +1,4 @@ +Overlays +-------- + +Combining multiple spatial element layers in a single plot. diff --git a/docs/gallery/overlays/plot_color_by_category.py b/docs/gallery/overlays/plot_color_by_category.py new file mode 100644 index 00000000..d336bc23 --- /dev/null +++ b/docs/gallery/overlays/plot_color_by_category.py @@ -0,0 +1,14 @@ +""" +Color spots by category +======================= + +Overlay spots colored by a categorical annotation. +""" + +from _helpers import load_visium_breast_cancer + +import spatialdata_plot # noqa: F401 + +sdata = load_visium_breast_cancer() + +(sdata.pl.render_images("tissue").pl.render_shapes("spots", color="in_tissue", fill_alpha=0.7).pl.show()) diff --git a/docs/gallery/overlays/plot_color_by_gene.py b/docs/gallery/overlays/plot_color_by_gene.py new file mode 100644 index 00000000..bdf7f669 --- /dev/null +++ b/docs/gallery/overlays/plot_color_by_gene.py @@ -0,0 +1,14 @@ +""" +Color spots by gene expression +=============================== + +Overlay spots colored by a gene on an H&E tissue image. +""" + +from _helpers import load_visium_breast_cancer + +import spatialdata_plot # noqa: F401 + +sdata = load_visium_breast_cancer() + +(sdata.pl.render_images("tissue").pl.render_shapes("spots", color="ERBB2", fill_alpha=0.8).pl.show()) diff --git a/docs/gallery/overlays/plot_filter_groups.py b/docs/gallery/overlays/plot_filter_groups.py new file mode 100644 index 00000000..3532c8c6 --- /dev/null +++ b/docs/gallery/overlays/plot_filter_groups.py @@ -0,0 +1,14 @@ +""" +Filter by category groups +========================= + +Show only selected categories using the ``groups`` parameter. +""" + +from _helpers import load_visium_breast_cancer + +import spatialdata_plot # noqa: F401 + +sdata = load_visium_breast_cancer() + +(sdata.pl.render_images("tissue").pl.render_shapes("spots", color="in_tissue", groups=["1"], fill_alpha=0.7).pl.show()) diff --git a/docs/gallery/overlays/plot_images_shapes.py b/docs/gallery/overlays/plot_images_shapes.py new file mode 100644 index 00000000..068f938e --- /dev/null +++ b/docs/gallery/overlays/plot_images_shapes.py @@ -0,0 +1,14 @@ +""" +Tissue with spots +================= + +Overlay Visium spots on an H&E tissue image. +""" + +from _helpers import load_visium_breast_cancer + +import spatialdata_plot # noqa: F401 + +sdata = load_visium_breast_cancer() + +(sdata.pl.render_images("tissue").pl.render_shapes("spots", fill_alpha=0.5).pl.show()) diff --git a/docs/index.md b/docs/index.md index 53ff8f2a..0d4bca75 100644 --- a/docs/index.md +++ b/docs/index.md @@ -4,8 +4,9 @@ ```{toctree} :hidden: true -:maxdepth: 1 +:maxdepth: 2 +gallery api.md changelog.md contributing.md diff --git a/pyproject.toml b/pyproject.toml index bf21ab7b..31527420 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -50,11 +50,14 @@ test = [ doc = [ "ipykernel", "ipython>=8.6", + "matplotlib", "myst-nb>=1.1", + "pillow", "sphinx>=8.1", "sphinx-autodoc-typehints", "sphinx-book-theme>=1", "sphinx-copybutton", + "sphinx-gallery", "sphinx-tabs", "sphinxcontrib-bibtex>=1", "sphinxcontrib-katex",