Geoprocessing
  
Generally speaking, geoprocessing functions are functions that analyze or alter the geometry of the data.
We can generally divide geoprocessing into 3 categories:
Geoprocessing functions do their calculations with map units.
If area or distance measurements are involved, use projected data.
| Function | Computes | 
|---|---|
| st_area() | area of polygons | 
| st_length() | length / perimeter | 
| st_distance() | distance between features | 
Measurement functions from sf compute length and area in map units, but the units are saved as part of the result. This makes it easy to convert to using set_units() function from units package.
To get accurate results, we should project the boundary into UTM:
## 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)## Reading layer `yose_boundary' from data source `D:\Workshops\R-Spatial\rspatial_mod\outputs\rspatial_data\data' using driver `ESRI Shapefile'
## Simple feature collection with 1 feature and 11 fields
## Geometry type: POLYGON
## Dimension:     XY
## Bounding box:  xmin: -119.8864 ymin: 37.4947 xmax: -119.1964 ymax: 38.18515
## Geodetic CRS:  North_American_Datum_1983
Next, find the area:
## 3019918537 [m^2]
Large values of meters-squared can be hard to interpret. Let’s convert the area hectares and square miles using set_units() from the units package.
library(units)
## View in hectacres
set_units(yose_area, ha)
## View in square miles
set_units(yose_area, mi^2)## 301991.9 [ha]
## 1165.997 [mi^2]
Import the burned area layer from historical fires, and compute the area of each fires (see code below to import them). Which was the biggest fire?
[Hint] [Solution]
You can use which.max() to find the largest element of a vector.
## Compute the area of each polygon
fire_areas <- st_area(yose_firepolys_utm)
## Find the index of the largest value
i <- which.max(fire_areas)
## Take a look at the row
yose_firepolys_utm[i,]
## Computet the size in miles squared
units::set_units(fire_areas[i], mi^2)## Simple feature collection with 1 feature and 21 fields
## Geometry type: MULTIPOLYGON
## Dimension:     XY
## Bounding box:  xmin: 246268.6 ymin: 4182145 xmax: 264049.5 ymax: 4215598
## Projected CRS: NAD83 / UTM zone 11N
##          FIRE_ID LINK_ID SACS_ID FILE_ID NAME    ACRES YEAR TYPE CAUSE HECTARES AREA PERIMETER SEQ_NO DECADE  STARTDATE    OUTDATE X_COORD Y_COORD
## 4251 CA-YNP-0126    <NA>    <NA>    <NA>  Rim 78892.24 2013   WF    HC       NA   NA   1123267     NA   2010 2013-08-17 2013-10-18      NA      NA
##      ET_ID Shape_Length Shape_Area                          Shape
## 4251    NA     209505.2  318835601 MULTIPOLYGON (((247876.4 42...
## 123.1031 [mi^2]
⇒ the 2013 Rim fire was the largest!
Computing lengths and perimeters is done similarly with st_length().
First import the roads and inspect the attribute table.
## Import the roads
gdb_fn <- "./data/yose_roads.gdb"
yose_roads <- st_read(gdb_fn, "Yosemite_Roads")
glimpse(yose_roads)## Reading layer `Yosemite_Roads' from data source `D:\Workshops\R-Spatial\rspatial_mod\outputs\rspatial_data\data\yose_roads.gdb' using driver `OpenFileGDB'
## Simple feature collection with 823 features and 40 fields
## Geometry type: MULTILINESTRING
## Dimension:     XY
## Bounding box:  xmin: 234658.1 ymin: 4139484 xmax: 324852.6 ymax: 4250252
## Projected CRS: NAD83 / UTM zone 11N
## Rows: 823
## Columns: 41
## $ RDNAME         <chr> "Northside Drive", "Northside Drive", "Northside Drive", "Southside Drive", "Northside Drive", "Northside Drive", "Mirror ~
## $ RDALTNAME      <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA~
## $ MAPLABEL       <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA~
## $ RDSTATUS       <chr> "Existing", "Existing", "Existing", "Existing", "Existing", "Existing", "Existing", "Existing", "Existing", "Existing", "E~
## $ RDCLASS        <chr> "Primary", "Primary", "Primary", "Primary", "Primary", "Primary", "Secondary", "Primary", "Secondary", "Primary", "Primary~
## $ RDSURFACE      <chr> "Asphalt", "Asphalt", "Asphalt", "Asphalt", "Asphalt", "Asphalt", "Asphalt", "Asphalt", "Asphalt", "Asphalt", "Asphalt", "~
## $ RDONEWAY       <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA~
## $ RDLANES        <int> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA~
## $ RDHICLEAR      <chr> "No", "No", "No", "No", "No", "No", "No", "No", "No", "No", "No", "No", "No", "No", "No", "No", "No", "No", "No", "No", "N~
## $ RTENUMBER      <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA~
## $ SEASONAL       <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA~
## $ SEASDESC       <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA~
## $ RDMAINTAINER   <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA~
## $ ISEXTANT       <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA~
## $ PUBLICDISPLAY  <chr> "Public Map Display", "Public Map Display", "Public Map Display", "Public Map Display", "Public Map Display", "Public Map ~
## $ DATAACCESS     <chr> "Unrestricted", "Unrestricted", "Unrestricted", "Unrestricted", "Unrestricted", "Unrestricted", "Unrestricted", "Unrestric~
## $ UNITCODE       <chr> "YOSE", "YOSE", "YOSE", "YOSE", "YOSE", "YOSE", "YOSE", "YOSE", "YOSE", "YOSE", "YOSE", "YOSE", "YOSE", "YOSE", "YOSE", "Y~
## $ UNITNAME       <chr> "Yosemite National Park", "Yosemite National Park", "Yosemite National Park", "Yosemite National Park", "Yosemite National~
## $ GROUPCODE      <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA~
## $ GROUPNAME      <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA~
## $ REGIONCODE     <chr> "PWR", "PWR", "PWR", "PWR", "PWR", "PWR", "PWR", "PWR", "PWR", "PWR", "PWR", "PWR", "PWR", "PWR", "PWR", "PWR", "PWR", "PW~
## $ CREATEDATE     <dttm> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N~
## $ CREATEUSER     <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA~
## $ EDITDATE       <dttm> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N~
## $ EDITUSER       <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA~
## $ MAPMETHOD      <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA~
## $ MAPSOURCE      <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA~
## $ SOURCEDATE     <dttm> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N~
## $ XYACCURACY     <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA~
## $ ROUTEID        <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA~
## $ FACLOCID       <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA~
## $ FACASSETID     <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA~
## $ FEATUREID      <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA~
## $ GEOMETRYID     <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA~
## $ NOTES          <chr> "In Park = Y, Fire Road = No", "In Park = Y, Fire Road = No", "In Park = Y, Fire Road = No", "In Park = Y, Fire Road = No"~
## $ YOSE_Surface   <chr> "Paved", "Paved", "Paved", "Paved", "Paved", "Paved", "Paved", "Paved", "Paved", "Paved", "Paved", "Paved", "Paved", "Pave~
## $ YOSE_FIRE_ROAD <chr> "No", "No", "No", "No", "No", "No", "No", "No", "No", "No", "No", "No", "No", "No", "No", "No", "No", "No", "No", "No", "N~
## $ YOSE_INPARK    <chr> "Yes", "Yes", "Yes", "Yes", "Yes", "Yes", "Yes", "Yes", "Yes", "Yes", "Yes", "Yes", "Yes", "Yes", "Yes", "Yes", "Yes", "Ye~
## $ YOSE_Type      <chr> "Primary Road", "Primary Road", "Primary Road", "Primary Road", "Primary Road", "Primary Road", "Secondary Road", "Primary~
## $ Shape_Length   <dbl> 493.53512, 67.90781, 33.35863, 258.00073, 117.94169, 167.51558, 71.82903, 330.03084, 73.37367, 80.21527, 173.71187, 114.03~
## $ Shape          <MULTILINESTRING [m]> MULTILINESTRING ((270865.2 ..., MULTILINESTRING ((270821.5 ..., MULTILINESTRING ((270797.2 ..., MULTILINES~
Next, write an expression that returns the total length of all primary roads.
## Load the units package so we can convert meters to miles
library(units)
## Use a mix of functions from dplyr, sf, base R, and units 
yose_roads %>% 
  dplyr::filter(RDCLASS == "Primary") %>%   ## grab just the primary roads
  sf::st_length() %>%                       ## compute length of each segment (as numeric vector)
  sum() %>%                                 ## take the sum
  units::set_units("miles")                 ## convert to miles## 375.5348 [miles]
How many miles of Secondary roads are there?
[Solution]
To add geometric measurements to the attribute table, use dplyr::mutate():
Calculating distances between features and finding nearest neighbors will be covered in the Spatial Queries session.
nb_geoproc_01_scgis21.Rmd
Geoprocessing measurement functions
sf has a number of functions that will create new geometries based on existing ones:
| Function | Computes | 
|---|---|
| st_buffer() | compute a buffer around this geometry/each geometry | 
| st_centroid() | gives centroid of geometry | 
| st_convex_hull() | creates convex hull of set of points | 
| st_line_merge() | merges lines | 
| st_segmentize() | adds points to straight lines | 
| st_voronoi() | creates voronoi tesselation | 
| st_triangulate() | triangulates set of points (not constrained) | 
| st_polygonize() | creates polygon from lines that form a closed ring | 
| st_simplify() | simplifies lines by removing vertices | 
| st_split() | split a polygon given line geometry | 
| st_boundary() | return the boundary of a geometry | 
First, import the watersheds:
## Import watersheds
gpkg_watershd_fn <- "./data/yose_watersheds.gpkg"
file.exists(gpkg_watershd_fn)
yose_watersheds <- st_read(gpkg_watershd_fn, layer="calw221")
## Plot it
plot(st_geometry(yose_watersheds), axes=TRUE)
Next, compute the centroids:
Let’s put a 1km buffer around each of the campgrounds.
First import the campgrounds (which are part of the yose_poi Shapefile):
## Import and project the campgrounds
yose_campgrnds_utm <- st_read(dsn="./data", layer="yose_poi") %>% 
  dplyr::filter(POITYPE == 'Campground') %>% 
  dplyr::select(POINAME) %>% 
  st_transform(epsg_utm11n_nad83)Next, buffer them (remember the buffer distance should be in map units):
## Create the 1km buffer
yose_campgrnds_buff <- st_buffer(yose_campgrnds_utm, dist=1000)
## Plot
tm_shape(yose_bnd_utm) +
  tm_borders() +
tm_shape(yose_campgrnds_buff) +
  tm_polygons(col="red") +
tm_layout(title="1km buffer around campgrounds", 
          title.bg.color="white") +
tm_scale_bar(position = c("right", "bottom"))
Other functions take two input layers and combine them in different ways.
| Function | Computes | 
|---|---|
| st_union() | union of several geometries | 
| st_intersection() | intersection of pairs of geometries | 
| st_difference() | difference between pairs of geometries | 
| st_sym_difference() | symmetric difference (xor, union w/o intersection) | 
Union all the burn scars for 1980s. What was the total area burned?
First we pull out all the fires for the 1980s:
## Extract fire scars from the 1980s
yose_fires_80s <- yose_firepolys_utm %>% filter(YEAR >= 1980 & YEAR <=1989) 
nrow(yose_fires_80s)
## Plot them
plot(yose_bnd_utm %>% st_geometry(), col=NA, border="red")
plot(yose_fires_80s %>% st_geometry(), col=NA, border="gray50", add=TRUE)## [1] 767

Next we union the polygons and compute the area.
## Union the polygons (to remove overlapping polygons)
yose_fires_80s_union <- yose_fires_80s %>% st_union()
plot(yose_bnd_utm %>% st_geometry(), col=NA, border="red")
plot(yose_fires_80s_union %>% st_geometry(), col="gray80", border="gray60", add=TRUE)
## Compute the total area, displying the units in square miles
yose_fires_80s_union %>% st_area() %>% set_units(mi2)## 87.66586 [mi2]

Find the total area burned during the 1990s.
[Solution]
Sometimes you may get a typology error caused by a self-intersecting polygon or something similar. You can repair some types of typology errors with st_make_valid() from the lwgeom package.
Run the following code and you will probably get an error message because one of the fire polygons for the 2000s has a self-intersecton. Un-comment the the st_make_valid() line and try again.
Today we saw how to:
Additional Resources
sf Vignette 3 - Manipulating Simple Feature Geometries. sf package Vignette