Skip to content

Commit

Permalink
chore: revert higher base reqs
Browse files Browse the repository at this point in the history
  • Loading branch information
jiajic committed Jul 12, 2024
2 parents 13e9fcb + f14c63c commit 02b5f2a
Show file tree
Hide file tree
Showing 13 changed files with 294 additions and 154 deletions.
4 changes: 4 additions & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ export(dimPlot)
export(dimPlot2D)
export(dimPlot3D)
export(expand_feature_info)
export(geom_label_repel)
export(geom_text_repel)
export(getColors)
export(getDistinctColors)
export(getRainbowColors)
Expand Down Expand Up @@ -107,6 +109,8 @@ importFrom(GiottoUtils,getRainbowColors)
importFrom(colorRamp2,colorRamp2)
importFrom(data.table,dcast)
importFrom(data.table,dcast.data.table)
importFrom(ggrepel,geom_label_repel)
importFrom(ggrepel,geom_text_repel)
importFrom(igraph,as_data_frame)
importFrom(methods,new)
importFrom(methods,setGeneric)
Expand Down
6 changes: 6 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@

# GiottoVisuals 0.2.4

## enhancements
- `giottoLargeImage` `max_window` and `colors` slot info is now followed during ggplot plotting
- `giottoAffineImage` compatibility for giotto ggplot2 plotting functions

## new
- `geom_text_repel()` and `geom_label_repel()` from `ggplot2` are now re-exported

# GiottoVisuals 0.2.3 (2024/05/28)

Expand Down
269 changes: 179 additions & 90 deletions R/gg_annotation_raster.R
Original file line number Diff line number Diff line change
Expand Up @@ -62,77 +62,46 @@ setMethod(
"gg_annotation_raster",
signature(ggobj = "gg", gimage = "giottoLargeImage"),
function(ggobj, gimage, ext = NULL, ...) {
# resample from extent
if (is.null(ext)) ext <- ext(gimage)
gimage <- .auto_resample_gimage(
img = gimage,
plot_ext = ext,
crop_ratio_fun = .img_to_crop_ratio_gimage,
sample_fun = .sample_gimage,
...
)

# 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"]]
xmax <- extent[["xmax"]]
ymin <- extent[["ymin"]]
ymax <- extent[["ymax"]]

# convert raster object into array with 3 channels
img_array <- terra::as.array(gimage@raster_object)

# TODO: check if required, fixes NaN values
# replacing NA's by zero or another value directly in raster object?
# raster[is.na(raster[])] <- 0
if (is.nan(max(img_array[, , 1]))) {
img_array[, , 1][is.nan(img_array[, , 1])] <- max(img_array[, , 1],
na.rm = TRUE
)
}

if (dim(img_array)[3] > 1) {
if (is.nan(max(img_array[, , 2]))) {
img_array[, , 2][is.nan(img_array[, , 2])] <-
max(img_array[, , 2], na.rm = TRUE)
}
}

if (dim(img_array)[3] > 2) {
if (is.nan(max(img_array[, , 3]))) {
img_array[, , 3][is.nan(img_array[, , 3])] <-
max(img_array[, , 3], na.rm = TRUE)
}
}

img_array <- img_array / max(img_array, na.rm = TRUE)
if (dim(img_array)[3] == 1) {
img_array_RGB <- array(NA, dim = c(dim(img_array)[seq_len(2)], 3))
img_array_RGB[, , seq_len(3)] <- img_array
} else {
img_array_RGB <- img_array
}
ggobj <- .gg_append_image(ggobj = ggobj, gimage = gimage)

# handle NA values
img_array_RGB[is.na(img_array_RGB)] <- 0
return(ggobj)
}
)

# append to ggobj
ggobj <- ggobj + annotation_raster(
img_array_RGB,
xmin = xmin, xmax = xmax,
ymin = ymin, ymax = ymax
#' @rdname gg_annotation_raster
setMethod(
"gg_annotation_raster",
signature(ggobj = "gg", gimage = "giottoAffineImage"),
function(ggobj, gimage, ext, ...) {
# resample from extent
if (is.null(ext)) ext <- ext(gimage)
gimage <- .auto_resample_gimage(
img = gimage,
plot_ext = ext,
crop_ratio_fun = .img_to_crop_ratio_gaffimage,
sample_fun = .sample_gaffimage,
...
)

# TODO geom_raster to accommodate single-channel
ggobj <- .gg_append_image(ggobj = ggobj, gimage = gimage)

return(ggobj)
}
)






# Internals ####

# returns the spatial extent needed for the plot
Expand Down Expand Up @@ -202,7 +171,7 @@ setMethod(
#' 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 plot_ext extent of plot (defaults to the image extent)
#' @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
Expand Down Expand Up @@ -236,6 +205,8 @@ setMethod(
img,
plot_ext = NULL,
img_border = 0.125,
crop_ratio_fun = .img_to_crop_ratio_gimage,
sample_fun = .sample_gimage,
flex_resample = TRUE,
max_sample = getOption("giotto.plot_img_max_sample", 5e5),
max_crop = getOption("giotto.plot_img_max_crop", 1e8),
Expand All @@ -244,16 +215,16 @@ setMethod(
)
) {

img_ext <- terra::ext(img)
if (is.null(plot_ext)) crop_ext <- img_ext # default
# 1. determine source image and cropping extents
if (is.null(plot_ext)) crop_ext <- ext(img) # default to img extent
else crop_ext <- ext(plot_ext)
bound_poly <- as.polygons(crop_ext)

# override max_crop if needed
# 1.1. 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
# 1.2. apply img border expansion
# - note: cropping with extent larger than the image extent is supported
if (img_border > 0) {

crop_ext <- bound_poly %>%
Expand All @@ -264,35 +235,30 @@ setMethod(
crop_ext <- ext(crop(bound_poly, crop_ext))
}

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

# 3. perform flexible resample/crop based on cropping area
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.")
warning(
"Plotting large regions with flex_resample == FALSE will\n ",
"increase time and may require scratch space."
)
}

vmsg(.is_debug = TRUE,
sprintf("img auto_res: [A] | area: %f | max: %f",
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
)
crop_img <- terra::crop(img, crop_ext)
res <- sample_fun(crop_img, size = max_sample)
} else {
# [METHOD B]:
# 1. Oversample
Expand All @@ -308,18 +274,141 @@ setMethod(
sprintf("img auto_res: [B] | scalef: %f | max_scale: %f",
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
)
oversample_img <- sample_fun(img, size = round(max_sample * scalef))
res <- terra::crop(oversample_img, crop_ext)
}
return(res)
}




# determine ratio of crop vs full image extent
.img_to_crop_ratio_gimage <- function(img, crop_ext) {
img_ext <- ext(img)
ratio <- range(crop_ext) / range(img_ext)
# crops larger than the image are possible, but meaningless for this
# calculate. so the ratios are capped at 1.
ratio[ratio > 1] <- 1
return(ratio)
}

.img_to_crop_ratio_gaffimage <- function(img, crop_ext) {
# Do not use the ext() method for giottoAffineImage
# Instead use the mapping applied to the underlying SpatRaster.
# For giottoAffineImage, these two values are usually different.
img_ext <- ext(img@raster_object)
# find the extent needed in the source (untransformed) image
crop_bound <- terra::as.polygons(crop_ext)
crop_bound$id <- "bound" # affine() requires ID values
crop_ext <- ext(affine(crop_bound, img@affine, inv = TRUE))
ratio <- range(crop_ext) / range(img_ext)
# crops larger than the image are possible, but meaningless for this
# calculate. so the ratios are capped at 1.
ratio[ratio > 1] <- 1
return(ratio)
}




# pull sampled values from original image into target spatial mapping
# should return a giottoLargeImage
.sample_gimage <- function(x, size) {
x@raster_object <- terra::spatSample(
x = x@raster_object,
size = size,
method = "regular",
as.raster = TRUE
)
return(x)
}

.sample_gaffimage <- function(x, size) {
res <- x@funs$realize_magick(size = size)
return(res)
}




# make an image array compatible with ggplot::annotation_raster()
# maxval is the cutoff after which everything is max intensity
# returns: raster
.gg_imgarray_2_raster <- function(x, maxval = NULL, col = NULL) {
nlyr <- dim(x)[3L] # number of channels/layers
if (is.na(nlyr)) nlyr <- 1L
# NOTE: 4 layers allowed (rgba), but may conflict with actual 4 info
# layer cases which SHOULD be converted to 3 layer
#
# more than 4 layers -> directly ignore layers past the 3rd
if (nlyr > 4L) {
nlyr <- 3L
x <- x[, , seq_len(3)]
}

# handle NaN values -- set as max value of that layer
# these may arise due to save artefacting when values are larger than
# expected
for (lyr in seq_len(nlyr)) {
if (is.nan(max(x[, , lyr]))) {
x[, , lyr][is.nan(x[, , lyr])] <-
max(x[, , lyr], na.rm = TRUE)
}
}
return(img)

# handle NA values -- set as 0
x[is.na(x)] <- 0

if (nlyr == 1L) {
# SINGLE CHANNEL #
# max window cutoff
if (!is.null(maxval)) x[x > maxval] <- maxval
# colorize
if (is.null(col)) {
col <- getMonochromeColors("white", n = 256)
}
r <- .colorize_single_channel_raster(x, col = col)
} else {
# RGB EXPECTED #
# convert to range 0:1 (needed for as.raster())
x <- scales::rescale(x, to = c(0, 1))
r <- as.raster(x)
}

return(r)
}




# `x` is array to use
# `col` is character vector of colors to use
.colorize_single_channel_raster <- function(x, col) {
if (!is.na(dim(x)[3L]))x <- x[,, 1L] # convert to matrix
r <- range(x, na.rm = TRUE)
x <- (x - r[1])/(r[2] - r[1])
x <- round(x * (length(col) - 1) + 1)
x[] <- col[x]
as.raster(x)
}

# append a giotto image object containing a SpatRaster that has already been
# resampled/pulled into memory. Output is a `gg` object
.gg_append_image <- function(ggobj, gimage) {
# convert gimage to a raster
r <- terra::as.array(gimage@raster_object) %>%
.gg_imgarray_2_raster(
maxval = gimage@max_window,
col = gimage@colors
)

# append to ggobj
extent <- ext(gimage)[seq_len(4L)]
ggobj <- ggobj + annotation_raster(r,
xmin = extent[["xmin"]], xmax = extent[["xmax"]],
ymin = extent[["ymin"]], ymax = extent[["ymax"]]
)

return(ggobj)
}
Loading

0 comments on commit 02b5f2a

Please sign in to comment.