Skip to content

Commit e045875

Browse files
committed
feat(spatial): add Spatial.Seurat.SlideSeq
1 parent f6ae67d commit e045875

File tree

7 files changed

+620
-169
lines changed

7 files changed

+620
-169
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@ docs/
44
/*.tar.gz
55
Rplots.pdf
66
inst/doc
7+
notebooks/spatial/data/

R/spatialplot.R

Lines changed: 218 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,15 @@ SpatialPlot <- function(object, ...) {
1414
#' @rdname SpatialPlot
1515
SpatialPlot.Seurat <- function(object, image = NULL, ...) {
1616
first_image <- Seurat::Images(object)[1]
17+
if (is.null(first_image)) {
18+
stop("[SpatialPlot] No images found in the Seurat object. Is this an object with spatial data? ")
19+
}
1720
image <- image %||% first_image
1821
stype <- class(object@images[[first_image]])
1922
if ("VisiumV2" %in% stype) {
2023
SpatialPlot.Seurat.VisiumV2(object, image = image, ...)
24+
} else if ("SlideSeq" %in% stype) {
25+
SpatialPlot.Seurat.SlideSeq(object, image = NULL, ...)
2126
}
2227
}
2328

@@ -27,24 +32,22 @@ SpatialPlot.Seurat <- function(object, image = NULL, ...) {
2732
SpatialPlot.Seurat.VisiumV2 <- function(
2833
object, image = NULL, masks = NULL, shapes = NULL, points = NULL, ext = NULL,
2934
image_scale = NULL, crop = TRUE, group_by = NULL, features = NULL, layer = "data",
30-
layers = NULL, flip_y = TRUE, theme = "plotthis::theme_box", theme_args = list(),
35+
layers = NULL, flip_y = TRUE, theme = "theme_box", theme_args = list(),
3136
label = FALSE, label_size = 4, label_fg = "white", label_bg = "black", label_bg_r = 0.1,
3237
label_repel = FALSE, label_repulsion = 20, label_pt_size = 1, label_pt_color = "black",
3338
label_segment_color = "black", label_insitu = FALSE,
3439
palette = NULL, palette_reverse = FALSE, palcolor = NULL,
3540
highlight = NULL, highlight_alpha = 1, highlight_size = 1, highlight_color = "black", highlight_stroke = 0.8,
3641
legend.position = "right", legend.direction = "vertical",
42+
title = NULL, subtitle = NULL, xlab = NULL, ylab = NULL,
3743
facet_scales = "fixed", facet_nrow = NULL, facet_ncol = NULL, facet_byrow = TRUE,
3844
...
3945

4046
) {
41-
42-
ggplot <- if (getOption("plotthis.gglogger.enabled", FALSE)) {
43-
gglogger::ggplot
44-
} else {
45-
ggplot2::ggplot
46-
}
4747
stopifnot("[SpatialPlot] Either 'group_by' or 'features' should be provided, not both." = is.null(group_by) || is.null(features))
48+
if (theme %in% c("theme_box", "theme_this", "theme_blank")) {
49+
theme <- utils::getFromNamespace(theme, "plotthis")
50+
}
4851

4952
points <- points %||% TRUE
5053

@@ -70,6 +73,7 @@ SpatialPlot.Seurat.VisiumV2 <- function(
7073
ext_unscaled <- NULL
7174
if (crop) {
7275
points_data <- Seurat::GetTissueCoordinates(object)
76+
points_data <- points_data[, colnames(points_data)[!is.na(colnames(points_data))], drop = FALSE]
7377
points_data$.y <- points_data$x
7478
points_data$x <- points_data$y
7579
points_data$y <- points_data$.y
@@ -108,6 +112,7 @@ SpatialPlot.Seurat.VisiumV2 <- function(
108112
if (crop) {
109113
# attach metadata for highlighting selection
110114
points_args$data <- object@meta.data[rownames(points_data), , drop = FALSE]
115+
points_args$data <- points_args$data[, colnames(points_args$data)[!is.na(colnames(points_args$data))], drop = FALSE]
111116
points_args$data <- cbind(points_args$data, points_data)
112117
points_args$data$x <- points_args$data$x * scale_factor
113118
points_args$data$y <- points_args$data$y * scale_factor
@@ -126,19 +131,193 @@ SpatialPlot.Seurat.VisiumV2 <- function(
126131
if (!is.null(group_by)) {
127132
points_args$data[[group_by]] <- object@meta.data[[group_by]]
128133
points_args$color_by <- group_by
134+
points_args$label <- points_args$label %||% label
135+
points_args$label_size <- points_args$label_size %||% label_size
136+
points_args$label_fg <- points_args$label_fg %||% label_fg
137+
points_args$label_bg <- points_args$label_bg %||% label_bg
138+
points_args$label_bg_r <- points_args$label_bg_r %||% label_bg_r
139+
points_args$label_repel <- points_args$label_repel %||% label_repel
140+
points_args$label_repulsion <- points_args$label_repulsion %||% label_repulsion
141+
points_args$label_pt_size <- points_args$label_pt_size %||% label_pt_size
142+
points_args$label_pt_color <- points_args$label_pt_color %||% label_pt_color
143+
points_args$label_segment_color <- points_args$label_segment_color %||% label_segment_color
144+
points_args$label_insitu <- points_args$label_insitu %||% label_insitu
145+
} else if (!is.null(features)) {
146+
cells_by_image <- utils::getFromNamespace("CellsByImage", "Seurat")
147+
cells <- unique(cells_by_image(object, images = if(isFALSE(image)) NULL else image, unlist = TRUE))
148+
featdata <- Seurat::FetchData(
149+
object = object,
150+
vars = features,
151+
cells = cells,
152+
layer = layer,
153+
clean = FALSE
154+
)
155+
features <- colnames(featdata)
156+
points_args$data[, features] <- featdata
157+
points_args$color_by <- colnames(featdata)
129158
points_args$legend.position <- legend.position
130159
points_args$legend.direction <- legend.direction
131-
points_args$label <- label
132-
points_args$label_size <- label_size
133-
points_args$label_fg <- label_fg
134-
points_args$label_bg <- label_bg
135-
points_args$label_bg_r <- label_bg_r
136-
points_args$label_repel <- label_repel
137-
points_args$label_repulsion <- label_repulsion
138-
points_args$label_pt_size <- label_pt_size
139-
points_args$label_pt_color <- label_pt_color
140-
points_args$label_segment_color <- label_segment_color
141-
points_args$label_insitu <- label_insitu
160+
if (length(features) == 1) {
161+
points_args$color_name <- points_args$color_name %||% features
162+
} else {
163+
points_args$color_name <- points_args$color_name %||% "feature"
164+
facet_by <- ".facet_var"
165+
}
166+
}
167+
points_args$highlight <- points_args$highlight %||% highlight
168+
points_args$highlight_alpha <- points_args$highlight_alpha %||% highlight_alpha
169+
points_args$highlight_size <- points_args$highlight_size %||% highlight_size
170+
points_args$highlight_color <- points_args$highlight_color %||% highlight_color
171+
points_args$highlight_stroke <- points_args$highlight_stroke %||% highlight_stroke
172+
points_args$palette <- points_args$palette %||% palette
173+
points_args$palette_reverse <- points_args$palette_reverse %||% palette_reverse
174+
points_args$palcolor <- points_args$palcolor %||% palcolor
175+
points_args$legend.position <- legend.position
176+
points_args$legend.direction <- legend.direction
177+
points_args$flip_y <- flip_y
178+
points_args$return_layer <- TRUE
179+
player <- do.call(SpatialPointsPlot, points_args)
180+
scales_reused <- intersect(scales_used, attr(player, "scales"))
181+
if ("fill" %in% scales_reused) {
182+
players <- c(players, list(ggnewscale::new_scale_fill()))
183+
}
184+
players <- c(players, list(player))
185+
scales_used <- unique(c(scales_used, attr(player, "scales")))
186+
}
187+
if (element == "masks" && !is.null(masks)) {
188+
stop("[SpatialPlot] 'masks' is not supported for Seurat Visium V2 objects.")
189+
}
190+
if (element == "shapes" && !is.null(shapes)) {
191+
stop("[SpatialPlot] 'shapes' is not supported for Seurat Visium V2 objects.")
192+
}
193+
}
194+
195+
if (!is.null(ext_unscaled)) {
196+
ext <- ext %||% ext_unscaled * scale_factor
197+
}
198+
199+
p <- utils::getFromNamespace(".wrap_spatial_layers", "plotthis")(
200+
layers = players,
201+
ext = ext,
202+
flip_y = flip_y,
203+
legend.position = legend.position,
204+
legend.direction = legend.direction,
205+
title = title,
206+
subtitle = subtitle,
207+
xlab = xlab,
208+
ylab = ylab,
209+
theme = theme,
210+
theme_args = theme_args
211+
)
212+
213+
if (!is.null(facet_by)) {
214+
p <- utils::getFromNamespace("facet_plot", "plotthis")(
215+
p, facet_by, facet_scales, facet_nrow, facet_ncol, facet_byrow,
216+
legend.position = legend.position, legend.direction = legend.direction
217+
)
218+
}
219+
220+
p
221+
}
222+
223+
#' @keywords internal
224+
#' @rdname SpatialPlot
225+
SpatialPlot.Seurat.SlideSeq <- function(
226+
object, image = NULL, masks = NULL, shapes = NULL, points = NULL, ext = NULL,
227+
image_scale = NULL, crop = TRUE, group_by = NULL, features = NULL, layer = "data",
228+
layers = NULL, flip_y = TRUE, theme = "theme_box", theme_args = list(),
229+
label = FALSE, label_size = 4, label_fg = "white", label_bg = "black", label_bg_r = 0.1,
230+
label_repel = FALSE, label_repulsion = 20, label_pt_size = 1, label_pt_color = "black",
231+
label_segment_color = "black", label_insitu = FALSE,
232+
palette = NULL, palette_reverse = FALSE, palcolor = NULL,
233+
highlight = NULL, highlight_alpha = 1, highlight_size = 1, highlight_color = "black", highlight_stroke = 0.8,
234+
legend.position = "right", legend.direction = "vertical",
235+
title = NULL, subtitle = NULL, xlab = NULL, ylab = NULL,
236+
facet_scales = "fixed", facet_nrow = NULL, facet_ncol = NULL, facet_byrow = TRUE,
237+
...
238+
239+
) {
240+
stopifnot("[SpatialPlot] Either 'group_by' or 'features' should be provided, not both." = is.null(group_by) || is.null(features))
241+
if (theme %in% c("theme_box", "theme_this", "theme_blank")) {
242+
theme <- utils::getFromNamespace(theme, "plotthis")
243+
}
244+
245+
points <- points %||% TRUE
246+
247+
layers <- intersect(
248+
layers %||% c("image", "masks", "shapes", "points"),
249+
c(
250+
if (!is.null(image) && !isFALSE(image)) "image",
251+
if (!is.null(masks) && !isFALSE(masks)) "masks",
252+
if (!is.null(shapes) && !isFALSE(shapes)) "shapes",
253+
if (!is.null(points) && !isFALSE(points)) "points"
254+
)
255+
)
256+
stopifnot('Either "image", "masks", "shapes", or "points" must be provided.' = any(layers %in% c("image", "masks", "shapes", "points")))
257+
258+
players <- list()
259+
scales_used <- c()
260+
args <- rlang::dots_list(...)
261+
scale_factor <- 1
262+
facet_by <- NULL
263+
ext_unscaled <- NULL
264+
if (crop) {
265+
points_data <- Seurat::GetTissueCoordinates(object)
266+
points_data <- points_data[, colnames(points_data)[!is.na(colnames(points_data))], drop = FALSE]
267+
points_data$.y <- points_data$x
268+
points_data$x <- points_data$y
269+
points_data$y <- points_data$.y
270+
points_data$.y <- NULL
271+
padding <- 0.05
272+
delta_x <- diff(range(points_data$x, na.rm = TRUE)) * padding
273+
delta_y <- diff(range(points_data$y, na.rm = TRUE)) * padding
274+
ext_unscaled <- c(
275+
min(points_data$x, na.rm = TRUE) - delta_x,
276+
max(points_data$x, na.rm = TRUE) + delta_x,
277+
min(points_data$y, na.rm = TRUE) - delta_y,
278+
max(points_data$y, na.rm = TRUE) + delta_y
279+
)
280+
}
281+
for (element in layers) {
282+
if (element == "image" && !is.null(image)) {
283+
stop("[SpatialPlot] 'image' is not supported for Seurat SlideSeq objects.")
284+
}
285+
if (element == "points" && !is.null(points)) {
286+
points_args <- args[startsWith(names(args), "points_")]
287+
names(points_args) <- sub("^points_", "", names(points_args))
288+
if (crop) {
289+
# attach metadata for highlighting selection
290+
points_args$data <- object@meta.data[rownames(points_data), , drop = FALSE]
291+
points_args$data <- cbind(points_args$data, points_data)
292+
points_args$data$x <- points_args$data$x * scale_factor
293+
points_args$data$y <- points_args$data$y * scale_factor
294+
points_args$ext <- ext %||% (ext_unscaled * scale_factor)
295+
} else {
296+
points_args$data <- Seurat::GetTissueCoordinates(object, image = if(isFALSE(image)) NULL else image)
297+
points_args$data <- points_args$data[, colnames(points_args$data)[!is.na(colnames(points_args$data))], drop = FALSE]
298+
points_args$data$.y <- points_args$data$x * scale_factor
299+
points_args$data$x <- points_args$data$y * scale_factor
300+
points_args$data$y <- points_args$data$.y
301+
points_args$data$.y <- NULL
302+
points_args$data <- cbind(
303+
object@meta.data[rownames(points_args$data), , drop = FALSE],
304+
points_args$data
305+
)
306+
}
307+
if (!is.null(group_by)) {
308+
points_args$data[[group_by]] <- object@meta.data[[group_by]]
309+
points_args$color_by <- group_by
310+
points_args$label <- points_args$label %||% label
311+
points_args$label_size <- points_args$label_size %||% label_size
312+
points_args$label_fg <- points_args$label_fg %||% label_fg
313+
points_args$label_bg <- points_args$label_bg %||% label_bg
314+
points_args$label_bg_r <- points_args$label_bg_r %||% label_bg_r
315+
points_args$label_repel <- points_args$label_repel %||% label_repel
316+
points_args$label_repulsion <- points_args$label_repulsion %||% label_repulsion
317+
points_args$label_pt_size <- points_args$label_pt_size %||% label_pt_size
318+
points_args$label_pt_color <- points_args$label_pt_color %||% label_pt_color
319+
points_args$label_segment_color <- points_args$label_segment_color %||% label_segment_color
320+
points_args$label_insitu <- points_args$label_insitu %||% label_insitu
142321
} else if (!is.null(features)) {
143322
cells_by_image <- utils::getFromNamespace("CellsByImage", "Seurat")
144323
cells <- unique(cells_by_image(object, images = if(isFALSE(image)) NULL else image, unlist = TRUE))
@@ -161,14 +340,16 @@ SpatialPlot.Seurat.VisiumV2 <- function(
161340
facet_by <- ".facet_var"
162341
}
163342
}
164-
points_args$highlight <- highlight
165-
points_args$highlight_alpha <- highlight_alpha
166-
points_args$highlight_size <- highlight_size
167-
points_args$highlight_color <- highlight_color
168-
points_args$highlight_stroke <- highlight_stroke
343+
points_args$highlight <- points_args$highlight %||% highlight
344+
points_args$highlight_alpha <- points_args$highlight_alpha %||% highlight_alpha
345+
points_args$highlight_size <- points_args$highlight_size %||% highlight_size
346+
points_args$highlight_color <- points_args$highlight_color %||% highlight_color
347+
points_args$highlight_stroke <- points_args$highlight_stroke %||% highlight_stroke
169348
points_args$palette <- points_args$palette %||% palette
170349
points_args$palette_reverse <- points_args$palette_reverse %||% palette_reverse
171350
points_args$palcolor <- points_args$palcolor %||% palcolor
351+
points_args$legend.position <- legend.position
352+
points_args$legend.direction <- legend.direction
172353
points_args$flip_y <- flip_y
173354
points_args$return_layer <- TRUE
174355
player <- do.call(SpatialPointsPlot, points_args)
@@ -187,23 +368,26 @@ SpatialPlot.Seurat.VisiumV2 <- function(
187368
}
188369
}
189370

190-
xlim <- ylim <- NULL
191371
if (!is.null(ext_unscaled)) {
192372
ext <- ext %||% ext_unscaled * scale_factor
193373
}
194-
if (!is.null(ext)) {
195-
xlim <- c(ext[1], ext[2])
196-
ylim <- c(-ext[4], -ext[3])
197-
}
198374

199-
p <- ggplot() +
200-
players +
201-
ggplot2::coord_sf(expand = 0, xlim = xlim, ylim = ylim) +
202-
do.call(plotthis:::process_theme(theme), theme_args) +
203-
ggplot2::scale_y_continuous(labels = abs)
375+
p <- utils::getFromNamespace(".wrap_spatial_layers", "plotthis")(
376+
layers = players,
377+
ext = ext,
378+
flip_y = flip_y,
379+
legend.position = legend.position,
380+
legend.direction = legend.direction,
381+
title = title,
382+
subtitle = subtitle,
383+
xlab = xlab,
384+
ylab = ylab,
385+
theme = theme,
386+
theme_args = theme_args
387+
)
204388

205389
if (!is.null(facet_by)) {
206-
p <- plotthis:::facet_plot(
390+
p <- utils::getFromNamespace("facet_plot", "plotthis")(
207391
p, facet_by, facet_scales, facet_nrow, facet_ncol, facet_byrow,
208392
legend.position = legend.position, legend.direction = legend.direction
209393
)
@@ -212,7 +396,6 @@ SpatialPlot.Seurat.VisiumV2 <- function(
212396
p
213397
}
214398

215-
216399
#' Plot features for spatial data
217400
#'
218401
#' The features can include expression, dimension reduction components, metadata, etc

_pkgdown.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ navbar:
1212
menu:
1313
- text: "Visualizing 10x Visum data prepared with Seurat"
1414
href: Seurat.10x_Visium.html
15+
- text: "Visualizing SlideSeq data prepared with Seurat"
16+
href: Seurat.SlideSeq.html
1517
reference:
1618
- title: scRNA-seq
1719
desc: Functions for plotting single cell RNA-seq data

notebooks/spatial/Seurat.10x_Visium.ipynb

Lines changed: 43 additions & 53 deletions
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)