Skip to content

Commit

Permalink
Merge pull request #111 from poissonconsulting/spatial
Browse files Browse the repository at this point in the history
  • Loading branch information
joethorley authored Feb 18, 2025
2 parents 9f42c93 + 338aac3 commit bee9936
Show file tree
Hide file tree
Showing 73 changed files with 750 additions and 48 deletions.
1 change: 1 addition & 0 deletions DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ License: MIT + file LICENSE
Imports:
chk,
data.table,
dplyr,
DBI,
dttr2,
fs,
Expand Down
4 changes: 4 additions & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ export(sbf_load_plot_data)
export(sbf_load_plots_data)
export(sbf_load_plots_data_recursive)
export(sbf_load_plots_recursive)
export(sbf_load_spatial)
export(sbf_load_spatials)
export(sbf_load_string)
export(sbf_load_strings)
export(sbf_load_strings_recursive)
Expand Down Expand Up @@ -119,6 +121,8 @@ export(sbf_save_object)
export(sbf_save_objects)
export(sbf_save_plot)
export(sbf_save_png)
export(sbf_save_spatial)
export(sbf_save_spatials)
export(sbf_save_string)
export(sbf_save_strings)
export(sbf_save_table)
Expand Down
42 changes: 42 additions & 0 deletions R/load.R
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ load_rds <- function(x_name, class, sub, main, fun = NULL, exists = TRUE) {
if (!is.null(fun)) {
object <- fun(object)
}

if(class == "spatial"){
valid <- valid_spatial(object)
if(!valid) wrn(backtick_chk(x_name), "is not a valid spatial object.")
}

object
}

Expand Down Expand Up @@ -40,6 +46,21 @@ sbf_load_data <- function(x_name,
load_rds(x_name, class = "data", sub = sub, main = main, exists = exists)
}

#' Load Spatial Data
#'
#' Loads an sf tbl that must meet the same requirements as `sbf_save_spatial`.
#' @inheritParams sbf_save_object
#' @inheritParams sbf_load_object
#' @return An sf tbl or NULL if doesn't exist.
#' @family load functions
#' @export
sbf_load_spatial <- function(x_name,
sub = sbf_get_sub(),
main = sbf_get_main(),
exists = TRUE) {
load_rds(x_name, class = "spatial", sub = sub, main = main, exists = exists)
}

#' Load Number
#'
#' @inheritParams sbf_save_object
Expand Down Expand Up @@ -189,6 +210,12 @@ load_rdss <- function(class, sub, main, env, rename, fun = NULL) {
names <- tools::file_path_sans_ext(basename(files))
for (i in seq_along(files)) {
object <- readRDS(files[i])

if(class == "spatial"){
valid <- valid_spatial(object)
if(!valid) wrn(backtick_chk(names[i]), "is not a valid spatial object.")
}

if (!is.null(fun)) {
object <- fun(object)
}
Expand Down Expand Up @@ -228,6 +255,21 @@ sbf_load_datas <- function(sub = sbf_get_sub(),
load_rdss("data", sub = sub, main = main, env = env, rename = rename)
}

#' Load Spatial Datas
#'
#' Loads sf tbls that must meet the same requirements as `sbf_save_spatials`.
#' @inheritParams sbf_save_object
#' @inheritParams sbf_load_objects
#' @return A invisble character vector of the data frames' names.
#' @family load functions
#' @export
sbf_load_spatials <- function(sub = sbf_get_sub(),
main = sbf_get_main(),
rename = identity,
env = parent.frame()) {
load_rdss("spatial", sub = sub, main = main, env = env, rename = rename)
}

#' Load Tables
#'
#' @inheritParams sbf_save_object
Expand Down
148 changes: 148 additions & 0 deletions R/save.R
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
file_name <- function(main, class, sub, x_name, ext) {
dir <- file_path(main, class, sub)
dir_create(dir)
x_name <- chk::unbacktick_chk(x_name)
file <- file_path(dir, x_name)
ext <- sub("[.]$", "", ext)
if (!identical(ext, file_ext(x_name))) {
Expand Down Expand Up @@ -217,6 +218,116 @@ sbf_save_data <- function(x, x_name = substitute(x), sub = sbf_get_sub(),
save_rds(x, "data", sub = sub, main = main, x_name = x_name)
}

#' Save Spatial Data
#'
#' Saves an sf tbl with at least one row
#' for which the first column (not a geometry) is unique
#' with no missing values and only one geometry column which must have a defined projection.
#'
#' @param x The sf tbl to save.
#' @inheritParams sbf_save_object
#' @return An invisible string of the path to the saved data.frame
#' @family save functions
#' @export
sbf_save_spatial <- function(x, x_name = NULL, sub = sbf_get_sub(),
main = sbf_get_main()) {

if(is.null(x_name)) {
x_name <- chk::deparse_backtick_chk(substitute(x))
}

chk_data(x, x_name = x_name)
chk_character(sub)
chk_range(length(sub))
chk_string(main)

check_spatial(x, x_name = x_name)

sub <- sanitize_path(sub)
main <- sanitize_path(main, rm_leading = FALSE)

save_rds(x, "spatial", sub = sub, main = main, x_name = x_name)
}

check_spatial <- function(x, x_name = NULL) {
rlang::check_installed("sf")

if(is.null(x_name)) {
x_name <- chk::deparse_backtick_chk(substitute(x))
}
chk_s3_class(x, "sf", x_name = x_name)

check_dim(x, nrow, values = c(1L, Inf), x_name = x_name)
check_dim(x, ncol, values = c(2L, Inf), x_name = x_name)

if(is.na(sf::st_crs(x))) {
err(x_name, " must not have a missing projection")
}

geom_name <- dplyr::select(x, dplyr::where(is.sfc)) |>
colnames()

if(length(geom_name) != 1L) {
err(x_name, " must have exactly one geometry column")
}

index_name <- colnames(x)[1]

if(index_name == geom_name) {
err(x_name, " must not have a first (index) column that is also the geometry column")
}

if(!chk::vld_not_any_na(x[[index_name]])) {
err(x_name, " must not have a first (index) column with missing values")
}

if(!chk::vld_unique(x[[index_name]])) {
err(x_name, " must not have a first (index) column with duplicated values")
}

invisible(x)
}

valid_spatial <- function(x) {
rlang::check_installed("sf")

if(!vld_s3_class(x, "sf")) return(FALSE)

try <- try(check_dim(x, nrow, values = c(1L, Inf)), silent = TRUE)
if(inherits(try, "try-error")) return(FALSE)

try <- try(check_dim(x, ncol, values = c(2L, Inf)), silent = TRUE)
if(inherits(try, "try-error")) return(FALSE)

if(is.na(sf::st_crs(x))) {
return(FALSE)
}

geom_name <- dplyr::select(x, dplyr::where(is.sfc)) |>
colnames()

if(length(geom_name) != 1L) {
return(FALSE)
}

index_name <- colnames(x)[1]

if(index_name == geom_name) {
return(FALSE)
}

if(!chk::vld_not_any_na(x[[index_name]])) {
return(FALSE)
}

if(!chk::vld_unique(x[[index_name]])) {
return(FALSE)
}

return(TRUE)
}


#' Save Number
#'
#' @param x The number to save.
Expand Down Expand Up @@ -835,6 +946,43 @@ sbf_save_datas <- function(sub = sbf_get_sub(),
invisible(names)
}

#' Save Spatial Data Frames
#'
#' Saves sf tbls each with at least one row
#' for which the first column (not a geometry) is unique
#' with no missing values and only one geometry column which must have a defined projection.
#' The functions expects that all data frames in the environment meet these requirements.
#'
#' @inheritParams sbf_save_object
#' @inheritParams sbf_save_objects
#' @return An invisible character vector of the paths to the saved objects.
#' @family save functions
#' @export
sbf_save_spatials <- function(sub = sbf_get_sub(),
main = sbf_get_main(), env = parent.frame()) {
chk_s3_class(env, "environment")

names <- objects(envir = env)
is <- vector("logical", length(names))
for (i in seq_along(names)) {
x_name <- names[i]
x <- get(x = x_name, envir = env)
is[i] <- is.data.frame(x)
if (is[i]) {
x_name <- chk::backtick_chk(x_name)
sbf_save_spatial(x, x_name, sub, main)
}
}
names <- names[is]
if (!length(names)) {
warning("no spatial datas to save")
invisible(character(0))
}
names <- file_path(main, "spatial", sub, names)
names <- p0(names, ".rds")
invisible(names)
}

#' Save Numbers
#'
#' @inheritParams sbf_save_object
Expand Down
4 changes: 4 additions & 0 deletions _pkgdown.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ reference:
- '`sbf_save_objects`'
- '`sbf_save_plot`'
- '`sbf_save_png`'
- '`sbf_save_spatial`'
- '`sbf_save_spatials`'
- '`sbf_save_string`'
- '`sbf_save_strings`'
- '`sbf_save_table`'
Expand Down Expand Up @@ -48,6 +50,8 @@ reference:
- '`sbf_load_plots_data`'
- '`sbf_load_plots_data_recursive`'
- '`sbf_load_plots_recursive`'
- '`sbf_load_spatial`'
- '`sbf_load_spatials`'
- '`sbf_load_string`'
- '`sbf_load_strings`'
- '`sbf_load_strings_recursive`'
Expand Down
2 changes: 2 additions & 0 deletions man/sbf_basename_sans_ext.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions man/sbf_load_block.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions man/sbf_load_blocks.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions man/sbf_load_blocks_recursive.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions man/sbf_load_data.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions man/sbf_load_data_from_db.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions man/sbf_load_data_from_pg.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions man/sbf_load_datas.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions man/sbf_load_datas_from_db.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions man/sbf_load_datas_from_pg.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions man/sbf_load_datas_recursive.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions man/sbf_load_db_metatable.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions man/sbf_load_number.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit bee9936

Please sign in to comment.