The WSEP Tier 2 field sampling package uses a probabilistic sample design to select sampling sites within a watershed. Each of the three components (fish passage, sediment delivery, and riparian/stream channel) use a different sample design; however, they share underlying spatial data layers. These spatial layers should be compiled and processed early on in any watershed assessment project to help inform reconnaissance and planning. The WSEP Tier 2 R-package (wsep.t2) can be used to help process and assemble the spatial data needed to generate sampling sites for a watershed of interest.
The easiest way to install the wsep.t2
package is from within the RStudio IDE using remotes::install_github()
. At this time the package has not been published to CRAN so the default install.packages()
will not work. Instead, use remotes [or devtools] to install the package directly from GitHub:
# You may need to install remotes
library(remotes)
# Choose all if promp to update packages
remotes::install_github("essatech/wsep.t2")
library(wsep.t2)
An example dataset is included in the wsep.t2 package for the Tsolum River watershed in Courtenay British Columbia, but the model can be run on any watershed of interest. The following section provides an example for the Tsolum River. It is expected that users will apply the tool to their own watershed of interest; in order to run the model on your own, you will need the following geospatial files:
# Run with sample dataset
library(wsep.t2)
data(TsolumStreams)
strm <- TsolumStreams
data(TsolumRoads)
roads <- TsolumRoads
# Import your own data here (update directories and uncomment lines)
# library(sf)
# strm <- st_read(dsn = "my_watershed.gdb", layer = "my_streams")
# roads <- st_read(dsn = "my_watershed.gdb", layer = "my_roads")
Before we can generate our sampling sites across a watershed, we first need to refine the stream network sampling frame to fit within the bounds of what is appropriate for sampling (remove micro-drainages that are not relevant for this sampling program; remove lakes and wetlands; remove alpine stream reaches). These pre-processing steps ensure that the field sampling program will be successful and can reduce the frequency of any last-minute adjustments in the field:
# Load the WSEP Tier 2 R-package
library(wsep.t2)
# Will must re-project our data so that x,y are cartesian coordinates with units of meters
strm <- utm_projection(data = strm)
# Then constrain the sampling frame to remove 1st order tribs less than 500 m in length and clip the upper 200 m off of other tributaries. see ?constrain_streams for details
c_ctrm <- constrain_streams(strm = strm,
length_remove = 500,
length_trim = 200)
# Remove lakes and other lotic reaches.
# Consider removing any other lakes manually or through a simple filter
c_strm <- remove_lentic_bcfwa(strm = c_ctrm,
EDGE_TYPE = "EDGE_TYPE")
# Remove alpine areas
# In this example set to all segments over 800 m (adjust this value for your region)
ca_strm <- remove_alpine_bcfwa(strm = c_strm,
elevation_threshold = 800)
# (Optional) Visualize original (raw) and constrained streams.
# Finalize and adjust with any additional filters
strm1_plot <- sf::st_zm(strm)
strm2_plot <- sf::st_zm(ca_strm)
plot(sf::st_geometry(strm1_plot), col = "lightgrey",
main = "Constrained Streams")
plot(sf::st_geometry(strm2_plot), col = "blue", add = TRUE)
legend("topright", c("original", "adjusted"),
col = c("lightgrey", "blue"), lwd = 1)
Create a new field called strata
that divides the remaining stream network based on stream order: stratum_1
< 3rd order streams and stratum_2
≥ 3rd order streams.
ca_strm$strata <- NA
ca_strm$strata <- ifelse(ca_strm$STREAM_ORDER < 3, "stratum_1", ca_strm$strata)
ca_strm$strata <- ifelse(ca_strm$STREAM_ORDER >= 3, "stratum_2", ca_strm$strata)
plot(ca_strm["strata"], main = "Sampling Stratum")
A list of stream-road crossings in the watershed can be generated by taking the intersection of the stream layer and the road layer. We can then use the new stream crossing layer as our first sampling frame to select sites to assess connectivity (fish passage) and sediment inputs.
The grouped_random_sample() function needs the intersection dataframe (crossings), the divided stream order data from the previous step (“strata”), a sample size (n), and a column name. It will then generate a random sample from the list of stream crossings for each of the two strata (stratum 1: < 3rd order vs. stratum 2: ≥ 3rd order). If the specified sample size is greater than the number of stream crossings in a stratum, then only the limited number of available crossings will be returned. This function returns a spatial dataframe containing site id, strata information, UTM coordinates, type_a designation, and original stream order value.
# Ensure road spatial projection matches that of the stream layer
# and that units are in meters (cartesian coordinates)
roads <- utm_projection(data = roads)
# Define crossings as the intersection of streams and roads
crossings <- sf::st_intersection(ca_strm, roads)
# Take a random sample of crossings by strata
site_type_a <- grouped_random_sample(data = crossings,
group_name = "strata",
n = 40,
stream_order = "STREAM_ORDER"
)
# -------------------------------------------
# (Optional) visualize
strm_plot <- sf::st_zm(ca_strm)
road_plot <- sf::st_zm(roads)
plot(sf::st_geometry(strm_plot), col = "darkblue", main = "Site Type A (stream crossing)")
plot(sf::st_geometry(road_plot), add = TRUE, col = "burlywood")
plot(sf::st_geometry(site_type_a), add = TRUE, col = ifelse(site_type_a$strata == "stratum_1", "black", "red"), pch = 19)
legend("topright", c("roads", "streams", "stratum 1", "stratum 2"), col = c("burlywood", "darkblue", "black", "red"), lwd = c(1, 1, NA, NA), pch = c(NA, NA, 19, 19))
It may also be of interest to sample streams that are in close proximity to roads (Site Type B). The road_proximity_sample() function takes the streams and roads data and outputs a list containing both points and line segments. These are then pulled into site_type_b and line_segments appropriately. The road_proximity_sample function may take a moment to run but will update you with key processing milestones as it works through the following steps:
Sample streams in close proximity to roads.
Note that road_proximity_sample() returns a list object with the sample points and the streamline segments.
# Run the function for road proximity samples
?road_proximity_sample
type_b <- road_proximity_sample(
n = 60,
strm = ca_strm,
roads = roads,
buffer_s1_m = 20,
buffer_s2_m = 40,
buffer_crossings_m = 50, # originally 100m
small_strm_segment_m = 30, # originally 50m
stream_order = "STREAM_ORDER"
)
# Distances adjusted to provide better fit for this urban watershed
# Get the points object
names(type_b)
site_type_b <- type_b$points
line_segments <- type_b$line_segments
table(site_type_b$strata)
# # Preview
# library(mapview)
# mapview(list(site_type_b, line_segments))
# -------------------------------------------
# (Optional) visualize
strm_plot <- sf::st_zm(ca_strm)
road_plot <- sf::st_zm(roads)
plot(sf::st_geometry(strm_plot), col = "darkblue", main = "Site Type B (road proximity)")
plot(sf::st_geometry(road_plot), add = TRUE, col = "burlywood")
plot(sf::st_geometry(site_type_b), add = TRUE, col = ifelse(site_type_b$strata == "stratum_1", "black", "red"), pch = 19, cex = 0.5)
plot(sf::st_geometry(line_segments), add = TRUE, col = ifelse(site_type_b$strata == "stratum_1", "black", "red"), lwd = 2.5)
legend("topright", c("roads", "streams", "stratum 1", "stratum 2"), col = c("burlywood", "darkblue", "black", "red"), lwd = c(1, 1, 2, 2), pch = c(NA, NA, 19, 19))
Lastly, we can sample sites for the riparian component (Site Type C). Riparian sites are selected from each stratum using a spatially balanced design (GRTS, Stevens 2002) from the stream network (constrained version). We have to run the code with each stratum separately but will bind these together to create our site_type_c spatial dataframe. This function also takes a sample size (n), the stream dataset specified by stratum.
site_type_c_1 <- strm_grts(
n = 40,
strm = ca_strm[which(ca_strm$strata == "stratum_1"), ],
stream_order = 'STREAM_ORDER'
)
site_type_c_2 <- strm_grts(
n = 40,
strm = ca_strm[which(ca_strm$strata == "stratum_2"), ],
stream_order = 'STREAM_ORDER'
)
site_type_c <- rbind(site_type_c_1, site_type_c_2)
table(site_type_c$strata)
# -------------------------------------------
# (Optional) visualize
plot(sf::st_geometry(strm_plot), col = "darkblue", main = "Site Type C (riparian)")
plot(sf::st_geometry(road_plot), add = TRUE, col = "burlywood")
plot(sf::st_geometry(site_type_c), add = TRUE, col = ifelse(site_type_b$strata == "stratum_1", "black", "red"), pch = 19)
legend("topright", c("roads", "streams", "stratum 1", "stratum 2"), col = c("burlywood", "darkblue", "black", "red"), lwd = c(1, 1, NA, NA), pch = c(NA, NA, 19, 19))
# (Optional) Visualize with Mapview
# install.packages("mapview")
# library(mapview)
# road_plot$col <- 1
# mapview(list(strm_plot["strata"], road_plot, site_type_a["strata"], site_type_b["strata"], site_type_c["strata"]))
Finally, when all the sampling frames (above) have been generated it is possible to export the sample sites in neatly formatted csv, shp and kml file formats for review and field planning. Use the export_sites()
function after defining a local export directory.
# Output directory
# Change this path for your computer!
output_dir <- "C:/Users/mbayly/Desktop/delete/my_sites"
export_sites(output_dir = output_dir,
site_type_a = site_type_a,
type_b = type_b,
site_type_c = site_type_c,
export_csv = TRUE,
export_shp = TRUE,
export_kml = TRUE)
(Optional) It also is convenient to produce a summary table of the entire sampling frame. This table summarizes the total length of streams by strata and the total count of stream crossings by strata.
# Generate a summary of the sampling frame by strata
# For stream lengths
df1 <- sample_frame_summary(
strm = strm,
constrained_strm = ca_strm,
roads = roads,
stream_order = "STREAM_ORDER",
summary_type = "stream_lengths")
print(df1)
# and stream crossings
df2 <- sample_frame_summary(
strm = strm,
constrained_strm = ca_strm,
roads = roads,
stream_order = "STREAM_ORDER",
summary_type = "stream_crossings")
print(df2)
Table 1: Stream length summary by strata and stream type | Strata | Unconstrained (m) | Constrained (m) | |-----------|---------------|-------------| | Stratum 1 | 326,267 | 230,664 | | stratum 2 | 102,614 | 94,700 | | Total | 428,881 | 325,364 |
Table 2: Stream-road crossing summary by strata and stream type | Strata | Unconstrained (#) | Constrained (#) | |-----------|---------------|-------------| | Stratum 1 | 436 | 314 | | Stratum 2 | 75 | 69 | | Total | 511 | 383 |
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.