--- title: "Spatial Queries" date: "`r format(Sys.time(), '%d %B, %Y')`" output: html_document: theme: version: 5 vignette: > %\VignetteIndexEntry{Spatial Queries} %\VignetteEncoding{UTF-8} %\VignetteEngine{knitr::rmarkdown} editor_options: markdown: wrap: 72 --- --- title: "Spatial Queries" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Introduction to arcpullr} %\VignetteEncoding{UTF-8} %\VignetteEngine{knitr::rmarkdown} editor_options: markdown: wrap: 72 --- To see more complete package documentation check out: <a href="https://pfrater.github.io/arcpullr/"> https://pfrater.github.io/arcpullr/</a> <hr> ```{r, include = FALSE} knitr::opts_chunk$set( collapse = TRUE, comment = "#>", warning = FALSE, message = FALSE ) library(arcpullr) library(sf) sf::sf_use_s2(FALSE) ``` ```{r, echo = FALSE} #<img src='../man/figures/logo.png' width="160" height="180" style="border: none; float: right"/> ``` ArcGIS REST API's may be spatially queried using the get_layer_by\_\* family of functions. These functions require a spatial object of class `sf` (*i.e.* of the R package [sf: Simple Features for R](https://r-spatial.github.io/sf/)) and a [Spatial Relationship](https://help.arcgis.com/en/webapi/wpf/apiref/ESRI.ArcGIS.Client~ESRI.ArcGIS.Client.Tasks.SpatialRelationship.html) to be passed to the geometry and `sp_rel` arguments respectively. The package contains five functions that can be used to perform spatial queries: <ul> <li> `get_layer_by_line` <li> `get_layer_by_point` <li> `get_layer_by_polygon` <li> `get_layer_by_multipoint` <li> `get_layer_by_envelope` </u > ## URL's for examples <a class="btn btn-primary" data-bs-toggle="collapse"href="#collapseExample" role="button" aria-expanded="false" aria-controls="collapseExample"> Example Source Data </a> <div class="collapse" id="collapseExample"> <div class="card card-body"> ```{r url_setup, eval = FALSE} #WDNR Server server <- "https://dnrmaps.wi.gov/arcgis/rest/services/" server2 <- "https://dnrmaps.wi.gov/arcgis2/rest/services/" #River URL layer <- "TS_AGOL_STAGING_SERVICES/EN_AGOL_STAGING_SurfaceWater_WTM/MapServer/2" river_url <- paste0(server2,layer) #Country URL layer <- "DW_Map_Dynamic/EN_Basic_Basemap_WTM_Ext_Dynamic_L16/MapServer/3" county_url <- paste0(server,layer) #Trout URL layer <- "FM_Trout/FM_TROUT_HAB_SITES_WTM_Ext/MapServer/0" trout_url <- paste0(server,layer) #Watershed URL layer <- "WT_SWDV/WT_Federal_Hydrologic_Units_WTM_Ext/MapServer/0" watershed_url <- paste0(server,layer) #get layers for queries mke_river <- get_spatial_layer( river_url, where = "RIVER_SYS_NAME = 'Milwaukee River'" ) trout_hab_project_pts <- get_spatial_layer( trout_url, where = "WATERBODYNAMECOMBINED = 'Sugar Creek' and FISCALYEAR = 2017" ) trout_hab_project_pt <- trout_hab_project_pts[1, ] # get watershed layer for Cook Creek cook_creek_ws <- get_spatial_layer( watershed_url, where = "HUC12_NAME = 'Cook Creek'" ) ``` </div> </div> <br> ## get_layer_by_line The `get_layer_by_line` function uses A LINSESTRING or MULTILINESTRING sf object to query an ArcGIS REST API. The below example uses a MULTILINESTRING sf object of the Milwaukee River to query the Wisconsin County polygon layer. ```{r pull_by_line, eval = FALSE, echo = FALSE} mke_river_counties <- get_layer_by_line(url = county_url, geometry = mke_river) ``` ```{r, echo = FALSE} mke_river_counties <- sf::st_filter( wis_counties, mke_river, .pred = sf::st_intersects ) ``` ```{r show_by_line, ref.label=c("pull_by_line", "plot_by_line"), eval = FALSE} ``` ```{r plot_by_line, echo = FALSE} plot_layer(mke_river, outline_poly = mke_river_counties) ``` ## get_layer_by_point The `get_layer_by_line` function uses a POINT sf object to query an ArcGIS REST API. The below example shows how this can be used to return which rivers intersect with a trout habitat project on Sugar Creek in southeast Wisconsin. ```{r pull_by_point, eval = FALSE, echo = FALSE} trout_stream <- get_layer_by_point(url = river_url, geometry = trout_hab_project_pt) ``` ```{r, echo = FALSE, mesage = FALSE} trout_stream <- sf::st_filter( sugar_creek, sf::st_buffer(trout_hab_project_pt, 0.0001), .pred = sf::st_intersects ) ``` ```{r show_by_point, ref.label=c("pull_by_point", "plot_by_point"), eval = FALSE} ``` ```{r plot_by_point, echo = FALSE} plot_layer(trout_stream) + ggplot2::geom_sf(data = trout_hab_project_pt, color = "red", size = 2) ``` `get_layer_by_point` can also handle multipoint objects. This example shows the same stream as above with a single point, but now with multiple restoration points. ```{r pull_by_multipoint, eval = FALSE, echo = FALSE} restored_streams <- get_layer_by_point(url = river_url, geometry = trout_hab_project_pts) ``` ```{r show_by_multipoint, ref.label=c("pull_by_multipoint", "plot_by_multipoint"), eval = FALSE} ``` ```{r, echo = FALSE} restored_streams <- sugar_creek ``` ```{r plot_by_multipoint, fig.height = 7, fig.width = 7, echo = FALSE} plot_layer(restored_streams) + ggplot2::geom_sf(data = trout_hab_project_pts, color = "blue") ``` ## get_layer_by_polygon The `get_layer_by_line` function uses a POLYGON sf object to query an ArcGIS REST API. The below examples shows how this can be used to find what rivers are within a particular watershed. ```{r, pull_by_poly, eval = FALSE, echo = FALSE} cook_creek_streams <- `get_layer_by_poly(river_url, cook_creek_ws) ``` ```{r show_by_poly, ref.label=c("pull_by_poly", "plot_by_poly"), eval = FALSE} ``` ```{r plot_by_poly, echo = FALSE} plot_layer(cook_creek_streams, cook_creek_ws) ``` ## get_layer_by_envelope The `get_layer_by_envelope` function accepts any sf object to query an ArcGIS REST API using the sf objects bounding box. The below example shows how this is used to query WI's Rivers ArcGIS REST API using a sf POLYGON object of a watershed for a small stream. Note how the results compare to when this same object is queried using the `get_layer_by_poly` function. ```{r, pull_by_env, eval = FALSE, echo = FALSE} cook_creek_env <- get_layer_by_envelope(river_url, cook_creek_ws) ``` ```{r show_by_env, ref.label=c("pull_by_env", "plot_by_env"), eval = FALSE} ``` ```{r plot_by_env, echo = FALSE} # example of the envelope to visualize how it spatially queries example_env <- sf::st_as_sfc(sf::st_bbox(cook_creek_ws)) plot_layer(cook_creek_env, cook_creek_ws) + ggplot2::geom_sf(data = example_env, fill = NA) ``` \ \ ## Combining Spatial and SQL Queries Spatial queries can be combined with SQL statements to further refine queries. ## Spatial Relationship The `sp_rel` argument can be used to define the spatial relationship between the two feature classes involved within a spatial query. The default spatial relationships for the `get_layer_by_poly` function is "contains". All other functions default to "intersects". ```{r pull_sp_rel_contains, eval = FALSE, echo = FALSE} example_poly <- sf_polygon( c(-90.62, 43.76), c(-90.62, 43.77), c(-90.61, 43.77), c(-90.61, 43.76), c(-90.62, 43.76) ) poly_streams_contains <- get_layer_by_poly(river_url, example_poly) ``` ```{r show_sp_rel_contains, ref.label=c("pull_sp_rel_contains", "plot_sp_rel_contains"), eval = FALSE} ``` ```{r plot_sp_rel_contains, echo = FALSE} plot_layer(poly_streams_contains, outline_poly = example_poly) ``` Using "crosses" returns different records compared to the above example (i.e. this returns records when they cross the polygon border). ```{r pull_sp_rel_crosses, eval = FALSE, echo = FALSE} poly_streams_crosses <- get_layer_by_poly(river_url, example_poly, sp_rel = "crosses") ``` ```{r show_sp_rel_crosses, ref.label=c("pull_sp_rel_crosses", "plot_sp_rel_crosses"), eval = FALSE} ``` ```{r plot_sp_rel_crosses, echo = FALSE} plot_layer(poly_streams_crosses, outline_poly = example_poly) ``` <br> ### Lookup Tables The `sp_rel_lookup` data.frame explains the various types of spatial relationships available through ArcGIS REST APIs. ```{r, echo = FALSE} sp_rel_lookup %>% DT::datatable() ``` <br> The `sp_rel_valid` data.frame shows which spatial relationships are valid with different geometry types being queried and used to do spatial queries. ```{r, echo = FALSE} sp_rel_valid %>% DT::datatable() ``` ### The valid_sp_rel Function The `valid_sp_rel` function can be used to to see which spatial relation types are applicable to different geometries. ```{r, echo = TRUE} valid_sp_rel("line","line") ```