Skip to content

Commit

Permalink
Merge pull request #83 from iiasa/dev
Browse files Browse the repository at this point in the history
Before christmas version push
  • Loading branch information
Martin-Jung authored Dec 14, 2023
2 parents f8d204b + 5158598 commit 1098bf0
Show file tree
Hide file tree
Showing 74 changed files with 2,682 additions and 649 deletions.
26 changes: 25 additions & 1 deletion CITATION.cff
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ message: 'To cite package "ibis.iSDM" in publications use:'
type: software
license: CC-BY-4.0
title: 'ibis.iSDM: Modelling framework for integrated biodiversity distribution scenarios'
version: 0.1.0
version: 0.1.1
abstract: Integrated framework of modelling the distribution of species and ecosystems
in a suitability framing. This package allows the estimation of integrated species
distribution models (iSDM) based on several sources of evidence and provided presence-only
Expand Down Expand Up @@ -344,6 +344,25 @@ references:
orcid: https://orcid.org/0000-0001-8049-7069
year: '2023'
version: '>= 0.5'
- type: software
title: zoo
abstract: 'zoo: S3 Infrastructure for Regular and Irregular Time Series (Z''s Ordered
Observations)'
notes: Imports
url: https://zoo.R-Forge.R-project.org/
repository: https://CRAN.R-project.org/package=zoo
authors:
- family-names: Zeileis
given-names: Achim
email: Achim.Zeileis@R-project.org
orcid: https://orcid.org/0000-0003-0918-3766
- family-names: Grothendieck
given-names: Gabor
email: ggrothendieck@gmail.com
- family-names: Ryan
given-names: Jeffrey A.
email: jeff.a.ryan@gmail.com
year: '2023'
- type: software
title: stats
abstract: 'R: A Language and Environment for Statistical Computing'
Expand Down Expand Up @@ -638,6 +657,7 @@ references:
authors:
- family-names: Csárdi
given-names: Gábor
email: csardi.gabor@gmail.com
- family-names: FitzJohn
given-names: Rich
year: '2023'
Expand Down Expand Up @@ -732,6 +752,10 @@ references:
- family-names: Weber
given-names: Sebastian
email: sdw.post@waebers.de
- family-names: Badr
given-names: Hamada S.
email: badr@jhu.edu
orcid: https://orcid.org/0000-0002-9808-2344
year: '2023'
version: '>= 2.21.0'
- type: software
Expand Down
5 changes: 4 additions & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Package: ibis.iSDM
Type: Package
Title: Modelling framework for integrated biodiversity distribution scenarios
Version: 0.1.0
Version: 0.1.1
Authors@R:
c(person(given = "Martin",
family = "Jung",
Expand Down Expand Up @@ -40,6 +40,7 @@ Imports:
lwgeom,
sf (>= 1.0),
stars (>= 0.5),
zoo,
stats,
tibble (>= 2.0.0),
uuid,
Expand Down Expand Up @@ -92,6 +93,7 @@ Collate:
'add_constraint.R'
'add_constraint_MigClim.R'
'add_control_bias.R'
'add_control_extrapolation.R'
'add_latent.R'
'bdproto-log.R'
'add_log.R'
Expand All @@ -115,6 +117,7 @@ Collate:
'engine_bart.R'
'engine_breg.R'
'engine_gdb.R'
'engine_glm.R'
'engine_glmnet.R'
'utils-inla.R'
'engine_inla.R'
Expand Down
5 changes: 5 additions & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ export(add_constraint_connectivity)
export(add_constraint_dispersal)
export(add_constraint_minsize)
export(add_control_bias)
export(add_control_extrapolation)
export(add_latent_spatial)
export(add_log)
export(add_offset)
Expand All @@ -95,6 +96,7 @@ export(emptyraster)
export(engine_bart)
export(engine_breg)
export(engine_gdb)
export(engine_glm)
export(engine_glmnet)
export(engine_inla)
export(engine_inlabru)
Expand All @@ -110,6 +112,7 @@ export(get_rastervalue)
export(ibis_dependencies)
export(ibis_future)
export(ibis_options)
export(interpolate_gaps)
export(is.Id)
export(is.Raster)
export(is.Waiver)
Expand Down Expand Up @@ -174,6 +177,7 @@ exportMethods(add_constraint_connectivity)
exportMethods(add_constraint_dispersal)
exportMethods(add_constraint_minsize)
exportMethods(add_control_bias)
exportMethods(add_control_extrapolation)
exportMethods(add_latent_spatial)
exportMethods(add_log)
exportMethods(add_offset)
Expand Down Expand Up @@ -226,3 +230,4 @@ importFrom(stats,terms.formula)
importFrom(stats,update.formula)
importFrom(terra,mask)
importFrom(utils,install.packages)
importFrom(zoo,na.approx)
16 changes: 15 additions & 1 deletion NEWS.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,18 @@
# ibis.iSDM 0.1.0 (current dev branch)
# ibis.iSDM 0.1.1 (current dev branch)

#### New features
* Added default `engine_glm()` for dependency-free inference and projection.
* Harmonized controls settings and added option to contrain extrapolation `add_control_extrapolation()`
* Adding a function for temporal interpolation of predictors #52

#### Minor improvements and bug fixes
* Minor corrective fixes and additions to `add_offset()`.
* Switch to `engine_glm()` in many of the unittests for better coverage.
* Several bug fixes and improvements in `thin_observations`
* `global`, `probs`, and `centers` argument for better control of `thin_observations`
* Harmonization of parameters for `spartial()` and addressing #80

# ibis.iSDM 0.1.0

#### New features
* Added a small convenience wrapper to add model outputs to another model `add_predictors_model()`
Expand Down
11 changes: 5 additions & 6 deletions R/add_biodiversity.R
Original file line number Diff line number Diff line change
Expand Up @@ -52,15 +52,14 @@ NULL
#' @keywords biodiversity
#' @aliases add_biodiversity_poipo
#' @examples
#' \dontrun{
#' background <- terra::rast("inst/extdata/europegrid_50km.tif")
#' # Load virtual species
#' virtual_species <- sf::st_read("inst/extdata/input_data.gpkg", "points")
#' # Load background
#' background <- terra::rast(system.file('extdata/europegrid_50km.tif', package='ibis.iSDM',mustWork = TRUE))
#' # Load virtual species
#' virtual_points <- sf::st_read(system.file('extdata/input_data.gpkg', package='ibis.iSDM',mustWork = TRUE),'points',quiet = TRUE)
#' # Define model
#' x <- distribution(background) |>
#' add_biodiversity_poipo(virtual_species)
#' add_biodiversity_poipo(virtual_points, field_occurrence = "Observed")
#' x
#' }
#' @name add_biodiversity_poipo
NULL

Expand Down
18 changes: 10 additions & 8 deletions R/add_control_bias.R
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#' Add a specified variable which should be controlled for somehow.
#' Add a control to a BiodiversityModel object to control biases
#'
#' @description Sampling and other biases are pervasive drivers of the spatial
#' location of biodiversity datasets. While the integration of other, presumably
Expand All @@ -10,10 +10,11 @@
#' control the biases in a model, by including a specified variable ("layer") in
#' the model, but "partialling" it out during the projection phase. Specifically
#' the variable is set to a specified value ("bias_value"), which is by default
#' the minimum value observed across the background. [*] \code{"offset"} - Dummy
#' the minimum value observed across the background.
#' [*] \code{"offset"} - Dummy
#' method that points to the [`add_offset_bias()`] functionality (see note).
#' Makes use of offsets to factor out a specified bias variable. [*]
#' \code{"proximity"} - Use the proximity or distance between points as a weight
#' Makes use of offsets to factor out a specified bias variable.
#' [*] \code{"proximity"} - Use the proximity or distance between points as a weight
#' in the model. This option effectively places greater weight on points farther
#' away. *Note:* In the best case this can control for spatial bias and
#' aggregation, in the worst case it can place a lot of emphasis on points that
Expand Down Expand Up @@ -71,7 +72,8 @@
#' * Merow, C., Allen, J.M., Aiello-Lammens, M., Silander, J.A., 2016. Improving niche and range estimates with Maxent and point process models by integrating spatially explicit information. Glob. Ecol. Biogeogr. 25, 1022–1036. https://doi.org/10.1111/geb.12453
#' * Botella, C., Joly, A., Bonnet, P., Munoz, F., & Monestiez, P. (2021). Jointly estimating spatial sampling effort and habitat suitability for multiple species from opportunistic presence‐only data. Methods in Ecology and Evolution, 12(5), 933-945.
#' @returns Adds bias control option to a [`distribution`] object.
#' @keywords bias, offset
#' @keywords bias, offset, control
#' @seealso [add_control_extrapolation()]
#' @aliases add_control_bias
#' @examples
#' \dontrun{
Expand Down Expand Up @@ -133,11 +135,11 @@ methods::setMethod(
if(method == "partial"){
if(getOption('ibis.setupmessages')) myLog('[Setup]','green','Adding bias controlled variable...')

if(!is.Waiver(x$get_biascontrol())){
if(!is.Waiver(x$get_control())){
if(getOption('ibis.setupmessages')) myLog('[Setup]','yellow','Overwriting existing bias variable...')
}
# Add to bias control
x <- x$set_biascontrol(layer, method, bias_value)
x <- x$set_control(type = "bias", layer, method, bias_value)

} else if(method == "offset") {
x <- x |> add_offset_bias(layer = layer, add = add)
Expand All @@ -147,7 +149,7 @@ methods::setMethod(
# Here we use proximity as a weight to any points. Those will be applied
# during the model training, thus we simply define the bias control here
if(is.null(maxdist)) maxdist <- 0
x <- x$set_biascontrol(method = method, value = c(maxdist, alpha))
x <- x$set_control(type = "bias", method = method, value = c(maxdist, alpha))
}
return(x)
}
Expand Down
175 changes: 175 additions & 0 deletions R/add_control_extrapolation.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
#' Add a control to a BiodiversityModel object to control extrapolation
#'
#' @description One of the main aims of species distribution models (SDMs) is to project
#' in space and time. For projections a common issue is extrapolation as - unconstrained -
#' SDMs can indicate areas as suitable which are unlikely to be occupied by species
#' or habitats (often due to historic or biotic factors). To some extent this can
#' be related to an insufficient quantification of the niche (e.g. niche truncation
#' by considering only a subset of observations within the actual distribution),
#' in other cases there can also be general barriers or constraints that limit
#' any projections (e.g. islands). This control method adds some of those options
#' to a model distribution object. Currently supported methods are:
#'
#' [*] \code{"zones"} - This is a wrapper to allow the addition of zones to a
#' distribution model object, similar to what is also possible via [distribution()].
#' Required is a spatial layer that describes a environmental zoning.
#'
#' [*] \code{"mcp"} - Rather than using an external or additional layer, this option constraints
#' predictions by a certain distance of points in its vicinity. Buffer distances
#' have to be in the unit of the projection used and can be configured via
#' \code{"mcp_buffer"}.
#'
#' [*] \code{"nt2"} - Constraints the predictions using the multivariate combination novelty index (NT2)
#' following Mesgaran et al. (2014). This method is also available in the [similarity()]
#' function.
#'
#' [*] \code{"shape"} - This is an implementation of the 'shape' method introduced
#' by Velazco et al. (2023). Through a user defined threshold it effectively limits
#' model extrapolation so that no projections are made beyond the extent judged as
#' defensible and informed by the training observations.
#'
#' See also details for further explanations.
#'
#' @details
#' For method \code{"zones"} a zoning layer can be supplied which is then used to intersect
#' the provided training points with. Any projections made with the model can
#' then be constrained so as to not project into areas that do not consider any
#' training points and are unlikely to have any. Examples for zones are for the
#' separation of islands and mainlands, biomes, or lithological soil conditions.
#'
#' If no layer is available, it is also possible to constraint predictions by the
#' distance to a minimum convex polygon surrounding the training points with
#' method \code{"mcp"} (optionally buffered). This can make sense particular for
#' rare species or those fully sampled across their niche.
#'
#' For the \code{"NT2"} and \code{"MESS"} index it is possible to constrain
#' the prediction to conditions within (\code{novel = "within"}) or also include
#' outside (\code{novel = "outside"}) conditions.
#'
#' @note
#' The method \code{"zones"} is also possible directly within [distribution()].
#'
#' @param x [distribution()] (i.e. [`BiodiversityDistribution-class`]) object.
#' @param layer A [`SpatRaster`] or [`sf`] object that limits the prediction
#' surface when intersected with input data (Default: \code{NULL}).
#' @param method A [`character`] vector describing the method used for controlling
#' extrapolation. Available options are \code{"zones"}, \code{"mcp"} (Default),
#' or \code{"nt2"} or \code{"shape"}.
#' @param mcp_buffer A [`numeric`] distance to buffer the mcp (Default
#' \code{0}). Only used if \code{"mcp"} is used.
#' @param novel Which conditions are to be masked out respectively, either the
#' novel conditions within only \code{"within"} (Default) or also including outside
#' reference conditions \code{"outside"}. Only use for \code{method = "nt2"}, for
#' \code{method = "mess"} this variable is always \code{"within"}.
#' @param limits_clip [`logical`] Should the limits clip all predictors before
#' fitting a model (\code{TRUE}) or just the prediction (\code{FALSE},
#' default).
#'
#' @references
#' * Randin, C. F., Dirnböck, T., Dullinger, S., Zimmermann, N. E., Zappa, M., & Guisan, A. (2006). Are niche‐based species distribution models transferable in space?. Journal of biogeography, 33(10), 1689-1703. https://doi.org/10.1111/j.1365-2699.2006.01466.x
#' * Chevalier, M., Broennimann, O., Cornuault, J., & Guisan, A. (2021). Data integration methods to account for spatial niche truncation effects in regional projections of species distribution. Ecological Applications, 31(7), e02427. https://doi.org/10.1002/eap.2427
#' * Velazco, S. J. E., Brooke, M. R., De Marco Jr., P., Regan, H. M., & Franklin, J. (2023). How far can I extrapolate my species distribution model? Exploring Shape, a novel method. Ecography, 11, e06992. https://doi.org/10.1111/ecog.06992
#' * Mesgaran, M. B., R. D. Cousens, B. L. Webber, and J. Franklin. (2014) Here be dragons: a tool for quantifying novelty due to covariate range and correlation change when projecting species distribution models. Diversity and Distributions 20:1147-1159.
#' @returns Adds extrapolation control option to a [`distribution`] object.
#' @keywords control
#' @aliases add_control_extrapolation
#' @examples
#' \dontrun{
#' # To add a zone layer for extrapolation constraints.
#' x <- distribution(background) |>
#' add_predictors(covariates) |>
#' add_control_extrapolation(method = "zones", layer = zones)
#' }
#' @name add_control_extrapolation
NULL

#' @name add_control_extrapolation
#' @rdname add_control_extrapolation
#' @exportMethod add_control_extrapolation
#' @export
methods::setGeneric(
"add_control_extrapolation",
signature = methods::signature("x"),
function(x, layer, method = "mcp", mcp_buffer = 0,
novel = "within", limits_clip = FALSE) standardGeneric("add_control_extrapolation"))

#' @name add_control_extrapolation
#' @rdname add_control_extrapolation
#' @usage
#' \S4method{add_control_extrapolation}{BiodiversityDistribution,SpatRaster,character,numeric,character,logical}(x,layer,method,mcp_buffer,novel,limits_clip)
methods::setMethod(
"add_control_extrapolation",
methods::signature(x = "BiodiversityDistribution"),
function(x, layer, method = "mcp", mcp_buffer = 0, novel = "within", limits_clip = FALSE) {
assertthat::assert_that(inherits(x, "BiodiversityDistribution"),
missing(layer) || (is.Raster(layer) || inherits(layer, "sf")),
(is.numeric(mcp_buffer) && mcp_buffer >=0),
is.logical(limits_clip),
is.character(novel),
is.character(method)
)
# Match method
method <- match.arg(method, c("zones", "mess", "nt2", "mcp", "shape"), several.ok = FALSE)
novel <- match.arg(novel, c("within", "outside"), several.ok = FALSE)

# Apply method specific settings
if(method == "zones"){
assertthat::assert_that((is.Raster(layer) || inherits(layer, "sf")),
msg = "No zone layer specified!")

if(inherits(layer,'SpatRaster')){
assertthat::assert_that(terra::is.factor(layer),
msg = 'Provided limit raster needs to be ratified (categorical)!')
layer <- sf::st_as_sf( terra::as.polygons(layer, dissolve = TRUE) ) |> sf::st_cast("MULTIPOLYGON")
}
assertthat::assert_that(inherits(layer, "sf"),
unique(sf::st_geometry_type(layer)) %in% c('MULTIPOLYGON','POLYGON'),
msg = "Limits need to be of polygon type."
)

# Get background
background <- x$background

# Ensure that limits has the same projection as background
if(sf::st_crs(layer) != sf::st_crs(background)) layer <- sf::st_transform(layer, background)

# Ensure that limits is intersecting the background
if(is.Raster(background)){
if(suppressMessages(length( sf::st_intersects(layer, terra::as.polygons(background) |> sf::st_as_sf()) )) == 0 ) { layer <- NULL; warning('Provided zones do not intersect the background!') }
} else {
if(suppressMessages(length( sf::st_intersects(layer, background |> sf::st_as_sf()) )) == 0 ) { layer <- NULL; warning('Provided zones do not intersect the background!') }
}

# Get first column for zone description and rename
layer <- layer[,1]; names(layer) <- c('limit','geometry')
limits <- list(layer = layer, "limits_method" = method,
"mcp_buffer" = mcp_buffer, "limits_clip" = limits_clip)
x <- x$set_limits(x = limits)
} else if(method == "mcp"){
# Specify the option to calculate a mcp based on the added data.
# This is done directly in train.
limits <- list("layer" = NULL, "limits_method" = method,
"mcp_buffer" = mcp_buffer, "limits_clip" = limits_clip)
x <- x$set_limits(x = limits)
} else if(method == "nt2"){
# Specify that the multivariate combination novelty index (NT2) is
# to be applied
limits <- list("layer" = NULL, "limits_method" = method,
"mcp_buffer" = mcp_buffer, "novel" = novel,
"limits_clip" = limits_clip)
x <- x$set_limits(x = limits)
} else if(method == "mess"){
# Specify that the multivariate combination novelty index (NT2) is
# to be applied
limits <- list("layer" = NULL, "limits_method" = method,
"mcp_buffer" = mcp_buffer, "novel" = novel,
"limits_clip" = limits_clip)
x <- x$set_limits(x = limits)
} else if(method == "shape"){
stop("Method not yet implemented")
}

# Return the altered object
return(x)
}
)
Loading

0 comments on commit 1098bf0

Please sign in to comment.