--- title: "Spatial Queries - Sample Park Vegetation with Random Points" output: html_notebook: toc: yes toc_float: yes --- ```{css echo = FALSE} h1,h2 {font-weight:bold;} ``` In this Notebook we'll use geoprocessing functions from `sf` to: - generate 500 randomly distributed sample points within YNP - identify the vegetation class for sample point and save it in the attribute table - find all sample points within 2km of each campground, and compute their distances to the campground ## Setup Load the packages we'll need and set tmap mode to 'plot': ```{r chunk01, message = FALSE} library(sf) library(tibble) library(tmap) tmap_mode("plot") ``` Load `dplyr` and set name conflict preferences: ```{r chunk02, message = FALSE} library(dplyr) ## Load the conflicted package library(conflicted) # Set conflict preferences conflict_prefer("filter", "dplyr", quiet = TRUE) conflict_prefer("count", "dplyr", quiet = TRUE) conflict_prefer("select", "dplyr", quiet = TRUE) conflict_prefer("arrange", "dplyr", quiet = TRUE) ``` \ ## Import the Park Boundary First, we import the boundary: ```{r chunk03} ## Define a convenience variable for UTM Zone 11 epsg_utm11n_nad83 <- 26911 ## Import the YNP border yose_bnd_utm <- st_read(dsn="./data", layer="yose_boundary") %>% st_transform(epsg_utm11n_nad83) ``` \ ## Generate Random Points Next, generate uniformly distributed random numbers between the x and y ranges of the park's extent (i.e., bounding box). We'll generate more points than we actually needed knowing that some of them will fall outside the park boundary. ```{r chunk04} n <- 500 ## Get the Extent of the park boundary yose_ext <- st_bbox(yose_bnd_utm) yose_ext ## Generate uniformly distributed random numbers xs <- runif(n * 3, min = yose_ext[1], max = yose_ext[3]) ys <- runif(n * 3, min = yose_ext[2], max = yose_ext[4]) ``` \ Create a sf object from the random points ```{r chunk05} rand_pts_sf <- st_as_sf(data.frame(lon = xs, lat = ys, ptid = 1:length(xs)), coords = c("lon", "lat"), crs = st_crs(yose_bnd_utm)) ## Plot to verify tm_shape(rand_pts_sf) + tm_dots(col = "dimgray") + tm_shape(yose_bnd_utm) + tm_borders(col = "red", lwd = 2) ``` Next, test which points fall within the park boundary, and save those to a new object. ```{r chunk06} ## Test which points are within the YNP boundary in_yose_mat <- st_within(rand_pts_sf, yose_bnd_utm, sparse = FALSE) head(in_yose_mat) ## Save those in the park to a new sf object pts_in_park_sf <- rand_pts_sf %>% filter(in_yose_mat[,1]) nrow(pts_in_park_sf) ``` \ Plot the results: ```{r chunk07} tm_shape(yose_bnd_utm) + tm_borders(col="palegreen4", lwd = 3) + tm_shape(pts_in_park_sf) + tm_dots(col="dimgray") ``` \ Lastly, all we need is 500 points, so take a random sample: ```{r chunk08} ## Take a random sample of the points yose_sample_pts_utm <- pts_in_park_sf %>% sample_n(n) ## Plot tm_shape(yose_bnd_utm) + tm_borders(col="palegreen4", lwd = 4) + tm_shape(yose_sample_pts_utm) + tm_dots(col="dimgray") ``` \ # Find and Record the Vegetation Class for Each Sample Point Next we'll use a **spatial join** to do two things at once: 1) find the vegetation type for each sample point, and ii) save it in the attribute table. First import the vegetation class layer and the legend: ```{r chunk09} yose_veg37_utm <- st_read(dsn="./data", layer="veg37") %>% st_transform(epsg_utm11n_nad83) %>% select(DOMINANT, ALLIANCE, RIM_CODE) ## Import the legend for the vegetation layer (saved separately) yose_veg37_legend_df <- foreign::read.dbf("./data/veg37_alliances.dbf") ## Join the legend to the sf data frame on the 'ALLIANCE' column yose_veg37leg_utm <- yose_veg37_utm %>% left_join(yose_veg37_legend_df, by = "ALLIANCE") yose_veg37leg_utm %>% slice(1:10) %>% as_tibble() ``` \ Plot the vegetation layer: ```{r chunk10} tmap_options(max.categories = 31) tm_shape(yose_bnd_utm) + tm_borders(col="palegreen4", lwd = 4) + tm_shape(yose_veg37leg_utm) + tm_fill("ALLIANCE") + tm_layout(legend.outside = TRUE) ``` Now we're ready to spatially join the sample points to the vegetation layer using `st_join()`. This will add the columns from the vegetation layer to the attribute table of the sample points. ```{r chunk11} yose_sample_pts_veg_utm <- yose_sample_pts_utm %>% st_join(yose_veg37leg_utm) yose_sample_pts_veg_utm %>% slice(1:10) %>% as_tibble() ``` \ ## CHALLENGE: Plot the sample points Plot the sample points according to their vegetation class. [Answer](https://bit.ly/3u8JvUM) ```{r chunk12} ## your answer here ``` \ ## Find All Sample Points within 2km of Each Campground First we import the campgrounds: ```{r chunk13} ## Import the campground yose_campgrounds_utm <- st_read("./data", layer="yose_poi") %>% st_transform(epsg_utm11n_nad83) %>% filter(POITYPE == 'Campground') %>% select(POINAME) yose_campgrounds_utm ``` \ To identify the sample points within 2km of each campground, we could construct a buffer around each campground and take the intersection. But there's an easier way - we can do the spatial query directly using `st_is_within_distance()`. The number of sample points near each campground will vary. Hence we'll save the results in a list (`sparse = TRUE`): ```{r chunk14} samp_pts_near_each_campgrnd_lst <- yose_campgrounds_utm %>% st_is_within_distance(yose_sample_pts_utm, 2000, sparse = TRUE) samp_pts_near_each_campgrnd_lst ``` \ ## Compute Distances Between each Campground and its Nearby Sample Points Finally, We'll compute the distance between each campground and the sample points that lie within 2km. To do this, we'll loop through the campgrounds, and for each one we'll use `st_distance()` to find the distance between the campground and the nearby points (whose row numbers are saved in `samp_pts_near_each_campgrnd_lst`) . To complile all the results, we'll append the distances to a data frame each iteration of the loop. ```{r chunk15} ## Initialize campgrnd_samppts_dist_df to NULL ## (we'll use it below to compile and save the results of a loop) campgrnd_samppts_dist_df <- NULL for (i in 1:nrow(yose_campgrounds_utm)) { if (length(samp_pts_near_each_campgrnd_lst[[i]]) > 0) { dist_mat <- st_distance(x = yose_campgrounds_utm %>% slice(i), y = yose_sample_pts_utm %>% slice(samp_pts_near_each_campgrnd_lst[[i]])) ## Create a data frame with the campground rown number, the sample point row numbers, and the distances dist2samppts_df <- data.frame(campgrnd_idx = i, samp_pt_idx = samp_pts_near_each_campgrnd_lst[[i]], dist = as.numeric(dist_mat[1,])) ## Add these rows to campgrnd_samppts_dist_df campgrnd_samppts_dist_df <- campgrnd_samppts_dist_df %>% bind_rows(dist2samppts_df) } } ## View results campgrnd_samppts_dist_df ``` ## End Congratulations, you've completed another Notebook! To view your Notebook as HTML, save it (again), then click the 'Preview' button in the RStudio toolbar.