Skip to content

Commit

Permalink
change: update GiottoVisuals for GiottoClass 0.3.0
Browse files Browse the repository at this point in the history
- Moved image resampling functions to this package, added it into the `gg_annotation_raster()` workflow
- plotting y dim values is now vertical
- fixed unintended plotting of centroids in polygons
- deprecated `largeImage_name` param in favor of `image_name`
- swapped gene for feat naming scheme for the 3D plotting functions. Deprecated the originals.
- added `theme_param` for some plotting functions, allowing fine tuning of ggplot2 themes
- changed `polygon_alpha` default to be 1 and 0.5 when not plotting and plotting the image, respectively
  • Loading branch information
jiajic committed May 1, 2024
1 parent 31de8d5 commit 0ef8d2b
Show file tree
Hide file tree
Showing 27 changed files with 2,032 additions and 1,695 deletions.
5 changes: 3 additions & 2 deletions DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Package: GiottoVisuals
Title: Visuals for the Giotto spatial biology analysis ecosystem
Version: 0.1.7
Version: 0.2.0
Authors@R: c(
person("Ruben", "Dries", email = "rubendries@gmail.com",
role = c("aut", "cre"), comment = c(ORCID = "0000-0001-7650-7754")),
Expand Down Expand Up @@ -33,7 +33,7 @@ Imports:
data.table,
ggplot2 (>= 3.1.1),
GiottoUtils (>= 0.1.0.1),
GiottoClass (>= 0.1.0.1),
GiottoClass (>= 0.3.0),
ggrepel,
igraph (>= 1.2.4.1),
methods,
Expand Down Expand Up @@ -83,6 +83,7 @@ Collate:
'generics.R'
'gg_annotation_raster.R'
'gg_info_layers.R'
'gg_settings.R'
'globals.R'
'gstop.R'
'mixcolor.R'
Expand Down
3 changes: 3 additions & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export(create_cluster_dendrogram)
export(dimCellPlot)
export(dimCellPlot2D)
export(dimFeatPlot2D)
export(dimFeatPlot3D)
export(dimGenePlot3D)
export(dimPlot)
export(dimPlot2D)
Expand Down Expand Up @@ -67,12 +68,14 @@ export(spatDeconvPlot)
export(spatDimCellPlot)
export(spatDimCellPlot2D)
export(spatDimFeatPlot2D)
export(spatDimFeatPlot3D)
export(spatDimGenePlot3D)
export(spatDimPlot)
export(spatDimPlot2D)
export(spatDimPlot3D)
export(spatFeatPlot2D)
export(spatFeatPlot2D_single)
export(spatFeatPlot3D)
export(spatGenePlot3D)
export(spatInSituPlotDensity)
export(spatInSituPlotHex)
Expand Down
21 changes: 19 additions & 2 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,20 @@
# GiottoVisuals 0.2.0 (2024/05/01)

## breaking changes

## changes
- _GiottoClass >= 0.3.0_ is now required. (object no longer has `@largeImages` slot)
- `largeImage_name` arg is deprecated. `image_name` arg should be used instead

## bug fixes
- removed centroid point that unintentionally gets plotted in polygons

## enhancements
- `theme_param` arg has been added to some plotting functions that provides access to `ggplot2::theme()` finetuning
- `gg_annotation_raster()` now performs plot extent detection and resampling of images
- resampling args are now globally settable (see `?auto_image_resample`)


# GiottoVisuals 0.1.7 (2024/03/11)

## Changes
Expand All @@ -9,7 +26,7 @@

## bug fixes
- fix `spatPlot2D()` and `spatFeatPlot2D()` largeImage plotting when using `group_by`
- fix `edge_alpha` param in `spatFeatPlot2D()`
- fix `edge_alpha` arg in `spatFeatPlot2D()`

## new
- `mixRGB()` vectorized additive mixing in RGB space
Expand All @@ -19,7 +36,7 @@

# GiottoVisuals 0.1.4 (2024/01/25)
## bug fixes
- fix plotting color gradient when using param `point_shape = "no_border"`
- fix plotting color gradient when using arg `point_shape = "no_border"`
- fix image NA value [#865](https://github.com/drieslab/Giotto/issues/865) by rbutleriii

## new
Expand Down
2 changes: 1 addition & 1 deletion R/dd.R
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ NULL
#' @param show_polygon overlay polygon information (e.g. cell shape)
#' @param use_overlap use polygon and feature coordinates overlap results
#' @param polygon_feat_type feature type associated with polygon information
#' @param polygon_color color for polygon border
#' @param polygon_color color for polygon border. Set `NA` to remove border
#' @param polygon_bg_color color for polygon background
#' (overruled by polygon_fill)
#' @param polygon_fill character. what to color to fill polgyons by
Expand Down
213 changes: 212 additions & 1 deletion R/gg_annotation_raster.R
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@
#' @title Append image to ggplot as annotation_raster
#' @param ggobj ggplot2 `gg` object
#' @param gimage `giottoLargeImage`, `giottoImage` or `list` thereof
#' @param \dots additional params to pass
#' @details
#' No ... params are implemented for `giottoImage`. \cr ... params for
#' `giottoLargeImage` passes to automated resampling params see
#' `?auto_image_resample` for details
#' @return `gg` object with images to plot appended as annotation rasters
NULL

Expand Down Expand Up @@ -51,10 +56,22 @@ setMethod(
)

#' @rdname gg_annotation_raster
#' @param ext Object that responds to `ext()`. Defines the plot spatial ROI
#' that the image should be sampled for.
setMethod(
"gg_annotation_raster",
signature(ggobj = "gg", gimage = "giottoLargeImage"),
function(ggobj, gimage, ...) {
function(ggobj, gimage, ext = NULL, ...) {

# apply plot ext
if (!is.null(ext)) {
gimage <- .auto_resample_gimage(
img = gimage,
plot_ext = ext,
...
)
}

# get plotting minmax
extent <- terra::ext(gimage@raster_object)[seq_len(4)]
xmin <- extent[["xmin"]]
Expand Down Expand Up @@ -110,3 +127,197 @@ setMethod(
return(ggobj)
}
)






# Internals ####

# returns the spatial extent needed for the plot
# ... passes to ext() `giotto` method
.guess_plot_extent <- function(
gobject, spat_unit = NULL, spat_loc_name = NULL, ext = NULL, ...
) {

if (!is.null(ext)) ext <- ext(ext) # normalize to `SpatExtent` class
# if ext already given, directly return
if (inherits(ext, "SpatExtent")) return(ext)

# find extent from one of poly, spatlocs, points, in that order of pref
e <- ext(
gobject,
spat_unit = spat_unit,
verbose = FALSE,
name = spat_loc_name,
# `name` only passes to `getSpatialLocations()` if spatlocs are
# present and used to find extent
... # You can ensure they are used by setting prefer = "spatlocs
)

if (is.null(e)) {
stop(wrap_txt(
"No `ext` provided and no spatial locations or polygons discovered.
Cannot determine largeImage resample extent"
))
}
return(e)
}


#' @name auto_image_resample
#' @title Optimized image resampling
#' @description
#' Downsample terra-based images for plotting. Uses
#' \code{\link[terra]{spatSample}} to load onlya portion of the original image,
#' speeding up plotting and lowering memory footprint.
#'
#' Default behavior of `spatSample` is to crop if only a smaller ROI is
#' needed for plotting followed by the sampling process in order to reduce
#' wasted sampling by focusing the sample space. For very large ROIs, this
#' crop can be time intensive and require writing to disk.
#'
#' This function examines the ROI dimensions as defined through the limits of
#' the spatial locations to be plotted, and decides between the following two
#' methods in order to avoid this issue:
#' \itemize{
#' \item{\strong{Method A.} First crop original image, then sample
#' `max_sample` (default = 5e5) values to generate final image. Intended
#' for smaller ROIs. Force usage of this method by setting
#' `flex_resample = FALSE`}
#' \item{\strong{Method B.} First oversample, then crop. Intended for larger
#' ROIs. Base sample size is `max_sample`, which is then multiplied by a
#' scale factor >1 that increases the smaller the ROI is and is defined by:
#' original dimensions/crop dimensions where the larger ratio between x
#' and y dims is chosen. Scale factor is capped by
#' \code{max_resample_scale}}
#' }
#' Control points for this function are set by \code{max_crop} which decides
#' the max ROI area after which switchover to method B happens in order to
#' avoid laborious crops and \code{max_resample_scale} which determines the
#' maximum scale factor for number of values to sample. Both values can be
#' adjusted depending on system resources. Additionally, \code{flex_resample}
#' determines if this switching behavior happens.
#' When set to \code{FALSE}, only method A is used.
#' @param img giotto image to plot
#' @param plot_ext extent of plot (required)
#' @param img_border if not 0 or FALSE, expand plot_ext by this percentage on
#' each side before applying crop on image. See details
#' @param flex_resample logical. Default = TRUE. Forces usage of method A when
#' FALSE.
#' @param max_sample numeric. Default = 5e5. Maximum n values to sample from the
#' image. If larger than `max_crop`, will override `max_crop.`
#' Globally settable with the option "giotto.plot_img_max_sample"
#' @param max_crop numeric. Default = 1e8. Maximum crop size (px area) allowed
#' for \strong{method A} before switching to \strong{method B}
#' (see description).
#' Globally settable with option "giotto.plot_img_max_crop"
#' @param max_resample_scale numeric. Default = 100. Maximum scalefactor allowed
#' to be applied on `max_sample` in order to oversample when compensating
#' for decreased resolution when cropping after sampling. Globally settable with
#' option "giotto.plot_img_max_resample_scale".
#' @details
#' **img_border**
#' expand ext to use for plotting the image. This makes it so that the image
#' is not cut off sharply at the edge of the plot extent. Needed since plots
#' often define extent by centroids, and polygons may hang over the edge of the
#' extent.
#' @returns a giotto image cropped and resampled properly for plotting
#' @examples
#' \dontrun{
#' img <- GiottoData::loadSubObjectMini("giottoLargeImage")
#' .auto_resample_gimage(img)
#' }
#' @seealso \code{\link[terra]{spatSample}}
#' @keywords internal
.auto_resample_gimage <- function(
img,
plot_ext,
img_border = 0.125,
flex_resample = TRUE,
max_sample = getOption("giotto.plot_img_max_sample", 5e5),
max_crop = getOption("giotto.plot_img_max_crop", 1e8),
max_resample_scale = getOption(
"giotto.plot_img_max_resample_scale", 100
)
) {

img_ext <- terra::ext(img)
bound_poly <- as.polygons(img_ext)
crop_ext <- img_ext # default

# override max_crop if needed
if (max_sample > max_crop) max_crop <- max_sample

# apply img border
# - cropping with extent larger than the image extent works
if (img_border > 0) {

crop_ext <- bound_poly %>%
rescale(1 + img_border) %>%
ext()

# determine final crop (normalizes extent when larger than available)
crop_ext <- ext(crop(bound_poly, crop_ext))
}

# determine ratio of crop vs original
original_dims <- dim(img)[c(2L, 1L)] # x, y ordering
ratios <- range(crop_ext) / range(img_ext) # x, y ordering
crop_dims <- original_dims * ratios
crop_area_px <- prod(crop_dims)

if (!isTRUE(flex_resample) || crop_area_px <= max_crop) {
# [METHOD A]:
# 1. Crop if needed
# 2. resample to final image
if (!isTRUE(flex_resample) && crop_area_px > max_crop) {
warning("Plotting large regions with flex_resample == FALSE will
increase time and may require scratch space.")
}

vmsg(.is_debug = TRUE,
sprintf("img auto_res: [A] | area: %d | max: %d",
crop_area_px, max_crop))

crop_img <- terra::crop(
x = img@raster_object,
y = crop_ext
)
img@raster_object <- terra::spatSample(
crop_img,
size = max_sample,
method = "regular",
as.raster = TRUE
)
} else {
# [METHOD B]:
# 1. Oversample
# 2. crop to final image
# Sample n values where max_sample is scaled by a value >1
# Scale factor is fullsize image dim/crop dim. Larger of the two
# ratios is chosen
scalef <- max(1/ratios)
# This scaling is ALSO capped by max_resample_scale
if (scalef > max_resample_scale) scalef <- max_resample_scale

vmsg(.is_debug = TRUE,
sprintf("img auto_res: [B] | scalef: %d | max_scale: %d",
scalef, max_resample_scale))

oversample_img <- terra::spatSample(
img@raster_object,
size = round(max_sample * scalef),
method = "regular",
as.raster = TRUE
)
img@raster_object <- terra::crop(
x = oversample_img,
y = crop_ext
)
}
return(img)
}


Loading

0 comments on commit 0ef8d2b

Please sign in to comment.