Visualization
SpatialOmics extends standard Makie verbs to dispatch directly on spatial element types. Users write the same Makie code they already know; the type system routes to spatial-aware implementations.
Backend choice
SpatialOmics depends only on the abstract Makie package. Load a concrete backend before calling any plot verb:
using GLMakie # recommended for interactive exploration
# or
using CairoMakie # recommended for notebooks / publication figures
# or
using WGLMakie # for Pluto / JupyterPlotting verbs
| Call | What it does |
|---|---|
image!(ax, channel(img, 1)) | Pyramid-aware image; selects the correct level on each zoom |
scatter!(ax, pts) | Transcript / centroid scatter from coords(pts) |
poly!(ax, shp) | Cell boundary / annotation polygons from SpatialShapes |
heatmap!(ax, lbl) | Integer label overlay from SpatialLabels |
All verbs have mutating ! variants and accept the full set of Makie keyword arguments (color, strokewidth, markersize, etc.).
Pyramid-aware images
SpatialImage objects loaded from OME-Zarr carry pre-computed pyramid levels as lazy DiskArray-backed arrays. image!(ax, img) selects the correct resolution level on every zoom or pan event. No pixels are loaded until a viewport is established.
img = images(ds, "morphology_focus")
fig, ax, _ = image(channel(img, 1);
axis=(; aspect=DataAspect(), yreversed=true),
figure=(; size=(700, 230)))
tightlimits!(ax)Channel selection and display scaling
For multi-channel images, extract a single channel with channel, then apply scaleminmax for display-time intensity normalisation:
dapi = channel(img, 1) # or channel(img, "DAPI")
image!(ax, scaleminmax(dapi))scaleminmax samples the intensity range from the coarsest pyramid level and attaches a min-max display transform applied at render time — no copy is made.
Lazy spatial views
view(ds, ext) returns a SpatialDatasetView scoped to a bounding box. This is the preferred way to build composite panels: define the ROI once, then pass the view to each layer independently.
ext = SpatialExtent(cx - 500.0, cx + 500.0, cy - 500.0, cy + 500.0; coord_system="global")
roi = view(ds, ext)
fig = Figure(size=(600, 600))
ax = Axis(fig[1, 1]; aspect=DataAspect(), yreversed=true,
xlabel="x (µm)", ylabel="y (µm)")
# Each call applies the crop independently — no intermediate copies
image!(ax, scaleminmax(channel(images(roi, "morphology_focus"), 1)))
poly!(ax, shapes(roi, "cell_boundaries"); color=:transparent,
strokecolor=:cyan, strokewidth=0.4)
scatter!(ax, points(roi, "transcripts"); color=(:red, 0.25), markersize=1)
tightlimits!(ax)
figWhen a SpatialImage is accessed through a SpatialDatasetView, the axis limits are automatically constrained to the view extent.
Multi-channel display
ch_names = ["DAPI", "ATP1A1/CD45/E-Cad", "18S", "AlphaSMA/Vim"]
fig = Figure(size=(900, 250))
for (i, lbl) in enumerate(ch_names)
ax = Axis(fig[1, i]; title=lbl, aspect=DataAspect(),
yreversed=true, xticksvisible=false, yticksvisible=false)
image!(ax, scaleminmax(channel(images(ds, "morphology_focus"), i)))
tightlimits!(ax)
end
figRGB composite
Use colorview to combine single-channel images into a colour composite:
r = scaleminmax(channel(img, 1))
g = scaleminmax(channel(img, 2))
b = scaleminmax(channel(img, 3))
image!(ax, colorview(RGB, r, g, b))