Generate random sample points for RSF
Overview:
In this module we use our prepared spatial data to generate the input data files to conduct a resource selection function analysis. This includes:
- generate a set of “background points” from our study area.
- extract spatial information for presence and background point locations
- export as table for future use
- generate summary statistics
Background: Resource Selection Functions
Now that we have a cleaned and standardized data set for the Scott Herd Caribou’s we can prepare the data for further analysis.
Resource Selection Functions are a common method used to assess what are the driving patterns of animal habitat preference. This process uses information or covariates (i.e. landscape attribute features) for locations where animals are present and compares them to all possible locations. In this way we can gather information on what conditions (i.e. landscape, aspect, distance from road, etc) characterize higher habitat use and selection for.
In addition to preparing data for future analysis, understanding the association with telemetry point locations and landscape information can provide meaningful summary statistics, i.e. proportion of locations within a specific BEC zone.
1. Generate background location points.
We will generate a simple set of “background points” based on the geographic distribution of the study area. Note you can limit the area in which these points are drawn from using more sophisticated methods, such as restricting points within a kernal density or home range estimate.
Lets start by reading in the libraries we will use.
Next, read in our point locations and rater template.
# read in the aoi template
template <- rast("clean_data/template.tif")
# read in points
bou_pts <- st_read("clean_data/scott_herd_subset.gpkg")
Reading layer `scott_herd_subset' from data source
`/Users/andy/dev/r-telemetry-workshop-nov-2023/clean_data/scott_herd_subset.gpkg'
using driver `GPKG'
Simple feature collection with 2906 features and 16 fields
Geometry type: POINT
Dimension: XY
Bounding box: xmin: 1145474 ymin: 1190327 xmax: 1185596 ymax: 1227451
Projected CRS: NAD83 / BC Albers
# Lets keep only the important columns and add a "presence/background" column.
bou_pts <- bou_pts |>
dplyr::select(animal.id, jdate)|>
mutate(pres_bkg = 1)
We can use the spatSample function to generate random points for our given study area. This function has many more options which can be reviewed by using : ?spatSample in the console.
Lets generate a set of points the same length as our “presence” locations using a “random” method.
# Generate random points for RSF use areas.
set.seed(123)
avail_points <- spatSample(template, size = 2906, as.points = TRUE, na.rm = TRUE, method = "random")
avail_points <- st_as_sf(avail_points)
# lets rename the column to make it clear these are background points
avail_points <- avail_points |>
rename("pres_bkg" = aoi )
We can do a quick review of the points using mapview
mapview(avail_points) +
mapview(bou_pts, color = "red", cex= 3)
We can now combine our caribou locations and “background” locations into a single data set. We will retain the spatial information to allow us to easily extract the values in the corresponding raster stack
head(avail_points)
Simple feature collection with 6 features and 1 field
Geometry type: POINT
Dimension: XY
Bounding box: xmin: 1155662 ymin: 1195888 xmax: 1185438 ymax: 1225412
Projected CRS: NAD83 / BC Albers
pres_bkg geometry
1 0 POINT (1155662 1195888)
2 0 POINT (1185438 1225412)
3 0 POINT (1166262 1200912)
4 0 POINT (1179562 1198038)
5 0 POINT (1179038 1219712)
6 0 POINT (1162338 1221188)
head(bou_pts)
Simple feature collection with 6 features and 3 fields
Geometry type: POINT
Dimension: XY
Bounding box: xmin: 1164599 ymin: 1211860 xmax: 1165761 ymax: 1213029
Projected CRS: NAD83 / BC Albers
animal.id jdate geom pres_bkg
1 SC_car168 15789 POINT (1164855 1211860) 1
2 SC_car168 15790 POINT (1164599 1211888) 1
3 SC_car168 15791 POINT (1165702 1212694) 1
4 SC_car168 15792 POINT (1165754 1212486) 1
5 SC_car168 15793 POINT (1165668 1213029) 1
6 SC_car168 15794 POINT (1165761 1212577) 1
allpts <- bind_rows(bou_pts, avail_points)
head(allpts)
Simple feature collection with 6 features and 3 fields
Active geometry column: geom
Geometry type: POINT
Dimension: XY
Bounding box: xmin: 1164599 ymin: 1211860 xmax: 1165761 ymax: 1213029
Projected CRS: NAD83 / BC Albers
animal.id jdate pres_bkg geom geometry
1 SC_car168 15789 1 POINT (1164855 1211860) POINT EMPTY
2 SC_car168 15790 1 POINT (1164599 1211888) POINT EMPTY
3 SC_car168 15791 1 POINT (1165702 1212694) POINT EMPTY
4 SC_car168 15792 1 POINT (1165754 1212486) POINT EMPTY
5 SC_car168 15793 1 POINT (1165668 1213029) POINT EMPTY
6 SC_car168 15794 1 POINT (1165761 1212577) POINT EMPTY
# hmmmmm...........that looks weird what happened?
# note we have slightly different column headers "geom" vs "geometry"
st_geometry(avail_points) = "geom"
allpts <- bind_rows(bou_pts, avail_points)
Extract point values
Next, we can read in our prepared raster stack as an .rds object. We can now use the extract function from the terra package to extract information for all layers in the raster stack for each of our points.
# read in the raster stack
rstack <- readRDS("clean_data/covars.RDS")
# extract all values in the raster stack for each location in the bou_pts file.
atts <- terra::extract(rstack, allpts)
head(atts)
ID MAP_LABEL conif age_class cc_class HARVEST_YEAR WATERBODY_TYPE
1 1 ESSFwc3 <NA> NA NA NA <NA>
2 2 ESSFwc3 TC 9 5 NA <NA>
3 3 ESSFwcp TC 8 3 NA <NA>
4 4 ESSFwcp TC 8 3 NA <NA>
5 5 ESSFwcp <NA> 8 1 NA <NA>
6 6 ESSFwcp TC 8 3 NA <NA>
STREAM_ORDER ROAD_SURFACE layer focal_sum elevation slope aspect
1 NA <NA> 1.5773045 NA 1411.423 14.082280 2.045941
2 NA <NA> 1.6258846 NA 1392.466 13.354155 320.360718
3 NA <NA> 0.9053469 NA 1601.210 8.430601 138.105759
4 NA <NA> 1.0990066 NA 1575.208 5.037906 95.403023
5 NA <NA> 0.6147004 NA 1634.010 9.453105 94.128174
6 NA <NA> 1.0157410 NA 1583.300 6.978374 175.275101
TRI
1 4.809753
2 4.420059
3 2.718521
4 1.784927
5 3.197327
6 2.330856
# remove unused columns
# Could show how to use st_write() and explain the cbind(st_coordinates()) bit
bou_full_pts <- cbind(allpts, atts) |>
select(-ID)
Export as geo package or as csv table.
3. Summarise attributed point data
We can use our attributed point data to provide valuable summaries of landscape feature. Lets looks at aspect and BEC zones.
# Aspect
ggplot(bou_table, aes(aspect)) +
geom_histogram(binwidth = 20) +
facet_wrap(~pres_bkg)
Warning: Removed 8 rows containing non-finite values (`stat_bin()`).
# BEC zones
ggplot(bou_table, aes(MAP_LABEL, group = pres_bkg, fill = factor(pres_bkg))) +
geom_bar(position = "dodge", show.legend = TRUE)
Your turn
- Generate a second set of background points used a method other than “random”.
avail_pts_regular <- spatSample(template, size = 50, as.points = TRUE, na.rm = TRUE, method = "regular")
mapview(avail_pts_regular)
- Use the bou_table we created above to explore other landscape patters. Build a ggplot to show the differences between the presence and background points.
# Here are a few example using elevation and distance to water.
# elevation
ggplot(bou_table, aes(elevation)) +
geom_histogram(binwidth = 20) +
facet_wrap(~pres_bkg)
Warning: Removed 2 rows containing non-finite values (`stat_bin()`).
# distance to water (layer)
ggplot(bou_table, aes(layer)) +
geom_histogram() +
facet_wrap(~pres_bkg)
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
Warning: Removed 2 rows containing non-finite values (`stat_bin()`).