R(Lovelace, Nowosad, & Muenchow, 2019). This document is prepared for CP6521 Advanced GIS, a graduate-level city planning elective course at Georgia Tech in Spring 2019. For any question, contact the instructor, Yongsung Lee, Ph.D. via yongsung.lee(at)gatech.edu.install.packages("tidyverse", dependencies = TRUE)
install.packages("stringr", dependencies = TRUE)
install.packages("sf", dependencies = TRUE)
install.packages("raster", dependencies = TRUE)
install.packages("spData", dependencies = TRUE)
install.packages("devtools") # for this, you need Rtools installed on your machine
devtools::install_github("Nowosad/spDataLarge")
# install.packages("sp", dependencies = TRUE)
# install.packages("mapview", dependencies = TRUE)
# install.packages("tmap", dependencies = TRUE)
install.packages("geosphere", dependencies = TRUE)
library(tidyverse)
library(stringr) # for working with strings (pattern matching)
library(sf) # classes and functions for vector data
library(raster) # classes and functions for raster data
library(spData) # load geographic data
library(spDataLarge) # load larger geographic data
# library(sp)
# library(mapview)
# library(tmap)
library(geosphere)
What we do:
1. Learn geometry operations on raster data
- Spatial intersection between rasters
- Incraese/decrease raster resolutions
raster and vector togetherdata("elev", package = "spData")
clip = raster(xmn = 0.9, xmx = 1.8, ymn = -0.45, ymx = 0.45,
res =0.3, vals = rep(1, 9))
elev[clip, drop = FALSE]
data(elev, package = "spData")
elev_2 = extend(elev, c(1, 2), value = 1000) # add one row and two columns to each side of the raster
plot(elev_2)
What if the extents of two rasters differ? R will return an error.
elev_3 = elev + elev_2
# Raster objects have different extents. Result for their intersection is returned
# extend the first raster to be fitted to the second raster
# note that the default value for new raster cells are NA here.
elev_4 = extend(elev, elev_2)
Change the origin of a raster (the point in a raster, which is the closest to (0.0) based on the size of its resolution. That is, changing the resoltion of a raster may change its origin.)
origin(elev_4)
#> [1] 0 0
# change the origin
origin(elev_4) = c(0.25, 0.25)
plot(elev_4)
# and add the original raster
plot(elev, add = TRUE)
To match resolutions of rasters,
- decrease by aggregate()
- increase by disaggregate()
By default, output values are the mean of input values
data("dem", package = "RQGIS")
dem_agg = aggregate(dem, fact = 5, fun = mean)
disaggregate() requires a method that computes the values of smaller cells. By default, method="" returns values taken from immediate neighbors of those cells. method = "bilinear" interpolates using the values of four nearest (larger) cells and their distances to the new (smaller) cell.
dem_disagg = disaggregate(dem_agg, fact = 5, method = "bilinear")
identical(dem, dem_disagg)
resample() computes values for new pixel locations, and by default it uses bilinear interpolation.
# add 2 rows and columns, i.e. change the extent
dem_agg = extend(dem_agg, 2)
plot(dem)
plot(dem_agg)
# resample() takes the first argeument as an input and
# returns a raster with the same extent and resolution as the second argument
# below, the added rows/columns are removed after resampling
dem_disagg_2 = resample(dem_agg, dem)
crop() returns a raster in a new (smaller) extent defined by a cropping object.
srtm = raster(system.file("raster/srtm.tif", package = "spDataLarge")) # target object
zion = st_read(system.file("vector/zion.gpkg", package = "spDataLarge")) # cropping object
zion = st_transform(zion, projection(srtm))
srtm_cropped = crop(srtm, zion) # the extent becomes slightly smaller
mask() returns NA for those raster cells outside of the cropping object.
srtm_masked = mask(srtm, zion)
srtm_inv_masked = mask(srtm, zion, inverse = TRUE)
raster::extract() takes values from a ‘target’ raster for a given geographic ‘selector’ (typically vector: e.g., points, lines, or polygons).
data("zion_points", package = "spDataLarge") # 30 random points in the Zion National Park
zion_points$elevation = raster::extract(srtm, zion_points) # create a new column
summary(zion_points$elevation)
buffer argument generates a list of vectors. Each vector in the list contains raster values (elevation) inside the buffer of a given point.
raster::extract(srtm, zion_points, buffer = 1000)
extract() works with lines.
# create a line
zion_transect = cbind(c(-113.2, -112.9), c(37.45, 37.2)) %>%
st_linestring() %>%
st_sfc(crs = projection(srtm)) %>%
st_sf()
# extract elevation along the line
# returns a list containing a sinlge matrix: first column - cell id, second column - elevation
# if multiple lines, then it will return a list of multiple matrices, one list element for each line
transect = raster::extract(srtm, zion_transect,
along = TRUE, cellnumbers = TRUE)
as.data.frame(transect[[1]])
purrr::map_dfr(): https://purrr.tidyverse.org/reference/map.html
transect_df = purrr::map_dfr(transect, as_data_frame, .id="ID")
transect_coords = xyFromCell(srtm, transect_df$cell)
transect_df$dist = c(0, cumsum(geosphere::distGeo(transect_coords)))
An example of raster::extract() work with a polygon. This helps generate summary statistics for raster cells, grouped by polygons (boundaries).
zion_srtm_values = raster::extract(x = srtm, y = zion, df = TRUE)
group_by(zion_srtm_values, ID) %>%
summarize_at(vars(srtm), list(~min, ~mean, ~max))
Besides continuous values such as elevation, raster::extract() also handles categorical values by counting the number of cells in each category given a geographic selector.
zion_nlcd <- raster::extract(nlcd, zion, df = TRUE, factors = TRUE)
dplyr::select(zion_nlcd, ID, levels) %>%
tidyr::gather(key, value, -ID) %>%
group_by(ID, key, value) %>%
tally() %>%
tidyr::spread(value, n, fill = 0)
Conversion of vector objects into their represeation in raster objects.
- the first argument of rasterize() is a vector object
- the seond argument is a raster object that works as a template for extent, resolution, crs, etc.
cycle_hire_osm_projected = st_transform(cycle_hire_osm, 27700)
raster_template = raster(extent(cycle_hire_osm_projected), resolution = 1000,
crs = st_crs(cycle_hire_osm_projected)$proj4string)
First, create a present/absence rater. filed defines values for all non-empty cells.
ch_raster1 = rasterize(cycle_hire_osm_projected, raster_template, field = 1)
fun specifies summary statistics, which convert multiple observations in close proximity into associated cells in the raster object: e.g., fun = "count" counts the number of cycle hire points in each grid cell here.
ch_raster2 <- rasterize(cycle_hire_osm_projected, raster_template,
field = 1, fun = "count")
By specifying field and fun, we can create a raster presenting the capacity of cycle hire points across London.
ch_raster3 <- rasterize(cycle_hire_osm_projected, raster_template,
field = "capacity", fun = sum)
Conversion of spatialy continuous raster data into spatially discrete vector data such as points, lines, or polygons.
First, raster to points
elev_point <- rasterToPoints(elev, spatial = TRUE) %>%
st_as_sf()
plot(elev)
plot(elev_point, add = TRUE)
Second, raster to lines
data(dem, package = "RQGIS")
cl = rasterToContour(dem)
plot(dem, axes = FALSE)
plot(cl, add = TRUE)
Better visualization options
# create hillshade
hs = hillShade(slope = terrain(dem, "slope"), aspect = terrain(dem, "aspect"))
plot(hs, col = gray(0:100 / 100), legend = FALSE)
# overlay with DEM
plot(dem, col = terrain.colors(25), alpha = 0.5, legend = FALSE, add = TRUE)
# add contour lines
contour(dem, col = "white", add = TRUE)
Third, raster to polygons
grain_poly <- rasterToPolygons(grain) %>% # each cell converted to separate polygons
st_as_sf()
plot(grain)
plot(grain_poly)
grain_poly2 <- grain_poly %>% # cells are "dissolved"; in total there are three multipolygon features
group_by(layer) %>%
summarize()
plot(grain_poly2)
Some of the exercises use a raster dataset (ndvi) from the RQGIS package. They also use a polygonal ‘convex hull’ derived from the vector dataset (ch) to represent the area of interest:
install.packages("RQGIS", dependencies = TRUE)
library(RQGIS)
data(random_points) #for questions before 7.
data(ndvi)
ch = st_combine(random_points) %>%
st_convex_hull()
ndvi raster using (1) the random_points dataset and (2) the ch dataset. Are there any differences in the output maps? Next, mask ndvi using these two datasets. Can you see any difference now? How can you explain that?plot(ndvi)
plot(st_geometry(random_points), add = TRUE)
plot(ch, add = TRUE)
Firstly, extract values from ndvi at the points represented in random_points. Next, extract average values of ndvi using a 90 buffer around each point from random_points and compare these two sets of values. When would extracting values by buffers be more suitable than by points alone?
nz_height object) and create a template raster with a resolution of 3 km. Using these objects:
nz_height3100 <- nz_height %>% filter(elevation>3100)
new_graticule <- st_graticule(nz_height3100, datum = 2193)
plot(st_geometry(nz_height3100), graticule = new_graticule, axes = TRUE)
Aggregate the raster counting high points in New Zealand (created in the previous exercise), reduce its geographic resolution by half (so cells are 6 by 6 km) and plot the result.
Resample the lower resolution raster back to a resolution of 3 km. How have the results changed?
Name two advantages and disadvantages of reducing raster resolution.
grain dataset and filter all squares representing clay.