Step 1. Manipulate sea surface temperature and dissolved oxygen as proxies for ocean acidificiation change

Load packages

if (!require(pacman)) install.packages("pacman")
library(pacman)
p_load(
  tidyverse, here, glue,
  devtools,
  raster,
  sdmpredictors, dismo, 
  deldir, 
  mapview,
  tmap)

devtools::load_all(here("../oatools"))

Set paths and variables

dir_data        <- here("data")
dir_sdmdata_old <- here("data/sdmpredictors")
dir_cache       <- here("cache")
dir_sdmdata     <- here("cache/sdmpredictors")

SST_tif <- here("data/sst_mean.tif")
DO_tif  <- here("data/do_mean.tif")

Set cache

if (!dir.exists(dir_data))    dir.create(dir_data)
if (!dir.exists(dir_cache))   dir.create(dir_cache)
if (!dir.exists(dir_sdmdata) & dir.exists(dir_sdmdata_old))
  file.rename(dir_sdmdata_old, dir_sdmdata)
if (!dir.exists(dir_sdmdata)) dir.create(dir_sdmdata)

Set extent and coordinate reference system

This is specific to our study area. We used NAD 83 California Teale Albers and used an extent that included waters off all three West Coast States.

ext_study <- extent(-670000, 350000, -885000, 1400000)
crs_study <- '+init=EPSG:6414'

Create sea surface temperature layer (mean and range)

We used filled and _nofill to ensure non NA values for cells near the coast

r_sst_mean_nofill <- lyr_to_tif(
  lyr = "BO_sstmean", 
  tif = here("data/sst_mean.tif"),
  crs = crs_study,
  dir_sdm_cache = dir_sdmdata,
  extent_crop   = ext_study, 
  redo=T, fill_na=FALSE)

r_sst_mean <- lyr_to_tif(
  lyr = "BO_sstmean", 
  tif = here("data/sst_mean.tif"),
  crs = crs_study,
  dir_sdm_cache = dir_sdmdata,
  extent_crop   = ext_study, 
  redo=T, fill_na=TRUE, fill_window=11) #caclulate mean

n_na_nofill <- sum(is.na(raster::getValues(r_sst_mean_nofill)))
n_na        <- sum(is.na(raster::getValues(r_sst_mean)))

r_sst_range_nofill <- lyr_to_tif(
  lyr = "BO_sstrange", 
  tif = here("data/sst_range.tif"),
  crs = crs_study,
  dir_sdm_cache = dir_sdmdata,
  extent_crop   = ext_study, 
  redo=T, fill_na=FALSE)

r_sst_range <- lyr_to_tif(
  lyr = "BO_sstrange", 
  tif = here("data/sst_range.tif"),
  crs = crs_study,
  dir_sdm_cache = dir_sdmdata,
  extent_crop   = ext_study, 
  redo=T, fill_na=TRUE, fill_window=11)

Create dissolved oxygen layer (mean and range)

r_do_mean_nofill <- lyr_to_tif(
  lyr = "BO_dissox", 
  tif = here("data/do_mean.tif"),
  crs = crs_study,
  dir_sdm_cache = dir_sdmdata,
  extent_crop   = ext_study, 
  redo=T, fill_na=FALSE)

r_do_mean <- lyr_to_tif(
  lyr = "BO_dissox", 
  tif = here("data/do_mean.tif"),
  crs = crs_study,
  dir_sdm_cache = dir_sdmdata,
  extent_crop   = ext_study, 
  redo=T, fill_na=TRUE, fill_window=11) #calculate mean

r_do_range_nofill <- lyr_to_tif(
  lyr = "BO2_dissoxrange_bdmin", 
  tif = here("data/do_range.tif"),
  crs = crs_study,
  dir_sdm_cache = dir_sdmdata,
  extent_crop   = ext_study, 
  redo=T, fill_na=FALSE)

r_do_range <- lyr_to_tif(
  lyr = "BO2_dissoxrange_bdmin", 
  tif = here("data/do_range.tif"),
  crs = crs_study,
  dir_sdm_cache = dir_sdmdata,
  extent_crop   = ext_study, 
  redo=T, fill_na=TRUE, fill_window=11)

Step 3. Create “oceanographic dissimiarity” layer relative to monitoring asset

Normalize SST, DO, and distance

Normalize by dividing each raster by the maximum ovbserved value in that raster. Now each raster contains values between 0 and 1.

r_sst_mean_nofill_norm<-r_sst_mean_nofill/maxValue(r_sst_mean_nofill)
r_sst_range_nofill_norm<-r_sst_range_nofill/maxValue(r_sst_range_nofill)
r_do_mean_nofill_norm<-r_do_mean_nofill/maxValue(r_do_mean_nofill)
r_do_range_nofill_norm<-r_do_range_nofill/maxValue(r_do_range_nofill)

polygonsst_norm<-polygonsst/maxValue(r_sst_mean_nofill)
carbcompletepolygonsst_norm<-carbcompletepolygonsst/maxValue(r_sst_mean_nofill)
highfreqpolygonsst_norm<-highfreqpolygonsst/maxValue(r_sst_mean_nofill)

polygonsstrange_norm<-polygonsstrange/maxValue(r_sst_range_nofill)
carbcompletepolygonsstrange_norm<-carbcompletepolygonsstrange/maxValue(r_sst_range_nofill)
highfreqpolygonsstrange_norm<-highfreqpolygonsstrange/maxValue(r_sst_range_nofill)

polygondo_norm<-polygondo/maxValue(r_do_mean_nofill)
carbcompletepolygondo_norm<-carbcompletepolygondo/maxValue(r_do_mean_nofill)
highfreqpolygondo_norm<-highfreqpolygondo/maxValue(r_do_mean_nofill)

polygondorange_norm<-polygondorange/maxValue(r_do_range_nofill)
carbcompletepolygondorange_norm<-carbcompletepolygondorange/maxValue(r_do_range_nofill)
highfreqpolygondorange_norm<-highfreqpolygondorange/maxValue(r_do_range_nofill)

Calculate differences between each cell and the closest monitoring site

# sst mean
sstmeandiff <- abs(r_sst_mean_nofill_norm - polygonsst)
carbcompletesstmeandiff <- abs(r_sst_mean_nofill_norm - carbcompletepolygonsst)
highfreqsstmeandiff <- abs(r_sst_mean_nofill_norm - highfreqpolygonsst)

# sst range
sstrangediff <- abs(r_sst_range_nofill_norm - polygonsstrange)
carbcompletesstrangediff <- abs(r_sst_range_nofill_norm - carbcompletepolygonsstrange)
highfreqsstrangediff <- abs(r_sst_range_nofill_norm - highfreqpolygonsstrange)

# do mean
domeandiff <- abs(r_do_mean_nofill_norm - polygondo)
carbcompletedomeandiff <- abs(r_do_mean_nofill_norm - carbcompletepolygondo)
highfreqdomeandiff <- abs(r_do_mean_nofill_norm - highfreqpolygondo)

# do range
dorangediff <- abs(r_do_range_nofill_norm - polygondorange)
carbcompletedorangediff <- abs(r_do_range_nofill_norm - carbcompletepolygondorange)
highfreqdorangediff <- abs(r_do_range_nofill_norm - highfreqpolygondorange)

Determine Weights

We chose these weights because we want to be sure to capture the “extreme” acidification events, influenced more by the range (temporal variation) than by the mean. We chose the distance weight because the other aspects of our analysis are all normalized between 0 and 1, so the distance values must we weighted such that they are on the same order. A sensitivity analysis on these weighting factors follows.

distanceweight = 10^-6
temporalweight = 10

Create oceanographic dissimilarity layer

To account for variation in both SST and DO as a combined proxy for ocean acidification variability, we use the following formula:

spatial dissimilarity = (normalized difference between mean sst in cell and at nearest monitoring site)^2 + (normalized difference between mean do in cell and at nearest monitoring site)^2

temporal dissimilarity = (normalized difference between range sst in cell and at nearest monitoring site)^2 + (normalized difference between range do in cell and at nearest monitoring site)^2

oceanographic dissimilarity = sqrt(spatial dissimilarity + weighted temporal dissimilarity)

dissimilarity <- sqrt((sstmeandiff^2+domeandiff^2)+temporalweight*(sstrangediff^2+dorangediff^2))

carbcompletedissimilarity<- sqrt((carbcompletesstmeandiff^2+carbcompletedomeandiff^2)+temporalweight*(carbcompletesstrangediff^2+carbcompletedorangediff^2))

highfreqdissimilarity <- sqrt((highfreqsstmeandiff^2+highfreqdomeandiff^2)+temporalweight*(highfreqsstrangediff^2+highfreqdorangediff^2))

Step 4. Combine dissimilarity and distance to find gaps

To combine oceanigraphic dissimilarity and distnce, we use the following formula:

gap = sqrt(oceanographic dissimilarity^2 + weighted geographic distance^2)

distance<-distanceFromPoints(dissimilarity, inventorycoords)*distanceweight
carbcompletedistance<-distanceFromPoints(carbcompletedissimilarity, carbcompletecoords)*distanceweight
highfreqdistance<-distanceFromPoints(highfreqdissimilarity, highfreqcoords)*distanceweight

gap<-setValues(distance, sqrt((getValues(distance)^2+(getValues(dissimilarity)^2))))
carbcompletegap<-setValues(carbcompletedistance, sqrt((getValues(carbcompletedistance)^2+(getValues(carbcompletedissimilarity)^2))))
highfreqgap<-setValues(highfreqdistance, sqrt((getValues(highfreqdistance)^2+(getValues(highfreqdissimilarity)^2))))

severegaps <- setValues(distance, sqrt((getValues(distance)^2+(getValues(dissimilarity)^2)))) > quantile(gap, (.999))
highprioritygaps <- setValues(distance, sqrt((getValues(distance)^2+(getValues(dissimilarity)^2)))) > quantile(gap, (.99))
lowprioritygaps<-setValues(distance, sqrt((getValues(distance)^2+(getValues(dissimilarity)^2)))) > quantile(gap, (.75))
finalgaps<- severegaps+lowprioritygaps+highprioritygaps

carbcompleteseveregaps <- setValues(carbcompletedistance, sqrt((getValues(carbcompletedistance)^2+(getValues(carbcompletedissimilarity)^2)))) > quantile(carbcompletegap, (.999))
carbcompletehighprioritygaps<- setValues(carbcompletedistance, sqrt((getValues(carbcompletedistance)^2+(getValues(carbcompletedissimilarity)^2)))) > quantile(carbcompletegap, (.99))
carbcompletelowprioritygaps<- setValues(carbcompletedistance, sqrt((getValues(carbcompletedistance)^2+(getValues(carbcompletedissimilarity)^2)))) > quantile(carbcompletegap, (.75))
carbcompletefinalgaps<- carbcompleteseveregaps + carbcompletelowprioritygaps+carbcompletehighprioritygaps

highfreqseveregaps <- setValues(highfreqdistance, sqrt((getValues(highfreqdistance)^2+(getValues(highfreqdissimilarity)^2)))) > quantile(highfreqgap, (.999))
highfreqhighprioritygaps<-setValues(highfreqdistance, sqrt((getValues(highfreqdistance)^2+(getValues(highfreqdissimilarity)^2)))) > quantile(highfreqgap, (.99))
highfreqlowprioritygaps<-setValues(highfreqdistance, sqrt((getValues(highfreqdistance)^2+(getValues(highfreqdissimilarity)^2)))) > quantile(highfreqgap, (.75))
highfreqfinalgaps<- highfreqseveregaps+highfreqlowprioritygaps+highfreqhighprioritygaps

Visualize gaps along the West Coast

Here, severe gaps, or the top 0.1% of gaps will have a value of 3, high priority gaps, or the top 1% of gaps will have a value of 2, and low priority gaps, or the top 25% of gaps will have a value of 1.

plot(finalgaps)

plot(carbcompletefinalgaps)

plot(highfreqfinalgaps)

Step 5. Sensitivity Analysis on the weighting factors

Create matrix of possible weights for temoral weight and distance weight

distanceweight = c(0.2*10^-6, 0.4*10^-6, 0.6*10^-6, 0.8*10^-6, 10^-6, 1.2*10^-6, 1.4*10^-6, 1.6*10^-6, 1.8*10^-6, 2*10^-6)
temporalweight = c(2, 4, 6, 8, 10, 12, 14, 16, 18, 20)

Run gap analysis through all cominations of weights to compare where high priority gaps exist

The result is 100 rasters of the gap analysis result for where high priority gaps exist under different weighting combinations. This can be repeated for high priority gaps and low priority gaps.

rastersensitivity <- list()

for(i in 1:length(distanceweight)){
  for(j in 1:length(temporalweight)){
    dissimilarity <- sqrt((sstmeandiff^2+domeandiff^2)+temporalweight[j]*(sstrangediff^2+dorangediff^2))
    distance<-distanceFromPoints(dissimilarity, inventorycoords)*distanceweight[i]
    gap<-setValues(distance, sqrt((getValues(distance)^2+(getValues(dissimilarity)^2))))
    severegaps <- setValues(distance, sqrt((getValues(distance)^2+(getValues(dissimilarity)^2)))) > quantile(gap, (.999))
    name = paste(temporalweight[j], distanceweight[i], sep = "_")
    rastersensitivity[[name]] = highprioritygaps
  }
}

Transform to raster stack

sensitivitystack <- stack(rastersensitivity[[1]])
for(i in 2:length(rastersensitivity)) sensitivitystack <- addLayer(sensitivitystack, rastersensitivity[[i]])

Sum across all rasters within raster stack

In cells where the sum is 100, severe gaps existed in all of the weighing combnations. Where the sum is 0, severe gaps were not present under any of the weighting combinations. In cells where the sum is between 0 and 100, the presence of a severe gap was sensitive to weighting factors.

sum <- sum(sensitivitystack)
plot(sum)

freq(sum)