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 1/6] 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 2/6] 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 3/6] 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 4/6] 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 5/6] 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 6/6] 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")