Skip to content

Commit 129b021

Browse files
committed
Prepping v1.4.0 for CRAN submission. This version migrates the package to use the new QS2 format for saving and loading simulation results. Not backward compatible with QS.
1 parent d53582a commit 129b021

File tree

10 files changed

+166
-23
lines changed

10 files changed

+166
-23
lines changed

DESCRIPTION

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
Package: poems
22
Type: Package
33
Title: Pattern-Oriented Ensemble Modeling System
4-
Version: 1.3.3
4+
Version: 1.4.0
55
Authors@R: c(
66
person("Sean", "Haythorne", email = "[email protected]", role = "aut"),
77
person("Damien", "Fordham", email = "[email protected]", role = "aut"),
@@ -30,7 +30,7 @@ Imports:
3030
trend (>= 1.1.4),
3131
truncnorm (>= 1.0),
3232
gdistance,
33-
qs
33+
qs2
3434
Collate:
3535
'GenericClass.R'
3636
'Region.R'

NAMESPACE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,6 @@ importFrom(gdistance,geoCorrection)
4141
importFrom(gdistance,transition)
4242
importFrom(lhs,randomLHS)
4343
importFrom(metRology,qtri)
44-
importFrom(qs,qread)
44+
importFrom(qs2,qs_read)
4545
importFrom(trend,sens.slope)
4646
importFrom(truncnorm,qtruncnorm)

NEWS.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
# version 1.4.0
2+
3+
The R package `qs` will be deprecated soon, so `poems` has updated its `qs` dependency to `qs2`. Please note that `qs2` is not backward-compatible, so `poems` will no longer be able to handle `qs` data files.
4+
15
# version 1.3.3
26
## Bug fixes
37
- Fixed a bug that caused the `population_density` function to fail when population density was logistic and there were no growth rates (that is, a stagnant population).

R/Generator.R

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@
8181
#'
8282
#' @importFrom R6 R6Class
8383
#' @importFrom metRology qtri
84-
#' @importFrom qs qread
84+
#' @importFrom qs2 qs_read
8585
#' @include SpatialModel.R
8686
#' @include GenerativeTemplate.R
8787
#' @export Generator
@@ -388,7 +388,7 @@ Generator <- R6Class(
388388
#' @param path_params Array of the names of the simulation sample
389389
#' parameters to be substituted (in order) into the path template.
390390
#' @param file_type File type raster \emph{"GRD"} (default), \emph{"TIF"},
391-
#' \emph{"RData/RDS"}, \emph{"QS"}, or \emph{"CSV"} to be read.
391+
#' \emph{"RData/RDS"}, \emph{"QS2"}, or \emph{"CSV"} to be read.
392392
add_file_template = function(
393393
param,
394394
path_template,
@@ -408,12 +408,12 @@ Generator <- R6Class(
408408
toupper(file_type) == "RDS" ||
409409
toupper(file_type) == "CSV" ||
410410
toupper(file_type) == "TIF" ||
411-
toupper(file_type) == "QS"
411+
toupper(file_type) == "QS2"
412412
) {
413413
self$file_templates[[param]]$file_type <- toupper(file_type)
414414
} else {
415415
stop(
416-
"The file type should be GRD (raster), TIF (raster), RDS, QS, or CSV",
416+
"The file type should be GRD (raster), TIF (raster), RDS, QS2, or CSV",
417417
call. = FALSE
418418
)
419419
}
@@ -617,8 +617,8 @@ Generator <- R6Class(
617617
} else if (self$file_templates[[param]]$file_type == "RDS") {
618618
# RDS (RData)
619619
value_list[[param]] <- readRDS(file = file_path)
620-
} else if (self$file_templates[[param]]$file_type == "QS") {
621-
value_list[[param]] <- qread(file = file_path)
620+
} else if (self$file_templates[[param]]$file_type == "QS2") {
621+
value_list[[param]] <- qs_read(file = file_path)
622622
} else {
623623
# raster
624624
value_list[[param]] <- raster::brick(file_path)
@@ -687,10 +687,12 @@ Generator <- R6Class(
687687
sample_names <- names(sample)
688688
if (is.list(sample) && all(sample_names %in% c("mid", "window"))) {
689689
# window-based
690-
if (is.character(sample$mid))
690+
if (is.character(sample$mid)) {
691691
sample$mid <- self$get_attribute(sample$mid)
692-
if (is.character(sample$window))
692+
}
693+
if (is.character(sample$window)) {
693694
sample$window <- self$get_attribute(sample$window)
695+
}
694696
if (is.numeric(sample$mid) && is.numeric(sample$window)) {
695697
sample <- c(
696698
max(sample$mid - sample$window / 2, 0),

codemeta.json

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,13 @@
88
"codeRepository": "https://github.com/GlobalEcologyLab/poems",
99
"issueTracker": "https://github.com/GlobalEcologyLab/poems/issues",
1010
"license": "https://spdx.org/licenses/GPL-3.0",
11-
"version": "1.3.3",
11+
"version": "1.4.0",
1212
"programmingLanguage": {
1313
"@type": "ComputerLanguage",
1414
"name": "R",
1515
"url": "https://r-project.org"
1616
},
17-
"runtimePlatform": "R version 4.5.0 (2025-04-11)",
17+
"runtimePlatform": "R version 4.5.1 (2025-06-13)",
1818
"provider": {
1919
"@id": "https://cran.r-project.org",
2020
"@type": "Organization",
@@ -295,19 +295,19 @@
295295
},
296296
"13": {
297297
"@type": "SoftwareApplication",
298-
"identifier": "qs",
299-
"name": "qs",
298+
"identifier": "qs2",
299+
"name": "qs2",
300300
"provider": {
301301
"@id": "https://cran.r-project.org",
302302
"@type": "Organization",
303303
"name": "Comprehensive R Archive Network (CRAN)",
304304
"url": "https://cran.r-project.org"
305305
},
306-
"sameAs": "https://CRAN.R-project.org/package=qs"
306+
"sameAs": "https://CRAN.R-project.org/package=qs2"
307307
},
308308
"SystemRequirements": null
309309
},
310-
"fileSize": "6407.82KB",
310+
"fileSize": "5287.888KB",
311311
"citation": [
312312
{
313313
"@type": "ScholarlyArticle",
@@ -357,7 +357,7 @@
357357
}
358358
}
359359
],
360-
"releaseNotes": "https://github.com/GlobalEcologyLab/poems/blob/master/NEWS.md",
360+
"releaseNotes": "https://github.com/GlobalEcologyLab/poems/blob/main/NEWS.md",
361361
"readme": "https://github.com/GlobalEcologyLab/poems/blob/main/README.md",
362362
"contIntegration": ["https://app.codecov.io/gh/GlobalEcologyLab/poems?branch=main", "https://github.com/GlobalEcologyLab/poems/actions/workflows/R-CMD-check.yaml"],
363363
"developmentStatus": "https://www.repostatus.org/#active",

cran-comments.md

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,25 @@
11
## Test environments
2-
* local macOS 15 install, Intel chip, R 4.5.0
2+
* local macOS 15.6.1 install, Apple Silicon chip, R 4.5.1
33
* Windows, R-devel (via win-builder)
44
* macOS, Apple Silicon chip, R 4.5.0 (via mac-builder)
55
* Ubuntu 24.04, R-devel (via Github Actions)
6-
* Ubuntu 24.04, R 4.5.0 (via Github Actions)
6+
* Ubuntu 24.04, R 4.5.2 (via Github Actions)
77
* Ubuntu 24.04, R 4.4.3 (via Github Actions)
88

99
## R CMD check results
1010

1111
0 ERRORs, 0 WARNINGs, 0 NOTEs.
1212

13-
## Past R CMD check results
13+
## Changes in this version
1414

15-
`poems` did not have a dependency on R 4.1.0 as it should have. I have now updated the DESCRIPTION file.
15+
This version (1.4.0) updates the dependency from the deprecated `qs` package to `qs2`.
16+
17+
**Important note**: `qs2` is not backward-compatible with `qs`. Users with existing `qs` data files will need to convert them to `qs2` format or continue using an older version of `poems`.
18+
19+
New features:
20+
* Added support for reading `.qs2` files in the Generator class via the file_type = "QS2" parameter
21+
* Added comprehensive test coverage for QS2 file handling (21 new test assertions)
22+
* Updated all documentation to reflect the qs2 migration
1623

1724
## Other notes
1825

man/Generator.Rd

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/testthat/test_generator.R

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,136 @@ test_that("file template reading raster", {
198198
)
199199
})
200200

201+
test_that("file template reading QS2 data frame", {
202+
TEST_DIRECTORY <- test_path("test_inputs")
203+
coordinates <- data.frame(x = c(1:4, 4:2), y = c(1, 1:4, 4:3))
204+
occupancy_mask <- array(c(1, 1, 0, 0, 1, 1, 1))
205+
generator <- Generator$new(
206+
description = "Test generator", decimals = 4,
207+
region = Region$new(coordinates = coordinates, use_raster = FALSE),
208+
occupancy_mask = occupancy_mask
209+
)
210+
generator$set_attributes(attr1 = 1, attr2 = 2)
211+
# Test QS2 data frame reading
212+
generator$add_file_template("attr3",
213+
path_template = file.path(TEST_DIRECTORY, "Test_%s_%s_df.qs2"),
214+
path_params = c("attr1", "attr2"), file_type = "QS2"
215+
)
216+
test_data <- generator$read_file("attr3")
217+
expect_is(test_data, "data.frame")
218+
expect_equal(generator$attached$attr3, test_data)
219+
expect_equal(generator$file_templates$attr3$file_type, "QS2")
220+
generator$attached$attr3 <- test_data * 2
221+
expect_equal(generator$get_attribute("attr3"), test_data * 2)
222+
generator$attached$attr3 <- NULL # clear
223+
expect_equal(generator$get_attribute("attr3"), test_data)
224+
expect_equal(generator$attached$attr3, test_data)
225+
generator$attached$attr3 <- NULL # clear
226+
generator$outputs <- "attr3"
227+
expect_equal(generator$get_attribute("attr3"), round(test_data * occupancy_mask, 4))
228+
generator$attached$attr3 <- NULL # clear
229+
generator$occupancy_mask <- array(1, c(7, 10))
230+
generator$occupancy_mask[3:4, 5:8] <- 0
231+
expect_equal(generator$get_attribute("attr3"), round(test_data * generator$occupancy_mask, 4))
232+
generator$attached$attr3 <- NULL # clear
233+
# Via nested aliases
234+
generator$set_attributes(attr0 = list(first = 1, second = 2), attr1 = NULL, attr2 = NULL)
235+
generator$attribute_aliases <- list(a1 = "attr0$first", a2 = "attr0$second", a3 = "attr3", a3_1 = "attr3[1]")
236+
generator$add_file_template("attr3",
237+
path_template = file.path(TEST_DIRECTORY, "Test_%s_%s_df.qs2"),
238+
path_params = c("a1", "a2"), file_type = "QS2"
239+
)
240+
expect_equal(generator$get_attribute("a3"), round(test_data * generator$occupancy_mask, 4))
241+
generator$attached$attr3 <- NULL # clear
242+
expect_equal(generator$get_attribute("a3_1"), test_data[1])
243+
# Generate rasters from QS2 data frame
244+
region <- Region$new(coordinates = coordinates, use_raster = TRUE)
245+
generator <- Generator$new(
246+
description = "Test generator", decimals = 4, outputs = "attr3",
247+
region = region, occupancy_mask = region$raster_from_values(occupancy_mask)
248+
)
249+
generator$add_generative_requirements(attr3 = "file")
250+
generator$add_file_template("attr3",
251+
path_template = file.path(TEST_DIRECTORY, "Test_%s_%s_df.qs2"),
252+
path_params = c("attr1", "attr2"), file_type = "QS2"
253+
)
254+
generator$set_attributes(attr1 = 1, attr2 = 2)
255+
expect_equal(
256+
unname(generator$get_attribute("attr3")[region$region_indices]),
257+
unname(round(as.matrix(test_data * occupancy_mask), 4))
258+
)
259+
})
260+
261+
test_that("file template reading QS2 raster", {
262+
TEST_DIRECTORY <- test_path("test_inputs")
263+
coordinates <- data.frame(x = c(1:4, 4:2), y = c(1, 1:4, 4:3))
264+
region <- Region$new(coordinates = coordinates, use_raster = TRUE)
265+
occupancy_mask <- array(1, c(7, 10))
266+
occupancy_mask[3:4, 5:8] <- 0
267+
raster_occupancy_mask <- raster::stack(replicate(10, raster::raster(
268+
vals = region$region_raster[],
269+
nrows = 4, ncol = 4,
270+
xmn = 0, xmx = 4000, ymn = 0, ymx = 4000,
271+
crs = "+proj=eqc +lat_ts=0 +lat_0=0 +lon_0=0 +x_0=0 +y_0=0 +ellps=WGS84 +datum=WGS84 +units=m +no_defs"
272+
)))
273+
raster_occupancy_mask[][region$region_indices, ] <- occupancy_mask[raster_occupancy_mask[][region$region_indices, 1], ]
274+
raster::crs(raster_occupancy_mask) <- raster::crs(region$region_raster)
275+
raster::extent(raster_occupancy_mask) <- raster::extent(region$region_raster)
276+
generator <- Generator$new(
277+
description = "Test generator", decimals = 4,
278+
region = Region$new(coordinates = coordinates, use_raster = TRUE),
279+
occupancy_mask = raster_occupancy_mask
280+
)
281+
generator$set_attributes(attr1 = 1, attr2 = 2)
282+
# Via QS2
283+
generator$add_file_template("attr3",
284+
path_template = file.path(TEST_DIRECTORY, "Test_%s_%s.qs2"),
285+
path_params = c("attr1", "attr2"), file_type = "QS2"
286+
)
287+
test_raster <- generator$read_file("attr3")
288+
expect_is(test_raster, "RasterBrick")
289+
expect_equal(generator$file_templates$attr3$file_type, "QS2")
290+
expect_equal(generator$attached$attr3[], test_raster[])
291+
generator$attached$attr3 <- test_raster * 2
292+
expect_equal(generator$get_attribute("attr3")[], test_raster[] * 2)
293+
generator$attached$attr3 <- NULL # clear
294+
expect_equal(generator$get_attribute("attr3")[], test_raster[])
295+
generator$attached$attr3 <- NULL # clear
296+
# Via nested aliases
297+
generator$set_attributes(attr0 = list(first = 1, second = 2))
298+
generator$attribute_aliases <- list(
299+
a1 = "attr0$first", a2 = "attr0$second", a3 = "attr3", a3_8 = "attr3[8]",
300+
a3_X5 = "attr3[][, 5]"
301+
)
302+
generator$add_file_template("attr3",
303+
path_template = file.path(TEST_DIRECTORY, "Test_%s_%s.qs2"),
304+
path_params = c("a1", "a2"), file_type = "QS2"
305+
)
306+
generator$outputs <- "attr3"
307+
expect_equal(generator$get_attribute("a3")[], (round(test_raster, 4) * raster_occupancy_mask)[])
308+
generator$attached$attr3 <- NULL # clear
309+
expect_equal(generator$get_attribute("a3_8"), test_raster[8])
310+
generator$attached$attr3 <- NULL # clear
311+
expect_equal(generator$get_attribute("a3_X5"), test_raster[][, 5])
312+
# Generate arrays from QS2 rasters
313+
generator <- Generator$new(
314+
description = "Test generator", decimals = 4, outputs = "attr3",
315+
region = region, generate_rasters = FALSE,
316+
occupancy_mask = raster_occupancy_mask
317+
)
318+
expect_equal(unname(generator$occupancy_mask), occupancy_mask)
319+
generator$add_generative_requirements(attr3 = "file")
320+
generator$add_file_template("attr3",
321+
path_template = file.path(TEST_DIRECTORY, "Test_%s_%s.qs2"),
322+
path_params = c("attr1", "attr2"), file_type = "QS2"
323+
)
324+
generator$set_attributes(attr1 = 1, attr2 = 2)
325+
expect_equal(
326+
unname(generator$get_attribute("attr3")),
327+
unname(round(as.matrix(test_raster[region$region_indices] * occupancy_mask), 4))
328+
)
329+
})
330+
201331
test_that("function execution template", {
202332
TEST_DIRECTORY <- test_path("test_inputs")
203333
generator <- Generator$new(description = "Test generator", decimals = 4, outputs = "attr4")
1.31 KB
Binary file not shown.
424 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)