Skip to content

Commit

Permalink
Version 1.2.0
Browse files Browse the repository at this point in the history
  • Loading branch information
dimitri-justeau committed Oct 11, 2022
1 parent 404a44e commit 84ecf4a
Show file tree
Hide file tree
Showing 58 changed files with 303 additions and 169 deletions.
8 changes: 4 additions & 4 deletions DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Package: rflsgen
Type: Package
Title: Neutral Landscape Generator with Targets on Landscape Indices
Version: 1.0.0
Version: 1.2.0
Author: Dimitri Justeau-Allaire, Grégoire Blanchard, Thomas Ibanez, Xavier Lorca, Ghislain Vieilledent, Philippe Birnbaum
Maintainer: Dimitri Justeau-Allaire <[email protected]>
Description: Interface to the 'flsgen' neutral landscape generator <https://github.com/dimitri-justeau/flsgen>. It allows to
Expand All @@ -13,9 +13,9 @@ Encoding: UTF-8
LazyData: true
Depends:
rJava,
raster,
terra (>= 1.5-12)
Imports: rgdal, checkmate, utils, jsonlite
terra (>= 1.5-12),
jsonlite
Imports: rgdal, checkmate, utils
SystemRequirements: Java (>= 8)
RoxygenNote: 7.2.0
Suggests: testthat (>= 3.0.0), knitr, rmarkdown, landscapemetrics
Expand Down
3 changes: 2 additions & 1 deletion NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@ export(flsgen_extract_structure_from_raster)
export(flsgen_generate)
export(flsgen_structure)
export(flsgen_terrain)
import(jsonlite)
import(rJava)
import(raster)
import(terra)
13 changes: 13 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,16 @@
# rflsgen 1.2.0

* Rely on flsgen-1.2.0, which is now divided into modules. The R package only relies on the flsgen-core-1.2.0, which
allowed to remove dependencies to Geotools, resulting in a much lighter JAR that can now be directly package
into rflsgen (no need to download anymore). This also reduces the time that was need for read and write operation
for exchanging rasters between R and Java, now only data is exchanged.

* Add IS_SQUARE target, which allows to produce only square patches.

* Add ALL_DIFFERENT target, which allows to produce patches having all different areas.

* Remove dependency to the raster package, and now switch to terra.

# rflsgen 1.0.0

* Rely on flsgen-1.1.0, which fixes several bug, improve performances, provide two new indices (AREA_MN, mean patch area and NON_FOCAL_PLAND, which is PLAND applied to the non-focal class), implement variable neighborhood for more flexible distance between patches in landscape generation, masking, landscape extraction from existing rasters, and more fine tuning.
Expand Down
2 changes: 1 addition & 1 deletion R/flsgen_available_targets.R
Original file line number Diff line number Diff line change
Expand Up @@ -38,5 +38,5 @@ CLASS_LEVEL_TARGETS <- c(
"COHE", # Degree of coherence
"DIVI", # Degree of division
"IS_SQUARE", # If TRUE, all patches are square
"patchesAllDifferent" # If TRUE, all patches must have different size
"ALL_DIFFERENT" # If TRUE, all patches must have different size
)
5 changes: 3 additions & 2 deletions R/flsgen_create_class_targets.R
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
#' @param COHE degree of coherence target (must be a vector of length 2)
#' @param DIVI degree of landscape division target (must be a vector of length 2)
#' @param IS_SQUARE if TRUE, the class is required to only produce square patches
#' @param ALL_DIFFERENT if TRUE, the class is required to have differently sized patches
#'
#' @return A class targets object which can be converted to JSON for flsgen
#'
Expand All @@ -58,14 +59,14 @@ flsgen_create_class_targets <- function(class_name, NP=NULL, AREA=NULL, AREA_MN=
CA=NULL, PLAND=NULL, PD=NULL, SPI=NULL,
LPI=NULL, MESH=NULL, SPLI=NULL, NPRO=NULL,
SDEN=NULL, COHE=NULL, DIVI=NULL, IS_SQUARE=FALSE,
patchesAllDifferent=FALSE) {
ALL_DIFFERENT=FALSE) {
checkmate::assert_string(class_name)
class_targets <- list(
name=class_name
)
for (i in CLASS_LEVEL_TARGETS) {
if (!is.null(get(i))) {
if (i %in% c("IS_SQUARE", "patchesAllDifferent")) {
if (i %in% c("IS_SQUARE", "ALL_DIFFERENT")) {
checkmate::assert_flag(get(i))
} else {
checkmate::assert_vector(get(i), len = 2)
Expand Down
18 changes: 9 additions & 9 deletions R/flsgen_create_landscape_structure.R
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@
#' @param nb_rows Number of rows
#' @param nb_cols Number of columns
#' @param classes list of class structures
#' @param mask_raster mask raster (path or raster object)
#' @param mask_raster mask raster (path or terra::rast object)
#'
#' @import terra
#'
#' @details Either nb_rows and nb_cols, or mask_raster must be specified. The dimensions
#' of the landscape are deduced from the mask raster if it is used.
Expand Down Expand Up @@ -62,16 +64,14 @@ flsgen_create_landscape_structure <- function(nb_rows, nb_cols, classes, mask_ra
lstruct$nbRows <- nb_rows
lstruct$nbCols <- nb_cols
} else {
if (inherits(mask_raster, "Raster")) {
if (nchar(filename(mask_raster)) > 0) {
mask_raster <- filename(mask_raster)
} else {
file_name <- tempfile(fileext = ".tif")
writeRaster(mask_raster, file_name)
mask_raster <- file_name
if (inherits(mask_raster, "SpatRaster")) {
s <- terra::sources(mask_raster)[[1]]
on_disk <- all(nchar(s > 0) && all(file.exists(s)))
if (on_disk) {
lstruct$maskRasterPath <- s
}
lstruct$maskRaster <- mask_raster
}
lstruct$maskRasterPath <- mask_raster
}
return(structure(lstruct, class="FlsgenLandscapeStructure"))
}
18 changes: 9 additions & 9 deletions R/flsgen_create_landscape_targets.R
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,11 @@
#' @param nb_rows Number of rows
#' @param nb_cols Number of columns
#' @param classes list of class targets
#' @param mask_raster mask raster (path or raster object)
#' @param mask_raster mask raster (path or terra::rast object)
#' @param NON_FOCAL_PLAND PLAND (proportion of landscape) target on the non-focal land-use class
#'
#' @import terra
#'
#' @details Either nb_rows and nb_cols, or mask_raster must be specified. The dimensions
#' of the landscape are deduced from the mask raster if it is used.
#'
Expand Down Expand Up @@ -61,16 +63,14 @@ flsgen_create_landscape_targets <- function(nb_rows, nb_cols, classes, mask_rast
base_targets$nbRows <- nb_rows
base_targets$nbCols <- nb_cols
} else {
if (inherits(mask_raster, "Raster")) {
if (nchar(filename(mask_raster)) > 0) {
mask_raster <- filename(mask_raster)
} else {
file_name <- tempfile(fileext = ".tif")
writeRaster(mask_raster, file_name)
mask_raster <- file_name
if (inherits(mask_raster, "SpatRaster")) {
s <- terra::sources(mask_raster)[[1]]
on_disk <- all(nchar(s > 0) && all(file.exists(s)))
if (on_disk) {
base_targets$maskRasterPath <- s
}
base_targets$maskRaster <- mask_raster
}
base_targets$maskRasterPath <- mask_raster
}
if (!is.null(NON_FOCAL_PLAND)) {
base_targets$NON_FOCAL_PLAND <- NON_FOCAL_PLAND
Expand Down
32 changes: 16 additions & 16 deletions R/flsgen_extract_structure_from_raster.R
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,13 @@
#'
#' @description Extracts a landscape structure from an existing raster
#'
#' @param raster_file raster object or path of the raster
#' @param raster_file terra::rast object or path of the raster
#' @param focal_classes vector of integers representing the raster values of
#' the focal classes to extract the structure from
#' @param connectivity Connectivity definition in the regular square grid (4 or 8)."
#'
#' @import rJava
#' @import raster
#' @import terra
#'
#' @return A JSON landscape structure that can be used with flsgen generate
#'
Expand All @@ -41,29 +41,29 @@
flsgen_extract_structure_from_raster <- function(raster_file, focal_classes, connectivity=4) {
checkmate::assert_vector(focal_classes, min.len = 1)
checkmate::assert_choice(connectivity, c(4, 8))
if (inherits(raster_file, "Raster")) {
if (nchar(filename(raster_file)) > 0) {
raster_file <- filename(raster_file)
} else {
file_name <- tempfile(fileext = ".tif")
writeRaster(raster_file, file_name)
raster_file <- file_name
}
} else {
checkmate::assert_string(raster_file)
checkmate::assert(file.exists(raster_file))
if (!inherits(raster_file, "SpatRaster")) {
raster_file <- terra::rast(raster_file)
}
nb_rows <- nrow(raster_file)
nb_cols <- ncol(raster_file)
no_data_cells <- which(is.na(raster_file[,])) - 1
no_data_value <-terra::NAflag(raster_file)
s <- terra::sources(raster_file)[[1]]
if (connectivity == 4) {
neigh <- J("org.flsgen.grid.neighborhood.Neighborhoods")$FOUR_CONNECTED
} else {
if (connectivity == 8) {
neigh <- J("org.flsgen.grid.neighborhood.Neighborhoods")$HEIGHT_CONNECTED
}
}
struct <- J("org.flsgen.solver.LandscapeStructure")$fromRaster(
raster_file,
struct <- J("org.flsgen.solver.LandscapeStructure")$fromRasterData(
.jarray(as.integer(values(raster_file))),
as.integer(nb_rows),
as.integer(nb_cols),
as.integer(no_data_value),
.jarray(as.integer(focal_classes)),
neigh
neigh,
s
)
.jgc()
return(struct$toJSON())
Expand Down
31 changes: 27 additions & 4 deletions R/flsgen_generate.R
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
#' @description Generate landscape raster from landscape structure
#'
#' @import rJava
#' @import terra
#' @import jsonlite
#'
#' @details The input landscape structure must be either specified as a JSON-formatted string
#' (structure_str parameter) or as a JSON file (structure_file parameter)
Expand Down Expand Up @@ -87,6 +89,7 @@ flsgen_generate <- function(structure_str, structure_file, terrain_file=NULL,
min_max_distance=NULL, connectivity=4, x=0, y=0,
resolution_x=0.0001, resolution_y=NULL, epsg="EPSG:4326",
max_try=2, max_try_patch=10, verbose=TRUE) {
mask_raster <- NULL
# Check arguments
if (missing(structure_str)) {
if (missing(structure_file)) {
Expand All @@ -98,6 +101,10 @@ flsgen_generate <- function(structure_str, structure_file, terrain_file=NULL,
stop("Either structure_str or structure_file must be used in generate_landscape_raster function to specify user targets, not both")
}
if (inherits(structure_str, "FlsgenLandscapeStructure")) {
if (!is.null(structure_str$maskRaster)) {
mask_raster <- structure_str$maskRaster
structure_str$maskRaster <- NULL
}
for (i in 1:length(structure_str$classes)) {
structure_str$classes[[i]] <- unclass(structure_str$classes[[i]])
}
Expand Down Expand Up @@ -126,11 +133,27 @@ flsgen_generate <- function(structure_str, structure_file, terrain_file=NULL,
struct_json <- jsonlite::fromJSON(structure_str)
nb_rows <- struct_json$nbRows
nb_cols <- struct_json$nbCols
no_data_cells <- c()
no_data_value <- -3000
if (!is.null(mask_raster)) {
nb_rows <- nrow(mask_raster)
nb_cols <- ncol(mask_raster)
no_data_cells <- which(is.na(mask_raster[,])) - 1
no_data_value <-terra::NAflag(mask_raster)
} else {
if (!is.null(struct_json$maskRasterPath)) {
mask_raster <- terra::rast(struct_json$maskRasterPath)
nb_rows <- nrow(mask_raster)
nb_cols <- ncol(mask_raster)
no_data_cells <- which(is.na(mask_raster[,])) - 1
no_data_value <-terra::NAflag(mask_raster)
}
}

# Generate landscape raster using flsgen jar
reader <- .jnew("java.io.StringReader", structure_str)
struct <- J("org.flsgen.solver.LandscapeStructure")$fromJSON(reader)
reader$close()
struct <- J("org.flsgen.solver.LandscapeStructure")$fromJSON(
structure_str, as.integer(nb_rows), as.integer(nb_cols), .jarray(as.integer(no_data_cells))
)
grid <- .jnew("org.flsgen.grid.regular.square.RegularSquareGrid", struct$getNbRows(), struct$getNbCols())
terrain <- .jnew("org.flsgen.solver.Terrain", grid)
if (is.null(terrain_file)) {
Expand All @@ -149,7 +172,7 @@ flsgen_generate <- function(structure_str, structure_file, terrain_file=NULL,
generator <- .jnew("org.flsgen.solver.LandscapeGenerator", struct, as.integer(connectivity), as.integer(min_distance), as.integer(min_max_distance), terrain)
}
if (.jcall(generator, "Z", "generate", terrain_dependency, as.integer(max_try), as.integer(max_try_patch), verbose)) {
landscape_data <- .jcall(generator, "[I", "getRasterData", -2L)
landscape_data <- .jcall(generator, "[I", "getRasterData", as.integer(no_data_value))
landscape_raster <- terra::rast(xmin = x, xmax = x + (nb_cols * resolution_x),
ymax = y, ymin = y - (nb_rows * resolution_y),
crs = epsg, nrows = nb_rows, ncols = nb_cols,
Expand Down
31 changes: 29 additions & 2 deletions R/flsgen_structure.R
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
#' @description Find landscape structures satisfying user targets
#'
#' @import rJava
#' @import terra
#' @import jsonlite
#'
#' @details The input user targets must be either specified as a JSON-formatted
#' string (targets_str parameter) or as a JSON file (target_file parameter).
Expand Down Expand Up @@ -67,6 +69,7 @@
#' @export
#'
flsgen_structure <- function(targets_str, targets_file, nb_solutions=1, time_limit = 60, search_strategy="DEFAULT") {
mask_raster <- NULL
# Check arguments
if (missing(targets_str)) {
if (missing(targets_file)) {
Expand All @@ -78,6 +81,10 @@ flsgen_structure <- function(targets_str, targets_file, nb_solutions=1, time_lim
stop("Either targets_str or targets_file must be used in generate_landscape_structure function to specify user targets, not both")
}
if (inherits(targets_str, "FlsgenLandscapeTargets")) {
if (!is.null(targets_str$maskRaster)) {
mask_raster <- targets_str$maskRaster
targets_str$maskRaster <- NULL
}
for (i in 1:length(targets_str$classes)) {
targets_str$classes[[i]] <- unclass(targets_str$classes[[i]])
}
Expand All @@ -88,9 +95,29 @@ flsgen_structure <- function(targets_str, targets_file, nb_solutions=1, time_lim
checkmate::assert_int(time_limit, lower=0)
checkmate::assert_choice(search_strategy, c("DEFAULT", "RANDOM", "DOM_OVER_W_DEG", "DOM_OVER_W_DEG_REF", "ACTIVITY_BASED", "CONFLICT_HISTORY", "MIN_DOM_LB", "MIN_DOM_UB"))

json_targets <- jsonlite::fromJSON(targets_str)

nb_rows <- 0
nb_cols <- 0
no_data_cells <- c()

if (!is.null(mask_raster)) {
nb_rows <- nrow(mask_raster)
nb_cols <- ncol(mask_raster)
no_data_cells <- which(is.na(mask_raster[,])) - 1
} else {
if (!is.null(json_targets$maskRasterPath)) {
mask_raster <- terra::rast(json_targets$maskRasterPath)
nb_rows <- nrow(mask_raster)
nb_cols <- ncol(mask_raster)
no_data_cells <- which(is.na(mask_raster[,])) - 1
}
}

# Generate landscape structure using flsgen jar
reader <- .jnew("java.io.StringReader", targets_str)
solver <- J("org.flsgen.solver.LandscapeStructureSolver")$readFromJSON(reader)
solver <- J("org.flsgen.solver.LandscapeStructureSolver")$readFromJSON(
unclass(targets_str), as.integer(nb_rows), as.integer(nb_cols), .jarray(as.integer(no_data_cells))
)
.jcall(solver, "V", "build")
structs_json <- c()
switch(search_strategy,
Expand Down
2 changes: 1 addition & 1 deletion R/flsgen_terrain.R
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#' @description Fractal terrain generation with the diamond-square algorithm
#'
#' @import rJava
#' @import terra
#'
#' @param width Width (in pixels) of output raster
#' @param height Height (in pixels) of output raster
Expand Down Expand Up @@ -60,7 +61,6 @@ flsgen_terrain <- function(width, height, roughness=0.5, x=0, y=0, resolution=0.
crs = epsg, nrows = height, ncols = width,
nlyrs = 1)
values(terrain_raster) <- raster_data
#.jcall(terrain, "V", "exportRaster", x, y, resolution, epsg, output)
.jgc()
return(terrain_raster)
}
4 changes: 2 additions & 2 deletions R/zzz.R
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@
if(jvn < 1.8) stop("Java >= 8 is needed for this package but not available")
}
# Download flsgen jar if not already downloaded
jar_path <- file.path(libname, pkgname, "java", "flsgen-1.2.0.jar")
jar_path <- file.path(libname, pkgname, "java", "flsgen-core-1.2.0.jar")
if (!file.exists(jar_path)) {
old_options <- options(timeout = max(1000, getOption("timeout")))
on.exit(options(old_options))
utils::download.file("https://github.com/dimitri-justeau/flsgen/releases/download/v1.2.0/flsgen-1.2.0.jar", destfile = jar_path)
utils::download.file("https://github.com/dimitri-justeau/flsgen/releases/download/v1.2.0/flsgen-core-1.2.0.jar", destfile = jar_path)
}
.jpackage(pkgname, lib.loc = libname)
J("java.lang.System")$setProperty("EPSG-HSQL.directory", tempdir())
Expand Down
8 changes: 8 additions & 0 deletions cran-comments.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
## rflsgen 1.2.0

CRAN team complained about the size of the tarball, exceeding 5MB. To fix this:

* We reduced the spatial resolution of example data to fix this issue;

* We used pdf instead of html for documentation.

## rflsgen 0.1.2

Configure Geotools to store its temporary EPSG database in R session's temporary directory.
Expand Down
2 changes: 1 addition & 1 deletion docs/404.html

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

2 changes: 1 addition & 1 deletion docs/articles/FAQ.html

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

Loading

0 comments on commit 84ecf4a

Please sign in to comment.