Geospatial Blog Post

Creating a Function for Coastal Species Habitat
MEDS
Geospatial
R
Coastal Ecology
Author
Affiliation
Published

December 2, 2022

Introduction

This project was adapted from an assignment to explore the suitable habitats for species along the Western Coast, beginning with an Oyster and then creating a function to explore any species. The parameters to explore are depth and sea surface temperature. This could further be expanded with more variables, including change of temperature within the water column. These regions are broken down to five Exclusive Economic Zones (EEZ) along the West Coast of the US.

This could be applied for restoration efforts to determine where habitat is suitable for any oceanic species.

Data

library(terra)      # Work with large raster spatial data
library(here)       # Set file location
library(sf)         # Work with vector spatial data
library(tidyverse)  # Data manipulization and visualization
library(tmap)       # Create maps
library(ggpubr)     # Combines ggplots

The data is in both raster and vector formats, reprojected to match coordinate reference systems. The series of raster files depicting each yearly sea surface temperature are all collapsed to show the average for the time range. We begin with the regional data as an sf object, a series of annual sea surface temperature raster files, and a single raster ocean depth file.

#### Stack annual data ----
SSTAnnual <- c(Annual2008, Annual2009, Annual2010, Annual2011, Annual2012)


#### Reproject Coordinate System ----
st_crs(SSTAnnual)     # WGS84, EPSG 9122
st_crs(Regions)       # WGS84 EPSG 4326
st_crs(Depth)         # WGS84, EPSG 4326

SSTAnnual <- project(SSTAnnual, Depth)
st_crs(SSTAnnual)     # WGS84, EPSG 4326


#### Find mean Sea Surface Temperature ----

# Collapse down layers to one SST mean
SSTMean <- mean(SSTAnnual)
SSTMean <- (SSTMean$mean - 273.15)

# Check they will match
SSTDepth <- c(Depth, SSTMean)

Find suitable locations

Reclassification here is used to select cells matching range of sea surface temperature and depth suitable for Oysters and set them as 1, all other cells are set as NA. A function is then used to combine the two reclassified raster files to find cells that satisfy both. Each plot depicts the reclassified cells for each step.

#### Set up reclassification matrices ----

SSTrcl <- matrix(c(-Inf, 11, NA,
                   11, 30, 1,        # sea surface temperature: 11-30°C
                   30, Inf, NA),
                     ncol = 3, byrow = TRUE)
OysterSST <- classify(SSTMean, rcl = SSTrcl)
plot(OysterSST, col = "#3632a8", bty = "L",
     main = "Suitable Sea Surface Temperature for Oysters")

Depthrcl <- matrix(c(-Inf, -70, NA,
                     -70, 0, 1,      # depth: 0-70 meters below sea level
                     0, Inf, NA),
                     ncol = 3, byrow = TRUE)
OysterDepth <- classify(Depth, rcl = Depthrcl)
plot(OysterDepth, col = "#3632a8", bty = "L",
     main = "Suitable Depth for Oysters")

#### Overlay to find areas that satisfy BOTH ----
fun_mult = function(x,y){return(x*y)}     # function to multiply layers

OystersHabitat <- lapp(c(OysterSST, OysterDepth), fun_mult)
plot(OystersHabitat, col = "#3632a8", bty = "L",
     main = "Suitable Habitat for Oysters")

Determine the most suitable EEZ

The Regions vector is now overlayed with the Oysters Habitat as a mask to determine suitable habitat in each region.

#### Turn Regions data into raster ----
Regions$rgn <- as.factor(Regions$rgn)
RegionsRast <- rasterize(vect(Regions), OystersHabitat, field = "rgn")


#### Select suitable cells in EEZ ----
ROmask <- mask(RegionsRast, OystersHabitat)


#### Find total suitable area within each EEZ ----
OysterZone <- expanse(ROmask, unit = "km", byValue = TRUE)


#### Find percentage of each zone ----
OysterRegion <- cbind(OysterZone, Regions)
OysterRegion <- OysterRegion |> 
  mutate(percent = (area/area_km2)*100)

Visualizations

#### Total suitable area by region
tm_shape(OysterRegion) +
  tm_polygons("area", palette = "YlGn") +
  tm_text(text = "rgn",
          size = .65) +
  tm_layout(main.title = "Suitable Oyster Area",
            main.title.size = 1.2,
            main.title.position = "center") +
  tm_layout(legend.position = c("left", "bottom"))
Some legend labels were too wide. These labels have been resized to 0.55, 0.55, 0.55. Increase legend.width (argument of tm_layout) to make the legend wider and therefore the labels larger.

#### Percent suitable area by region
tm_shape(OysterRegion) +
  tm_polygons("percent", palette = "YlGn") +
  tm_text(text = "rgn",
          size = .7) +
  tm_layout(main.title = "Suitable Oyster Percentage",
            main.title.size = 1.2,
            main.title.position = "center") +
  tm_layout(legend.position = c("left", "bottom"))

Creating a Function

This function will repeat the previous steps for any species habitat with the inputs of temperrature and depth ranges.

Species_Habitat <- function(species_name, temp_min, temp_max, depth_max, depth_min){
  
  # RECLASSIFICATION
  SSTrcl <- matrix(c(-Inf, temp_min, NA,
                   temp_min, temp_max, 1,
                   temp_max, Inf, NA),
                   ncol = 3, byrow = TRUE)
  Depthrcl <- matrix(c(-Inf, depth_max, NA,
                     depth_max, depth_min, 1,
                     depth_min, Inf, NA),
                     ncol = 3, byrow = TRUE)  
  
  # APPLY RECLASS TO BOTH
  SpeciesSST <- classify(SSTMean, rcl = SSTrcl)
  SpeciesDepth <- classify(Depth, rcl = Depthrcl)
  SpeciesHabitat <- lapp(c(SpeciesSST, SpeciesDepth), fun_mult)
  
  # SELECT SUITABLE CELLS IN EEZ
  Regions$rgn <- as.factor(Regions$rgn)
  RegionsSpecRast <- rasterize(vect(Regions), SpeciesHabitat, field = "rgn")
  RSpecmask <- mask(RegionsSpecRast, SpeciesHabitat)

  # TOTAL SUITABLE AREA PER EEZ
  SpeciesZone <- expanse(RSpecmask, unit = "km", byValue = TRUE)
  SpeciesZone # Error on row number matching???

  # PERCENTAGE PER EEZ
  SpeciesRegion <- cbind(SpeciesZone, Regions)
  SpeciesRegion <- SpeciesRegion |>
     mutate(percent = (area/area_km2)*100)
  
  # MAP AREA BY REGION
  SpecAreaMap <- tm_shape(SpeciesRegion) +
  tm_polygons("area", palette = "YlGn") +
  tm_text(text = "rgn",
          size = .7) +
  tm_layout(main.title = print(paste0(species_name,  " Suitable Area")),
            main.title.size = 1.2,
            main.title.position = "center") +
  tm_layout(legend.position = c("left", "bottom"))

  # MAP PERCENT BY REGION
  SpecPercMap <- tm_shape(SpeciesRegion) +
  tm_polygons("percent", palette = "YlGn") +
  tm_text(text = "rgn",
          size = .7) +
  tm_layout(main.title = print(paste0(species_name, " Suitable Percentage")),
            main.title.size = 1.2,
            main.title.position = "center") +
  tm_layout(legend.position = c("left", "bottom"))
  
  # DISPLAY MAPS
  tmap_arrange(SpecAreaMap, SpecPercMap)
  
}

Let’s try the function!

Species_Habitat("Bearica", 10, 40, -30, 0)
[1] "Bearica Suitable Area"
[1] "Bearica Suitable Percentage"

It appears there is very little suitable habitat for our creature the “bearica” along the coast. The water temperature is too cold! This creature will probably be better suited along the equator with warmer waters, hopefully we can find it there!

Footnotes

  1. GEBCO Compilation Group (2022) GEBCO_2022 Grid (doi:10.5285/e0f0bb80-ab44-2739-e053-6c86abc0289c).↩︎

Citation

BibTeX citation:
@online{dale2022,
  author = {Dale, Erica},
  title = {Geospatial {Blog} {Post}},
  date = {2022-12-02},
  url = {http://ericamarie9016.github.io/2022-12-02-geospatial},
  langid = {en}
}
For attribution, please cite this work as:
Dale, Erica. 2022. “Geospatial Blog Post.” December 2, 2022. http://ericamarie9016.github.io/2022-12-02-geospatial.