From d7239c63a20df234ef7b84f296cd764851479489 Mon Sep 17 00:00:00 2001 From: jiajic <72078254+jiajic@users.noreply.github.com> Date: Thu, 29 Feb 2024 02:05:00 -0500 Subject: [PATCH 01/12] chore: update GHA for changes --- .github/workflows/covr.yml | 14 +++++++++++++- .github/workflows/dev_check.yml | 19 ++++++++++++++++++- .github/workflows/main_check.yml | 19 ++++++++++++++++++- 3 files changed, 49 insertions(+), 3 deletions(-) diff --git a/.github/workflows/covr.yml b/.github/workflows/covr.yml index db981c9f..4187bce9 100644 --- a/.github/workflows/covr.yml +++ b/.github/workflows/covr.yml @@ -42,7 +42,19 @@ jobs: _R_CHECK_RD_XREFS: false with: dependencies: '"hard"' # do not use suggested dependencies - extra-packages: any::rcmdcheck, any::testthat, any::rlang, any::R.utils, any::sp, any::stars, any::raster, any::sf, any::RTriangle, any::geometry, any::covr + install-pandoc: false + extra-packages: | + any::rcmdcheck + any::testthat + any::rlang + any::R.utils + any::sp + any::stars + any::raster + any::sf + any::RTriangle + any::geometry + any::covr needs: coverage - name: Generate coverage report diff --git a/.github/workflows/dev_check.yml b/.github/workflows/dev_check.yml index 00f49405..af4b57f5 100644 --- a/.github/workflows/dev_check.yml +++ b/.github/workflows/dev_check.yml @@ -42,7 +42,24 @@ jobs: _R_CHECK_RD_XREFS: false with: dependencies: '"hard"' # do not use suggested dependencies - extra-packages: any::rcmdcheck, any::testthat, any::rlang, any::R.utils, any::knitr, any::rmarkdown, any::qs, any::sp, any::stars, any::raster, any::sf, any::scattermore, any::exactextractr, any::RTriangle, any::geometry, github::drieslab/GiottoData + install-pandoc: false + extra-packages: | + any::rcmdcheck + any::testthat + any::rlang + any::R.utils + any::knitr + any::rmarkdown + any::qs + any::sp + any::stars + any::raster + any::sf + any::scattermore + any::exactextractr + any::RTriangle + any::geometry + github::drieslab/GiottoData - name: Run R CMD check uses: r-lib/actions/check-r-package@v2 diff --git a/.github/workflows/main_check.yml b/.github/workflows/main_check.yml index 1e360a70..00a8d733 100644 --- a/.github/workflows/main_check.yml +++ b/.github/workflows/main_check.yml @@ -58,7 +58,24 @@ jobs: _R_CHECK_RD_XREFS: false with: dependencies: '"hard"' # do not use suggested dependencies - extra-packages: any::rcmdcheck, any::testthat, any::rlang, any::R.utils, any::remotes, any::knitr, any::rmarkdown, any::sp, any::stars, any::raster, any::sf, any::scattermore, any::exactextractr, any::RTriangle, any::geometry, github::drieslab/GiottoData + install-pandoc: false + extra-packages: | + any::rcmdcheck + any::testthat + any::rlang + any::R.utils + any::remotes + any::knitr + any::rmarkdown + any::sp + any::stars + any::raster + any::sf + any::scattermore + any::exactextractr + any::RTriangle + any::geometry + github::drieslab/GiottoData - name: Test python env build run: | From 09fc5327367cff4810958d6bd11b23a2a0a845a9 Mon Sep 17 00:00:00 2001 From: jiajic <72078254+jiajic@users.noreply.github.com> Date: Thu, 29 Feb 2024 13:54:26 -0500 Subject: [PATCH 02/12] update `createGiottoPolygonsFromMask()` ## bug fixes - fix `createGiottoPolygonsFromMask()` IDs being applied out of sync to mask values - remove unused `fix_multipart` param in `createGiottoPolygonsFromMask()` ## enhancements - `createGiottoPolygonsFromMask()` now has `ID_fmt` param for finer control of automatic poly_ID generation --- DESCRIPTION | 2 +- NEWS.md | 10 +++ R/create.R | 148 +++++++++++++++++++++++++------------ R/data_input.R | 4 +- man/createGiottoPolygon.Rd | 42 +++++++++-- 5 files changed, 149 insertions(+), 57 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index c8321e2f..5454b683 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,6 +1,6 @@ Package: GiottoClass Title: Giotto Suite object definitions and framework -Version: 0.2.1 +Version: 0.2.2 Authors@R: c( person("Ruben", "Dries", email = "rubendries@gmail.com", role = c("aut", "cre")), diff --git a/NEWS.md b/NEWS.md index 0245cbd4..053c9ea4 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,15 @@ +# GiottoClass 0.2.2 + +## bug fixes +- fix `createGiottoPolygonsFromMask()` IDs being applied out of sync to mask values +- remove unused `fix_multipart` param in `createGiottoPolygonsFromMask()` + +## enhancements +- `createGiottoPolygonsFromMask()` now has `ID_fmt` param for finer control of automatic poly_ID generation + + # GiottoClass 0.2.1 (2024/02/28) ## breaking changes diff --git a/R/create.R b/R/create.R index 817d79ed..3dbdb1aa 100644 --- a/R/create.R +++ b/R/create.R @@ -672,13 +672,13 @@ createGiottoObjectSubcellular <- function( remove_background_polygon = TRUE, background_algo = c("range"), fill_holes = TRUE, + ID_fmt = "cell_", poly_IDs = NULL, flip_vertical = TRUE, shift_vertical_step = TRUE, flip_horizontal = TRUE, shift_horizontal_step = TRUE, - calc_centroids = FALSE, - fix_multipart = TRUE + calc_centroids = FALSE ) } @@ -1961,11 +1961,12 @@ setMethod( #' @param background_algo algorithm to remove background polygon #' @param fill_holes fill holes within created polygons #' @param poly_IDs unique names for each polygon in the mask file +#' @param ID_fmt character. Only applied if `poly_IDs = NULL`. Naming scheme for +#' poly_IDs. Default = "cell_". See details. #' @param flip_vertical flip mask figure in a vertical manner #' @param shift_vertical_step shift vertical (boolean or numerical) #' @param flip_horizontal flip mask figure in a horizontal manner #' @param shift_horizontal_step shift horizontal (boolean or numerical) -#' @param fix_multipart try to split polygons with multiple parts (default: TRUE) #' @param remove_unvalid_polygons remove unvalid polygons (default: TRUE) #' @export setMethod( @@ -1977,11 +1978,11 @@ setMethod( background_algo = c("range"), fill_holes = TRUE, poly_IDs = NULL, + ID_fmt = "cell_", flip_vertical = TRUE, shift_vertical_step = TRUE, flip_horizontal = TRUE, shift_horizontal_step = TRUE, - fix_multipart = TRUE, remove_unvalid_polygons = TRUE, calc_centroids = FALSE, verbose = TRUE) { @@ -1995,11 +1996,11 @@ setMethod( background_algo = background_algo, fill_holes = fill_holes, poly_IDs = poly_IDs, + ID_fmt = ID_fmt, flip_vertical = flip_vertical, shift_vertical_step = shift_vertical_step, flip_horizontal = flip_horizontal, shift_horizontal_step = shift_horizontal_step, - fix_multipart = fix_multipart, remove_unvalid_polygons = remove_unvalid_polygons, calc_centroids = calc_centroids ) @@ -2063,39 +2064,70 @@ setMethod( #' @title Create giotto polygons from mask file #' @rdname createGiottoPolygon #' @param maskfile path to mask file -#' @param mask_method how the mask file defines individual segmentation annotations -#' @param name name for polygons +#' @param mask_method how the mask file defines individual segmentation annotations. +#' see details. +#' @param name character. Name to assign created `giottoPolygon` #' @param remove_background_polygon try to remove background polygon (default: FALSE) #' @param background_algo algorithm to remove background polygon #' @param fill_holes fill holes within created polygons -#' @param poly_IDs unique names for each polygon in the mask file +#' @param poly_IDs character vector. Default = NULL. Custom unique names for +#' each polygon in the mask file. +#' @param ID_fmt character. Only applied if `poly_IDs = NULL`. Naming scheme for +#' poly_IDs. Default = "cell_". See details. #' @param flip_vertical flip mask figure in a vertical manner #' @param shift_vertical_step shift vertical (boolean or numerical) #' @param flip_horizontal flip mask figure in a horizontal manner #' @param shift_horizontal_step shift horizontal (boolean or numerical) #' @param calc_centroids calculate centroids for polygons -#' @param fix_multipart try to split polygons with multiple parts (default: TRUE) #' @param remove_unvalid_polygons remove unvalid polygons (default: TRUE) -#' @return a giotto polygon object +#' @param verbose verbosity #' @concept mask polygon +#' @details +#' *mask_method* +#' One of "single", "multiple", or "guess". +#' - "single" assumes that the provided mask image is binary, with only polygon +#' vs background being distinct values. With this kind of image, the expected +#' generated polygons is a single multipart polygon. "single" takes this +#' multipart polygon and breaks it apart into individual singlepart polygons. +#' An initial simple `numeric` index as the 'nth' polygon found in the mask image +#' will be applied as an ID (see *ID_fmt* section). +#' - "multiple" assumes that the provided mask image has distinct intensity +#' values to specify the IDs of individual polygons. An initial `numeric` ID is +#' applied as the intensity value of the pixels that made up the annotation for +#' that polygon in the mask image (see *ID_fmt* section). +#' - "guess" examines the values in the image to pick the most likely appropriate +#' method out of "single" or "multiple". +#' *ID_fmt* +#' Defaults to applying the input as a prefix (using `paste0()`) to the numerical +#' ID values detected by `mask_method`. (ie `ID_fmt = "cell_"` produces +#' `cell_1`, `cell_2`, `cell_3`, ...) +#' If a "%" character is detected in the input then the input will be treated as +#' a `sprintf()` `fmt` param input instead. (ie `ID_fmt = "cell_%03d"` produces +#' `cell_001`, `cell_002`, `cell_003`, ...) +#' @return a giotto polygon object #' @export -createGiottoPolygonsFromMask <- function(maskfile, - mask_method = c("guess", "single", "multiple"), - name = "cell", - remove_background_polygon = FALSE, - background_algo = c("range"), - fill_holes = TRUE, - poly_IDs = NULL, - flip_vertical = TRUE, - shift_vertical_step = TRUE, - flip_horizontal = TRUE, - shift_horizontal_step = TRUE, - calc_centroids = FALSE, - fix_multipart = TRUE, - remove_unvalid_polygons = TRUE) { +createGiottoPolygonsFromMask <- function( + maskfile, + mask_method = c("guess", "single", "multiple"), + name = "cell", + remove_background_polygon = FALSE, + background_algo = c("range"), + fill_holes = TRUE, + poly_IDs = NULL, + ID_fmt = "cell_", + flip_vertical = TRUE, + shift_vertical_step = TRUE, + flip_horizontal = TRUE, + shift_horizontal_step = TRUE, + calc_centroids = FALSE, + remove_unvalid_polygons = TRUE, + verbose = FALSE +) { # data.table vars x <- y <- geom <- part <- NULL + remove_unvalid_polygons <- as.logical(remove_unvalid_polygons) + # select background algo background_algo <- match.arg(background_algo, choices = "range") @@ -2120,6 +2152,9 @@ createGiottoPolygonsFromMask <- function(maskfile, # create polygons from mask rast_dimensions <- dim(terra_rast) + # value = TRUE here means that the intensity value of the mask image + # (which usually encodes the intended polygon ID) is added to the resulting + # SpatVector as the only attribute. terra_polygon <- terra::as.polygons(x = terra_rast, value = TRUE) # fill holes @@ -2127,8 +2162,10 @@ createGiottoPolygonsFromMask <- function(maskfile, terra_polygon <- terra::fillHoles(terra_polygon) } - # remove unvalid polygons - if (isTRUE(remove_unvalid_polygons)) { + # handle unvalid polygons ## + # The unvalid polys formed from as.polygons are usually very misshapen and + # artefacted. It is impossible to fix them using `terra::makeValid()` + if (remove_unvalid_polygons) { valid_index <- terra::is.valid(terra_polygon) terra_polygon <- terra_polygon[valid_index] } @@ -2147,30 +2184,41 @@ createGiottoPolygonsFromMask <- function(maskfile, spatVecDT[, x := -x] } - # guess mask method + ## guess mask method ## if (mask_method == "guess") { uniq_geoms <- length(unique(spatVecDT$geom)) uniq_parts <- length(unique(spatVecDT$part)) mask_method <- ifelse(uniq_geoms > uniq_parts, "multiple", "single") } + vmsg(.v = verbose, sprintf("parsing mask using mask_method: %s", mask_method)) - if (mask_method == "multiple") { - if (is.null(poly_IDs)) { - spatVecDT[, geom := paste0(name, geom)] - } - g_polygon <- createGiottoPolygonsFromDfr(segmdfr = spatVecDT[, .(x, y, geom)]) - terra_polygon <- g_polygon@spatVector - } else if (mask_method == "single") { - if (is.null(poly_IDs)) { - spatVecDT[, part := paste0(name, part)] + + ## define polys and apply auto IDs ## + naming_fun <- ifelse(grepl("%", ID_fmt), sprintf, paste0) + # If poly_IDs are NOT provided, then terra_polygon IDs created here will be + # `character` and the finalized ID values. + # If not, the IDs are still temporary and `numeric`, pending the `poly_IDs` + # param being applied downstream. + terra_polygon <- switch(mask_method, + "multiple" = { + if (is.null(poly_IDs)) { + spatVecDT[, geom := naming_fun(ID_fmt, geom)] + } + g_polygon <- createGiottoPolygonsFromDfr(segmdfr = spatVecDT[, .(x, y, geom)]) + g_polygon@spatVector + }, + "single" = { + if (is.null(poly_IDs)) { + spatVecDT[, part := naming_fun(ID_fmt, part)] + } + g_polygon <- createGiottoPolygonsFromDfr(segmdfr = spatVecDT[, .(x, y, part)]) + g_polygon@spatVector } - g_polygon <- createGiottoPolygonsFromDfr(segmdfr = spatVecDT[, .(x, y, part)]) - terra_polygon <- g_polygon@spatVector - } + ) - ## shift values ## + ## apply spatial shifts ## if (identical(shift_vertical_step, TRUE)) { shift_vertical_step <- rast_dimensions[1] # nrows of raster } else if (is.numeric(shift_vertical_step)) { @@ -2193,29 +2241,35 @@ createGiottoPolygonsFromMask <- function(maskfile, ) - # remove background polygon + ## remove background polygon ## if (isTRUE(remove_background_polygon)) { if (background_algo == "range") { backgr_poly_id <- .identify_background_range_polygons(terra_polygon) - # print(backgr_poly_id) uneccessary to print? + vmsg(.v = verbose, sprintf("removed background poly.\n ID was: %s", + backgr_poly_id)) } - terra_polygon <- terra::subset(x = terra_polygon, terra_polygon[["poly_ID"]] != backgr_poly_id) + terra_polygon <- terra::subset( + x = terra_polygon, + terra_polygon[["poly_ID"]] != backgr_poly_id + ) } - # provide own cell_ID name + ## apply custom poly_IDs ## if (!is.null(poly_IDs)) { + # first sort the polys by ID to ensure that custom poly_IDs are applied in a + # meaningful manner + terra_polygon <- terra_polygon[order(terra_polygon$poly_ID)] + if (isTRUE(remove_unvalid_polygons)) { poly_IDs <- poly_IDs[valid_index] } if (length(poly_IDs) != nrow(terra::values(terra_polygon))) { - stop("length cell_IDs does not equal number of found polyongs \n") + stop("length cell_IDs does not equal number of found polygons \n") } terra_polygon$poly_ID <- as.character(poly_IDs) - } else { - terra_polygon$poly_ID <- paste0(name, "_", 1:nrow(terra::values(terra_polygon))) } diff --git a/R/data_input.R b/R/data_input.R index 33f6d6a5..f33fe799 100644 --- a/R/data_input.R +++ b/R/data_input.R @@ -1710,11 +1710,11 @@ readPolygonData <- function(data_list, background_algo = c("range"), fill_holes = TRUE, poly_IDs = NULL, + ID_fmt = "cell_", flip_vertical = TRUE, shift_vertical_step = TRUE, flip_horizontal = TRUE, - shift_horizontal_step = TRUE, - fix_multipart = TRUE + shift_horizontal_step = TRUE ) } diff --git a/man/createGiottoPolygon.Rd b/man/createGiottoPolygon.Rd index b9683299..99bc87e5 100644 --- a/man/createGiottoPolygon.Rd +++ b/man/createGiottoPolygon.Rd @@ -21,11 +21,11 @@ background_algo = c("range"), fill_holes = TRUE, poly_IDs = NULL, + ID_fmt = "cell_", flip_vertical = TRUE, shift_vertical_step = TRUE, flip_horizontal = TRUE, shift_horizontal_step = TRUE, - fix_multipart = TRUE, remove_unvalid_polygons = TRUE, calc_centroids = FALSE, verbose = TRUE @@ -50,13 +50,14 @@ createGiottoPolygonsFromMask( background_algo = c("range"), fill_holes = TRUE, poly_IDs = NULL, + ID_fmt = "cell_", flip_vertical = TRUE, shift_vertical_step = TRUE, flip_horizontal = TRUE, shift_horizontal_step = TRUE, calc_centroids = FALSE, - fix_multipart = TRUE, - remove_unvalid_polygons = TRUE + remove_unvalid_polygons = TRUE, + verbose = FALSE ) createGiottoPolygonsFromDfr( @@ -85,7 +86,8 @@ data.frame with vertex 'x', 'y', and 'poly_ID' information.} \item{verbose}{be verbose} -\item{mask_method}{how the mask file defines individual segmentation annotations} +\item{mask_method}{how the mask file defines individual segmentation annotations. +see details.} \item{remove_background_polygon}{try to remove background polygon (default: FALSE)} @@ -93,7 +95,11 @@ data.frame with vertex 'x', 'y', and 'poly_ID' information.} \item{fill_holes}{fill holes within created polygons} -\item{poly_IDs}{unique names for each polygon in the mask file} +\item{poly_IDs}{character vector. Default = NULL. Custom unique names for +each polygon in the mask file.} + +\item{ID_fmt}{character. Only applied if \code{poly_IDs = NULL}. Naming scheme for +poly_IDs. Default = "cell_". See details.} \item{flip_vertical}{flip mask figure in a vertical manner} @@ -103,8 +109,6 @@ data.frame with vertex 'x', 'y', and 'poly_ID' information.} \item{shift_horizontal_step}{shift horizontal (boolean or numerical)} -\item{fix_multipart}{try to split polygons with multiple parts (default: TRUE)} - \item{remove_unvalid_polygons}{remove unvalid polygons (default: TRUE)} \item{skip_eval_dfr}{(default FALSE) skip evaluation of provided dataframe} @@ -135,6 +139,30 @@ polygon ID and additional columns are set as attributes, a spatial file such as wkt, .shp, or .GeoJSON, or a mask file (e.g. segmentation results) } \details{ +\emph{mask_method} +One of "single", "multiple", or "guess". +\itemize{ +\item "single" assumes that the provided mask image is binary, with only polygon +vs background being distinct values. With this kind of image, the expected +generated polygons is a single multipart polygon. "single" takes this +multipart polygon and breaks it apart into individual singlepart polygons. +An initial simple \code{numeric} index as the 'nth' polygon found in the mask image +will be applied as an ID (see \emph{ID_fmt} section). +\item "multiple" assumes that the provided mask image has distinct intensity +values to specify the IDs of individual polygons. An initial \code{numeric} ID is +applied as the intensity value of the pixels that made up the annotation for +that polygon in the mask image (see \emph{ID_fmt} section). +\item "guess" examines the values in the image to pick the most likely appropriate +method out of "single" or "multiple". +\emph{ID_fmt} +Defaults to applying the input as a prefix (using \code{paste0()}) to the numerical +ID values detected by \code{mask_method}. (ie \code{ID_fmt = "cell_"} produces +\code{cell_1}, \code{cell_2}, \code{cell_3}, ...) +If a "\%" character is detected in the input then the input will be treated as +a \code{sprintf()} \code{fmt} param input instead. (ie \code{ID_fmt = "cell_\%03d"} produces +\code{cell_001}, \code{cell_002}, \code{cell_003}, ...) +} + When determining which column within the tabular data is intended to provide polygon information, Giotto first checks the column names for 'x', 'y', and 'poly_ID'. If any of these are discovered, they are directly selected. If From 55a9d4ba59579264ae39a27ec6924a7fecfaa580 Mon Sep 17 00:00:00 2001 From: jiajic <72078254+jiajic@users.noreply.github.com> Date: Thu, 29 Feb 2024 14:38:32 -0500 Subject: [PATCH 03/12] fix: gpoly ID cacheing after `rbind()` --- NEWS.md | 1 + R/methods-rbind.R | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index 053c9ea4..99534387 100644 --- a/NEWS.md +++ b/NEWS.md @@ -5,6 +5,7 @@ ## bug fixes - fix `createGiottoPolygonsFromMask()` IDs being applied out of sync to mask values - remove unused `fix_multipart` param in `createGiottoPolygonsFromMask()` +- fix `giottoPolygon` ID cacheing after `rbind()` ## enhancements - `createGiottoPolygonsFromMask()` now has `ID_fmt` param for finer control of automatic poly_ID generation diff --git a/R/methods-rbind.R b/R/methods-rbind.R index 9477713b..f6d8afd6 100644 --- a/R/methods-rbind.R +++ b/R/methods-rbind.R @@ -81,6 +81,8 @@ rbind2_giotto_polygon_homo <- function(x, y) { } else { slot(x, "overlaps") <- rbind(slot(x, "overlaps"), slot(y, "overlaps")) } + + slot(x, "unique_ID_cache") <- unique(c(spatIDs(x), spatIDs(y))) x } @@ -159,7 +161,8 @@ rbind2_giotto_polygon_hetero <- function(x, y, new_name, add_list_ID = TRUE) { name = new_name, spatVector = new_sv, spatVectorCentroids = new_svc, - overlaps = new_ovlp + overlaps = new_ovlp, + unique_IDs = unique(c(spatIDs(x), spatIDs(y))) ) new_poly } From 86ac53117422fc7e74dc201233b1b009a579b3d7 Mon Sep 17 00:00:00 2001 From: jiajic <72078254+jiajic@users.noreply.github.com> Date: Thu, 29 Feb 2024 20:12:43 -0500 Subject: [PATCH 04/12] chore: improve docs --- R/create.R | 63 +++++++++++++++----------------------- man/createGiottoPolygon.Rd | 61 +++++++++++++++++++----------------- 2 files changed, 58 insertions(+), 66 deletions(-) diff --git a/R/create.R b/R/create.R index 3dbdb1aa..c9ebd168 100644 --- a/R/create.R +++ b/R/create.R @@ -1917,7 +1917,7 @@ create_giotto_points_object <- function(feat_type = "rna", #' spatial annotations and polygons. Inputs can be from a structured data.frame #' object where three of the columns should correspond to x/y vertices and the #' polygon ID and additional columns are set as attributes, a spatial file -#' such as wkt, .shp, or .GeoJSON, or a mask file (e.g. segmentation results) +#' such as wkt, .shp, or .GeoJSON, or a mask file (e.g. segmentation results). #' @param x input. Filepath to a .GeoJSON or a mask image file. Can also be a #' data.frame with vertex 'x', 'y', and 'poly_ID' information. #' @param name name for polygons @@ -1956,18 +1956,6 @@ setMethod( ) #' @rdname createGiottoPolygon -#' @param mask_method how the mask file defines individual segmentation annotations -#' @param remove_background_polygon try to remove background polygon (default: FALSE) -#' @param background_algo algorithm to remove background polygon -#' @param fill_holes fill holes within created polygons -#' @param poly_IDs unique names for each polygon in the mask file -#' @param ID_fmt character. Only applied if `poly_IDs = NULL`. Naming scheme for -#' poly_IDs. Default = "cell_". See details. -#' @param flip_vertical flip mask figure in a vertical manner -#' @param shift_vertical_step shift vertical (boolean or numerical) -#' @param flip_horizontal flip mask figure in a horizontal manner -#' @param shift_horizontal_step shift horizontal (boolean or numerical) -#' @param remove_unvalid_polygons remove unvalid polygons (default: TRUE) #' @export setMethod( "createGiottoPolygon", signature("SpatRaster"), @@ -2061,11 +2049,11 @@ setMethod( ) -#' @title Create giotto polygons from mask file + #' @rdname createGiottoPolygon #' @param maskfile path to mask file #' @param mask_method how the mask file defines individual segmentation annotations. -#' see details. +#' See *mask_method* section #' @param name character. Name to assign created `giottoPolygon` #' @param remove_background_polygon try to remove background polygon (default: FALSE) #' @param background_algo algorithm to remove background polygon @@ -2073,36 +2061,35 @@ setMethod( #' @param poly_IDs character vector. Default = NULL. Custom unique names for #' each polygon in the mask file. #' @param ID_fmt character. Only applied if `poly_IDs = NULL`. Naming scheme for -#' poly_IDs. Default = "cell_". See details. +#' poly_IDs. Default = "cell_". See *ID_fmt* section. #' @param flip_vertical flip mask figure in a vertical manner #' @param shift_vertical_step shift vertical (boolean or numerical) #' @param flip_horizontal flip mask figure in a horizontal manner #' @param shift_horizontal_step shift horizontal (boolean or numerical) -#' @param calc_centroids calculate centroids for polygons #' @param remove_unvalid_polygons remove unvalid polygons (default: TRUE) -#' @param verbose verbosity #' @concept mask polygon -#' @details -#' *mask_method* +#' @section mask_method: #' One of "single", "multiple", or "guess". -#' - "single" assumes that the provided mask image is binary, with only polygon -#' vs background being distinct values. With this kind of image, the expected -#' generated polygons is a single multipart polygon. "single" takes this -#' multipart polygon and breaks it apart into individual singlepart polygons. -#' An initial simple `numeric` index as the 'nth' polygon found in the mask image -#' will be applied as an ID (see *ID_fmt* section). -#' - "multiple" assumes that the provided mask image has distinct intensity -#' values to specify the IDs of individual polygons. An initial `numeric` ID is -#' applied as the intensity value of the pixels that made up the annotation for -#' that polygon in the mask image (see *ID_fmt* section). -#' - "guess" examines the values in the image to pick the most likely appropriate -#' method out of "single" or "multiple". -#' *ID_fmt* -#' Defaults to applying the input as a prefix (using `paste0()`) to the numerical -#' ID values detected by `mask_method`. (ie `ID_fmt = "cell_"` produces -#' `cell_1`, `cell_2`, `cell_3`, ...) +#' \itemize{ +#' \item{*"single"* assumes that the provided mask image is binary, with only +#' polygon vs background being distinct values. With this kind of image, the +#' expected generated polygons is a single multipart polygon. "single" takes +#' this multipart polygon and breaks it apart into individual singlepart +#' polygons. An initial simple `numeric` index as the 'nth' polygon found in +#' the mask image will be applied as an ID (see *ID_fmt* section).} +#' \item{*"multiple"* assumes that the provided mask image has distinct +#' intensity values to specify the IDs of individual polygons. An initial +#' `numeric` ID is applied as the intensity value of the pixels that made up +#' the annotation for that polygon in the mask image (see *ID_fmt* section).} +#' \item{*"guess"* examines the values in the image to pick the most likely +#' appropriate method out of "single" or "multiple".} +#' } +#' @section ID_fmt: +#' Defaults to applying the input as a prefix (using `paste0()`) to the +#' numerical ID values detected by `mask_method`. (ie: `ID_fmt = "cell_"` +#' produces `cell_1`, `cell_2`, `cell_3`, ...)\cr #' If a "%" character is detected in the input then the input will be treated as -#' a `sprintf()` `fmt` param input instead. (ie `ID_fmt = "cell_%03d"` produces +#' a `sprintf()` `fmt` param input instead. (ie: `ID_fmt = "cell_%03d"` produces #' `cell_001`, `cell_002`, `cell_003`, ...) #' @return a giotto polygon object #' @export @@ -2308,7 +2295,7 @@ createGiottoPolygonsFromMask <- function( #' @param copy_dt (default TRUE) if segmdfr is provided as dt, this determines #' whether a copy is made #' @param verbose be verbose -#' @details When determining which column within the tabular data is intended to +#' @details When determining which column within tabular data is intended to #' provide polygon information, Giotto first checks the column names for 'x', 'y', #' and 'poly_ID'. If any of these are discovered, they are directly selected. If #' this is not discovered then Giotto checks the data type of the columns and selects diff --git a/man/createGiottoPolygon.Rd b/man/createGiottoPolygon.Rd index 99bc87e5..97f0f31e 100644 --- a/man/createGiottoPolygon.Rd +++ b/man/createGiottoPolygon.Rd @@ -87,7 +87,7 @@ data.frame with vertex 'x', 'y', and 'poly_ID' information.} \item{verbose}{be verbose} \item{mask_method}{how the mask file defines individual segmentation annotations. -see details.} +See \emph{mask_method} section} \item{remove_background_polygon}{try to remove background polygon (default: FALSE)} @@ -99,7 +99,7 @@ see details.} each polygon in the mask file.} \item{ID_fmt}{character. Only applied if \code{poly_IDs = NULL}. Naming scheme for -poly_IDs. Default = "cell_". See details.} +poly_IDs. Default = "cell_". See \emph{ID_fmt} section.} \item{flip_vertical}{flip mask figure in a vertical manner} @@ -136,34 +136,10 @@ Create a \code{giottoPolygon} object that is used to represent spatial annotations and polygons. Inputs can be from a structured data.frame object where three of the columns should correspond to x/y vertices and the polygon ID and additional columns are set as attributes, a spatial file -such as wkt, .shp, or .GeoJSON, or a mask file (e.g. segmentation results) +such as wkt, .shp, or .GeoJSON, or a mask file (e.g. segmentation results). } \details{ -\emph{mask_method} -One of "single", "multiple", or "guess". -\itemize{ -\item "single" assumes that the provided mask image is binary, with only polygon -vs background being distinct values. With this kind of image, the expected -generated polygons is a single multipart polygon. "single" takes this -multipart polygon and breaks it apart into individual singlepart polygons. -An initial simple \code{numeric} index as the 'nth' polygon found in the mask image -will be applied as an ID (see \emph{ID_fmt} section). -\item "multiple" assumes that the provided mask image has distinct intensity -values to specify the IDs of individual polygons. An initial \code{numeric} ID is -applied as the intensity value of the pixels that made up the annotation for -that polygon in the mask image (see \emph{ID_fmt} section). -\item "guess" examines the values in the image to pick the most likely appropriate -method out of "single" or "multiple". -\emph{ID_fmt} -Defaults to applying the input as a prefix (using \code{paste0()}) to the numerical -ID values detected by \code{mask_method}. (ie \code{ID_fmt = "cell_"} produces -\code{cell_1}, \code{cell_2}, \code{cell_3}, ...) -If a "\%" character is detected in the input then the input will be treated as -a \code{sprintf()} \code{fmt} param input instead. (ie \code{ID_fmt = "cell_\%03d"} produces -\code{cell_001}, \code{cell_002}, \code{cell_003}, ...) -} - -When determining which column within the tabular data is intended to +When determining which column within tabular data is intended to provide polygon information, Giotto first checks the column names for 'x', 'y', and 'poly_ID'. If any of these are discovered, they are directly selected. If this is not discovered then Giotto checks the data type of the columns and selects @@ -171,5 +147,34 @@ the first \code{'character'} type column to be 'poly_ID' and the first two \code columns as 'x' and 'y' respectively. If this is also unsuccessful then poly_ID defaults to the 3rd column. 'x' and 'y' then default to the 1st and 2nd columns. } +\section{mask_method}{ + +One of "single", "multiple", or "guess". +\itemize{ +\item{\emph{"single"} assumes that the provided mask image is binary, with only +polygon vs background being distinct values. With this kind of image, the +expected generated polygons is a single multipart polygon. "single" takes +this multipart polygon and breaks it apart into individual singlepart +polygons. An initial simple \code{numeric} index as the 'nth' polygon found in +the mask image will be applied as an ID (see \emph{ID_fmt} section).} +\item{\emph{"multiple"} assumes that the provided mask image has distinct +intensity values to specify the IDs of individual polygons. An initial +\code{numeric} ID is applied as the intensity value of the pixels that made up +the annotation for that polygon in the mask image (see \emph{ID_fmt} section).} +\item{\emph{"guess"} examines the values in the image to pick the most likely +appropriate method out of "single" or "multiple".} +} +} + +\section{ID_fmt}{ + +Defaults to applying the input as a prefix (using \code{paste0()}) to the +numerical ID values detected by \code{mask_method}. (ie: \code{ID_fmt = "cell_"} +produces \code{cell_1}, \code{cell_2}, \code{cell_3}, ...)\cr +If a "\%" character is detected in the input then the input will be treated as +a \code{sprintf()} \code{fmt} param input instead. (ie: \code{ID_fmt = "cell_\%03d"} produces +\code{cell_001}, \code{cell_002}, \code{cell_003}, ...) +} + \concept{mask polygon} \concept{polygon} From b8f5ecd023e99f316eb3a65fa53c0be763e56e6c Mon Sep 17 00:00:00 2001 From: jiajic <72078254+jiajic@users.noreply.github.com> Date: Fri, 1 Mar 2024 03:16:56 -0500 Subject: [PATCH 05/12] feat: add internal flip function for terra SpatVectors terra has a flip function, but this internal adds the ability to flip over arbitrary x and y values in the same manner that has been implemented for `giottoPolygon` and `giottoPoints` --- R/methods-flip.R | 38 +++++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/R/methods-flip.R b/R/methods-flip.R index 5da5f3f8..81684fda 100644 --- a/R/methods-flip.R +++ b/R/methods-flip.R @@ -164,7 +164,7 @@ setMethod( ) } } else { - # flip about y0 + # flip about x0 # poly dx_p <- x0 - x_min_p gpoly@spatVector <- terra::shift( @@ -190,6 +190,42 @@ setMethod( +.flip_spatvect <- function( + x, direction = "vertical", x0 = 0, y0 = 0 + ) { + checkmate::assert_class(x, "SpatVector") + if (!is.null(x0)) { + checkmate::assert_numeric(x0) + } + if (!is.null(y0)) { + checkmate::assert_numeric(y0) + } + + # 1. perform flip + e <- terra::ext(x) + x <- terra::flip(x, direction = direction) + + x <- switch(direction, + "vertical" = { + if (!is.null(y0)) { # flip about y0 if not NULL + ymin <- as.numeric(e$ymin) + dy <- y0 - ymin + terra::shift(x, dy = 2 * dy) + } + }, + "horizontal" = { + if (!is.null(x0)) { # flip about x0 if not NULL + xmin <- as.numeric(e$xmin) + dx <- x0 - xmin + terra::shift(x, dx = 2 * dx) + } + } + ) + + # 3. return + return(x) +} + From 17080937dbbd37603fd7049d3142c534cb303469 Mon Sep 17 00:00:00 2001 From: jiajic <72078254+jiajic@users.noreply.github.com> Date: Fri, 1 Mar 2024 12:12:30 -0500 Subject: [PATCH 06/12] add: new toy poly and tif files for testing & examples --- inst/extdata/toy_mask_multi.tif | Bin 0 -> 3358 bytes inst/extdata/toy_mask_single.tif | Bin 0 -> 2926 bytes inst/extdata/toy_poly.cpg | 1 + inst/extdata/toy_poly.dbf | Bin 0 -> 301 bytes inst/extdata/toy_poly.shp | Bin 0 -> 3484 bytes inst/extdata/toy_poly.shx | Bin 0 -> 156 bytes 6 files changed, 1 insertion(+) create mode 100644 inst/extdata/toy_mask_multi.tif create mode 100644 inst/extdata/toy_mask_single.tif create mode 100644 inst/extdata/toy_poly.cpg create mode 100644 inst/extdata/toy_poly.dbf create mode 100644 inst/extdata/toy_poly.shp create mode 100644 inst/extdata/toy_poly.shx diff --git a/inst/extdata/toy_mask_multi.tif b/inst/extdata/toy_mask_multi.tif new file mode 100644 index 0000000000000000000000000000000000000000..da28db1802285a388c645347e453dc1ad4bc1186 GIT binary patch literal 3358 zcma)72UL^U68@=DLKVVWiiD074JDwE1VR#GfY6cvE+DcH5u_)8N?Ea?B2prVB3Ka> zLKmb2P(n>8LIB0CATCBwK|v|LAD?@Ud)}UP?>TelyLZ0%Z<+b$5{X8D1ONc*0YFFu z5CTy!C_+DA;qMp*&Wn7jV&5A)FPSPA5)eCK=svHA}j)DM{a z9gBb**+URuP5TK0z`?^1Kpeyj5Lcv$05A|=1#x8>1OOE<0d`es!rx9Z58?x$5d&^a z6_ivEi-MfUz(}wMfO2pSqyl1~AV8@B$IGC;36jj#0f3bx0PK+l03-p2aUszdfx&@{ zK(rJLh9oj*5wOU>2pUS8Ou*j5mP{+hUmn z8~^}_$g-5A_`$a4@Vyjm4VhzDZTH(((FV{HoFKEk{NGR-Sp;2=2Hg~beoi;dd>`eq zziGa+zGuVLmJq9Rybz0h{1?fni*APGM%~pk$IFAEmiwvNg{13bBT8d2y2R@ye-C2+ z$Zu@_N^cWtlU_>KuIl>{R*BThk0WmTZt`j>ZkdX2d=zD!IPzj8xh(+h-K_U@-O;1bqh2budcU$ZmF>z$x)O&SeC6%K}Ioe~z6CSDslVEIM;NnO!Jz5wP66CcKx zkA{4Dzp`V2+`^BnS8EU-&ZwFQ|A=%Akt@u67FNH4mUBKb_UP?CRJnIT!_!xvkf?K0 zgg*Y8{c$OFw35sx3k$2Zk-szFygl>zOKXMq=9#ahvjz1+Xt}u#06l24VHJ~!y}XPu zIxO+oKDv}GftAM|7jKPbAg9~mo`I5_tlEtVIQU6xxzLgjG+lco&lo-Q5|AI4Qa`|nKV$#j)#H#tTP^Ve-Vm0UY9F{IG zbh*ZY%hc6@_hnl)UWUPb7Og`EQiU_O={%E!oBVy7pr`R*tmow()1&Z`No z88u)O&{Q99s~?;(x5bZ+B-HpnR8p5U7G(CfKQ&V^K5G3-X5Gv3{IcSu z^?96KArg`s=w^~_&n>+)G!7vy@M%*pVTkm0`u*U#tum)BQ0pGi;y*Xfd=e`HqPIXZ zWSJQqFJ|J9WmYX>tSP|OyDNWkpO+Q9Nc$6TJpZ$#yloCJctqj}k@`d&~g& zo#eBRrp3@jZd0{3%FsiE3#3={u;9*Iw`)UxjHn)QWtnFYjm>|ZjAU!y@CmCkD$c72 zs&&4hpF3*Pm?siwvwIc;ob?6!d88jj_4Gq#XeP^$n8p zZcR^-nyo!EOOM$|O@f@@lie%_NJYbSCf-?;&x?t*!S|G{RLcj=Nw!5Ir2vQ7tBUXL z43_E1X~7SDxr}{pVk_kE-YFS!M|R0h3U}|Yn|y1wQjol(M=wp;{+_^0q&*fAqt!X< zhpX$ZIv=Nmj}1@WfFC-^)a&t7k1*#H;%-Y`c;gTTB+%s>VuSwhgmhoRS#^Fz|X*q^M&8URr(6* z3nou2C9>pU_YF5Fq#kV#sPci(^dDwk4>t8wTXvX@<@sA!?{4u%rL%X_7atVqtSh-Y z4gW$CKh?+V7R0MT$^#Jf{gGDVrjZNVTc3E(j~D4zq_>a7CJYxAHWlRbzqU?BJ|(C2 zzWJ~OO(7&upFCSkq|2so3#U3i)}vdgFK^8XmXmxod>zaD^nMwgEH_o~rI}q`Ci>VS ziaRM{7q!0aB__)cx`L7BawXX^5D{UlT(GnRHVbKU4>Q1Vc+vjuM7ES2>q@auyE|{Q zC}(g!cn0TDI;+-ccCbUq?w&vhivJ*e<(DpV6D@^1RkcjTZP_MnQe7u?;hfDBaPSkJXEBfOm$hSM=^Zk0ds~ z80CG5afCo5=f;twskwa!U+>(qQ1?;y3>~x8I-idbW zMqGSDK<8a_yFPDm)zmEPdrg^N*7)jnh4W@^@m|r6v0R; zN=v0vPxmFETL$VhA204jJ*l`*lQFy;wc6Th@Gxt(rmt!7i(RFf;WB%ZsEk-!Wf*6% zW?)`V-2RlV{Io;mdGMdDJlG_QlfUY4)?r4;S^=+MvvY!j6<6cGH4HM^BK5Mgc&T^9( Zk8ILz!CUXr1J(;lpEZe@G~Rm=_FutVYajps literal 0 HcmV?d00001 diff --git a/inst/extdata/toy_mask_single.tif b/inst/extdata/toy_mask_single.tif new file mode 100644 index 0000000000000000000000000000000000000000..0efec8e8fa7a51cdeed70fc5c81ae32322836d59 GIT binary patch literal 2926 zcmbVN2UJtp7JU>^AOj)-N|8vBQGpPoh!8@LA)tW-aU{SXL=-{~C?$%Df;vc3q(xLf zR7!wB6bQW}qk)7fpQbVa90(ekQ3>)NerGLRYyO{E|E+cI+3%cv-Yxsy`_Sk;z$O3y z0B|7z0!VQQ5E>q`K07~Rgm3t% zQ1YpeU=2%>Pc$+7&p}(jzF#q5-}V~%lmar$xRiB>v`!f9srVp$eC(|;p^YCx{p6RFsxWt=3u8u5>2R zFdBc)Q!fj%%eA(!(cdJ8<-aewe}aOzbNEMGoz~|>hkKk;Mlo(0RObh{J@~pJ%i<%C z`U8z)297s)R=DfB)oY(+oTwTKGKqD&Hx~5NL;py9QR8$}-Drqu?7+k-rPEmIG}V%szJ^EX_mMi8&$tn0af7}HD#w3kl88_meZM!}=#%PrO@7HxKxB+w z)UU|u6keoC!;$B>rz4@{*tx0M*eAn$GY>CQ(<_YE;n8`YPb;14i zQ9l>oihe!!iL!;Q<1xlrey_h{T#);E;lqa}x%^_mZDE5WMkuJS5QnVRV^wBFk$gMR zTP@gY<*PQSu@@z+Q{rT$8d9p^8(KM|HuN^W&|c22VsTEsrN_5c!4@tqt=O3qhucP% zD@DoJr6my69m2Sm)EQgE-PP=ShR>@o(@E(|=rp6$M^NbBNpS%$?En=&n7K+BJ;7th==XHA*g$@ZR^i#PTuEnl@fkAoaLM*kgj8Acr`(>R0a#+h#t# zWjV2t!yi&v?Gw@N;D*+kT{hjG!tz|m(jCXh-M&5*YLZuTOXNaWYL_!tt|5erLZS>d4O`L?^@G%_lE;|Y*=D9H>}p!GEv(5#qaN9D0^ky@2buQ zt$20`5*yebIl4bu&wXXTzCguC=C){4Uw%R1K4F#^N3O+BoE69D6JmEr*!5vlQ5e zu}-Nl(#SDGunN^YwfDs z!1Ffcnq62I2v0@p82-e4OVP;gdVt&BAaTXE!1ZKxoxNe0Retv)g#$NtlsVoX3|N}! z2u^dkJ)mpsp%&Fes6JBqzQj3Zj95V+xjYXle;G;lfMHi|rmuu1dj$*XYm_!zZuOpb zw~&#SO=0+jo@NnO_QwARvg#*2 zF-K&+MEyL*kIK^s_{XLq>Mq`9$U+TxQ`Ae&o-rgbOQJ*bCA;2`^9bICzMYfM4w8A>6Y%riLz7%WS zT!0j_=2ROE+h7T%Qf;t|yPMnPba!OimiZy&JAy{eZ?R8H(A!FzClOWJd#WQ;ZTPxz z!cO?;B`7nk|Jp`})Vyd-bUJ~$vw4dXLWjAK@Nn0&Ue@;Aj#a!$nByYkvA)BvTU+!n ziM zo5ky;K2=$zV{67a&$;AdYP&pk=69cpP(Br0pj}?{CQA8-6Dfg@Mto~xsEvZiys_Dd z75$A$?u^le)7x*Goc3*;B=5JFo$M>Fm|QZqxc(BCJ1}>K)Npo9Mlku>ufDNK359s` z?jVKAMi&3xJwc)8AD*rk&RWW}4Sy_UkGunrSgD($)~TwhiOn4O6^Y`Ct7GDp{A!(z zO?<8~gw7eYD{O)9wU=$h@=#J{aNAr(2T3P_pq(T1RcK3LSu3~Bv*HF@oQ2UltHS7R zn)Q`=vg*G{CwTpF5I&Nx>X;r-qHU61`b!I{+^%9AkN8Tex!wCO(T`@8i`Y4lg>3FIzJ={e_s{{p9}kW?dI^3a6L<`~hBB zt2}Z)wP(6Ihp0it6!$(L2k41DbkFPyP^h~ZQ13!J>aTE&daR3BeY7O=(Q0{1$P-)M ztpgqRFeExb?@4`Er-1zHD?QVaxkbsuCLfLLR=Xn~M`O?I8)kLndsYqw30$c6Q-Z3--kwo4&&;4OUcEIV-1gIn zy)ebEBQ$EAPQLeM`2ZykW;r`L^T9kixak&SdM+j!F}GIGF~gss4Zb za_v1%E(+}~q{B+~E(t#=b*B9ixV^K76{gnd!V6JHxr#@tGt(j0b~1aN(zKbbq!b;r NYeKfpv$S5(e*<|-$Xfsa literal 0 HcmV?d00001 diff --git a/inst/extdata/toy_poly.cpg b/inst/extdata/toy_poly.cpg new file mode 100644 index 00000000..3ad133c0 --- /dev/null +++ b/inst/extdata/toy_poly.cpg @@ -0,0 +1 @@ +UTF-8 \ No newline at end of file diff --git a/inst/extdata/toy_poly.dbf b/inst/extdata/toy_poly.dbf new file mode 100644 index 0000000000000000000000000000000000000000..7aa15a1af174708590f1167d6974b562cf4a9ea3 GIT binary patch literal 301 zcmZRsVP<4!U|>jOkOh*Mpdde|GTze#B;gDqxIhFF$V{n#@%%sxAs7!z^C~1N-~xsQ m3Q4%+6cj8Kl5tB~D5T()G*?K)Eor8ZhFj89Asx4*i4*`Kjv=-H literal 0 HcmV?d00001 diff --git a/inst/extdata/toy_poly.shp b/inst/extdata/toy_poly.shp new file mode 100644 index 0000000000000000000000000000000000000000..e41424ee091e92375cc61ca878edeb7222e61a5d GIT binary patch literal 3484 zcmaJ@YfO_@82(ylEkzJ|VTD%djTY*n28-n)_MI>^AnPHRMl%?hjZo4Q(^^# ztgld>6l1E5td-L-0=1}^oy&Q<2i!8Y>O?cX+q zfU7Oz;ie`T9Q5s4T<%f<*0!RlCT@;P0bE{h)XoJmxc4}_95+3Ct~Kj?J3&#D500dbt`j!0SZR6*lQ z>WSp`Ldojz{W5EnvN_xqjIf zZfL0=#1{JnbxYBe16snP(*M_|(;CAFFUf-Bw*Jkbgr{wAfAx`$mk9684M)P4jHro^ zXZR8G-#t9v?9NduW%~b}nMi?W<#TxE-+3N96OjVX6QI+lGO4x83V{W64H*(&@Q-c& zZ249zRI8h->QXrHN#D}&k=p`&U3b5lp&4?%C)xIWjoAWuEeE*d;Q{Bq(GC6UW=8;5 zeeury-S?e~3UeY;!A!Ni-2JmI984g3*zsmqX`l1&FV=43QjM^|Z8BB)_dBnyiqg;j z&;aVPV+RM)A33u{J13T2(L=@Y;^EF2BhF*0xhHz&>*0J`+uA6d7_j%E*4N4^Mk=OWo7clWHds-Vavwa$k)wcb4D{ zJf{3aZ#~Zv=ICiI({ni76}1@L_hJM+PXVc}C%L*IM$oek3a2J!CgjGzlXDks6F&)q z+QP53RwHDKXd=7d5@!G+C^Lw0CzPs^LVMg!JHhsvV2Q*9v+vP&ja%mmjr56kA6 zHPHXl*;hErOgK2yBy(TZKzEI5_t6W{Ac=^wjS5| zV2T#>JQ=WerspX%?vnSuo$k`p{Lc*(UN}-z+u3WPewgo?sNag>tjm8~GZ7vw+wT5Xy7i>DGfO`>to_^osNZ$Dx$nq#7)h^&LB;W^0uzlHuV&IO z<28cTW4u~uKgO$-^vig)l71Pl*71IXJVpI7Uaf=&BkWVxH#*uBddGyPAvjb{|aTxmo{l)^DV-|eE z0jOVAhXYW*j8_Msepw$5K>e~lX94P$;lO8v;3W*F7(o3p9Ag0W%W$>>>X-3g2h=a) z$quMr>=pHkW7IFsNB!b@s9)R<^^5hOez9KEFZCnn7yCv1A|9w;#w)Kmk1tyUUv>cM zm+|TVZ~cz*!SgbK|9JFUf3?eI2@0Z&W1Q(FM-epw i@;)#yFsealCMgKb%m<`jFfcIxg78`1LHMk Date: Fri, 1 Mar 2024 12:14:55 -0500 Subject: [PATCH 07/12] fix: `createGiottoPolygon`, `character` method --- R/create.R | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/R/create.R b/R/create.R index c9ebd168..16f42e90 100644 --- a/R/create.R +++ b/R/create.R @@ -2028,23 +2028,21 @@ setMethod( # try success means it should be mask file # try failure means it should be vector file - try_rast <- try( + try_rast <- tryCatch( { terra::rast(x) }, - silent = TRUE + error = function(e) return(invisible(NULL)), + warning = function(w) {NULL} ) # mask workflow if (inherits(try_rast, "SpatRaster")) { - return(createGiottoPolygon(x, ...)) + return(createGiottoPolygon(try_rast, ...)) } # file workflow - return(createGiottoPolygon( - x = terra::vect(x), - ... - )) + return(createGiottoPolygon(x = terra::vect(x), ...)) } ) From c9d69e6977394c1094658822a02b0878076b274d Mon Sep 17 00:00:00 2001 From: jiajic <72078254+jiajic@users.noreply.github.com> Date: Fri, 1 Mar 2024 12:16:34 -0500 Subject: [PATCH 08/12] fix: poly_ID setting for `giottoPolygon` creation from mask image Also updates to documentation and examples --- R/create.R | 101 ++++++++++++++++++++++++++++++++----- man/createGiottoPolygon.Rd | 57 +++++++++++++++++++++ 2 files changed, 144 insertions(+), 14 deletions(-) diff --git a/R/create.R b/R/create.R index 16f42e90..8a8de6cb 100644 --- a/R/create.R +++ b/R/create.R @@ -1996,6 +1996,17 @@ setMethod( ) #' @rdname createGiottoPolygon +#' @examples +#' # ------- create from data.frame-like ------- # +#' shp <- system.file("extdata/toy_poly.shp", package = "GiottoClass") +#' gpoly <- createGiottoPolygon(shp, name = "test") +#' plot(gpoly) +#' gpoly_dt <- data.table::as.data.table(gpoly, geom = "XY") +#' out <- createGiottoPolygon(gpoly_dt[, .(geom, part, x, y, hole, poly_ID)], +#' name = "test") +#' plot(out) +#' +#' #' @export setMethod( "createGiottoPolygon", signature("data.frame"), @@ -2020,6 +2031,27 @@ setMethod( #' @param \dots additional params to pass. For character method, params pass to #' SpatRaster or SpatVector methods, depending on whether x was a filepath to #' a maskfile or a spatial file (ex: wkt, shp, GeoJSON) respectively. +#' @examples +#' # %%%%%%%%% `createGiottoPolygon()` examples %%%%%%%%% # +#' # ------- create from a mask image ------- # +#' m <- system.file("extdata/toy_mask_multi.tif", package = "GiottoClass") +#' plot(terra::rast(m), col = grDevices::hcl.colors(7)) +#' gp <- createGiottoPolygon( +#' m, +#' flip_vertical = FALSE, flip_horizontal = FALSE, +#' shift_horizontal_step = FALSE, shift_vertical_step = FALSE, +#' ID_fmt = "id_test_%03d", +#' name = "test" +#' ) +#' plot(gp, col = grDevices::hcl.colors(7)) +#' +#' # ------- create from an shp file ------- # +#' shp <- system.file("extdata/toy_poly.shp", package = "GiottoClass") +#' # vector inputs do not have params for flipping and shifting +#' gp2 <- createGiottoPolygon(shp, name = "test") +#' plot(gp2, col = grDevices::hcl.colors(7)) +#' +#' #' @export setMethod( "createGiottoPolygon", signature("character"), @@ -2090,6 +2122,32 @@ setMethod( #' a `sprintf()` `fmt` param input instead. (ie: `ID_fmt = "cell_%03d"` produces #' `cell_001`, `cell_002`, `cell_003`, ...) #' @return a giotto polygon object +#' @examples +#' # %%%%%%%%% `createGiottoPolygonsFromMask()` examples %%%%%%%%% # +#' mask_multi <- system.file("extdata/toy_mask_multi.tif", +#' package = "GiottoClass") +#' mask_single <- system.file("extdata/toy_mask_single.tif", +#' package = "GiottoClass") +#' plot(terra::rast(mask_multi), col = grDevices::hcl.colors(7)) +#' plot(terra::rast(mask_single)) +#' +#' gpoly1 = createGiottoPolygonsFromMask( +#' mask_multi, +#' flip_vertical = FALSE, flip_horizontal = FALSE, +#' shift_horizontal_step = FALSE, shift_vertical_step = FALSE, +#' ID_fmt = "id_test_%03d", +#' name = "multi_test" +#' ) +#' plot(gpoly1, col = grDevices::hcl.colors(7)) +#' +#' gpoly2 = createGiottoPolygonsFromMask( +#' mask_single, +#' flip_vertical = FALSE, flip_horizontal = FALSE, +#' shift_horizontal_step = FALSE, shift_vertical_step = FALSE, +#' ID_fmt = "id_test_%03d", +#' name = "single_test" +#' ) +#' plot(gpoly2, col = grDevices::hcl.colors(5)) #' @export createGiottoPolygonsFromMask <- function( maskfile, @@ -2141,6 +2199,7 @@ createGiottoPolygonsFromMask <- function( # (which usually encodes the intended polygon ID) is added to the resulting # SpatVector as the only attribute. terra_polygon <- terra::as.polygons(x = terra_rast, value = TRUE) + val_col <- names(terra_polygon) # the only col should be from the values # fill holes if (isTRUE(fill_holes)) { @@ -2155,20 +2214,19 @@ createGiottoPolygonsFromMask <- function( terra_polygon <- terra_polygon[valid_index] } - - spatVecDT <- .spatvector_to_dt(terra_polygon) - ## flip across axes ## if (isTRUE(flip_vertical)) { - # terra_polygon = terra::flip(terra_polygon, direction = 'vertical') - spatVecDT[, y := -y] + terra_polygon <- .flip_spatvect(terra_polygon) } - if (isTRUE(flip_horizontal)) { - # terra_polygon = terra::flip(terra_polygon, direction = 'horizontal') - spatVecDT[, x := -x] + terra_polygon <- .flip_spatvect(terra_polygon) } + # convert to DT format since we want to be able to compare number of geoms + # vs polys to determine correct mask method. + # TODO only test a subset of polys here? + spatVecDT <- .spatvector_to_dt(terra_polygon) + ## guess mask method ## if (mask_method == "guess") { uniq_geoms <- length(unique(spatVecDT$geom)) @@ -2182,21 +2240,36 @@ createGiottoPolygonsFromMask <- function( naming_fun <- ifelse(grepl("%", ID_fmt), sprintf, paste0) # If poly_IDs are NOT provided, then terra_polygon IDs created here will be # `character` and the finalized ID values. - # If not, the IDs are still temporary and `numeric`, pending the `poly_IDs` - # param being applied downstream. + # If poly_IDs ARE provided, the IDs are still temporary and MUST remain + # `numeric`, pending the `poly_IDs` param being applied downstream. terra_polygon <- switch(mask_method, "multiple" = { + names(terra_polygon) <- "poly_ID" if (is.null(poly_IDs)) { - spatVecDT[, geom := naming_fun(ID_fmt, geom)] + # spatVecDT[, geom := naming_fun(ID_fmt, geom)] + # spatVecDT[, (val_col) := naming_fun(ID_fmt, get(val_col))] + # g_polygon <- createGiottoPolygonsFromDfr( + # segmdfr = spatVecDT[, .(x, y, get(val_col))] + # ) + # g_polygon@spatVector + terra_polygon$poly_ID <- naming_fun(ID_fmt, terra_polygon$poly_ID) } - g_polygon <- createGiottoPolygonsFromDfr(segmdfr = spatVecDT[, .(x, y, geom)]) - g_polygon@spatVector + terra_polygon }, "single" = { + # TODO ordering may be performed based on centroids xy instead of + # converting the full polygon and then ordering on parts + # May improve the speed if (is.null(poly_IDs)) { spatVecDT[, part := naming_fun(ID_fmt, part)] } - g_polygon <- createGiottoPolygonsFromDfr(segmdfr = spatVecDT[, .(x, y, part)]) + g_polygon <- createGiottoPolygonsFromDfr( + segmdfr = spatVecDT[, .(x, y, part)] + ) + if (!is.null(poly_IDs)) { + g_polygon@spatVector$poly_ID <- as.numeric(g_polygon@spatVector$poly_ID) + } + g_polygon@spatVector } ) diff --git a/man/createGiottoPolygon.Rd b/man/createGiottoPolygon.Rd index 97f0f31e..0ff740a1 100644 --- a/man/createGiottoPolygon.Rd +++ b/man/createGiottoPolygon.Rd @@ -176,5 +176,62 @@ a \code{sprintf()} \code{fmt} param input instead. (ie: \code{ID_fmt = "cell_\%0 \code{cell_001}, \code{cell_002}, \code{cell_003}, ...) } +\examples{ +# ------- create from data.frame-like ------- # +shp <- system.file("extdata/toy_poly.shp", package = "GiottoClass") +gpoly <- createGiottoPolygon(shp, name = "test") +plot(gpoly) +gpoly_dt <- data.table::as.data.table(gpoly, geom = "XY") +out <- createGiottoPolygon(gpoly_dt[, .(geom, part, x, y, hole, poly_ID)], + name = "test") +plot(out) + + +# \%\%\%\%\%\%\%\%\% `createGiottoPolygon()` examples \%\%\%\%\%\%\%\%\% # +# ------- create from a mask image ------- # +m <- system.file("extdata/toy_mask_multi.tif", package = "GiottoClass") +plot(terra::rast(m), col = grDevices::hcl.colors(7)) +gp <- createGiottoPolygon( + m, + flip_vertical = FALSE, flip_horizontal = FALSE, + shift_horizontal_step = FALSE, shift_vertical_step = FALSE, + ID_fmt = "id_test_\%03d", + name = "test" +) +plot(gp, col = grDevices::hcl.colors(7)) + +# ------- create from an shp file ------- # +shp <- system.file("extdata/toy_poly.shp", package = "GiottoClass") +# vector inputs do not have params for flipping and shifting +gp2 <- createGiottoPolygon(shp, name = "test") +plot(gp2, col = grDevices::hcl.colors(7)) + + +# \%\%\%\%\%\%\%\%\% `createGiottoPolygonsFromMask()` examples \%\%\%\%\%\%\%\%\% # +mask_multi <- system.file("extdata/toy_mask_multi.tif", + package = "GiottoClass") +mask_single <- system.file("extdata/toy_mask_single.tif", + package = "GiottoClass") +plot(terra::rast(mask_multi), col = grDevices::hcl.colors(7)) +plot(terra::rast(mask_single)) + +gpoly1 = createGiottoPolygonsFromMask( + mask_multi, + flip_vertical = FALSE, flip_horizontal = FALSE, + shift_horizontal_step = FALSE, shift_vertical_step = FALSE, + ID_fmt = "id_test_\%03d", + name = "multi_test" +) +plot(gpoly1, col = grDevices::hcl.colors(7)) + +gpoly2 = createGiottoPolygonsFromMask( + mask_single, + flip_vertical = FALSE, flip_horizontal = FALSE, + shift_horizontal_step = FALSE, shift_vertical_step = FALSE, + ID_fmt = "id_test_\%03d", + name = "single_test" +) +plot(gpoly2, col = grDevices::hcl.colors(5)) +} \concept{mask polygon} \concept{polygon} From 04403ba3d0888caa1dca9ca7308139612288fc44 Mon Sep 17 00:00:00 2001 From: jiajic <72078254+jiajic@users.noreply.github.com> Date: Fri, 1 Mar 2024 12:17:00 -0500 Subject: [PATCH 09/12] chore: add tests for `giottoPolygon` creation from mask files --- tests/testthat/test-createObject.R | 104 +++++++++++++++++++++++++++-- 1 file changed, 100 insertions(+), 4 deletions(-) diff --git a/tests/testthat/test-createObject.R b/tests/testthat/test-createObject.R index f6a59433..655d936a 100644 --- a/tests/testthat/test-createObject.R +++ b/tests/testthat/test-createObject.R @@ -48,10 +48,106 @@ test_that("giottoPolygon is created from data.table", { expect_setequal(gp_IDs, spatIDs(gp)) }) -# TODO need the file uploaded to do this easily -# test_that('giottoPolygon is created from maskfile', { -# gp = createGiottoPolygonsFromMask() -# }) + +test_that('giottoPolygon is created from maskfile', { + # make a faux mask (DO NOT DELETE COMMENTED CODE HERE) + # a <- circleVertices(2) + b <- data.table::data.table(sdimx = c(5, 10, 20, 10, 25, 22, 6), + sdimy = c(5, 3, 8, 10, 3, 10, 8), + cell_ID = letters[seq(7)]) + # x <- createGiottoPolygon(polyStamp(a, b))[] + # x$idx <- rev(4:10) + # r <- terra::rast(ncol = 100, nrow = 100) + # ext(r) <- c(0, 30, 0, 13) + # mask_multi <- terra::rasterize(x, r, field = "idx") + # terra::writeRaster(mask_multi, + # filename = "inst/extdata/toy_mask_multi.tif", + # gdal = "COG", + # overwrite = TRUE) + # mask_single <- terra::rasterize(x, r) + # terra::writeRaster(mask_single, + # filename = "inst/extdata/toy_mask_single.tif", + # gdal = "COG", + # overwrite = TRUE) + # terra::writeVector(x, + # filename = "inst/extdata/toy_poly.shp", + # overwrite = TRUE) + + m <- system.file("extdata/toy_mask_multi.tif", package = "GiottoClass") + s <- system.file("extdata/toy_mask_single.tif", package = "GiottoClass") + + # expect all 7 polys + gpm = createGiottoPolygonsFromMask(m, + flip_vertical = FALSE, + flip_horizontal = FALSE, + shift_horizontal_step = FALSE, + shift_vertical_step = FALSE, + ID_fmt = "id_test_%03d", + name = "multi_test", + verbose = FALSE) + expect_equal(nrow(gpm), 7) + gpm_centroids_dt <- data.table::as.data.table(centroids(gpm), geom = "XY") + expect_identical(gpm_centroids_dt$poly_ID, sprintf("id_test_%03d", 4:10)) + # compare against reversed values from spatlocs DT since values were applied + # in reverse (from idx col) + expect_identical(round(gpm_centroids_dt$x), rev(b$sdimx)) + expect_identical(round(gpm_centroids_dt$y), rev(b$sdimy)) + + # expect 5 polys + gps = createGiottoPolygonsFromMask(s, + flip_vertical = FALSE, + flip_horizontal = FALSE, + shift_horizontal_step = FALSE, + shift_vertical_step = FALSE, + ID_fmt = "id_test_%03d", + name = "single_test", + verbose = FALSE) + expect_equal(nrow(gps), 5) + gps_centroids_dt <- data.table::as.data.table(centroids(gps), geom = "XY") + expect_identical(gps_centroids_dt$poly_ID, sprintf("id_test_%03d", seq(1:5))) + # ordering from readin for "single" is ordered first by row then col + data.table::setkeyv(b, c("sdimy", "sdimx")) # note that y ordering is still inverted + singles_x <- c(b$sdimx[6], mean(b$sdimx[c(7, 5)]), mean(b$sdimx[c(3, 4)]), b$sdimx[c(1, 2)]) + singles_y <- c(b$sdimy[6], mean(b$sdimy[c(7, 5)]), mean(b$sdimy[c(3, 4)]), b$sdimy[c(1, 2)]) + + expect_identical(round(gps_centroids_dt$x, digits = 1), singles_x) + expect_identical(round(gps_centroids_dt$y, digits = 1), singles_y) + + # try again with specified poly_ID values --------------------------------- # + + gpm2 = createGiottoPolygonsFromMask(m, + flip_vertical = FALSE, + flip_horizontal = FALSE, + shift_horizontal_step = FALSE, + shift_vertical_step = FALSE, + poly_IDs = letters[1:7], + ID_fmt = "id_test_%03d", # ignored + name = "multi_test", + verbose = FALSE) + expect_identical(gpm2$poly_ID, letters[1:7]) + gpm2_centroids_dt <- data.table::as.data.table(centroids(gpm2), geom = "XY") + data.table::setkey(b, cell_ID) + expect_identical(round(gpm2_centroids_dt$x), rev(b$sdimx)) + expect_identical(round(gpm2_centroids_dt$y), rev(b$sdimy)) + + gps2 = createGiottoPolygonsFromMask(s, + flip_vertical = FALSE, + flip_horizontal = FALSE, + shift_horizontal_step = FALSE, + shift_vertical_step = FALSE, + poly_IDs = LETTERS[1:5], + ID_fmt = "id_test_%03d", # ignored + name = "single_test", + verbose = FALSE) + expect_identical(gps2$poly_ID, LETTERS[1:5]) + gps2_centroids_dt <- data.table::as.data.table(centroids(gps2), geom = "XY") + data.table::setkeyv(b, c("sdimy", "sdimx")) # note that y ordering is still inverted + singles_x <- c(b$sdimx[6], mean(b$sdimx[c(7, 5)]), mean(b$sdimx[c(3, 4)]), b$sdimx[c(1, 2)]) + singles_y <- c(b$sdimy[6], mean(b$sdimy[c(7, 5)]), mean(b$sdimy[c(3, 4)]), b$sdimy[c(1, 2)]) + + expect_identical(round(gps2_centroids_dt$x, digits = 1), singles_x) + expect_identical(round(gps2_centroids_dt$y, digits = 1), singles_y) +}) From 54800ca00e72a2d896e4feaab7b5fe54c160e224 Mon Sep 17 00:00:00 2001 From: jiajic <72078254+jiajic@users.noreply.github.com> Date: Fri, 1 Mar 2024 12:24:39 -0500 Subject: [PATCH 10/12] chore: change examples order - also specifically show the needed columns for `data.frame-like` inputs --- R/create.R | 66 ++++++++++++++++++++------------------ man/createGiottoPolygon.Rd | 37 +++++++++++---------- 2 files changed, 55 insertions(+), 48 deletions(-) diff --git a/R/create.R b/R/create.R index 8a8de6cb..8f89e23b 100644 --- a/R/create.R +++ b/R/create.R @@ -1995,37 +1995,6 @@ setMethod( } ) -#' @rdname createGiottoPolygon -#' @examples -#' # ------- create from data.frame-like ------- # -#' shp <- system.file("extdata/toy_poly.shp", package = "GiottoClass") -#' gpoly <- createGiottoPolygon(shp, name = "test") -#' plot(gpoly) -#' gpoly_dt <- data.table::as.data.table(gpoly, geom = "XY") -#' out <- createGiottoPolygon(gpoly_dt[, .(geom, part, x, y, hole, poly_ID)], -#' name = "test") -#' plot(out) -#' -#' -#' @export -setMethod( - "createGiottoPolygon", signature("data.frame"), - function(x, - name = "cell", - calc_centroids = FALSE, - skip_eval_dfr = FALSE, - copy_dt = TRUE, - verbose = TRUE) { - createGiottoPolygonsFromDfr( - segmdfr = x, - name = name, - calc_centroids = calc_centroids, - skip_eval_dfr = skip_eval_dfr, - copy_dt = copy_dt, - verbose = verbose - ) - } -) #' @rdname createGiottoPolygon #' @param \dots additional params to pass. For character method, params pass to @@ -2079,6 +2048,41 @@ setMethod( ) +#' @rdname createGiottoPolygon +#' @examples +#' # ------- create from data.frame-like ------- # +#' shp <- system.file("extdata/toy_poly.shp", package = "GiottoClass") +#' gpoly <- createGiottoPolygon(shp, name = "test") +#' plot(gpoly) +#' gpoly_dt <- data.table::as.data.table(gpoly, geom = "XY") +#' needed_cols_dt <- gpoly_dt[, .(geom, part, x, y, hole, poly_ID)] +#' force(needed_cols_dt) +#' +#' out <- createGiottoPolygon(needed_cols_dt, +#' name = "test") +#' plot(out) +#' +#' +#' @export +setMethod( + "createGiottoPolygon", signature("data.frame"), + function(x, + name = "cell", + calc_centroids = FALSE, + skip_eval_dfr = FALSE, + copy_dt = TRUE, + verbose = TRUE) { + createGiottoPolygonsFromDfr( + segmdfr = x, + name = name, + calc_centroids = calc_centroids, + skip_eval_dfr = skip_eval_dfr, + copy_dt = copy_dt, + verbose = verbose + ) + } +) + #' @rdname createGiottoPolygon #' @param maskfile path to mask file diff --git a/man/createGiottoPolygon.Rd b/man/createGiottoPolygon.Rd index 0ff740a1..8d038cb7 100644 --- a/man/createGiottoPolygon.Rd +++ b/man/createGiottoPolygon.Rd @@ -4,8 +4,8 @@ \alias{createGiottoPolygon} \alias{createGiottoPolygon,SpatVector-method} \alias{createGiottoPolygon,SpatRaster-method} -\alias{createGiottoPolygon,data.frame-method} \alias{createGiottoPolygon,character-method} +\alias{createGiottoPolygon,data.frame-method} \alias{createGiottoPolygonsFromMask} \alias{createGiottoPolygonsFromDfr} \alias{createGiottoPolygonsFromGeoJSON} @@ -31,6 +31,8 @@ verbose = TRUE ) +\S4method{createGiottoPolygon}{character}(x, ...) + \S4method{createGiottoPolygon}{data.frame}( x, name = "cell", @@ -40,8 +42,6 @@ verbose = TRUE ) -\S4method{createGiottoPolygon}{character}(x, ...) - createGiottoPolygonsFromMask( maskfile, mask_method = c("guess", "single", "multiple"), @@ -111,15 +111,15 @@ poly_IDs. Default = "cell_". See \emph{ID_fmt} section.} \item{remove_unvalid_polygons}{remove unvalid polygons (default: TRUE)} +\item{\dots}{additional params to pass. For character method, params pass to +SpatRaster or SpatVector methods, depending on whether x was a filepath to +a maskfile or a spatial file (ex: wkt, shp, GeoJSON) respectively.} + \item{skip_eval_dfr}{(default FALSE) skip evaluation of provided dataframe} \item{copy_dt}{(default TRUE) if segmdfr is provided as dt, this determines whether a copy is made} -\item{\dots}{additional params to pass. For character method, params pass to -SpatRaster or SpatVector methods, depending on whether x was a filepath to -a maskfile or a spatial file (ex: wkt, shp, GeoJSON) respectively.} - \item{maskfile}{path to mask file} \item{segmdfr}{data.frame-like object with polygon coordinate information (x, y, poly_ID) @@ -177,16 +177,6 @@ a \code{sprintf()} \code{fmt} param input instead. (ie: \code{ID_fmt = "cell_\%0 } \examples{ -# ------- create from data.frame-like ------- # -shp <- system.file("extdata/toy_poly.shp", package = "GiottoClass") -gpoly <- createGiottoPolygon(shp, name = "test") -plot(gpoly) -gpoly_dt <- data.table::as.data.table(gpoly, geom = "XY") -out <- createGiottoPolygon(gpoly_dt[, .(geom, part, x, y, hole, poly_ID)], - name = "test") -plot(out) - - # \%\%\%\%\%\%\%\%\% `createGiottoPolygon()` examples \%\%\%\%\%\%\%\%\% # # ------- create from a mask image ------- # m <- system.file("extdata/toy_mask_multi.tif", package = "GiottoClass") @@ -207,6 +197,19 @@ gp2 <- createGiottoPolygon(shp, name = "test") plot(gp2, col = grDevices::hcl.colors(7)) +# ------- create from data.frame-like ------- # +shp <- system.file("extdata/toy_poly.shp", package = "GiottoClass") +gpoly <- createGiottoPolygon(shp, name = "test") +plot(gpoly) +gpoly_dt <- data.table::as.data.table(gpoly, geom = "XY") +needed_cols_dt <- gpoly_dt[, .(geom, part, x, y, hole, poly_ID)] +force(needed_cols_dt) + +out <- createGiottoPolygon(needed_cols_dt, + name = "test") +plot(out) + + # \%\%\%\%\%\%\%\%\% `createGiottoPolygonsFromMask()` examples \%\%\%\%\%\%\%\%\% # mask_multi <- system.file("extdata/toy_mask_multi.tif", package = "GiottoClass") From 107c43d2aff033483b571d5f4251fd8b47e12573 Mon Sep 17 00:00:00 2001 From: jiajic <72078254+jiajic@users.noreply.github.com> Date: Fri, 1 Mar 2024 12:38:46 -0500 Subject: [PATCH 11/12] chore: update covr GHA --- .github/workflows/covr.yml | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/.github/workflows/covr.yml b/.github/workflows/covr.yml index 4187bce9..e8e52a4e 100644 --- a/.github/workflows/covr.yml +++ b/.github/workflows/covr.yml @@ -27,14 +27,6 @@ jobs: with: use-public-rspm: true - - name: Set up dependencies (GiottoData) - run: | - suppressWarnings({ - install.packages("remotes") - remotes::install_github("drieslab/GiottoData", build = FALSE) - }) - shell: Rscript {0} - - name: Set up dependencies (general) uses: r-lib/actions/setup-r-dependencies@v2 env: @@ -44,6 +36,7 @@ jobs: dependencies: '"hard"' # do not use suggested dependencies install-pandoc: false extra-packages: | + github::drieslab/GiottoData any::rcmdcheck any::testthat any::rlang From 57cfcb9c0a815ebf24c5e5464e445a16960f46cb Mon Sep 17 00:00:00 2001 From: jiajic <72078254+jiajic@users.noreply.github.com> Date: Fri, 1 Mar 2024 12:43:17 -0500 Subject: [PATCH 12/12] chore: update NEWS.md --- NEWS.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/NEWS.md b/NEWS.md index 99534387..826c6631 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,14 +1,16 @@ -# GiottoClass 0.2.2 +# GiottoClass 0.2.2 (2024/03/01) ## bug fixes - fix `createGiottoPolygonsFromMask()` IDs being applied out of sync to mask values +- fix `createGiottoPolygon()` `character` method dispatch for `raster` inputs - remove unused `fix_multipart` param in `createGiottoPolygonsFromMask()` -- fix `giottoPolygon` ID cacheing after `rbind()` +- fix `giottoPolygon` ID caching after `rbind()` ## enhancements -- `createGiottoPolygonsFromMask()` now has `ID_fmt` param for finer control of automatic poly_ID generation +- `createGiottoPolygonsFromMask()` now has `ID_fmt` param for finer control of automatic `poly_ID` generation +- `.flip_spatvect()` internal for flipping `SpatVector` across arbitrary x and y vals # GiottoClass 0.2.1 (2024/02/28)