Global Ecosystem Dynamics Investigation (GEDI) Level 2A Product Tutorial¶
This tutorial aims to provide information and code to help users get started working with the GEDI Level 2A (GEDI02_A) product using the MAAP. Information about the GEDI02_A product may be found at the Data Set Landing Page. We will start by importing the packages which will allow us to search for, access, explore, and visualize GEDI02_A product data.
Note: This Jupyter notebook utilizes the folium
and pystac_client
packages. If you do not have these packages installed, uncomment the lines and run the following code block.
[1]:
# !pip install folium
# !pip install pystac_client
For this tutorial, we will import boto3
, folium
, h5py
, pandas
, exists
from os.path
and Client
from pystac_client
as shown in the following codeblock.
[2]:
# Install packages
import boto3
import folium
import h5py
import os
import pandas as pd
from maap.maap import MAAP
from os.path import exists
from pystac_client import Client
Searching for and accessing GEDI02_A data¶
As of the time of the writing of this tutorial (2/10/23), two recommended ways for searching and accessing GEDI02_A data for use on the MAAP ADE are through the maap-stac
as well as through NASA’s Common Metadata Repository (CMR). The methods for using these two ways are different and documented in the following two sub-sections.
Via maap-stac
¶
To search for data from the GEDI02_A product, we will use the Client
package to open the `maap-stac
URL <https://stac.maap-project.org/>`__ and assign this to a variable (client
in this case).
[3]:
# Open the maap-stac URL with the Client package
URL = 'https://stac.maap-project.org/'
client = Client.open(URL)
Now we can use the client specified above to search for data within the GEDI02_A product. Let’s search for the first item that is found in the GEDI02_A collection and assign this to a variable (search
in this case).
[4]:
collection = 'GEDI02_A' # assign collection name
# Search for 1st item found in the collection
search = client.search(
max_items = 1,
collections = collection,
)
Let’s inspect this item using the get_all_items()
function.
[5]:
# Inspect first item
item = search.get_all_items()[0]
item
[5]:
Item: GEDI02_A_2021272190541_O15849_04_T03030_02_003_02_V002
ID: GEDI02_A_2021272190541_O15849_04_T03030_02_003_02_V002 |
Bounding Box: [-51.6720605, 52.2787758, 0.6119569, 136.2318287] |
Datetime: 2021-09-29 19:05:41+00:00 |
links: [{'rel': 'http://esipfed.org/ns/fedsearch/1.1/s3#', 'href': 's3://nasa-maap-data-store/file-staging/nasa-map/GEDI02_A___002/2021.09.29/GEDI02_A_2021272190541_O15849_04_T03030_02_003_02_V002.h5', 'title': 'File to download', 'hreflang': 'en-US'}, {'rel': 'http://esipfed.org/ns/fedsearch/1.1/data#', 'href': 'https://search.earthdata.nasa.gov/search?q=C1908348134-LPDAAC_ECS', 'hreflang': 'en-US', 'inherited': True}, {'rel': 'http://esipfed.org/ns/fedsearch/1.1/data#', 'href': 'https://e4ftl01.cr.usgs.gov/GEDI/GEDI02_A.002/', 'hreflang': 'en-US', 'inherited': True}, {'rel': 'http://esipfed.org/ns/fedsearch/1.1/metadata#', 'href': 'https://lpdaac.usgs.gov/', 'hreflang': 'en-US', 'inherited': True}, {'rel': 'http://esipfed.org/ns/fedsearch/1.1/metadata#', 'href': 'https://doi.org/10.5067/GEDI/GEDI02_A.002', 'hreflang': 'en-US', 'inherited': True}, {'rel': 'http://esipfed.org/ns/fedsearch/1.1/documentation#', 'href': 'https://lpdaac.usgs.gov/documents/982/gedi_l2a_dictionary_P003_v2.html', 'hreflang': 'en-US', 'inherited': True}, {'rel': 'http://esipfed.org/ns/fedsearch/1.1/documentation#', 'href': 'https://doi.org/10.5067/DOC/GEDI/GEDI_WF_ATBD.001', 'hreflang': 'en-US', 'inherited': True}, {'rel': 'http://esipfed.org/ns/fedsearch/1.1/documentation#', 'href': 'https://doi.org/10.5067/DOC/GEDI/GEDI_WFGEO_ATBD.001', 'hreflang': 'en-US', 'inherited': True}, {'rel': 'http://esipfed.org/ns/fedsearch/1.1/metadata#', 'href': 'https://gedi.umd.edu/', 'hreflang': 'en-US', 'inherited': True}, {'rel': 'http://esipfed.org/ns/fedsearch/1.1/metadata#', 'href': 'https://gedi.umd.edu/', 'hreflang': 'en-US', 'inherited': True}, {'rel': 'http://esipfed.org/ns/fedsearch/1.1/documentation#', 'href': 'https://lpdaac.usgs.gov/documents/998/GEDI02_UserGuide_V21.pdf', 'hreflang': 'en-US', 'inherited': True}, {'rel': 'http://esipfed.org/ns/fedsearch/1.1/documentation#', 'href': 'https://git.earthdata.nasa.gov/projects/LPDUR/repos/gedi-subsetter/browse', 'hreflang': 'en-US', 'inherited': True}, {'rel': 'http://esipfed.org/ns/fedsearch/1.1/documentation#', 'href': 'https://lpdaac.usgs.gov/resources/e-learning/accessing-and-analyzing-gedi-lidar-data-for-vegetation-studies/', 'hreflang': 'en-US', 'inherited': True}, {'rel': 'http://esipfed.org/ns/fedsearch/1.1/documentation#', 'href': 'https://lpdaac.usgs.gov/documents/989/GEDI_Quick_Guide_V2.pdf', 'hreflang': 'en-US', 'inherited': True}, {'rel': 'http://esipfed.org/ns/fedsearch/1.1/documentation#', 'href': 'https://lpdaac.usgs.gov/resources/e-learning/getting-started-gedi-l2a-version-2-data-python/', 'hreflang': 'en-US', 'inherited': True}, {'rel': 'http://esipfed.org/ns/fedsearch/1.1/documentation#', 'href': 'https://git.earthdata.nasa.gov/projects/LPDUR/repos/gedi-finder-tutorial-r/browse', 'hreflang': 'en-US', 'inherited': True}, {'rel': 'http://esipfed.org/ns/fedsearch/1.1/documentation#', 'href': 'https://git.earthdata.nasa.gov/projects/LPDUR/repos/gedi-finder-tutorial-python/browse', 'hreflang': 'en-US', 'inherited': True}] |
title: SC:GEDI02_A.002:2533601921 |
updated: 2022-02-10T08:15:05.774000+00:00 |
datetime: 2021-09-29T19:05:41+00:00 |
polygons: [['0.5709932 52.2787758 -2.4837905 54.4316567 -5.5328295 56.5942654 -8.5732867 58.7822787 -11.5952411 61.0071903 -14.5949371 63.2858784 -17.6278042 65.5668278 -20.6452263 67.911168 -23.5849439 70.3953373 -26.4176399 73.0568996 -29.1328601 75.9163838 -31.7396895 78.9674042 -34.2609565 82.2009755 -36.7156154 85.6053658 -39.1137102 89.1901586 -41.4397104 92.9886682 -43.6451425 97.046529 -45.6716906 101.4222361 -47.4495791 106.1521866 -48.9231111 111.2374384 -50.0653904 116.639812 -50.8805866 122.2892713 -51.3958888 128.1072619 -51.6443466 134.0234704 -51.6720605 136.2060011 -51.5850527 136.2318287 -51.5574836 134.0519625 -51.3106524 128.1464018 -50.7980206 122.3417482 -49.9852616 116.6995877 -48.8450859 111.3099074 -47.3757449 106.2307827 -45.602624 101.5029249 -43.5796986 97.1298416 -41.3776405 93.072544 -39.0538705 89.274506 -36.6592349 85.6893964 -34.204794 82.2836814 -31.6866687 79.0502519 -29.0823064 75.9989759 -26.3695137 73.1396771 -23.5392267 70.4778269 -20.6015322 67.9929961 -17.5846501 65.6469727 -14.552509 63.3653664 -11.5536516 61.0860916 -8.5312307 58.8600711 -5.4923005 56.6723228 -2.4435753 54.5093005 0.6119569 52.3560804 0.5709932 52.2787758']] |
time_end: 2021-09-29T20:38:35.000Z |
concept_id: G1201851166-NASA_MAAP |
dataset_id: GEDI L2A Elevation and Height Metrics Data Global Footprint Level V002 |
time_start: 2021-09-29T19:05:41.000Z |
browse_flag: False |
data_center: NASA_MAAP |
granule_size: 1901.12 |
day_night_flag: UNSPECIFIED |
original_format: ECHO10 |
coordinate_system: GEODETIC |
online_access_flag: False |
producer_granule_id: GEDI02_A_2021272190541_O15849_04_T03030_02_003_02_V002.h5 |
collection_concept_id: C1201746156-NASA_MAAP |
stac_extensions: [] |
Assets
Asset: None
href: s3://nasa-maap-data-store/file-staging/nasa-map/GEDI02_A___002/2021.09.29/GEDI02_A_2021272190541_O15849_04_T03030_02_003_02_V002.h5 |
Media type: application/x-hdf5 |
Roles: ['data'] |
Owner: |
Asset: None
href: https://lpdaac.usgs.gov/ |
Roles: ['metadata'] |
Owner: |
Asset: None
href: https://lpdaac.usgs.gov/documents/982/gedi_l2a_dictionary_P003_v2.html |
Roles: ['documentation'] |
Owner: |
Links
Link:
Rel: collection |
Target: https://stac.maap-project.org/collections/GEDI02_A |
Media Type: application/json |
Link:
Rel: parent |
Target: https://stac.maap-project.org/collections/GEDI02_A |
Media Type: application/json |
Link:
maap-stac
Rel: root |
Target: |
Media Type: application/json |
Link:
Rel: self |
Target: https://stac.maap-project.org/collections/GEDI02_A/items/GEDI02_A_2021272190541_O15849_04_T03030_02_003_02_V002 |
Media Type: application/geo+json |
After running the code block above, you should receive an output such as the output shown above. Click the arrow next to the item to see information such as the ID, bounding box coordinates, datetime, and more. In order to access the data, we will use the item
variable from the above section in order to extract the necessary information to set and display bucket
, key
, and filename
variables. These will be useful to use as arguments when downloading the file.
[6]:
# Use the item variable to extract information about the bucket, key, and file name
href = item.assets['data'].href
path_parts = href.split('/')
bucket = path_parts[2]
key = href.split(bucket)[1][1:]
filename = path_parts[-1]
# Display arguments
bucket, key, filename
[6]:
('nasa-maap-data-store',
'file-staging/nasa-map/GEDI02_A___002/2021.09.29/GEDI02_A_2021272190541_O15849_04_T03030_02_003_02_V002.h5',
'GEDI02_A_2021272190541_O15849_04_T03030_02_003_02_V002.h5')
Now let’s set an s3
variable using the boto3.client
function. We can use the function download_file
along with the arguments we set in the previous block to download the GEDI02_A data we need.
[7]:
# Set s3 variable
s3 = boto3.client('s3')
# If file already exists, do not download file
if exists(filename):
print("File already downloaded")
# Otherwise, download the file
else:
s3.download_file(bucket, key, filename)
print("Finished downloading")
Finished downloading
After the previous block has finished running, we should see the message Finished downloading
and the file should appear in the same directory that the Jupyter notebook is in.
Via NASA’s CMR¶
To search for data from the GEDI02_A product using NASA’s CMR, we invoke the MAAP
constructor, setting the maap_host
argument to 'api.ops.maap-project.org'
.
[8]:
# Invoke the MAAP using the MAAP host argument
maap = MAAP(maap_host='api.ops.maap-project.org')
Now we can use the searchGranule
function to find granule data within the collection, using the collection short name “GEDI_02A”. Note that we can use searchGranule
’s cmr_host
argument to specify a CMR instance external to MAAP and the readable_granule_name
argument to find granules matching either granule UR or producer granule id (please see the API documentation for more information). In order to download
data from NASA’s CMR, we will set a variable to the first result from the results
we obtained.
[9]:
# Search for granule data using CMR host name and collection short name, and readable_granule_name arguments
results = maap.searchGranule(
cmr_host='cmr.earthdata.nasa.gov',
short_name='GEDI02_A',
readable_granule_name = "GEDI02_A_2021272190541_O15849_04_T03030_02_003_02_V002.h5")
# Download first result
filename = results[0].getData()
If desired, the print
function can be utilized to see the file name and directory.
[10]:
# Print file directory
print(filename)
./GEDI02_A_2021272190541_O15849_04_T03030_02_003_02_V002.h5
Explore¶
Now that we have downloaded the data, let’s look into what it contains.
[11]:
# Create variable containing info from the file we downloaded
gediL2A = h5py.File(filename, 'r')
GEDI02_A data has data for 8 different beams. Let’s create a list of beam names to help explore the data.
[12]:
# Create list of beam names
beamNames = [g for g in gediL2A.keys() if g.startswith('BEAM')]
beamNames
[12]:
['BEAM0000',
'BEAM0001',
'BEAM0010',
'BEAM0011',
'BEAM0101',
'BEAM0110',
'BEAM1000',
'BEAM1011']
Now let’s explore the information available for one of the beams (in this case ‘BEAM0000’).
[13]:
# Get list of objects in the data pertaining to 'BEAM0000'
beam = beamNames[0]
gediL2A_objs = []
gediL2A.visit(gediL2A_objs.append)
gediSDS = [o for o in gediL2A_objs if isinstance(gediL2A[o], h5py.Dataset)]
[i for i in gediSDS if beam in i][0:20]
[13]:
['BEAM0000/ancillary/l2a_alg_count',
'BEAM0000/beam',
'BEAM0000/channel',
'BEAM0000/degrade_flag',
'BEAM0000/delta_time',
'BEAM0000/digital_elevation_model',
'BEAM0000/digital_elevation_model_srtm',
'BEAM0000/elev_highestreturn',
'BEAM0000/elev_lowestmode',
'BEAM0000/elevation_bias_flag',
'BEAM0000/elevation_bin0_error',
'BEAM0000/energy_total',
'BEAM0000/geolocation/elev_highestreturn_a1',
'BEAM0000/geolocation/elev_highestreturn_a2',
'BEAM0000/geolocation/elev_highestreturn_a3',
'BEAM0000/geolocation/elev_highestreturn_a4',
'BEAM0000/geolocation/elev_highestreturn_a5',
'BEAM0000/geolocation/elev_highestreturn_a6',
'BEAM0000/geolocation/elev_lowestmode_a1',
'BEAM0000/geolocation/elev_lowestmode_a2']
Visualize¶
Now that we’ve seen the various labels within the /BEAM0000 group, let’s use this information to visualize the GEDI orbit path for our scenes. To start, we shall get samples for various shots, the beam number, longitude, latitude, and quality flags. We can use these samples to create and display a pandas
dataframe.
[14]:
# Set variables for shot, beam number, longitude, latitude, and quality flag samples
lonSample, latSample, shotSample, qualitySample, beamSample = [], [], [], [], []
lats = gediL2A[f'{beamNames[0]}/lat_lowestmode'][()]
lons = gediL2A[f'{beamNames[0]}/lon_lowestmode'][()]
shots = gediL2A[f'{beamNames[0]}/shot_number'][()]
quality = gediL2A[f'{beamNames[0]}/quality_flag'][()]
for i in range(len(shots)):
if i % 100 == 0:
shotSample.append(str(shots[i]))
lonSample.append(lons[i])
latSample.append(lats[i])
qualitySample.append(quality[i])
beamSample.append(beamNames[0])
# Create a pandas dataframe containing the sample information
latslons = pd.DataFrame({'Beam': beamSample, 'Shot Number': shotSample, 'Longitude': lonSample,
'Latitude': latSample, 'Quality Flag': qualitySample})
# Display the dataframe
latslons
[14]:
Beam | Shot Number | Longitude | Latitude | Quality Flag | |
---|---|---|---|---|---|
0 | BEAM0000 | 158490000400502383 | 52.337698 | 0.599011 | 0 |
1 | BEAM0000 | 158490000400502483 | 52.367554 | 0.556129 | 0 |
2 | BEAM0000 | 158490000400502583 | 52.396932 | 0.514800 | 0 |
3 | BEAM0000 | 158490000400502683 | 52.426548 | 0.472746 | 0 |
4 | BEAM0000 | 158490000400502783 | 52.456248 | 0.430695 | 0 |
... | ... | ... | ... | ... | ... |
1685 | BEAM0000 | 158490000400670883 | 135.837810 | -51.605016 | 0 |
1686 | BEAM0000 | 158490000400670983 | 135.921291 | -51.605653 | 0 |
1687 | BEAM0000 | 158490000400671083 | 136.003206 | -51.606299 | 0 |
1688 | BEAM0000 | 158490000400671183 | 136.086629 | -51.606741 | 0 |
1689 | BEAM0000 | 158490000400671283 | 136.168523 | -51.607159 | 0 |
1690 rows × 5 columns
We can now create a map of the orbit path using the dataframe that we have created and utilizing the folium.Map
and folium.Circle
functions. Include map
in the code block to inspect the map which is created within the Jupyter notebook.
[15]:
# Create a map
map = folium.Map(
location=[
latslons.Latitude.mean(),
latslons.Longitude.mean()
], zoom_start=3, control_scale=True
)
# Add variables to the map
for index, location_info in latslons.iterrows():
folium.Circle(
[location_info["Latitude"], location_info["Longitude"]],
popup=f"Shot Number: {location_info['Shot Number']}"
).add_to(map)
# Display map
map
[15]: