rGEDI: An R Package for NASA’s Global Ecosystem Dynamics Investigation (GEDI) Data Visualization and Processing

Carlos Alberto Silva, Caio Hamamura, Ruben Valbuena, Steve Hancock, Adrian Cardil, Eben N. Broadbent, Danilo R. A. de Almeida, Celso H. L. Silva Junior and Carine Klauberg

The rGEDI package provides functions for i) downloading, ii) visualizing, iii) clipping, iv) gridding, iv) simulating and v) exporting GEDI data.

Getting Started

Installation

#The CRAN version:
install.packages("rGEDI")

#The development version:
library(devtools)
devtools::install_git("https://github.com/carlos-alberto-silva/rGEDI", dependencies = TRUE)

# loading rGEDI package
library(rGEDI)

Find GEDI data within your study area (GEDI finder tool)

# Study area boundary box coordinates
ul_lat<- -44.0654
lr_lat<- -44.17246
ul_lon<- -13.76913
lr_lon<- -13.67646

# Specifying the date range
daterange=c("2019-07-01","2020-05-22")

# Get path to GEDI data
gLevel1B<-gedifinder(product="GEDI01_B",ul_lat, ul_lon, lr_lat, lr_lon,version="001",daterange=daterange)
gLevel2A<-gedifinder(product="GEDI02_A",ul_lat, ul_lon, lr_lat, lr_lon,version="001",daterange=daterange)
gLevel2B<-gedifinder(product="GEDI02_B",ul_lat, ul_lon, lr_lat, lr_lon,version="001",daterange=daterange)

# Set output dir for downloading the files
outdir=getwd()

Downloading GEDI data

# Downloading GEDI data
gediDownload(filepath=gLevel1B,outdir=outdir)
gediDownload(filepath=gLevel2A,outdir=outdir)
gediDownload(filepath=gLevel2B,outdir=outdir)

#** Herein, we are using only a GEDI sample dataset for this tutorial.
# downloading zip file
zipfile = file.path(outdir, "examples.zip")
download.file("https://github.com/carlos-alberto-silva/rGEDI/releases/download/datasets/examples.zip", destfile=zipfile)

# unzip file
unzip(zipfile, exdir=outdir)

Reading GEDI data

# Reading GEDI data
gedilevel1b<-readLevel1B(level1Bpath = file.path(outdir, "GEDI01_B_2019108080338_O01964_T05337_02_003_01_sub.h5"))
gedilevel2a<-readLevel2A(level2Apath = file.path(outdir, "GEDI02_A_2019108080338_O01964_T05337_02_001_01_sub.h5"))
gedilevel2b<-readLevel2B(level2Bpath = file.path(outdir, "GEDI02_B_2019108080338_O01964_T05337_02_001_01_sub.h5"))

Get GEDI Pulse Full-Waveform Geolocation (GEDI Level1B)

level1bGeo<-getLevel1BGeo(level1b=gedilevel1b,select=c("elevation_bin0"))
head(level1bGeo)
shot_number latitude_bin0 latitude_lastbin longitude_bin0 longitude_lastbin elevation_bin0
1 0.00 -13.76 -13.76 -44.17 -44.17 784.83
2 0.00 -13.76 -13.76 -44.17 -44.17 799.05
3 0.00 -13.76 -13.76 -44.17 -44.17 814.46
4 0.00 -13.76 -13.76 -44.17 -44.17 820.14
5 0.00 -13.76 -13.76 -44.17 -44.17 821.70
6 0.00 -13.76 -13.76 -44.17 -44.17 823.25
# Converting shot_number as "integer64" to "character"
level1bGeo$shot_number<-paste0(level1bGeo$shot_number)

# Converting level1bGeo as data.table to SpatialPointsDataFrame
library(sp)
level1bGeo_spdf<-SpatialPointsDataFrame(cbind(level1bGeo$longitude_bin0, level1bGeo$latitude_bin0),
                                        data=level1bGeo)

# Exporting level1bGeo as ESRI Shapefile
raster::shapefile(level1bGeo_spdf,paste0(outdir,"\\GEDI01_B_2019108080338_O01964_T05337_02_003_01_sub"))
library(leaflet)
library(leafsync)

m = leaflet() %>%
  addCircleMarkers(level1bGeo$longitude_bin0,
                  level1bGeo$latitude_bin0,
                  radius = 1,
                  opacity = 1,
                  color = "red")  %>%
  addScaleBar(options = list(imperial = FALSE)) %>%
  addProviderTiles(providers$Esri.WorldImagery) %>%
  addLegend(colors = "red", labels= "Samples",title ="GEDI Level1B")

m

Get GEDI Pulse Full-waveform (GEDI Level1B)

# Extracting GEDI full-waveform for a giving shotnumber
wf <- getLevel1BWF(gedilevel1b, shot_number="19640521100108408")

oldpar <- par()
par(mfrow = c(1,2), mar=c(4,4,1,1), cex.axis = 1.5)

plot(wf, relative=FALSE, polygon=TRUE, type="l", lwd=2, col="forestgreen",
    xlab="Waveform Amplitude", ylab="Elevation (m)")
grid()
plot(wf, relative=TRUE, polygon=FALSE, type="l", lwd=2, col="forestgreen",
    xlab="Waveform Amplitude (%)", ylab="Elevation (m)")
grid()

par(oldpar)

Get GEDI Elevation and Height Metrics (GEDI Level2A)

# Get GEDI Elevation and Height Metrics
level2AM<-getLevel2AM(gedilevel2a)
head(level2AM[,c("beam","shot_number","elev_highestreturn","elev_lowestmode","rh100")])
beam shot_number elev_highestreturn elev_lowestmode rh100
1 BEAM0000 0.00 740.75 736.33 4.41
2 BEAM0000 0.00 756.09 746.76 9.32
3 BEAM0000 0.00 770.34 763.15 7.19
4 BEAM0000 0.00 775.98 770.67 5.31
5 BEAM0000 0.00 777.84 773.08 4.75
6 BEAM0000 0.00 778.72 773.70 5.01
# Converting shot_number as "integer64" to "character"
level2AM$shot_number<-paste0(level2AM$shot_number)

# Converting Elevation and Height Metrics as data.table to SpatialPointsDataFrame
level2AM_spdf<-SpatialPointsDataFrame(cbind(level2AM$lon_lowestmode,level2AM$lat_lowestmode),
                                        data=level2AM)

# Exporting Elevation and Height Metrics as ESRI Shapefile
raster::shapefile(level2AM_spdf,paste0(outdir,"\\GEDI02_A_2019108080338_O01964_T05337_02_001_01_sub"))

Plot waveform with RH metrics

shot_number = "19640521100108408"

plotWFMetrics(gedilevel1b, gedilevel2a, shot_number, rh=c(25, 50, 75, 90))

Get GEDI Vegetation Biophysical Variables (GEDI Level2B)

level2BVPM<-getLevel2BVPM(gedilevel2b)
head(level2BVPM[,c("beam","shot_number","pai","fhd_normal","omega","pgap_theta","cover")])
beam shot_number pai fhd_normal omega pgap_theta cover
1 BEAM0000 0.00 0.01 0.64 1.00 1.00 0.00
2 BEAM0000 0.00 0.09 2.26 1.00 0.96 0.04
3 BEAM0000 0.00 0.30 1.89 1.00 0.86 0.14
4 BEAM0000 0.00 0.08 1.66 1.00 0.96 0.04
5 BEAM0000 0.00 0.02 1.58 1.00 0.99 0.01
6 BEAM0000 0.00 0.02 1.25 1.00 0.99 0.01
# Converting shot_number as "integer64" to "character"
level2BVPM$shot_number<-paste0(level2BVPM$shot_number)

# Converting GEDI Vegetation Profile Biophysical Variables as data.table to SpatialPointsDataFrame
level2BVPM_spdf<-SpatialPointsDataFrame(cbind(level2BVPM$longitude_lastbin,level2BVPM$latitude_lastbin),data=level2BVPM)

# Exporting GEDI Vegetation Profile Biophysical Variables as ESRI Shapefile
raster::shapefile(level2BVPM_spdf,paste0(outdir,"\\GEDI02_B_2019108080338_O01964_T05337_02_001_01_sub_VPM"))

Get Plant Area Index (PAI) and Plant Area Volume Density (PAVD) Profiles (GEDI Level2B)

level2BPAIProfile<-getLevel2BPAIProfile(gedilevel2b)
head(level2BPAIProfile[,c("beam","shot_number","pai_z0_5m","pai_z5_10m")])
beam shot_number pai_z0_5m pai_z5_10m
1 BEAM0000 0.00 0.01 -0.00
2 BEAM0000 0.00 0.09 0.06
3 BEAM0000 0.00 0.30 0.05
4 BEAM0000 0.00 0.08 0.00
5 BEAM0000 0.00 0.02 -0.00
6 BEAM0000 0.00 0.02 -0.00
level2BPAVDProfile<-getLevel2BPAVDProfile(gedilevel2b)
head(level2BPAVDProfile[,c("beam","shot_number","pavd_z0_5m","pavd_z5_10m")])
beam shot_number pavd_z0_5m pavd_z5_10m
1 BEAM0000 0.00 0.00 0.00
2 BEAM0000 0.00 0.01 0.01
3 BEAM0000 0.00 0.05 0.03
4 BEAM0000 0.00 0.02 0.01
5 BEAM0000 0.00 0.00 0.00
6 BEAM0000 0.00 0.00 0.00
# Converting shot_number as "integer64" to "character"
level2BPAIProfile$shot_number<-paste0(level2BPAIProfile$shot_number)
level2BPAVDProfile$shot_number<-paste0(level2BPAVDProfile$shot_number)

# Converting PAI and PAVD Profiles as data.table to SpatialPointsDataFrame
level2BPAIProfile_spdf<-SpatialPointsDataFrame(cbind(level2BPAIProfile$lon_lowestmode,level2BPAIProfile$lat_lowestmode),data=level2BPAIProfile)

level2BPAVDProfile_spdf<-SpatialPointsDataFrame(cbind(level2BPAVDProfile$lon_lowestmode,level2BPAVDProfile$lat_lowestmode),data=level2BPAVDProfile)

# Exporting PAI and PAVD Profiles as ESRI Shapefile.
raster::shapefile(level2BPAIProfile_spdf,paste0(outdir,"\\GEDI02_B_2019108080338_O01964_T05337_02_001_01_sub_PAIProfile"))
raster::shapefile(level2BPAVDProfile_spdf,paste0(outdir,"\\GEDI02_B_2019108080338_O01964_T05337_02_001_01_sub_PAVDProfile"))

Plot Plant Area Index (PAI) and Plant Area Volume Density (PAVD) Profiles

#specify GEDI beam
beam="BEAM0101"

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

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

Clip GEDI data (h5 files; gedi.level1b, gedi.level2a and gedi.level2b objects)

## Clip GEDI data by coordinates
# Study area boundary box
xmin = -44.15036
xmax = -44.10066
ymin = -13.75831
ymax = -13.71244

## clipping GEDI data within boundary box
level1b_clip_bb <- clipLevel1B(gedilevel1b, xmin, xmax, ymin, ymax,output=file.path(outdir, "level1b_clip_bb.h5"))
level2a_clip_bb <- clipLevel2A(gedilevel2a, xmin, xmax, ymin, ymax, output=file.path(outdir, "level2a_clip_bb.h5"))
level2b_clip_bb <- clipLevel2B(gedilevel2b, xmin, xmax, ymin, ymax,output=file.path(outdir, "level2b_clip_bb.h5"))

## Clipping GEDI data by geometry
# specify the path to shapefile for the study area
polygon_filepath <- system.file("extdata", "stands_cerrado.shp", package="rGEDI")

# Reading shapefile as SpatialPolygonsDataFrame object
library(raster)
polygon_spdf<-raster::shapefile(polygon_filepath)
head(polygon_spdf@data) # column id name "id"
id
0 1
1 2
2 3
3 4
4 5
5 6
split_by="id"

# Clipping h5 files
level1b_clip_gb <- clipLevel1BGeometry(gedilevel1b,polygon_spdf,output=file.path(outdir, "level1b_clip_gb.h5"), split_by=split_by)
level2a_clip_gb <- clipLevel2AGeometry(gedilevel2a,polygon_spdf,output=file.path(outdir, "level2a_clip_gb.h5"), split_by=split_by)
level2b_clip_gb <- clipLevel2BGeometry(gedilevel2b,polygon_spdf,output=file.path(outdir, "level2b_clip_gb.h5"), split_by=split_by)

Clip GEDI data (data.table objects)

## Clipping GEDI data within boundary box
level1bGeo_clip_bb <-clipLevel1BGeo(level1bGeo, xmin, xmax, ymin, ymax)
level2AM_clip_bb <- clipLevel2AM(level2AM, xmin, xmax, ymin, ymax)
level2BVPM_clip_bb <- clipLevel2BVPM(level2BVPM, xmin, xmax, ymin, ymax)
level1BPAIProfile_clip_bb <- clipLevel2BPAIProfile(level2BPAIProfile, xmin, xmax, ymin, ymax)
level2BPAVDProfile_clip_bb <- clipLevel2BPAVDProfile(level2BPAVDProfile, xmin, xmax, ymin, ymax)

## Clipping GEDI data by geometry
level1bGeo_clip_gb <- clipLevel1BGeoGeometry(level1bGeo,polygon_spdf, split_by=split_by)
level2AM_clip_gb <- clipLevel2AMGeometry(level2AM,polygon_spdf, split_by=split_by)
level2BVPM_clip_gb <- clipLevel2BVPMGeometry(level2BVPM,polygon_spdf, split_by=split_by)
level1BPAIProfile_clip_gb <- clipLevel2BPAIProfileGeometry(level2BPAIProfile,polygon_spdf, split_by=split_by)
level2BPAVDProfile_clip_gb <- clipLevel2BPAVDProfileGeometry(level2BPAVDProfile,polygon_spdf, split_by=split_by)


## View GEDI clipped data by bbox
m1<-leaflet() %>%
 addCircleMarkers(level2AM$lon_lowestmode,
                  level2AM$lat_lowestmode,
                  radius = 1,
                  opacity = 1,
                  color = "red")  %>%
 addCircleMarkers(level2AM_clip_bb$lon_lowestmode,
                  level2AM_clip_bb$lat_lowestmode,
                  radius = 1,
                  opacity = 1,
                  color = "green")  %>%
 addScaleBar(options = list(imperial = FALSE)) %>%
 addProviderTiles(providers$Esri.WorldImagery)  %>%
 addLegend(colors = c("red","green"), labels= c("All samples","Clip bbox"),title ="GEDI Level2A")

## View GEDI clipped data by geometry
# color palette
pal <- colorFactor(
 palette = c('blue', 'green', 'purple', 'orange',"white","black","gray","yellow"),
 domain = level2AM_clip_gb$poly_id
)

m2<-leaflet() %>%
 addCircleMarkers(level2AM$lon_lowestmode,
                  level2AM$lat_lowestmode,
                  radius = 1,
                  opacity = 1,
                  color = "red")  %>%
 addCircleMarkers(level2AM_clip_gb$lon_lowestmode,
                  level2AM_clip_gb$lat_lowestmode,
                  radius = 1,
                  opacity = 1,
                  color = pal(level2AM_clip_gb$poly_id))  %>%
 addScaleBar(options = list(imperial = FALSE)) %>%
 addPolygons(data=polygon_spdf,weight=1,col = 'white',
             opacity = 1, fillOpacity = 0) %>%
 addProviderTiles(providers$Esri.WorldImagery) %>%
 addLegend(pal = pal, values = level2AM_clip_gb$poly_id,title ="Poly IDs" )

m3 = sync(m1, m2)

m3

Compute descriptive statistics of GEDI Level2A and Level2B data

# Define your own function
mySetOfMetrics = function(x)
{
 metrics = list(
   min =min(x), # Min of x
   max = max(x), # Max of x
   mean = mean(x), # Mean of x
   sd = sd(x)# Sd of x
 )
 return(metrics)
}

# Computing the maximum of RH100 stratified by polygon
rh100max_st<-polyStatsLevel2AM(level2AM_clip_gb,func=max(rh100), id="poly_id")
head(rh100max_st)
poly_id max
1 2 12.81
2 1 12.62
3 5 9.96
4 6 8.98
5 4 10.33
6 8 8.72
# Computing a serie statistics for GEDI metrics stratified by polygon
rh100metrics_st<-polyStatsLevel2AM(level2AM_clip_gb,func=mySetOfMetrics(rh100),
                                  id="poly_id")
head(rh100metrics_st)
poly_id min max mean sd
1 2 4.08 12.81 5.51 1.45
2 1 3.78 12.62 5.51 1.75
3 5 4.12 9.96 5.10 1.20
4 6 4.64 8.98 5.60 1.02
5 4 4.38 10.33 7.91 1.76
6 8 4.45 8.72 6.14 1.10
# Computing the max of the Total Plant Area Index
pai_max<-polyStatsLevel2BVPM(level2BVPM_clip_gb,func=max(pai), id=NULL)
head(pai_max)
max
1 1.22
# Computing a serie of statistics of Canopy Cover stratified by polygon
cover_metrics_st<-polyStatsLevel2BVPM(level2BVPM_clip_gb,func=mySetOfMetrics(cover),id="poly_id")
head(cover_metrics_st)
poly_id min max mean sd
1 2 0.00 0.35 0.05 0.06
2 1 0.00 0.38 0.05 0.06
3 5 0.00 0.43 0.04 0.06
4 6 0.00 0.24 0.03 0.06
5 4 0.00 0.35 0.11 0.09
6 8 0.01 0.15 0.05 0.04

Compute Grids with descriptive statistics of GEDI-derived Elevation and Height Metrics (Level2A)

# Computing a serie of statistics of GEDI RH100 metric
rh100metrics<-gridStatsLevel2AM(level2AM = level2AM, func=mySetOfMetrics(rh100), res=0.005)
# View maps
library(rasterVis)
library(viridis)

levelplot(rh100metrics,
                    layout=c(4, 1),
                    margin=FALSE,
                    xlab = "Longitude (degree)", ylab = "Latitude (degree)",
                    colorkey=list(
                      space='right',
                      labels=list(at=seq(0, 18, 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, 18, len=101),
                    names.attr=c("rh100 min","rh100 max","rh100 mean", "rh100 sd"))