From a5fe9ee7e0418e259c92a1cc47c4a8402653a661 Mon Sep 17 00:00:00 2001
From: Insang Song Package setup
path = ".",
fields = list(
Package = params$package_name,
- Version = "0.0.2.09192023",
+ Version = "0.0.2.10022023",
Title = "Scalable R geospatial computation",
Description = "A package for scalable geospatial computation for environmental health research",
`Authors@R` = person(
@@ -386,10 +386,13 @@ Package setup
# usethis::use_package("stars") # Default is "Imports"
usethis::use_package("terra") # Default is "Imports"
usethis::use_package("rlang") # Default is "Imports"
+usethis::use_package("testthat")
usethis::use_package("logr", "Suggests") # Default is "Imports"
usethis::use_package("future") # Default is "Imports"
usethis::use_package("future.apply") # Default is "Imports"
usethis::use_package("igraph", "Suggests") # Default is "Imports"
+usethis::use_package("withr", "Suggests")
+usethis::use_package("covr")
usethis::use_package("future.batchtools", "Suggests") # Default is "Imports"
# usethis::use_gpl3_license()
@@ -438,6 +441,41 @@ Create functions
return("raster")
}
}
+
+testthat::test_that("What package does the input object belong?",
+{
+ withr::local_package("stars")
+ withr::local_package("terra")
+ withr::local_options(list(sf_use_s2 = FALSE))
+ bcsd_path = system.file(package = "stars", "nc/bcsd_obs_1999.nc")
+ bcsd_stars = stars::read_stars(bcsd_path)
+
+ packbound_stars = check_packbound(bcsd_stars)
+ sprast_bcsd = terra::rast(bcsd_path)
+ packbound_terra = check_packbound(sprast_bcsd)
+
+ testthat::expect_equal(packbound_stars, "sf")
+ testthat::expect_equal(packbound_terra, "terra")
+})
+
+
+testthat::test_that("What package does the input object belong?",
+{
+ withr::local_package("stars")
+ withr::local_package("terra")
+ withr::local_options(list(sf_use_s2 = FALSE))
+ data(stars_sentinel2)
+ nc = system.file(package = "sf", "shape/nc.shp")
+ nc = sf::read_sf(nc)
+
+ datatype_stars = check_datatype(stars_sentinel2)
+ datatype_sf = check_datatype(nc)
+
+ testthat::expect_equal(datatype_stars, "raster")
+ testthat::expect_equal(datatype_sf, "vector")
+})
+## pr, tas,
+## Test passed
## Test passed
#' @title Switch spatial data class
#' @description Convert stars into SpatRaster and vice versa; sf into SpatVector and vice versa.
#' @author Insang Song
@@ -1263,7 +1301,7 @@
Documenting the package and building
the package. It may also be a good idea to check the package from within
this file.
litr::document() # <-- use instead of devtools::document()
-
+devtools::test()
devtools::build()
devtools::install(build_vignettes = TRUE, dependencies = F)
# devtools::check(document = FALSE)
@@ -1299,6 +1337,15 @@ Documenting the package and building
## Writing 'sp_indexing.Rd'
## Writing 'switch_packbound.Rd'
## Writing 'validate_and_repair_vectors.Rd'
+## ℹ Testing scomps
+## ✔ | F W S OK | Context
+##
+## ⠏ | 0 | tests pr, tas,
+##
+## ✔ | 4 | tests
+##
+## ══ Results ═════════════════════════════════════════════════════════════════════
+## [ FAIL 0 | WARN 0 | SKIP 0 | PASS 4 ]
## ── R CMD build ─────────────────────────────────────────────────────────────────
## * checking for file ‘/Users/songi2/Documents/GitHub/Scalable_GIS/scomps/DESCRIPTION’ ... OK
## * preparing ‘scomps’:
@@ -1314,11 +1361,17 @@ Documenting the package and building
## * checking for LF line-endings in source and make files and shell scripts
## * checking for empty or unneeded directories
## Removed empty directory ‘scomps/build’
-## * building ‘scomps_0.0.2.09192023.tar.gz’
+## * building ‘scomps_0.0.2.10022023.tar.gz’
## Warning: invalid uid value replaced by that for user 'nobody'
## Warning: invalid gid value replaced by that for user 'nobody'
-## [1] "/Users/songi2/Documents/GitHub/Scalable_GIS/scomps_0.0.2.09192023.tar.gz"
+## [1] "/Users/songi2/Documents/GitHub/Scalable_GIS/scomps_0.0.2.10022023.tar.gz"
+## curl (5.0.2 -> 5.1.0) [CRAN]
+## Installing 1 packages: curl
+## Installing package into '/Users/songi2/Library/R/arm64/4.3/library'
+## (as 'lib' is unspecified)
##
+## The downloaded binary packages are in
+## /var/folders/58/7rn_bn5d6k3_cxwnzdhswpz4n0z2n9/T//RtmpLf04ac/downloaded_packages
## ── R CMD build ─────────────────────────────────────────────────────────────────
## * checking for file ‘/Users/songi2/Documents/GitHub/Scalable_GIS/scomps/DESCRIPTION’ ... OK
## * preparing ‘scomps’:
@@ -1334,18 +1387,19 @@ Documenting the package and building
## * checking for LF line-endings in source and make files and shell scripts
## * checking for empty or unneeded directories
## Removed empty directory ‘scomps/build’
-## * building ‘scomps_0.0.2.09192023.tar.gz’
+## * building ‘scomps_0.0.2.10022023.tar.gz’
## Warning: invalid uid value replaced by that for user 'nobody'
## Warning: invalid gid value replaced by that for user 'nobody'
##
## Running /Library/Frameworks/R.framework/Resources/bin/R CMD INSTALL \
-## /var/folders/58/7rn_bn5d6k3_cxwnzdhswpz4n0z2n9/T//RtmpP7s4LX/scomps_0.0.2.09192023.tar.gz \
+## /var/folders/58/7rn_bn5d6k3_cxwnzdhswpz4n0z2n9/T//RtmpLf04ac/scomps_0.0.2.10022023.tar.gz \
## --install-tests
## * installing to library ‘/Users/songi2/Library/R/arm64/4.3/library’
## * installing *source* package ‘scomps’ ...
## ** using staged installation
## ** R
## ** inst
+## ** tests
## ** byte-compile and prepare package for lazy loading
## ** help
## *** installing help indices
diff --git a/scomps_rmarkdown_litr.rmd b/scomps_rmarkdown_litr.rmd
index 5cc40fed..f3767faf 100644
--- a/scomps_rmarkdown_litr.rmd
+++ b/scomps_rmarkdown_litr.rmd
@@ -21,7 +21,7 @@ usethis::create_package(
path = ".",
fields = list(
Package = params$package_name,
- Version = "0.0.2.09192023",
+ Version = "0.0.2.10022023",
Title = "Scalable R geospatial computation",
Description = "A package for scalable geospatial computation for environmental health research",
`Authors@R` = person(
@@ -39,10 +39,13 @@ usethis::use_package("sf") # Default is "Imports"
# usethis::use_package("stars") # Default is "Imports"
usethis::use_package("terra") # Default is "Imports"
usethis::use_package("rlang") # Default is "Imports"
+usethis::use_package("testthat")
usethis::use_package("logr", "Suggests") # Default is "Imports"
usethis::use_package("future") # Default is "Imports"
usethis::use_package("future.apply") # Default is "Imports"
usethis::use_package("igraph", "Suggests") # Default is "Imports"
+usethis::use_package("withr", "Suggests")
+usethis::use_package("covr")
usethis::use_package("future.batchtools", "Suggests") # Default is "Imports"
# usethis::use_gpl3_license()
@@ -103,6 +106,41 @@ check_datatype <- function(input) {
}
```
+```{r}
+testthat::test_that("What package does the input object belong?",
+{
+ withr::local_package("stars")
+ withr::local_package("terra")
+ withr::local_options(list(sf_use_s2 = FALSE))
+ bcsd_path = system.file(package = "stars", "nc/bcsd_obs_1999.nc")
+ bcsd_stars = stars::read_stars(bcsd_path)
+
+ packbound_stars = check_packbound(bcsd_stars)
+ sprast_bcsd = terra::rast(bcsd_path)
+ packbound_terra = check_packbound(sprast_bcsd)
+
+ testthat::expect_equal(packbound_stars, "sf")
+ testthat::expect_equal(packbound_terra, "terra")
+})
+
+
+testthat::test_that("What package does the input object belong?",
+{
+ withr::local_package("stars")
+ withr::local_package("terra")
+ withr::local_options(list(sf_use_s2 = FALSE))
+ data(stars_sentinel2)
+ nc = system.file(package = "sf", "shape/nc.shp")
+ nc = sf::read_sf(nc)
+
+ datatype_stars = check_datatype(stars_sentinel2)
+ datatype_sf = check_datatype(nc)
+
+ testthat::expect_equal(datatype_stars, "raster")
+ testthat::expect_equal(datatype_sf, "vector")
+})
+```
+
```{r}
#' @title Switch spatial data class
#' @description Convert stars into SpatRaster and vice versa; sf into SpatVector and vice versa.
@@ -1015,7 +1053,7 @@ We finish by running commands that will document, build, and install the package
```{r}
litr::document() # <-- use instead of devtools::document()
-
+devtools::test()
devtools::build()
devtools::install(build_vignettes = TRUE, dependencies = F)
# devtools::check(document = FALSE)
diff --git a/scomps_0.0.1.09032023.tar.gz b/tarballs/scomps_0.0.1.09032023.tar.gz
similarity index 100%
rename from scomps_0.0.1.09032023.tar.gz
rename to tarballs/scomps_0.0.1.09032023.tar.gz
diff --git a/scomps_0.0.2.09152023.tar.gz b/tarballs/scomps_0.0.2.09152023.tar.gz
similarity index 100%
rename from scomps_0.0.2.09152023.tar.gz
rename to tarballs/scomps_0.0.2.09152023.tar.gz
diff --git a/scomps_0.0.2.09192023.tar.gz b/tarballs/scomps_0.0.2.09192023.tar.gz
similarity index 100%
rename from scomps_0.0.2.09192023.tar.gz
rename to tarballs/scomps_0.0.2.09192023.tar.gz
diff --git a/tarballs/scomps_0.0.2.10022023.tar.gz b/tarballs/scomps_0.0.2.10022023.tar.gz
new file mode 100644
index 0000000000000000000000000000000000000000..51ae9e2314f70c20edddb53a404d328ec16182c2
GIT binary patch
literal 22366
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
Date: Tue, 3 Oct 2023 11:51:03 -0400
Subject: [PATCH 03/21] Function restructuring - functions are reorganized in a
fewer R files - workflow yaml extension fix - Added one test, fixed errors in
previous tests
---
...check-standard.yml => check-standard.yaml} | 0
.github/workflows/codecov.yaml | 18 +-
scomps/DESCRIPTION | 4 +-
scomps/R/aw_covariates.R | 68 ----
scomps/R/calculate_sedc.R | 48 ---
scomps/R/check.R | 139 +++++++
scomps/R/check_bbox.R | 34 --
scomps/R/check_crs.R | 27 --
scomps/R/check_crs2.R | 29 --
scomps/R/check_packbound.R | 26 --
scomps/R/check_within_reference.R | 27 --
scomps/R/clip_as_extent.R | 37 --
scomps/R/clip_as_extent_ras.R | 20 -
scomps/R/clip_as_extent_ras2.R | 20 -
scomps/R/distribute_process.R | 39 --
scomps/R/extract_with.R | 33 --
scomps/R/extract_with_buffer.R | 50 ---
scomps/R/extract_with_buffer.flat.R | 23 --
scomps/R/extract_with_buffer.kernel.R | 27 --
scomps/R/extract_with_polygons.R | 55 ---
scomps/R/{sp_indexing.R => indexing.R} | 0
...ons.R => interpret_computational_domain.R} | 38 ++
scomps/R/{initate_log.R => logging.R} | 0
.../R/{set_clip_extent.R => preprocessing.R} | 11 +
scomps/R/processing.R | 372 ++++++++++++++++++
scomps/R/rast_short.R | 12 -
.../R/{switch_packbound.R => switch_format.R} | 0
...lidate_and_repair_vectors.R => validate.R} | 0
scomps/tests/testthat/tests.R | 35 +-
scomps_rmarkdown_litr.html | 100 +++--
scomps_rmarkdown_litr.rmd | 83 ++--
tarballs/scomps_0.0.2.10032023.tar.gz | Bin 0 -> 21871 bytes
32 files changed, 727 insertions(+), 648 deletions(-)
rename .github/workflows/{check-standard.yml => check-standard.yaml} (100%)
delete mode 100644 scomps/R/aw_covariates.R
delete mode 100644 scomps/R/calculate_sedc.R
create mode 100644 scomps/R/check.R
delete mode 100644 scomps/R/check_bbox.R
delete mode 100644 scomps/R/check_crs.R
delete mode 100644 scomps/R/check_crs2.R
delete mode 100644 scomps/R/check_packbound.R
delete mode 100644 scomps/R/check_within_reference.R
delete mode 100644 scomps/R/clip_as_extent.R
delete mode 100644 scomps/R/clip_as_extent_ras.R
delete mode 100644 scomps/R/clip_as_extent_ras2.R
delete mode 100644 scomps/R/distribute_process.R
delete mode 100644 scomps/R/extract_with.R
delete mode 100644 scomps/R/extract_with_buffer.R
delete mode 100644 scomps/R/extract_with_buffer.flat.R
delete mode 100644 scomps/R/extract_with_buffer.kernel.R
delete mode 100644 scomps/R/extract_with_polygons.R
rename scomps/R/{sp_indexing.R => indexing.R} (100%)
rename scomps/R/{get_computational_regions.R => interpret_computational_domain.R} (83%)
rename scomps/R/{initate_log.R => logging.R} (100%)
rename scomps/R/{set_clip_extent.R => preprocessing.R} (77%)
create mode 100644 scomps/R/processing.R
delete mode 100644 scomps/R/rast_short.R
rename scomps/R/{switch_packbound.R => switch_format.R} (100%)
rename scomps/R/{validate_and_repair_vectors.R => validate.R} (100%)
create mode 100644 tarballs/scomps_0.0.2.10032023.tar.gz
diff --git a/.github/workflows/check-standard.yml b/.github/workflows/check-standard.yaml
similarity index 100%
rename from .github/workflows/check-standard.yml
rename to .github/workflows/check-standard.yaml
diff --git a/.github/workflows/codecov.yaml b/.github/workflows/codecov.yaml
index 260f7f27..30ba6e81 100644
--- a/.github/workflows/codecov.yaml
+++ b/.github/workflows/codecov.yaml
@@ -1,12 +1,12 @@
-name: R Package CI
+on:
+ push:
+ branches:
+ - main
+ pull_request:
+ branches:
+ - main
-#on:
-# push:
-# branches:
-# - main
-# pull_request:
-# branches:
-# - main
+name: R Package CI
jobs:
build:
@@ -27,7 +27,7 @@ jobs:
path: |
~/.cache/R
~/.local/share/R
- key: dependencies-${{ runner.os }}-${{ hashFiles('**/DESCRIPTION') }}
+ key: dependencies-${{ runner.os }}-${{ hashFiles('**/scomps/DESCRIPTION') }}
restore-keys: |
dependencies-${{ runner.os }}-
diff --git a/scomps/DESCRIPTION b/scomps/DESCRIPTION
index 58d77a8a..2f72afbd 100644
--- a/scomps/DESCRIPTION
+++ b/scomps/DESCRIPTION
@@ -1,6 +1,6 @@
Package: scomps
Title: Scalable R geospatial computation
-Version: 0.0.2.10022023
+Version: 0.0.2.10032023
Authors@R:
person("Insang", "Song", , "geoissong@gmail.com", role = c("aut", "cre"),
comment = c(ORCID = "0000-0001-8732-3256"))
@@ -30,4 +30,4 @@ Suggests:
VignetteBuilder: knitr
Config/testthat/edition: 3
LitrVersionUsed: 0.9.0
-LitrId: 820777ef4091f4dab083c17458b45127
+LitrId: 7c9484dd9127c939a90d433ec83cd305
diff --git a/scomps/R/aw_covariates.R b/scomps/R/aw_covariates.R
deleted file mode 100644
index 118c041a..00000000
--- a/scomps/R/aw_covariates.R
+++ /dev/null
@@ -1,68 +0,0 @@
-# Generated from scomps_rmarkdown_litr.rmd: do not edit by hand
-
-#' Computing area weighted covariates using two polygon sf or SpatVector objects
-#' @param poly_in A sf/SpatVector object at weighted means will be calculated.
-#' @param poly_weight A sf/SpatVector object from which weighted means will be calculated.
-#' @param id_poly_in character(1). The unique identifier of each polygon in poly_in
-#' @return A data.frame with all numeric fields of area-weighted means.
-#' @description When poly_in and poly_weight are different classes, poly_weight will be converted to the class of poly_in.
-#' @author Insang Song \email{geoissong@@gmail.com}
-#' @examples
-#' # package
-#' library(sf)
-#' library(tictoc)
-#'
-#' # run
-#' nc = st_read(system.file("shape/nc.shp", package="sf"))
-#' nc = st_transform(nc, 5070)
-#' pp = st_sample(nc, size = 300)
-#' pp[["id"]] = seq(1,nrow(pp))
-#' st_crs(pp) = "EPSG:5070"
-#' ppb = st_buffer(pp, nQuadSegs=180, dist = units::set_units(20, 'km'))
-#'
-#' tic()
-#' ppb_nc_aw = aw_covariates(ppb, nc, 'id')
-#' toc()
-#' summary(ppb_nc_aw)
-#' #### Example of aw_covariates ends ####
-#' @export
-aw_covariates <- function(poly_in, poly_weight, id_poly_in = "ID") {
- stopifnot("Inputs have invalid classes.\n" =
- is(poly_in, "sf") || is(poly_weight, "sf") || is(poly_in, "SpatVector") || is(poly_weight, "SpatVector"))
- #check_crs()
- aw_covariates.terra = function(poly_in, poly_weight, id_poly_in = id_poly_in) {
-
- poly_intersected = terra::intersect(poly_in, poly_weight)
- poly_intersected[["area_segment_"]] = terra::expanse(poly_intersected)
- poly_intersected = data.frame(poly_intersected) |>
- dplyr::group_by(!!rlang::sym(id_poly_in)) |>
- dplyr::summarize(across(is.numeric,
- ~weighted.mean(., w = area_segment_))) |>
- dplyr::ungroup()
- return(poly_intersected)
- }
-
- class_poly_in = check_packbound(poly_in)
- class_poly_weight = check_packbound(poly_weight)
-
- if (class_poly_in != class_poly_weight) {
- class_poly_weight = as(class_poly_weight, class(class_poly_in)[1])
- }
-
- switch(class_poly_in,
- sf = sf::st_interpolate_aw(poly_weight, poly_in, extensive = FALSE),
- terra = aw_covariates.terra(poly_in, poly_weight, id_poly_in = id_poly_in))
-
-}
-
-# ncbuf = terra::intersect(vect(ppb), vect(nc))
-# ncbuf_a = ncbuf
-# ncbuf_a$segarea = expanse(ncbuf_a)
-# ncbuf_k = data.frame(ncbuf_a) |>
-# dplyr::group_by(id) |>
-# dplyr::summarize(across(is.numeric,
-# ~weighted.mean(., w = segarea))) |>
-# dplyr::ungroup()
-
-#ncbufagg = terra::aggregate(ncbuf, by = 'id', fun = weighted.mean, w = ncbuf_a$segarea)
-
diff --git a/scomps/R/calculate_sedc.R b/scomps/R/calculate_sedc.R
deleted file mode 100644
index 6aed8d49..00000000
--- a/scomps/R/calculate_sedc.R
+++ /dev/null
@@ -1,48 +0,0 @@
-# Generated from scomps_rmarkdown_litr.rmd: do not edit by hand
-
-#' Calculate SEDC covariates
-#' @param point_from SpatVector object. Locations where the sum of SEDCs are calculated.
-#' @param point_to SpatVector object. Locations where each SEDC is calculated.
-#' @param sdec_bandwidth numeric(1). Distance at which the source concentration is reduced to exp(-3) (approximately 95 %)
-#' @param threshold numeric(1). For computational efficiency, the nearest points in threshold will be selected
-#' @param target_fields character(varying). Field names in characters.
-#' @description NOTE: sf implementation is pending. Only available for terra.
-#' @author Insang Song
-#' @export
-calculate_sedc <- function(point_from, point_to, id, sedc_bandwidth, threshold, target_fields) {
- # define sources, set SEDC exponential decay range
- len_point_from = seq_len(nrow(point_from))
- len_point_to = seq_len(nrow(point_to))
-
- point_from$from_id = len_point_from
- # select egrid_v only if closer than 3e5 meters from each aqs
- point_from_buf = terra::buffer(point_from_buf, threshold = threshold, quadsegs = 90)
- point_to = point_to[point_from_buf,]
- point_to$to_id = len_point_to
-
- # near features with distance argument: only returns integer indices
- near_from_to = terra::nearby(point_from, point_to, distance = threshold)
- # attaching actual distance
- dist_near_to = terra::distance(point_from, point_to)
- dist_near_to_df = as.vector(dist_near_to)
- # adding integer indices
- dist_near_to_tdf = expand.grid(from_id = len_point_from, to_id = len_point_to)
- dist_near_to_df = cbind(dist_near_to_tdf, dist = dist_near_to_df)
-
- # summary
- near_from_to = near_from_to |>
- as_tibble() |>
- left_join(data.frame(point_from)) |>
- left_join(data.frame(point_to)) |>
- left_join(dist_near_to_df) |>
- # per the definition in https://mserre.sph.unc.edu/BMElab_web/SEDCtutorial/index.html
- # exp(-3) is about 0.05
- mutate(w_sedc = exp((-3 * dist) / sedc_bandwidth)) |>
- group_by(!!sym(id)) |>
- summarize(across(all_of(target_fields), list(sedc = ~sum(w_sedc * ., na.rm = TRUE)))) |>
- ungroup()
-
- invisible(near_from_to)
-
-}
-
diff --git a/scomps/R/check.R b/scomps/R/check.R
new file mode 100644
index 00000000..3fed02f7
--- /dev/null
+++ b/scomps/R/check.R
@@ -0,0 +1,139 @@
+# Generated from scomps_rmarkdown_litr.rmd: do not edit by hand
+
+#' @title Return the package the input object is based on
+#' @description Detect whether the input object is sf or Spat* object.
+#' @author Insang Song
+#' @param input Spat* in terra or sf object.
+#' @return A character object; one of 'terra' and 'sf'
+#' @export
+check_packbound <- function(input) {
+ # cl_inobj = class(input)[1]
+ stopifnot("Input should be one of sf or Spat* object.\n" = any(is(input, "sf"), is(input, "stars"), is(input, "SpatVector"), is(input, "SpatRaster")))
+ if (is(input, "SpatVector") || is(input, "SpatRaster")) {
+ return("terra")
+ }
+ return("sf")
+}
+
+check_datatype <- function(input) {
+ stopifnot("Input should be one of sf or Spat* object.\n" = any(is(input, "sf"), is(input, "stars"), is(input, "SpatVector"), is(input, "SpatRaster")))
+ if (any(is(input, "SpatVector"), is(input, "sf"))) {
+ return("vector")
+ }
+ if (any(is(input, "SpatRaster"), is(input, "stars"))) {
+ return("raster")
+ }
+}
+
+#' @title check_crs2: Coordinate system checker
+#' @description The input is checked whether its coordinate system is present. If not, it is reprojected to EPSG:5179.
+#' @param input Input object one of sf or terra::Spat* object
+#' @return A (reprojected) sf or SpatVector object.
+#' @export
+check_crs2 <- function(input, crs_standard = "EPSG:4326") {
+ check_crs.sf <- function(input){
+ if (is.na(st_crs(input))){cat('Please check the coordinate system or its EPSG code of your input object.'); return(NULL)}
+ if (sf::st_crs(input)$epsg == crs_standard ) {
+ return(input)
+ }
+ input_transformed <- sf::st_transform(input, sf::st_crs(crs_standard))
+ return(input_transformed)
+ }
+
+ check_crs.terra <- function(input) {
+ if (terra::crs(input, describe = TRUE)$code == crs_standard) {
+ return(input)
+ }
+ input_transformed = terra::project(input, terra::crs(crs_standard))
+ return(input_transformed)
+ }
+ detected = check_packbound(input)
+ switch(detected,
+ terra = check_crs.terra(input),
+ sf = check_crs.sf(input))
+ }
+
+#' Check if the data extent is inside the reference bounding box
+#'
+#' @description One of the most common errors in spatial computation is rooted in the entirely or partly incomparable spatial extents of input datasets. This function returns whether your data is inside the target computational extent. It is assumed that you know and have the exact computational region. This function will return TRUE if the reference region completely contains your data's extent and FALSE otherwise.
+#' @param data_query sf*/stars/SpatVector/SpatRaster object.
+#' @param reference sf*/stars/SpatVector/SpatRaster object or a named numeric vector with four names (xmin, ymin, xmax, and ymax).
+#' @param reference_crs Well-known-text-formatted or EPSG code of the reference's coordinate system. Only required when a named numeric vector is passed to reference.
+#' @return TRUE (the queried data extent is completely within the reference bounding box) or FALSE
+#' @author Insang Song \email{geoissong@@gmail.com}
+#'
+#' @export
+check_bbox <- function(
+ data_query, reference, reference_crs = NULL
+) {
+ if (is.numeric(reference) && is.null(reference_crs)) {
+ stop("CRS should be entered when the reference extent is a vector.\n")
+ }
+ if (is.numeric(reference) && !is.null(reference_crs)) {
+ reference = sf::st_as_sfc(sf::st_bbox(reference), crs = reference_crs)
+ }
+ query_crs = check_crs(data_query)
+ ref_crs = check_crs(reference)
+ if (is.na(query_crs) || is.null(query_crs)) {
+ stop("The dataset you queried has no CRS. Please make sure your dataset has the correct CRS.\n")
+ }
+
+ # ...
+
+ check_result
+ return(check_result)
+}
+
+
+
+#' Check Coordinate Reference System
+#' @param x sf/stars/SpatVector/SpatRaster object.
+#' @return A st_crs or crs object.
+#' @description
+#' @author Insang Song \email{geoissong@@gmail.com}
+#' @examples
+#' # data
+#' install.packages('spData')
+#' library(spData)
+#' data(us_states)
+#' check_crs(us_states)
+#'
+#' @export
+check_crs <- function(x) {
+ stopifnot("Input is invalid.\n" = (is(x, "sf") || is(x, "stars") || is(x, "SpatVector") || is(x, "SpatRaster")))
+
+ if (is(x, "sf") || is(x, "stars")) {
+ crs_wkt = sf::st_crs(x)
+ } else {
+ crs_wkt = terra::crs(x)
+ }
+
+ stopifnot("No CRS is defined in the input. Please consult the metadata or the data source.\n" = !is.na(crs_wkt) || crs_wkt != "")
+ return(crs_wkt)
+}
+
+#' Check if the boundary of the vector/raster object is inside the reference
+#' @param input_object sf/stars/SpatVector/SpatRaster object.
+#' @param reference sf/stars/SpatVector/SpatRaster object.
+#' @return logical
+#' @author Insang Song \email{geoissong@@gmail.com}
+#' @export
+check_within_reference <- function(input_object, reference) {
+ stopifnot("Input is invalid.\n" = (is(x, "sf") || is(x, "stars") || is(x, "SpatVector") || is(x, "SpatRaster")))
+ stopifnot("Reference is invalid.\n" = (is(x, "sf") || is(x, "stars") || is(x, "SpatVector") || is(x, "SpatRaster")))
+
+ bbox_input <- input_object
+ sf::st_bbox() |>
+ sf::st_as_sfc()
+
+ bbox_reference = reference |>
+ sf::st_bbox() |>
+ sf::st_as_sfc()
+
+ iswithin = sf::st_covered_by(bbox_input, bbox_reference)
+ iswithin = length(iswithin[[1]])
+ iswithin = (iswithin == 1)
+ invisible(iswithin)
+}
+
+
diff --git a/scomps/R/check_bbox.R b/scomps/R/check_bbox.R
deleted file mode 100644
index 5f823688..00000000
--- a/scomps/R/check_bbox.R
+++ /dev/null
@@ -1,34 +0,0 @@
-# Generated from scomps_rmarkdown_litr.rmd: do not edit by hand
-
-#' Check if the data extent is inside the reference bounding box
-#'
-#' @description One of the most common errors in spatial computation is rooted in the entirely or partly incomparable spatial extents of input datasets. This function returns whether your data is inside the target computational extent. It is assumed that you know and have the exact computational region. This function will return TRUE if the reference region completely contains your data's extent and FALSE otherwise.
-#' @param data_query sf*/stars/SpatVector/SpatRaster object.
-#' @param reference sf*/stars/SpatVector/SpatRaster object or a named numeric vector with four names (xmin, ymin, xmax, and ymax).
-#' @param reference_crs Well-known-text-formatted or EPSG code of the reference's coordinate system. Only required when a named numeric vector is passed to reference.
-#' @return TRUE (the queried data extent is completely within the reference bounding box) or FALSE
-#' @author Insang Song \email{geoissong@@gmail.com}
-#'
-#' @export
-check_bbox <- function(
- data_query, reference, reference_crs = NULL
-) {
- if (is.numeric(reference) && is.null(reference_crs)) {
- stop("CRS should be entered when the reference extent is a vector.\n")
- }
- if (is.numeric(reference) && !is.null(reference_crs)) {
- reference = sf::st_as_sfc(sf::st_bbox(reference), crs = reference_crs)
- }
- query_crs = check_crs(data_query)
- ref_crs = check_crs(reference)
- if (is.na(query_crs) || is.null(query_crs)) {
- stop("The dataset you queried has no CRS. Please make sure your dataset has the correct CRS.\n")
- }
-
- # ...
-
- check_result
- return(check_result)
-}
-
-
diff --git a/scomps/R/check_crs.R b/scomps/R/check_crs.R
deleted file mode 100644
index 6a31ba76..00000000
--- a/scomps/R/check_crs.R
+++ /dev/null
@@ -1,27 +0,0 @@
-# Generated from scomps_rmarkdown_litr.rmd: do not edit by hand
-
-#' Check Coordinate Reference System
-#' @param x sf/stars/SpatVector/SpatRaster object.
-#' @return A st_crs or crs object.
-#' @description
-#' @author Insang Song \email{geoissong@@gmail.com}
-#' @examples
-#' # data
-#' install.packages('spData')
-#' library(spData)
-#' data(us_states)
-#' check_crs(us_states)
-#'
-#' @export
-check_crs <- function(x) {
- stopifnot("Input is invalid.\n" = (is(x, "sf") || is(x, "stars") || is(x, "SpatVector") || is(x, "SpatRaster")))
-
- if (is(x, "sf") || is(x, "stars")) {
- crs_wkt = sf::st_crs(x)
- } else {
- crs_wkt = terra::crs(x)
- }
-
- stopifnot("No CRS is defined in the input. Please consult the metadata or the data source.\n" = !is.na(crs_wkt) || crs_wkt != "")
- return(crs_wkt)
-}
diff --git a/scomps/R/check_crs2.R b/scomps/R/check_crs2.R
deleted file mode 100644
index 2852796a..00000000
--- a/scomps/R/check_crs2.R
+++ /dev/null
@@ -1,29 +0,0 @@
-# Generated from scomps_rmarkdown_litr.rmd: do not edit by hand
-
-#' @title check_crs2: Coordinate system checker
-#' @description The input is checked whether its coordinate system is present. If not, it is reprojected to EPSG:5179.
-#' @param input Input object one of sf or terra::Spat* object
-#' @return A (reprojected) sf or SpatVector object.
-#' @export
-check_crs2 <- function(input, crs_standard = "EPSG:4326") {
- check_crs.sf <- function(input){
- if (is.na(st_crs(input))){cat('Please check the coordinate system or its EPSG code of your input object.'); return(NULL)}
- if (sf::st_crs(input)$epsg == crs_standard ) {
- return(input)
- }
- input_transformed <- sf::st_transform(input, sf::st_crs(crs_standard))
- return(input_transformed)
- }
-
- check_crs.terra <- function(input) {
- if (terra::crs(input, describe = TRUE)$code == crs_standard) {
- return(input)
- }
- input_transformed = terra::project(input, terra::crs(crs_standard))
- return(input_transformed)
- }
- detected = check_packbound(input)
- switch(detected,
- terra = check_crs.terra(input),
- sf = check_crs.sf(input))
- }
diff --git a/scomps/R/check_packbound.R b/scomps/R/check_packbound.R
deleted file mode 100644
index 4de77af7..00000000
--- a/scomps/R/check_packbound.R
+++ /dev/null
@@ -1,26 +0,0 @@
-# Generated from scomps_rmarkdown_litr.rmd: do not edit by hand
-
-#' @title Return the package the input object is based on
-#' @description Detect whether the input object is sf or Spat* object.
-#' @author Insang Song
-#' @param input Spat* in terra or sf object.
-#' @return A character object; one of 'terra' and 'sf'
-#' @export
-check_packbound <- function(input) {
- # cl_inobj = class(input)[1]
- stopifnot("Input should be one of sf or Spat* object.\n" = any(is(input, "sf"), is(input, "stars"), is(input, "SpatVector"), is(input, "SpatRaster")))
- if (is(input, "SpatVector") || is(input, "SpatRaster")) {
- return("terra")
- }
- return("sf")
-}
-
-check_datatype <- function(input) {
- stopifnot("Input should be one of sf or Spat* object.\n" = any(is(input, "sf"), is(input, "stars"), is(input, "SpatVector"), is(input, "SpatRaster")))
- if (any(is(input, "SpatVector"), is(input, "sf"))) {
- return("vector")
- }
- if (any(is(input, "SpatRaster"), is(input, "stars"))) {
- return("raster")
- }
-}
diff --git a/scomps/R/check_within_reference.R b/scomps/R/check_within_reference.R
deleted file mode 100644
index 2df7c63c..00000000
--- a/scomps/R/check_within_reference.R
+++ /dev/null
@@ -1,27 +0,0 @@
-# Generated from scomps_rmarkdown_litr.rmd: do not edit by hand
-
-#' Check if the boundary of the vector/raster object is inside the reference
-#' @param input_object sf/stars/SpatVector/SpatRaster object.
-#' @param reference sf/stars/SpatVector/SpatRaster object.
-#' @return logical
-#' @author Insang Song \email{geoissong@@gmail.com}
-#' @export
-check_within_reference <- function(input_object, reference) {
- stopifnot("Input is invalid.\n" = (is(x, "sf") || is(x, "stars") || is(x, "SpatVector") || is(x, "SpatRaster")))
- stopifnot("Reference is invalid.\n" = (is(x, "sf") || is(x, "stars") || is(x, "SpatVector") || is(x, "SpatRaster")))
-
- bbox_input <- input_object
- sf::st_bbox() |>
- sf::st_as_sfc()
-
- bbox_reference = reference |>
- sf::st_bbox() |>
- sf::st_as_sfc()
-
- iswithin = sf::st_covered_by(bbox_input, bbox_reference)
- iswithin = length(iswithin[[1]])
- iswithin = (iswithin == 1)
- invisible(iswithin)
-}
-
-
diff --git a/scomps/R/clip_as_extent.R b/scomps/R/clip_as_extent.R
deleted file mode 100644
index 6cea0ca4..00000000
--- a/scomps/R/clip_as_extent.R
+++ /dev/null
@@ -1,37 +0,0 @@
-# Generated from scomps_rmarkdown_litr.rmd: do not edit by hand
-
-#' Extent clipping
-#' @description Clip input vector by the expected maximum extent of computation.
-#' @author Insang Song
-#' @param pnts sf or SpatVector object
-#' @param buffer_r numeric(1). buffer radius. this value will be automatically multiplied by 1.25
-#' @param nqsegs integer(1). the number of points per a quarter circle; SOON TO BE DEPRECATED
-#' @param target_input sf or SpatVector object to be clipped
-#' @return A clipped sf or SpatVector object.
-#' @export
-clip_as_extent <- function(pnts, buffer_r, nqsegs=NULL, target_input){
- if (any(sapply(list(pnts, buffer_r, target_input), is.null))) {
- stop("One or more required arguments are NULL. Please check.\n")
- }
- detected_pnts = check_packbound(pnts)
- detected_target = check_packbound(target_input)
-
- if (detected_pnts != detected_target) {
- warning("Inputs are not the same class.\n")
- target_input = switch(detected_target,
- sf = terra::vect(target_input),
- terra = sf::st_as_sf(target_input))
- }
-
- ext_input = set_clip_extent(pnts, buffer_r)
- cat("Clip target features with the input feature extent...\n")
- if (detected_pnts == "sf") {
- cae = ext_input |>
- sf::st_intersection(x = target_input)
- }
- if (detected_pnts == "terra") {
- cae = terra::intersect(target_input, ext_input)
- }
-
- return(cae)
-}
diff --git a/scomps/R/clip_as_extent_ras.R b/scomps/R/clip_as_extent_ras.R
deleted file mode 100644
index 9ad93a61..00000000
--- a/scomps/R/clip_as_extent_ras.R
+++ /dev/null
@@ -1,20 +0,0 @@
-# Generated from scomps_rmarkdown_litr.rmd: do not edit by hand
-
-#' @title clip_as_extent_ras: Clip input raster.
-#' @description Clip input raster by the expected maximum extent of computation.
-#' @author Insang Song
-#' @param pnts sf or SpatVector object
-#' @param buffer_r numeric(1). buffer radius. this value will be automatically multiplied by 1.25
-#' @param nqsegs integer(1). the number of points per a quarter circle
-#' @param ras SpatRaster object to be clipped
-#' @export
-clip_as_extent_ras <- function(pnts, buffer_r, nqsegs = 180, ras){
- if (any(sapply(list(pnts, buffer_r, ras), is.null))) {
- stop("Any of required arguments are NULL. Please check.\n")
- }
- ext_input = set_clip_extent(pnts, buffer_r) |>
- terra::ext()
-
- cae <- terra::crop(ras, ext_input, snap = 'out')
- return(cae)
-}
diff --git a/scomps/R/clip_as_extent_ras2.R b/scomps/R/clip_as_extent_ras2.R
deleted file mode 100644
index d6c6e2ff..00000000
--- a/scomps/R/clip_as_extent_ras2.R
+++ /dev/null
@@ -1,20 +0,0 @@
-# Generated from scomps_rmarkdown_litr.rmd: do not edit by hand
-
-#' @title clip_as_extent_ras2: Clip input raster (version 2).
-#' @description Clip input raster by the expected maximum extent of computation.
-#' @author Insang Song
-#' @param points_in sf or SpatVector object
-#' @param buffer_r numeric(1). buffer radius. this value will be automatically multiplied by 1.25
-#' @param nqsegs integer(1). the number of points per a quarter circle
-#' @param ras SpatRaster object to be clipped
-#' @export
-clip_as_extent_ras2 <- function(points_in, buffer_r, nqsegs=180, ras){
- if (any(sapply(list(points_in, buffer_r, ras), is.null))) {
- stop("Any of required arguments are NULL. Please check.\n")
- }
- ext_input = set_clip_extent(points_in, buffer_r) |>
- terra::ext()
-
- cae <- terra::crop(ras, ext_input, snap = 'out')
- return(cae)
-}
diff --git a/scomps/R/distribute_process.R b/scomps/R/distribute_process.R
deleted file mode 100644
index 2fc47eb4..00000000
--- a/scomps/R/distribute_process.R
+++ /dev/null
@@ -1,39 +0,0 @@
-# Generated from scomps_rmarkdown_litr.rmd: do not edit by hand
-
-#' @title Process a given function in the entire or partial computational grids (under construction)
-#'
-#' @description Should
-#' @param grids sf/SpatVector object. Computational grids.
-#' @param grid_id character(1) or numeric(2). Default is NULL. If NULL, all grid_ids are used. "id_from:id_to" format or c(unique(grid_id)[id_from], unique(grid_id)[id_to])
-#' @param fun function supported in scomps.
-#' @param ... Arguments passed to fun.
-#' @return a data.frame object with mean value
-#' @author Insang Song \email{geoissong@@gmail.com}
-#'
-#' @export
-distribute_process <- function(grids, grid_id = NULL, fun, ...) {
- require(future.apply)
-
- # subset using grids and grid_id
- if (!is.null(grid_id)) {
- if (is.character(grid_id)) {
- grid_id_parsed = strsplit(grid_id, ":", fixed = TRUE)[[1]]
- grid_ids = c(which(unique(grids[["CGRIDID"]]) == grid_id_parsed[1]),
- which(unique(grids[["CGRIDID"]]) == grid_id_parsed[2]))
- }
- if (is.numeric(grid_id)) {
- grid_ids = unique(grids[["CGRIDID"]])[grid_id]
- }
- }
- grids_target = grids[grid_ids,]
- grids_target_list = split(grids_target, grids_target[["CGRIDID"]])
-
- results_distributed = future.apply::future_lapply(
- \(x, ...) {
- fun(...)
- }, grids_target_list, points_cutslist, SIMPLIFY = FALSE,
- future.seed = TRUE)
- results_distributed = do.call(rbind, results_distributed)
- return(results_distributed)
-}
-
diff --git a/scomps/R/extract_with.R b/scomps/R/extract_with.R
deleted file mode 100644
index c1d4ad95..00000000
--- a/scomps/R/extract_with.R
+++ /dev/null
@@ -1,33 +0,0 @@
-# Generated from scomps_rmarkdown_litr.rmd: do not edit by hand
-
-#' Extract raster values with point buffers or polygons
-#'
-#' @param raster SpatRaster object.
-#' @param vector SpatVector object.
-#' @param id character(1). Unique identifier of each point.
-#' @param func function taking one numeric vector argument.
-#' @param mode one of "polygon" (generic polygons to extract raster values with) or "buffer" (point with buffer radius)
-#' @param ... various. Passed to extract_with_buffer. See \code{?extract_with_buffer} for details.
-#' @return
-#' @author Insang Song \email{geoissong@@gmail.com}
-#' @examples
-#' # data
-#'
-#' # run
-#' clip_with()
-#'
-#' @export
-extract_with <- function(raster, vector, id, func = mean, mode = "polygon", ...) {
- if (!mode %in% c("polygon", "buffer")) {
- stop("Argument 'mode' should be one of 'polygon' or 'buffer'.\n")
- }
- stopifnot(is.character(id))
- stopifnot(id %in% names(vector))
-
- extracted =
- switch(mode,
- polygon = extract_with_polygons(vector, raster, id, func),
- buffer = extract_with_buffer(vector, raster, id = id, func = func, ...))
- return(extracted)
-}
-
diff --git a/scomps/R/extract_with_buffer.R b/scomps/R/extract_with_buffer.R
deleted file mode 100644
index 05b79640..00000000
--- a/scomps/R/extract_with_buffer.R
+++ /dev/null
@@ -1,50 +0,0 @@
-# Generated from scomps_rmarkdown_litr.rmd: do not edit by hand
-
-#' @title Extract summarized values from raster with points and a buffer radius (to be written)
-#'
-#' @description For simplicity, it is assumed that the coordinate systems of the points and the raster are the same. Kernel function is not yet implemented.
-#' @param points SpatVector object. Coordinates where buffers will be generated
-#' @param surf SpatRaster object. A raster of whatnot a summary will be calculated
-#' @param radius numeric(1). Buffer radius. here we assume circular buffers only
-#' @param id character(1). Unique identifier of each point.
-#' @param qsegs integer(1). Number of vertices at a quarter of a circle. Default is 90.
-#' @param func a function taking a numeric vector argument.
-#' @param kernel character(1). Name of a kernel function (yet to be implemented)
-#' @param bandwidth numeric(1). Kernel bandwidth.
-#' @return a data.frame object with mean value
-#' @author Insang Song \email{geoissong@@gmail.com}
-#'
-#' @export
-extract_with_buffer <- function(
- points, surf, radius, id, qsegs = 90, func = mean, kernel = NULL, bandwidth = NULL
- ) {
- # type check
- stopifnot("Check class of the input points.\n" = is(points, "SpatVector"))
- stopifnot("Check class of the input radius.\n" = is.numeric(radius))
- stopifnot(is.character(id))
- stopifnot(is.integer(qsegs))
-
- if (!is.null(kernel)) {
- extracted = extract_with_buffer.flat(points = points,
- surf = surf,
- radius = radius,
- id = id,
- func = func,
- qsegs = qsegs)
- return(extracted)
- }
-
- extracted = extract_with_buffer.kernel(points = points,
- surf = surf,
- radius = radius,
- id = id,
- func = func,
- qsegs = qsegs,
- kernel = kernel,
- bandwidth = bandwidth)
- return(extracted)
-
-}
-
-
-
diff --git a/scomps/R/extract_with_buffer.flat.R b/scomps/R/extract_with_buffer.flat.R
deleted file mode 100644
index bcf81c57..00000000
--- a/scomps/R/extract_with_buffer.flat.R
+++ /dev/null
@@ -1,23 +0,0 @@
-# Generated from scomps_rmarkdown_litr.rmd: do not edit by hand
-
-#' Subfunction: extract with buffers (flat weight; simple mean)
-#'
-#' @export
-extract_with_buffer.flat <- function(
- points, surf, radius, id, qsegs, func = mean, kernel = NULL, bandwidth = NULL
- ) {
- # generate buffers
- bufs = terra::buffer(points, width = radius, quadsegs = qsegs)
- # crop raster
- bufs_extent = terra::ext(bufs)
- surf_cropped = terra::crop(surf, bufs_extent)
- name_surf_val = names(surf)
- # extract raster values
- surf_at_bufs = terra::extract(surf_cropped, bufs)
- surf_at_bufs_summary =
- surf_at_bufs |>
- group_by(ID) |>
- summarize(across(all_of(name_surf_val), ~mean, na.rm=T)) |>
- ungroup()
- return(surf_at_bufs_summary)
-}
diff --git a/scomps/R/extract_with_buffer.kernel.R b/scomps/R/extract_with_buffer.kernel.R
deleted file mode 100644
index 3967965f..00000000
--- a/scomps/R/extract_with_buffer.kernel.R
+++ /dev/null
@@ -1,27 +0,0 @@
-# Generated from scomps_rmarkdown_litr.rmd: do not edit by hand
-
-#' Subfunction: extract with buffers (kernel weight; weighted mean)
-#'
-#' @export
-extract_with_buffer.kernel <- function(
- points, surf, radius, id, qsegs, func = mean, kernel, bandwidth
- ) {
- # generate buffers
- bufs = terra::buffer(points, width = radius, quadsegs = qsegs)
- # crop raster
- bufs_extent = terra::ext(bufs)
- surf_cropped = terra::crop(surf, bufs_extent)
- name_surf_val = names(surf)
-
- # TODO: kernel implementation
-
-
- # extract raster values
- surf_at_bufs = terra::extract(surf_cropped, bufs)
- surf_at_bufs_summary =
- surf_at_bufs |>
- group_by(ID) |>
- summarize(across(all_of(name_surf_val), ~mean, na.rm=T)) |>
- ungroup()
- return(surf_at_bufs_summary)
-}
diff --git a/scomps/R/extract_with_polygons.R b/scomps/R/extract_with_polygons.R
deleted file mode 100644
index 4b37763a..00000000
--- a/scomps/R/extract_with_polygons.R
+++ /dev/null
@@ -1,55 +0,0 @@
-# Generated from scomps_rmarkdown_litr.rmd: do not edit by hand
-
-#' @title Extract summarized values from raster with generic polygons
-#'
-#' @description For simplicity, it is assumed that the coordinate systems of the points and the raster are the same. Kernel function is not yet implemented.
-#' @param polys sf/SpatVector object. Polygons.
-#' @param surf stars/SpatRaster object. A raster of whatnot a summary will be calculated
-#' @param id character(1). Unique identifier of each point.
-#' @param func a function taking one argument. For example, function(x) mean(x, na.rm = TRUE) or \(x) mode(x, na.rm = TRUE)
-#' @param na.rm logical(1). NA values are omitted when summary is calculated.
-#' @return a data.frame object with function value
-#' @author Insang Song \email{geoissong@@gmail.com}
-#'
-#' @export
-extract_with_polygons <- function(
- polys, surf, id, func = mean, na.rm = TRUE
- ) {
- # type check
- stopifnot("Check class of the input points.\n" = any(is(polys, "sf"), is(polys, "SpatVector")))
- stopifnot("Check class of the input raster.\n" = any(is(surf, "stars"), is(surf, "SpatRaster")))
- stopifnot(is.character(id))
-
- cls_polys = check_packbound(polys)
- cls_surf = check_packbound(surf)
-
- if (cls_polys != cls_surf) {
- polys = switch_packbound(polys)
- }
-
- extract_with_polygons.sf = function(polys, surf, id, func) {
- extracted = stars::st_extract(x = surf, at = polys, FUN = func)
- # extracted = extracted |>
- # group_by(!!sym(id)) |>
- # summarize(across(-!!sym(id), ~func)) |>
- # ungroup()
- return(extracted)
- }
-
- extract_with_polygons.terra = function(polys, surf, id, func) {
- extracted = terra::extract(surf, polys)
- extracted = extracted |>
- group_by(!!sym(id)) |>
- summarize(across(-!!sym(id), ~func)) |>
- ungroup()
- return(extracted)
- }
-
- extracted_poly = switch(
- cls_surf,
- sf = extract_with_polygons.sf(polys = polys, surf = surf, id = id, func = func),
- terra = extract_with_polygons.terra(polys = polys, surf = surf, id = id, func = func)
- )
- return(extracted_poly)
-}
-
diff --git a/scomps/R/sp_indexing.R b/scomps/R/indexing.R
similarity index 100%
rename from scomps/R/sp_indexing.R
rename to scomps/R/indexing.R
diff --git a/scomps/R/get_computational_regions.R b/scomps/R/interpret_computational_domain.R
similarity index 83%
rename from scomps/R/get_computational_regions.R
rename to scomps/R/interpret_computational_domain.R
index bac913a5..757acf37 100644
--- a/scomps/R/get_computational_regions.R
+++ b/scomps/R/interpret_computational_domain.R
@@ -166,3 +166,41 @@ grid_merge <- function(points_in, grid_in, grid_min_features){
+
+#' @title Process a given function in the entire or partial computational grids (under construction)
+#'
+#' @description Should
+#' @param grids sf/SpatVector object. Computational grids.
+#' @param grid_id character(1) or numeric(2). Default is NULL. If NULL, all grid_ids are used. "id_from:id_to" format or c(unique(grid_id)[id_from], unique(grid_id)[id_to])
+#' @param fun function supported in scomps.
+#' @param ... Arguments passed to fun.
+#' @return a data.frame object with mean value
+#' @author Insang Song \email{geoissong@@gmail.com}
+#'
+#' @export
+distribute_process <- function(grids, grid_id = NULL, fun, ...) {
+ require(future.apply)
+
+ # subset using grids and grid_id
+ if (!is.null(grid_id)) {
+ if (is.character(grid_id)) {
+ grid_id_parsed = strsplit(grid_id, ":", fixed = TRUE)[[1]]
+ grid_ids = c(which(unique(grids[["CGRIDID"]]) == grid_id_parsed[1]),
+ which(unique(grids[["CGRIDID"]]) == grid_id_parsed[2]))
+ }
+ if (is.numeric(grid_id)) {
+ grid_ids = unique(grids[["CGRIDID"]])[grid_id]
+ }
+ }
+ grids_target = grids[grid_ids,]
+ grids_target_list = split(grids_target, grids_target[["CGRIDID"]])
+
+ results_distributed = future.apply::future_lapply(
+ \(x, ...) {
+ fun(...)
+ }, grids_target_list, points_cutslist, SIMPLIFY = FALSE,
+ future.seed = TRUE)
+ results_distributed = do.call(rbind, results_distributed)
+ return(results_distributed)
+}
+
diff --git a/scomps/R/initate_log.R b/scomps/R/logging.R
similarity index 100%
rename from scomps/R/initate_log.R
rename to scomps/R/logging.R
diff --git a/scomps/R/set_clip_extent.R b/scomps/R/preprocessing.R
similarity index 77%
rename from scomps/R/set_clip_extent.R
rename to scomps/R/preprocessing.R
index 3a699be8..8f8f6cb8 100644
--- a/scomps/R/set_clip_extent.R
+++ b/scomps/R/preprocessing.R
@@ -22,3 +22,14 @@ set_clip_extent <- function(pnts, buffer_r) {
}
return(ext_input)
}
+
+#' Quick call for SpatRaster with a window
+#'
+#' @param rasterpath character(1). Path to the raster file.
+#' @param win Named integer vector (4) or terra::ext() results.
+#' @param author Insang Song
+#' @export
+rast_short <- function(rasterpath, win) {
+ terra::rast(rasterpath, win = win)
+}
+
diff --git a/scomps/R/processing.R b/scomps/R/processing.R
new file mode 100644
index 00000000..c1c84a03
--- /dev/null
+++ b/scomps/R/processing.R
@@ -0,0 +1,372 @@
+# Generated from scomps_rmarkdown_litr.rmd: do not edit by hand
+
+#' Extent clipping
+#' @description Clip input vector by the expected maximum extent of computation.
+#' @author Insang Song
+#' @param pnts sf or SpatVector object
+#' @param buffer_r numeric(1). buffer radius. this value will be automatically multiplied by 1.25
+#' @param nqsegs integer(1). the number of points per a quarter circle; SOON TO BE DEPRECATED
+#' @param target_input sf or SpatVector object to be clipped
+#' @return A clipped sf or SpatVector object.
+#' @export
+clip_as_extent <- function(pnts, buffer_r, nqsegs=NULL, target_input){
+ if (any(sapply(list(pnts, buffer_r, target_input), is.null))) {
+ stop("One or more required arguments are NULL. Please check.\n")
+ }
+ detected_pnts = check_packbound(pnts)
+ detected_target = check_packbound(target_input)
+
+ if (detected_pnts != detected_target) {
+ warning("Inputs are not the same class.\n")
+ target_input = switch(detected_target,
+ sf = terra::vect(target_input),
+ terra = sf::st_as_sf(target_input))
+ }
+
+ ext_input = set_clip_extent(pnts, buffer_r)
+ cat("Clip target features with the input feature extent...\n")
+ if (detected_pnts == "sf") {
+ cae = ext_input |>
+ sf::st_intersection(x = target_input)
+ }
+ if (detected_pnts == "terra") {
+ cae = terra::intersect(target_input, ext_input)
+ }
+
+ return(cae)
+}
+
+#' @title clip_as_extent_ras: Clip input raster.
+#' @description Clip input raster by the expected maximum extent of computation.
+#' @author Insang Song
+#' @param pnts sf or SpatVector object
+#' @param buffer_r numeric(1). buffer radius. this value will be automatically multiplied by 1.25
+#' @param nqsegs integer(1). the number of points per a quarter circle
+#' @param ras SpatRaster object to be clipped
+#' @export
+clip_as_extent_ras <- function(pnts, buffer_r, nqsegs = 180, ras){
+ if (any(sapply(list(pnts, buffer_r, ras), is.null))) {
+ stop("Any of required arguments are NULL. Please check.\n")
+ }
+ ext_input = set_clip_extent(pnts, buffer_r) |>
+ terra::ext()
+
+ cae <- terra::crop(ras, ext_input, snap = 'out')
+ return(cae)
+}
+
+#' @title clip_as_extent_ras2: Clip input raster (version 2).
+#' @description Clip input raster by the expected maximum extent of computation.
+#' @author Insang Song
+#' @param points_in sf or SpatVector object
+#' @param buffer_r numeric(1). buffer radius. this value will be automatically multiplied by 1.25
+#' @param nqsegs integer(1). the number of points per a quarter circle
+#' @param ras SpatRaster object to be clipped
+#' @export
+clip_as_extent_ras2 <- function(points_in, buffer_r, nqsegs=180, ras){
+ if (any(sapply(list(points_in, buffer_r, ras), is.null))) {
+ stop("Any of required arguments are NULL. Please check.\n")
+ }
+ ext_input = set_clip_extent(points_in, buffer_r) |>
+ terra::ext()
+
+ cae <- terra::crop(ras, ext_input, snap = 'out')
+ return(cae)
+}
+
+#' @title Extract summarized values from raster with generic polygons
+#'
+#' @description For simplicity, it is assumed that the coordinate systems of the points and the raster are the same. Kernel function is not yet implemented.
+#' @param polys sf/SpatVector object. Polygons.
+#' @param surf stars/SpatRaster object. A raster of whatnot a summary will be calculated
+#' @param id character(1). Unique identifier of each point.
+#' @param func a function taking one argument. For example, function(x) mean(x, na.rm = TRUE) or \(x) mode(x, na.rm = TRUE)
+#' @param na.rm logical(1). NA values are omitted when summary is calculated.
+#' @return a data.frame object with function value
+#' @author Insang Song \email{geoissong@@gmail.com}
+#'
+#' @export
+extract_with_polygons <- function(
+ polys, surf, id, func = mean, na.rm = TRUE
+ ) {
+ # type check
+ stopifnot("Check class of the input points.\n" = any(is(polys, "sf"), is(polys, "SpatVector")))
+ stopifnot("Check class of the input raster.\n" = any(is(surf, "stars"), is(surf, "SpatRaster")))
+ stopifnot(is.character(id))
+
+ cls_polys = check_packbound(polys)
+ cls_surf = check_packbound(surf)
+
+ if (cls_polys != cls_surf) {
+ polys = switch_packbound(polys)
+ }
+
+ extract_with_polygons.sf = function(polys, surf, id, func) {
+ extracted = stars::st_extract(x = surf, at = polys, FUN = func)
+ # extracted = extracted |>
+ # group_by(!!sym(id)) |>
+ # summarize(across(-!!sym(id), ~func)) |>
+ # ungroup()
+ return(extracted)
+ }
+
+ extract_with_polygons.terra = function(polys, surf, id, func) {
+ extracted = terra::extract(surf, polys)
+ extracted = extracted |>
+ group_by(!!sym(id)) |>
+ summarize(across(-!!sym(id), ~func)) |>
+ ungroup()
+ return(extracted)
+ }
+
+ extracted_poly = switch(
+ cls_surf,
+ sf = extract_with_polygons.sf(polys = polys, surf = surf, id = id, func = func),
+ terra = extract_with_polygons.terra(polys = polys, surf = surf, id = id, func = func)
+ )
+ return(extracted_poly)
+}
+
+
+#' Extract raster values with point buffers or polygons
+#'
+#' @param raster SpatRaster object.
+#' @param vector SpatVector object.
+#' @param id character(1). Unique identifier of each point.
+#' @param func function taking one numeric vector argument.
+#' @param mode one of "polygon" (generic polygons to extract raster values with) or "buffer" (point with buffer radius)
+#' @param ... various. Passed to extract_with_buffer. See \code{?extract_with_buffer} for details.
+#' @return
+#' @author Insang Song \email{geoissong@@gmail.com}
+#' @examples
+#' # data
+#'
+#' # run
+#' clip_with()
+#'
+#' @export
+extract_with <- function(raster, vector, id, func = mean, mode = "polygon", ...) {
+ if (!mode %in% c("polygon", "buffer")) {
+ stop("Argument 'mode' should be one of 'polygon' or 'buffer'.\n")
+ }
+ stopifnot(is.character(id))
+ stopifnot(id %in% names(vector))
+
+ extracted =
+ switch(mode,
+ polygon = extract_with_polygons(vector, raster, id, func),
+ buffer = extract_with_buffer(vector, raster, id = id, func = func, ...))
+ return(extracted)
+}
+
+
+#' Calculate SEDC covariates
+#' @param point_from SpatVector object. Locations where the sum of SEDCs are calculated.
+#' @param point_to SpatVector object. Locations where each SEDC is calculated.
+#' @param sdec_bandwidth numeric(1). Distance at which the source concentration is reduced to exp(-3) (approximately 95 %)
+#' @param threshold numeric(1). For computational efficiency, the nearest points in threshold will be selected
+#' @param target_fields character(varying). Field names in characters.
+#' @description NOTE: sf implementation is pending. Only available for terra.
+#' @author Insang Song
+#' @export
+calculate_sedc <- function(point_from, point_to, id, sedc_bandwidth, threshold, target_fields) {
+ # define sources, set SEDC exponential decay range
+ len_point_from = seq_len(nrow(point_from))
+ len_point_to = seq_len(nrow(point_to))
+
+ point_from$from_id = len_point_from
+ # select egrid_v only if closer than 3e5 meters from each aqs
+ point_from_buf = terra::buffer(point_from_buf, threshold = threshold, quadsegs = 90)
+ point_to = point_to[point_from_buf,]
+ point_to$to_id = len_point_to
+
+ # near features with distance argument: only returns integer indices
+ near_from_to = terra::nearby(point_from, point_to, distance = threshold)
+ # attaching actual distance
+ dist_near_to = terra::distance(point_from, point_to)
+ dist_near_to_df = as.vector(dist_near_to)
+ # adding integer indices
+ dist_near_to_tdf = expand.grid(from_id = len_point_from, to_id = len_point_to)
+ dist_near_to_df = cbind(dist_near_to_tdf, dist = dist_near_to_df)
+
+ # summary
+ near_from_to = near_from_to |>
+ as_tibble() |>
+ left_join(data.frame(point_from)) |>
+ left_join(data.frame(point_to)) |>
+ left_join(dist_near_to_df) |>
+ # per the definition in https://mserre.sph.unc.edu/BMElab_web/SEDCtutorial/index.html
+ # exp(-3) is about 0.05
+ mutate(w_sedc = exp((-3 * dist) / sedc_bandwidth)) |>
+ group_by(!!sym(id)) |>
+ summarize(across(all_of(target_fields), list(sedc = ~sum(w_sedc * ., na.rm = TRUE)))) |>
+ ungroup()
+
+ invisible(near_from_to)
+
+}
+
+
+#' Computing area weighted covariates using two polygon sf or SpatVector objects
+#' @param poly_in A sf/SpatVector object at weighted means will be calculated.
+#' @param poly_weight A sf/SpatVector object from which weighted means will be calculated.
+#' @param id_poly_in character(1). The unique identifier of each polygon in poly_in
+#' @return A data.frame with all numeric fields of area-weighted means.
+#' @description When poly_in and poly_weight are different classes, poly_weight will be converted to the class of poly_in.
+#' @author Insang Song \email{geoissong@@gmail.com}
+#' @examples
+#' # package
+#' library(sf)
+#' library(tictoc)
+#'
+#' # run
+#' nc = st_read(system.file("shape/nc.shp", package="sf"))
+#' nc = st_transform(nc, 5070)
+#' pp = st_sample(nc, size = 300)
+#' pp[["id"]] = seq(1,nrow(pp))
+#' st_crs(pp) = "EPSG:5070"
+#' ppb = st_buffer(pp, nQuadSegs=180, dist = units::set_units(20, 'km'))
+#'
+#' tic()
+#' ppb_nc_aw = aw_covariates(ppb, nc, 'id')
+#' toc()
+#' summary(ppb_nc_aw)
+#' #### Example of aw_covariates ends ####
+#' @export
+aw_covariates <- function(poly_in, poly_weight, id_poly_in = "ID") {
+ stopifnot("Inputs have invalid classes.\n" =
+ is(poly_in, "sf") || is(poly_weight, "sf") || is(poly_in, "SpatVector") || is(poly_weight, "SpatVector"))
+ #check_crs()
+ aw_covariates.terra = function(poly_in, poly_weight, id_poly_in = id_poly_in) {
+
+ poly_intersected = terra::intersect(poly_in, poly_weight)
+ poly_intersected[["area_segment_"]] = terra::expanse(poly_intersected)
+ poly_intersected = data.frame(poly_intersected) |>
+ dplyr::group_by(!!rlang::sym(id_poly_in)) |>
+ dplyr::summarize(across(is.numeric,
+ ~weighted.mean(., w = area_segment_))) |>
+ dplyr::ungroup()
+ return(poly_intersected)
+ }
+
+ class_poly_in = check_packbound(poly_in)
+ class_poly_weight = check_packbound(poly_weight)
+
+ if (class_poly_in != class_poly_weight) {
+ class_poly_weight = as(class_poly_weight, class(class_poly_in)[1])
+ }
+
+ switch(class_poly_in,
+ sf = sf::st_interpolate_aw(poly_weight, poly_in, extensive = FALSE),
+ terra = aw_covariates.terra(poly_in, poly_weight, id_poly_in = id_poly_in))
+
+}
+
+# ncbuf = terra::intersect(vect(ppb), vect(nc))
+# ncbuf_a = ncbuf
+# ncbuf_a$segarea = expanse(ncbuf_a)
+# ncbuf_k = data.frame(ncbuf_a) |>
+# dplyr::group_by(id) |>
+# dplyr::summarize(across(is.numeric,
+# ~weighted.mean(., w = segarea))) |>
+# dplyr::ungroup()
+
+#ncbufagg = terra::aggregate(ncbuf, by = 'id', fun = weighted.mean, w = ncbuf_a$segarea)
+
+
+#' Subfunction: extract with buffers (flat weight; simple mean)
+#'
+#' @export
+extract_with_buffer.flat <- function(
+ points, surf, radius, id, qsegs, func = mean, kernel = NULL, bandwidth = NULL
+ ) {
+ # generate buffers
+ bufs = terra::buffer(points, width = radius, quadsegs = qsegs)
+ # crop raster
+ bufs_extent = terra::ext(bufs)
+ surf_cropped = terra::crop(surf, bufs_extent)
+ name_surf_val = names(surf)
+ # extract raster values
+ surf_at_bufs = terra::extract(surf_cropped, bufs)
+ surf_at_bufs_summary =
+ surf_at_bufs |>
+ group_by(ID) |>
+ summarize(across(all_of(name_surf_val), ~mean, na.rm=T)) |>
+ ungroup()
+ return(surf_at_bufs_summary)
+}
+
+#' Subfunction: extract with buffers (kernel weight; weighted mean)
+#'
+#' @export
+extract_with_buffer.kernel <- function(
+ points, surf, radius, id, qsegs, func = mean, kernel, bandwidth
+ ) {
+ # generate buffers
+ bufs = terra::buffer(points, width = radius, quadsegs = qsegs)
+ # crop raster
+ bufs_extent = terra::ext(bufs)
+ surf_cropped = terra::crop(surf, bufs_extent)
+ name_surf_val = names(surf)
+
+ # TODO: kernel implementation
+
+
+ # extract raster values
+ surf_at_bufs = terra::extract(surf_cropped, bufs)
+ surf_at_bufs_summary =
+ surf_at_bufs |>
+ group_by(ID) |>
+ summarize(across(all_of(name_surf_val), ~mean, na.rm=T)) |>
+ ungroup()
+ return(surf_at_bufs_summary)
+}
+
+#' @title Extract summarized values from raster with points and a buffer radius (to be written)
+#'
+#' @description For simplicity, it is assumed that the coordinate systems of the points and the raster are the same. Kernel function is not yet implemented.
+#' @param points SpatVector object. Coordinates where buffers will be generated
+#' @param surf SpatRaster object. A raster of whatnot a summary will be calculated
+#' @param radius numeric(1). Buffer radius. here we assume circular buffers only
+#' @param id character(1). Unique identifier of each point.
+#' @param qsegs integer(1). Number of vertices at a quarter of a circle. Default is 90.
+#' @param func a function taking a numeric vector argument.
+#' @param kernel character(1). Name of a kernel function (yet to be implemented)
+#' @param bandwidth numeric(1). Kernel bandwidth.
+#' @return a data.frame object with mean value
+#' @author Insang Song \email{geoissong@@gmail.com}
+#'
+#' @export
+extract_with_buffer <- function(
+ points, surf, radius, id, qsegs = 90, func = mean, kernel = NULL, bandwidth = NULL
+ ) {
+ # type check
+ stopifnot("Check class of the input points.\n" = is(points, "SpatVector"))
+ stopifnot("Check class of the input radius.\n" = is.numeric(radius))
+ stopifnot(is.character(id))
+ stopifnot(is.integer(qsegs))
+
+ if (!is.null(kernel)) {
+ extracted = extract_with_buffer.flat(points = points,
+ surf = surf,
+ radius = radius,
+ id = id,
+ func = func,
+ qsegs = qsegs)
+ return(extracted)
+ }
+
+ extracted = extract_with_buffer.kernel(points = points,
+ surf = surf,
+ radius = radius,
+ id = id,
+ func = func,
+ qsegs = qsegs,
+ kernel = kernel,
+ bandwidth = bandwidth)
+ return(extracted)
+
+}
+
+
+
diff --git a/scomps/R/rast_short.R b/scomps/R/rast_short.R
deleted file mode 100644
index 042d12f5..00000000
--- a/scomps/R/rast_short.R
+++ /dev/null
@@ -1,12 +0,0 @@
-# Generated from scomps_rmarkdown_litr.rmd: do not edit by hand
-
-#' Quick call for SpatRaster with a window
-#'
-#' @param rasterpath character(1). Path to the raster file.
-#' @param win Named integer vector (4) or terra::ext() results.
-#' @param author Insang Song
-#' @export
-rast_short <- function(rasterpath, win) {
- terra::rast(rasterpath, win = win)
-}
-
diff --git a/scomps/R/switch_packbound.R b/scomps/R/switch_format.R
similarity index 100%
rename from scomps/R/switch_packbound.R
rename to scomps/R/switch_format.R
diff --git a/scomps/R/validate_and_repair_vectors.R b/scomps/R/validate.R
similarity index 100%
rename from scomps/R/validate_and_repair_vectors.R
rename to scomps/R/validate.R
diff --git a/scomps/tests/testthat/tests.R b/scomps/tests/testthat/tests.R
index 35965382..6efa4142 100644
--- a/scomps/tests/testthat/tests.R
+++ b/scomps/tests/testthat/tests.R
@@ -21,14 +21,45 @@ testthat::test_that("What package does the input object belong?",
withr::local_package("stars")
withr::local_package("terra")
withr::local_options(list(sf_use_s2 = FALSE))
- data(stars_sentinel2)
+ bcsd_path = system.file(package = "stars", "nc/bcsd_obs_1999.nc")
+ bcsd_stars = stars::read_stars(bcsd_path)
+
nc = system.file(package = "sf", "shape/nc.shp")
nc = sf::read_sf(nc)
- datatype_stars = check_datatype(stars_sentinel2)
+ datatype_stars = check_datatype(bcsd_stars)
datatype_sf = check_datatype(nc)
testthat::expect_equal(datatype_stars, "raster")
testthat::expect_equal(datatype_sf, "vector")
})
+
+testthat::test_that("Format is well converted",
+{
+ withr::local_package("stars")
+ withr::local_package("terra")
+ withr::local_options(list(sf_use_s2 = FALSE))
+
+ # starts from sf/stars
+ bcsd_path = system.file(package = "stars", "nc/bcsd_obs_1999.nc")
+ bcsd_stars = stars::read_stars(bcsd_path)
+ nc = system.file(package = "sf", "shape/nc.shp")
+ nc = sf::read_sf(nc)
+
+ stars_bcsd_tr = switch_packbound(bcsd_stars)
+ sf_nc_tr = switch_packbound(nc)
+
+ testthat::expect_equal(check_packbound(stars_bcsd_tr), "terra")
+ testthat::expect_equal(check_packbound(sf_nc_tr), "terra")
+
+ stars_bcsd_trb = switch_packbound(stars_bcsd_tr)
+ sf_nc_trb = switch_packbound(sf_nc_tr)
+
+ testthat::expect_equal(check_packbound(stars_bcsd_trb), "sf")
+ testthat::expect_equal(check_packbound(sf_nc_trb), "sf")
+
+})
+
+
+
diff --git a/scomps_rmarkdown_litr.html b/scomps_rmarkdown_litr.html
index 974a9d21..240b4444 100644
--- a/scomps_rmarkdown_litr.html
+++ b/scomps_rmarkdown_litr.html
@@ -368,7 +368,7 @@ Package setup
path = ".",
fields = list(
Package = params$package_name,
- Version = "0.0.2.10022023",
+ Version = "0.0.2.10032023",
Title = "Scalable R geospatial computation",
Description = "A package for scalable geospatial computation for environmental health research",
`Authors@R` = person(
@@ -463,11 +463,13 @@ Create functions
withr::local_package("stars")
withr::local_package("terra")
withr::local_options(list(sf_use_s2 = FALSE))
- data(stars_sentinel2)
+ bcsd_path = system.file(package = "stars", "nc/bcsd_obs_1999.nc")
+ bcsd_stars = stars::read_stars(bcsd_path)
+
nc = system.file(package = "sf", "shape/nc.shp")
nc = sf::read_sf(nc)
- datatype_stars = check_datatype(stars_sentinel2)
+ datatype_stars = check_datatype(bcsd_stars)
datatype_sf = check_datatype(nc)
testthat::expect_equal(datatype_stars, "raster")
@@ -475,7 +477,8 @@ Create functions
})
## pr, tas,
## Test passed
-## Test passed
+## pr, tas,
+## Test passed
#' @title Switch spatial data class
#' @description Convert stars into SpatRaster and vice versa; sf into SpatVector and vice versa.
#' @author Insang Song
@@ -499,6 +502,36 @@ Create functions
invisible(switched)
}
+
+testthat::test_that("Format is well converted",
+{
+ withr::local_package("stars")
+ withr::local_package("terra")
+ withr::local_options(list(sf_use_s2 = FALSE))
+
+ # starts from sf/stars
+ bcsd_path = system.file(package = "stars", "nc/bcsd_obs_1999.nc")
+ bcsd_stars = stars::read_stars(bcsd_path)
+ nc = system.file(package = "sf", "shape/nc.shp")
+ nc = sf::read_sf(nc)
+
+ stars_bcsd_tr = switch_packbound(bcsd_stars)
+ sf_nc_tr = switch_packbound(nc)
+
+ testthat::expect_equal(check_packbound(stars_bcsd_tr), "terra")
+ testthat::expect_equal(check_packbound(sf_nc_tr), "terra")
+
+ stars_bcsd_trb = switch_packbound(stars_bcsd_tr)
+ sf_nc_trb = switch_packbound(sf_nc_tr)
+
+ testthat::expect_equal(check_packbound(stars_bcsd_trb), "sf")
+ testthat::expect_equal(check_packbound(sf_nc_trb), "sf")
+
+})
+
+
+## pr, tas,
+## Test passed
#' @title check_crs2: Coordinate system checker
#' @description The input is checked whether its coordinate system is present. If not, it is reprojected to EPSG:5179.
#' @param input Input object one of sf or terra::Spat* object
@@ -1308,44 +1341,47 @@ Documenting the package and building
## ℹ Updating scomps documentation
## ℹ Loading scomps
-## Warning: [check_crs.R:6] @description requires a value
-## Warning: [extract_with.R:11] @return requires a value
+## Warning: [check.R:92] @description requires a value
+## Warning: [processing.R:139] @return requires a value
## Writing 'NAMESPACE'
-## Writing 'aw_covariates.Rd'
-## Writing 'calculate_sedc.Rd'
+## Writing 'check_packbound.Rd'
+## Writing 'check_crs2.Rd'
## Writing 'check_bbox.Rd'
## Writing 'check_crs.Rd'
-## Writing 'check_crs2.Rd'
-## Writing 'check_packbound.Rd'
## Writing 'check_within_reference.Rd'
-## Writing 'clip_as_extent.Rd'
-## Writing 'clip_as_extent_ras.Rd'
-## Writing 'clip_as_extent_ras2.Rd'
-## Writing 'distribute_process.Rd'
## Writing 'estimate_demands.Rd'
-## Writing 'extract_with.Rd'
-## Writing 'extract_with_buffer.Rd'
-## Writing 'extract_with_buffer.flat.Rd'
-## Writing 'extract_with_buffer.kernel.Rd'
-## Writing 'extract_with_polygons.Rd'
+## Writing 'sp_indexing.Rd'
## Writing 'get_computational_regions.Rd'
## Writing 'sp_index_grid.Rd'
## Writing 'grid_merge.Rd'
+## Writing 'distribute_process.Rd'
## Writing 'initate_log.Rd'
-## Writing 'rast_short.Rd'
## Writing 'set_clip_extent.Rd'
-## Writing 'sp_indexing.Rd'
+## Writing 'rast_short.Rd'
+## Writing 'clip_as_extent.Rd'
+## Writing 'clip_as_extent_ras.Rd'
+## Writing 'clip_as_extent_ras2.Rd'
+## Writing 'extract_with_polygons.Rd'
+## Writing 'extract_with.Rd'
+## Writing 'calculate_sedc.Rd'
+## Writing 'aw_covariates.Rd'
+## Writing 'extract_with_buffer.flat.Rd'
+## Writing 'extract_with_buffer.kernel.Rd'
+## Writing 'extract_with_buffer.Rd'
## Writing 'switch_packbound.Rd'
## Writing 'validate_and_repair_vectors.Rd'
## ℹ Testing scomps
## ✔ | F W S OK | Context
##
## ⠏ | 0 | tests pr, tas,
+## pr, tas,
+## pr, tas,
##
-## ✔ | 4 | tests
+## ⠼ | 5 | tests
+## ✔ | 8 | tests
##
## ══ Results ═════════════════════════════════════════════════════════════════════
-## [ FAIL 0 | WARN 0 | SKIP 0 | PASS 4 ]
+## [ FAIL 0 | WARN 0 | SKIP 0 | PASS 8 ]
## ── R CMD build ─────────────────────────────────────────────────────────────────
## * checking for file ‘/Users/songi2/Documents/GitHub/Scalable_GIS/scomps/DESCRIPTION’ ... OK
## * preparing ‘scomps’:
@@ -1361,18 +1397,12 @@ Documenting the package and building
## * checking for LF line-endings in source and make files and shell scripts
## * checking for empty or unneeded directories
## Removed empty directory ‘scomps/build’
-## * building ‘scomps_0.0.2.10022023.tar.gz’
+## * building ‘scomps_0.0.2.10032023.tar.gz’
## Warning: invalid uid value replaced by that for user 'nobody'
## Warning: invalid gid value replaced by that for user 'nobody'
-## [1] "/Users/songi2/Documents/GitHub/Scalable_GIS/scomps_0.0.2.10022023.tar.gz"
-## curl (5.0.2 -> 5.1.0) [CRAN]
-## Installing 1 packages: curl
-## Installing package into '/Users/songi2/Library/R/arm64/4.3/library'
-## (as 'lib' is unspecified)
-##
-## The downloaded binary packages are in
-## /var/folders/58/7rn_bn5d6k3_cxwnzdhswpz4n0z2n9/T//RtmpLf04ac/downloaded_packages
-## ── R CMD build ─────────────────────────────────────────────────────────────────
+## [1] "/Users/songi2/Documents/GitHub/Scalable_GIS/scomps_0.0.2.10032023.tar.gz"
+## Skipping 1 packages ahead of CRAN: data.table
+## ── R CMD build ─────────────────────────────────────────────────────────────────
## * checking for file ‘/Users/songi2/Documents/GitHub/Scalable_GIS/scomps/DESCRIPTION’ ... OK
## * preparing ‘scomps’:
## * checking DESCRIPTION meta-information ... OK
@@ -1387,12 +1417,12 @@ Documenting the package and building
## * checking for LF line-endings in source and make files and shell scripts
## * checking for empty or unneeded directories
## Removed empty directory ‘scomps/build’
-## * building ‘scomps_0.0.2.10022023.tar.gz’
+## * building ‘scomps_0.0.2.10032023.tar.gz’
## Warning: invalid uid value replaced by that for user 'nobody'
## Warning: invalid gid value replaced by that for user 'nobody'
##
## Running /Library/Frameworks/R.framework/Resources/bin/R CMD INSTALL \
-## /var/folders/58/7rn_bn5d6k3_cxwnzdhswpz4n0z2n9/T//RtmpLf04ac/scomps_0.0.2.10022023.tar.gz \
+## /var/folders/58/7rn_bn5d6k3_cxwnzdhswpz4n0z2n9/T//Rtmp2UqRyZ/scomps_0.0.2.10032023.tar.gz \
## --install-tests
## * installing to library ‘/Users/songi2/Library/R/arm64/4.3/library’
## * installing *source* package ‘scomps’ ...
diff --git a/scomps_rmarkdown_litr.rmd b/scomps_rmarkdown_litr.rmd
index f3767faf..e67a1222 100644
--- a/scomps_rmarkdown_litr.rmd
+++ b/scomps_rmarkdown_litr.rmd
@@ -21,7 +21,7 @@ usethis::create_package(
path = ".",
fields = list(
Package = params$package_name,
- Version = "0.0.2.10022023",
+ Version = "0.0.2.10032023",
Title = "Scalable R geospatial computation",
Description = "A package for scalable geospatial computation for environmental health research",
`Authors@R` = person(
@@ -79,7 +79,7 @@ litr::add_vignettes("../vignettes-sources/01_generate_computational_grid.Rmd")
# Now to the package itself
### Create functions
-```{r}
+```{r, send_to = "R/check.R"}
#' @title Return the package the input object is based on
#' @description Detect whether the input object is sf or Spat* object.
#' @author Insang Song
@@ -129,11 +129,13 @@ testthat::test_that("What package does the input object belong?",
withr::local_package("stars")
withr::local_package("terra")
withr::local_options(list(sf_use_s2 = FALSE))
- data(stars_sentinel2)
+ bcsd_path = system.file(package = "stars", "nc/bcsd_obs_1999.nc")
+ bcsd_stars = stars::read_stars(bcsd_path)
+
nc = system.file(package = "sf", "shape/nc.shp")
nc = sf::read_sf(nc)
- datatype_stars = check_datatype(stars_sentinel2)
+ datatype_stars = check_datatype(bcsd_stars)
datatype_sf = check_datatype(nc)
testthat::expect_equal(datatype_stars, "raster")
@@ -141,7 +143,7 @@ testthat::test_that("What package does the input object belong?",
})
```
-```{r}
+```{r, send_to = "R/switch_format.R"}
#' @title Switch spatial data class
#' @description Convert stars into SpatRaster and vice versa; sf into SpatVector and vice versa.
#' @author Insang Song
@@ -169,6 +171,37 @@ switch_packbound <- function(input) {
```{r}
+
+testthat::test_that("Format is well converted",
+{
+ withr::local_package("stars")
+ withr::local_package("terra")
+ withr::local_options(list(sf_use_s2 = FALSE))
+
+ # starts from sf/stars
+ bcsd_path = system.file(package = "stars", "nc/bcsd_obs_1999.nc")
+ bcsd_stars = stars::read_stars(bcsd_path)
+ nc = system.file(package = "sf", "shape/nc.shp")
+ nc = sf::read_sf(nc)
+
+ stars_bcsd_tr = switch_packbound(bcsd_stars)
+ sf_nc_tr = switch_packbound(nc)
+
+ testthat::expect_equal(check_packbound(stars_bcsd_tr), "terra")
+ testthat::expect_equal(check_packbound(sf_nc_tr), "terra")
+
+ stars_bcsd_trb = switch_packbound(stars_bcsd_tr)
+ sf_nc_trb = switch_packbound(sf_nc_tr)
+
+ testthat::expect_equal(check_packbound(stars_bcsd_trb), "sf")
+ testthat::expect_equal(check_packbound(sf_nc_trb), "sf")
+
+})
+
+
+```
+
+```{r, send_to = "R/check.R"}
#' @title check_crs2: Coordinate system checker
#' @description The input is checked whether its coordinate system is present. If not, it is reprojected to EPSG:5179.
#' @param input Input object one of sf or terra::Spat* object
@@ -198,7 +231,7 @@ check_crs2 <- function(input, crs_standard = "EPSG:4326") {
}
```
-```{r}
+```{r, send_to = "R/validate.R"}
#' Validate and repair input vector data
#' @description It tries repairing input vector data. Vector validity violation usually appears in polygon data with self-crossing or hole orders. This function will pass the input_vector object to sf::st_make_valid() (if input_vector is sf) or terra::makeValid() (if input_vector is SpatVector). May take some time depending on the geometry complexity.
#' @author Insang Song
@@ -218,7 +251,7 @@ validate_and_repair_vectors <- function(input_vector) {
```
-```{r}
+```{r, send_to = "R/logging.R"}
#' Turn on logging
#' @author Insang Song
#' @param expr expression. Any function call to be logged.
@@ -239,7 +272,7 @@ initate_log <- function(expr, dolog = FALSE, logpath) {
```
-```{r}
+```{r, send_to = "R/preprocessing.R"}
#' Setting the clipping extent
#' @description Return clipping extent with buffer radius. It assumes the input CRS is projected and linear unit is meters.
#' @author Insang Song
@@ -265,7 +298,7 @@ set_clip_extent <- function(pnts, buffer_r) {
```
-```{r}
+```{r, send_to = "R/processing.R"}
#' Extent clipping
#' @description Clip input vector by the expected maximum extent of computation.
#' @author Insang Song
@@ -304,7 +337,7 @@ clip_as_extent <- function(pnts, buffer_r, nqsegs=NULL, target_input){
```
-```{r}
+```{r, send_to = "R/processing.R"}
#' @title clip_as_extent_ras: Clip input raster.
#' @description Clip input raster by the expected maximum extent of computation.
#' @author Insang Song
@@ -326,7 +359,7 @@ clip_as_extent_ras <- function(pnts, buffer_r, nqsegs = 180, ras){
```
-```{r}
+```{r, send_to = "R/processing.R"}
#' @title clip_as_extent_ras2: Clip input raster (version 2).
#' @description Clip input raster by the expected maximum extent of computation.
#' @author Insang Song
@@ -348,7 +381,7 @@ clip_as_extent_ras2 <- function(points_in, buffer_r, nqsegs=180, ras){
```
-```{r}
+```{r, send_to = "R/indexing.R"}
#' @title Create integer indices for grid
#' @description Returns a tibble object that includes x- and y- index by using two inputs ncutsx and ncutsy, which are x- and y-directional splits, respectively.
#' @author Insang Song
@@ -374,7 +407,7 @@ sp_indexing <- function(points_in, ncutsx, ncutsy){
}
```
-```{r}
+```{r, send_to = "R/preprocessing.R"}
#' Quick call for SpatRaster with a window
#'
#' @param rasterpath character(1). Path to the raster file.
@@ -414,7 +447,7 @@ estimate_demands <- function(
```
-```{r}
+```{r, send_to = "R/interpret_computational_domain.R"}
#' Get a set of computational regions
#'
#' @param input sf or Spat* object.
@@ -585,7 +618,7 @@ grid_merge <- function(points_in, grid_in, grid_min_features){
-```{r}
+```{r, send_to = "R/check.R"}
#' Check if the data extent is inside the reference bounding box
#'
#' @description One of the most common errors in spatial computation is rooted in the entirely or partly incomparable spatial extents of input datasets. This function returns whether your data is inside the target computational extent. It is assumed that you know and have the exact computational region. This function will return TRUE if the reference region completely contains your data's extent and FALSE otherwise.
@@ -636,7 +669,7 @@ check_bbox <- function(
```
-```{r}
+```{r, send_to = "R/processing.R"}
#' @title Extract summarized values from raster with generic polygons
#'
#' @description For simplicity, it is assumed that the coordinate systems of the points and the raster are the same. Kernel function is not yet implemented.
@@ -692,7 +725,7 @@ extract_with_polygons <- function(
```
-```{r}
+```{r, send_to = "R/processing.R"}
#' Extract raster values with point buffers or polygons
#'
#' @param raster SpatRaster object.
@@ -726,7 +759,7 @@ extract_with <- function(raster, vector, id, func = mean, mode = "polygon", ...)
```
-```{r}
+```{r, send_to = "R/check.R"}
#' Check Coordinate Reference System
#' @param x sf/stars/SpatVector/SpatRaster object.
#' @return A st_crs or crs object.
@@ -755,7 +788,7 @@ check_crs <- function(x) {
```
-```{r}
+```{r, send_to = "R/check.R"}
#' Check if the boundary of the vector/raster object is inside the reference
#' @param input_object sf/stars/SpatVector/SpatRaster object.
#' @param reference sf/stars/SpatVector/SpatRaster object.
@@ -784,7 +817,7 @@ check_within_reference <- function(input_object, reference) {
```
-```{r}
+```{r, send_to = "R/processing.R"}
#' Calculate SEDC covariates
#' @param point_from SpatVector object. Locations where the sum of SEDCs are calculated.
#' @param point_to SpatVector object. Locations where each SEDC is calculated.
@@ -833,7 +866,7 @@ calculate_sedc <- function(point_from, point_to, id, sedc_bandwidth, threshold,
```
-```{r}
+```{r, send_to = "R/processing.R"}
#' Computing area weighted covariates using two polygon sf or SpatVector objects
#' @param poly_in A sf/SpatVector object at weighted means will be calculated.
#' @param poly_weight A sf/SpatVector object from which weighted means will be calculated.
@@ -902,7 +935,7 @@ aw_covariates <- function(poly_in, poly_weight, id_poly_in = "ID") {
```
-```{r}
+```{r, send_to = "R/processing.R"}
#' Subfunction: extract with buffers (flat weight; simple mean)
#'
#' @export
@@ -926,7 +959,7 @@ extract_with_buffer.flat <- function(
}
```
-```{r}
+```{r, send_to = "R/processing.R"}
#' Subfunction: extract with buffers (kernel weight; weighted mean)
#'
#' @export
@@ -955,7 +988,7 @@ extract_with_buffer.kernel <- function(
```
-```{r}
+```{r, send_to = "R/processing.R"}
#' @title Extract summarized values from raster with points and a buffer radius (to be written)
#'
#' @description For simplicity, it is assumed that the coordinate systems of the points and the raster are the same. Kernel function is not yet implemented.
@@ -1007,7 +1040,7 @@ extract_with_buffer <- function(
```
-```{r}
+```{r, send_to = "R/interpret_computational_domain.R"}
#' @title Process a given function in the entire or partial computational grids (under construction)
#'
#' @description Should
diff --git a/tarballs/scomps_0.0.2.10032023.tar.gz b/tarballs/scomps_0.0.2.10032023.tar.gz
new file mode 100644
index 0000000000000000000000000000000000000000..9f194ddcd35c000903b9fc653b4d0ac6bdfe2624
GIT binary patch
literal 21871
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
Yw*IU?>(BbL{$!v3A9$XTg8=3M0Md*I&j0`b
literal 0
HcmV?d00001
From 9ca2eae5c6374ff5266257aa249b38bab88be780 Mon Sep 17 00:00:00 2001
From: Insang Song
Date: Tue, 3 Oct 2023 12:05:22 -0400
Subject: [PATCH 04/21] Directory restructuring - While keeping litr Rmd,
others were adjusted - codecov edit to find if it works
---
.github/workflows/codecov.yaml | 2 +-
{containers => tools/containers}/apptainer_rocker | 0
.../slurm_test}/merra1h_aero_extract.r | 0
.../slurm_test}/merra1h_aero_extract_local.r | 0
.../slurm_test}/merra1h_aero_extract_local_stars.r | 0
.../slurm_test}/terra_runs_Rcode_file copy.sh | 0
.../slurm_test}/terra_runs_Rcode_file.sh | 0
.../tarballs}/scomps_0.0.1.09032023.tar.gz | Bin
.../tarballs}/scomps_0.0.2.09152023.tar.gz | Bin
.../tarballs}/scomps_0.0.2.09192023.tar.gz | Bin
.../tarballs}/scomps_0.0.2.10022023.tar.gz | Bin
.../tarballs}/scomps_0.0.2.10032023.tar.gz | Bin
.../00_good_practice_parallelization.Rmd | 0
.../01_generate_computational_grid.Rmd | 0
14 files changed, 1 insertion(+), 1 deletion(-)
rename {containers => tools/containers}/apptainer_rocker (100%)
rename {slurm_test => tools/slurm_test}/merra1h_aero_extract.r (100%)
rename {slurm_test => tools/slurm_test}/merra1h_aero_extract_local.r (100%)
rename {slurm_test => tools/slurm_test}/merra1h_aero_extract_local_stars.r (100%)
rename {slurm_test => tools/slurm_test}/terra_runs_Rcode_file copy.sh (100%)
rename {slurm_test => tools/slurm_test}/terra_runs_Rcode_file.sh (100%)
rename {tarballs => tools/tarballs}/scomps_0.0.1.09032023.tar.gz (100%)
rename {tarballs => tools/tarballs}/scomps_0.0.2.09152023.tar.gz (100%)
rename {tarballs => tools/tarballs}/scomps_0.0.2.09192023.tar.gz (100%)
rename {tarballs => tools/tarballs}/scomps_0.0.2.10022023.tar.gz (100%)
rename {tarballs => tools/tarballs}/scomps_0.0.2.10032023.tar.gz (100%)
rename {vignettes-sources => tools/vignettes-sources}/00_good_practice_parallelization.Rmd (100%)
rename {vignettes-sources => tools/vignettes-sources}/01_generate_computational_grid.Rmd (100%)
diff --git a/.github/workflows/codecov.yaml b/.github/workflows/codecov.yaml
index 30ba6e81..779871b8 100644
--- a/.github/workflows/codecov.yaml
+++ b/.github/workflows/codecov.yaml
@@ -27,7 +27,7 @@ jobs:
path: |
~/.cache/R
~/.local/share/R
- key: dependencies-${{ runner.os }}-${{ hashFiles('**/scomps/DESCRIPTION') }}
+ key: dependencies-${{ runner.os }}-${{ hashFiles('**/**/DESCRIPTION') }}
restore-keys: |
dependencies-${{ runner.os }}-
diff --git a/containers/apptainer_rocker b/tools/containers/apptainer_rocker
similarity index 100%
rename from containers/apptainer_rocker
rename to tools/containers/apptainer_rocker
diff --git a/slurm_test/merra1h_aero_extract.r b/tools/slurm_test/merra1h_aero_extract.r
similarity index 100%
rename from slurm_test/merra1h_aero_extract.r
rename to tools/slurm_test/merra1h_aero_extract.r
diff --git a/slurm_test/merra1h_aero_extract_local.r b/tools/slurm_test/merra1h_aero_extract_local.r
similarity index 100%
rename from slurm_test/merra1h_aero_extract_local.r
rename to tools/slurm_test/merra1h_aero_extract_local.r
diff --git a/slurm_test/merra1h_aero_extract_local_stars.r b/tools/slurm_test/merra1h_aero_extract_local_stars.r
similarity index 100%
rename from slurm_test/merra1h_aero_extract_local_stars.r
rename to tools/slurm_test/merra1h_aero_extract_local_stars.r
diff --git a/slurm_test/terra_runs_Rcode_file copy.sh b/tools/slurm_test/terra_runs_Rcode_file copy.sh
similarity index 100%
rename from slurm_test/terra_runs_Rcode_file copy.sh
rename to tools/slurm_test/terra_runs_Rcode_file copy.sh
diff --git a/slurm_test/terra_runs_Rcode_file.sh b/tools/slurm_test/terra_runs_Rcode_file.sh
similarity index 100%
rename from slurm_test/terra_runs_Rcode_file.sh
rename to tools/slurm_test/terra_runs_Rcode_file.sh
diff --git a/tarballs/scomps_0.0.1.09032023.tar.gz b/tools/tarballs/scomps_0.0.1.09032023.tar.gz
similarity index 100%
rename from tarballs/scomps_0.0.1.09032023.tar.gz
rename to tools/tarballs/scomps_0.0.1.09032023.tar.gz
diff --git a/tarballs/scomps_0.0.2.09152023.tar.gz b/tools/tarballs/scomps_0.0.2.09152023.tar.gz
similarity index 100%
rename from tarballs/scomps_0.0.2.09152023.tar.gz
rename to tools/tarballs/scomps_0.0.2.09152023.tar.gz
diff --git a/tarballs/scomps_0.0.2.09192023.tar.gz b/tools/tarballs/scomps_0.0.2.09192023.tar.gz
similarity index 100%
rename from tarballs/scomps_0.0.2.09192023.tar.gz
rename to tools/tarballs/scomps_0.0.2.09192023.tar.gz
diff --git a/tarballs/scomps_0.0.2.10022023.tar.gz b/tools/tarballs/scomps_0.0.2.10022023.tar.gz
similarity index 100%
rename from tarballs/scomps_0.0.2.10022023.tar.gz
rename to tools/tarballs/scomps_0.0.2.10022023.tar.gz
diff --git a/tarballs/scomps_0.0.2.10032023.tar.gz b/tools/tarballs/scomps_0.0.2.10032023.tar.gz
similarity index 100%
rename from tarballs/scomps_0.0.2.10032023.tar.gz
rename to tools/tarballs/scomps_0.0.2.10032023.tar.gz
diff --git a/vignettes-sources/00_good_practice_parallelization.Rmd b/tools/vignettes-sources/00_good_practice_parallelization.Rmd
similarity index 100%
rename from vignettes-sources/00_good_practice_parallelization.Rmd
rename to tools/vignettes-sources/00_good_practice_parallelization.Rmd
diff --git a/vignettes-sources/01_generate_computational_grid.Rmd b/tools/vignettes-sources/01_generate_computational_grid.Rmd
similarity index 100%
rename from vignettes-sources/01_generate_computational_grid.Rmd
rename to tools/vignettes-sources/01_generate_computational_grid.Rmd
From 661866aa9f8699e10dd795bcff35421ec0537e16 Mon Sep 17 00:00:00 2001
From: Insang Song
Date: Tue, 3 Oct 2023 12:53:49 -0400
Subject: [PATCH 05/21] YAML working directory edit
---
.github/workflows/check-standard.yaml | 1 +
.github/workflows/codecov.yaml | 2 +-
2 files changed, 2 insertions(+), 1 deletion(-)
diff --git a/.github/workflows/check-standard.yaml b/.github/workflows/check-standard.yaml
index a71ab36e..0e914c84 100644
--- a/.github/workflows/check-standard.yaml
+++ b/.github/workflows/check-standard.yaml
@@ -38,6 +38,7 @@ jobs:
- uses: r-lib/actions/setup-r-dependencies@v2
with:
+ working-directory: "./scomps"
extra-packages: any::rcmdcheck
needs: check
diff --git a/.github/workflows/codecov.yaml b/.github/workflows/codecov.yaml
index 779871b8..bff599f3 100644
--- a/.github/workflows/codecov.yaml
+++ b/.github/workflows/codecov.yaml
@@ -27,7 +27,7 @@ jobs:
path: |
~/.cache/R
~/.local/share/R
- key: dependencies-${{ runner.os }}-${{ hashFiles('**/**/DESCRIPTION') }}
+ key: dependencies-${{ runner.os }}-${{ hashFiles('**/DESCRIPTION') }}
restore-keys: |
dependencies-${{ runner.os }}-
From 01d2064aec02d251ce18ecea9da4423ed5ad34da Mon Sep 17 00:00:00 2001
From: Insang Song
Date: Tue, 3 Oct 2023 13:10:23 -0400
Subject: [PATCH 06/21] Added working-directory in relevant entries
---
.github/workflows/check-standard.yaml | 1 +
.github/workflows/codecov.yaml | 1 +
.github/workflows/test-coverage.yaml | 1 +
3 files changed, 3 insertions(+)
diff --git a/.github/workflows/check-standard.yaml b/.github/workflows/check-standard.yaml
index 0e914c84..70f743d2 100644
--- a/.github/workflows/check-standard.yaml
+++ b/.github/workflows/check-standard.yaml
@@ -44,4 +44,5 @@ jobs:
- uses: r-lib/actions/check-r-package@v2
with:
+ working-directory: "./scomps"
upload-snapshots: true
diff --git a/.github/workflows/codecov.yaml b/.github/workflows/codecov.yaml
index bff599f3..34b8f901 100644
--- a/.github/workflows/codecov.yaml
+++ b/.github/workflows/codecov.yaml
@@ -56,6 +56,7 @@ jobs:
with:
token: ${{ secrets.CODECOV_TOKEN }}
coverage_reports: ${{ github.workspace }}/coverage.xml
+ working-directory: "./scomps"
- name: Cleanup
run: |
diff --git a/.github/workflows/test-coverage.yaml b/.github/workflows/test-coverage.yaml
index ad146389..c34f5922 100644
--- a/.github/workflows/test-coverage.yaml
+++ b/.github/workflows/test-coverage.yaml
@@ -26,6 +26,7 @@ jobs:
with:
extra-packages: any::covr
needs: coverage
+ working-directory: "./scomps"
- name: Test coverage
run: |
From c6946d3977a9cc9212cab701b2dd4802bf2502cd Mon Sep 17 00:00:00 2001
From: Insang Song
Date: Tue, 3 Oct 2023 13:18:15 -0400
Subject: [PATCH 07/21] package path fix (2)
---
.github/workflows/codecov.yaml | 4 ++--
.github/workflows/test-coverage.yaml | 1 +
2 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/.github/workflows/codecov.yaml b/.github/workflows/codecov.yaml
index 34b8f901..cb8a71b8 100644
--- a/.github/workflows/codecov.yaml
+++ b/.github/workflows/codecov.yaml
@@ -40,7 +40,7 @@ jobs:
- name: Install dependencies
run: |
R -e 'install.packages("remotes")'
- R -e 'remotes::install_deps()'
+ R -e 'remotes::install_deps(pkgdir = "./scomps")'
- name: Install dependencies for covr and testthat
run: R -e 'install.packages(c("covr", "testthat"), dependencies = TRUE)'
@@ -48,7 +48,7 @@ jobs:
- name: Run tests and calculate coverage
run: |
echo "Current working directory: $(pwd)"
- R -e 'library(covr); covr::package_coverage(coverage_file = "coverage.xml")'
+ R -e 'library(covr); covr::package_coverage(path = "./scomps", coverage_file = "coverage.xml")'
- name: Upload coverage to Codecov
diff --git a/.github/workflows/test-coverage.yaml b/.github/workflows/test-coverage.yaml
index c34f5922..545aea4a 100644
--- a/.github/workflows/test-coverage.yaml
+++ b/.github/workflows/test-coverage.yaml
@@ -31,6 +31,7 @@ jobs:
- name: Test coverage
run: |
covr::codecov(
+ path = "./scomps",
quiet = FALSE,
clean = FALSE,
install_path = file.path(Sys.getenv("RUNNER_TEMP"), "package")
From 8f1433058d1c09cc53e8d77b7b3c8bc798cd3764 Mon Sep 17 00:00:00 2001
From: Insang Song
Date: Tue, 3 Oct 2023 13:27:21 -0400
Subject: [PATCH 08/21] Edited DESCRIPTION - added stars in "Imports"
---
scomps/DESCRIPTION | 3 ++-
scomps_rmarkdown_litr.html | 7 ++++---
scomps_rmarkdown_litr.rmd | 5 +++--
tools/tarballs/scomps_0.0.2.10032023.tar.gz | Bin 21871 -> 21884 bytes
4 files changed, 9 insertions(+), 6 deletions(-)
diff --git a/scomps/DESCRIPTION b/scomps/DESCRIPTION
index 2f72afbd..90815873 100644
--- a/scomps/DESCRIPTION
+++ b/scomps/DESCRIPTION
@@ -18,6 +18,7 @@ Imports:
future.apply,
rlang,
sf,
+ stars,
terra,
testthat
Suggests:
@@ -30,4 +31,4 @@ Suggests:
VignetteBuilder: knitr
Config/testthat/edition: 3
LitrVersionUsed: 0.9.0
-LitrId: 7c9484dd9127c939a90d433ec83cd305
+LitrId: f6481d6de18f4e515f064d81bc8ae4ed
diff --git a/scomps_rmarkdown_litr.html b/scomps_rmarkdown_litr.html
index 240b4444..35f9efea 100644
--- a/scomps_rmarkdown_litr.html
+++ b/scomps_rmarkdown_litr.html
@@ -385,6 +385,7 @@ Package setup
usethis::use_package("sf") # Default is "Imports"
# usethis::use_package("stars") # Default is "Imports"
usethis::use_package("terra") # Default is "Imports"
+usethis::use_package("stars")
usethis::use_package("rlang") # Default is "Imports"
usethis::use_package("testthat")
usethis::use_package("logr", "Suggests") # Default is "Imports"
@@ -406,11 +407,11 @@ Package setup
Vignettes 1
-litr::add_vignettes("../vignettes-sources/00_good_practice_parallelization.Rmd")
+litr::add_vignettes("../tools/vignettes-sources/00_good_practice_parallelization.Rmd")
Vignettes 2
-litr::add_vignettes("../vignettes-sources/01_generate_computational_grid.Rmd")
+litr::add_vignettes("../tools/vignettes-sources/01_generate_computational_grid.Rmd")
@@ -1422,7 +1423,7 @@ Documenting the package and building
## Warning: invalid gid value replaced by that for user 'nobody'
##
## Running /Library/Frameworks/R.framework/Resources/bin/R CMD INSTALL \
-## /var/folders/58/7rn_bn5d6k3_cxwnzdhswpz4n0z2n9/T//Rtmp2UqRyZ/scomps_0.0.2.10032023.tar.gz \
+## /var/folders/58/7rn_bn5d6k3_cxwnzdhswpz4n0z2n9/T//RtmpfPUv07/scomps_0.0.2.10032023.tar.gz \
## --install-tests
## * installing to library ‘/Users/songi2/Library/R/arm64/4.3/library’
## * installing *source* package ‘scomps’ ...
diff --git a/scomps_rmarkdown_litr.rmd b/scomps_rmarkdown_litr.rmd
index e67a1222..d0608b0c 100644
--- a/scomps_rmarkdown_litr.rmd
+++ b/scomps_rmarkdown_litr.rmd
@@ -38,6 +38,7 @@ usethis::use_package("dplyr") # Default is "Imports"
usethis::use_package("sf") # Default is "Imports"
# usethis::use_package("stars") # Default is "Imports"
usethis::use_package("terra") # Default is "Imports"
+usethis::use_package("stars")
usethis::use_package("rlang") # Default is "Imports"
usethis::use_package("testthat")
usethis::use_package("logr", "Suggests") # Default is "Imports"
@@ -66,13 +67,13 @@ usethis::use_package("future.batchtools", "Suggests") # Default is "Imports"
### Vignettes 1
```{r}
-litr::add_vignettes("../vignettes-sources/00_good_practice_parallelization.Rmd")
+litr::add_vignettes("../tools/vignettes-sources/00_good_practice_parallelization.Rmd")
```
### Vignettes 2
```{r}
-litr::add_vignettes("../vignettes-sources/01_generate_computational_grid.Rmd")
+litr::add_vignettes("../tools/vignettes-sources/01_generate_computational_grid.Rmd")
```
diff --git a/tools/tarballs/scomps_0.0.2.10032023.tar.gz b/tools/tarballs/scomps_0.0.2.10032023.tar.gz
index 9f194ddcd35c000903b9fc653b4d0ac6bdfe2624..6ac28a5d720b22e5a7de6fa4cfc69d66c1ae344e 100644
GIT binary patch
delta 20607
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
&Ym%uAz}N`Fp)P`?65NpH?i7>$-p9
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
delta 20605
zcmV)KK)S#DssZn+0e>Hh2mk;800003?Y-@K+qRZ4+TXegRBp#qE6I}ll`3`iPHne4
z@0%tWCv9gxX-1(%NMcNpDoNSa_IRGThx7NGo0}^+>jNMGl9HXoN!wJ~yRk$93%~-f
zzOdFpGK{9PWb5eo`Qgc(8l+u4D?`}i^b(toGB+ui@HyR+Nb?`&`H
zZ|{AEPxkgd6P?dK!e^eOZVX*XqHyf@mX~|-=pVlxMN`)g#ZQThlg~bl&og&;=8k1w
zB(xA}FZ?usU(bhb;GPCjoQScEl9`+OZXmG8JcaL3So;}p0Dt?U)9$o;?QW;D-RtzW
zYY*q?B#M*2pMUg4O~7Xc<)YB3KME5!9M_wo{yd_;@C`uw2_pXQV?b=s2KewIj-cHG
zF?8zgJjDvbSk@a&f~G!Da4N%;N
bRC#xSm?~_P2Yj?cVNQz0s&0$z&M&
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