diff --git a/IsoriX/NAMESPACE b/IsoriX/NAMESPACE index 65f5214..dda3437 100644 --- a/IsoriX/NAMESPACE +++ b/IsoriX/NAMESPACE @@ -10,6 +10,8 @@ S3method(print,CALIBFIT) S3method(print,ISOFIND) S3method(print,ISOFIT) S3method(print,ISOSCAPE) +S3method(readRDS,character) +S3method(saveRDS,ISOSCAPE) S3method(summary,CALIBFIT) S3method(summary,ISOFIND) S3method(summary,ISOFIT) @@ -45,9 +47,14 @@ export(prepcipitate) export(prepraster) export(prepsources) export(rast) +export(readRDS) +export(saveRDS) export(shift) export(values) export(xyplot) +exportClasses(ISOSCAPE) +exportMethods(readRDS) +exportMethods(saveRDS) importFrom(grid,gpar) importFrom(grid,grid.text) importFrom(lattice,panel.points) diff --git a/IsoriX/R/S4classes.R b/IsoriX/R/S4classes.R new file mode 100644 index 0000000..bf842cc --- /dev/null +++ b/IsoriX/R/S4classes.R @@ -0,0 +1,11 @@ +# This S4 definitions of classes are required for saveRDS to work and interact properly with the package terra. + +#' Class ISOSCAPE +#' +#' @slot isoscapes a SpatRaster storing the isoscapes. +#' @slot sp_points a list of spatial points. +#' +#' @export +#' @rdname ISOSCAPE-class +#' +setClass("ISOSCAPE", slots = c(isoscapes = "SpatRaster", sp_points = "list")) \ No newline at end of file diff --git a/IsoriX/R/serialize.R b/IsoriX/R/serialize.R new file mode 100644 index 0000000..909da89 --- /dev/null +++ b/IsoriX/R/serialize.R @@ -0,0 +1,172 @@ +#' Save and read objects produced by IsoriX into RDS files +#' +#' Because files created with IsoriX contain [`terra::SpatRaster`] and +#' [`terra::SpatVector`] objects, they cannot be saved using [`base::saveRDS`] or +#' [`base::save`] functions. The reason is that objects created with [terra] point +#' to data stored in memory which are not contained in the R objects themselves. +#' Adapting the approach implemented in the [terra] package, we provide a +#' wrapper for [`base::saveRDS`] and [`base::readRDS`] functions, which allows one +#' to save and read objects produced with IsoriX by simply using `saveRDS()` and +#' `readRDS()`. +#' +#' [`base::saveRDS`] and [`base::readRDS`] are standard S3 functions. So in +#' order to be able to have a specific behaviour for objects produced with +#' IsoriX, we turned `saveRDS` and `readRDS` into S4 generics that dispatch both +#' S3 and S4 methods (see [Methods_for_S3]). The S3 implementation is consistent +#' with the rest of the package and presents all usual benefits associated with +#' S3 methods (e.g. simple access to the code). The S4 implementation makes +#' IsoriX methods compatible with the use of [`terra::saveRDS`] and +#' [`terra::readRDS`]. +#' +#' @param object R object to serialize. +#' @param file a connection or the name of the file where the R object is saved to or read from. +#' @param ascii a logical. If `TRUE` or `NA`, an ASCII representation is written; otherwise (default), a binary one is used. See the comments in the help for save. +#' @param version the workspace format version to use. `NULL` specifies the current default version (3). The only other supported value is `2`, the default from R 1.4.0 to R 3.5.0. +#' @param compress a logical specifying whether saving to a named file is to use "gzip" compression, or one of "gzip", "bzip2" or "xz" to indicate the type of compression to be used. Ignored if file is a connection. +#' @param refhook a hook function for handling reference objects. +#' +#' @name serialize +#' +#' @return +#' For `saveRDS`, `NULL` invisibly. +#' +#' For `readRDS`, an R object. +#' +#' @examples +#' if (getOption_IsoriX("example_maxtime") > 30) { +#' +#' ## We prepare the data +#' GNIPDataDEagg <- prepsources(data = GNIPDataDE) +#' +#' ## We fit the models +#' GermanFit <- isofit(data = GNIPDataDEagg, +#' mean_model_fix = list(elev = TRUE, lat_abs = TRUE)) +#' +#' ## We build the isoscapes +#' GermanScape <- isoscape(raster = ElevRasterDE, isofit = GermanFit) +#' +#' ## Saving as RDS +#' filename <- tempfile(fileext = ".rds") # or whatever names you want +#' saveRDS(GermanScape, file = filename) +#' +#' ## Reading RDS +#' GermanScape2 <- readRDS(filename) +#' GermanScape2 +#' +#' ## Saving data.frame object as RDS +#' filename2 <- tempfile(fileext = ".rds") # or whatever names you want +#' saveRDS(iris, file = filename2) +#' +#' ## Reading RDS containing data.frame +#' iris2 <- readRDS(filename2) +#' iris2 +#' +#' ## Saving terra object as RDS +#' filename3 <- tempfile(fileext = ".rds") # or whatever names you want +#' f <- system.file("ex/elev.tif", package="terra") +#' r <- rast(f) +#' saveRDS(r, file = filename3) +#' +#' ## Reading RDS containing terra object +#' r2 <- readRDS(filename3) +#' r2 +#' +#' } +#' +NULL + + +# Defining S4 generics ----------------------------------------------------- + +#' @describeIn serialize S4 generic function for `saveRDS` +#' +#' @export +#' +saveRDS <- function(object, file = "", ascii = FALSE, version = NULL, compress = TRUE, refhook = NULL) UseMethod("saveRDS") + +#' @describeIn serialize S4 generic function for `readRDS` +#' +#' @export +#' +readRDS <- function(file, refhook = NULL) UseMethod("readRDS") + + +# Defining S3 methods ----------------------------------------------------- + +#' @describeIn serialize S3 method to save an `ISOSCAPE` object into a RDS file +#' +#' @method saveRDS ISOSCAPE +#' @exportS3Method saveRDS ISOSCAPE +#' +saveRDS.ISOSCAPE <- function(object, file = "", ascii = FALSE, version = NULL, compress = TRUE, refhook = NULL) { + message("Saving RDS using IsoriX method") + if (!is.null(object$isoscapes)) { + if (inherits(object$isoscapes, "SpatRaster")) { + object$isoscapes <- terra::wrap(object$isoscapes) + } else { + stop("Saving situation not implemented yet. Please contact the package maintainer.") + } + } + if (!is.null(object$sp_points)) { + object$sp_points <- lapply(object$sp_points, \(x) { + if (inherits(x, "SpatVector")) { + terra::wrap(x) + } else { + stop("Saving situation not implemented yet. Please contact the package maintainer.") + } + }) + } + # class(object) <- setdiff("ISOSCAPE", class(object)) + base::saveRDS(object, file = file, ascii = ascii, version = version, compress = compress, refhook = refhook) +} + +#' @describeIn serialize S3 method to read an object produced with IsoriX (or other) stored in a RDS file +#' +#' @method readRDS character +#' @exportS3Method saveRDS ISOSCAPE +#' @export +#' +readRDS.character <- function(file, refhook = NULL) { + message("Reading RDS using IsoriX wrapper") + object <- base::readRDS(file = file, refhook = refhook) + if (inherits(object, "PackedSpatRaster") || inherits(object, "PackedSpatVector")) { + return(terra::unwrap(object)) + } + if (!inherits(object, "ISOSCAPE")) { + return(object) + } + if (!is.null(object$isoscapes)) { + if (inherits(object$isoscapes, "PackedSpatRaster")) { + object$isoscapes <- terra::unwrap(object$isoscapes) + } else { + stop("Saving situation not implemented yet. Please contact the package maintainer.") + } + } + if (!is.null(object$sp_points)) { + object$sp_points <- lapply(object$sp_points, \(x) { + if (inherits(x, "PackedSpatVector")) { + terra::unwrap(x) + } else { + stop("Saving situation not implemented yet. Please contact the package maintainer.") + } + }) + } + object +} + + +# Defining S4 methods ----------------------------------------------------- + +#' @describeIn serialize S4 method to save an `ISOSCAPE` object into a RDS file +#' +#' @method saveRDS ISOSCAPE +#' @export +#' +setMethod("saveRDS", signature(object = "ISOSCAPE"), saveRDS.ISOSCAPE) + +#' @describeIn serialize S4 method to read an object produced with IsoriX (or other) stored in a RDS file +#' +#' @method readRDS character +#' @export +#' +setMethod("readRDS", signature(file = "character"), readRDS.character) diff --git a/IsoriX/man/ISOSCAPE-class.Rd b/IsoriX/man/ISOSCAPE-class.Rd new file mode 100644 index 0000000..9ffa01b --- /dev/null +++ b/IsoriX/man/ISOSCAPE-class.Rd @@ -0,0 +1,17 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/S4classes.R +\docType{class} +\name{ISOSCAPE-class} +\alias{ISOSCAPE-class} +\title{Class ISOSCAPE} +\description{ +Class ISOSCAPE +} +\section{Slots}{ + +\describe{ +\item{\code{isoscapes}}{a SpatRaster storing the isoscapes.} + +\item{\code{sp_points}}{a list of spatial points.} +}} + diff --git a/IsoriX/man/serialize.Rd b/IsoriX/man/serialize.Rd new file mode 100644 index 0000000..759856f --- /dev/null +++ b/IsoriX/man/serialize.Rd @@ -0,0 +1,140 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/serialize.R +\name{serialize} +\alias{serialize} +\alias{saveRDS} +\alias{readRDS} +\alias{saveRDS.ISOSCAPE} +\alias{readRDS.character} +\alias{saveRDS,ISOSCAPE-method} +\alias{readRDS,character-method} +\title{Save and read objects produced by IsoriX into RDS files} +\usage{ +saveRDS( + object, + file = "", + ascii = FALSE, + version = NULL, + compress = TRUE, + refhook = NULL +) + +readRDS(file, refhook = NULL) + +\method{saveRDS}{ISOSCAPE}( + object, + file = "", + ascii = FALSE, + version = NULL, + compress = TRUE, + refhook = NULL +) + +\method{readRDS}{character}(file, refhook = NULL) + +\S4method{saveRDS}{ISOSCAPE}( + object, + file = "", + ascii = FALSE, + version = NULL, + compress = TRUE, + refhook = NULL +) + +\S4method{readRDS}{character}(file, refhook = NULL) +} +\arguments{ +\item{object}{R object to serialize.} + +\item{file}{a connection or the name of the file where the R object is saved to or read from.} + +\item{ascii}{a logical. If \code{TRUE} or \code{NA}, an ASCII representation is written; otherwise (default), a binary one is used. See the comments in the help for save.} + +\item{version}{the workspace format version to use. \code{NULL} specifies the current default version (3). The only other supported value is \code{2}, the default from R 1.4.0 to R 3.5.0.} + +\item{compress}{a logical specifying whether saving to a named file is to use "gzip" compression, or one of "gzip", "bzip2" or "xz" to indicate the type of compression to be used. Ignored if file is a connection.} + +\item{refhook}{a hook function for handling reference objects.} +} +\value{ +For \code{saveRDS}, \code{NULL} invisibly. + +For \code{readRDS}, an R object. +} +\description{ +Because files created with IsoriX contain \code{\link[terra:SpatRaster-class]{terra::SpatRaster}} and +\code{\link[terra:SpatVector-class]{terra::SpatVector}} objects, they cannot be saved using \code{\link[base:readRDS]{base::saveRDS}} or +\code{\link[base:save]{base::save}} functions. The reason is that objects created with \link{terra} point +to data stored in memory which are not contained in the R objects themselves. +Adapting the approach implemented in the \link{terra} package, we provide a +wrapper for \code{\link[base:readRDS]{base::saveRDS}} and \code{\link[base:readRDS]{base::readRDS}} functions, which allows one +to save and read objects produced with IsoriX by simply using \code{saveRDS()} and +\code{readRDS()}. +} +\details{ +\code{\link[base:readRDS]{base::saveRDS}} and \code{\link[base:readRDS]{base::readRDS}} are standard S3 functions. So in +order to be able to have a specific behaviour for objects produced with +IsoriX, we turned \code{saveRDS} and \code{readRDS} into S4 generics that dispatch both +S3 and S4 methods (see \link{Methods_for_S3}). The S3 implementation is consistent +with the rest of the package and presents all usual benefits associated with +S3 methods (e.g. simple access to the code). The S4 implementation makes +IsoriX methods compatible with the use of \code{\link[terra:serialize]{terra::saveRDS}} and +\code{\link[terra:serialize]{terra::readRDS}}. +} +\section{Functions}{ +\itemize{ +\item \code{saveRDS()}: S4 generic function for \code{saveRDS} + +\item \code{readRDS()}: S4 generic function for \code{readRDS} + +\item \code{saveRDS(ISOSCAPE)}: S3 method to save an \code{ISOSCAPE} object into a RDS file + +\item \code{readRDS(character)}: S3 method to read an object produced with IsoriX (or other) stored in a RDS file + +\item \code{saveRDS(ISOSCAPE)}: S4 method to save an \code{ISOSCAPE} object into a RDS file + +\item \code{readRDS(character)}: S4 method to read an object produced with IsoriX (or other) stored in a RDS file + +}} +\examples{ +if (getOption_IsoriX("example_maxtime") > 30) { + +## We prepare the data +GNIPDataDEagg <- prepsources(data = GNIPDataDE) + +## We fit the models +GermanFit <- isofit(data = GNIPDataDEagg, + mean_model_fix = list(elev = TRUE, lat_abs = TRUE)) + +## We build the isoscapes +GermanScape <- isoscape(raster = ElevRasterDE, isofit = GermanFit) + +## Saving as RDS +filename <- tempfile(fileext = ".rds") # or whatever names you want +saveRDS(GermanScape, file = filename) + +## Reading RDS +GermanScape2 <- readRDS(filename) +GermanScape2 + +## Saving data.frame object as RDS +filename2 <- tempfile(fileext = ".rds") # or whatever names you want +saveRDS(iris, file = filename2) + +## Reading RDS containing data.frame +iris2 <- readRDS(filename2) +iris2 + +## Saving terra object as RDS +filename3 <- tempfile(fileext = ".rds") # or whatever names you want +f <- system.file("ex/elev.tif", package="terra") +r <- rast(f) +saveRDS(r, file = filename3) + +## Reading RDS containing terra object +r2 <- readRDS(filename3) +r2 + +} + +}