Skip to content

Commit 7b2d351

Browse files
Merge pull request #48 from tcarleton/terra_conversion
Terra conversion
2 parents 980cdaa + c78730b commit 7b2d351

63 files changed

Lines changed: 1281 additions & 692 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.Rbuildignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,7 @@
55
^data-raw$
66
^doc$
77
^Meta$
8+
^_pkgdown\.yml$
9+
^docs$
10+
^pkgdown$
11+
^\.github$

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@
66
inst/doc
77
/doc/
88
/Meta/
9+
docs

DESCRIPTION

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,16 +23,19 @@ Imports:
2323
lubridate,
2424
magrittr,
2525
methods,
26-
raster,
2726
sf,
2827
stringr,
2928
terra
3029
Suggests:
30+
climateR (>= 0.3.5),
3131
knitr,
32+
raster,
3233
rmarkdown,
3334
testthat (>= 3.0.0),
3435
tidyr,
35-
tigris,
36-
usethis
36+
usethis,
37+
tigris
3738
Config/testthat/edition: 3
3839
VignetteBuilder: knitr
40+
Remotes:
41+
mikejohnson51/climateR

NAMESPACE

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,4 @@ importFrom(magrittr,"%>%")
1919
importFrom(methods,as)
2020
importFrom(methods,setMethod)
2121
importFrom(methods,signature)
22+
importFrom(stats,na.omit)

R/data_cropland_world_2015_era5.R

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
#' Global cropland weights
22
#'
33
#' A data.table with ERA5 resolution returned by running secondary_weights() with cropland data from
4-
#' 2015
4+
#' 2015 from https://glad.umd.edu/dataset/croplands
5+
#'
6+
#' Dataset Reference: P. Potapov, S. Turubanova, M.C. Hansen, A. Tyukavina, V.
7+
# Zalles, A. Khan, X.-P. Song, A. Pickens, Q. Shen, J. Cortez. (2021) Global
8+
# maps of cropland extent and change show accelerated cropland expansion in the
9+
# twenty-first century. Nature Food. https://doi.org/10.1038/s43016-021-00429-z
510
#'
611
"cropland_world_2015_era5"

R/data_nj_counties.R

Lines changed: 0 additions & 6 deletions
This file was deleted.

R/global_variables.R

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,31 @@
22
# This is because we are referencing column names in dplyr / data.table objects which use
33
# Non-Standard Evaluation.
44

5-
globalVariables(c("cell_area_km2", "coverage_fraction", "day", "era5_grid",
6-
"hour", "month", "poly_id", "sum_weight", "value", "w_area",
7-
"w_sum", "weight", "x", "y", "year", "."))
5+
globalVariables(
6+
c(
7+
"cell_area_km2",
8+
"coverage_fraction",
9+
"day",
10+
"era5_grid",
11+
"hour",
12+
"month",
13+
"minute",
14+
"poly_id",
15+
"sum_weight",
16+
"value",
17+
"w_area",
18+
"w_sum",
19+
"weight",
20+
"x",
21+
"y",
22+
"year",
23+
".",
24+
"is_right_xmin",
25+
"is_left_xmax",
26+
"x_low",
27+
"x_high",
28+
"y_low",
29+
"y_high",
30+
"..cols_to_keep"
31+
)
32+
)

R/overlay_weights.R

Lines changed: 49 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#' @param polygon_id_col the name of a column in the `polygons` object with a
99
#' unique identifier for each polygon
1010
#' @param grid a raster layer with the same spatial resolution as the data
11+
#' (must use geographic coordinates)
1112
#' @param secondary_weights an optional table of secondary weights, output from
1213
#' the `secondary_weights()` function
1314
#'
@@ -16,7 +17,7 @@
1617
#'
1718
#' @examples
1819
#' overlay_output_with_secondary_weights <- overlay_weights(
19-
#' polygons = nj_counties, # Polygons outlining the 21 counties of New Jersey
20+
#' polygons = tigris::counties("nj"), # Polygons outlining the 21 counties of New Jersey
2021
#' polygon_id_col = "COUNTYFP", # The name of the column with the unique
2122
#' # county identifiers
2223
#' grid = era5_grid, # The grid to use when extracting area weights (era5_grid is the
@@ -32,7 +33,7 @@
3233
#'
3334
#'
3435
#' overlay_output_without_secondary_weights <- overlay_weights(
35-
#' polygons = nj_counties, # Polygons outlining the 21 counties of New Jersey
36+
#' polygons = tigris::counties("nj"), # Polygons outlining the 21 counties of New Jersey
3637
#' polygon_id_col = "COUNTYFP" # The name of the column with the unique county
3738
#' # identifiers
3839
#' )
@@ -43,10 +44,14 @@
4344
#' @export
4445
overlay_weights <- function(polygons, polygon_id_col, grid = era5_grid, secondary_weights = NULL){
4546

46-
# Create raster (Putting terra::rast() here creates, for unknown reasons,
47-
# issues with devtools::check())
48-
clim_raster <- as(grid, "SpatRaster") # only reads the first band
47+
## check to make sure climate raster is a spatraster, change if not
48+
if (!inherits(grid, "SpatRaster")) {
49+
clim_raster <- terra::rast(grid)
50+
} else {
51+
52+
clim_raster <- grid
4953

54+
}
5055

5156
## Raster cell area
5257
## -----------------------------------------------
@@ -62,6 +67,12 @@ overlay_weights <- function(polygons, polygon_id_col, grid = era5_grid, secondar
6267
rast_xmax <- terra::ext(clim_area_raster)$xmax
6368
rast_res <- terra::xres(clim_area_raster)
6469

70+
## check if SpatRaster is in geographic coodrinates
71+
if(!terra::is.lonlat(clim_raster)) {
72+
stop(crayon::red('Grid does not have geographic coordinates.'))
73+
74+
}
75+
6576
## stop if polygons are not in standard coordinate system
6677
if(poly_xmax > 180) {
6778

@@ -70,7 +81,13 @@ overlay_weights <- function(polygons, polygon_id_col, grid = era5_grid, secondar
7081
}
7182

7283
## check if coordinate systems match, if no shift raster to -180 to 180
73-
if(rast_xmax > 180 + rast_res / 2) {
84+
if(rast_xmax > 180 + rast_res) {
85+
86+
# Make sure the cell widths aren't peculiar otherwise the rotate function will
87+
# mess things up
88+
if(360 %% terra::xres(clim_raster) != 0){
89+
stop(crayon::red('Grid is in climate coordinate system (longitude 0 to 360) and grid cell width does not divide 360 evenly, making accurate alignment impossible.'))
90+
}
7491

7592
message(crayon::yellow('Aligning longitudes to standard coordinates.'))
7693

@@ -91,8 +108,13 @@ overlay_weights <- function(polygons, polygon_id_col, grid = era5_grid, secondar
91108
## rotate
92109
clim_area_raster <- terra::rotate(clim_area_raster)
93110

94-
}
111+
}
95112

113+
# Extend the grid to cover all polygons consistent with the extended rotate
114+
# above. exact_extract already does this in the background to a certain
115+
# degree, so this just allows us to be explicit about how we handle NAs later
116+
# on.
117+
clim_area_raster <- terra::extend(clim_area_raster, terra::ext(polygons), snap = 'out')
96118

97119
## Match raster and polygon crs
98120
crs_raster <- terra::crs(clim_area_raster)
@@ -122,11 +144,11 @@ overlay_weights <- function(polygons, polygon_id_col, grid = era5_grid, secondar
122144
s_weight_max <- max(weights_dt$x)
123145

124146
## if secondary_weights is in 0-360, adjust x val
125-
if(s_weight_max > 180 + rast_res) {
147+
if(s_weight_max > 180 + rast_res / 2) {
126148

127149
message(crayon::yellow('Adjusting secondary weights longitude to standard coordinates.'))
128150

129-
weights_dt[, x := data.table::fifelse(x > 180 + rast_res, x - 360, x)]
151+
weights_dt[, x := data.table::fifelse(x > 180 + rast_res / 2, x - 360, x)]
130152

131153
}
132154

@@ -165,25 +187,19 @@ overlay_weights <- function(polygons, polygon_id_col, grid = era5_grid, secondar
165187
w_merged[, weight := weight * w_area]
166188

167189
# Create column that determines if entire polygon has a weight == 0
168-
zero_polys <- data.frame(w_merged) |>
169-
dplyr::group_by(poly_id) |>
170-
dplyr::summarise(sum_weight = sum(weight)) |>
171-
dplyr::ungroup() |>
172-
dplyr::filter(sum_weight == 0) |>
173-
dplyr::select(poly_id) |>
174-
dplyr::distinct()
190+
zero_polys <- w_merged[, .(sum_weight = sum(weight)),
191+
by = .(poly_id)]
192+
193+
zero_polys <- unique(zero_polys[sum_weight == 0, .(poly_id)])
175194

176-
if(nrow(zero_polys > 0)) {
195+
if(nrow(zero_polys) > 0) {
177196

178197
warning(crayon::red("Warning: weight = 0 for all pixels in some of your polygons; NAs will be returned for weights"))
179198

180199
}
181200

182201
# List any polygons with NA values in 1 or more grid cells
183-
na_polys <- data.frame(w_merged) |>
184-
dplyr::filter(is.na(weight)) |>
185-
dplyr::select(poly_id) |>
186-
dplyr::distinct()
202+
na_polys <- unique(w_merged[is.na(weight), .(poly_id)])
187203

188204
# # Warning if there are polygons with NA weight values
189205
# if(nrow(na_polys > 0)) {
@@ -193,12 +209,8 @@ overlay_weights <- function(polygons, polygon_id_col, grid = era5_grid, secondar
193209
# }
194210

195211
# Update the weight to NA for all grid cells in na_polys
196-
w_merged <- w_merged |>
197-
dplyr::mutate(weight = ifelse(poly_id %in% c(na_polys$poly_id, zero_polys$poly_id), NA, weight)) |>
198-
data.table::as.data.table()
199-
200-
## this doesn't work with dt... figure out or delete and use above
201-
# w_merged[, weight := data.table::fifelse(poly_id %in% c(na_polys$poly_id, zero_polys$poly_id), NA, weight)]
212+
w_merged <- w_merged[, weight := ifelse(poly_id %in% c(na_polys$poly_id,
213+
zero_polys$poly_id), NA, weight)]
202214

203215
}
204216

@@ -231,7 +243,7 @@ overlay_weights <- function(polygons, polygon_id_col, grid = era5_grid, secondar
231243
# Check that polygon weights sum to 1 or 0 if all weights are NA
232244
if (!is.null(secondary_weights)){
233245

234-
for(i in nrow(check_weights)){
246+
for(i in seq_len(nrow(check_weights))){
235247

236248
if(!dplyr::near(check_weights$w_area[i], 1, tol=0.001)) {
237249

@@ -246,7 +258,7 @@ overlay_weights <- function(polygons, polygon_id_col, grid = era5_grid, secondar
246258

247259
} else {
248260

249-
for(i in nrow(check_weights)){
261+
for(i in seq_len(nrow(check_weights))){
250262

251263
if(!dplyr::near(check_weights$w_sum[i], 1, tol=0.001)){
252264

@@ -262,10 +274,15 @@ overlay_weights <- function(polygons, polygon_id_col, grid = era5_grid, secondar
262274
# If it doesn't error out then all weight sums = 1
263275
message(crayon::green('All weights sum to 1.'))
264276

265-
## Convert back to 0-360
266-
## -----------------------------------------------
277+
## Return table in coordinate system that matches that of the climate data
278+
## ------------------------------------------------------------------------
279+
280+
if(rast_xmax > 180 + rast_res) {
281+
282+
w_norm[, x := data.table::fifelse(x < 0 + rast_res / 2, x + 360, x)]
283+
284+
}
267285

268-
w_norm[, x := data.table::fifelse(x < 0, x + 360, x)]
269286

270287
return(w_norm)
271288

R/secondary_weights.R

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
#' @param secondary_raster a raster of a secondary variable, for example
88
#' cropland coverage or population
99
#' @param grid a raster layer with the same spatial resolution as the climate
10-
#' data
10+
#' data (must use geographic coordinates)
1111
#' @param extent an optional extent to crop the secondary_raster to for faster
1212
#' processing. Format must be compatible with raster::crop(). The default is "full", which
1313
#' resamples the whole secondary raster without cropping.
@@ -32,7 +32,7 @@ secondary_weights <- function(secondary_raster, grid = era5_grid, extent = "full
3232
## Return error if terra::extent can't inherit from the value supplied
3333
## won't be able to check if secondary raster fully overlaps if
3434
## this input isn't compatible with terra::extent()
35-
if(!is.character(extent) | length(extent) > 1){
35+
if(!is.character(extent) || length(extent) > 1){
3636
tryCatch({
3737
terra::ext(extent)
3838
message(crayon::green("User-defined extent compatible with raster"))
@@ -72,12 +72,26 @@ secondary_weights <- function(secondary_raster, grid = era5_grid, extent = "full
7272

7373
}
7474

75+
## check to make sure climate raster is a spatraster, change if not
76+
if (!inherits(grid, "SpatRaster")) {
77+
clim_raster <- terra::rast(grid)
78+
} else {
79+
80+
clim_raster <- grid
81+
82+
}
83+
84+
## check if SpatRaster is in geographic coodrinates
85+
if(!terra::is.lonlat(clim_raster)) {
86+
stop(crayon::red('Grid does not have geographic coordinates.'))
87+
88+
}
89+
7590
# Create climate raster from input raster
76-
clim_raster <- terra::rast(grid) # only reads the first band
91+
clim_raster <- clim_raster[[1]] # only reads the first band
7792

7893
## climate raster information for creating buffer and doing checks/rotations
7994
c_rast_xmax <- terra::ext(clim_raster)$xmax
80-
# c_rast_xmin <- raster::extent(clim_raster)@xmin
8195

8296
## find xy resolution for rasters
8397
c_rast_xres <- terra::xres(clim_raster)
@@ -137,7 +151,13 @@ secondary_weights <- function(secondary_raster, grid = era5_grid, extent = "full
137151

138152

139153
## if secondary raster in -180 to 180 and clim raster 0-360, rotate clim raster
140-
if(s_rast_xmax <= (180 + s_rast_xres / 2) & c_rast_xmax >= (180 + c_rast_xres / 2)) {
154+
if(s_rast_xmax < (180 + s_rast_xres) & c_rast_xmax > (180 + c_rast_xres)) {
155+
156+
# Make sure the cell widths aren't peculiar otherwise the rotate function will
157+
# mess things up
158+
if(360 %% c_rast_xres != 0){
159+
stop(crayon::red('Grid is in climate coordinate system (longitude 0 to 360) and grid cell width does not divide 360 evenly, making accurate alignment impossible.'))
160+
}
141161

142162
message(crayon::yellow('Longitude coordinates do not match. Aligning longitudes to standard coordinates.'))
143163

@@ -160,7 +180,13 @@ secondary_weights <- function(secondary_raster, grid = era5_grid, extent = "full
160180
}
161181

162182
## if secondary raster in 0-360 and clim raster -180 to 180, rotate secondary raster
163-
if(s_rast_xmax >= (180 + s_rast_xres / 2) & c_rast_xmax <= (180 + c_rast_xres / 2)) {
183+
if(s_rast_xmax > (180 + s_rast_xres) & c_rast_xmax < (180 + c_rast_xres)) {
184+
185+
# Make sure the cell widths aren't peculiar otherwise the rotate function will
186+
# mess things up
187+
if(360 %% s_rast_xres != 0){
188+
stop(crayon::red('Grid is in climate coordinate system (longitude 0 to 360) and grid cell width does not divide 360 evenly, making accurate alignment impossible.'))
189+
}
164190

165191
message(crayon::yellow('Longitude coordinates do not match. Aligning longitudes to standard coordinates.'))
166192

@@ -186,14 +212,14 @@ secondary_weights <- function(secondary_raster, grid = era5_grid, extent = "full
186212
## crop the ERA/climate raster to the appropriate extent
187213
## use the extent of the previously user-cropped secondary raster
188214
## -----------------------------------------------
215+
189216
# Find the difference between the climate raster resolution and secondary raster resolution
190217
clim_raster <- terra::crop(clim_raster, terra::ext(secondary_raster), snap="out")
191218

192219
## set crs of secondary raster to match climate data
193220
## -----------------------------------------------
194221
terra::crs(secondary_raster) <- terra::crs(clim_raster)
195222

196-
197223
## check if the cropped secondary raster contains NA values
198224
if(isTRUE(any(is.na(terra::values(secondary_raster, na.rm=FALSE))))) {
199225

@@ -206,7 +232,7 @@ secondary_weights <- function(secondary_raster, grid = era5_grid, extent = "full
206232
## -----------------------------------------------
207233
message(crayon::green("Resampling secondary_raster"))
208234

209-
resampled_raster = terra::resample(secondary_raster, clim_raster, method="bilinear")
235+
resampled_raster <- terra::resample(secondary_raster, clim_raster, method="bilinear")
210236

211237
## Make a data.table of the values of the resampled raster with lat/lon
212238
## -----------------------------------------------

R/stagg-package.R

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#' @importFrom data.table .NGRP
1212
#' @importFrom data.table .SD
1313
#' @importFrom data.table data.table
14+
#' @importFrom stats na.omit
1415
#' @importFrom magrittr %>%
1516
#' @importFrom methods as
1617
#' @importFrom methods setMethod

0 commit comments

Comments
 (0)