Workshops

AGU 2022 GEDI Workshop

Workshop Icebreaker Activity

Studying forest structure from space: An introduction to GEDI lidar data pre-processing and analysis in ecological applications

Code authors: Melissa Rose, Ivan Gonzalez, Jenna Keany \(\\\)\(\\\)

Technical contributors: Emma Reich, Shelby Sunquist, Patrick Jantz

The purpose of this workshop is to assist interested Ecologists, Biologists, and Geologists in accessing NASA’s Global Ecosystem Dynamics Investigation (GEDI) near-global lidar data and applying it to their research. We will review recent applications and discuss future directions for GEDI data. Using open-access R software, we will demonstrate how participants may easily access, manipulate, explore, assess, and apply GEDI data in their study areas. We will develop modules (download > cleaning > visualization, etc) and share code, inputs, and outputs to prevent bottlenecks in case of technical issues. This strategy allows attendees to participate in every module with products derived from previous stages even if previous steps/results weren’t completed. Participants may be required to install open-source software prior to the workshop. This workshop is organized primarily by PhD students in the Ecological and Environmental Informatics program at Northern Arizona University.

Objectives

  1. The primary objective of this workshop is to disseminate information on the use and functionality of spaceborne lidar (light detection and ranging) from GEDI, including: how the instrument functions, spatial resolution and coverage, available data products, where and how to access the data, and uses of lidar data in a variety of earth science subjects.

  2. Participants will learn how to download GEDI data for an area of interest, process the data, and apply it to an ecological question in R.

  3. Presenters will provide example code and intermediate data products so that participants can follow along and explore results in the event of technical challenges. \(\\\)\(\\\)\(\\\)\(\\\)

To launch an interactive coding environment:

Launch Rstudio Binder

To download this code, please refer to the workshop github:

https://github.com/IGSA-SICCS/GEDI_workshop

Tutorial:

SECTION 0: PREPARE R ENVIRONMENT

0.1: Libraries

Do not need to run this chunk when running in Binder

#Install libraries if not already installed
#libs <- c('raster', 'devtools', 'sf', 'sp', 'data.table', 'dplyr', 'lidR',
#          'httr', 'tidyr', 'curl', 'hdf5r', 'leaflet', 'leaflet.extras',
#          'leafsync', 'ggplot2', 'rgeos', 'RColorBrewer', 'rasterVis',
#          'viridis', 'rdgal', 'rhdf5', 'dismo', 'sdm', 'randomForest', 'hdf5r')

#sapply(libs, FUN = function(x){
#  if (!x %in% installed.packages()){
#    install.packages(x)
#  }
#})

#sdm::installAll()
#Define which libraries to use
library(raster)       ## spatial tools
library(devtools)     ## tools for building and installing libraries
library(sf)           ## spatial tools
library(sp)           ## spatial tools
library(data.table)   ## data / table manipulation
library(lidR)         ## lidar manipulation tools
library(httr)         ## methods for web files interaction
library(tidyr)        ## data / table manipulation
library(curl)         ## download files from the web
library(hdf5r)        ## reading hdf5 files
library(leaflet)      ## color palettes
library(leafsync)     ## interactive maps
library(ggplot2)      ## nice plot tools
library(rgeos)        ## geometric for vector data
library(RColorBrewer) ## color palettes
library(rasterVis)    ## raster viz tools
library(viridis)      ## color palettes
library(rgdal)        ## readOGR: Read OGR vector maps into Spatial objects
library(rhdf5)        ## R Interface to HDF5
library(dismo)        ## Species Distribution Modeling
library(sdm)          ## Species Distribution Modelling
library(randomForest) ## For sdm methd 'rf'
library(hdf5r)        ## To show h5 file structure
#library(rGEDI)       ## GEDI functions in R

0.2: Directory and Subdirectories

#define folder paths
wd <- getwd()

gL1B_dir <- './data/GEDI01_B.002/'
gL2A_dir <- './data/GEDI02_A.002/'
gL2B_dir <- './data/GEDI02_B.002/'

0.3: Source Scripts

#source rGEDI functions in scripts directory
source('./scripts/gedi_functions.R')

SECTION 1: DOWNLOAD GEDI DATA FOR A REGION OF INTEREST

1.1: Define region of interest and date range

#ROI: Colombia
#specify bounding box coordinates for ROI
ll_lon <- -75  #lower left longitude
ll_lat <- 0    #lower left latitude
ur_lon <- -74  #upper right longitude
ur_lat <- 1    #upper right latitude

#convert into bounding box coordinates
bbox <- paste(ll_lon, ll_lat, ur_lon, ur_lat, sep = ',')

#specify the date range to filter orbits by
daterange <- c("2020-05-01","2020-07-31")

1.2: Visualize region of interest

#view footprint locations for entire orbit on map
leaflet() %>%
  addRectangles(
    lng1=ll_lon, lat1=ll_lat,
    lng2=ur_lon, lat2=ur_lat,
    color = "grey",
    fillColor = "transparent") %>%
  addScaleBar(options = list(imperial = FALSE)) %>%
  addProviderTiles(providers$Esri.WorldImagery) %>%
  addLegend(colors = "grey", labels = "Region of Interest") 

1.3: Use gedi_finder to find overlapping GEDI orbits

#create a list of all overlapping orbits for each GEDI data product
gL1B <- gedi_finder('GEDI01_B.002', bbox)  
## [1] "Found 157 GEDI01_B.002 overlapping orbits found."
gL2A <- gedi_finder('GEDI02_A.002', bbox)
## [1] "Found 157 GEDI02_A.002 overlapping orbits found."
gL2B <- gedi_finder('GEDI02_B.002', bbox)
## [1] "Found 157 GEDI02_B.002 overlapping orbits found."

1.4 Use gedi_temp_filter to subset orbit list within date range

#subset orbit list using daterange
gL1B <- gedi_temp_filter(gL1B, 'GEDI01_B.002', daterange)
## [1] "Found 21 GEDI01_B.002 overlapping orbits from 2020-05-01 to 2020-07-31."
gL2A <- gedi_temp_filter(gL2A, 'GEDI02_A.002', daterange)
## [1] "Found 21 GEDI02_A.002 overlapping orbits from 2020-05-01 to 2020-07-31."
gL2B <- gedi_temp_filter(gL2B, 'GEDI02_B.002', daterange)
## [1] "Found 21 GEDI02_B.002 overlapping orbits from 2020-05-01 to 2020-07-31."

Stop here ——————————- 1.5: Use gedi_download to download GEDI data (DO NOT RUN)

#download data product to their output data directory

# gedi_download(filepath=gL1B,outdir=gL1B_dir)
# gedi_download(filepath=gL2A,outdir=gL2A_dir)
# gedi_download(filepath=gL2B,outdir=gL2B_dir)

SECTION 2: CLIP GEDI DATA PRODUCTS TO REGION OF INTEREST AND FILTER FOR QUALITY SHOTS 2.1 Read GEDI data (DO NOT RUN)

#read the level 1B, 2A, and 2B for a single orbit
## Remove the "#" symbol in the next lines in order to run them
# gedilevel1B <- readLevel1B(level1Bpath = paste0(gL1B_dir,"GEDI01_B_2020160005619_O08428_03_T01337_02_005_01_V002.h5"))
# gedilevel2A <- readLevel2A(level2Apath = paste0(gL2A_dir,"GEDI02_A_2020160005619_O08428_03_T01337_02_003_01_V002.h5"))
# gedilevel2B <- readLevel2B(level2Bpath = paste0(gL2B_dir,"GEDI02_B_2020160005619_O08428_03_T01337_02_003_01_V002.h5"))

2.2 Clip data within bbox coordinates (DO NOT RUN)

## Remove the "#" symbol in the next lines in order to run them
# gedilevel1B <- clipLevel1B(gedilevel1B, ll_lon, ur_lon, ll_lat, ur_lat, output = paste0(gL1B_dir,"GEDI01_B_2020160005619_O08428_03_T01337_02_005_01_V002_clip.h5"))
# gedilevel2A <- clipLevel2A(gedilevel2A, ll_lon, ur_lon, ll_lat, ur_lat, output = paste0(gL2A_dir,"GEDI02_A_2020160005619_O08428_03_T01337_02_003_01_V002_clip.h5"))
# gedilevel2B <- clipLevel2B(gedilevel2B, ll_lon, ur_lon, ll_lat, ur_lat, output = paste0(gL2B_dir,"GEDI02_B_2020160005619_O08428_03_T01337_02_003_01_V002_clip.h5"))

Restart here ——————————-

Read clipped files into R

gedilevel1B <- readLevel1B(level1Bpath = paste0(gL1B_dir,"GEDI01_B_2020160005619_O08428_03_T01337_02_005_01_V002_clip.h5"))
gedilevel2A <- readLevel2A(level2Apath = paste0(gL2A_dir,"GEDI02_A_2020160005619_O08428_03_T01337_02_003_01_V002_clip.h5"))
gedilevel2B <- readLevel2B(level2Bpath = paste0(gL2B_dir,"GEDI02_B_2020160005619_O08428_03_T01337_02_003_01_V002_clip.h5"))

2.3 Use GEDI L2A quality flag to select only quality shots

#view GEDI L2A h5 layers
h5groups2A <- hdf5r::list.groups(gedilevel2A@h5[['BEAM0000']])
h5groups2A
##  [1] "ancillary"                  "geolocation"
##  [3] "land_cover_data"            "rx_1gaussfit"
##  [5] "rx_1gaussfit/ancillary"     "rx_assess"
##  [7] "rx_assess/ancillary"        "rx_processing_a1"
##  [9] "rx_processing_a1/ancillary" "rx_processing_a2"
## [11] "rx_processing_a2/ancillary" "rx_processing_a3"
## [13] "rx_processing_a3/ancillary" "rx_processing_a4"
## [15] "rx_processing_a4/ancillary" "rx_processing_a5"
## [17] "rx_processing_a5/ancillary" "rx_processing_a6"
## [19] "rx_processing_a6/ancillary"
#view GEDI L2A h5 layers
h5datasets2A <- hdf5r::list.datasets(gedilevel2A@h5[['BEAM0000']])
head(h5datasets2A, 100)
##   [1] "ancillary/l2a_alg_count"           "beam"
##   [3] "channel"                           "degrade_flag"
##   [5] "delta_time"                        "digital_elevation_model"
##   [7] "digital_elevation_model_srtm"      "elev_highestreturn"
##   [9] "elev_lowestmode"                   "elevation_bias_flag"
##  [11] "elevation_bin0_error"              "energy_total"
##  [13] "geolocation/elev_highestreturn_a1" "geolocation/elev_highestreturn_a2"
##  [15] "geolocation/elev_highestreturn_a3" "geolocation/elev_highestreturn_a4"
##  [17] "geolocation/elev_highestreturn_a5" "geolocation/elev_highestreturn_a6"
##  [19] "geolocation/elev_lowestmode_a1"    "geolocation/elev_lowestmode_a2"
##  [21] "geolocation/elev_lowestmode_a3"    "geolocation/elev_lowestmode_a4"
##  [23] "geolocation/elev_lowestmode_a5"    "geolocation/elev_lowestmode_a6"
##  [25] "geolocation/elev_lowestreturn_a1"  "geolocation/elev_lowestreturn_a2"
##  [27] "geolocation/elev_lowestreturn_a3"  "geolocation/elev_lowestreturn_a4"
##  [29] "geolocation/elev_lowestreturn_a5"  "geolocation/elev_lowestreturn_a6"
##  [31] "geolocation/elevation_1gfit"       "geolocation/elevs_allmodes_a1"
##  [33] "geolocation/elevs_allmodes_a2"     "geolocation/elevs_allmodes_a3"
##  [35] "geolocation/elevs_allmodes_a4"     "geolocation/elevs_allmodes_a5"
##  [37] "geolocation/elevs_allmodes_a6"     "geolocation/energy_lowestmode_a1"
##  [39] "geolocation/energy_lowestmode_a2"  "geolocation/energy_lowestmode_a3"
##  [41] "geolocation/energy_lowestmode_a4"  "geolocation/energy_lowestmode_a5"
##  [43] "geolocation/energy_lowestmode_a6"  "geolocation/lat_highestreturn_a1"
##  [45] "geolocation/lat_highestreturn_a2"  "geolocation/lat_highestreturn_a3"
##  [47] "geolocation/lat_highestreturn_a4"  "geolocation/lat_highestreturn_a5"
##  [49] "geolocation/lat_highestreturn_a6"  "geolocation/lat_lowestmode_a1"
##  [51] "geolocation/lat_lowestmode_a2"     "geolocation/lat_lowestmode_a3"
##  [53] "geolocation/lat_lowestmode_a4"     "geolocation/lat_lowestmode_a5"
##  [55] "geolocation/lat_lowestmode_a6"     "geolocation/lat_lowestreturn_a1"
##  [57] "geolocation/lat_lowestreturn_a2"   "geolocation/lat_lowestreturn_a3"
##  [59] "geolocation/lat_lowestreturn_a4"   "geolocation/lat_lowestreturn_a5"
##  [61] "geolocation/lat_lowestreturn_a6"   "geolocation/latitude_1gfit"
##  [63] "geolocation/lats_allmodes_a1"      "geolocation/lats_allmodes_a2"
##  [65] "geolocation/lats_allmodes_a3"      "geolocation/lats_allmodes_a4"
##  [67] "geolocation/lats_allmodes_a5"      "geolocation/lats_allmodes_a6"
##  [69] "geolocation/lon_highestreturn_a1"  "geolocation/lon_highestreturn_a2"
##  [71] "geolocation/lon_highestreturn_a3"  "geolocation/lon_highestreturn_a4"
##  [73] "geolocation/lon_highestreturn_a5"  "geolocation/lon_highestreturn_a6"
##  [75] "geolocation/lon_lowestmode_a1"     "geolocation/lon_lowestmode_a2"
##  [77] "geolocation/lon_lowestmode_a3"     "geolocation/lon_lowestmode_a4"
##  [79] "geolocation/lon_lowestmode_a5"     "geolocation/lon_lowestmode_a6"
##  [81] "geolocation/lon_lowestreturn_a1"   "geolocation/lon_lowestreturn_a2"
##  [83] "geolocation/lon_lowestreturn_a3"   "geolocation/lon_lowestreturn_a4"
##  [85] "geolocation/lon_lowestreturn_a5"   "geolocation/lon_lowestreturn_a6"
##  [87] "geolocation/longitude_1gfit"       "geolocation/lons_allmodes_a1"
##  [89] "geolocation/lons_allmodes_a2"      "geolocation/lons_allmodes_a3"
##  [91] "geolocation/lons_allmodes_a4"      "geolocation/lons_allmodes_a5"
##  [93] "geolocation/lons_allmodes_a6"      "geolocation/num_detectedmodes_a1"
##  [95] "geolocation/num_detectedmodes_a2"  "geolocation/num_detectedmodes_a3"
##  [97] "geolocation/num_detectedmodes_a4"  "geolocation/num_detectedmodes_a5"
##  [99] "geolocation/num_detectedmodes_a6"  "geolocation/quality_flag_a1"
#read select GEDI L2A layers into data.table
level2AM <- getLevel2AM(gedilevel2A)
##
  |
  |                                                                      |   0%
  |
  |=========                                                             |  12%
  |
  |==================                                                    |  25%
  |
  |==========================                                            |  38%
  |
  |===================================                                   |  50%
  |
  |============================================                          |  62%
  |
  |====================================================                  |  75%
  |
  |=============================================================         |  88%
  |
  |======================================================================| 100%
head(level2AM, 5)
##        beam       shot_number degrade_flag quality_flag delta_time sensitivity
## 1: BEAM0000 84280000300247456            0            1   76817129   0.9446448
## 2: BEAM0000 84280000300247457            0            1   76817129   0.9768031
## 3: BEAM0000 84280000300247458            0            1   76817129   0.9755690
## 4: BEAM0000 84280000300247459            0            1   76817129   0.9304212
## 5: BEAM0000 84280000300247460            0            1   76817129   0.9657014
##    sensitivity_a2 surface_flag water_persistence urban_proportion
## 1:      0.9773547            1                 0                0
## 2:      0.9768031            1                 0                0
## 3:      0.9755690            1                 0                0
## 4:      0.9676956            1                 0                0
## 5:      0.9657014            1                 0                0
##    solar_elevation lat_lowestmode lon_lowestmode elev_highestreturn
## 1:       -41.60833      0.2771671      -74.99973           215.9003
## 2:       -41.60883      0.2767456      -74.99944           217.4226
## 3:       -41.60933      0.2763244      -74.99914           221.4247
## 4:       -41.60983      0.2759028      -74.99884           221.2567
## 5:       -41.61033      0.2754821      -74.99855           227.2185
##    elev_lowestmode   rh0   rh1   rh2   rh3   rh4   rh5   rh6   rh7   rh8   rh9
## 1:        193.7073 -1.27 -0.85 -0.48 -0.11  0.22  0.59  1.00  1.45  1.98  2.54
## 2:        193.6978 -3.28 -2.46 -1.94 -1.56 -1.30 -1.08 -0.85 -0.67 -0.48 -0.33
## 3:        194.3747 -4.03 -3.21 -2.57 -2.01 -1.56 -1.19 -0.85 -0.52 -0.26  0.03
## 4:        194.4308 -3.62 -3.21 -2.91 -2.65 -2.42 -2.24 -2.09 -1.94 -1.79 -1.64
## 5:        196.0961 -4.25 -3.51 -2.98 -2.42 -1.68 -0.70  0.11  0.97  1.90  2.72
##     rh10  rh11  rh12  rh13  rh14  rh15  rh16  rh17  rh18  rh19  rh20  rh21
## 1:  3.02  3.51  3.96  4.40  4.85  5.38  5.90  6.50  7.17  7.77  8.33  8.78
## 2: -0.14  0.00  0.18  0.33  0.52  0.67  0.85  1.00  1.19  1.34  1.53  1.71
## 3:  0.29  0.59  0.89  1.15  1.49  1.86  2.39  2.91  3.36  3.77  4.14  4.59
## 4: -1.53 -1.41 -1.30 -1.19 -1.08 -0.97 -0.89 -0.78 -0.70 -0.59 -0.52 -0.41
## 5:  3.36  4.07  4.85  5.67  6.72  7.92  9.15 10.42 11.47 12.14 12.77 13.37
##     rh22  rh23  rh24  rh25  rh26  rh27  rh28  rh29  rh30  rh31  rh32  rh33
## 1:  9.19  9.56  9.90 10.23 10.57 10.87 11.13 11.39 11.65 11.91 12.14 12.32
## 2:  1.90  2.09  2.27  2.50  2.69  2.87  3.10  3.36  3.58  3.88  4.14  4.48
## 3:  5.11  5.86  6.53  7.09  7.54  7.92  8.33  8.70  9.11  9.45  9.78 10.12
## 4: -0.33 -0.22 -0.14 -0.07  0.03  0.11  0.22  0.29  0.37  0.48  0.56  0.67
## 5: 13.97 14.45 14.87 15.24 15.61 15.99 16.40 16.77 17.14 17.52 17.89 18.23
##     rh34  rh35  rh36  rh37  rh38  rh39  rh40  rh41  rh42  rh43  rh44  rh45
## 1: 12.55 12.74 12.92 13.11 13.30 13.45 13.63 13.78 13.93 14.08 14.23 14.38
## 2:  4.85  5.23  5.64  6.05  6.42  6.83  7.24  7.69  8.14  8.59  9.00  9.34
## 3: 10.46 10.87 11.32 11.76 12.17 12.59 12.96 13.30 13.59 13.89 14.19 14.45
## 4:  0.74  0.85  0.97  1.04  1.15  1.27  1.38  1.53  1.64  1.79  1.94  2.09
## 5: 18.53 18.79 19.05 19.27 19.46 19.65 19.83 20.02 20.21 20.39 20.62 20.81
##     rh46  rh47  rh48  rh49  rh50  rh51  rh52  rh53  rh54  rh55  rh56  rh57
## 1: 14.53 14.68 14.83 14.98 15.13 15.24 15.39 15.54 15.69 15.84 15.95 16.10
## 2:  9.63  9.90 10.16 10.38 10.61 10.83 11.05 11.32 11.58 11.91 12.25 12.66
## 3: 14.72 15.01 15.28 15.54 15.80 16.06 16.32 16.58 16.88 17.14 17.37 17.59
## 4:  2.27  2.50  2.72  2.98  3.32  3.69  4.14  4.63  5.15  5.71  6.23  6.76
## 5: 20.99 21.18 21.37 21.55 21.74 21.89 22.08 22.23 22.37 22.52 22.71 22.86
##     rh58  rh59  rh60  rh61  rh62  rh63  rh64  rh65  rh66  rh67  rh68  rh69
## 1: 16.25 16.36 16.51 16.62 16.77 16.88 16.99 17.11 17.22 17.33 17.44 17.56
## 2: 13.00 13.33 13.63 13.89 14.16 14.38 14.60 14.83 15.01 15.20 15.43 15.61
## 3: 17.78 17.97 18.19 18.38 18.56 18.79 19.01 19.20 19.42 19.65 19.83 20.02
## 4:  7.24  7.73  8.25  8.89  9.86 11.58 13.37 14.94 15.95 16.66 17.26 17.70
## 5: 23.01 23.16 23.31 23.46 23.61 23.76 23.91 24.06 24.24 24.39 24.54 24.69
##     rh70  rh71  rh72  rh73  rh74  rh75  rh76  rh77  rh78  rh79  rh80  rh81
## 1: 17.67 17.74 17.85 17.93 18.04 18.15 18.23 18.34 18.41 18.53 18.60 18.71
## 2: 15.80 15.99 16.21 16.40 16.58 16.77 16.96 17.14 17.33 17.52 17.70 17.89
## 3: 20.21 20.39 20.54 20.69 20.84 20.99 21.14 21.29 21.44 21.59 21.74 21.89
## 4: 18.15 18.53 18.86 19.20 19.50 19.80 20.10 20.36 20.62 20.88 21.14 21.37
## 5: 24.84 24.99 25.10 25.25 25.40 25.55 25.70 25.85 26.00 26.15 26.34 26.48
##     rh82  rh83  rh84  rh85  rh86  rh87  rh88  rh89  rh90  rh91  rh92  rh93
## 1: 18.83 18.90 19.01 19.12 19.20 19.31 19.42 19.54 19.65 19.80 19.91 20.06
## 2: 18.08 18.26 18.41 18.60 18.75 18.90 19.09 19.24 19.42 19.65 19.83 20.10
## 3: 22.08 22.23 22.41 22.60 22.79 22.97 23.16 23.35 23.53 23.76 23.98 24.21
## 4: 21.59 21.81 22.04 22.26 22.49 22.71 22.90 23.12 23.35 23.57 23.79 24.02
## 5: 26.63 26.82 26.97 27.16 27.31 27.49 27.68 27.83 28.02 28.20 28.39 28.58
##     rh94  rh95  rh96  rh97  rh98  rh99 rh100
## 1: 20.21 20.39 20.58 20.84 21.10 21.52 22.19
## 2: 20.39 20.81 21.37 21.93 22.41 22.90 23.72
## 3: 24.43 24.69 24.95 25.25 25.66 26.26 27.05
## 4: 24.28 24.54 24.84 25.18 25.55 26.04 26.82
## 5: 28.80 29.03 29.25 29.51 29.85 30.26 31.12
#convert shot_number from integer to character
level2AM$shot_number <- paste0(level2AM$shot_number)
dim(level2AM)
## [1] 3403  116
#filtering for quality_flag == 1
level2AM_qual <- level2AM[quality_flag == 1]
dim(level2AM_qual)
## [1] 3323  116

2.4 Visualize location of quality_flag filtered GEDI footprints

#view footprint locations on map
leaflet() %>%
  addRectangles(
    lng1=ll_lon, lat1=ll_lat,
    lng2=ur_lon, lat2=ur_lat,
    color = "grey",
    fillColor = "transparent") %>%
  addCircleMarkers(level2AM[quality_flag == 0, lon_lowestmode],
                   level2AM[quality_flag == 0, lat_lowestmode],
                   radius = 1,
                   opacity = 0.5,
                   color = "red",
                   popup = paste0("shot_number: ", level2AM$shot_number))  %>%
  addCircleMarkers(level2AM_qual$lon_lowestmode,
                   level2AM_qual$lat_lowestmode,
                   radius = 1,
                   opacity = 0.5,
                   color = "blue",
                   popup = paste0("shot_number: ", level2AM_qual$shot_number))  %>%
  setView(median(level2AM_qual$lon_lowestmode),
          median(level2AM_qual$lat_lowestmode),
          zoom = 10) %>%
  addScaleBar(options = list(imperial = FALSE)) %>%
  addProviderTiles(providers$Esri.WorldImagery) %>%
  addLegend(colors = c("grey", "blue", "red"), labels = c("Region of Interest", "Quality GEDI Footprints", "Non-Quality GEDI Footprints"))

2.5 Filter beams to select only Power Beams

#view beams based on their type
  #BEAM0000,BEAM0001,BEAM0010,BEAM0011 are Coverage Beams.
  #BEAM0101,BEAM0110,BEAM1000,BEAM1011 are Full Power Beams
level2AM_qual$beamType <- ifelse(level2AM_qual$beam %in% c('BEAM0000','BEAM0001','BEAM0010','BEAM0011'), 'Coverage', 'Power')

#view footprint locations on map
leaflet() %>%
  addCircleMarkers(level2AM_qual$lon_lowestmode,
                   level2AM_qual$lat_lowestmode,
                   radius = 1,
                   opacity = 0.5,
                   color = c('red', 'blue')[as.numeric(as.factor(level2AM_qual$beamType))],
                   popup = paste0(level2AM_qual$beamType, ' beam - ', level2AM_qual$beam,
                                  '<br>', 'shot_number: ', level2AM_qual$shot_number ))  %>%

  addScaleBar(options = list(imperial = FALSE)) %>%
  addProviderTiles(providers$Esri.WorldImagery) %>%
  addLegend(colors = c( "blue", "red"),
            labels = c("Power beam", "Coverage beam"))
#plot power and coverage shots ID from previous interactive map
plotVertProfiles <- plot_rh_profiles(level2AM_qual[shot_number %in%
                                                     c("84280500300243078", "84280300300243183")], # power and coverage
                                     idcolumn = 'beamType', col_vector = c('blue', 'red'))

#subset data frame to only include Power Beam shots
dim(level2AM_qual)
## [1] 3323  117
level2AM_qual <- level2AM_qual[beamType == 'Power']
dim(level2AM_qual)
## [1] 1496  117

2.6 Further filter shots using region-specific quality parameters

#using a set of other region-specific quality parameters
dim(level2AM_qual)
## [1] 1496  117
level2AM_qual <- level2AM_qual[sensitivity_a2 > 0.95]
level2AM_qual <- level2AM_qual[rh100 <= 50]
level2AM_qual <- level2AM_qual[surface_flag == 1]
level2AM_qual <- level2AM_qual[water_persistence < 10]
level2AM_qual <- level2AM_qual[urban_proportion < 50]
dim(level2AM_qual)
## [1] 1491  117

2.7 Visualize final filtered GEDI L2A dataset

#view footprint locations on map
leaflet() %>%
  addCircleMarkers(level2AM_qual$lon_lowestmode,
                   level2AM_qual$lat_lowestmode,
                   radius = 1,
                   opacity = 0.5,
                   color =  'blue',
                   popup = paste0('shot_number: ', level2AM_qual$shot_number ))  %>%

  addScaleBar(options = list(imperial = FALSE)) %>%
  addProviderTiles(providers$Esri.WorldImagery) %>%
  addLegend(colors = "blue",
            labels = "Final Filtered Shots")

2.8 Select individual footprints along a structurally heterogeneous gradient

sample_shots <- c("84280500300242755", "84280500300242756", "84280500300242757", "84280500300242758", "84280500300242759")
sample_region <- level2AM_qual[shot_number %in% sample_shots]
sample_region$sortid <- 1:nrow(sample_region)

#view footprint in sample region
colorRamp <- colorNumeric( palette = "viridis", domain = sample_region$sortid, reverse = TRUE)
pointColors <- colorRamp(sample_region$sortid)

leaflet() %>%
  addRectangles(
    lng1=min(sample_region$lon_lowestmode)-0.00025, lat1=min(sample_region$lat_lowestmode)-0.00025,
    lng2=max(sample_region$lon_lowestmode)+0.00025, lat2=max(sample_region$lat_lowestmode)+0.00025,
    color = "grey",
    fillColor = "transparent") %>%
  addCircleMarkers(sample_region$lon_lowestmode,
                   sample_region$lat_lowestmode,
                   radius = 25,
                   weight = 5,
                   fillColor = "transparent",
                   color = pointColors,
                   popup = paste0("shot_number: ", sample_region$shot_number))  %>%
  addScaleBar(options = list(imperial = FALSE)) %>%
  addProviderTiles(providers$Esri.WorldImagery) %>%
  addLegend(colors = c("grey", "blue"), labels = c("Sample Region", "Selected Shots")) 

Now lets see their vertical profiles

plotVertProfiles <- plot_rh_profiles(sample_region, idcolumn = 'shot_number', col_vector = pointColors)

SECTION 3: Visualize GEDI Data Products

3.1 Plot GEDIL1B Full Waveform

#select shot_numbers for different cover types
shotnum_forest <- "84280500300242755"
shotnum_edge <- "84280500300242757"
shotnum_grass <- "84280500300242759"
sample_shotnums <- c(shotnum_forest, shotnum_edge, shotnum_grass)

#get waveform data for each selected footprint
wf_forest <- getLevel1BWF(gedilevel1B, shot_number=shotnum_forest)
wf_edge <- getLevel1BWF(gedilevel1B, shot_number=shotnum_edge)
wf_grass <- getLevel1BWF(gedilevel1B, shot_number=shotnum_grass)

#check shot's data structure
head(wf_forest@dt)
##    rxwaveform elevation
## 1:   205.6654  282.0606
## 2:   206.3967  281.9114
## 3:   207.3341  281.7621
## 4:   208.4659  281.6128
## 5:   209.7527  281.4636
## 6:   211.0228  281.3143
#plot waveforms
par(mfrow = c(1,1), mar=c(4,4,1,1), cex.axis = 1.5)

plot(wf_forest, relative=TRUE, polygon=TRUE, type="l", lwd=2, col="#FDE725",
    xlab="Waveform Amplitude(%)", ylab="Elevation (m)", main="GEDIL1B rxwaveform: Forest", ylim=c(200,260))

plot(wf_edge, relative=TRUE, polygon=TRUE, type="l", lwd=2, col="#21908D",
    xlab="Waveform Amplitude(%)", ylab="Elevation (m)", main="GEDIL1B rxwaveform: Edge", ylim=c(200,260), add = TRUE)

plot(wf_grass, relative=TRUE, polygon=TRUE, type="l", lwd=2, col="#440154",
    xlab="Waveform Amplitude(%)", ylab="Elevation (m)", main="GEDIL1B rxwaveform: Grass", ylim=c(200,260), add = TRUE)

3.2 Plot GEDIL2A Elevation and Height Metrics

#plot waveform with GEDIL2A relative height metrics
plotWFMetrics(gedilevel1B, gedilevel2A, shotnum_forest, rh=c(25, 50, 75, 90), customylim = c(200, 240), main = 'Forest', add = TRUE, colBG = "#FDE725")
##
  |
  |                                                                      |   0%
  |
  |=========                                                             |  12%
  |
  |==================                                                    |  25%
  |
  |==========================                                            |  38%
  |
  |===================================                                   |  50%
  |
  |============================================                          |  62%
  |
  |====================================================                  |  75%
  |
  |=============================================================         |  88%
  |
  |======================================================================| 100%

plotWFMetrics(gedilevel1B, gedilevel2A, shotnum_edge, rh=c(25, 50, 75, 90), customylim = c(200, 240), main = 'Edge', add = TRUE, colBG = "#21908D")
##
  |
  |                                                                      |   0%
  |
  |=========                                                             |  12%
  |
  |==================                                                    |  25%
  |
  |==========================                                            |  38%
  |
  |===================================                                   |  50%
  |
  |============================================                          |  62%
  |
  |====================================================                  |  75%
  |
  |=============================================================         |  88%
  |
  |======================================================================| 100%

plotWFMetrics(gedilevel1B, gedilevel2A, shotnum_grass, rh=c(25, 50, 75, 90), customylim = c(200, 240), main = 'grass', add = TRUE, colBG = "#440154")
##
  |
  |                                                                      |   0%
  |
  |=========                                                             |  12%
  |
  |==================                                                    |  25%
  |
  |==========================                                            |  38%
  |
  |===================================                                   |  50%
  |
  |============================================                          |  62%
  |
  |====================================================                  |  75%
  |
  |=============================================================         |  88%
  |
  |======================================================================| 100%

3.3 Plot GEDIL2B Plant Area Index (PAI) and Plant Area Volume Density (PAVD)

#get canopy cover and vertical profile metrics
level2BVPM <- getLevel2BVPM(gedilevel2B)
##
  |
  |                                                                      |   0%
  |
  |=========                                                             |  12%
  |
  |==================                                                    |  25%
  |
  |==========================                                            |  38%
  |
  |===================================                                   |  50%
  |
  |============================================                          |  62%
  |
  |====================================================                  |  75%
  |
  |=============================================================         |  88%
  |
  |======================================================================| 100%
#check the data structure and 3 first values
head(level2BVPM, 3) 
##        beam       shot_number algorithmrun_flag l2b_quality_flag delta_time
## 1: BEAM0000 84280000300247456                 1                1   76817129
## 2: BEAM0000 84280000300247457                 1                1   76817129
## 3: BEAM0000 84280000300247458                 1                1   76817129
##    sensitivity solar_elevation latitude_lastbin latitude_bin0 longitude_bin0
## 1:   0.9446448       -41.60833        0.2771651     0.2771795      -74.99974
## 2:   0.9768031       -41.60883        0.2767428     0.2767589      -74.99945
## 3:   0.9755690       -41.60933        0.2763217     0.2763395      -74.99915
##    longitude_lastbin elev_highestreturn elev_lowestmode rh100      pai
## 1:         -74.99973           215.9003        193.7073  2219 4.205245
## 2:         -74.99944           217.4226        193.6978  2371 2.380235
## 3:         -74.99914           221.4247        194.3747  2704 2.826008
##    fhd_normal omega pgap_theta     cover
## 1:   3.001456     1  0.1213672 0.8760028
## 2:   3.095614     1  0.3031006 0.6948133
## 3:   3.235472     1  0.2423805 0.7553517
# check the dimensions
dim(level2BVPM)
## [1] 3403   19
#filtering for quality_flag == 1
level2BVPM <- level2BVPM[l2b_quality_flag == 1]
dim(level2BVPM)
## [1] 3320   19
#removing NA values
level2BVPM$pai[level2BVPM$pai==-9999]<-NA # assigning NA to -9999
dim(level2BVPM)
## [1] 3320   19
#get pai profile metrics
level2BPAIProfile <- getLevel2BPAIProfile(gedilevel2B)
##
  |
  |                                                                      |   0%
  |
  |=========                                                             |  12%
  |
  |==================                                                    |  25%
  |
  |==========================                                            |  38%
  |
  |===================================                                   |  50%
  |
  |============================================                          |  62%
  |
  |====================================================                  |  75%
  |
  |=============================================================         |  88%
  |
  |======================================================================| 100%
dim(level2BPAIProfile)
## [1] 3403   41
#filtering for quality_flag == 1
level2BPAIProfile <- level2BPAIProfile[l2b_quality_flag == 1]
dim(level2BPAIProfile)
## [1] 3320   41
#convert shot_number from integer to character
level2BPAIProfile$shot_number <- paste0(level2BPAIProfile$shot_number)

#select the previous shots to examine their PAI
smallLevel2BPAIProfile <- level2BPAIProfile[level2BPAIProfile$shot_number %in% sample_shotnums, ]
sample_colors <- c("#FDE725", "#21908D",  "#440154")

#checking the data structure
head(smallLevel2BPAIProfile)[, 1:18]
##        beam       shot_number algorithmrun_flag l2b_quality_flag delta_time
## 1: BEAM0101 84280500300242755                 1                1   76817130
## 2: BEAM0101 84280500300242757                 1                1   76817130
## 3: BEAM0101 84280500300242759                 1                1   76817130
##    lat_lowestmode lon_lowestmode elev_highestreturn elev_lowestmode
## 1:      0.2362527      -74.99871           238.1628        211.9363
## 2:      0.2354130      -74.99811           233.5918        216.8920
## 3:      0.2345715      -74.99752           222.5892        218.8159
##    height_lastbin height_bin0  pai_z0_5m pai_z5_10m pai_z10_15m pai_z15_20m
## 1:     -11.120045   26.354006 4.50259972   4.127542    2.777871  1.67275357
## 2:     -18.360970   16.714741 1.45967424   1.211199    0.574635  0.01757622
## 3:      -6.554091    3.788747 0.07969099   0.000000    0.000000  0.00000000
##    pai_z20_25m pai_z25_30m pai_z30_35m
## 1:    0.895469 0.006062514           0
## 2:    0.000000 0.000000000           0
## 3:    0.000000 0.000000000           0
#plot some shots
PAIplot <- plotPAIshots(smallLevel2BPAIProfile, col_vector = sample_colors, cutUntil0 = TRUE, idcolumn = 'shot_number')

#plot Level2B PAI Profile
gPAIprofile <- plotPAIProfile(level2BPAIProfile, beam="BEAM0101", elev=TRUE)

#get pavd profile metrics
level2BPAVDProfile <- getLevel2BPAVDProfile(gedilevel2B)
##
  |
  |                                                                      |   0%
  |
  |=========                                                             |  12%
  |
  |==================                                                    |  25%
  |
  |==========================                                            |  38%
  |
  |===================================                                   |  50%
  |
  |============================================                          |  62%
  |
  |====================================================                  |  75%
  |
  |=============================================================         |  88%
  |
  |======================================================================| 100%
dim(level2BPAVDProfile)
## [1] 3403   41
#filtering for quality_flag == 1
level2BPAVDProfile <- level2BPAVDProfile[l2b_quality_flag == 1]
dim(level2BPAVDProfile)
## [1] 3320   41
#convert shot_number from integer to character
level2BPAVDProfile$shot_number <- paste0(level2BPAVDProfile$shot_number)

#select the previous shots to examine their PAI
smallLevel2BPVDProfile <- level2BPAVDProfile[as.character(level2BPAVDProfile$shot_number) %in% sample_shotnums, ]
sample_colors <- c("#FDE725", "#21908D",  "#440154")

#checking the data structure
head(smallLevel2BPVDProfile)[, 1:18]
##        beam       shot_number algorithmrun_flag l2b_quality_flag delta_time
## 1: BEAM0101 84280500300242755                 1                1   76817130
## 2: BEAM0101 84280500300242757                 1                1   76817130
## 3: BEAM0101 84280500300242759                 1                1   76817130
##    lat_lowestmode lon_lowestmode elev_highestreturn elev_lowestmode
## 1:      0.2362527      -74.99871           238.1628        211.9363
## 2:      0.2354130      -74.99811           233.5918        216.8920
## 3:      0.2345715      -74.99752           222.5892        218.8159
##    height_lastbin height_bin0 pavd_z0_5m pavd_z5_10m pavd_z10_15m pavd_z15_20m
## 1:     -11.120045   26.354006 0.07501164 0.172472835    0.2454788    0.1882402
## 2:     -18.360970   16.714741 0.04969503 0.088503920    0.1193623    0.0574635
## 3:      -6.554091    3.788747 0.01593820 0.007969098    0.0000000    0.0000000
##    pavd_z20_25m pavd_z25_30m pavd_z30_35m
## 1:  0.166669115    0.0895469 0.0006062514
## 2:  0.001757622    0.0000000 0.0000000000
## 3:  0.000000000    0.0000000 0.0000000000
#plot some shots
PAVDplot <- plotPAVDshots(smallLevel2BPVDProfile, col_vector = sample_colors, cutUntil0 = TRUE, idcolumn = 'shot_number')

#plot Level2B PAVD Profile
gPAVDprofile<-plotPAVDProfile(level2BPAVDProfile, beam="BEAM0101", elev=TRUE)

SECTION 4: Create Raster Layers

4.1 Compute Descriptive Statistics

#compute a series of statistics for GEDI relative height metrics
rh100metrics_st <- polyStatsLevel2AM(level2AM_qual, func=SetOfMetrics(rh100))
print(rh100metrics_st) # Check the results
##     min   max    mean       sd
## 1: 3.77 45.38 31.1662 5.485769
#compute a series of statistics for GEDI canopy cover metrics
cover_metrics_st <- polyStatsLevel2BVPM(level2BVPM, func=SetOfMetrics(cover))
print(cover_metrics_st) # Check the results
##            min       max      mean        sd
## 1: 0.000909579 0.9775591 0.7713407 0.2403696

4.1 Compute Descriptive Height Statistics

#compute a series of statistics for GEDI relative height metrics
rh100metrics <- gridStatsLevel2AM(level2AM_qual, func=SetOfMetrics(rh100), res=0.010)
rh100metrics # the results here are rasters
## class      : RasterBrick
## dimensions : 18, 12, 216, 4  (nrow, ncol, ncell, nlayers)
## resolution : 0.01, 0.01  (x, y)
## extent     : -74.9999, -74.8799, 0.05794554, 0.2379455  (xmin, xmax, ymin, ymax)
## crs        : NA
## source     : memory
## names      :       min,       max,      mean,        sd
## min values :  3.770000, 27.450001, 13.343846,  2.528121
## max values : 33.889999, 45.380001, 36.730000,  8.796759
#plot rh100 stats
rh100maps <- levelplot(rh100metrics,
                     layout=c(4, 1),
                     margin=FALSE,
                     xlab = "Longitude (degree)", ylab = "Latitude (degree)",
                     colorkey=list(
                       space='right',
                       labels=list(at=seq(0, 46, 2), font=4),
                       axis.line=list(col='black'),
                       width=1),
                     par.settings=list(
                       strip.border=list(col='gray'),
                       strip.background=list(col='gray'),
                       axis.line=list(col='gray')
                     ),
                     scales=list(draw=TRUE),
                     col.regions=viridis,
                     at=seq(0, 46, len=101),
                     names.attr=c("rh100 min","rh100 max","rh100 mean", "rh100 sd"))
rh100maps

#plot rh100 stats over the map
rh100metrics@crs <- CRS("+proj=longlat +datum=WGS84 +no_defs")
sample_level2AM_qual <- level2AM_qual[sample(size = 100, nrow(level2AM_qual)), ]
rhrastvalues <- unique(as.numeric(rh100metrics@data@values))
rhPal<-  colorNumeric(palette = "viridis", reverse = FALSE,
                      domain = rhrastvalues, na.color = "transparent")


leaflet() %>% addTiles() %>%

  addRasterImage(rh100metrics[['mean']], colors = rhPal, opacity = .7, group = "Mean") %>%
  addRasterImage(rh100metrics[['min']], colors = rhPal, opacity = .7, group = "Min") %>%
  addRasterImage(rh100metrics[['max']], colors = rhPal, opacity = .7, group = "Max") %>%
  addRasterImage(rh100metrics[['sd']], colors = rhPal, opacity = .7, group = "SD") %>%


  addLegend(pal = rhPal, values = rhrastvalues,
            position = 'topleft',
            title= ""#, opacity = .3
            #, labFormat = labelFormat(transform = function(x) sort(x, decreasing = TRUE))
  ) %>%
     addCircleMarkers(data = sample_level2AM_qual, lng = sample_level2AM_qual$lon_lowestmode,
                   lat = sample_level2AM_qual$lat_lowestmode,
                   popup = paste0('RH100:', round(sample_level2AM_qual$rh100, 2),'<br>',
                                  'Shot:', sample_level2AM_qual$shot_number),
                   radius = .2, group = 'GEDI',
                   color = ~rhPal(sample_level2AM_qual$rh100))  %>%

  addLayersControl(position = 'topleft',
                   overlayGroups = c('Mean', 'Min', 'Max', 'SD', 'GEDI'),
                   options = layersControlOptions(collapsed = FALSE),
                   baseGroups = c("OpenStreetMap", "Esri.WorldImagery")) %>%
  addProviderTiles( "Esri.WorldImagery", group = "Esri.WorldImagery" )
## Warning in showSRID(uprojargs, format = "PROJ", multiline = "NO"): Discarded
## ellps WGS 84 in CRS definition: +proj=merc +a=6378137 +b=6378137 +lat_ts=0
## +lon_0=0 +x_0=0 +y_0=0 +k=1 +units=m +nadgrids=@null +wktext +no_defs
## Warning in showSRID(uprojargs, format = "PROJ", multiline = "NO"): Discarded
## datum WGS_1984 in CRS definition

4.2 Compute Descriptive Canopy Cover Stats

#compute a series of statistics for GEDI plant area index
pai_metrics <- gridStatsLevel2BVPM(level2BVPM = level2BVPM, func=SetOfMetrics(pai), res=0.010)
fhd_metrics <- gridStatsLevel2BVPM(level2BVPM = level2BVPM, func=SetOfMetrics(fhd_normal), res=0.010)
cover_metrics <- gridStatsLevel2BVPM(level2BVPM = level2BVPM, func=SetOfMetrics(cover), res=0.010)
#plot pai stats
pai_maps <- levelplot(pai_metrics,
                    layout=c(4, 1),
                    margin=FALSE,
                    xlab = "Longitude (degree)", ylab = "Latitude (degree)",
                    colorkey=list(
                      space='right',
                      labels=list(at=seq(0, 8, 0.2), font=4),
                      axis.line=list(col='black'),
                      width=1),
                    par.settings=list(
                      strip.border=list(col='gray'),
                      strip.background=list(col='gray'),
                      axis.line=list(col='gray')
                    ),
                    scales=list(draw=TRUE),
                    col.regions=viridis,
                    at=seq(0, 8, len=101),
                    names.attr=c("PAI min","PAI max","PAI mean", "PAI sd"))
pai_maps

#plot pai stats over the map
pai_metrics@crs <- CRS("+proj=longlat +datum=WGS84 +no_defs")
sample_level2BPAI <- level2BPAIProfile[sample(size = 100, nrow(level2BPAIProfile)), ]
pairastvalues <- unique(as.numeric(pai_metrics@data@values))
paiPal<-  colorNumeric(palette = "viridis",
                      domain = pairastvalues, na.color = "transparent")


leaflet() %>% addTiles() %>%
  addRasterImage(pai_metrics[['mean']], colors = rhPal, opacity = .7, group = "Mean") %>%
  addRasterImage(pai_metrics[['min']], colors = rhPal, opacity = .7, group = "Min") %>%
  addRasterImage(pai_metrics[['max']], colors = rhPal, opacity = .7, group = "Max") %>%
  addRasterImage(pai_metrics[['sd']], colors = rhPal, opacity = .7, group = "SD") %>%


  addLegend(pal = paiPal, values = pairastvalues,
            position = 'topleft',
            title= ""#, opacity = .
  ) %>%
     addCircleMarkers(data = sample_level2BPAI, lng = sample_level2BPAI$lon_lowestmode,
                   lat = sample_level2BPAI$lat_lowestmode,
                   popup = paste0('PAI-5m: ' , round(sample_level2BPAI$pai_z0_5m, 2),'<br>',
                                  'Shot: ', sample_level2BPAI$shot_number),
                   radius = .2, group = 'GEDI',
                   color = ~paiPal(sample_level2BPAI$pai_z0_5m))  %>%

  addLayersControl(position = 'topleft',
                   overlayGroups = c('Mean', 'Min', 'Max', 'SD', 'GEDI'),
                   options = layersControlOptions(collapsed = FALSE),
                   baseGroups = c("OpenStreetMap", "Esri.WorldImagery")) %>%
  addProviderTiles( "Esri.WorldImagery", group = "Esri.WorldImagery" )
#plot fhd stats
fhd_maps <- levelplot(fhd_metrics,
                    layout=c(2, 2),
                    margin=FALSE,
                    xlab = "Longitude (degree)", ylab = "Latitude (degree)",
                    colorkey=list(
                      space='right',
                      labels=list(at=seq(0, 3.5, 0.25), font=4),
                      axis.line=list(col='black'),
                      width=1),
                    par.settings=list(
                      strip.border=list(col='gray'),
                      strip.background=list(col='gray'),
                      axis.line=list(col='gray')
                    ),
                    scales=list(draw=TRUE),
                    col.regions=viridis,
                    at=seq(0, 3.5, len=101),
                    names.attr=c("FHD min","FHD max","FHD mean", "FHD sd"))
fhd_maps

#plot cover stats
cover_maps <- levelplot(cover_metrics,
                    layout=c(2, 2),
                    margin=FALSE,
                    xlab = "Longitude (degree)", ylab = "Latitude (degree)",
                    colorkey=list(
                      space='right',
                      labels=list(at=seq(0, 1, 0.05), font=4),
                      axis.line=list(col='black'),
                      width=1),
                    par.settings=list(
                      strip.border=list(col='gray'),
                      strip.background=list(col='gray'),
                      axis.line=list(col='gray')
                    ),
                    scales=list(draw=TRUE),
                    col.regions=viridis,
                    at=seq(0, 1, len=101),
                    names.attr=c("CCF min","CCF max","CCF mean", "CCF sd"))
cover_maps

SECTION 5: Compare two vegetation covers

5.1 Define polygon areas

We are definig two contrasting polygons, humand grasslands and forests. For this you can read polygons in WKT format (text) or from vecor/raster files using functions like raster::raster(“mytif.tif”) or rgdal::readOGR(‘mygpkg.gpkg’)

# Provide study areas
wkt_deforested <- "POLYGON((-75.001718%200.23935,-74.998442%200.238726,-74.991579%200.237478,-74.986899%200.236073,-74.981439%200.234513,-74.983311%200.231079,-74.980659%200.22749,-74.975355%200.22234,-74.966463%200.225149,-74.965371%200.229831,-74.969115%200.234513,-74.977695%200.241223,-74.977383%200.244188,-74.969895%200.240287,-74.967243%200.23701,-74.958976%200.234357,-74.958352%200.228114,-74.962564%200.22156,-74.966463%200.220936,-74.967555%200.217971,-74.966775%200.214538,-74.966307%200.213913,-74.96116%200.20892,-74.955076%200.209232,-74.957884%200.204862,-74.96038%200.200961,-74.962252%200.195343,-74.966151%200.196591,-74.967711%200.201117,-74.967087%200.205799,-74.965527%200.209856,-74.970675%200.217035,-74.973764%200.219715,-74.977586%200.221665,-74.982343%200.22346,-74.983045%200.226113,-74.986165%200.229,-74.991157%200.231341,-74.989861%200.230941,-74.99232%200.231292,-74.99388%200.231994,-74.994582%200.234608,-74.995791%200.236481,-74.998794%200.237066,-75.000822%200.237027,-75.001718%200.23935))"

wkt_forest <-  "POLYGON((-74.927009%200.104278,-74.892711%200.149222,-74.830976%200.101157,-74.874627%200.049971,-74.927009%200.104278))"


# Convert WKT into R polygons
forest_poly <- readWKT(gsub('%20', ' ', wkt_forest))
deforested_poly <- readWKT(gsub('%20', ' ', wkt_deforested))

## You can load your own polygons loading shapefiles:
# forest_poly <- rgdal::readOGR('/path/to', 'myforestlayer.shp')
# Filter coordinates
deforested_pts <- level2AM_qual[na.omit(raster::extract(deforested_poly, level2AM_qual[, c('lon_lowestmode', 'lat_lowestmode')]))$point.ID,]
forest_pts <- level2AM_qual[na.omit(raster::extract(forest_poly, level2AM_qual[, c('lon_lowestmode', 'lat_lowestmode')]))$point.ID,]

ptsPal<-  colorNumeric(palette = "viridis", reverse = TRUE,
                      domain = c(deforested_pts$rh100, forest_pts$rh100), na.color = "transparent")


# Mapping the areas and GEDI points
leaflet() %>%
  addCircleMarkers(data = deforested_pts,
                   lng = deforested_pts$lon_lowestmode,
                   lat = deforested_pts$lat_lowestmode,
                   radius = 1, opacity = 0.5,
                   color = ~ptsPal(rh100),
                   popup = paste0("shot_number: ", deforested_pts$shot_number)) %>%
  addCircleMarkers(data = forest_pts,
                   forest_pts$lon_lowestmode,
                   forest_pts$lat_lowestmode,
                   radius = 1, opacity = 0.5,
                   color = ~ptsPal(rh100),
                   popup = paste0("shot_number: ", forest_pts$shot_number))  %>%
  addPolygons(data = deforested_poly, color = 'red', fill = NA, group = 'Deforested' ) %>%
  addPolygons(data = forest_poly, color = 'blue', fill = NA, group = 'Forest' ) %>%
    addLegend(pal = paiPal, values = pairastvalues,
            position = 'topleft', title= "Height (m)") %>%
  addScaleBar(options = list(imperial = FALSE)) %>%
  addProviderTiles(providers$Esri.WorldImagery) %>%
  addLegend(colors = c("blue", "red"), labels = c("Forest", "Grasslands"))

Explore how the energy curves looks in both polygons

rh100Pts <- rbind.data.frame(cbind(deforested_pts, label = 'Deforested', clr = "#F8766D"),
                                   cbind(forest_pts, label = 'Forest', clr = '#00BFC4'))

plotRh100 <- plot_rh_profiles(rh100Pts, idcolumn = 'shot_number', col_vector = rh100Pts$clr, bunch = TRUE)

Now we will compare the profiles and max. height (rh100) in both polygons

ggp <- ggplot(data = rh100Pts[, c('shot_number', 'rh100', 'label', 'clr')],
       aes(x = label, y = rh100, color = label)) +
  geom_boxplot(fill=0.8, outlier.size=0.5, color = 'black') +
  geom_point(position=position_jitter(width=0.3), alpha=0.8) +
  guides(colour = guide_legend(override.aes = list(alpha=1))) +
  labs(x = "Area", y = "Heigh (m)t", title = 'Max. heigth', color = 'Area')
ggp

SECTION 6: Model applications

Species Distribution Model fake species (created artificial presence/absence points in QGIS)

sp_locations <- readOGR(dsn= "data/fake_sp.shp")
## OGR data source with driver: ESRI Shapefile
## Source: "/Users/megreich/Documents/Emma/NAU/GEDI_workshop/workshop/data/fake_sp.shp", layer: "fake_sp"
## with 40 features
## It has 2 fields
## Integer64 fields read as strings:  id
head(sp_locations)
##   id Presence
## 1  1        1
## 2  2        1
## 3  3        1
## 4  4        1
## 5  5        1
## 6  6        1
plot(sp_locations[sp_locations$Presence == 0,],col='red',pch=16)

plot(sp_locations[sp_locations$Presence == 1,],col='blue',pch=16)

Add environmental variable raster brick and then add GEDI rasters to it

bioclim_data <- brick(paste0(wd,"/data/bioclim_data_AOI2.tif"))

#change names of layers
names(bioclim_data) <- c("MeanAnnualTemp", "TempAnnualRange", "MeanAnnualPrecip")

#take a look
plot(bioclim_data$MeanAnnualTemp)

plot(bioclim_data$TempAnnualRange)

plot(bioclim_data$MeanAnnualPrecip)

max_canopyht <- rh100metrics$max

#look at extents
bioclim_data
## class      : RasterBrick
## dimensions : 6, 4, 24, 3  (nrow, ncol, ncell, nlayers)
## resolution : 0.04166667, 0.04166667  (x, y)
## extent     : -75, -74.83333, 0.04166667, 0.2916667  (xmin, xmax, ymin, ymax)
## crs        : +proj=longlat +datum=WGS84 +no_defs
## source     : bioclim_data_AOI2.tif
## names      : MeanAnnualTemp, TempAnnualRange, MeanAnnualPrecip
## min values :            258,             111,             2784
## max values :            260,             113,             2902
rh100metrics
## class      : RasterBrick
## dimensions : 18, 12, 216, 4  (nrow, ncol, ncell, nlayers)
## resolution : 0.01, 0.01  (x, y)
## extent     : -74.9999, -74.8799, 0.05794554, 0.2379455  (xmin, xmax, ymin, ymax)
## crs        : +proj=longlat +datum=WGS84 +no_defs
## source     : memory
## names      :       min,       max,      mean,        sd
## min values :  3.770000, 27.450001, 13.343846,  2.528121
## max values : 33.889999, 45.380001, 36.730000,  8.796759
#resample so that we can combine rasters
#rh100_resample<-resample(rh100metrics$max, bioclim_data, method="ngb")
bioclim_resample<-resample(bioclim_data, max_canopyht, method="ngb")

#add the GEDI max canopy height layer to the Bioclim raster stack
preds <- addLayer(bioclim_resample, max_canopyht)
plot(preds$max)

Using the sdm package to run a Linear model and Random Forest model

#get data into format for sdm package and add 60 random background points
d<- sdmData(Presence~ ., sp_locations, predictors=preds) 
## Loading required package: gbm
## Warning: package 'gbm' was built under R version 4.0.2
## Loaded gbm 2.1.8
## Loading required package: tree
## Warning: package 'tree' was built under R version 4.0.2
## Loading required package: mda
## Warning: package 'mda' was built under R version 4.0.2
## Loading required package: class
## Loaded mda 0.5-2
## Loading required package: mgcv
## Loading required package: nlme
##
## Attaching package: 'nlme'
## The following object is masked from 'package:raster':
##
##     getData
## This is mgcv 1.8-31. For overview type 'help("mgcv-package")'.
## Loading required package: glmnet
## Warning: package 'glmnet' was built under R version 4.0.2
## Loading required package: Matrix
##
## Attaching package: 'Matrix'
## The following objects are masked from 'package:tidyr':
##
##     expand, pack, unpack
## Loaded glmnet 4.1-1
## Loading required package: earth
## Warning: package 'earth' was built under R version 4.0.2
## Loading required package: Formula
## Warning: package 'Formula' was built under R version 4.0.2
## Loading required package: plotmo
## Warning: package 'plotmo' was built under R version 4.0.2
## Loading required package: plotrix
## Warning: package 'plotrix' was built under R version 4.0.2
## Loading required package: TeachingDemos
## Warning: package 'TeachingDemos' was built under R version 4.0.2
## Loading required package: rJava
## Warning: package 'rJava' was built under R version 4.0.2
## Loading required package: RSNNS
## Warning: package 'RSNNS' was built under R version 4.0.2
## Loading required package: Rcpp
## Loading required package: ranger
##
## Attaching package: 'ranger'
## The following object is masked from 'package:randomForest':
##
##     importance
## Loading required package: rpart
## Loading required package: kernlab
## Warning: package 'kernlab' was built under R version 4.0.2
##
## Attaching package: 'kernlab'
## The following object is masked from 'package:ggplot2':
##
##     alpha
## The following objects are masked from 'package:raster':
##
##     buffer, rotated
#what models are available in the sdm package?
getmethodNames()
## $bioclim
## [1] "bioclim" "Bioclim"
##
## $bioclim.dismo
## [1] "bioclim.dismo" "BioclimDismo"  "bioclimDismo"  "bioclim.dis"
## [5] "bioclimD"
##
## $brt
## [1] "brt" "BRT" "gbm" "GBM"
##
## $cart
## [1] "cart" "CART" "tree"
##
## $domain.dismo
## [1] "domain.dismo" "DomainDismo"  "domainDismo"  "domain.dis"   "domainD"
##
## $fda
## [1] "fda" "FDA"
##
## $gam
## [1] "gam" "GAM"
##
## $glm
## [1] "glm" "GLM" "lm"
##
## $glmnet
## [1] "glmnet"     "GLMNET"     "glmelastic" "glmlasso"
##
## $glmpoly
## [1] "glmpoly"       "glmpolynomial" "glmp"
##
## $mahal.dismo
## [1] "mahal.dismo"       "MahalDismo"        "mahalanoubisDismo"
## [4] "mahal.dis"         "mahalD"
##
## $mars
## [1] "mars"  "MARS"  "earth"
##
## $maxent
## [1] "maxent"     "Maxent"     "MAXENT"     "entropy"    "maxentropy"
##
## $maxlike
## [1] "maxlike" "MaxLike"
##
## $mda
## [1] "mda" "MDA"
##
## $mlp
## [1] "mlp"      "MLP"      "nnet.mlp" "nnetMLP"
##
## $ranger
## [1] "ranger"       "rangerRF"     "rangerForest"
##
## $rbf
## [1] "rbf"      "RBF"      "nnet.rbf" "nnetRbf"
##
## $rf
## [1] "rf"           "RF"           "randomForest" "rforest"
##
## $rpart
## [1] "rpart" "RPART"
##
## $svm
## [1] "svm"  "SVM"  "ksvm"
#Let's try 2: a generalized linear model and a random forest model
m1 <- sdm(Presence~.,data=d,methods=c('glm', 'rf'))
## Loading required package: parallel
m1
## class                                 : sdmModels
## ========================================================
## number of species                     :  1
## number of modelling methods           :  2
## names of modelling methods            :  glm, rf
## ------------------------------------------
## model run success percentage (per species)  :
## ------------------------------------------
## method          Presence
## ----------------------
## glm        :        100   %
## rf         :        100   %
##
## ###################################################################
## model performance (per species), using training test dataset:
## -------------------------------------------------------------------------------
##
##  ## species   :  Presence
## =========================
##
## methods    :     AUC     |     COR     |     TSS     |     Deviance
## -------------------------------------------------------------------------
## glm        :     0.98    |     0.85    |     0.9     |     0.38
## rf         :     0.98    |     0.9     |     0.9     |     0.46
#evaluate with ROC curves
roc(m1)

#variable importance Plot
getVarImp(m1)
##
## The variable importance for all the models are combined (averaged)...
## Relative Variable Importance List
## =============================================================
## method              : Permutation based on two metrics (Pearson Correlation and AUC)
## number of variables :  4
## variable names      :  MeanAnnualTemp, TempAnnualRange, MeanAnnualPrecip, max, ...
## number of models    :  2
## =============================================================
## Summary of relative variable importance
## ----------------------------------------------
## Based on Correlation metric:
## ----------------------------------------------
## MeanAnnualTemp      : [] (3.5 %)
## TempAnnualRange     : ***[***--] (14 %)
## MeanAnnualPrecip    : *******************[**-] (44.2 %)
## max                 : ] (2 %)
## =============================================================
## Based on AUC metric:
## ----------------------------------------------
## MeanAnnualTemp     [: ***---] (6.1 %)
## TempAnnualRange     : [******-----] (13.6 %)
## MeanAnnualPrecip    : ***************[**-] (35.9 %)
## max                 [*] (1.3 %)
## =============================================================
#create outputs folder for prediction files
if(!file.exists("outputs")) {dir.create("outputs")}

#predict
p1 <- predict(m1,newdata=preds, filename="./outputs/sdm_prediction", overwrite=T) 
## Warning in .rasterFromRasterFile(grdfile, band = band, objecttype, ...): size of
## values file does not match the number of cells (given the data type)

## Warning in .rasterFromRasterFile(grdfile, band = band, objecttype, ...): size of
## values file does not match the number of cells (given the data type)
plot(p1)