model_vars <- attr(rf_final$terms, "term.labels")
cat("Model expects these predictors:\n")
## Model expects these predictors:
print(model_vars)
## [1] "Cogongrass_Cover" "elevation" "pr" "srad"
## [5] "TreeCanopy_NLCD" "avg_pop_den_1km" "MEM2" "MEM5"
## [9] "MEM23" "MEM118" "MEM203"
# Then separate env vs MEM vars as before
env_vars <- model_vars[!grepl("^MEM", model_vars)]
mem_vars <- model_vars[ grepl("^MEM", model_vars)]
cat(sprintf("\nEnvironmental predictors : %s\n", paste(env_vars, collapse = ", ")))
##
## Environmental predictors : Cogongrass_Cover, elevation, pr, srad, TreeCanopy_NLCD, avg_pop_den_1km
cat(sprintf("MEM predictors (→ set 0) : %s\n\n", paste(mem_vars, collapse = ", ")))
## MEM predictors (→ set 0) : MEM2, MEM5, MEM23, MEM118, MEM203
# After building env_vars, exclude Cogongrass_Cover since it's not a raster predictor but a fixed value in scenarios
env_vars <- env_vars[env_vars != "Cogongrass_Cover"]
raster_paths <- list(
pr = "C:/Users/alanivory34428/Desktop/03_Biodiversity/02_Data/5_Year_Average_Precipitation.tif",
sph = "C:/Users/alanivory34428/Desktop/03_Biodiversity/02_Data/5_Year_Average_Specific_Humidity.tif",
srad = "C:/Users/alanivory34428/Desktop/03_Biodiversity/02_Data/5_Year_Average_Radiation.tif",
TreeCanopy_NLCD = "C:/Users/alanivory34428/Desktop/03_Biodiversity/02_Data/Tree_Cover_SE.tif",
ndvi = "C:/Users/alanivory34428/Desktop/03_Biodiversity/02_Data/CombinedMedianNDVI.tif",
tmmn = "C:/Users/alanivory34428/Desktop/03_Biodiversity/02_Data/5_Year_Average_Min_Temperature.tif",
elevation = "C:/Users/alanivory34428/Desktop/03_Biodiversity/02_Data/Elevation_CONUS.tif",
SoilType = "C:/Users/alanivory34428/Desktop/03_Biodiversity/02_Data/taxorder_CONUS_30m 1.tif",
avg_pop_den_1km = "C:/Users/alanivory34428/Desktop/03_Biodiversity/02_Data/US_PopDensity_2020.tif",
nlcd = "C:/Users/alanivory34428/Desktop/03_Biodiversity/02_Data/Annual_NLCD_LndCov_2024_CU_C1V1.tif"
)
missing_rasts <- setdiff(env_vars, names(raster_paths))
if (length(missing_rasts) > 0) {
stop(sprintf("Missing raster paths for: %s", paste(missing_rasts, collapse = ", ")))
}
## Load only the rasters needed by the model
cat("Loading rasters...\n")
## Loading rasters...
rast_list <- lapply(env_vars, function(v) {
cat(sprintf(" Loading: %s\n", v))
rast(raster_paths[[v]])
})
## Loading: elevation
## Loading: pr
## Loading: srad
## Loading: TreeCanopy_NLCD
## Loading: avg_pop_den_1km
names(rast_list) <- env_vars
# Loop through and plot each raster
for (name in names(raster_paths)) {
r <- rast(raster_paths[[name]])
plot(r, main = name)
}
# --- Check what CRS your rasters are in ---
cat("CRS of each raster:\n")
## CRS of each raster:
for (v in names(rast_list)) {
cat(sprintf(" %-20s %s\n", v, crs(rast_list[[v]], describe = TRUE)$code))
}
## elevation 4326
## pr 4326
## srad 4326
## TreeCanopy_NLCD 5070
## avg_pop_den_1km 4326
# --- Use NDVI raster extent as the boundary ---
cat("\nExtracting extent from NDVI raster...\n")
##
## Extracting extent from NDVI raster...
ndvi_rast <- rast(raster_paths[["ndvi"]])
ndvi_crs <- crs(ndvi_rast)
ndvi_extent <- ext(ndvi_rast)
cat(sprintf("NDVI CRS: %s\n", crs(ndvi_rast, describe = TRUE)$code))
## NDVI CRS: 4326
cat("NDVI Extent:\n")
## NDVI Extent:
print(ndvi_extent)
## SpatExtent : -131.325070711546, -59.42391537062, 19.9520316179366, 55.2019233667867 (xmin, xmax, ymin, ymax)
# --- Load, crop each raster to NDVI extent (reprojecting extent if needed) ---
local_dir <- "C:/local_cache"
dir.create(local_dir, showWarnings = FALSE, recursive = TRUE)
rast_list <- lapply(names(raster_paths), function(v) {
src_path <- raster_paths[[v]]
local_path <- file.path(local_dir, paste0(v, ".tif"))
if (!file.exists(local_path)) {
cat(sprintf(" Copying %-20s to local cache...\n", v))
r_src <- rast(src_path)
writeRaster(r_src, local_path, overwrite = TRUE)
}
r <- rast(local_path)
# Reproject the NDVI extent into this raster's CRS for cropping
if (!same.crs(r, ndvi_rast)) {
ndvi_ext_reproj <- ext(project(vect(ndvi_extent, crs = ndvi_crs), crs(r)))
} else {
ndvi_ext_reproj <- ndvi_extent
}
r_crop <- crop(r, ndvi_ext_reproj)
cat(sprintf(" %-20s done | dims: %d x %d | CRS: %s\n",
v, nrow(r_crop), ncol(r_crop),
crs(r_crop, describe = TRUE)$code))
r_crop
})
## pr done | dims: 3924 x 8004 | CRS: 4326
## sph done | dims: 3924 x 8004 | CRS: 4326
## srad done | dims: 3924 x 8004 | CRS: 4326
## |---------|---------|---------|---------|========================================= TreeCanopy_NLCD done | dims: 93792 x 154328 | CRS: 5070
## ndvi done | dims: 3924 x 8004 | CRS: 4326
## tmmn done | dims: 3924 x 8004 | CRS: 4326
## elevation done | dims: 1252 x 2841 | CRS: 4326
## |---------|---------|---------|---------|========================================= SoilType done | dims: 93376 x 153996 | CRS: 5070
## avg_pop_den_1km done | dims: 2766 x 6433 | CRS: 4326
## |---------|---------|---------|---------|========================================= nlcd done | dims: 98117 x 160000 | CRS: NA
names(rast_list) <- names(raster_paths)
I will change ref_rast to pr for now, to make predictions at the 4km scale. I can switch back to ndvi if we decide 30 m resolution is needed. Because weather data is at 4km, weather pixels become roughly 17,000 identical pixels.
# Use the NDVI raster as the reference grid for alignment
ref_rast <- rast_list[["ndvi"]]
cat("Aligning all rasters to the NDVI 30m reference grid...\n")
## Aligning all rasters to the NDVI 30m reference grid...
cat("\nReprojecting and resampling TreeCanopy and SoilType to match ref grid...\n")
##
## Reprojecting and resampling TreeCanopy and SoilType to match ref grid...
rast_aligned <- lapply(names(rast_list), function(v) {
r <- rast_list[[v]]
# Define categorical layers that MUST use Nearest Neighbor
categorical_layers <- c("SoilType", "nlcd")
method <- if (v %in% categorical_layers) "near" else "bilinear"
if (!same.crs(r, ref_rast)) {
cat(sprintf(" Reprojecting: %s to 30m...\n", v))
r <- project(r, ref_rast, method = method)
} else if (!compareGeom(r, ref_rast, stopOnError = FALSE)) {
cat(sprintf(" Resampling: %s to 30m...\n", v))
r <- resample(r, ref_rast, method = method)
} else {
cat(sprintf(" Already Aligned: %s\n", v))
}
r
})
## Already Aligned: pr
## Already Aligned: sph
## Already Aligned: srad
## Reprojecting: TreeCanopy_NLCD to 30m...
## |---------|---------|---------|---------|========================================= Already Aligned: ndvi
## Already Aligned: tmmn
## Resampling: elevation to 30m...
## Reprojecting: SoilType to 30m...
## |---------|---------|---------|---------|========================================= Resampling: avg_pop_den_1km to 30m...
## Reprojecting: nlcd to 30m...
## |---------|---------|---------|---------|=========================================
names(rast_aligned) <- names(rast_list)
env_stack <- rast(rast_aligned)
names(env_stack) <- names(rast_aligned)
cat(sprintf("\nenv_stack: %d layers, %d x %d cells\n",
nlyr(env_stack), nrow(env_stack), ncol(env_stack)))
##
## env_stack: 10 layers, 3924 x 8004 cells
# Check for NaN/NA in each layer
cat("\nNon-NA cell counts per layer:\n")
##
## Non-NA cell counts per layer:
for (v in names(env_stack)) {
n <- global(env_stack[[v]], fun = "notNA")[1,1]
cat(sprintf(" %-20s %d\n", v, n))
}
## pr 10408368
## sph 10408368
## srad 10408368
## TreeCanopy_NLCD 16872041
## ndvi 6217899
## tmmn 10408368
## elevation 18998331
## SoilType 9512108
## avg_pop_den_1km 10160873
## nlcd 10514534
Only focus on forest, shrubland, or heraceous areas NLCD Classes: 41, 42, 43 (Forests); 52 (Shrub); 71 (Herbaceous); 90 (Woody Wetland); 95 (Emergent Wetland)
mask_habitats <- function(env_stack, nlcd_raster) {
cat("Masking landscape to Forest, Shrubland, and Herbaceous cover...\n")
# Align NLCD to the stack if necessary
if(!compareGeom(nlcd_raster, env_stack, stopOnError = FALSE)) {
nlcd_raster <- project(nlcd_raster, env_stack, method = "near")
}
# Define target classes
target_classes <- c(41, 42, 43, 52, 71, 90, 95)
mask_layer <- nlcd_raster %in% target_classes
mask_layer[mask_layer == 0] <- NA
# Apply mask to the entire stack
env_stack_masked <- mask(env_stack, mask_layer)
return(env_stack_masked)
}
env_stack <- mask_habitats(env_stack, rast_list[["nlcd"]])
## Masking landscape to Forest, Shrubland, and Herbaceous cover...
## |---------|---------|---------|---------|=========================================
# Takes a cogongrass cover value, builds predictor dataframe,
# sets MEMs to 0, predicts, returns a raster
predict_diversity <- function(cogongrass_value, env_stack, rf_model, mem_vars) {
cat(sprintf(" Predicting at Cogongrass_Cover = %d%%...\n", cogongrass_value))
# Convert stack to data frame
pred_df <- as.data.frame(env_stack, xy = FALSE, na.rm = FALSE)
# Logic: Only predict for cells that aren't NA (masked out by NLCD or missing data)
valid_cells <- complete.cases(pred_df)
if(sum(valid_cells) == 0) stop("No valid cells found for prediction. Check NLCD mask.")
# Build scenario data
pred_df$Cogongrass_Cover <- cogongrass_value
for (mem in mem_vars) pred_df[[mem]] <- 0 # Methods: MEMs held constant at 0
# Reorder to match model requirements
all_vars <- attr(rf_model$terms, "term.labels")
pred_df_valid <- pred_df[valid_cells, all_vars, drop = FALSE]
# Generate predictions
predicted_vals <- predict(rf_model, newdata = pred_df_valid)
# Map values back to raster space
predicted_full <- rep(NA_real_, nrow(pred_df))
predicted_full[valid_cells] <- predicted_vals
result_rast <- env_stack[[1]]
values(result_rast) <- predicted_full
names(result_rast) <- paste0("Shannon_", cogongrass_value)
return(result_rast)
}
scenario_levels <- seq(0, 100, by = 1)
prediction_list <- list()
cat("\n========== Running All Cogongrass Scenarios ==========\n")
##
## ========== Running All Cogongrass Scenarios ==========
for(level in scenario_levels) {
prediction_list[[paste0("p", level)]] <- predict_diversity(level, env_stack, rf_final, mem_vars)
}
## Predicting at Cogongrass_Cover = 0%...
## Predicting at Cogongrass_Cover = 1%...
## Predicting at Cogongrass_Cover = 2%...
## Predicting at Cogongrass_Cover = 3%...
## Predicting at Cogongrass_Cover = 4%...
## Predicting at Cogongrass_Cover = 5%...
## Predicting at Cogongrass_Cover = 6%...
## Predicting at Cogongrass_Cover = 7%...
## Predicting at Cogongrass_Cover = 8%...
## Predicting at Cogongrass_Cover = 9%...
## Predicting at Cogongrass_Cover = 10%...
## Predicting at Cogongrass_Cover = 11%...
## Predicting at Cogongrass_Cover = 12%...
## Predicting at Cogongrass_Cover = 13%...
## Predicting at Cogongrass_Cover = 14%...
## Predicting at Cogongrass_Cover = 15%...
## Predicting at Cogongrass_Cover = 16%...
## Predicting at Cogongrass_Cover = 17%...
## Predicting at Cogongrass_Cover = 18%...
## Predicting at Cogongrass_Cover = 19%...
## Predicting at Cogongrass_Cover = 20%...
## Predicting at Cogongrass_Cover = 21%...
## Predicting at Cogongrass_Cover = 22%...
## Predicting at Cogongrass_Cover = 23%...
## Predicting at Cogongrass_Cover = 24%...
## Predicting at Cogongrass_Cover = 25%...
## Predicting at Cogongrass_Cover = 26%...
## Predicting at Cogongrass_Cover = 27%...
## Predicting at Cogongrass_Cover = 28%...
## Predicting at Cogongrass_Cover = 29%...
## Predicting at Cogongrass_Cover = 30%...
## Predicting at Cogongrass_Cover = 31%...
## Predicting at Cogongrass_Cover = 32%...
## Predicting at Cogongrass_Cover = 33%...
## Predicting at Cogongrass_Cover = 34%...
## Predicting at Cogongrass_Cover = 35%...
## Predicting at Cogongrass_Cover = 36%...
## Predicting at Cogongrass_Cover = 37%...
## Predicting at Cogongrass_Cover = 38%...
## Predicting at Cogongrass_Cover = 39%...
## Predicting at Cogongrass_Cover = 40%...
## Predicting at Cogongrass_Cover = 41%...
## Predicting at Cogongrass_Cover = 42%...
## Predicting at Cogongrass_Cover = 43%...
## Predicting at Cogongrass_Cover = 44%...
## Predicting at Cogongrass_Cover = 45%...
## Predicting at Cogongrass_Cover = 46%...
## Predicting at Cogongrass_Cover = 47%...
## Predicting at Cogongrass_Cover = 48%...
## Predicting at Cogongrass_Cover = 49%...
## Predicting at Cogongrass_Cover = 50%...
## Predicting at Cogongrass_Cover = 51%...
## Predicting at Cogongrass_Cover = 52%...
## Predicting at Cogongrass_Cover = 53%...
## Predicting at Cogongrass_Cover = 54%...
## Predicting at Cogongrass_Cover = 55%...
## Predicting at Cogongrass_Cover = 56%...
## Predicting at Cogongrass_Cover = 57%...
## Predicting at Cogongrass_Cover = 58%...
## Predicting at Cogongrass_Cover = 59%...
## Predicting at Cogongrass_Cover = 60%...
## Predicting at Cogongrass_Cover = 61%...
## Predicting at Cogongrass_Cover = 62%...
## Predicting at Cogongrass_Cover = 63%...
## Predicting at Cogongrass_Cover = 64%...
## Predicting at Cogongrass_Cover = 65%...
## Predicting at Cogongrass_Cover = 66%...
## Predicting at Cogongrass_Cover = 67%...
## Predicting at Cogongrass_Cover = 68%...
## Predicting at Cogongrass_Cover = 69%...
## Predicting at Cogongrass_Cover = 70%...
## Predicting at Cogongrass_Cover = 71%...
## Predicting at Cogongrass_Cover = 72%...
## Predicting at Cogongrass_Cover = 73%...
## Predicting at Cogongrass_Cover = 74%...
## Predicting at Cogongrass_Cover = 75%...
## Predicting at Cogongrass_Cover = 76%...
## Predicting at Cogongrass_Cover = 77%...
## Predicting at Cogongrass_Cover = 78%...
## Predicting at Cogongrass_Cover = 79%...
## Predicting at Cogongrass_Cover = 80%...
## Predicting at Cogongrass_Cover = 81%...
## Predicting at Cogongrass_Cover = 82%...
## Predicting at Cogongrass_Cover = 83%...
## Predicting at Cogongrass_Cover = 84%...
## Predicting at Cogongrass_Cover = 85%...
## Predicting at Cogongrass_Cover = 86%...
## Predicting at Cogongrass_Cover = 87%...
## Predicting at Cogongrass_Cover = 88%...
## Predicting at Cogongrass_Cover = 89%...
## Predicting at Cogongrass_Cover = 90%...
## Predicting at Cogongrass_Cover = 91%...
## Predicting at Cogongrass_Cover = 92%...
## Predicting at Cogongrass_Cover = 93%...
## Predicting at Cogongrass_Cover = 94%...
## Predicting at Cogongrass_Cover = 95%...
## Predicting at Cogongrass_Cover = 96%...
## Predicting at Cogongrass_Cover = 97%...
## Predicting at Cogongrass_Cover = 98%...
## Predicting at Cogongrass_Cover = 99%...
## Predicting at Cogongrass_Cover = 100%...
baseline <- prediction_list[["p0"]]
diff_list <- list()
cat("\nCalculating differences relative to 0% baseline...\n")
##
## Calculating differences relative to 0% baseline...
for(level in scenario_levels[-1]) { # Skip 0
diff_name <- paste0("Diff_", level, "_vs_0")
diff_list[[diff_name]] <- prediction_list[[paste0("p", level)]] - baseline
# Save result
writeRaster(diff_list[[diff_name]], paste0(diff_name, ".tif"), overwrite = TRUE)
}
cat("\n========== Scenario Summary Statistics (Relative to Baseline) ==========\n")
##
## ========== Scenario Summary Statistics (Relative to Baseline) ==========
stats_summary <- lapply(names(diff_list), function(nm) {
v <- values(diff_list[[nm]], na.rm = TRUE)
data.frame(
Scenario = nm,
Mean_Change = mean(v),
Max_Loss = min(v),
Max_Gain = max(v)
)
}) %>% bind_rows()
print(stats_summary)
## Scenario Mean_Change Max_Loss Max_Gain
## 1 Diff_1_vs_0 0.0000000000 0.00000000 0.000000000
## 2 Diff_2_vs_0 -0.0021540131 -0.01394934 0.002456030
## 3 Diff_3_vs_0 -0.0042723674 -0.01680358 0.001551865
## 4 Diff_4_vs_0 -0.0031893542 -0.01614842 0.003525222
## 5 Diff_5_vs_0 -0.0030533206 -0.01546306 0.003627290
## 6 Diff_6_vs_0 -0.0059809641 -0.01815757 0.003074285
## 7 Diff_7_vs_0 -0.0061097043 -0.01806224 0.002744493
## 8 Diff_8_vs_0 -0.0054600432 -0.01930045 0.003588835
## 9 Diff_9_vs_0 -0.0064533995 -0.02049284 0.003906713
## 10 Diff_10_vs_0 -0.0059528291 -0.02006224 0.004664293
## 11 Diff_11_vs_0 -0.0077585724 -0.02355668 0.004115986
## 12 Diff_12_vs_0 -0.0083093447 -0.02778413 0.005370331
## 13 Diff_13_vs_0 -0.0076678153 -0.02830676 0.005504541
## 14 Diff_14_vs_0 -0.0093519640 -0.03213204 0.005096504
## 15 Diff_15_vs_0 -0.0092119642 -0.03213204 0.005624360
## 16 Diff_16_vs_0 -0.0098959369 -0.03346820 0.005228866
## 17 Diff_17_vs_0 -0.0106298301 -0.03488670 0.004815168
## 18 Diff_18_vs_0 -0.0141680850 -0.04545048 0.006023090
## 19 Diff_19_vs_0 -0.0162319467 -0.05172267 0.005773319
## 20 Diff_20_vs_0 -0.0127924791 -0.05158535 0.015512752
## 21 Diff_21_vs_0 -0.0104323249 -0.05441639 0.018929875
## 22 Diff_22_vs_0 -0.0097424302 -0.05719260 0.020692801
## 23 Diff_23_vs_0 -0.0070556732 -0.05626770 0.025451575
## 24 Diff_24_vs_0 -0.0070456318 -0.05652031 0.025451575
## 25 Diff_25_vs_0 -0.0064397541 -0.05567266 0.025767808
## 26 Diff_26_vs_0 -0.0072063180 -0.05754866 0.026291933
## 27 Diff_27_vs_0 -0.0082399993 -0.06340502 0.026581197
## 28 Diff_28_vs_0 -0.0062874268 -0.06016956 0.027171052
## 29 Diff_29_vs_0 -0.0049062673 -0.05961793 0.027624910
## 30 Diff_30_vs_0 -0.0051644401 -0.06274534 0.027874919
## 31 Diff_31_vs_0 -0.0054857286 -0.06274534 0.027316503
## 32 Diff_32_vs_0 0.0011483770 -0.06088260 0.032315468
## 33 Diff_33_vs_0 -0.0004380289 -0.06695150 0.032896355
## 34 Diff_34_vs_0 0.0011782388 -0.06624781 0.035408420
## 35 Diff_35_vs_0 0.0072456335 -0.06665967 0.042139969
## 36 Diff_36_vs_0 0.0083568628 -0.06600022 0.043397560
## 37 Diff_37_vs_0 0.0124433856 -0.06900259 0.057737536
## 38 Diff_38_vs_0 0.0137675130 -0.06900259 0.058829244
## 39 Diff_39_vs_0 0.0134999143 -0.06908232 0.058482580
## 40 Diff_40_vs_0 0.0144412892 -0.06908232 0.059403210
## 41 Diff_41_vs_0 0.0184937047 -0.06623721 0.069487902
## 42 Diff_42_vs_0 0.0181115545 -0.06821772 0.071552340
## 43 Diff_43_vs_0 0.0187098493 -0.06821772 0.072084156
## 44 Diff_44_vs_0 0.0183470254 -0.06786004 0.070371292
## 45 Diff_45_vs_0 0.0190174970 -0.06876698 0.072117867
## 46 Diff_46_vs_0 0.0200931985 -0.06655336 0.071392016
## 47 Diff_47_vs_0 0.0206258782 -0.06655336 0.071294718
## 48 Diff_48_vs_0 0.0191064566 -0.06757407 0.070575105
## 49 Diff_49_vs_0 0.0191852137 -0.06743069 0.070976002
## 50 Diff_50_vs_0 0.0189447628 -0.06733882 0.068259534
## 51 Diff_51_vs_0 0.0186182779 -0.06733882 0.067487132
## 52 Diff_52_vs_0 0.0186360666 -0.06733882 0.067757581
## 53 Diff_53_vs_0 0.0186360666 -0.06733882 0.067757581
## 54 Diff_54_vs_0 0.0188030811 -0.06733882 0.069717367
## 55 Diff_55_vs_0 0.0146487042 -0.07372641 0.060543340
## 56 Diff_56_vs_0 0.0134787738 -0.07372641 0.058959844
## 57 Diff_57_vs_0 0.0080625522 -0.08382356 0.057403494
## 58 Diff_58_vs_0 0.0009203502 -0.08556228 0.049371938
## 59 Diff_59_vs_0 -0.0081000160 -0.10097455 0.046659279
## 60 Diff_60_vs_0 -0.0092560231 -0.10153448 0.045956722
## 61 Diff_61_vs_0 -0.0084564573 -0.10032120 0.046224504
## 62 Diff_62_vs_0 -0.0085034323 -0.10100991 0.046224504
## 63 Diff_63_vs_0 -0.0121587579 -0.10745498 0.046224504
## 64 Diff_64_vs_0 -0.0227089131 -0.12904099 0.046224504
## 65 Diff_65_vs_0 -0.0236224508 -0.12904099 0.045465173
## 66 Diff_66_vs_0 -0.0250066436 -0.12904099 0.042640401
## 67 Diff_67_vs_0 -0.0270765664 -0.13099822 0.041228519
## 68 Diff_68_vs_0 -0.0287132448 -0.13589608 0.039720320
## 69 Diff_69_vs_0 -0.0303893284 -0.13892651 0.040668821
## 70 Diff_70_vs_0 -0.0460456701 -0.18011276 0.045188279
## 71 Diff_71_vs_0 -0.0464116649 -0.18011276 0.045188279
## 72 Diff_72_vs_0 -0.0466464828 -0.18082731 0.045369487
## 73 Diff_73_vs_0 -0.0444535865 -0.18082731 0.050197245
## 74 Diff_74_vs_0 -0.0447191905 -0.18082731 0.049392118
## 75 Diff_75_vs_0 -0.0447191905 -0.18082731 0.049392118
## 76 Diff_76_vs_0 -0.0447191905 -0.18082731 0.049392118
## 77 Diff_77_vs_0 -0.0465898367 -0.18126790 0.045148113
## 78 Diff_78_vs_0 -0.0465898367 -0.18126790 0.045148113
## 79 Diff_79_vs_0 -0.0591954274 -0.19946714 0.036588753
## 80 Diff_80_vs_0 -0.0775670203 -0.21280704 0.018447754
## 81 Diff_81_vs_0 -0.0878868200 -0.22314340 0.009208218
## 82 Diff_82_vs_0 -0.0887865590 -0.22396412 0.009208218
## 83 Diff_83_vs_0 -0.2995711595 -0.45656224 -0.171893089
## 84 Diff_84_vs_0 -0.3577491988 -0.50707599 -0.218858391
## 85 Diff_85_vs_0 -0.3577936592 -0.50707599 -0.218858391
## 86 Diff_86_vs_0 -0.3577936592 -0.50707599 -0.218858391
## 87 Diff_87_vs_0 -0.6922712985 -0.91755522 -0.461275359
## 88 Diff_88_vs_0 -0.7077630935 -0.95183815 -0.463285660
## 89 Diff_89_vs_0 -0.7077630935 -0.95183815 -0.463285660
## 90 Diff_90_vs_0 -0.7027803186 -0.94475127 -0.464129708
## 91 Diff_91_vs_0 -0.6991085152 -0.94198597 -0.459767567
## 92 Diff_92_vs_0 -0.6965744487 -0.93950273 -0.456285078
## 93 Diff_93_vs_0 -0.6965744487 -0.93950273 -0.456285078
## 94 Diff_94_vs_0 -0.6965744487 -0.93950273 -0.456285078
## 95 Diff_95_vs_0 -0.6965744487 -0.93950273 -0.456285078
## 96 Diff_96_vs_0 -0.6965744487 -0.93950273 -0.456285078
## 97 Diff_97_vs_0 -0.6965744487 -0.93950273 -0.456285078
## 98 Diff_98_vs_0 -0.6965744487 -0.93950273 -0.456285078
## 99 Diff_99_vs_0 -0.6965744487 -0.93950273 -0.456285078
## 100 Diff_100_vs_0 -0.6965744487 -0.93950273 -0.456285078
plot_raster <- function(rast_layer, title, subtitle = NULL,
palette = "viridis", midpoint = NULL,
low = NULL, high = NULL, mid = NULL,
limits = NULL) {
# Convert SpatRaster to data frame for ggplot
df <- as.data.frame(rast_layer, xy = TRUE)
colnames(df)[3] <- "value"
df <- df[!is.na(df$value), ]
p <- ggplot() +
geom_raster(data = df, aes(x = x, y = y, fill = value)) +
# Ensure states_sf is already loaded in your environment
geom_sf(data = states_sf, fill = NA, colour = "grey30", linewidth = 0.3) +
coord_sf(xlim = c(-92, -75), ylim = c(24, 37)) +
labs(title = title, subtitle = subtitle, x = NULL, y = NULL) +
theme_bw(base_size = 12) +
theme(legend.position = "right",
axis.text = element_text(size = 8))
# Logic for diverging (diff) vs sequential (absolute) scales
if (!is.null(midpoint)) {
p <- p + scale_fill_gradient2(
low = low, mid = mid, high = high,
midpoint = midpoint,
limits = limits,
name = "ΔShannon",
na.value = "transparent"
)
} else {
p <- p + scale_fill_viridis_c(
option = palette,
limits = limits,
name = "Shannon\nDiversity",
na.value = "transparent"
)
}
return(p)
}
se_states <- c("Florida", "Georgia", "Alabama", "Mississippi", "South Carolina",
"North Carolina", "Tennessee", "Arkansas", "Louisiana", "Virginia")
states_sf <- maps::map("state", regions = tolower(se_states), fill = TRUE, plot = FALSE) %>%
st_as_sf()
p_final <- plot_raster(diff_list[["Diff_100_vs_0"]],
title = "Total Predicted Biodiversity Impact",
subtitle = "100% Cogongrass vs. 0% Baseline",
midpoint = 0,
low = "#d73027", mid = "white", high = "#1a9850")
# To see it:
print(p_final)
## Warning: Raster pixels are placed at uneven horizontal intervals and will be shifted
## ℹ Consider using `geom_tile()` instead.
## Area-Based Impact
# Example for one scenario
v <- values(diff_list[["Diff_100_vs_0"]])
prop_loss <- mean(v < 0, na.rm = TRUE)
prop_gain <- mean(v > 0, na.rm = TRUE)
## Threshold Mapping
severe_loss <- diff_list[["Diff_100_vs_0"]] < -0.7
plot(severe_loss, main = "Areas with Severe Predicted Loss (>0.7 decrease)")
# Regional Summaries
states_vect <- terra::vect(states_sf)
regional_summary <- lapply(names(diff_list), function(nm) {
r <- diff_list[[nm]]
vals <- terra::extract(r, states_vect, fun = mean, na.rm = TRUE)
# Add state names
vals$State <- states_sf$ID[vals$ID]
vals$Scenario <- nm
vals
}) %>% bind_rows()
## Warning: [extract] transforming vector data to the CRS of the raster
## Warning: [extract] transforming vector data to the CRS of the raster
## Warning: [extract] transforming vector data to the CRS of the raster
## Warning: [extract] transforming vector data to the CRS of the raster
## Warning: [extract] transforming vector data to the CRS of the raster
## Warning: [extract] transforming vector data to the CRS of the raster
## Warning: [extract] transforming vector data to the CRS of the raster
## Warning: [extract] transforming vector data to the CRS of the raster
## Warning: [extract] transforming vector data to the CRS of the raster
## Warning: [extract] transforming vector data to the CRS of the raster
## Warning: [extract] transforming vector data to the CRS of the raster
## Warning: [extract] transforming vector data to the CRS of the raster
## Warning: [extract] transforming vector data to the CRS of the raster
## Warning: [extract] transforming vector data to the CRS of the raster
## Warning: [extract] transforming vector data to the CRS of the raster
## Warning: [extract] transforming vector data to the CRS of the raster
## Warning: [extract] transforming vector data to the CRS of the raster
## Warning: [extract] transforming vector data to the CRS of the raster
## Warning: [extract] transforming vector data to the CRS of the raster
## Warning: [extract] transforming vector data to the CRS of the raster
## Warning: [extract] transforming vector data to the CRS of the raster
## Warning: [extract] transforming vector data to the CRS of the raster
## Warning: [extract] transforming vector data to the CRS of the raster
## Warning: [extract] transforming vector data to the CRS of the raster
## Warning: [extract] transforming vector data to the CRS of the raster
## Warning: [extract] transforming vector data to the CRS of the raster
## Warning: [extract] transforming vector data to the CRS of the raster
## Warning: [extract] transforming vector data to the CRS of the raster
## Warning: [extract] transforming vector data to the CRS of the raster
## Warning: [extract] transforming vector data to the CRS of the raster
## Warning: [extract] transforming vector data to the CRS of the raster
## Warning: [extract] transforming vector data to the CRS of the raster
## Warning: [extract] transforming vector data to the CRS of the raster
## Warning: [extract] transforming vector data to the CRS of the raster
## Warning: [extract] transforming vector data to the CRS of the raster
## Warning: [extract] transforming vector data to the CRS of the raster
## Warning: [extract] transforming vector data to the CRS of the raster
## Warning: [extract] transforming vector data to the CRS of the raster
## Warning: [extract] transforming vector data to the CRS of the raster
## Warning: [extract] transforming vector data to the CRS of the raster
## Warning: [extract] transforming vector data to the CRS of the raster
## Warning: [extract] transforming vector data to the CRS of the raster
## Warning: [extract] transforming vector data to the CRS of the raster
## Warning: [extract] transforming vector data to the CRS of the raster
## Warning: [extract] transforming vector data to the CRS of the raster
## Warning: [extract] transforming vector data to the CRS of the raster
## Warning: [extract] transforming vector data to the CRS of the raster
## Warning: [extract] transforming vector data to the CRS of the raster
## Warning: [extract] transforming vector data to the CRS of the raster
## Warning: [extract] transforming vector data to the CRS of the raster
## Warning: [extract] transforming vector data to the CRS of the raster
## Warning: [extract] transforming vector data to the CRS of the raster
## Warning: [extract] transforming vector data to the CRS of the raster
## Warning: [extract] transforming vector data to the CRS of the raster
## Warning: [extract] transforming vector data to the CRS of the raster
## Warning: [extract] transforming vector data to the CRS of the raster
## Warning: [extract] transforming vector data to the CRS of the raster
## Warning: [extract] transforming vector data to the CRS of the raster
## Warning: [extract] transforming vector data to the CRS of the raster
## Warning: [extract] transforming vector data to the CRS of the raster
## Warning: [extract] transforming vector data to the CRS of the raster
## Warning: [extract] transforming vector data to the CRS of the raster
## Warning: [extract] transforming vector data to the CRS of the raster
## Warning: [extract] transforming vector data to the CRS of the raster
## Warning: [extract] transforming vector data to the CRS of the raster
## Warning: [extract] transforming vector data to the CRS of the raster
## Warning: [extract] transforming vector data to the CRS of the raster
## Warning: [extract] transforming vector data to the CRS of the raster
## Warning: [extract] transforming vector data to the CRS of the raster
## Warning: [extract] transforming vector data to the CRS of the raster
## Warning: [extract] transforming vector data to the CRS of the raster
## Warning: [extract] transforming vector data to the CRS of the raster
## Warning: [extract] transforming vector data to the CRS of the raster
## Warning: [extract] transforming vector data to the CRS of the raster
## Warning: [extract] transforming vector data to the CRS of the raster
## Warning: [extract] transforming vector data to the CRS of the raster
## Warning: [extract] transforming vector data to the CRS of the raster
## Warning: [extract] transforming vector data to the CRS of the raster
## Warning: [extract] transforming vector data to the CRS of the raster
## Warning: [extract] transforming vector data to the CRS of the raster
## Warning: [extract] transforming vector data to the CRS of the raster
## Warning: [extract] transforming vector data to the CRS of the raster
## Warning: [extract] transforming vector data to the CRS of the raster
## Warning: [extract] transforming vector data to the CRS of the raster
## Warning: [extract] transforming vector data to the CRS of the raster
## Warning: [extract] transforming vector data to the CRS of the raster
## Warning: [extract] transforming vector data to the CRS of the raster
## Warning: [extract] transforming vector data to the CRS of the raster
## Warning: [extract] transforming vector data to the CRS of the raster
## Warning: [extract] transforming vector data to the CRS of the raster
## Warning: [extract] transforming vector data to the CRS of the raster
## Warning: [extract] transforming vector data to the CRS of the raster
## Warning: [extract] transforming vector data to the CRS of the raster
## Warning: [extract] transforming vector data to the CRS of the raster
## Warning: [extract] transforming vector data to the CRS of the raster
## Warning: [extract] transforming vector data to the CRS of the raster
## Warning: [extract] transforming vector data to the CRS of the raster
## Warning: [extract] transforming vector data to the CRS of the raster
## Warning: [extract] transforming vector data to the CRS of the raster
## Warning: [extract] transforming vector data to the CRS of the raster
regional_summary$Scenario_num <- as.numeric(gsub("Diff_|_vs_0", "", regional_summary$Scenario))
regional_summary$Scenario_num <- as.numeric(gsub("Diff_|_vs_0", "", regional_summary$Scenario))
regional_long <- regional_summary %>%
pivot_longer(
cols = starts_with("Shannon_"),
names_to = "Replicate",
values_to = "Mean_Change"
) %>%
filter(!is.na(Mean_Change))
print(regional_long)
## # A tibble: 1,000 × 6
## ID State Scenario Scenario_num Replicate Mean_Change
## <int> <chr> <chr> <dbl> <chr> <dbl>
## 1 1 alabama Diff_1_vs_0 1 Shannon_1 0
## 2 2 arkansas Diff_1_vs_0 1 Shannon_1 0
## 3 3 florida Diff_1_vs_0 1 Shannon_1 0
## 4 4 georgia Diff_1_vs_0 1 Shannon_1 0
## 5 5 louisiana Diff_1_vs_0 1 Shannon_1 0
## 6 6 mississippi Diff_1_vs_0 1 Shannon_1 0
## 7 7 north carolina Diff_1_vs_0 1 Shannon_1 0
## 8 8 south carolina Diff_1_vs_0 1 Shannon_1 0
## 9 9 tennessee Diff_1_vs_0 1 Shannon_1 0
## 10 10 virginia Diff_1_vs_0 1 Shannon_1 0
## # ℹ 990 more rows
ggplot(regional_long, aes(x = Scenario_num, y = Mean_Change, color = State)) +
geom_line() +
theme_bw() +
labs(x = "Cogongrass Cover (%)",
y = "Δ Shannon Diversity",
color = "State")
## Subset regional_long for 100% scenario
regional_100 <- regional_long %>%
filter(Scenario_num == 100)