ICESat2VegR: An R Package for NASA’s Ice, Cloud, and Elevation Satellite (ICESat-2) Data Processing and Visualization for Land and Vegetation Applications.
Authors: Carlos Alberto Silva and Caio Hamamura
The ICESat2VegR package provides functions for downloading, reading, visualizing, processing and exporting NASA’s ICESat-2 ATL03 (Global Geolocated Photon Data) and ATL08 (Land and Vegetation Height) products for Land and Vegetation Applications in R environment.
# The r-universe version (recommended for the latest version)
install.packages("ICESat2VegR", , repos = c("https://caiohamamura.r-universe.dev", "https://cloud.r-project.org"))
# The CRAN version
install.packages("ICESat2VegR")library(ICESat2VegR)This package uses three Python packages through reticulate:
- earthaccess: allows reading directly from the cloud
- h5py: for reading hdf5 content from the cloud
- earthengine-api: integration with Google Earth Engine for sampling and extracting raster data and upscalling models.
For configuring the package you can use:
ICESat2VegR_configure()This will install miniconda if not available and the necessary packages.
- There are some issues regarding some Python packages not being compatible with the Python version. The above configure function will also try to update python version in that case.
- The configure function may warn you about the need to restart R after installing some packages, restart if needed.
There are two different ways of working with the ICESat-2 data. Locally or using cloud computing. Common users should work locally, unless they are working within an AWS cloud computing within zone us-west-2.
As we will be working with multiple h5 granules, we will be using
lapply for reading and extracting information from the granules.
If you are working with a single granule you can execute the simpler
instructions without lapply as per the function documentation examples
instead.
# Load the ICESat2VegR package
library(ICESat2VegR)
# Set output directory
outdir <- tempdir()
# Download example dataset
ATLAS_dataDownload(
"https://github.com/carlos-alberto-silva/ICESat2VegR/releases/download/example_datasets/Study_Site.zip",
outdir
)
# Unzip the example dataset
unzip(file.path(outdir, "Study_Site.zip"), exdir = outdir)# Specifying bounding box coordinates
lower_left_lon <- -96.0
lower_left_lat <- 40.0
upper_right_lon <- -100
upper_right_lat <- 42.0
# Specifying the date range
daterange <- c("2021-10-02", "2021-10-03")First we need to find the granules:
atl03_granules_local <- ATLAS_dataFinder(
short_name = "ATL03",
lower_left_lon,
lower_left_lat,
upper_right_lon,
upper_right_lat,
version = "006",
daterange = daterange,
persist = TRUE,
cloud_computing = FALSE
)
head(atl03_granules_local)## C2596864127-NSIDC_CPRD
## [1,] "https://data.nsidc.earthdatacloud.nasa.gov/nsidc-cumulus-prod-protected/ATLAS/ATL03/006/2021/10/02/ATL03_20211002001658_01461302_006_01.h5"
## [2,] "https://data.nsidc.earthdatacloud.nasa.gov/nsidc-cumulus-prod-protected/ATLAS/ATL03/006/2021/10/02/ATL03_20211002004127_01461306_006_01.h5"
## [3,] "https://data.nsidc.earthdatacloud.nasa.gov/nsidc-cumulus-prod-protected/ATLAS/ATL03/006/2021/10/02/ATL03_20211002015115_01471302_006_01.h5"
## [4,] "https://data.nsidc.earthdatacloud.nasa.gov/nsidc-cumulus-prod-protected/ATLAS/ATL03/006/2021/10/02/ATL03_20211002021545_01471306_006_01.h5"
## [5,] "https://data.nsidc.earthdatacloud.nasa.gov/nsidc-cumulus-prod-protected/ATLAS/ATL03/006/2021/10/02/ATL03_20211002032533_01481302_006_01.h5"
## [6,] "https://data.nsidc.earthdatacloud.nasa.gov/nsidc-cumulus-prod-protected/ATLAS/ATL03/006/2021/10/02/ATL03_20211002035002_01481306_006_01.h5"
Now we download the granules:
# Download all granules
ATLAS_dataDownload(atl03_granules_local, outdir)And then we can open and work with them
## ATL03
# Read the granules
atl03_files <- list.files(outdir, "ATL03.*h5", full.names = TRUE)
atl03_h5 <- lapply(atl03_files, ATL03_read)
## ATL08
# Read the granules
atl08_files <- list.files(outdir, "ATL08.*h5", full.names = TRUE)
atl08_h5 <- lapply(atl08_files, ATL08_read)
# List groups within first file of atl08_h5
atl08_h5[[1]]$ls()## [1] "METADATA" "ancillary_data" "gt1r"
## [4] "gt2r" "gt3r" "orbit_info"
## [7] "quality_assessment"
atl03_granules_cloud <- ATLAS_dataFinder(
short_name = "ATL03",
lower_left_lon,
lower_left_lat,
upper_right_lon,
upper_right_lat,
version = "006",
daterange = daterange,
persist = TRUE,
cloud_computing = TRUE
)In cloud computing you don’t need to download data, instead you can read the data and start working with it.
# Read the granule (the ATL03_read can only read one granule per read)
atl03_h5_cloud <- ATL03_read(atl03_granules_cloud[1])
# List groups within the h5 in cloud
atl03_h5_cloud$beams## gt1l gt1r gt2l gt2r gt3l gt3rclose(atl03_h5_cloud)# ATL03 seg attributes
atl03_seg_att_ls <- lapply(
atl03_h5,
ATL03_seg_attributes_dt,
attributes = c("delta_time", "solar_elevation", "pitch", "h_ph", "ref_elev")
)
atl03_seg_dt <- rbindlist2(atl03_seg_att_ls)
# Remove segments above 20km
atl03_seg_dt <- atl03_seg_dt[h_ph < 20000]
head(atl03_seg_dt)| delta_time | solar_elevation | pitch | h_ph | ref_elev | reference_photon_lon | reference_photon_lat | beam | strong_beam |
|---|---|---|---|---|---|---|---|---|
| 40393396 | 15.39806 | -0.1688279 | 253.2678 | 1.564179 | -83.17184 | 31.96591 | gt1r | TRUE |
| 40393396 | 15.39806 | -0.1688280 | 268.9599 | 1.564179 | -83.17186 | 31.96609 | gt1r | TRUE |
| 40393396 | 15.39806 | -0.1688281 | 355.6263 | 1.564179 | -83.17188 | 31.96627 | gt1r | TRUE |
| 40393396 | 15.39806 | -0.1688282 | 276.9350 | 1.564180 | -83.17190 | 31.96645 | gt1r | TRUE |
| 40393396 | 15.39805 | -0.1688282 | 359.7021 | 1.564180 | -83.17192 | 31.96663 | gt1r | TRUE |
| 40393396 | 15.39805 | -0.1688283 | 292.5831 | 1.564180 | -83.17194 | 31.96681 | gt1r | TRUE |
# ATL08 seg attributes
atl08_seg_att_ls <- lapply(
atl08_h5,
ATL08_seg_attributes_dt,
attributes = c("h_canopy", "h_te_mean", "terrain_slope", "canopy_openness", "night_flag")
)
atl08_seg_dt <- rbindlist2(atl08_seg_att_ls)
# Consider only segment with h_canopy < 100 and terrain height < 20000
atl08_seg_dt <- atl08_seg_dt[h_canopy < 100 & h_te_mean < 20000]
head(atl08_seg_dt)| latitude | longitude | beam | strong_beam | h_canopy | h_te_mean | terrain_slope | canopy_openness | night_flag |
|---|---|---|---|---|---|---|---|---|
| 32.04510 | -83.18090 | gt1r | TRUE | 13.640770 | 47.51598 | 0.0830011 | 3.445780 | 0 |
| 32.04690 | -83.18111 | gt1r | TRUE | 11.407394 | 44.67390 | -0.0053365 | 2.606891 | 0 |
| 32.09549 | -83.18666 | gt1r | TRUE | 10.392395 | 65.23853 | 0.0053522 | 2.132361 | 0 |
| 32.09639 | -83.18677 | gt1r | TRUE | 10.364945 | 65.63503 | 0.0097772 | 3.251597 | 0 |
| 32.10629 | -83.18790 | gt1r | TRUE | 14.952076 | 58.39679 | 0.0042360 | 4.113675 | 0 |
| 32.10719 | -83.18800 | gt1r | TRUE | 9.288475 | 59.01027 | 0.0017870 | 3.213291 | 0 |
layout(t(1:2))
# ATL03 height histogram
hist(atl03_seg_dt$h_ph, col = "#bd8421", xlab = "Elevation (m)", main = "ATL03 h_ph")
hist(atl08_seg_dt$h_canopy, col = "green", xlab = "Height (m)", main = "ATL08 h_canopy")The function to_vect() will return a terra::vect object.
library(terra)
blueYellowRed <- function(n) grDevices::hcl.colors(n, "RdYlBu")
set.seed(123)
mask <- sample(seq_len(nrow(atl03_seg_dt)), 50)
atl03_seg_vect <- to_vect(atl03_seg_dt)
# Plot with mapview
mapview::mapview(
atl03_seg_vect[mask],
zcol = "h_ph",
layer.name = "h_ph",
breaks = 3,
col.regions = blueYellowRed,
map.types = c("Esri.WorldImagery")
)# Extract vector from atl08_seg_dt
atl08_seg_vect <- to_vect(atl08_seg_dt)
# Palette function
greenYellowRed <- function(n) {
grDevices::hcl.colors(n, "RdYlGn")
}
# Plot with mapview
map_vect <- mapview::mapView(
atl08_seg_vect,
layer.name = "h_canopy",
zcol = "h_canopy",
col.regions = greenYellowRed,
map.types = c("Esri.WorldImagery")
)
map_vectSave vector as geopackage file. The formats supported are as from GDAL terra package.
terra::writeVector(atl03_seg_vect, file.path(outdir, "atl03_seg.gpkg"))
terra::writeVector(atl08_seg_vect, file.path(outdir, "atl08_seg.gpkg"))Single max_h_canopy:
redYellowGreen <- function(n) grDevices::hcl.colors(n, "RdYlGn")
max_h_canopy <- ATL08_seg_attributes_dt_gridStat(atl08_seg_dt, func = max(h_canopy), res = 0.01)
mapview::mapView(
max_h_canopy,
map = map_vect,
col.regions = redYellowGreen
)multiple_data <- ATL08_seg_attributes_dt_gridStat(atl08_seg_dt, func = list(
max_h_canopy = max(h_canopy),
min_h_canopy = min(h_canopy),
mean_canopy_openness = mean(canopy_openness),
mean_h_te_mean = mean(h_te_mean)
), res = 0.01)
map_vect_openness <- mapview::mapView(
atl08_seg_vect,
zcol = "canopy_openness",
layer.name = "canopy_openness",
col.regions = redYellowGreen,
map.types = c("Esri.WorldImagery")
)
blueYellowRed <- function(n) grDevices::hcl.colors(n, "RdYlBu", rev = TRUE)
map_vect_terrain <- mapview::mapView(
atl08_seg_vect,
zcol = "h_te_mean",
layer.name = "h_te_mean",
col.regions = blueYellowRed,
map.types = c("Esri.WorldImagery")
)
m1 <- mapview::mapView(multiple_data[[1]], layer.name = "Max h_canopy", map = map_vect, col.regions = redYellowGreen)
m2 <- mapview::mapView(multiple_data[[2]], layer.name = "Min h_canopy", map = map_vect, col.regions = redYellowGreen)
m3 <- mapview::mapView(multiple_data[[3]], layer.name = "Mean canopy openness", map = map_vect_openness, col.regions = redYellowGreen)
m4 <- mapview::mapView(multiple_data[[4]], layer.name = "Mean h_te_mean", col.regions = blueYellowRed, map = map_vect_terrain)
leafsync::sync(m1, m2, m3, m4)Now we will use the clipping functions. There are two ways of clipping data in ICESat2VegR:
- Clipping raw hdf5 data from ATL03 and ATL08 files
- Clipping extracted attributes resulting from extraction functions: -
ATL03_seg_attributes_dt-ATL03_photon_attributes_dt-ATL08_seg_attributes_dt-ATL03_ATL08_photons_attributes_dt_join
The second method is preffered as it is faster and more efficient because it does not require reading the hdf5 and will clip only the subset of extracted attributes, while hdf5 has a lot of attributes that might not be needed.
There are multiple variants using either the bounding box or the geomtry to clip the data suffixed with _clipBox or _clipGeometry:
ATL03_h5_clipBoxATL03_h5_clipGeometryATL03_seg_attributes_dt_clipBoxATL03_seg_attributes_dt_clipGeometryATL03_photon_attributes_dt_clipBoxATL03_photon_attributes_dt_clipGeometryATL08_h5_clipBoxATL08_h5_clipGeometryATL08_seg_attributes_dt_clipBoxATL08_seg_attributes_dt_clipGeometryATL03_ATL08_photons_attributes_dt_join_clipBoxATL03_ATL08_photons_attributes_dt_join_clipGeometry
In the following two sections there are two small examples on how to clip the raw HDF5 and the extracted attributes.
leaflet_available <- require("leaflet")
if (!leaflet_available) stop("leaflet not found!")
# Define bbox
clip_region <- terra::ext(-83.2, -83.14, 32.12, 32.18)
# Define hdf5 output file
output <- tempfile(fileext = ".h5")
# Clip the data for only the first atl08 file
atl08_clipped <- ATL08_h5_clipBox(atl08_h5[[1]], output, bbox = clip_region)
atl08_seg_dt <- ATL08_seg_attributes_dt(atl08_h5[[1]], attributes = c("h_canopy"))
atl08_seg_dt_clip <- ATL08_seg_attributes_dt(atl08_clipped, attributes = c("h_canopy"))
# Display location of clipped data
atl08_seg_vect <- to_vect(atl08_seg_dt)
atl08_seg_clip_vect <- to_vect(atl08_seg_dt_clip)
bbox <- terra::vect(terra::ext(atl08_seg_clip_vect), crs = "epsg:4326")
centroid <- terra::geom(terra::centroids(bbox))
map1 <- mapview::mapview(atl08_seg_clip_vect, col.regions = "yellow", alpha.regions = 1, lwd = 5, map.types = c("Esri.WorldImagery"), alpha = 0, cex = 2, legend = FALSE)
# Final map
final_map <- map1@map %>%
leaflet::addCircleMarkers(data = atl08_seg_vect, radius = 2) %>%
leaflet::addPolygons(
data = bbox, fillOpacity = 0, weight = 3, color = "white", opacity = 1, dashArray = "5, 1, 0"
) %>%
leaflet::addLegend(
position = "topright",
colors = c("blue", "yellow", "white"),
labels = c("atl08_segment", "atl08_segment_clipped", "bbox"),
opacity = 1
) %>%
leaflet::setView(centroid[, "x"][[1]], centroid[, "y"][[1]], zoom = 13)aoi <- file.path(outdir, "example_aoi.gpkg")
aoi_vect <- terra::vect(aoi)
centroid <- terra::geom(terra::centroids(aoi_vect))
# Extract the h_canopy attribute from the first ATL08 file
atl08_seg_dt <- lapply(atl08_h5, ATL08_seg_attributes_dt, attributes = c("h_canopy"))
atl08_seg_dt <- rbindlist2(atl08_seg_dt)
atl08_seg_vect <- to_vect(atl08_seg_dt)
# Clip the data for only the first atl08 file
atl08_seg_dt_clip <- ATL08_seg_attributes_dt_clipGeometry(atl08_seg_dt, aoi_vect, split_by = "id")
atl08_seg_clip_vect <- to_vect(atl08_seg_dt_clip)
colors <- c("#00FF00", "#FF00FF")
map1 <- mapview::mapview(
atl08_seg_clip_vect,
alpha = 0,
col.regions = colors,
alpha.regions = 1,
zcol = "poly_id",
lwd = 5, map.types = c("Esri.WorldImagery"),
cex = 2,
legend = FALSE
)
# Final map
final_map <- map1@map %>%
leaflet::addCircleMarkers(data = atl08_seg_vect, color = "blue", radius = 2) %>%
leaflet::addPolygons(
data = aoi_vect, fillOpacity = 0, weight = 3, color = colors, opacity = 1, dashArray = "5, 1, 0"
) %>%
leaflet::addLegend(
position = "topright",
colors = c("blue", "yellow", colors),
labels = c("atl08_segment", "atl08_segment_clipped", "aoi_1", "aoi_2"),
opacity = 1
) %>%
leaflet::setView(centroid[, "x"][[1]], centroid[, "y"][[1]], zoom = 13)# Herein as we are working with list of h5 files we will need
# to loop over each file and extract the attributes and then
# concatenate them with rbindlist2
atl03_atl08_dts <- list()
for (ii in seq_along(atl03_h5)) {
atl03_file <- atl03_h5[[ii]]
atl08_file <- atl08_h5[[ii]]
atl03_atl08_dts[[ii]] <- ATL03_ATL08_photons_attributes_dt_join(
atl03_file,
atl08_file
)
}
atl03_atl08_dt <- rbindlist2(atl03_atl08_dts)
head(atl03_atl08_dt)| ph_segment_id | lon_ph | lat_ph | h_ph | quality_ph | solar_elevation | dist_ph_along | dist_ph_across | night_flag | classed_pc_indx | classed_pc_flag | ph_h | d_flag | delta_time | orbit_number | beam | strong_beam |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 177488 | -83.17570 | 31.99959 | 44.61499 | 0 | 15.39738 | 3762.072 | 3169.208 | 0 | 44 | 2 | 4.3425255 | 1 | 40393396 | 3208 | gt1r | TRUE |
| 177488 | -83.17570 | 31.99959 | 48.81370 | 0 | 15.39738 | 3762.074 | 3169.180 | 0 | 45 | 3 | 8.6167412 | 1 | 40393396 | 3208 | gt1r | TRUE |
| 177488 | -83.17571 | 31.99962 | 43.38353 | 0 | 15.39738 | 3765.626 | 3169.205 | 0 | 49 | 2 | 3.2494583 | 1 | 40393396 | 3208 | gt1r | TRUE |
| 177488 | -83.17571 | 31.99964 | 48.24021 | 0 | 15.39738 | 3767.768 | 3169.175 | 0 | 56 | 3 | 8.1592712 | 1 | 40393396 | 3208 | gt1r | TRUE |
| 177488 | -83.17571 | 31.99964 | 56.24585 | 0 | 15.39738 | 3767.773 | 3169.122 | 0 | 57 | 3 | 16.2089348 | 1 | 40393396 | 3208 | gt1r | TRUE |
| 177488 | -83.17571 | 31.99965 | 40.30040 | 0 | 15.39738 | 3769.191 | 3169.229 | 0 | 62 | 1 | 0.2985229 | 1 | 40393396 | 3208 | gt1r | TRUE |
oldpar <- par(no.readonly = TRUE)
par(oma = c(0, 0, 0, 0))
par(mar = c(2, 3, 1, 1))
layout(matrix(c(1, 2), ncol = 1))
plot(
atl03_atl08_dt[orbit_number == 3208],
y = "h_ph",
colors = c("gray", "#bd8421", "forestgreen", "green"),
xlim = c(25500, 28500),
beam = "gt2r",
cex = 0.5,
pch = 16
)
par(mar = c(3, 3, 1, 1))
plot(
atl03_atl08_dt[orbit_number == 3208],
y = "ph_h",
colors = c("gray", "#bd8421", "forestgreen", "green"),
xlim = c(25500, 28500),
beam = "gt2r",
cex = 0.5,
pch = 16,
legend = FALSE
)
par(
oldpar
)h_canopy <- ATL03_ATL08_photons_attributes_dt_gridStat(
atl03_atl08_dt[ph_h < 50 & ph_h > 0],
func = list(
h_canopy = quantile(ph_h, 0.98),
count = .N
),
res = 0.01
)
plot(h_canopy,
col = viridis::inferno(100),
xlab = "Langitude (degree)",
ylab = "Latitude (degree)",
ylim = c(32.1, 32.4)
)In this section, we will demonstrate how to use the
ATL03_ATL08_segment_create function from the ICESat2VegR package.
This function is used to compute segment IDs for ICESat-2 ATL03 and
ATL08 data and create segments based on a specified segment length.
# Herein as we are working with list of h5 files we will need
# to loop over each file and extract the attributes and then
# concatenate them with rbindlist2
atl03_atl08_dts <- list()
for (ii in seq_along(atl03_h5)) {
atl03_file <- atl03_h5[[ii]]
atl08_file <- atl08_h5[[ii]]
atl03_atl08_dts[[ii]] <- ATL03_ATL08_photons_attributes_dt_join(
atl03_file,
atl08_file
)
}
atl03_atl08_dt <- rbindlist2(atl03_atl08_dts)
head(atl03_atl08_dt)Now, we use the ATL03_ATL08_segment_create function to create segments
with a specified segment length.
atl03_atl08_photons_grouped_dt <- ATL03_ATL08_segment_create(atl03_atl08_dt,
segment_length = 30,
centroid = "mean",
output = NA,
overwrite = FALSE
)atl03_atl08_seg_dt <- ATL03_ATL08_compute_seg_attributes_dt_segStat(
atl03_atl08_photons_grouped_dt,
list(
h_canopy_ge0 = quantile(ph_h, 0.98),
h_canopy_gt0 = quantile(ph_h[ph_h > 0], 0.98),
n_ground = sum(classed_pc_flag == 1),
n_mid_canopy = sum(classed_pc_flag == 2),
n_top_canopy = sum(classed_pc_flag == 3),
n_canopy_total = sum(classed_pc_flag >= 2)
),
ph_class = c(1, 2, 3)
)
head(atl03_atl08_seg_dt)| segment_id | beam | longitude | latitude | h_canopy_ge0 | h_canopy_gt0 | n_ground | n_mid_canopy | n_top_canopy | n_canopy_total |
|---|---|---|---|---|---|---|---|---|---|
| 126 | gt1r | -83.17571 | 31.99965 | 15.611358 | 15.611358 | 1 | 7 | 3 | 10 |
| 127 | gt1r | -83.17574 | 31.99986 | 18.311493 | 18.311493 | 1 | 12 | 1 | 13 |
| 128 | gt1r | -83.17577 | 32.00011 | 12.236551 | 12.236551 | 1 | 8 | 0 | 8 |
| 293 | gt1r | -83.18086 | 32.04469 | 3.518969 | 3.518969 | 0 | 1 | 5 | 6 |
| 294 | gt1r | -83.18087 | 32.04482 | 5.167805 | 5.167805 | 7 | 4 | 2 | 6 |
| 295 | gt1r | -83.18091 | 32.04511 | 12.113018 | 12.258518 | 12 | 10 | 0 | 10 |
Now, we convert the data.table to a SpatVector object.
atl03_atl08_vect <- to_vect(atl03_atl08_seg_dt[h_canopy_gt0 <= 31])Finally, we visualize the SpatVector interactively using mapview.
centroid <- atl03_atl08_dt[, .(x = mean(lon_ph), y = mean(lat_ph))]
map_output <- mapview::mapview(
atl03_atl08_vect,
zcol = "h_canopy_gt0",
col.regions = grDevices::hcl.colors(9, "RdYlGn"),
alpha = 0,
layer.name = "h_canopy",
map.types = c("Esri.WorldImagery"),
cex = 4
)@map %>%
setView(lng = centroid$x, lat = centroid$y, zoom = 13)
##
## map_outputHere, we will create a simple model to predict the AGBD of ATL08 data
based on the height of the canopy. We will use the randomForest
package to create the model.
Let’s assume we have the following tabular data from ATL08 and field data.
# For the sake of the example, we will train and test the model with the same data
library(randomForest)
h_canopy <- c(
13.9, 3.1, 2.2, 4.6, 21.6,
7.2, 5, 7.7, 0.8, 9.7,
11, 11.3, 15.5, 5.1, 10.4,
0.6, 14.6, 13.3, 9.8, 14.7
)
agbd <- c(
144.8, 27.5, 51.6, 60.5, 232.3,
102.8, 33.1, 91.3, 23, 120.1,
125.7, 127.2, 147.4, 48.8, 103.3,
55.9, 181.8, 139.9, 120.1, 162.8
)
set.seed(172783946)
model <- randomForest::randomForest(data.frame(h_canopy = h_canopy), agbd)Now we will predict the data for the entire ATL08 dataset, this can be as large as you want. This will create or append the predicted values to an H5 file.
out_h5 <- tempfile(fileext = ".h5")
for (atl08_h5_item in atl08_h5) {
atl08_seg_dt <- ATL08_seg_attributes_dt(atl08_h5_item, attributes = c("h_canopy"))
atl08_seg_dt[h_canopy > 100, h_canopy := NA_real_]
atl08_seg_dt <- na.omit(atl08_seg_dt)
predicted_h5 <- predict_h5(model, atl08_seg_dt, out_h5)
}output_raster <- tempfile(fileext = ".tif")
x <- predicted_h5[["longitude"]][]
y <- predicted_h5[["latitude"]][]
bbox <- terra::ext(min(x), max(x), min(y), max(y))
# Creates the raster with statistics
res <- 0.005
rasterize_h5(predicted_h5, output_raster, bbox = bbox, res = res)
# Open the raster by file path
forest_height_palette <- c("#ffffff", "#4d994d", "#004d00")
# Open band 2 only (mean AGBD)
library(leaflet)
stars_rast <- stars::read_stars(output_raster, RasterIO = list(bands = 2))
res_map <- mapview::mapview(
stars_rast,
layer.name = "AGBD mean",
col.regions = forest_height_palette,
na.alpha = 0.1,
map = leaflet::leaflet() %>% leaflet::addProviderTiles("Esri.WorldImagery")
)
#res_mapIn this example we will model the h_canopy of the ICESat-2 using only
the Harmonized Landsat Sentinel-2 dataset (hls).
ee_initialize()atl08_seg_dt <- lapply(atl08_h5, ATL08_seg_attributes_dt, attribute = "h_canopy")
atl08_seg_dt <- rbindlist2(atl08_seg_dt)
# Remove h_canopy values that are above 100m
atl08_seg_dt <- atl08_seg_dt[h_canopy < 100]
head(atl08_seg_dt)| latitude | longitude | beam | strong_beam | h_canopy |
|---|---|---|---|---|
| 32.35687 | -83.13213 | gt1r | TRUE | 3.375763 |
| 32.34967 | -83.13296 | gt1r | TRUE | 3.256882 |
| 32.34877 | -83.13306 | gt1r | TRUE | 4.273857 |
| 32.34787 | -83.13316 | gt1r | TRUE | 2.742630 |
| 32.34427 | -83.13358 | gt1r | TRUE | 6.318420 |
| 32.34337 | -83.13367 | gt1r | TRUE | 16.272888 |
library(terra)
library(leaflet)
atl08_seg_vect <- to_vect(atl08_seg_dt)
centroid <- geom(centroids(vect(ext(atl08_seg_vect))))
map <- terra::plet(atl08_seg_vect, "h_canopy", col = grDevices::hcl.colors(9, "RdYlGn"), tiles = c("Esri.WorldImagery")) %>%
setView(lng = centroid[, "x"][[1]], lat = centroid[, "y"][[1]], zoom = 12)
# maphls_search <- search_datasets("Harmonized", "Landsat")
hls_search## id
## <char>
## 1: NASA_HLS_HLSL30_v002
## title
## <char>
## 1: HLSL30: HLS-2 Landsat Operational Land Imager Surface Reflectance and TOA Brightness Daily Global 30m
## description
## <char>
## 1: The Harmonized Landsat Sentinel-2 (HLS) project provides consistent surface reflectance (SR) and top of atmosphere (TOA) brightness data from a virtual constellation of satellite sensors. The Operational Land Imager (OLI) is housed aboard the joint NASA/USGS Landsat 8 and Landsat 9 satellites, while the Multi-Spectral …
hls_id <- get_catalog_id(hls_search$id)
hls_id## [1] "NASA/HLS/HLSL30/v002"
hls_collection <- ee$ImageCollection(hls_id)
names(hls_collection)## [1] "B1" "B2" "B3" "B4" "B5" "B6" "B7" "B9" "B10"
## [10] "B11" "Fmask" "SZA" "SAA" "VZA" "VAA"
bbox <- terra::ext(atl08_seg_vect)
aoi <- ee$Geometry$BBox(
west = bbox$xmin,
south = bbox$ymin,
east = bbox$xmax,
north = bbox$ymax
)
hls <- hls_collection$
filterDate("2019-04-01", "2019-05-31")$
filterBounds(aoi)$
map(function(x) x$updateMask(!(x[["Fmask"]] & 14)))$
median()
hls_unmasked <- hls_collection$
filterDate("2019-04-01", "2019-05-31")$
filterBounds(aoi)$
median()# Rename bands
hls_unmasked <- hls_unmasked[["B2", "B3", "B4", "B5", "B6", "B7"]]
names(hls_unmasked) <- c("blue", "green", "red", "nir", "swir1", "swir2")
hls <- hls[["B2", "B3", "B4", "B5", "B6", "B7"]]
names(hls) <- c("blue", "green", "red", "nir", "swir1", "swir2")
# Add evi
nir <- hls[["nir"]]
red <- hls[["red"]]
blue <- hls[["blue"]]
hls[["evi"]] <- (2.5 * (nir - red)) / (nir + 6 * red - 7.5 * blue + 1)
print(hls)## ee.image.Image
##
## Bands
## [1] "blue" "green" "red" "nir" "swir1" "swir2" "evi"
library(leaflet)
forest_height_palette <- c("#ffffff", "#99cc99", "#006600", "#004d00")
palette_colors <- colorNumeric(forest_height_palette, range(atl08_seg_dt$h_canopy))(atl08_seg_dt[order(h_canopy), h_canopy])
centroid <- mean(bbox)
map <- leaflet::leaflet() |>
addEEImage(hls, bands = list("red", "green", "blue"), group = "masked", max = 0.6) |>
addEEImage(hls_unmasked, bands = list("red", "green", "blue"), group = "unmasked", max = 0.6) |>
setView(lng = centroid[1], lat = centroid[2], zoom = 13) |>
addLayersControl(
baseGroups = c("unmasked", "masked"),
options = layersControlOptions(collapsed = FALSE)
)
# mapFor each segment extract the hls data:
extracted_dt <- seg_gee_ancillary_dt_extract(hls, atl08_seg_vect)
head(extracted_dt)| idx | beam | h_canopy | strong_beam | red | green | blue | nir | swir1 | swir2 | evi |
|---|---|---|---|---|---|---|---|---|---|---|
| 1 | gt1r | 3.375763 | TRUE | 0.0652 | 0.0675 | 0.0390 | 0.3125 | 0.2251 | 0.1265 | 0.4381023 |
| 2 | gt1r | 3.256882 | TRUE | 0.1435 | 0.1206 | 0.0768 | 0.2780 | 0.3518 | 0.3000 | 0.2151312 |
| 3 | gt1r | 4.273857 | TRUE | 0.1973 | 0.1675 | 0.1060 | 0.3066 | 0.4445 | 0.3807 | 0.1611714 |
| 4 | gt1r | 2.742630 | TRUE | 0.0667 | 0.0723 | 0.0337 | 0.2721 | 0.2431 | 0.1444 | 0.3617344 |
| 5 | gt1r | 6.318420 | TRUE | 0.0489 | 0.0488 | 0.0263 | 0.2571 | 0.1929 | 0.0994 | 0.3846296 |
| 6 | gt1r | 16.272888 | TRUE | 0.0719 | 0.0624 | 0.0323 | 0.2634 | 0.2349 | 0.1326 | 0.3295928 |
bandNames <- names(hls)
x <- extracted_dt[, .SD, .SDcols = bandNames]
y <- extracted_dt[["h_canopy"]]
# Mask the NA values
na_mask <- y < 100
x <- x[na_mask]
y <- y[na_mask]
set.seed(1)
rf_model <- randomForest::randomForest(x, y, ntree = 300, mtry = 1)
print(rf_model)##
## Call:
## randomForest(x = x, y = y, ntree = 300, mtry = 1)
## Type of random forest: regression
## Number of trees: 300
## No. of variables tried at each split: 1
##
## Mean of squared residuals: 48.81011
## % Var explained: 16.5
library(randomForest)
rf_importance <- importance(rf_model)
barplot(rf_importance[, "IncNodePurity"], main = "Variable importance (Increase Node Purity)")gee_model <- build_ee_forest(rf_model)
result <- hls$classify(gee_model)
min_hcanopy <- min(atl08_seg_dt$h_canopy)
max_hcanopy <- 20
atl08_seg_vect$h_canopy <- round(atl08_seg_vect$h_canopy, 3) # Round off to 3 decimal places
map <- terra::plet(
atl08_seg_vect,
"h_canopy",
palette_colors,
tiles = ""
)
modelled_map <- terra::plet(
atl08_seg_vect,
"h_canopy",
palette_colors,
tiles = ""
) |>
addEEImage(
hls,
bands = c("red", "green", "blue"),
group = "hls",
min = min_hcanopy,
max = 0.6
) |>
addEEImage(
result,
bands = "classification",
group = "classification",
min = min_hcanopy,
max = max_hcanopy,
palette = forest_height_palette
) |>
leaflet::addLegend(
pal = colorNumeric(forest_height_palette, seq(min_hcanopy, max_hcanopy)),
values = seq(min_hcanopy, max_hcanopy, length = 3),
opacity = 1,
title = "h_canopy",
position = "bottomleft",
) |>
setView(lng = centroid[1], lat = centroid[2], zoom = 12) |>
addLayersControl(
overlayGroups = c("classification"),
options = layersControlOptions(collapsed = FALSE)
)
modelled_mapDo not forget to close the files to properly release them.
lapply(atl03_h5, close)
## Non lapply single file
# close(atl03_h5)lapply(atl08_h5, close)
## Non lapply single file
# close(atl08_h5)We gratefully acknowledge funding from NASA’s ICESat-2 (ICESat-2, grant 22-ICESat2_22-0006), Carbon Monitoring System (CMS, grant 22-CMS22-0015) and Commercial Smallsat Data Scientific Analysis(CSDSA, grant 22-CSDSA22_2-0080).
Please report any issue regarding the ICESat2VegR package to Dr. Silva (c.silva@ufl.edu) or Caio Hamamura (hamamura.caio@ifsp.edu).
Silva,C.A; Hamamura,C. ICESat2VegR: An R Package for NASA’s Ice, Cloud, and Elevation Satellite (ICESat-2) Data Processing and Visualization for Terrestrial Applications.version 0.0.1, accessed on Jun. 13 2024, available at: https://CRAN.R-project.org/package=ICESat2VegR
ICESat2VegR package comes with no guarantee, expressed or implied, and the authors hold no responsibility for its use or reliability of its outputs.















