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" "sph"
## [5] "srad" "TreeCanopy_NLCD" "ndvi" "avg_pop_den_1km"
env_vars <- model_vars
cat(sprintf("\nEnvironmental predictors : %s\n", paste(env_vars, collapse = ", ")))
##
## Environmental predictors : Cogongrass_Cover, elevation, pr, sph, srad, TreeCanopy_NLCD, ndvi, avg_pop_den_1km
Aside from the code chunk “load_data” there is nothing else important above this point. It is how I collected nlcd, elevation etc. rasters, but those are all saved and ready to go. The code below is the actual workflow for building the env stack, running predictions, and analyzing results.
# 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.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: sph
## Loading: srad
## Loading: TreeCanopy_NLCD
## Loading: ndvi
## 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)
}
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
## sph 4326
## srad 4326
## TreeCanopy_NLCD 5070
## ndvi 4326
## 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 ---
local_dir <- "C:/Users/alanivory34428/Desktop/03_Biodiversity/02_Data/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(!compareGeom(nlcd_raster, env_stack, stopOnError = FALSE)) {
nlcd_raster <- project(nlcd_raster, env_stack, method = "near")
}
# 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...
## |---------|---------|---------|---------|========================================= |---------|---------|---------|---------|=========================================
# Builds predictor dataframe, inserts cogongrass scenario, predicts, returns raster
predict_diversity <- function(cogongrass_value, env_stack, rf_model) {
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)
# Only predict on valid cells
valid_cells <- complete.cases(pred_df)
if(sum(valid_cells) == 0)
stop("No valid cells found for prediction. Check NLCD mask.")
# Add scenario variable
pred_df$Cogongrass_Cover <- cogongrass_value
# Match model variables
all_vars <- attr(rf_model$terms, "term.labels")
pred_df_valid <- pred_df[valid_cells,
all_vars, drop = FALSE]
# Predict
predicted_vals <- predict(rf_model,
newdata = pred_df_valid)
# Map back to raster
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)
}
## 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.0001356290 -0.007701703 0.01886696
## 2 Diff_2_vs_0 0.0012985415 -0.030605133 0.02513047
## 3 Diff_3_vs_0 0.0003464533 -0.031955825 0.02569142
## 4 Diff_4_vs_0 0.0001623097 -0.034865115 0.02578952
## 5 Diff_5_vs_0 0.0009469812 -0.037114557 0.03552967
## 6 Diff_6_vs_0 0.0028589211 -0.048444702 0.03943584
## 7 Diff_7_vs_0 0.0030448051 -0.049212279 0.06042167
## 8 Diff_8_vs_0 0.0044482088 -0.049077183 0.07059618
## 9 Diff_9_vs_0 0.0031520803 -0.048343908 0.09670917
## 10 Diff_10_vs_0 0.0026407888 -0.049525306 0.10060042
## 11 Diff_11_vs_0 0.0034059948 -0.068899976 0.11157174
## 12 Diff_12_vs_0 0.0036137828 -0.079794582 0.12430000
## 13 Diff_13_vs_0 0.0051067339 -0.080592944 0.12430000
## 14 Diff_14_vs_0 0.0033961005 -0.092562239 0.12678151
## 15 Diff_15_vs_0 0.0027228508 -0.094338721 0.13314818
## 16 Diff_16_vs_0 0.0015774240 -0.101627664 0.13343950
## 17 Diff_17_vs_0 0.0006745478 -0.105587769 0.13735877
## 18 Diff_18_vs_0 0.0005670232 -0.106557257 0.13672746
## 19 Diff_19_vs_0 -0.0016408148 -0.109777571 0.13704183
## 20 Diff_20_vs_0 0.0002359400 -0.107769950 0.13852169
## 21 Diff_21_vs_0 -0.0014878391 -0.191695098 0.12947139
## 22 Diff_22_vs_0 0.0006770293 -0.198325316 0.13970027
## 23 Diff_23_vs_0 0.0018363881 -0.200017303 0.13878561
## 24 Diff_24_vs_0 0.0051559487 -0.200443834 0.16608199
## 25 Diff_25_vs_0 0.0050485733 -0.211993547 0.17854369
## 26 Diff_26_vs_0 0.0046767242 -0.213418565 0.18167894
## 27 Diff_27_vs_0 0.0029505598 -0.218194000 0.18875150
## 28 Diff_28_vs_0 0.0037415075 -0.217153256 0.19500866
## 29 Diff_29_vs_0 0.0057939135 -0.222023531 0.19918652
## 30 Diff_30_vs_0 0.0061298274 -0.219707626 0.19997227
## 31 Diff_31_vs_0 0.0095591858 -0.218397092 0.19984448
## 32 Diff_32_vs_0 0.0217528721 -0.217011236 0.20775359
## 33 Diff_33_vs_0 0.0212440256 -0.219742078 0.20977435
## 34 Diff_34_vs_0 0.0216392578 -0.220650905 0.21390007
## 35 Diff_35_vs_0 0.0233031734 -0.220495020 0.21366785
## 36 Diff_36_vs_0 0.0205190716 -0.237198662 0.21116477
## 37 Diff_37_vs_0 0.0222684845 -0.236160410 0.21336591
## 38 Diff_38_vs_0 0.0231988647 -0.235448398 0.21809616
## 39 Diff_39_vs_0 0.0293404946 -0.229471021 0.23044641
## 40 Diff_40_vs_0 0.0443139795 -0.221677157 0.26103981
## 41 Diff_41_vs_0 0.0527579741 -0.217161538 0.27773679
## 42 Diff_42_vs_0 0.0544097079 -0.216552544 0.28416686
## 43 Diff_43_vs_0 0.0566125262 -0.216446702 0.28924028
## 44 Diff_44_vs_0 0.0568104537 -0.215304206 0.28908263
## 45 Diff_45_vs_0 0.0622864549 -0.215366797 0.31383556
## 46 Diff_46_vs_0 0.0703279264 -0.211153479 0.33169245
## 47 Diff_47_vs_0 0.0781536020 -0.204317608 0.36582370
## 48 Diff_48_vs_0 0.0780383861 -0.204091955 0.36582370
## 49 Diff_49_vs_0 0.0775760817 -0.204296854 0.36328152
## 50 Diff_50_vs_0 0.0789341054 -0.201530620 0.36463211
## 51 Diff_51_vs_0 0.0784113122 -0.202125930 0.36346601
## 52 Diff_52_vs_0 0.0772504178 -0.203994742 0.35888934
## 53 Diff_53_vs_0 0.0771715049 -0.206847094 0.35967520
## 54 Diff_54_vs_0 0.0774628452 -0.207028951 0.35983005
## 55 Diff_55_vs_0 0.0772931183 -0.210197121 0.35921672
## 56 Diff_56_vs_0 0.0768955041 -0.212063524 0.35921672
## 57 Diff_57_vs_0 0.0773741561 -0.210871863 0.36059436
## 58 Diff_58_vs_0 0.0774650346 -0.210871863 0.36003046
## 59 Diff_59_vs_0 0.0809516528 -0.210170471 0.36853038
## 60 Diff_60_vs_0 0.0809737673 -0.210170471 0.36853038
## 61 Diff_61_vs_0 0.0811028651 -0.211346589 0.37089878
## 62 Diff_62_vs_0 0.0790629287 -0.215286409 0.37189814
## 63 Diff_63_vs_0 0.0815258992 -0.212022934 0.38080909
## 64 Diff_64_vs_0 0.0813978087 -0.213006372 0.38230923
## 65 Diff_65_vs_0 0.0780655218 -0.210710996 0.38493751
## 66 Diff_66_vs_0 0.0776210128 -0.210710996 0.38252104
## 67 Diff_67_vs_0 0.0770663241 -0.210710996 0.38252104
## 68 Diff_68_vs_0 0.0730758048 -0.218580457 0.38252104
## 69 Diff_69_vs_0 0.0734828674 -0.218151694 0.38129775
## 70 Diff_70_vs_0 0.0623700319 -0.242969883 0.37986219
## 71 Diff_71_vs_0 0.0623479910 -0.242969883 0.37986219
## 72 Diff_72_vs_0 0.0585921356 -0.247482725 0.37905681
## 73 Diff_73_vs_0 0.0335432996 -0.302407189 0.37436766
## 74 Diff_74_vs_0 0.0162075082 -0.336114407 0.36887995
## 75 Diff_75_vs_0 0.0133512096 -0.340534597 0.36881159
## 76 Diff_76_vs_0 0.0128212363 -0.340242420 0.36479731
## 77 Diff_77_vs_0 0.0125261076 -0.340242420 0.36479731
## 78 Diff_78_vs_0 0.0116188403 -0.340242420 0.36194245
## 79 Diff_79_vs_0 0.0039604238 -0.352064209 0.34795824
## 80 Diff_80_vs_0 -0.0169450402 -0.375664229 0.31747742
## 81 Diff_81_vs_0 -0.0171830120 -0.377589096 0.31747742
## 82 Diff_82_vs_0 -0.0171830120 -0.377589096 0.31747742
## 83 Diff_83_vs_0 -0.0683956110 -0.419646346 0.26749144
## 84 Diff_84_vs_0 -0.0721275413 -0.422996055 0.26576706
## 85 Diff_85_vs_0 -0.0721275413 -0.422996055 0.26576706
## 86 Diff_86_vs_0 -0.0721275413 -0.422996055 0.26576706
## 87 Diff_87_vs_0 -0.4473657701 -0.852807458 -0.17817782
## 88 Diff_88_vs_0 -0.4759827990 -0.894780528 -0.19835943
## 89 Diff_89_vs_0 -0.4759827990 -0.894780528 -0.19835943
## 90 Diff_90_vs_0 -0.4831894253 -0.899419291 -0.20447579
## 91 Diff_91_vs_0 -0.4821759852 -0.898405851 -0.20346235
## 92 Diff_92_vs_0 -0.4682266067 -0.884329236 -0.18938573
## 93 Diff_93_vs_0 -0.4682266067 -0.884329236 -0.18938573
## 94 Diff_94_vs_0 -0.4682266067 -0.884329236 -0.18938573
## 95 Diff_95_vs_0 -0.4682266067 -0.884329236 -0.18938573
## 96 Diff_96_vs_0 -0.4682266067 -0.884329236 -0.18938573
## 97 Diff_97_vs_0 -0.4682266067 -0.884329236 -0.18938573
## 98 Diff_98_vs_0 -0.4682266067 -0.884329236 -0.18938573
## 99 Diff_99_vs_0 -0.4682266067 -0.884329236 -0.18938573
## 100 Diff_100_vs_0 -0.4682266067 -0.884329236 -0.18938573
plot_raster <- function(rast_layer, title, subtitle = NULL,
palette = "viridis", midpoint = NULL,
low = NULL, high = NULL, mid = NULL,
limits = NULL) {
# Convert SpatRaster
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)) +
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.00171
## 2 2 arkansas Diff_1_vs_0 1 Shannon_1 0.000649
## 3 3 florida Diff_1_vs_0 1 Shannon_1 -0.000134
## 4 4 georgia Diff_1_vs_0 1 Shannon_1 0.000930
## 5 5 louisiana Diff_1_vs_0 1 Shannon_1 0.00130
## 6 6 mississippi Diff_1_vs_0 1 Shannon_1 0.00157
## 7 7 north carolina Diff_1_vs_0 1 Shannon_1 0.000228
## 8 8 south carolina Diff_1_vs_0 1 Shannon_1 0.000499
## 9 9 tennessee Diff_1_vs_0 1 Shannon_1 0.000317
## 10 10 virginia Diff_1_vs_0 1 Shannon_1 -0.000208
## # ℹ 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)
eco_l2 <- st_read("C:/Users/alanivory34428/Desktop/03_Biodiversity/02_Data/NA_CEC_Eco_Level2.shp") %>% st_transform(crs(diff_list[[1]]))
## Reading layer `NA_CEC_Eco_Level2' from data source
## `C:\Users\alanivory34428\Desktop\03_Biodiversity\02_Data\NA_CEC_Eco_Level2.shp'
## using driver `ESRI Shapefile'
## Simple feature collection with 2261 features and 8 fields
## Geometry type: POLYGON
## Dimension: XY
## Bounding box: xmin: -4334052 ymin: -3313739 xmax: 3324076 ymax: 4267265
## Projected CRS: Sphere_ARC_INFO_Lambert_Azimuthal_Equal_Area
eco_l3 <- st_read("C:/Users/alanivory34428/Desktop/03_Biodiversity/02_Data/NA_CEC_Eco_Level3.shp") %>% st_transform(crs(diff_list[[1]]))
## Reading layer `NA_CEC_Eco_Level3' from data source
## `C:\Users\alanivory34428\Desktop\03_Biodiversity\02_Data\NA_CEC_Eco_Level3.shp'
## using driver `ESRI Shapefile'
## Simple feature collection with 2548 features and 11 fields
## Geometry type: POLYGON
## Dimension: XY
## Bounding box: xmin: -4334052 ymin: -3313739 xmax: 3324076 ymax: 4267265
## Projected CRS: Sphere_ARC_INFO_Lambert_Azimuthal_Equal_Area
# Convert to SpatVector for terra::extract
eco_l2_vect <- terra::vect(eco_l2)
eco_l3_vect <- terra::vect(eco_l3)
summarize_by_ecoregion <- function(diff_list, eco_vect, name_col) {
# Rasterize polygons ONCE using the first raster
template_raster <- diff_list[[1]]
eco_rast <- terra::rasterize(eco_vect, template_raster, field = name_col)
lapply(names(diff_list), function(nm) {
r <- diff_list[[nm]]
z <- terra::zonal(r, eco_rast, fun = "mean", na.rm = TRUE)
colnames(z) <- c("Ecoregion", "Mean_Change")
z$Scenario <- nm
z
}) %>%
bind_rows() %>%
filter(!is.na(Mean_Change)) %>%
mutate(
Scenario_num = as.numeric(gsub("Diff_|_vs_0", "", Scenario))
)
}
# Check your column names first:
# names(as.data.frame(eco_l2_vect))
# names(as.data.frame(eco_l3_vect))
# Common EPA shapefile name columns:
# Level 2 -> "NA_L2NAME"
# Level 3 -> "US_L3NAME"
eco_l2_summary <- summarize_by_ecoregion(diff_list, eco_l2_vect, name_col = "NA_L2NAME")
eco_l3_summary <- summarize_by_ecoregion(diff_list, eco_l3_vect, name_col = "NA_L3NAME")
# L3 Ecoregion
eco_l3_100 <- eco_l3_summary %>%
dplyr::filter(Scenario_num == 100)
eco_l3_map <- eco_l3 %>%
dplyr::left_join(eco_l3_100, by = c("NA_L3NAME" = "Ecoregion"))
eco_l3_map_vect <- terra::vect(eco_l3_map)
template_raster <- diff_list[[1]]
eco_l3_map_vect <- terra::crop(eco_l3_map_vect, template_raster)
terra::plot(
eco_l3_map_vect,
"Mean_Change",
col = hcl.colors(50, "RdYlGn", rev = TRUE),
main = "Δ Shannon Diversity (100% Cogongrass)"
)
ggplot(eco_l3_map) +
geom_sf(aes(fill = Mean_Change), color = NA) +
scale_fill_gradient2(
low = "#d73027",
mid = "white",
high = "#1a9850",
midpoint = 0
) +
theme_bw() +
labs(
title = "Δ Shannon Diversity (100% Cogongrass)",
fill = "Change"
)
#L2 Ecoregion
eco_l2_100 <- eco_l2_summary %>%
dplyr::filter(Scenario_num == 100)
eco_l2_map <- eco_l2 %>%
dplyr::left_join(eco_l2_100, by = c("NA_L2NAME" = "Ecoregion"))
eco_l2_map_vect <- terra::vect(eco_l2_map)
template_raster <- diff_list[[1]]
eco_l2_map_vect <- terra::crop(eco_l2_map_vect, template_raster)
terra::plot(
eco_l2_map_vect,
"Mean_Change",
col = hcl.colors(50, "RdYlGn", rev = TRUE),
main = "Δ Shannon Diversity (Level 2 Ecoregions, 100% Cogongrass)"
)
ggplot(eco_l2_map) +
geom_sf(aes(fill = Mean_Change), color = "black", linewidth = 0.2) +
scale_fill_gradient2(
low = "#d73027",
mid = "white",
high = "#1a9850",
midpoint = 0
) +
theme_bw() +
labs(
title = "Δ Shannon Diversity (Level 2 Ecoregions, 100% Cogongrass)",
fill = "Change"
)