Global Forest Change
By Josh Erickson
March 4, 2021
Introduction
Below is a workflow used to create a forest change raster (2011-2019) for the Watershed Condition Class (WCC) analysis in USDA-USFS Region 1. The workflow leverages Google Earth Engine (GEE) (Gorelick et al. 2017) to get large remote sensing products fast and easy. This allows for reproducibility in the future as well as quick return times for clients, i.e. making large requests managable. The analysis uses the United States National Land Cover Database (NLCD) (Yang et al. 2018) and the Hansen Global Forest Change v1.7 (2000-2019) (Hansen et al. 2013) to create a raster with three bands: 1) forest loss, 2). loss year, 3). percentage of forest cover. The workflow will go through code in R (R Core Team 2020) using the rgee
package (Aybar et al. 2020) which does all of the heavy lifting; however, it is essentially JavaScript and if you want to do this workflow in JavaScript then please use this link. Also, the code is housed on github at this link if you wanted the scripts via file. The rest of the report will be mostly code and workflow, so you have been warned.
Workflow
Libraries we’ll use.
library(rgee)
ee_Initialize()
library(sf)
library(tidyverse)
First we’ll get the bbox for Region 1. This will be used for exporting the final image at the end of the analysis.
region_1_bbox <- st_bbox(c(xmin = -117.190616, xmax = -96.554411, ymax = 49.001390, ymin = 44.26879), crs = st_crs(4326))
r1_bb <- st_as_sfc(region_1_bbox) %>% st_as_sf()
geom = ee$Geometry$Rectangle(region_1_bbox)
Now we’ll just take a quick peak and see what we’ll be getting for our final image region.
state_r1 <- aoi_get(state = c("Montana", "Idaho", "Wyoming", "South Dakota", "North Dakota"))
# plot showing boundary
ggplot() + geom_sf(data = state_r1, fill = NA) +
geom_sf(data = r1_bb, fill = NA, color = 'red', aes(shape = "bbox")) + theme_bw() + labs(shape = "")
Below is where we’ll start working with earth engine. The goal is to get the global forest change (GFC) image from Hansen et al. (2013) and filter all the years greater than 2011. This will give us an image of only 2011-2019 data. To do this, we need to mask years greater than or equal to ‘11’ from the ‘lossyear’ band. We’ll also mask out no-data (0) and ‘permanent water bodies’ (2) using the ‘datamask’ band. Once we perform the masking steps we’ll have an image only including years from 2011-2019 and masked out water-bodies and no-data.
# now get the global forest change from 2011 up to 2019
gfc = ee$Image('UMD/hansen/global_forest_change_2019_v1_7')
# need to mask out years from 2011 to 2019
gfc_11_19_mask = gfc$select('lossyear')$gte(11)
gfc_11_19 = gfc$updateMask(gfc_11_19_mask)
gfc_dataMask = gfc$select('datamask')$eq(1)
gfc_11_19 = gfc_11_19$updateMask(gfc_dataMask) #this is the final image to use
# now visualise to see the difference
#Map$addLayer(gfc_11_19, visParams = list(bands = 'loss',min = 0, max = 1, palette = c('red'))) | Map$addLayer(gfc, visParams = list(bands = 'lossyear',min = 0, max = 1, palette = 'blue'))
The video below shows what it would look like if we didn’t mask-out the years; blue being 2000-2019 and red being 2011-2019. Notice how some older units in blue disappear when only looking at 2011-2019.
nlcd = ee$Image("USGS/NLCD/NLCD2011")
nlcd_tree <- nlcd$select('percent_tree_cover')
nlcd_tree_mask <- nlcd_tree$gt(.90) # I really think above 0 is ideal
nlcd_tree = nlcd_tree$updateMask(nlcd_tree_mask)
nlcd_tree = nlcd_tree$updateMask(gfc_dataMask)
Map$addLayer(nlcd_tree$clip(geom))
Now we can add this band (nlcd_tree
) to the gfc_11_19
image and we’ll only grab the relevant bands: ‘percent_tree_cover,’ ‘loss,’ ‘lossyear.’ You could just export all the bands if needed or if you wanted to add other information (e.g. precip, ndvi, npp, etc) you could that in this step.
gfc_nlcd <- gfc_11_19$addBands(nlcd_tree)
gfc_nlcd <- gfc_nlcd$select(c('percent_tree_cover', 'loss', 'lossyear'))
Now we can download to a tiff but first let’s find out the crs we need to use so that we can export it without having to do any postprocessing. The crs the region uses is below (e.g., NAD_1983_Albers/EPSG:5070/NAD83/Conus Albers).
st_read("T:/FS/Reference/GIS/r01/Data/SpatialReference/R1SpatialReference.gdb")
## Reading layer `R1SpatialRefTemplate100' from data source `T:\FS\Reference\GIS\r01\Data\SpatialReference\R1SpatialReference.gdb' using driver `OpenFileGDB'
## Simple feature collection with 0 features and 3 fields
## bbox: xmin: NA ymin: NA xmax: NA ymax: NA
## projected CRS: NAD_1983_Albers
This will probably take a couple of hours or more! If you want to try a smaller geom (e.g. region) and test out the process I’d recommend doing that before you export the entire image.
geom = ee$Geometry$Rectangle(region_1_bbox)
gfc_nlcd_5070 <- ee_as_raster(gfc_nlcd,
region = geom,
dsn = "gfc_nlcd_5070",
scale = 30,
crs = 'EPSG:5070',
container = "jle_rasters",
via = "gcs",
lazy = TRUE)
From here, we can start to make some quick graphics and maybe look at some distributions, e.g. yearloss to area, etc.
gfc_nlcd_tif <- stack("C:/Users/joshualerickson/Box/01. joshua.l.erickson Workspace/Detail/github/land_cover_change/images/gfc_nlcd_gte.tif")
gfc_nlcd_lossyear <- gfc_nlcd_tif[[3]]
gfc_nlcd_lossyear[gfc_nlcd_lossyear<11]<-NA
#write a raster because this calc above takes a while...
writeRaster(gfc_nlcd_lossyear, 'images/gfc_nlcd_lossyear.tif')
gfc_nlcd_lossyear <- raster('C:/Users/joshualerickson/Box/01. joshua.l.erickson Workspace/Detail/github/land_cover_change/images/gfc_nlcd_lossyear.tif')
## Warning in showSRID(uprojargs, format = "PROJ", multiline = "NO"): Discarded datum Unknown based on GRS80 ellipsoid in CRS definition,
## but +towgs84= values preserved
#stars for plotting
gfc_nlcd_stars <- st_as_stars(gfc_nlcd_lossyear)
proj_st <- "+proj=aea +lat_0=23 +lon_0=-96 +lat_1=29.5 +lat_2=45.5 +x_0=0 +y_0=0 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs"
ggplot() + geom_stars(data = gfc_nlcd_stars, downsample = c(20, 20, 1)) + geom_sf(data = st_transform(state_r1, crs = 5070, proj4string = proj_st), fill = NA) +
geom_sf(data = st_transform(r1_bb, crs = 5070, proj4string = proj_st), fill = NA, color = 'red', aes(shape = "bbox")) +
scale_fill_distiller(na.value = "white", palette = 'RdBu') + theme_bw() + labs(x = '', y = '', fill = 'Loss Years', shape = '')
As you can see this is not the most revealing graph. Also, the joys of working with crs and projections. So, I would recommend opening in your favorite graphical interface (ArcPro, ArcMap, QGIS, etc) and exploring!
References
- Posted on:
- March 4, 2021
- Length:
- 6 minute read, 1209 words
- See Also: