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

#Define which libraries to use
library(raster)       ## spatial tools
library(devtools)     ## tools for building and instaling 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)      ## nice plot tools
library(RColorBrewer) ## color palettes
library(rasterVis)    ## raster viz tools
library(viridis)      ## color palettes

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 5: DOWNLOAD GEDI DATA FOR A REGION OF INTEREST

5.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")

5.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") 

5.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."

5.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."

5.5: Use gedi_download to download GEDI data (DO NOT RUN – takes a while and we have some files downloades already)

#download data product to their output data directory
#This functions will download heavy files (>1GB each) that can take long time
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 2.1 Read GEDI data (DO NOT RUN)

#read the level 1B, 2A, and 2B for a single orbit
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)

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"))

###START 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

#read GEDI L2A data into data.table
level2AM <- getLevel2AM(gedilevel2A)
##
  |
  |                                                                      |   0%
  |
  |=========                                                             |  12%
  |
  |==================                                                    |  25%
  |
  |==========================                                            |  38%
  |
  |===================================                                   |  50%
  |
  |============================================                          |  62%
  |
  |====================================================                  |  75%
  |
  |=============================================================         |  88%
  |
  |======================================================================| 100%
head(level2AM, 3) ## Check the data structure
##        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
##    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
##    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
##     rh10 rh11 rh12 rh13 rh14 rh15 rh16 rh17 rh18 rh19 rh20 rh21 rh22 rh23 rh24
## 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 9.19 9.56 9.90
## 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 1.90 2.09 2.27
## 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 5.11 5.86 6.53
##     rh25  rh26  rh27  rh28  rh29  rh30  rh31  rh32  rh33  rh34  rh35  rh36
## 1: 10.23 10.57 10.87 11.13 11.39 11.65 11.91 12.14 12.32 12.55 12.74 12.92
## 2:  2.50  2.69  2.87  3.10  3.36  3.58  3.88  4.14  4.48  4.85  5.23  5.64
## 3:  7.09  7.54  7.92  8.33  8.70  9.11  9.45  9.78 10.12 10.46 10.87 11.32
##     rh37  rh38  rh39  rh40  rh41  rh42  rh43  rh44  rh45  rh46  rh47  rh48
## 1: 13.11 13.30 13.45 13.63 13.78 13.93 14.08 14.23 14.38 14.53 14.68 14.83
## 2:  6.05  6.42  6.83  7.24  7.69  8.14  8.59  9.00  9.34  9.63  9.90 10.16
## 3: 11.76 12.17 12.59 12.96 13.30 13.59 13.89 14.19 14.45 14.72 15.01 15.28
##     rh49  rh50  rh51  rh52  rh53  rh54  rh55  rh56  rh57  rh58  rh59  rh60
## 1: 14.98 15.13 15.24 15.39 15.54 15.69 15.84 15.95 16.10 16.25 16.36 16.51
## 2: 10.38 10.61 10.83 11.05 11.32 11.58 11.91 12.25 12.66 13.00 13.33 13.63
## 3: 15.54 15.80 16.06 16.32 16.58 16.88 17.14 17.37 17.59 17.78 17.97 18.19
##     rh61  rh62  rh63  rh64  rh65  rh66  rh67  rh68  rh69  rh70  rh71  rh72
## 1: 16.62 16.77 16.88 16.99 17.11 17.22 17.33 17.44 17.56 17.67 17.74 17.85
## 2: 13.89 14.16 14.38 14.60 14.83 15.01 15.20 15.43 15.61 15.80 15.99 16.21
## 3: 18.38 18.56 18.79 19.01 19.20 19.42 19.65 19.83 20.02 20.21 20.39 20.54
##     rh73  rh74  rh75  rh76  rh77  rh78  rh79  rh80  rh81  rh82  rh83  rh84
## 1: 17.93 18.04 18.15 18.23 18.34 18.41 18.53 18.60 18.71 18.83 18.90 19.01
## 2: 16.40 16.58 16.77 16.96 17.14 17.33 17.52 17.70 17.89 18.08 18.26 18.41
## 3: 20.69 20.84 20.99 21.14 21.29 21.44 21.59 21.74 21.89 22.08 22.23 22.41
##     rh85  rh86  rh87  rh88  rh89  rh90  rh91  rh92  rh93  rh94  rh95  rh96
## 1: 19.12 19.20 19.31 19.42 19.54 19.65 19.80 19.91 20.06 20.21 20.39 20.58
## 2: 18.60 18.75 18.90 19.09 19.24 19.42 19.65 19.83 20.10 20.39 20.81 21.37
## 3: 22.60 22.79 22.97 23.16 23.35 23.53 23.76 23.98 24.21 24.43 24.69 24.95
##     rh97  rh98  rh99 rh100
## 1: 20.84 21.10 21.52 22.19
## 2: 21.93 22.41 22.90 23.72
## 3: 25.25 25.66 26.26 27.05
dim(level2AM)
## [1] 3403  112
#convert shot_number from integer to character
level2AM$shot_number <- paste0(level2AM$shot_number)

#filtering for quality_flag == 1
level2AM_qual <- level2AM[quality_flag == 1]
dim(level2AM_qual)
## [1] 3323  112

2.4 Visualize location of quality filtered GEDI footprints

#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") %>%
  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.4 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", "GEDI Footprints")) 

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 raw waveforms
par(mfrow = c(1,1), mar=c(4,4,1,1), cex.axis = 1.5)

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

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

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

3.2 Plot GEDIL2A Elevation and Height Metrics

#plot waveform with GEDIL2A relative height metrics
plotWFMetrics(level1b = gedilevel1B, level2a = gedilevel2A, shot_number = shotnum_forest, rh=c(25, 50, 75, 90))
##
  |
  |                                                                      |   0%
  |
  |=========                                                             |  12%
  |
  |==================                                                    |  25%
  |
  |==========================                                            |  38%
  |
  |===================================                                   |  50%
  |
  |============================================                          |  62%
  |
  |====================================================                  |  75%
  |
  |=============================================================         |  88%
  |
  |======================================================================| 100%

plotWFMetrics(gedilevel1B, gedilevel2A, shotnum_edge, rh=c(25, 50, 75, 90))
##
  |
  |                                                                      |   0%
  |
  |=========                                                             |  12%
  |
  |==================                                                    |  25%
  |
  |==========================                                            |  38%
  |
  |===================================                                   |  50%
  |
  |============================================                          |  62%
  |
  |====================================================                  |  75%
  |
  |=============================================================         |  88%
  |
  |======================================================================| 100%

plotWFMetrics(gedilevel1B, gedilevel2A, shotnum_grass, rh=c(25, 50, 75, 90))
##
  |
  |                                                                      |   0%
  |
  |=========                                                             |  12%
  |
  |==================                                                    |  25%
  |
  |==========================                                            |  38%
  |
  |===================================                                   |  50%
  |
  |============================================                          |  62%
  |
  |====================================================                  |  75%
  |
  |=============================================================         |  88%
  |
  |======================================================================| 100%

Lets use the same elevation range to compare the previous shots

#plot waveform with GEDIL2A relative height metrics but with a standard scale

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.43 45.38 27.49649 8.093237
#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 Stats

#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 : 23, 15, 345, 4  (nrow, ncol, ncell, nlayers)
## resolution : 0.01, 0.01  (x, y)
## extent     : -74.9999, -74.8499, 0.04716706, 0.2771671  (xmin, xmax, ymin, ymax)
## crs        : NA
## source     : memory
## names      :        min,        max,       mean,         sd
## min values :  3.4300001, 19.9799995,  8.5405555,  0.4242643
## max values :   32.58000,   45.38000,   35.14636,   11.57263
#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 = TRUE,
                      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
## 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
## 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
## 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
## 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
## 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
## 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
## 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", reverse = TRUE,
                      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" )
## 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
## 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
## Warning in colors(.): Some values were outside the color scale and will be
## treated as NA
## 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
## 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
## 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
## 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
## Warning in colors(.): Some values were outside the color scale and will be
## treated as NA
## 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
## 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
#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
deforested_poly <- readWKT(gsub('%20', ' ', wkt_deforested))
forest_poly <- readWKT(gsub('%20', ' ', wkt_forest))


# 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() %>%
  addPolygons(data = deforested_poly, color = 'red', fill = NA, group = 'Deforested' ) %>%
  addPolygons(data = forest_poly, color = 'blue', fill = NA, group = 'Forest' ) %>%
  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))  %>%
    addLegend(pal = paiPal, values = pairastvalues,
            position = 'topleft', title= "Height (m)") %>%
  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"))

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

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

ggplot(data = rh100Pts[, c('shot_number', 'rh100', 'label', 'clr')],
       aes(x = label, y = label, 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 = "Height", title = 'Max. heigth', color = 'Area')

plotRh100 <- plot_rh_profiles(rh100Pts, idcolumn = 'label', col_vector = rh100Pts$clr)