---
title: "Spatial Queries"
date: "`r format(Sys.time(), '%d %B, %Y')`"
output:
rmarkdown::html_vignette
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:
https://pfrater.github.io/arcpullr/
```{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}
#
```
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:
-
`get_layer_by_line`
-
`get_layer_by_point`
-
`get_layer_by_polygon`
-
`get_layer_by_multipoint`
-
`get_layer_by_envelope`
## URL's for examples
Example Source Data
```{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'"
)
```
\
## 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)
```
### 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()
```
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")
```