diff --git a/.gitignore b/.gitignore index 80d3539..f6a85dc 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ *.ipynb_checkpoints *.h5* -*.json +*_V002.json *GranuleList* *.Rhistory* \ No newline at end of file diff --git a/CHANGE_LOG.md b/CHANGE_LOG.md index d8ca3b4..a716fad 100644 --- a/CHANGE_LOG.md +++ b/CHANGE_LOG.md @@ -6,6 +6,22 @@ and this project adheres to [Semantic Versioning](http://semver.org/). --- +## 2024-03-01 + +- Removed the Harmony API how-to because the subsetting service will not be available for GEDI V2 data stored in the Earthdata Cloud at the moment. +- Updated `how-to-find-and-access-GEDI-data_earthaccess.ipynb` with the expired token issue happening when higher number of granules were requested. +- Updated `how-to-find-and-access-GEDI-data_earthaccess.ipynb` narrative text. + + +## 2024-02-20 + +- Updated the current tutorials and scripts to work after GEDI V2 data collections are moved to the NASA Earthdata Cloud. +- Added new how-tos for accessing GEDI V2 + - [how-to-access-GEDI-data-Harmony.ipynb](https://github.com/nasa/GEDI-Data-Resources/tree/main/python/how-tos/how-to-access-GEDI-data-Harmony.ipynb) + - [how-to-find-and-access-GEDI-data_earthaccess.ipynb](https://github.com/nasa/GEDI-Data-Resources/tree/main/python/how-tos/how-to-find-and-access-GEDI-data_earthaccess.ipynb) + +- Updated the readme.md + ## 2023-11-30 - Updated deprecated pandas code in GEDI_Subsetter.py diff --git a/README.md b/README.md index aa98870..78f64c8 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # GEDI-Data-Resources -Welcome! This repository provides guides, short how-tos, and tutorials to help users access and work with data from the Global Ecosystem Dynamics Investigation (GEDI) mission. In the interest of open science this repository has been made public but is still under active development. All Jupyter notebooks and scripts should be functional, however, changes or additions may be made. Contributions from all parties are welcome. +Welcome! This repository provides guides, short how-tos, and tutorials to help users access and work with data from the Global Ecosystem Dynamics Investigation (GEDI) mission. All Jupyter notebooks and scripts should be functional, however, changes or additions may be made. Contributions from all parties are welcome. --- @@ -8,7 +8,7 @@ Welcome! This repository provides guides, short how-tos, and tutorials to help u The Global Ecosystem Dynamics Investigation ([GEDI](https://lpdaac.usgs.gov/data/get-started-data/collection-overview/missions/gedi-overview/)) mission aims to characterize ecosystem structure and dynamics to enable radically improved quantification and understanding of the Earth's carbon cycle and biodiversity. [GEDI](https://gedi.umd.edu/mission/mission-overview/) Level 1 and Level 2 Data Products are distributed by the Land Processes Distributed Active Archive Center ([LP DAAC](https://lpdaac.usgs.gov/data/get-started-data/collection-overview/missions/gedi-overview/)) and Level 3 and Level 4 Data Products are distributed by the [ORNL DAAC]([https://daac.ornl.gov/cgi-bin/dataset_lister.pl?p=40]). -Search for and download GEDI _Version 2_ data products via a graphical user interface (GUI) using [NASA EarthData Search](https://search.earthdata.nasa.gov/search?q=%22GEDI%22) or programmatically using NASA's [Common Metadata Repository](https://cmr.earthdata.nasa.gov/search) (CMR). +Search for and download GEDI _Version 2_ data products via a graphical user interface (GUI) using [NASA EarthData Search](https://search.earthdata.nasa.gov/search?ff=Available%20in%20Earthdata%20Cloud&fi=GEDI&as[instrument][0]=GEDI) or programmatically using NASA's [Common Metadata Repository](https://cmr.earthdata.nasa.gov/search) (CMR), earthaccess python package, or Harmony API. --- @@ -22,17 +22,6 @@ Search for and download GEDI _Version 2_ data products via a graphical user inte ## Prerequisites/Setup Instructions - -### File Downloads - -These granules below are used within the tutorials. Click/copy the URLs into a browser to download. Save them into the `./data/` folder within this repository. You will need a [NASA Earth Data Search](https://search.earthdata.nasa.gov/search) login to download the data used in this tutorial. You can create an account at the link provided. - -+ L1B Granule - -+ L2A Granule - -+ L2B Granule - - ---- - ### Environment Setup Instructions for setting up a compatible environment for working with GEDI data are linked to below. @@ -52,15 +41,16 @@ Instructions for setting up a compatible environment for working with GEDI data Content in this repository is divided into Python and R tutorials/scripts. The tutorials walk you through workin with GEDI data step by step while the scripts are command line executables. -| Repository Contents | Summary | Path | -|----|-----|----| -| **[GEDI_L1B_V2_Tutorial.ipynb](https://github.com/nasa/GEDI-Data-Resources/blob/main/python/tutorials/GEDI_L1B_V2_Tutorial.ipynb)** | Jupyter Notebook tutorial demonstrating how to work with the Geolocated Waveform GEDI01_B.002 data product using Python | `python\tutorials` | -| **[GEDI_L2A_V2_Tutorial.ipynb](https://github.com/nasa/GEDI-Data-Resources/blob/main/python/tutorials/GEDI_L2A_V2_Tutorial.ipynb)** | Jupyter Notebook tutorial demonstrating how to work with the Geolocated Waveform GEDI02_A.002 data product using Python | `python\tutorials` | -| **[GEDI_L2B_V2_Tutorial.ipynb](https://github.com/nasa/GEDI-Data-Resources/blob/main/python/tutorials/GEDI_L2B_V2_Tutorial.ipynb)** | Jupyter Notebook tutorial demonstrating how to how to work with the Geolocated Waveform GEDI02_B.002 data product using Python | `python\tutorials` | -| **[GEDI_Finder_Tutorial_Python.ipynb](https://github.com/nasa/GEDI-Data-Resources/blob/main/python/tutorials/GEDI_Finder_Tutorial_Python.ipynb)** | Jupyter Notebook tutorial demonstrating how to perform spatial [bounding box] queries for GEDI V2 L1B, L2A, and L2B data using NASA's CMR, and how to reformat the CMR response into a list of links that will allow users to download the intersecting GEDI V2 sub-orbit granules directly from the LP DAAC Data Pool using Python | `python\tutorials` | -| **[GEDI_Finder_Tutorial_R.Rmd](https://github.com/nasa/GEDI-Data-Resources/blob/main/r/GEDI_Finder_Tutorial_R.rmd)** | R Markdown tutorial demonstrating how to use R to perform spatial [bounding box] queries for GEDI V2 L1B, L2A, and L2B data using NASA's CMR, and how to reformat the CMR response into a list of links that will allow users to download the intersecting GEDI V2 sub-orbit granules directly from the LP DAAC Data Pool | `R` | -| **[GEDI_Finder.py](https://github.com/nasa/GEDI-Data-Resources/tree/main/python/scripts/GEDI_Finder)** | Command line executable performing spatial [bounding box] and temporal queries for GEDI V2 L1B, L2A, and L2B data using NASA's CMR and reformats the CMR response into a list of links that will allow users to download the intersecting GEDI V2 sub-orbit granules directly from the LP DAAC Data Pool. | `python/scripts/GEDI_Finder` | -| **[GEDI_Subsetter.py](https://github.com/nasa/GEDI-Data-Resources/tree/main/python/scripts/GEDI_Subsetter)** | Command line executable converting GEDI data products, stored in Hierarchical Data Format version 5 (HDF5, .h5) into GeoJSON files that can be loaded into GIS and Remote Sensing Software | `python/scripts/GEDI_Subsetter` | +| Name | Type | Summary | Services and Tools | +|----|-----|----|----| +| **GEDI_L1B_V2_Tutorial.ipynb** | [Jupyter Notebook](python/tutorials/GEDI_L1B_V2_Tutorial.ipynb)| Tutorial demonstrating how to work with the Geolocated Waveform GEDI01_B.002 data product using Python | | +| **GEDI_L2A_V2_Tutorial.ipynb** | [Jupyter Notebook](python/tutorials/GEDI_L2A_V2_Tutorial.ipynb) | Tutorial demonstrating how to work with the Geolocated Waveform GEDI02_A.002 data product using Python | | +| **GEDI_L2B_V2_Tutorial.ipynb** | [Jupyter Notebook](python/tutorials/GEDI_L2B_V2_Tutorial.ipynb) | Tutorial demonstrating how to how to work with the Geolocated Waveform GEDI02_B.002 data product using Python | | +| **GEDI_Finder_Tutorial_Python.ipynb** | [Jupyter Notebook](python/tutorials/GEDI_Finder_Tutorial_Python.ipynb) | Tutorial demonstrating how to perform spatial [bounding box] queries for GEDI V2 L1B, L2A, and L2B data using NASA's CMR, and how to reformat the CMR response into a list of links that will allow users to download the intersecting GEDI V2 sub-orbit granules using Python | [CMR API](https://cmr.earthdata.nasa.gov/search/site/docs/search/api.html) | +| **GEDI_Finder_Tutorial_R.Rmd** | [R Markdown](r/GEDI_Finder_Tutorial_R.rmd) | Tutorial demonstrating how to use R to perform spatial [bounding box] queries for GEDI V2 L1B, L2A, and L2B data using NASA's CMR, and how to reformat the CMR response into a list of links that will allow users to download the intersecting GEDI V2 sub-orbit granules | [CMR API](https://cmr.earthdata.nasa.gov/search/site/docs/search/api.html) | +| **GEDI_Finder.py** | [Command line executable](python/scripts/GEDI_Finder) | Script performing spatial [bounding box] and temporal queries for GEDI V2 L1B, L2A, and L2B data using NASA's CMR and reformats the CMR response into a list of links that will allow users to download the intersecting GEDI V2 sub-orbit granules | [CMR API](https://cmr.earthdata.nasa.gov/search/site/docs/search/api.html) | +| **GEDI_Subsetter.py** | [Command line executable](python/scripts/GEDI_Subsetter) | Script converting GEDI data products, stored in Hierarchical Data Format version 5 (HDF5, .h5) into GeoJSON files that can be loaded into GIS and Remote Sensing Software | | +| **how-to-find-and-access-GEDI-data_earthaccess.ipynb** | [Jupyter Notebook](python/how-tos/how-to-find-and-access-GEDI-data_earthaccess.ipynb) | Shows how to access GEDI data using earthaccess Python package locally and directly in the cloud | [earthaccess](https://github.com/nsidc/earthaccess) | --- @@ -72,6 +62,7 @@ Content in this repository is divided into Python and R tutorials/scripts. The t - [LP DAAC Website](https://lpdaac.usgs.gov/) - [LP DAAC GitHub](https://github.com/nasa/LPDAAC-Data-Resources) - [NASA Earthdata Search](https://search.earthdata.nasa.gov/search) +- [GEDI at NASA's Scientific Visualization Studio](https://svs.gsfc.nasa.gov/search/?search=GEDI) --- @@ -81,6 +72,6 @@ Email: LPDAAC@usgs.gov Voice: +1-866-573-3222 Organization: Land Processes Distributed Active Archive Center (LP DAAC)¹ Website: -Date last modified: 06-08-2023 +Date last modified: 02-20-2024 ¹Work performed under USGS contract G15PD00467 for NASA contract NNG14HH33I. diff --git a/data/GEDI_Datasets.json b/data/GEDI_Datasets.json new file mode 100644 index 0000000..11532fb --- /dev/null +++ b/data/GEDI_Datasets.json @@ -0,0 +1 @@ +{"GEDI_L1B": ["geolocation/altitude_instrument", "geolocation/local_beam_elevation", "tx_sample_start_index", "nsemean_odd", "ancillary/master_time_epoch", "noise_stddev_corrected", "tx_pulseflag", "geolocation/longitude_bin0_error", "selection_stretchers_x", "nsemean_even", "geolocation/latitude_instrument", "geolocation/digital_elevation_model", "geolocation/longitude_instrument_error", "geolocation/longitude_lastbin_error", "tx_egsigma", "geolocation/neutat_delay_total_bin0", "rx_energy", "master_frac", "rx_sample_count", "rxwaveform", "tx_gloc", "ancillary/mean_samples", "geolocation/longitude_instrument", "tx_eggamma_error", "rx_offset", "tx_egbias_error", "geolocation/bounce_time_offset_lastbin", "all_samples_sum", "geophys_corr/tide_load", "geolocation/solar_azimuth", "geolocation/mean_sea_surface", "geolocation/neutat_delay_total_lastbin", "th_left_used", "rx_sample_start_index", "geophys_corr/tide_ocean", "geophys_corr/tide_ocean_pole", "tx_eggamma", "geophys_corr/tide_pole", "tx_sample_count", "geolocation/surface_type", "geolocation/neutat_delay_derivative_bin0", "geolocation/digital_elevation_model_srtm", "shot_number", "txwaveform", "geolocation/latitude_bin0_error", "geolocation/altitude_instrument_error", "channel", "geolocation/longitude_lastbin", "geolocation/latitude_lastbin", "geolocation/elevation_bin0_error", "geolocation/shot_number", "geolocation/solar_elevation", "geolocation/local_beam_elevation_error", "tx_egamplitude", "geolocation/latitude_bin0", "geolocation/elevation_lastbin_error", "ancillary/smoothing_width", "geolocation/latitude_instrument_error", "tx_egbias", "geolocation/bounce_time_offset_bin0_error", "geolocation/bounce_time_offset_bin0", "rx_open", "beam", "geolocation/local_beam_azimuth", "tx_egflag", "tx_egsigma_error", "geolocation/bounce_time_offset_lastbin_error", "tx_gloc_error", "geophys_corr/geoid", "selection_stretchers_y", "geolocation/latitude_lastbin_error", "master_int", "geolocation/range_bias_correction", "geolocation/elevation_lastbin", "geolocation/longitude_bin0", "stale_return_flag", "geolocation/delta_time", "noise_mean_corrected", "geolocation/local_beam_azimuth_error", "geophys_corr/dynamic_atmosphere_correction", "geophys_corr/delta_time", "tx_egamplitude_error", "geolocation/elevation_bin0", "geolocation/neutat_delay_derivative_lastbin", "geolocation/degrade", "geophys_corr/tide_earth"], "GEDI_L2A": ["rx_processing_a4/ancillary/rx_subbin_resolution", "rx_processing_a6/ancillary/rx_use_fixed_thresholds", "rx_processing_a1/rx_iwaveamps", "rx_processing_a1/selected_mode_flag", "rx_processing_a2/sd_sm", "geolocation/elev_highestreturn_a2", "rx_processing_a2/zcross0", "geolocation/rh_a6", "rx_processing_a2/ancillary/preprocessor_threshold", "rx_processing_a4/ancillary/rx_max_mode_count", "geolocation/elevs_allmodes_a3", "rx_processing_a1/ancillary/rx_smoothing_width_zcross", "rx_processing_a6/sd_sm", "rx_processing_a3/rx_cumulative", "geolocation/quality_flag_a6", "lat_highestreturn", "rx_1gaussfit/ancillary/rx_constraint_gloc_upper", "rx_assess/ancillary/rx_pulsethresh", "rx_processing_a3/selected_mode", "rx_processing_a5/ancillary/enable_select_mode", "geolocation/elev_highestreturn_a1", "rx_processing_a1/toploc", "rx_1gaussfit/ancillary/rx_estimate_bias", "rx_processing_a1/search_start", "rx_processing_a6/ancillary/rx_subbin_resolution", "rx_processing_a6/zcross_localenergy", "geolocation/energy_lowestmode_a6", "master_frac", "rx_processing_a4/ancillary/ampval_limit3", "rx_processing_a6/selected_mode", "geolocation/lat_highestreturn_a4", "rx_processing_a1/ancillary/botlocdist_limit2", "rx_processing_a3/min_detection_energy", "rx_processing_a6/peak", "rx_processing_a5/rx_modelocalslope", "rx_processing_a6/ancillary/cumulative_energy_thresh", "geolocation/lon_lowestmode_a6", "rx_processing_a6/ancillary/pulse_sep_thresh", "rx_processing_a5/lastmodeenergy", "rx_1gaussfit/ancillary/rx_constraint_gamplitude_upper", "geolocation/elevs_allmodes_a5", "rx_assess/rx_energy", "rx_processing_a6/rx_cumulative", "delta_time", "rx_processing_a2/rx_modelocs", "rx_processing_a6/ancillary/rx_searchsize", "elev_lowestmode", "rx_processing_a3/selected_mode_flag", "rx_processing_a5/pk_sm", "rx_processing_a2/ancillary/rx_subbin_resolution", "rx_processing_a5/ancillary/botlocdist_limit2", "geolocation/lats_allmodes_a3", "geolocation/lat_highestreturn_a5", "geolocation/lon_lowestreturn_a6", "rx_processing_a1/energy_sm", "rx_processing_a5/stddev", "rx_processing_a2/ancillary/rx_use_fixed_thresholds", "geolocation/lon_lowestmode_a4", "rx_processing_a3/ancillary/rx_back_threshold", "geolocation/num_detectedmodes_a1", "rx_processing_a3/ancillary/botlocdist_limit3", "rx_processing_a1/ancillary/rx_searchsize", "rx_processing_a2/toploc_miss", "geolocation/lat_highestreturn_a6", "rx_processing_a3/ancillary/botlocdist_limit1", "rx_processing_a5/rx_modelocs", "rx_processing_a6/ancillary/botlocdist_limit3", "geolocation/lon_lowestreturn_a1", "geolocation/rh_a1", "land_cover_data/modis_nonvegetated_sd", "rx_processing_a1/rx_modeenergytobotloc", "rx_processing_a3/ancillary/pulse_sep_thresh", "rx_processing_a2/toploc", "rx_processing_a4/ancillary/rx_use_fixed_thresholds", "geolocation/sensitivity_a2", "rx_processing_a1/ancillary/energy_thresh", "rx_processing_a5/rx_modeenergytobotloc", "geolocation/lon_highestreturn_a6", "rx_processing_a1/min_detection_threshold", "rx_processing_a1/ancillary/rx_sentinel_location", "rx_processing_a1/rx_nummodes", "rx_processing_a6/zcross", "rx_processing_a4/rx_modeenergytobotloc", "selected_mode", "rx_processing_a5/botloc", "latitude_bin0_error", "rx_processing_a3/search_start", "rx_processing_a5/rx_modelocalenergyabovemean", "rx_1gaussfit/ancillary/mpfit_tolerance", "geolocation/lons_allmodes_a4", "rx_1gaussfit/rx_gwidth", "land_cover_data/pft_class", "rx_processing_a4/rx_cumulative", "geolocation/lat_lowestmode_a5", "rx_processing_a3/rx_algrunflag", "rx_processing_a3/ancillary/rx_smoothing_width_zcross", "rx_processing_a4/toploc", "rx_processing_a6/stddev", "rx_processing_a1/ancillary/ampval_limit2", "rx_assess/mean", "rx_processing_a4/botloc_amp", "geolocation/elev_lowestmode_a2", "rx_processing_a1/ancillary/preprocessor_threshold", "rx_processing_a5/smoothwidth_zcross", "rx_processing_a1/zcross", "rx_processing_a1/back_threshold", "rx_processing_a6/smoothwidth", "geolocation/lon_lowestmode_a2", "rx_processing_a4/ancillary/rx_sentinel_location", "geolocation/lat_lowestmode_a3", "rx_processing_a1/ancillary/rx_front_threshold", "rx_processing_a2/smoothwidth_zcross", "rx_processing_a6/ancillary/ampval_limit3", "rx_processing_a2/search_end", "rx_processing_a4/ancillary/rx_smoothing_width_locs", "rx_processing_a3/zcross", "rx_processing_a4/rx_modelocalenergyabovemean", "rx_processing_a2/botloc", "ancillary/l2a_alg_count", "rx_processing_a6/energy_sm", "rx_processing_a2/front_threshold", "land_cover_data/urban_proportion", "rx_processing_a6/search_start", "rx_processing_a2/ancillary/rx_searchsize", "sensitivity", "rx_processing_a5/ancillary/rx_front_threshold", "geolocation/num_detectedmodes_a4", "geolocation/sensitivity_a6", "geolocation/lats_allmodes_a5", "geolocation/lon_lowestmode_a3", "rx_processing_a6/ancillary/rx_front_threshold", "rx_processing_a3/botloc_amp", "rx_processing_a4/back_threshold", "rx_processing_a3/rx_modelocalenergy", "geolocation/quality_flag_a5", "rx_processing_a3/ancillary/rx_use_fixed_thresholds", "rx_processing_a6/rx_modewidths", "rx_processing_a2/min_detection_threshold", "geolocation/elev_highestreturn_a3", "geolocation/lon_highestreturn_a2", "rx_processing_a6/zcross0", "rx_processing_a1/rx_modelocalenergy", "rx_processing_a4/rx_iwaveamps", "rx_processing_a3/ancillary/rx_subbin_resolution", "rx_processing_a3/mean", "lon_highestreturn", "rx_processing_a3/smoothwidth_zcross", "rx_processing_a4/peak", "geolocation/num_detectedmodes_a6", "rx_processing_a2/zcross", "rx_processing_a6/ancillary/preprocessor_threshold", "rx_processing_a3/mean_sm", "rx_processing_a2/rx_modewidths", "digital_elevation_model", "land_cover_data/landsat_water_persistence", "geolocation/lat_lowestmode_a4", "rx_processing_a5/selected_mode", "rx_1gaussfit/ancillary/rx_constraint_gamplitude_lower", "rx_1gaussfit/rx_gflag", "rx_processing_a6/botloc", "rx_processing_a5/rx_iwaveamps", "rx_processing_a4/ancillary/cumulative_energy_minimum", "rx_processing_a4/ancillary/pulse_sep_thresh", "rx_assess/quality_flag", "rx_processing_a4/rx_modewidths", "rx_processing_a3/ancillary/botlocdist_limit2", "rx_processing_a3/ancillary/enable_select_mode", "rx_processing_a6/search_end", "rx_processing_a6/rx_modelocalenergyabovemean", "geolocation/elev_highestreturn_a4", "rx_processing_a2/ancillary/cumulative_energy_minimum", "rx_processing_a5/search_end", "geolocation/lats_allmodes_a2", "geolocation/quality_flag_a2", "rx_1gaussfit/ancillary/rx_constraint_gwidth_lower", "rx_processing_a6/rx_nummodes", "rx_processing_a5/rx_modelocalenergy", "rx_processing_a2/ancillary/rx_sentinel_location", "rx_processing_a3/zcross0", "rx_processing_a3/sd_sm", "land_cover_data/leaf_on_cycle", "rx_processing_a3/front_threshold", "rx_1gaussfit/ancillary/mpfit_max_func_evals", "rx_processing_a3/ancillary/ampval_limit3", "rx_processing_a1/zcross0", "rx_processing_a5/ancillary/rx_sentinel_location", "rx_assess/rx_clipbin0", "rx_processing_a1/botloc", "shot_number", "rx_processing_a5/sd_sm", "rx_processing_a6/botloc_amp", "geolocation/elev_highestreturn_a6", "digital_elevation_model_srtm", "rx_1gaussfit/ancillary/rx_mean_noise_level", "rx_processing_a2/rx_iwaveamps", "rx_processing_a6/ancillary/amp_thresh", "rx_processing_a6/ancillary/ampval_limit2", "channel", "rx_processing_a2/ancillary/enable_select_mode", "rx_processing_a3/pk_sm", "rx_assess/rx_maxamp", "rx_assess/ancillary/rx_ampbounds_ul", "rx_processing_a6/rx_modelocalenergy", "land_cover_data/landsat_treecover", "rx_assess/rx_clipbin_count", "rx_processing_a6/lastmodeenergy", "geolocation/latitude_1gfit", "geolocation/elevation_1gfit", "rx_processing_a3/ancillary/rx_smoothing_width_locs", "rx_processing_a1/smoothwidth", "rx_processing_a6/rx_modeamps", "rx_processing_a2/ancillary/cumulative_energy_thresh", "rx_processing_a1/botloc_amp", "rx_processing_a3/energy_sm", "rx_processing_a2/search_start", "rx_processing_a4/ancillary/botlocdist_limit1", "rx_processing_a3/smoothwidth", "rx_processing_a6/min_detection_threshold", "geolocation/lat_lowestmode_a6", "rh", "rx_processing_a4/ancillary/enable_select_mode", "rx_processing_a4/mean", "rx_processing_a4/zcross_localenergy", "geolocation/elev_highestreturn_a5", "rx_processing_a1/selected_mode", "rx_processing_a4/ancillary/ampval_limit2", "geolocation/lon_highestreturn_a5", "rx_processing_a2/rx_modelocalslope", "rx_processing_a5/selected_mode_flag", "geolocation/lon_highestreturn_a4", "mean_sea_surface", "geolocation/lat_lowestreturn_a2", "surface_flag", "rx_processing_a5/ancillary/rx_back_threshold", "rx_processing_a5/peak", "rx_processing_a2/back_threshold", "geolocation/sensitivity_a4", "geolocation/lat_lowestreturn_a3", "rx_processing_a3/zcross_localenergy", "rx_processing_a2/min_detection_energy", "geolocation/energy_lowestmode_a5", "rx_processing_a3/ancillary/rx_front_threshold", "rx_processing_a4/energy_sm", "rx_processing_a5/ancillary/botlocdist_limit3", "geolocation/stale_return_flag", "geolocation/lats_allmodes_a4", "rx_processing_a3/ancillary/amp_thresh", "rx_processing_a5/zcross_localenergy", "rx_processing_a4/selected_mode_flag", "geolocation/lat_highestreturn_a1", "master_int", "rx_processing_a2/zcross_amp", "geolocation/lat_highestreturn_a3", "rx_assess/ancillary/rx_ampbounds_ll", "rx_processing_a3/ancillary/preprocessor_threshold", "rx_processing_a6/ancillary/rx_max_mode_count", "rx_processing_a5/ancillary/amp_thresh", "rx_1gaussfit/rx_gloc_error", "rx_processing_a2/ancillary/rx_max_mode_count", "geolocation/lat_highestreturn_a2", "land_cover_data/urban_focal_window_size", "rx_processing_a2/smoothwidth", "rx_processing_a3/ancillary/cumulative_energy_minimum", "rx_1gaussfit/ancillary/rx_smoothwidth", "rx_processing_a4/zcross0", "rx_processing_a3/rx_modelocalslope", "rx_processing_a6/ancillary/rx_back_threshold", "geolocation/lats_allmodes_a1", "geolocation/lons_allmodes_a1", "geolocation/lon_lowestreturn_a3", "rx_processing_a1/zcross_amp", "lat_lowestmode", "geolocation/lat_lowestreturn_a4", "rx_processing_a5/front_threshold", "geolocation/lon_highestreturn_a1", "rx_processing_a2/ancillary/pulse_sep_thresh", "rx_processing_a1/ancillary/botlocdist_limit1", "geolocation/sensitivity_a3", "rx_assess/rx_maxpeakloc", "rx_processing_a5/ancillary/preprocessor_threshold", "geolocation/rh_a5", "rx_1gaussfit/ancillary/rx_constraint_gwidth_upper", "geolocation/elev_lowestmode_a4", "rx_processing_a1/mean", "geolocation/lat_lowestmode_a1", "land_cover_data/leaf_on_doy", "geolocation/lon_lowestreturn_a5", "longitude_bin0_error", "geolocation/elev_lowestreturn_a1", "rx_processing_a2/ancillary/botlocdist_limit2", "geolocation/energy_lowestmode_a1", "geolocation/elev_lowestmode_a6", "rx_processing_a1/ancillary/botlocdist_limit3", "rx_processing_a5/min_detection_energy", "rx_processing_a6/min_detection_energy", "rx_processing_a4/ancillary/amp_thresh", "rx_1gaussfit/rx_gloc", "rx_processing_a6/mean_sm", "rx_assess/ancillary/rx_clipamp", "rx_processing_a1/rx_modelocalenergyabovemean", "rx_processing_a3/peak", "rx_processing_a1/rx_modewidths", "geolocation/num_detectedmodes_a3", "geolocation/elev_lowestreturn_a4", "rx_processing_a3/rx_modewidths", "rx_processing_a4/ancillary/cumulative_energy_thresh", "geolocation/lat_lowestreturn_a1", "rx_processing_a1/rx_modelocs", "rx_processing_a4/smoothwidth_zcross", "rx_processing_a6/selected_mode_flag", "rx_processing_a1/ancillary/rx_smoothing_width_locs", "rx_processing_a4/rx_algrunflag", "rx_1gaussfit/rx_gamplitude", "rx_processing_a1/ancillary/rx_subbin_resolution", "rx_processing_a6/rx_modeenergytobotloc", "rx_processing_a2/ancillary/ampval_limit3", "rx_processing_a6/mean", "geolocation/elevs_allmodes_a6", "rx_1gaussfit/rx_gchisq", "geolocation/elev_lowestmode_a3", "geolocation/energy_lowestmode_a4", "rx_processing_a3/rx_modelocalenergyabovemean", "geolocation/elev_lowestreturn_a5", "rx_processing_a5/ancillary/rx_smoothing_width_zcross", "rx_processing_a2/ancillary/rx_smoothing_width_locs", "rx_processing_a1/pk_sm", "geolocation/lons_allmodes_a3", "geolocation/num_detectedmodes_a5", "rx_processing_a2/energy_sm", "land_cover_data/modis_treecover", "rx_1gaussfit/rx_gbias_error", "rx_processing_a4/botloc", "rx_processing_a1/ancillary/cumulative_energy_thresh", "geolocation/lat_lowestmode_a2", "geolocation/rh_a2", "rx_processing_a2/ancillary/amp_thresh", "rx_processing_a2/ancillary/botlocdist_limit3", "rx_processing_a2/lastmodeenergy", "rx_processing_a3/ancillary/energy_thresh", "rx_processing_a1/ancillary/rx_max_mode_count", "geolocation/elevs_allmodes_a1", "rx_processing_a6/ancillary/rx_smoothing_width_zcross", "rx_processing_a4/stddev", "rx_processing_a5/toploc", "rx_processing_a5/ancillary/rx_use_fixed_thresholds", "rx_assess/ancillary/smoothing_width_locs", "geolocation/longitude_1gfit", "geolocation/lons_allmodes_a6", "rx_1gaussfit/rx_gwidth_error", "rx_processing_a5/ancillary/ampval_limit3", "rx_processing_a5/toploc_miss", "rx_processing_a1/min_detection_energy", "rx_processing_a3/lastmodeenergy", "rx_processing_a3/rx_iwaveamps", "rx_processing_a3/ancillary/ampval_limit2", "geolocation/sensitivity_a5", "rx_processing_a4/selected_mode", "rx_processing_a4/search_end", "rx_processing_a2/ancillary/rx_front_threshold", "rx_processing_a4/search_start", "geolocation/elev_lowestmode_a1", "rx_processing_a5/rx_modeamps", "rx_processing_a6/ancillary/energy_thresh", "rx_processing_a2/ancillary/rx_smoothing_width_zcross", "rx_processing_a6/ancillary/botlocdist_limit2", "rx_processing_a4/ancillary/botlocdist_limit3", "rx_1gaussfit/rx_gbias", "rx_processing_a5/mean_sm", "rx_processing_a6/ancillary/rx_smoothing_width_locs", "rx_processing_a1/ancillary/rx_use_fixed_thresholds", "rx_processing_a4/rx_modelocalslope", "beam", "rx_processing_a6/front_threshold", "rx_processing_a3/toploc_miss", "rx_processing_a5/rx_modewidths", "rx_processing_a3/ancillary/rx_searchsize", "rx_processing_a4/ancillary/rx_back_threshold", "rx_processing_a1/ancillary/ampval_limit3", "rx_processing_a5/ancillary/pulse_sep_thresh", "rx_processing_a4/ancillary/preprocessor_threshold", "rx_processing_a2/zcross_localenergy", "rx_processing_a4/rx_modeamps", "rx_processing_a2/selected_mode_flag", "rx_processing_a4/ancillary/energy_thresh", "rx_processing_a2/rx_modelocalenergyabovemean", "geolocation/elev_lowestreturn_a3", "rx_processing_a6/rx_algrunflag", "rx_processing_a3/ancillary/rx_sentinel_location", "rx_processing_a5/ancillary/rx_subbin_resolution", "rx_processing_a6/pk_sm", "rx_processing_a4/pk_sm", "geolocation/rh_a3", "rx_processing_a4/min_detection_energy", "rx_processing_a4/zcross", "rx_processing_a4/smoothwidth", "rx_processing_a2/ancillary/rx_back_threshold", "rx_processing_a3/rx_modeamps", "rx_processing_a5/energy_sm", "lon_lowestmode", "rx_processing_a6/rx_iwaveamps", "rx_processing_a5/zcross_amp", "rx_processing_a1/ancillary/pulse_sep_thresh", "rx_processing_a6/ancillary/rx_sentinel_location", "rx_processing_a3/ancillary/cumulative_energy_thresh", "rx_processing_a1/smoothwidth_zcross", "degrade_flag", "geolocation/lons_allmodes_a5", "rx_processing_a5/rx_cumulative", "rx_processing_a4/ancillary/rx_front_threshold", "rx_processing_a1/front_threshold", "geolocation/quality_flag_a3", "rx_processing_a1/stddev", "rx_processing_a3/rx_modelocs", "rx_processing_a2/stddev", "rx_processing_a6/smoothwidth_zcross", "rx_processing_a3/botloc", "rx_processing_a5/ancillary/cumulative_energy_minimum", "rx_processing_a3/rx_nummodes", "rx_processing_a2/mean_sm", "rx_processing_a1/rx_modelocalslope", "geolocation/lats_allmodes_a6", "rx_processing_a1/sd_sm", "geolocation/lon_lowestreturn_a2", "geolocation/lon_lowestreturn_a4", "rx_processing_a2/mean", "rx_processing_a6/toploc", "elevation_bin0_error", "rx_processing_a5/ancillary/rx_max_mode_count", "land_cover_data/modis_nonvegetated", "rx_processing_a1/mean_sm", "geolocation/lat_lowestreturn_a6", "rx_processing_a5/zcross", "rx_processing_a4/front_threshold", "rx_processing_a1/toploc_miss", "rx_processing_a1/ancillary/cumulative_energy_minimum", "rx_processing_a1/rx_cumulative", "elevation_bias_flag", "rx_processing_a2/rx_modeamps", "rx_processing_a6/ancillary/botlocdist_limit1", "geolocation/rh_a4", "rx_processing_a5/search_start", "rx_processing_a1/peak", "rx_processing_a5/ancillary/cumulative_energy_thresh", "energy_total", "land_cover_data/leaf_off_flag", "rx_processing_a5/mean", "rx_processing_a6/ancillary/enable_select_mode", "rx_processing_a6/rx_modelocalslope", "rx_processing_a5/ancillary/rx_smoothing_width_locs", "rx_processing_a5/back_threshold", "rx_processing_a2/botloc_amp", "land_cover_data/modis_treecover_sd", "geolocation/elevs_allmodes_a4", "geolocation/lon_lowestmode_a5", "geolocation/quality_flag_a1", "rx_processing_a6/ancillary/cumulative_energy_minimum", "rx_processing_a2/pk_sm", "rx_processing_a5/ancillary/rx_searchsize", "rx_processing_a3/rx_modeenergytobotloc", "rx_assess/ocean_calibration_shot_flag", "rx_processing_a3/ancillary/rx_max_mode_count", "geolocation/elev_lowestreturn_a2", "rx_processing_a4/rx_nummodes", "rx_processing_a2/rx_nummodes", "num_detectedmodes", "geolocation/energy_lowestmode_a2", "rx_processing_a5/botloc_amp", "rx_processing_a2/rx_modeenergytobotloc", "rx_processing_a4/sd_sm", "geolocation/elev_lowestmode_a5", "rx_processing_a2/rx_modelocalenergy", "rx_processing_a2/rx_cumulative", "rx_assess/rx_assess_flag", "rx_processing_a5/ancillary/ampval_limit2", "geolocation/lat_lowestreturn_a5", "rx_processing_a5/rx_algrunflag", "rx_processing_a2/ancillary/energy_thresh", "rx_processing_a1/rx_algrunflag", "rx_processing_a4/zcross_amp", "rx_processing_a5/min_detection_threshold", "rx_processing_a2/ancillary/ampval_limit2", "rx_processing_a6/rx_modelocs", "rx_1gaussfit/rx_gamplitude_error", "geolocation/sensitivity_a1", "land_cover_data/leaf_off_doy", "rx_processing_a1/ancillary/enable_select_mode", "rx_processing_a4/ancillary/botlocdist_limit2", "solar_elevation", "land_cover_data/region_class", "geolocation/elevs_allmodes_a2", "rx_processing_a1/rx_modeamps", "rx_processing_a4/lastmodeenergy", "rx_1gaussfit/rx_giters", "rx_processing_a4/rx_modelocalenergy", "rx_processing_a1/search_end", "geolocation/energy_lowestmode_a3", "rx_processing_a1/zcross_localenergy", "rx_processing_a3/back_threshold", "rx_processing_a5/rx_nummodes", "geolocation/elev_lowestreturn_a6", "selected_mode_flag", "elev_highestreturn", "rx_processing_a4/rx_modelocs", "solar_azimuth", "rx_processing_a6/toploc_miss", "rx_processing_a1/ancillary/rx_back_threshold", "geolocation/num_detectedmodes_a2", "rx_processing_a4/min_detection_threshold", "rx_processing_a3/zcross_amp", "selected_algorithm", "geolocation/lon_highestreturn_a3", "rx_processing_a5/ancillary/botlocdist_limit1", "rx_processing_a3/stddev", "geolocation/lon_lowestmode_a1", "rx_processing_a3/min_detection_threshold", "rx_processing_a5/smoothwidth", "rx_1gaussfit/ancillary/rx_constraint_gloc_lower", "rx_processing_a2/rx_algrunflag", "rx_processing_a4/ancillary/rx_searchsize", "rx_assess/sd_corrected", "rx_processing_a6/zcross_amp", "rx_processing_a3/search_end", "quality_flag", "rx_assess/ancillary/rx_ringthresh", "rx_processing_a2/selected_mode", "geolocation/lons_allmodes_a2", "rx_processing_a1/ancillary/amp_thresh", "rx_processing_a5/ancillary/energy_thresh", "rx_processing_a4/toploc_miss", "rx_processing_a3/toploc", "rx_processing_a2/ancillary/botlocdist_limit1", "rx_processing_a2/peak", "rx_processing_a1/lastmodeenergy", "rx_processing_a4/ancillary/rx_smoothing_width_zcross", "geolocation/quality_flag_a4", "rx_processing_a5/zcross0", "rx_processing_a4/mean_sm", "rx_processing_a6/back_threshold", "rx_1gaussfit/ancillary/mpfit_maxiters", "rx_assess/mean_64kadjusted"], "GEDI_L2B": ["rx_processing/rg_eg_flag_a4", "rx_processing/rg_eg_gamma_error_a5", "rx_processing/rg_eg_gamma_error_a1", "rx_processing/rg_error_a3", "rx_processing/rg_eg_amplitude_a2", "geolocation/local_beam_elevation", "ancillary/rg_eg_constraint_center_buffer", "rx_processing/rx_energy_a6", "ancillary/tx_noise_stddev_multiplier", "pgap_theta_z", "rx_processing/rg_eg_center_error_a5", "rx_processing/pgap_theta_a4", "land_cover_data/modis_nonvegetated", "geolocation/longitude_bin0_error", "rx_processing/rg_error_a1", "geolocation/elev_highestreturn", "land_cover_data/landsat_water_persistence", "land_cover_data/leaf_on_doy", "rx_processing/algorithmrun_flag_a5", "geolocation/lon_lowestmode", "rx_processing/rx_energy_a4", "geolocation/digital_elevation_model", "rhov", "rx_processing/rg_a5", "rx_processing/rg_error_a2", "geolocation/longitude_lastbin_error", "rx_processing/rg_a6", "rx_processing/rg_eg_gamma_a1", "l2b_quality_flag", "rx_processing/rg_eg_amplitude_error_a4", "rx_processing/rg_eg_amplitude_a3", "rx_processing/pgap_theta_error_a3", "land_cover_data/leaf_off_flag", "rx_processing/rv_a2", "geolocation/lon_highestreturn", "ancillary/dz", "master_frac", "rx_processing/rg_eg_niter_a1", "l2a_quality_flag", "rx_sample_count", "rx_processing/rg_a4", "pavd_z", "geolocation/lat_highestreturn", "land_cover_data/modis_treecover_sd", "rx_processing/rg_eg_amplitude_error_a2", "rx_processing/rg_eg_center_a2", "rx_processing/rg_eg_center_error_a6", "geolocation/solar_azimuth", "rv", "rx_processing/shot_number", "rx_processing/rg_eg_sigma_a3", "rx_processing/rg_eg_center_a1", "rx_processing/rg_eg_sigma_error_a5", "rx_processing/rg_eg_flag_a6", "rx_processing/rg_eg_flag_a1", "rx_sample_start_index", "rx_processing/pgap_theta_a1", "rx_processing/rg_a2", "omega", "rx_processing/pgap_theta_error_a5", "rx_processing/pgap_theta_error_a4", "land_cover_data/leaf_on_cycle", "rx_processing/rg_eg_niter_a5", "ancillary/rg_eg_mpfit_max_func_evals", "rx_processing/rg_eg_center_error_a4", "rx_processing/rg_eg_gamma_a2", "num_detectedmodes", "rhov_error", "rx_processing/pgap_theta_error_a6", "rx_processing/rg_eg_center_error_a3", "rhog_error", "rx_processing/rg_eg_gamma_a5", "shot_number", "geolocation/latitude_bin0_error", "rx_processing/rg_eg_center_a5", "rx_processing/rg_eg_flag_a3", "fhd_normal", "rx_processing/rg_error_a4", "rx_processing/rg_eg_sigma_error_a2", "rx_processing/algorithmrun_flag_a3", "land_cover_data/modis_treecover", "channel", "rx_processing/rg_eg_gamma_error_a2", "cover", "geolocation/lat_lowestmode", "rx_processing/rg_eg_chisq_a4", "rx_processing/rg_eg_flag_a5", "rx_range_highestreturn", "geolocation/longitude_lastbin", "rx_processing/rg_eg_sigma_a1", "land_cover_data/modis_nonvegetated_sd", "land_cover_data/landsat_treecover", "rx_processing/rg_eg_gamma_error_a4", "rx_processing/rg_eg_amplitude_error_a5", "rx_processing/rg_eg_gamma_a3", "rx_processing/algorithmrun_flag_a2", "geolocation/latitude_lastbin", "rx_processing/rg_error_a5", "geolocation/elevation_bin0_error", "land_cover_data/leaf_off_doy", "rx_processing/rv_a4", "rx_processing/rv_a6", "rx_processing/rx_energy_a1", "rhog", "geolocation/shot_number", "geolocation/solar_elevation", "land_cover_data/region_class", "rx_processing/rv_a3", "rx_processing/rg_eg_amplitude_a4", "rx_processing/rg_eg_gamma_a6", "rx_processing/rx_energy_a5", "rx_processing/algorithmrun_flag_a4", "rx_processing/rg_eg_gamma_a4", "geolocation/latitude_bin0", "selected_mode", "geolocation/elevation_lastbin_error", "rx_processing/rv_a5", "rx_processing/pgap_theta_error_a2", "rx_processing/rv_a1", "rx_processing/rg_eg_niter_a3", "rx_processing/rg_eg_flag_a2", "selected_mode_flag", "land_cover_data/pft_class", "rx_processing/rg_eg_chisq_a1", "rx_processing/rg_eg_center_a3", "rx_processing/rg_eg_center_a6", "ancillary/rg_eg_mpfit_tolerance", "rx_processing/rg_eg_amplitude_a6", "rx_processing/rg_eg_center_error_a1", "algorithmrun_flag", "rx_processing/rg_eg_center_error_a2", "ancillary/rg_eg_mpfit_maxiters", "geolocation/height_lastbin", "rx_processing/pgap_theta_a3", "rx_processing/algorithmrun_flag_a1", "rx_processing/pgap_theta_error_a1", "surface_flag", "geolocation/height_bin0", "rossg", "rx_processing/rx_energy_a3", "pai_z", "beam", "rx_processing/rx_energy_a2", "ancillary/maxheight_cuttoff", "rx_processing/pgap_theta_a6", "rx_processing/rg_eg_chisq_a3", "rx_processing/rg_error_a6", "rx_processing/rg_eg_chisq_a2", "geolocation/local_beam_azimuth", "rh100", "rx_processing/pgap_theta_a5", "rx_processing/rg_eg_gamma_error_a3", "rx_processing/algorithmrun_flag_a6", "rx_processing/rg_eg_sigma_a5", "rx_processing/rg_a1", "rx_processing/rg_eg_niter_a6", "pgap_theta", "rx_processing/rg_eg_center_a4", "rx_processing/rg_eg_amplitude_a1", "geolocation/latitude_lastbin_error", "rx_processing/rg_eg_niter_a2", "rx_processing/rg_eg_amplitude_error_a6", "rx_processing/pgap_theta_a2", "cover_z", "rx_processing/rg_eg_amplitude_error_a1", "rx_processing/rg_eg_sigma_error_a4", "ancillary/l2a_alg_count", "master_int", "pai", "rx_processing/rg_a3", "land_cover_data/urban_proportion", "rx_processing/rg_eg_gamma_error_a6", "rx_processing/rg_eg_sigma_a4", "geolocation/elevation_lastbin", "geolocation/longitude_bin0", "sensitivity", "rx_processing/rg_eg_sigma_a2", "stale_return_flag", "rx_processing/rg_eg_chisq_a6", "land_cover_data/urban_focal_window_size", "rx_processing/rg_eg_niter_a4", "rg", "pgap_theta_error", "rx_processing/rg_eg_sigma_error_a3", "geolocation/degrade_flag", "selected_l2a_algorithm", "rx_processing/rg_eg_sigma_error_a1", "geolocation/elev_lowestmode", "geolocation/delta_time", "rx_processing/rg_eg_amplitude_error_a3", "selected_rg_algorithm", "ancillary/signal_search_buff", "rx_processing/rg_eg_chisq_a5", "rx_processing/rg_eg_amplitude_a5", "rx_processing/rg_eg_sigma_a6", "geolocation/elevation_bin0", "rx_processing/rg_eg_sigma_error_a6"]} \ No newline at end of file diff --git a/data/sequoia.geojson b/data/sequoia.geojson new file mode 100644 index 0000000..f213c94 --- /dev/null +++ b/data/sequoia.geojson @@ -0,0 +1 @@ +{"type":"FeatureCollection","features":[{"type":"Feature","properties":{},"geometry":{"type":"Polygon","coordinates":[[[-118.5562008683133,37.054833903438414],[-118.62023689977332,37.052306109907725],[-118.68364361577358,37.044747575631085],[-118.7457982619934,37.03223258018768],[-118.80609112257102,37.014884061658194],[-118.86393183151212,36.992872336414315],[-118.9187554394848,36.96641332980435],[-118.97002816222776,36.93576634013681],[-119.01725274308889,36.901231363963085],[-119.05997336964064,36.86314601570012],[-119.09778009263769,36.82188207903588],[-119.1303127045149,36.77784173126395],[-119.1572640438968,36.73145348466567],[-119.17838270193468,36.6831678912857],[-119.19347511545028,36.63345305893807],[-119.20240704063043,36.58279002706412],[-119.20510440919372,36.53166805118261],[-119.20155357639713,36.48057984418473],[-119.1918009768612,36.430016821695006],[-119.17595220990756,36.38046439721669],[-119.15417058090006,36.33239737087699],[-119.12667512897241,36.28627545335696],[-119.09373817455797,36.24253896410006],[-119.05568242237536,36.201604740205],[-119.01287765705642,36.1638622895781],[-118.96573706952763,36.129670218996594],[-118.9147132526765,36.09935296475546],[-118.86029390485886,36.073197850569485],[-118.80299727953474,36.05145249440219],[-118.74336741885551,36.03432258291131],[-118.68196920845594,36.021970029246155],[-118.61938329010519,36.01451152701098],[-118.5562008683133,36.01201751032007],[-118.4930184465214,36.01451152701098],[-118.43043252817067,36.021970029246155],[-118.3690343177711,36.03432258291131],[-118.30940445709187,36.05145249440219],[-118.25210783176772,36.073197850569485],[-118.1976884839501,36.09935296475546],[-118.14666466709897,36.129670218996594],[-118.09952407957019,36.1638622895781],[-118.05671931425125,36.201604740205],[-118.01866356206864,36.24253896410006],[-117.9857266076542,36.28627545335696],[-117.95823115572655,36.33239737087699],[-117.93644952671904,36.38046439721669],[-117.9206007597654,36.430016821695006],[-117.91084816022946,36.48057984418473],[-117.90729732743289,36.53166805118261],[-117.90999469599618,36.58279002706412],[-117.91892662117633,36.63345305893807],[-117.93401903469193,36.6831678912857],[-117.95513769272979,36.73145348466567],[-117.9820890321117,36.77784173126395],[-118.01462164398892,36.82188207903588],[-118.05242836698596,36.86314601570012],[-118.0951489935377,36.901231363963085],[-118.14237357439885,36.93576634013681],[-118.19364629714181,36.96641332980435],[-118.24846990511449,36.992872336414315],[-118.30631061405559,37.014884061658194],[-118.36660347463321,37.03223258018768],[-118.42875812085302,37.044747575631085],[-118.49216483685329,37.052306109907725],[-118.5562008683133,37.054833903438414]]]}}]} \ No newline at end of file diff --git a/guides/gedi-earthdata-search.md b/guides/gedi-earthdata-search.md new file mode 100644 index 0000000..74184e3 --- /dev/null +++ b/guides/gedi-earthdata-search.md @@ -0,0 +1,67 @@ +# GEDI Version 2 Earthdata Search Guide + +The Global Ecosystem Dynamics Investigation (GEDI) instrument aboard the International Space Station +(ISS) collects light detection and ranging (lidar) full waveform observations. The Level 1B Geolocated +Waveform Data ([GEDI01_B](https://doi.org/10.5067/GEDI/GEDI01_B.002)), Level 2A Elevation and Height Metrics Data ([GEDI02_A](https://doi.org/10.5067/GEDI/GEDI02_A.002)), and Level 2B +Canopy Cover and Vertical Profile Metrics Data ([GEDI02_B](https://doi.org/10.5067/GEDI/GEDI02_B.002)) granules are available through NASA’s Earthdata Search. This quick guide demonstrates how to find and subset GEDI Version 2 granules using Earthdata Search. GEDI Version 2 data are split into sub-orbit granules and contain the spatial metadata necessary to perform spatial queries in Earthdata Search. + +This tutorial guides you through how to use NASA's [Earthdata Search](https://search.earthdata.nasa.gov/) to search for GEDI data containing only data for a region of interest (ROI) and for a temporal range. It also provides instruction on how to perform spatial and/or layer subsetting of GEDI sub-orbit granules in Earthdata Search and how to connect the search output (e.g. download or access links) to a programmatic workflow (locally or from within the cloud). + +## Step 1. Earthdata Search Login + +Earthdata Login credentials are required to download or access NASA Earth data products available at [Earthdata Search](https://search.earthdata.nasa.gov/). If you do not have an Earthdata account, create one at https://urs.earthdata.nasa.gov. +Remember your username and password; and use it to log in to Earthdata Search. + + +## Step 2. Search for dataset of interest + +Search for a GEDI Version 2 collection by entering "GEDI v2"/"GEDI" or the dataset short name (e.g., GEDI01_B v002) into the search box in the upper left-hand corner of the page, and check the box for "Customizable" option under Feature options. The list of matching collections in the middle panel will be updated with your input. +Hover over the collections and select your collection. + +![Image shows the collection search for GEDI granules in Earthdata Search](https://github.com/nasa/GEDI-Data-Resources/tree/main/img/collection.png) + +## Step 3. Perform a Spatiotemporal Search for Granules + +All available granules for the product will be included in the list of matching granules. The list of granules can be queried by temporal and/or spatial boundaries using the tools below the search bar in Earthdata Search. +The temporal subsetting allows for user-provided start and end date/time and will return any available granules acquired between those dates. +The spatial subsetting allows you to draw a polygon, circle, or rectangle region of interest to filter granules by location. Other spatial options include submitting a lat/lon point location, or uploading a KML, shapefile, GeoJSON, or GeoRSS. + +**Note** that there is an existing issue with uploading zipped shapefile currently. + +![Image shows the spatiotemporal subset for GEDI granules in Earthdata Search](https://github.com/nasa/GEDI-Data-Resources/tree/main/img/granules.png) + +## Step 4: Selecting Granules for Download + +Now that the results have been filtered to the desired temporal and spatial extent, you can see the footprints of the GEDI Version 2 sub-orbit granules intersecting your spatiotemporal query. + +Download all granules associated with the selected collection using the green button located in the bottom right-hand corner (Download All), select specific granules to add to an order using the add (**+**) button, or directly download the full granule using the icon. + +## Step 5: Spatial and Band/Layer Subsetting + +Once you click on the green **Download All** button, you will be directed to Edit Options tab. Under “Select a data access method,” select **Stage For Delivery** to download source files or select **Customize** to create subset of granules. + +To set up the parameters for subsetting each granule to your region of interest, scroll down to the Spatial Subsetting section. Check the box next to "Click to enable" and it will populate the coordinates of the bounding box for the ROI. + +To clip the granules to the exact boundaries of a GeoJSON or shapefile, deselect "Click to enable" and select "Use Shapefile from Search". + +Select specific science dataset layers to extract by scrolling down to the Band Subsetting section. Expand the directories and select the desired GEDI beams and/or layers. Additional information for each of the data layers can be found on [GEDI product Digital Object Identifier (DOI) landing pages](https://lpdaac.usgs.gov/product_search/?collections=GEDI&status=Operational&view=cards&sort=title). +After the desired parameters for spatial and/or layer subsetting have been selected, click Done to complete the custom order form then click Download Data to initiate the order. + +![Image shows the customized option for GEDI data in Earthdata Search](https://github.com/nasa/GEDI-Data-Resources/tree/main/img/customize.png) + +## Step 6: Retrieve Data + +When the data request is submitted, a Download Status screen will monitor the progress of the order. + +The status update emails are sent to the email address associated with the Earthdata login credentials or specified in the custom order form. Check the status of the order in the [Download Status and History](https://search.earthdata.nasa.gov/downloads) page. The order completion email contains URLs for accessing the data outputs. Note that the URLs have an expiration date and are only valid for one week. + +Contact LP DAAC User Services at with any questions about the request. Be sure to reference the request ID in any correspondence. + +## Step 7: Download Data + +Download the output files by clicking on the .zip link in the email and unzipping into a local directory. Or, click on the .html link, which goes to a page including options to download files one by one, or download a .txt file containing links to all of the output files. + +Automate downloading by saving the .txt file and using command line utilities [wget](https://github.com/nasa/LPDAAC-Data-Resources/blob/main/guides/bulk_download_using_wget.md) and [curl](https://github.com/nasa/LPDAAC-Data-Resources/blob/main/guides/bulk_download_using_curl.md). Additionally, R or Python can be used to download data directly from the .txt or .csv file using the scripts provided in How to [Access the LP DAAC Data Pool with R](https://git.earthdata.nasa.gov/projects/LPDUR/repos/daac_data_download_r/browse) and [How to Access the LP DAAC Data Pool with Python](https://git.earthdata.nasa.gov/projects/LPDUR/repos/daac_data_download_python/browse/DAACDataDownload.py). + + +![Image shows the Dowanlaod Status page in Earthdata Search](https://github.com/nasa/GEDI-Data-Resources/tree/main/img/download.png) diff --git a/img/collection.png b/img/collection.png new file mode 100644 index 0000000..3b52cf9 Binary files /dev/null and b/img/collection.png differ diff --git a/img/customize.png b/img/customize.png new file mode 100644 index 0000000..6d02c9d Binary files /dev/null and b/img/customize.png differ diff --git a/img/download.png b/img/download.png new file mode 100644 index 0000000..8b75ee6 Binary files /dev/null and b/img/download.png differ diff --git a/img/granules.png b/img/granules.png new file mode 100644 index 0000000..3ce855b Binary files /dev/null and b/img/granules.png differ diff --git a/python/how-tos/how-to-find-and-access-GEDI-data_earthaccess.ipynb b/python/how-tos/how-to-find-and-access-GEDI-data_earthaccess.ipynb new file mode 100644 index 0000000..4635c29 --- /dev/null +++ b/python/how-tos/how-to-find-and-access-GEDI-data_earthaccess.ipynb @@ -0,0 +1,817 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "f5c09f31-9ee1-43f5-9921-bdae6d5172b8", + "metadata": {}, + "source": [ + "# How to: Find and Access GEDI Data\n" + ] + }, + { + "cell_type": "markdown", + "id": "4205b684-b1f3-4674-a27c-4eb191c6154c", + "metadata": {}, + "source": [ + "This notebook will provide a guide on how to find and access L1 and L2 Global Ecosystem Dynamics Investigation (GEDI) V2 data providing high-resolution laser ranging of Earth’s forests and topography from the International Space Station (ISS). Currently, there are two methods of finding and accessing L1 and L2  GEDI Version 2 data products:\n", + "\n", + "1. [NASA's Earthdata Search](https://search.earthdata.nasa.gov/search)\n", + "2. [NASA's CMR API](https://cmr.earthdata.nasa.gov/search/site/docs/search/api.html)\n", + "\n", + "This notebook will explain how to find and access L1 and L2 GEDI V2 data stored in Earthdata Cloud programmatically. While you can directly work with NASA's CMR API, [`earthaccess`](https://github.com/nsidc/earthaccess ) python library, mentioned in this notebook, provides a user-friendly way to login, search, and download or stream NASA Earth science data available in [Common Metadata Repository (CMR)](https://www.earthdata.nasa.gov/eosdis/science-system-description/eosdis-components/cmr). " + ] + }, + { + "cell_type": "markdown", + "id": "ecf2e23d-d60e-4bd4-9c32-bd9ee53979e1", + "metadata": {}, + "source": [ + "Let's start with importing the required packages." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "54a3417a-4017-42f9-ae77-5eb328d95bb5", + "metadata": {}, + "outputs": [], + "source": [ + "import os, json\n", + "import earthaccess\n", + "import pandas as pd\n", + "import geopandas as gp\n", + "import h5py\n", + "from pprint import pprint\n", + "from shapely import Polygon\n", + "from shapely.geometry import Point\n", + "from shapely.geometry.polygon import orient\n", + "from datetime import datetime\n", + "\n", + "os.chdir('../..')" + ] + }, + { + "cell_type": "markdown", + "id": "2ef20e1d-07d2-4a78-88df-530259af28b9", + "metadata": {}, + "source": [ + "## Authentication" + ] + }, + { + "cell_type": "markdown", + "id": "d2abb3f8-b34d-4e1f-af5a-589d819e138e", + "metadata": {}, + "source": [ + "To access or download NASA Earth data, you need a .netrc file containing your NASA Earthdata Login information is needed. You can manually create a .netrc file but you can use earthaccess package for easier authentication.  `earthaccess.login()` function is used to authenticate with NASA Earthdata Login credentials stored in a .netrc file. This function will prompt you to enter your NASA Earthdata username and password to create the .netrc file if it doesn't already exist, and then uses your account information for authentication. \n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "292c484b-3682-48f3-a145-5b5a57065f19", + "metadata": {}, + "outputs": [], + "source": [ + "earthaccess.login(persist=True)" + ] + }, + { + "cell_type": "markdown", + "id": "cd4805c8-84ac-4675-b7b4-75e77b01a55c", + "metadata": {}, + "source": [ + "## Search for GEDI Collections\n", + "\n", + "GEDI level 1 & level 2 data products are hosted by the Land Processes Distributed Active Archive Center (LP DAAC), while GEDI L3 & L4 are distributed by Oak Ridge National Laboratory (ORNL DAAC). In this example, we will use the cloud-hosted [GEDI L2B Canopy Cover and Vertical Profile Metrics Data Global Footprint Level (GEDI02_B)](https://lpdaac.usgs.gov/products/gedi02_bv002/) to find data, but the same routine can be used to access other products. To find the data we will use the `earthaccess` Python library. `earthaccess` searches [NASA's Common Metadata Repository (CMR)](https://cmr.earthdata.nasa.gov/search), a metadata system that catalogs Earth Science data and associated metadata records. `collection_query` from `earthaccess` is used to search for the NASA data collections. Various query parameters can be used to search collections and granules using attributes associated with them in the metadata. More details can be found [here](https://github.com/nsidc/earthaccess/blob/main/notebooks/Demo.ipynb). \n", + "Below, the CMR Catalog is searched to find collections with `gedi` keywords, managed by `LPCLOUD` provider, and with a `version` number of `002`. The returned response can be used to retrieve the concept-id for each dataset. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "96fbc843-1053-4266-bcaf-780f9e22b942", + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "collections = earthaccess.collection_query().keyword('gedi').version('002').provider('LPCLOUD').get()\n", + "pprint(collections[0].summary())\n", + " " + ] + }, + { + "cell_type": "markdown", + "id": "6b6de8f2-68cb-4b7d-87c9-e20f74c0de4e", + "metadata": {}, + "source": [ + "## Search for GEDI Granules" + ] + }, + { + "cell_type": "markdown", + "id": "a77410fb-ca69-4375-a740-dfca8932f419", + "metadata": {}, + "source": [ + "Collections `concept-id` is needed to search for data. Below, a dictionary is created to store GEDI V2 collection IDs distributed by LP DAAC, and `GEDI02_B` is selected. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cad25579-3aab-4e9d-ab2b-64dc42b4efc5", + "metadata": {}, + "outputs": [], + "source": [ + "gedi_collectionIDs = {}\n", + "for c in collections:\n", + " gedi_collectionIDs[c.summary()['short-name']] = c.summary()['concept-id']\n", + "gedi_collectionIDs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "85e5cf82-1948-427b-975a-472e66ab986f", + "metadata": {}, + "outputs": [], + "source": [ + "gedi_products = ['GEDI02_B'] #['GEDI01_B', 'GEDI02_B', 'GEDI02_A']\n", + "\n", + "conceptID = [gedi_collectionIDs[g] for g in gedi_products]\n", + "conceptID" + ] + }, + { + "cell_type": "markdown", + "id": "3e4bd31c-98a7-49d2-905a-dbb60d5f939e", + "metadata": {}, + "source": [ + "Next, define a temporal range for the query. \n", + "Please note that GEDI was moved temporarily into hibernation upon the completion of its first mission, which lasted from December 2018 to March 2023. \n", + "GEDI had been temporarily on pause after March 2023. The instrument returned to the original location on ISS on April 22nd, 2024. GEDI has been collecting data after its return on April 23, 2024, but data has not been publicaly distributed yet. More details can be found [here](https://lpdaac.usgs.gov/news/nasa-announces-pause-in-gedi-mission/).\n", + "Data is available for the first part of the mission from `2019-04-18` to `2023-03-16` and newly collected data will be available after the proper validations are complete. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "03422d51-6052-412b-8884-bda0eb0d504f", + "metadata": {}, + "outputs": [], + "source": [ + "tempRange = ('2022-04-01', '2022-05-31') " + ] + }, + { + "cell_type": "markdown", + "id": "7813a2ec-d225-4a40-928e-ffc0af94fce5", + "metadata": {}, + "source": [ + "A GeoJSON file is used to define the spatial region of interest (ROI). For this example, the ROI is an area in Sequoia National Forest, CA." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "72c4b8e1-89eb-401e-bf2c-698abaca2a93", + "metadata": {}, + "outputs": [], + "source": [ + "polygon = gp.read_file('data/sequoia.geojson')\n", + "polygon['geometry'][0]\n" + ] + }, + { + "cell_type": "markdown", + "id": "b07d5d6f-899d-41f5-832a-9c3d02d916da", + "metadata": {}, + "source": [ + "Next, submit the query using `search_data` function. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ef652862-bf59-4449-b9e3-dad074ac36d7", + "metadata": {}, + "outputs": [], + "source": [ + "params = {\n", + " \"concept_id\" : conceptID,\n", + " \"temporal\": tempRange,\n", + " \"polygon\": list(polygon['geometry'][0].exterior.coords),\n", + " # bounding_box = bbx,\n", + " \"count\": 200\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2912219f-12bf-4b5c-a659-ccda823db7c8", + "metadata": {}, + "outputs": [], + "source": [ + "results = earthaccess.search_data(**params)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "19495596-0aae-42b5-ab12-c0123c664e64", + "metadata": {}, + "outputs": [], + "source": [ + "results[0]" + ] + }, + { + "cell_type": "markdown", + "id": "ce103519-bfdd-4ac6-b45b-617753371bb2", + "metadata": {}, + "source": [ + "## Accessing GEDI Data\n", + "There are two options to access NASA Earth science data stored in the [Earthdata Cloud](https://www.earthdata.nasa.gov/technology/cloud-computing). You can download the data using the `HTTPS` links, create your subset and then work with data locally. The other option is loading data in the memory and only save a subset of data. Loading data in the memory will work both when you are working locally (using `HTTPS` links) or in the cloud (using `S3` links). If you have access to cloud preferably in the same Amazon Web Services (AWS) region us-west2, you can access and work with data virtually in a cloud-based environment using the `S3` links and skip the downloading part. This method is called “Direct Cloud Access” or, “Direct Access”. Please note that direct access using the `S3` links is only possible if you are working in the Amazon Web Services (AWS) Region us-west-2. If you are working with data locally, you still can load data usig `HTTPS` links but the process could be slower. \n" + ] + }, + { + "cell_type": "markdown", + "id": "4cb861d4-d951-4223-8cd7-03540f0200f2", + "metadata": {}, + "source": [ + "### Option 1: Donwnloading GEDI data using `HTTPS` links \n", + "Below, the `HTTPS` links are printed but data can be downloaded using the `download` function from `earthaccess` package directly. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b63ac317-5155-4b24-a356-0a3dd53b6e97", + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "data_links = [granule.data_links(access=\"external\") for granule in results]\n", + "data_links" + ] + }, + { + "cell_type": "markdown", + "id": "66294e21-4b2a-450d-b0f0-aee122b24f7a", + "metadata": {}, + "source": [ + "Below, the first two granules in the result response is downlaoded below but you can adjust to download them all by replacing `results` with `results[0:2]`. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2744cf7f-e42e-43df-b79a-9c972fdcc0f1", + "metadata": {}, + "outputs": [], + "source": [ + "# # Only downloaded the first 2 granules\n", + "# downloaded_files = earthaccess.download(\n", + "# results[0:3],\n", + "# local_path='data/',\n", + "# )" + ] + }, + { + "cell_type": "markdown", + "id": "8aa15881-2fd9-4e01-8e59-d0f8dec07061", + "metadata": {}, + "source": [ + "Once your data is downloaded, you can use the **GEDI Subsetter** available in [GEDI-Data_Resources repository](https://github.com/nasa/GEDI-Data-Resources) to subset GEDI sub-orbit granules to your spatial bound and your layers. It is easier to [clone the GEDI-Data-Resources repository](https://github.com/nasa/GEDI-Data-Resources?tab=readme-ov-file#getting-started) and run this notebook but if you have not, you need to adjust the directories below to where the `GEDI_Subsetter.py` is stored. \n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "557aa0d6-0adc-4d0b-9e9e-070d27beb7e1", + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "# !python python/scripts/GEDI_Subsetter/GEDI_Subsetter.py --dir data --roi data/sequoia.geojson --beams BEAM0101 --sds '/beam,/quality_flag,/rh,/pai,/pai_z,/pavd_z,/rh100'" + ] + }, + { + "cell_type": "markdown", + "id": "1f2ce35b-2f19-4491-aa36-12f40a84ec39", + "metadata": {}, + "source": [ + "As a final step, you can clean up and delete the downloaded source files to free your local space. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "38794842-5932-4e27-bd2f-e08735d0fa93", + "metadata": {}, + "outputs": [], + "source": [ + "# downloaded = [i for i in os.listdir('data') if i.endswith('.h5')]\n", + "# for file in downloaded: \n", + "# os.remove(f'data/{file}') " + ] + }, + { + "cell_type": "markdown", + "id": "37a84f48-e1c8-4ac5-bd82-f343ee9c024c", + "metadata": {}, + "source": [ + "### " + ] + }, + { + "cell_type": "markdown", + "id": "7a3958f8-283d-45fc-a44a-2b352a467972", + "metadata": {}, + "source": [ + "### Option 2: accessing GEDI data stored in Earthdata Cloud\n", + "\n", + "The other option is loading GEDI data in the memory and only saving a subset of data locally. GEDI V002 data is stored in Earthdata Cloud enabling us to access data in a different way than downloading them. The `open` function from `earthaccess` package will provide a straightforward way to access the data stored in the cloud.  The `open` function offers read and write access for our results to a particular key using a context manager. This will automatically handle the authentication and configurations needed when working locally or in the cloud. \n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb29ec73-0e25-424c-8569-aca028632e3f", + "metadata": {}, + "outputs": [], + "source": [ + "files = earthaccess.open(results)" + ] + }, + { + "cell_type": "markdown", + "id": "be9d2b29", + "metadata": {}, + "source": [ + "`h5py` package is used to read **GEDI HDF5** GEDI files." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cd6d58e2-a7ad-4cca-8975-b67ac2288429", + "metadata": {}, + "outputs": [], + "source": [ + "gedi_ds = h5py.File(files[0],'r')\n", + "print(gedi_ds.keys())\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bcb43aec-162a-4284-9ed6-f9c8d3d89eb9", + "metadata": {}, + "outputs": [], + "source": [ + "gedi_ds['METADATA']['DatasetIdentification'].attrs['shortName']" + ] + }, + { + "cell_type": "markdown", + "id": "10142aca-d2af-41bb-80e2-18acdd113dfe", + "metadata": {}, + "source": [ + "The available layers (`variables`) and datasets (`datasets`) in a `GEDI_L2B` granule can be accessed (see the commented cell below), but it will take longer for this cell to run. That is why the available GEDI datasets are saved into a JSON file (`GEDI_Datasets.json`) stored in the `data` folder and will be used here. \n", + "\n", + "To learn more about the available layers, you can view the GEDI Dictionaries provided in [GEDI products' DOI Landing pages](https://lpdaac.usgs.gov/product_search/?query=gedi&status=Operational&view=cards&sort=title). " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b858f798-26d3-40ee-9c5d-8961d97df47a", + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "# variables = []\n", + "# gedi_ds.visit(variables.append)\n", + "\n", + "# datasets = [v.split('/', 1)[-1] for v in variables if isinstance(gedi_ds[v], h5py.Dataset)]\n", + "# list(set(datasets))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b0ff4234", + "metadata": {}, + "outputs": [], + "source": [ + "del files, gedi_ds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f41e4b59-f63e-44ef-80ac-884488ec6916", + "metadata": {}, + "outputs": [], + "source": [ + "with open('data/GEDI_Datasets.json', 'r') as fp:\n", + " gedi_var = json.load(fp)\n", + "\n", + "gedi_var.keys()" + ] + }, + { + "cell_type": "markdown", + "id": "42348796-2fc2-4a45-8852-4ab7f54c1514", + "metadata": {}, + "source": [ + "Print 'gedi_var' to view the first 20 layers available in `GEDI_L2B` collection." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7b2d47c1-c004-4166-909a-6b29cf56072b", + "metadata": {}, + "outputs": [], + "source": [ + "gedi_var['GEDI_L2B'][0:20]" + ] + }, + { + "cell_type": "markdown", + "id": "b3cb56d3-4994-4b9c-b8f6-29108d65fe9a", + "metadata": {}, + "source": [ + "First, Define a subset of datasets you are interested in as alist. If you want to keep all the available datasets save `gedi_var['GEDI_L2B']` in a list. In this example, we defined a subset list for the `GEDI_L2B` product but you can subset more L1 & L2 GEDI products here if your query includes more than one product. To adjust the code, you only need to define subset data in a separate list for each product (`subset_L2A`, `subset_L2B`, and `subset_L1B`) and create a dictionary of all products and subset of layers (see the commented cell below). \n", + "\n", + "**Note that the `lat_lowestmode` and `lon_lowestmode` for both GEDI L2A and L2B products are used as reference Latitude and Longitude for each shot. For GEDI L1B, `latitude_bin0` and `longitude_bin0` can be used as reference Latitude and Longitude for each shot. The reference Latitude and Longitude for each shot in addition to `beam` and `shot_number` are included in the subset outputs by default.**\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4be15b72-9da7-4565-ae8b-48aaa470d5df", + "metadata": {}, + "outputs": [], + "source": [ + "subset_L2B = ['geolocation/degrade_flag', 'geolocation/digital_elevation_model', 'geolocation/elev_lowestmode', 'lat_highestreturn', 'geolocation/lon_highestreturn', 'geolocation/elev_highestreturn', 'l2b_quality_flag', 'rh100', 'pai', 'pai_z', 'pavd_z']\n", + "\n", + "subset_data = {'GEDI_L2B': subset_L2B }\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3302fce7-679d-49e1-97ad-f7dd2be5b889", + "metadata": {}, + "outputs": [], + "source": [ + "# # example of subsetting data for all three GEDI products distributed by LP DAAC\n", + "# subset_L2A = gedi_var['GEDI_L2A'] \n", + "# subset_L2B = gedi_var['GEDI_L2B'] \n", + "# subset_L1B = gedi_var['GEDI_L1B'] \n", + "\n", + "# subset_data = {'GEDI_L2B': subset_L2B, 'GEDI_L2A': subset_L2A, 'GEDI_L1B': subset_L1B } \n" + ] + }, + { + "cell_type": "markdown", + "id": "eafa1f38-321e-4428-90ae-99c582d6b4b0", + "metadata": {}, + "source": [ + "Next, the layers in our subset list provided above is compared with layers stored in `GEDI_Datasets.json`. This step verifies layers are valid and removes the layers that are not. It also creates full path for the layers in the list if that is not provided by user. For example, the dataset 'lat_highestreturn' is in our subset list but the full path should be 'geolocation/lat_highestreturn'. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "58383bfb-049d-4f5e-9311-a47c47248b82", + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "default = {'GEDI_L1B': ['shot_number', 'beam', 'geolocation/latitude_bin0', 'geolocation/longitude_bin0'],\n", + " 'GEDI_L2A': ['shot_number', 'beam', 'lat_lowestmode','lon_lowestmode'],\n", + " 'GEDI_L2B': ['geolocation/shot_number', 'beam', 'geolocation/lat_lowestmode','geolocation/lon_lowestmode']}\n", + "\n", + "subset_var = {}\n", + "\n", + "for p in list(subset_data.keys()):\n", + " subset = []\n", + " [subset.append(d) for d in subset_data[p] if d not in subset]\n", + " [subset.append(d) for d in default[p] if d not in subset]\n", + " datasets_p = []\n", + " for s in subset:\n", + " my_var = [v for v in gedi_var[p] if v.endswith(f'{s}')]\n", + " if len(my_var) == 1:\n", + " datasets_p.append(my_var[0])\n", + " \n", + " elif len(my_var) > 1:\n", + " my_var = [v for v in my_var if v.startswith(f'{s}')]\n", + " \n", + " for l in my_var:\n", + " if l not in datasets_p:\n", + " datasets_p.append(l) \n", + " \n", + " subset_var[p] = datasets_p\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "659b9c5c-5319-4f1e-b94e-18ee73b77e69", + "metadata": {}, + "outputs": [], + "source": [ + "subset_var" + ] + }, + { + "cell_type": "markdown", + "id": "ce0cafae-317b-43ee-a795-8af193e828fa", + "metadata": {}, + "source": [ + "In addition to layer subsetting, you can subset layers using specific beams. For instance, you can only select Full Power beams ('BEAM0101', 'BEAM0110', 'BEAM1000', 'BEAM1011')." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9d36bb79-3e22-47e3-80b9-c78a80c871bd", + "metadata": {}, + "outputs": [], + "source": [ + "beams = ['BEAM0000', 'BEAM0001', 'BEAM0010', 'BEAM0011', 'BEAM0101', 'BEAM0110', 'BEAM1000', 'BEAM1011'] # ['BEAM0101', 'BEAM0110', 'BEAM1000', 'BEAM1011']\n" + ] + }, + { + "cell_type": "markdown", + "id": "cd1b3a27-ce27-4b78-b69a-34cc5c0a210a", + "metadata": {}, + "source": [ + "Below, functions are defined to subset the GEDI granules using the beams, datasets, and area of interest. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cf23f6bb-237c-4a13-b1aa-bde30ee17e52", + "metadata": {}, + "outputs": [], + "source": [ + "def gedi_to_dataframe(granule, beams, vars):\n", + " \"\"\"\n", + " This function takes existing method of getting data from a GEDI hdf5\n", + " and makes it dynamic so it will retrieve subset of beams and variables from a list provided by the user.\n", + " All column names are taken from the hdf5 source file.\n", + " \"\"\"\n", + "\n", + " ds = earthaccess.open([granule])[0]\n", + " #read the dataset\n", + " gedi_ds = h5py.File(ds,'r')\n", + " # see what is the data product \n", + " product = gedi_ds['METADATA']['DatasetIdentification'].attrs['shortName']\n", + " print(product)\n", + " fileName = gedi_ds['METADATA']['DatasetIdentification'].attrs['fileName']\n", + " date = datetime.strptime(fileName.rsplit('_')[2], '%Y%j%H%M%S').strftime('%Y-%m-%d %H:%M:%S')\n", + " # Create empty DataFrame for this beam\n", + " df_beam = pd.DataFrame(columns=vars[product])\n", + " \n", + " for b in beams:\n", + " data_dic = {}\n", + " for v in vars[product]:\n", + " # print(b,v)\n", + " value = gedi_ds[f'{b}/{v}'][()]\n", + " data_dic[v] = value.tolist() \n", + " \n", + " df_beam = pd.concat([df_beam, pd.DataFrame(data_dic)],join=\"inner\")\n", + " \n", + " # add product, beam, file name, and date columns \n", + " df_beam.insert(0, 'product', product)\n", + " df_beam.insert(1, 'Beam', b)\n", + " df_beam.insert(2, 'fileName' , fileName)\n", + " df_beam.insert(3, 'date', date)\n", + "\n", + " # rename the latitude and longitude here to simplify the dataframe\n", + " df_beam= df_beam.rename(columns={'geolocation/lat_lowestmode': 'lat', 'geolocation/lon_lowestmode': 'lon', \n", + " 'geolocation/latitude_bin0':'lat_bin0', 'geolocation/longitude_bin0':'lon_bin0',\n", + " 'geolocation/shot_number':'shot_number',\n", + " 'lat_lowestmode': 'lat', 'lon_lowestmode': 'lon'})\n", + "\n", + " return(product, df_beam.reset_index(drop=True))\n", + "\n", + "\n", + "def clip_gedi(dataframe,geojson):\n", + " \"\"\"\n", + " This function takes the subset of GEDI data stored in a Geopandas dataframe and creates a spatial subset.\n", + " \"\"\"\n", + " #read the GeoJSON\n", + " ROI = gp.GeoDataFrame.from_file(geojson)\n", + " ROI.crs = 'EPSG:4326'\n", + "\n", + " # Take the lat/lon dataframe and convert each lat/lon to a shapely point and convert to a Geodataframe\n", + " try:\n", + " dataframe = gp.GeoDataFrame(dataframe, geometry=dataframe.apply(lambda row: Point(row.lon, row.lat), axis=1))\n", + " except:\n", + " dataframe = gp.GeoDataFrame(dataframe, geometry=dataframe.apply(lambda row: Point(row.lon_bin0, row.lat_bin0), axis=1))\n", + " \n", + " dataframe = dataframe.set_crs('EPSG:4326')\n", + "\n", + " shot_list = []\n", + " for num, geom in enumerate(dataframe['geometry']):\n", + " if ROI.contains(geom)[0]:\n", + " shot_n = dataframe.loc[num, 'shot_number']\n", + " shot_list.append(shot_n)\n", + " \n", + "\n", + " DF = dataframe.where(dataframe['shot_number'].isin(shot_list))\n", + " DF = DF.dropna().reset_index(drop=True)\n", + " return DF" + ] + }, + { + "cell_type": "markdown", + "id": "adc59f87-7b4a-4eb6-94e9-fe73c15933d4", + "metadata": {}, + "source": [ + "A separate empty dataframe is created for each product. In the first step, source granules are accessed and a subset of data by layer is created. Next, the spatial subset is implemented and the output Geodataframe object is returned. Finally, the subsets for all the source files are concatenated into a single Geodataframe. " + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "id": "c53d5f8f-4039-4aff-b575-87d3d1d679f7", + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "l2a_df = pd.DataFrame()\n", + "l2b_df = pd.DataFrame()\n", + "l1b_df = pd.DataFrame()\n", + "num = 0\n", + "for granule in results:\n", + " num += 0\n", + " # print(granule)\n", + " # subset data by band\n", + " prod, subset_df = gedi_to_dataframe(granule,beams,subset_var)\n", + " # clip to ROI \n", + " subset_df_clip = clip_gedi(subset_df, 'data/sequoia.geojson')\n", + " if 'L2A' in prod:\n", + " l2a_df = gp.GeoDataFrame(pd.concat([l2a_df, subset_df_clip]))\n", + " elif 'L2B' in prod:\n", + " l2b_df = gp.GeoDataFrame(pd.concat([l2b_df, subset_df_clip]))\n", + " elif 'L1B' in prod:\n", + " l1b_df = gp.GeoDataFrame(pd.concat([l1b_df, subset_df_clip]))\n", + "\n", + " del subset_df_clip, subset_df, prod\n", + "\n", + "# Reset the indeces\n", + "l1b_df = l1b_df.reset_index(drop=True)\n", + "l2a_df = l2a_df.reset_index(drop=True)\n", + "l2b_df = l2b_df.reset_index(drop=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cab89006-559d-4cea-a2ab-fa40a932d061", + "metadata": {}, + "outputs": [], + "source": [ + "l1b_df" + ] + }, + { + "cell_type": "markdown", + "id": "1d42f5ae-773a-4ede-831a-31c6c62657b0", + "metadata": {}, + "source": [ + "Data types are adjusted to save the GeoJSON file successfully. In this example, `pai_z` and `pavd_z` data types are updated to string. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f2764a76-ff25-474e-af69-b882409d8973", + "metadata": {}, + "outputs": [], + "source": [ + "l2b_df.columns\n", + "for c in ['pai_z', 'pavd_z']:\n", + " l2b_df[c] = str(l2b_df[c])\n" + ] + }, + { + "cell_type": "markdown", + "id": "897e537a-884e-4064-ab44-b26d5b789770", + "metadata": {}, + "source": [ + "Finally, we exported subset files as a GeoJSON using the `GeoDataFrame.to_file` function from `geopandas` package." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "185daa55-28ab-41ef-b905-6cb9350436f4", + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "from datetime import date\n", + "today = date.today()\n", + "\n", + "if len(l1b_df) != 0:\n", + " l1b_df.to_file(f'data/GEDI_L1B_{today}.geojson', driver='GeoJSON') \n", + "\n", + "if len(l2a_df) != 0:\n", + " l2a_df.to_file(f'data/GEDI_L2A_{today}.geojson', driver='GeoJSON') \n", + "\n", + "if len(l2b_df) != 0:\n", + " l2b_df.to_file(f'data/GEDI_L2B_{today}.geojson', driver='GeoJSON')" + ] + }, + { + "cell_type": "markdown", + "id": "8f0d679e-1648-4724-b5b6-ab1dd1d64ca2", + "metadata": {}, + "source": [ + "The GeoJSN object can be viewed using QGIS or programmatically. Below, a simple map is created to visualize the spatial coverage of data over our ROI." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f2a40eaf-a94a-4602-b039-de4bb7aa81d4", + "metadata": {}, + "outputs": [], + "source": [ + "from geoviews import opts, tile_sources as gvts\n", + "import geoviews\n", + "geoviews.extension('bokeh')\n", + "\n", + "gvts.EsriImagery * geoviews.Points(l2b_df, vdims=['date']).options(tools=['hover'], \n", + " height=900, width=900, size=1, color='yellow', \n", + " fontsize={'xticks': 10, 'yticks': 10, 'xlabel':16, 'ylabel': 16})\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b751275c-b79e-48cc-a006-4d7ac8a26ec3", + "metadata": {}, + "outputs": [], + "source": [ + "l2b_df" + ] + }, + { + "cell_type": "markdown", + "id": "06e514c2-89de-40c6-a704-4035a4b5d18c", + "metadata": {}, + "source": [ + "\n", + "## Contact Info: \n", + "\n", + "Email: LPDAAC@usgs.gov \n", + "Voice: +1-866-573-3222 \n", + "Organization: Land Processes Distributed Active Archive Center (LP DAAC)¹ \n", + "Website: \n", + "Date last modified: 02-20-2024 \n", + "\n", + "¹Work performed under USGS contract G15PD00467 for NASA contract NNG14HH33I. " + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.13" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/python/scripts/GEDI_Finder/GEDI_Finder.py b/python/scripts/GEDI_Finder/GEDI_Finder.py index ffb6cbf..5dc6be0 100644 --- a/python/scripts/GEDI_Finder/GEDI_Finder.py +++ b/python/scripts/GEDI_Finder/GEDI_Finder.py @@ -83,7 +83,7 @@ def date_validate(date): prod = args.product # Create dictionary of shortnames for GEDI products -concept_ids = {'GEDI01_B.002': 'C1908344278-LPDAAC_ECS', 'GEDI02_A.002': 'C1908348134-LPDAAC_ECS', 'GEDI02_B.002': 'C1908350066-LPDAAC_ECS'} +concept_ids = {'GEDI01_B.002': 'C2142749196-LPCLOUD', 'GEDI02_A.002': 'C2142771958-LPCLOUD', 'GEDI02_B.002': 'C2142776747-LPCLOUD'} if prod in concept_ids: concept_id = concept_ids[prod] @@ -94,7 +94,7 @@ def date_validate(date): def gedi_finder(concept_id, bbox, dates): # Define the base CMR granule search url, including LPDAAC provider name and max page size (2000 is the max allowed) - cmr = "https://cmr.earthdata.nasa.gov/search/granules.json?pretty=true&provider=LPDAAC_ECS&page_size=2000&concept_id=" + cmr = "https://cmr.earthdata.nasa.gov/search/granules.json?pretty=true&provider=LPCLOUD&page_size=2000&concept_id=" # CMR uses pagination for queries with more features returned than the page size diff --git a/python/scripts/GEDI_Subsetter/README.md b/python/scripts/GEDI_Subsetter/README.md index 3fdaeaa..8fb6a68 100644 --- a/python/scripts/GEDI_Subsetter/README.md +++ b/python/scripts/GEDI_Subsetter/README.md @@ -28,7 +28,7 @@ The GEDI_Subsetter.py script converts GEDI data products, stored in Hierarchical ### Getting Started -1. Download GEDI L1B-L2 Version 2 products from the [LP DAAC Data Pool](https://e4ftl01.cr.usgs.gov/GEDI/) or [Earthdata Search Client](https://search.earthdata.nasa.gov/search?q=GEDI) to a local directory (see above for applicable products). +1. Download GEDI L1B-L2 Version 1 or Version 2 products from the [LP DAAC Data Pool](https://e4ftl01.cr.usgs.gov/GEDI/) or [Earthdata Search Client](https://search.earthdata.nasa.gov/search?q=GEDI) to a local directory (see above for applicable products). > **TIP:** Use the LP DAAC [GEDI Finder](https://lpdaacsvc.cr.usgs.gov/services/gedifinder) web service to input your bounding box region of interest and find the specific GEDI granules (files) intersecting your ROI. The service will return direct links to download the files you are looking for. diff --git a/python/tutorials/GEDI_Finder_Tutorial_Python.ipynb b/python/tutorials/GEDI_Finder_Tutorial_Python.ipynb index 82bbc24..3d9795f 100644 --- a/python/tutorials/GEDI_Finder_Tutorial_Python.ipynb +++ b/python/tutorials/GEDI_Finder_Tutorial_Python.ipynb @@ -9,7 +9,7 @@ "\n", "The Global Ecosystem Dynamics Investigation ([GEDI](https://lpdaac.usgs.gov/data/get-started-data/collection-overview/missions/gedi-overview/)) mission aims to characterize ecosystem structure and dynamics to enable radically improved quantification and understanding of the Earth's carbon cycle and biodiversity. The Land Processes Distributed Active Archive Center (LP DAAC) distributes the GEDI Level 1 and Level 2 Version 1 and Version 2 products. The LP DAAC created the GEDI Finder _Web Service_ to allow users to perform spatial queries of GEDI _Version 1_ L1-L2 full-orbit granules. One of the updates for GEDI _Version 2_ included additional spatial metadata that allows users to perform spatial queries via a graphical user interface (GUI) using NASA's [Earthdata Search](https://search.earthdata.nasa.gov/search) or programmatically using NASA's [Common Metadata Repository](https://cmr.earthdata.nasa.gov/search) (CMR). Another update is that each GEDI V1 full-orbit granule has been divided into 4 sub-orbit granules in V2. \n", "\n", - "### This tutorial will show how to use Python to perform a spatial query for GEDI V2 data using NASA's CMR, how to reformat the CMR response into a list of links pointing to the intersecting sub-orbit granules on the LP DAAC Data Pool, and how to export the list of links as a text file. \n", + "### This tutorial will show how to use Python to perform a spatial query for GEDI V2 data using NASA's CMR, how to reformat the CMR response into a list of links pointing to the intersecting sub-orbit granules, and how to export the list of links as a text file. \n", "---\n", "## Use Case Example: \n", "This tutorial was developed using an example use case for a current GEDI Finder user who has been using the GEDI Finder web service in Python to find intersecting GEDI L2A Version 1 full-orbit granules over the Amazon Rainforest. The user is now looking to use the same workflow to find intersecting GEDI L2A V2 sub-orbit granules. \n", @@ -43,7 +43,7 @@ "id": "2aa9271b", "metadata": {}, "source": [ - "---\n", + "\n", "# 1. Import Packages \n", "#### All of the packages required to execute this tutorial are included in the Python Standard Library." ] @@ -85,12 +85,12 @@ "def gedi_finder(product, bbox):\n", " \n", " # Define the base CMR granule search url, including LPDAAC provider name and max page size (2000 is the max allowed)\n", - " cmr = \"https://cmr.earthdata.nasa.gov/search/granules.json?pretty=true&provider=LPDAAC_ECS&page_size=2000&concept_id=\"\n", + " cmr = \"https://cmr.earthdata.nasa.gov/search/granules.json?pretty=true&provider=LPCLOUD&page_size=2000&concept_id=\"\n", " \n", " # Set up dictionary where key is GEDI shortname + version\n", - " concept_ids = {'GEDI01_B.002': 'C1908344278-LPDAAC_ECS', \n", - " 'GEDI02_A.002': 'C1908348134-LPDAAC_ECS', \n", - " 'GEDI02_B.002': 'C1908350066-LPDAAC_ECS'}\n", + " concept_ids = {'GEDI02_B.002': 'C2142776747-LPCLOUD', \n", + " 'GEDI02_A.002': 'C2142771958-LPCLOUD', \n", + " 'GEDI01_B.002': 'C2142749196-LPCLOUD'}\n", " \n", " # CMR uses pagination for queries with more features returned than the page size\n", " page = 1\n", @@ -102,7 +102,7 @@ " while len(cmr_response) % 2000 == 0:\n", " page += 1\n", " cmr_response += r.get(f\"{cmr}{concept_ids[product]}&bounding_box={bbox}&pageNum={page}\").json()['feed']['entry']\n", - " # CMR returns more info than just the Data Pool links, below use list comprehension to return a list of DP links\n", + " # CMR returns more info than just the download links, below use list comprehension to return a list of DP links\n", " return [c['links'][0]['href'] for c in cmr_response]\n", " except:\n", " # If the request did not complete successfully, print out the response from CMR\n", @@ -114,7 +114,7 @@ "id": "3d76a504", "metadata": {}, "source": [ - "#### The function returns a list of links to download the intersecting GEDI sub-orbit V2 granules directly from the LP DAAC's Data Pool. " + "#### The function returns a list of links to download the intersecting GEDI sub-orbit V2 granules. " ] }, { @@ -179,7 +179,7 @@ "metadata": {}, "source": [ "# 4. Export Results \n", - "#### Below is a demonstration of how to take the `granules` list of Data Pool links for intersecting GEDI V2 granules and export as a text file. The text file will be written to your current working directory, and will be named based on the date and time that the file was created. " + "#### Below is a demonstration of how to take the `granules` list of download links for intersecting GEDI V2 granules and export as a text file. The text file will be written to your current working directory, and will be named based on the date and time that the file was created. " ] }, { @@ -204,28 +204,16 @@ "id": "4b1717ca", "metadata": {}, "source": [ - "
\n", - "

Contact Information

\n", - "

Material written by LP DAAC1

\n", - "
    \n", - " Contact: LPDAAC@usgs.gov
    \n", - " Voice: +1-605-594-6116
    \n", - " Organization: Land Processes Distributed Active Archive Center (LP DAAC)
    \n", - " Website: https://lpdaac.usgs.gov/
    \n", - " Date last modified: 03-29-2023
    \n", - "
\n", - " \n", - "1KBR Inc., contractor to the U.S. Geological Survey, Earth Resources Observation and Science (EROS) Center, Sioux Falls, South Dakota, 57198-001, USA. Work performed under USGS contract G15PD00467 for LP DAAC2.\n", + "## Contact Info: \n", + "\n", + "Email: LPDAAC@usgs.gov \n", + "Voice: +1-866-573-3222 \n", + "Organization: Land Processes Distributed Active Archive Center (LP DAAC)¹ \n", + "Website: \n", + "Date last modified: 02-20-2024 \n", "\n", - "2LP DAAC Work performed under NASA contract NNG14HH33I.\n", - "
" + "¹Work performed under USGS contract G15PD00467 for NASA contract NNG14HH33I. " ] - }, - { - "cell_type": "markdown", - "id": "80d5dab8", - "metadata": {}, - "source": [] } ], "metadata": { @@ -244,7 +232,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.7" + "version": "3.10.13" }, "vscode": { "interpreter": { diff --git a/python/tutorials/GEDI_L1B_V2_Tutorial.ipynb b/python/tutorials/GEDI_L1B_V2_Tutorial.ipynb index 60579ad..f202fe0 100644 --- a/python/tutorials/GEDI_L1B_V2_Tutorial.ipynb +++ b/python/tutorials/GEDI_L1B_V2_Tutorial.ipynb @@ -73,7 +73,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "---\n", + "\n", "# 1. Get Started " ] }, @@ -87,9 +87,709 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "data": { + "application/javascript": [ + "(function(root) {\n", + " function now() {\n", + " return new Date();\n", + " }\n", + "\n", + " var force = true;\n", + " var py_version = '3.2.2'.replace('rc', '-rc.').replace('.dev', '-dev.');\n", + " var is_dev = py_version.indexOf(\"+\") !== -1 || py_version.indexOf(\"-\") !== -1;\n", + " var reloading = false;\n", + " var Bokeh = root.Bokeh;\n", + " var bokeh_loaded = Bokeh != null && (Bokeh.version === py_version || (Bokeh.versions !== undefined && Bokeh.versions.has(py_version)));\n", + "\n", + " if (typeof (root._bokeh_timeout) === \"undefined\" || force) {\n", + " root._bokeh_timeout = Date.now() + 5000;\n", + " root._bokeh_failed_load = false;\n", + " }\n", + "\n", + " function run_callbacks() {\n", + " try {\n", + " root._bokeh_onload_callbacks.forEach(function(callback) {\n", + " if (callback != null)\n", + " callback();\n", + " });\n", + " } finally {\n", + " delete root._bokeh_onload_callbacks;\n", + " }\n", + " console.debug(\"Bokeh: all callbacks have finished\");\n", + " }\n", + "\n", + " function load_libs(css_urls, js_urls, js_modules, js_exports, callback) {\n", + " if (css_urls == null) css_urls = [];\n", + " if (js_urls == null) js_urls = [];\n", + " if (js_modules == null) js_modules = [];\n", + " if (js_exports == null) js_exports = {};\n", + "\n", + " root._bokeh_onload_callbacks.push(callback);\n", + "\n", + " if (root._bokeh_is_loading > 0) {\n", + " console.debug(\"Bokeh: BokehJS is being loaded, scheduling callback at\", now());\n", + " return null;\n", + " }\n", + " if (js_urls.length === 0 && js_modules.length === 0 && Object.keys(js_exports).length === 0) {\n", + " run_callbacks();\n", + " return null;\n", + " }\n", + " if (!reloading) {\n", + " console.debug(\"Bokeh: BokehJS not loaded, scheduling load and callback at\", now());\n", + " }\n", + "\n", + " function on_load() {\n", + " root._bokeh_is_loading--;\n", + " if (root._bokeh_is_loading === 0) {\n", + " console.debug(\"Bokeh: all BokehJS libraries/stylesheets loaded\");\n", + " run_callbacks()\n", + " }\n", + " }\n", + " window._bokeh_on_load = on_load\n", + "\n", + " function on_error() {\n", + " console.error(\"failed to load \" + url);\n", + " }\n", + "\n", + " var skip = [];\n", + " if (window.requirejs) {\n", + " window.requirejs.config({'packages': {}, 'paths': {'jspanel': 'https://cdn.jsdelivr.net/npm/jspanel4@4.12.0/dist/jspanel', 'jspanel-modal': 'https://cdn.jsdelivr.net/npm/jspanel4@4.12.0/dist/extensions/modal/jspanel.modal', 'jspanel-tooltip': 'https://cdn.jsdelivr.net/npm/jspanel4@4.12.0/dist/extensions/tooltip/jspanel.tooltip', 'jspanel-hint': 'https://cdn.jsdelivr.net/npm/jspanel4@4.12.0/dist/extensions/hint/jspanel.hint', 'jspanel-layout': 'https://cdn.jsdelivr.net/npm/jspanel4@4.12.0/dist/extensions/layout/jspanel.layout', 'jspanel-contextmenu': 'https://cdn.jsdelivr.net/npm/jspanel4@4.12.0/dist/extensions/contextmenu/jspanel.contextmenu', 'jspanel-dock': 'https://cdn.jsdelivr.net/npm/jspanel4@4.12.0/dist/extensions/dock/jspanel.dock', 'gridstack': 'https://cdn.jsdelivr.net/npm/gridstack@7.2.3/dist/gridstack-all', 'notyf': 'https://cdn.jsdelivr.net/npm/notyf@3/notyf.min'}, 'shim': {'jspanel': {'exports': 'jsPanel'}, 'gridstack': {'exports': 'GridStack'}}});\n", + " require([\"jspanel\"], function(jsPanel) {\n", + "\twindow.jsPanel = jsPanel\n", + "\ton_load()\n", + " })\n", + " require([\"jspanel-modal\"], function() {\n", + "\ton_load()\n", + " })\n", + " require([\"jspanel-tooltip\"], function() {\n", + "\ton_load()\n", + " })\n", + " require([\"jspanel-hint\"], function() {\n", + "\ton_load()\n", + " })\n", + " require([\"jspanel-layout\"], function() {\n", + "\ton_load()\n", + " })\n", + " require([\"jspanel-contextmenu\"], function() {\n", + "\ton_load()\n", + " })\n", + " require([\"jspanel-dock\"], function() {\n", + "\ton_load()\n", + " })\n", + " require([\"gridstack\"], function(GridStack) {\n", + "\twindow.GridStack = GridStack\n", + "\ton_load()\n", + " })\n", + " require([\"notyf\"], function() {\n", + "\ton_load()\n", + " })\n", + " root._bokeh_is_loading = css_urls.length + 9;\n", + " } else {\n", + " root._bokeh_is_loading = css_urls.length + js_urls.length + js_modules.length + Object.keys(js_exports).length;\n", + " }\n", + "\n", + " var existing_stylesheets = []\n", + " var links = document.getElementsByTagName('link')\n", + " for (var i = 0; i < links.length; i++) {\n", + " var link = links[i]\n", + " if (link.href != null) {\n", + "\texisting_stylesheets.push(link.href)\n", + " }\n", + " }\n", + " for (var i = 0; i < css_urls.length; i++) {\n", + " var url = css_urls[i];\n", + " if (existing_stylesheets.indexOf(url) !== -1) {\n", + "\ton_load()\n", + "\tcontinue;\n", + " }\n", + " const element = document.createElement(\"link\");\n", + " element.onload = on_load;\n", + " element.onerror = on_error;\n", + " element.rel = \"stylesheet\";\n", + " element.type = \"text/css\";\n", + " element.href = url;\n", + " console.debug(\"Bokeh: injecting link tag for BokehJS stylesheet: \", url);\n", + " document.body.appendChild(element);\n", + " } if (((window['jsPanel'] !== undefined) && (!(window['jsPanel'] instanceof HTMLElement))) || window.requirejs) {\n", + " var urls = ['https://cdn.holoviz.org/panel/1.2.3/dist/bundled/floatpanel/jspanel4@4.12.0/dist/jspanel.js', 'https://cdn.holoviz.org/panel/1.2.3/dist/bundled/floatpanel/jspanel4@4.12.0/dist/extensions/modal/jspanel.modal.js', 'https://cdn.holoviz.org/panel/1.2.3/dist/bundled/floatpanel/jspanel4@4.12.0/dist/extensions/tooltip/jspanel.tooltip.js', 'https://cdn.holoviz.org/panel/1.2.3/dist/bundled/floatpanel/jspanel4@4.12.0/dist/extensions/hint/jspanel.hint.js', 'https://cdn.holoviz.org/panel/1.2.3/dist/bundled/floatpanel/jspanel4@4.12.0/dist/extensions/layout/jspanel.layout.js', 'https://cdn.holoviz.org/panel/1.2.3/dist/bundled/floatpanel/jspanel4@4.12.0/dist/extensions/contextmenu/jspanel.contextmenu.js', 'https://cdn.holoviz.org/panel/1.2.3/dist/bundled/floatpanel/jspanel4@4.12.0/dist/extensions/dock/jspanel.dock.js'];\n", + " for (var i = 0; i < urls.length; i++) {\n", + " skip.push(urls[i])\n", + " }\n", + " } if (((window['GridStack'] !== undefined) && (!(window['GridStack'] instanceof HTMLElement))) || window.requirejs) {\n", + " var urls = ['https://cdn.holoviz.org/panel/1.2.3/dist/bundled/gridstack/gridstack@7.2.3/dist/gridstack-all.js'];\n", + " for (var i = 0; i < urls.length; i++) {\n", + " skip.push(urls[i])\n", + " }\n", + " } if (((window['Notyf'] !== undefined) && (!(window['Notyf'] instanceof HTMLElement))) || window.requirejs) {\n", + " var urls = ['https://cdn.holoviz.org/panel/1.2.3/dist/bundled/notificationarea/notyf@3/notyf.min.js'];\n", + " for (var i = 0; i < urls.length; i++) {\n", + " skip.push(urls[i])\n", + " }\n", + " } var existing_scripts = []\n", + " var scripts = document.getElementsByTagName('script')\n", + " for (var i = 0; i < scripts.length; i++) {\n", + " var script = scripts[i]\n", + " if (script.src != null) {\n", + "\texisting_scripts.push(script.src)\n", + " }\n", + " }\n", + " for (var i = 0; i < js_urls.length; i++) {\n", + " var url = js_urls[i];\n", + " if (skip.indexOf(url) !== -1 || existing_scripts.indexOf(url) !== -1) {\n", + "\tif (!window.requirejs) {\n", + "\t on_load();\n", + "\t}\n", + "\tcontinue;\n", + " }\n", + " var element = document.createElement('script');\n", + " element.onload = on_load;\n", + " element.onerror = on_error;\n", + " element.async = false;\n", + " element.src = url;\n", + " console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n", + " document.head.appendChild(element);\n", + " }\n", + " for (var i = 0; i < js_modules.length; i++) {\n", + " var url = js_modules[i];\n", + " if (skip.indexOf(url) !== -1 || existing_scripts.indexOf(url) !== -1) {\n", + "\tif (!window.requirejs) {\n", + "\t on_load();\n", + "\t}\n", + "\tcontinue;\n", + " }\n", + " var element = document.createElement('script');\n", + " element.onload = on_load;\n", + " element.onerror = on_error;\n", + " element.async = false;\n", + " element.src = url;\n", + " element.type = \"module\";\n", + " console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n", + " document.head.appendChild(element);\n", + " }\n", + " for (const name in js_exports) {\n", + " var url = js_exports[name];\n", + " if (skip.indexOf(url) >= 0 || root[name] != null) {\n", + "\tif (!window.requirejs) {\n", + "\t on_load();\n", + "\t}\n", + "\tcontinue;\n", + " }\n", + " var element = document.createElement('script');\n", + " element.onerror = on_error;\n", + " element.async = false;\n", + " element.type = \"module\";\n", + " console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n", + " element.textContent = `\n", + " import ${name} from \"${url}\"\n", + " window.${name} = ${name}\n", + " window._bokeh_on_load()\n", + " `\n", + " document.head.appendChild(element);\n", + " }\n", + " if (!js_urls.length && !js_modules.length) {\n", + " on_load()\n", + " }\n", + " };\n", + "\n", + " function inject_raw_css(css) {\n", + " const element = document.createElement(\"style\");\n", + " element.appendChild(document.createTextNode(css));\n", + " document.body.appendChild(element);\n", + " }\n", + "\n", + " var js_urls = [\"https://cdn.bokeh.org/bokeh/release/bokeh-3.2.2.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-gl-3.2.2.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-widgets-3.2.2.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-tables-3.2.2.min.js\", \"https://cdn.holoviz.org/panel/1.2.3/dist/panel.min.js\", \"https://cdn.jsdelivr.net/npm/@holoviz/geoviews@1.11.0/dist/geoviews.min.js\"];\n", + " var js_modules = [];\n", + " var js_exports = {};\n", + " var css_urls = [];\n", + " var inline_js = [ function(Bokeh) {\n", + " Bokeh.set_log_level(\"info\");\n", + " },\n", + "function(Bokeh) {} // ensure no trailing comma for IE\n", + " ];\n", + "\n", + " function run_inline_js() {\n", + " if ((root.Bokeh !== undefined) || (force === true)) {\n", + " for (var i = 0; i < inline_js.length; i++) {\n", + " inline_js[i].call(root, root.Bokeh);\n", + " }\n", + " // Cache old bokeh versions\n", + " if (Bokeh != undefined && !reloading) {\n", + "\tvar NewBokeh = root.Bokeh;\n", + "\tif (Bokeh.versions === undefined) {\n", + "\t Bokeh.versions = new Map();\n", + "\t}\n", + "\tif (NewBokeh.version !== Bokeh.version) {\n", + "\t Bokeh.versions.set(NewBokeh.version, NewBokeh)\n", + "\t}\n", + "\troot.Bokeh = Bokeh;\n", + " }} else if (Date.now() < root._bokeh_timeout) {\n", + " setTimeout(run_inline_js, 100);\n", + " } else if (!root._bokeh_failed_load) {\n", + " console.log(\"Bokeh: BokehJS failed to load within specified timeout.\");\n", + " root._bokeh_failed_load = true;\n", + " }\n", + " root._bokeh_is_initializing = false\n", + " }\n", + "\n", + " function load_or_wait() {\n", + " // Implement a backoff loop that tries to ensure we do not load multiple\n", + " // versions of Bokeh and its dependencies at the same time.\n", + " // In recent versions we use the root._bokeh_is_initializing flag\n", + " // to determine whether there is an ongoing attempt to initialize\n", + " // bokeh, however for backward compatibility we also try to ensure\n", + " // that we do not start loading a newer (Panel>=1.0 and Bokeh>3) version\n", + " // before older versions are fully initialized.\n", + " if (root._bokeh_is_initializing && Date.now() > root._bokeh_timeout) {\n", + " root._bokeh_is_initializing = false;\n", + " root._bokeh_onload_callbacks = undefined;\n", + " console.log(\"Bokeh: BokehJS was loaded multiple times but one version failed to initialize.\");\n", + " load_or_wait();\n", + " } else if (root._bokeh_is_initializing || (typeof root._bokeh_is_initializing === \"undefined\" && root._bokeh_onload_callbacks !== undefined)) {\n", + " setTimeout(load_or_wait, 100);\n", + " } else {\n", + " Bokeh = root.Bokeh;\n", + " bokeh_loaded = Bokeh != null && (Bokeh.version === py_version || (Bokeh.versions !== undefined && Bokeh.versions.has(py_version)));\n", + " root._bokeh_is_initializing = true\n", + " root._bokeh_onload_callbacks = []\n", + " if (!reloading && (!bokeh_loaded || is_dev)) {\n", + "\troot.Bokeh = undefined;\n", + " }\n", + " load_libs(css_urls, js_urls, js_modules, js_exports, function() {\n", + "\tconsole.debug(\"Bokeh: BokehJS plotting callback run at\", now());\n", + "\trun_inline_js();\n", + " });\n", + " }\n", + " }\n", + " // Give older versions of the autoload script a head-start to ensure\n", + " // they initialize before we start loading newer version.\n", + " setTimeout(load_or_wait, 100)\n", + "}(window));" + ], + "application/vnd.holoviews_load.v0+json": "(function(root) {\n function now() {\n return new Date();\n }\n\n var force = true;\n var py_version = '3.2.2'.replace('rc', '-rc.').replace('.dev', '-dev.');\n var is_dev = py_version.indexOf(\"+\") !== -1 || py_version.indexOf(\"-\") !== -1;\n var reloading = false;\n var Bokeh = root.Bokeh;\n var bokeh_loaded = Bokeh != null && (Bokeh.version === py_version || (Bokeh.versions !== undefined && Bokeh.versions.has(py_version)));\n\n if (typeof (root._bokeh_timeout) === \"undefined\" || force) {\n root._bokeh_timeout = Date.now() + 5000;\n root._bokeh_failed_load = false;\n }\n\n function run_callbacks() {\n try {\n root._bokeh_onload_callbacks.forEach(function(callback) {\n if (callback != null)\n callback();\n });\n } finally {\n delete root._bokeh_onload_callbacks;\n }\n console.debug(\"Bokeh: all callbacks have finished\");\n }\n\n function load_libs(css_urls, js_urls, js_modules, js_exports, callback) {\n if (css_urls == null) css_urls = [];\n if (js_urls == null) js_urls = [];\n if (js_modules == null) js_modules = [];\n if (js_exports == null) js_exports = {};\n\n root._bokeh_onload_callbacks.push(callback);\n\n if (root._bokeh_is_loading > 0) {\n console.debug(\"Bokeh: BokehJS is being loaded, scheduling callback at\", now());\n return null;\n }\n if (js_urls.length === 0 && js_modules.length === 0 && Object.keys(js_exports).length === 0) {\n run_callbacks();\n return null;\n }\n if (!reloading) {\n console.debug(\"Bokeh: BokehJS not loaded, scheduling load and callback at\", now());\n }\n\n function on_load() {\n root._bokeh_is_loading--;\n if (root._bokeh_is_loading === 0) {\n console.debug(\"Bokeh: all BokehJS libraries/stylesheets loaded\");\n run_callbacks()\n }\n }\n window._bokeh_on_load = on_load\n\n function on_error() {\n console.error(\"failed to load \" + url);\n }\n\n var skip = [];\n if (window.requirejs) {\n window.requirejs.config({'packages': {}, 'paths': {'jspanel': 'https://cdn.jsdelivr.net/npm/jspanel4@4.12.0/dist/jspanel', 'jspanel-modal': 'https://cdn.jsdelivr.net/npm/jspanel4@4.12.0/dist/extensions/modal/jspanel.modal', 'jspanel-tooltip': 'https://cdn.jsdelivr.net/npm/jspanel4@4.12.0/dist/extensions/tooltip/jspanel.tooltip', 'jspanel-hint': 'https://cdn.jsdelivr.net/npm/jspanel4@4.12.0/dist/extensions/hint/jspanel.hint', 'jspanel-layout': 'https://cdn.jsdelivr.net/npm/jspanel4@4.12.0/dist/extensions/layout/jspanel.layout', 'jspanel-contextmenu': 'https://cdn.jsdelivr.net/npm/jspanel4@4.12.0/dist/extensions/contextmenu/jspanel.contextmenu', 'jspanel-dock': 'https://cdn.jsdelivr.net/npm/jspanel4@4.12.0/dist/extensions/dock/jspanel.dock', 'gridstack': 'https://cdn.jsdelivr.net/npm/gridstack@7.2.3/dist/gridstack-all', 'notyf': 'https://cdn.jsdelivr.net/npm/notyf@3/notyf.min'}, 'shim': {'jspanel': {'exports': 'jsPanel'}, 'gridstack': {'exports': 'GridStack'}}});\n require([\"jspanel\"], function(jsPanel) {\n\twindow.jsPanel = jsPanel\n\ton_load()\n })\n require([\"jspanel-modal\"], function() {\n\ton_load()\n })\n require([\"jspanel-tooltip\"], function() {\n\ton_load()\n })\n require([\"jspanel-hint\"], function() {\n\ton_load()\n })\n require([\"jspanel-layout\"], function() {\n\ton_load()\n })\n require([\"jspanel-contextmenu\"], function() {\n\ton_load()\n })\n require([\"jspanel-dock\"], function() {\n\ton_load()\n })\n require([\"gridstack\"], function(GridStack) {\n\twindow.GridStack = GridStack\n\ton_load()\n })\n require([\"notyf\"], function() {\n\ton_load()\n })\n root._bokeh_is_loading = css_urls.length + 9;\n } else {\n root._bokeh_is_loading = css_urls.length + js_urls.length + js_modules.length + Object.keys(js_exports).length;\n }\n\n var existing_stylesheets = []\n var links = document.getElementsByTagName('link')\n for (var i = 0; i < links.length; i++) {\n var link = links[i]\n if (link.href != null) {\n\texisting_stylesheets.push(link.href)\n }\n }\n for (var i = 0; i < css_urls.length; i++) {\n var url = css_urls[i];\n if (existing_stylesheets.indexOf(url) !== -1) {\n\ton_load()\n\tcontinue;\n }\n const element = document.createElement(\"link\");\n element.onload = on_load;\n element.onerror = on_error;\n element.rel = \"stylesheet\";\n element.type = \"text/css\";\n element.href = url;\n console.debug(\"Bokeh: injecting link tag for BokehJS stylesheet: \", url);\n document.body.appendChild(element);\n } if (((window['jsPanel'] !== undefined) && (!(window['jsPanel'] instanceof HTMLElement))) || window.requirejs) {\n var urls = ['https://cdn.holoviz.org/panel/1.2.3/dist/bundled/floatpanel/jspanel4@4.12.0/dist/jspanel.js', 'https://cdn.holoviz.org/panel/1.2.3/dist/bundled/floatpanel/jspanel4@4.12.0/dist/extensions/modal/jspanel.modal.js', 'https://cdn.holoviz.org/panel/1.2.3/dist/bundled/floatpanel/jspanel4@4.12.0/dist/extensions/tooltip/jspanel.tooltip.js', 'https://cdn.holoviz.org/panel/1.2.3/dist/bundled/floatpanel/jspanel4@4.12.0/dist/extensions/hint/jspanel.hint.js', 'https://cdn.holoviz.org/panel/1.2.3/dist/bundled/floatpanel/jspanel4@4.12.0/dist/extensions/layout/jspanel.layout.js', 'https://cdn.holoviz.org/panel/1.2.3/dist/bundled/floatpanel/jspanel4@4.12.0/dist/extensions/contextmenu/jspanel.contextmenu.js', 'https://cdn.holoviz.org/panel/1.2.3/dist/bundled/floatpanel/jspanel4@4.12.0/dist/extensions/dock/jspanel.dock.js'];\n for (var i = 0; i < urls.length; i++) {\n skip.push(urls[i])\n }\n } if (((window['GridStack'] !== undefined) && (!(window['GridStack'] instanceof HTMLElement))) || window.requirejs) {\n var urls = ['https://cdn.holoviz.org/panel/1.2.3/dist/bundled/gridstack/gridstack@7.2.3/dist/gridstack-all.js'];\n for (var i = 0; i < urls.length; i++) {\n skip.push(urls[i])\n }\n } if (((window['Notyf'] !== undefined) && (!(window['Notyf'] instanceof HTMLElement))) || window.requirejs) {\n var urls = ['https://cdn.holoviz.org/panel/1.2.3/dist/bundled/notificationarea/notyf@3/notyf.min.js'];\n for (var i = 0; i < urls.length; i++) {\n skip.push(urls[i])\n }\n } var existing_scripts = []\n var scripts = document.getElementsByTagName('script')\n for (var i = 0; i < scripts.length; i++) {\n var script = scripts[i]\n if (script.src != null) {\n\texisting_scripts.push(script.src)\n }\n }\n for (var i = 0; i < js_urls.length; i++) {\n var url = js_urls[i];\n if (skip.indexOf(url) !== -1 || existing_scripts.indexOf(url) !== -1) {\n\tif (!window.requirejs) {\n\t on_load();\n\t}\n\tcontinue;\n }\n var element = document.createElement('script');\n element.onload = on_load;\n element.onerror = on_error;\n element.async = false;\n element.src = url;\n console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n document.head.appendChild(element);\n }\n for (var i = 0; i < js_modules.length; i++) {\n var url = js_modules[i];\n if (skip.indexOf(url) !== -1 || existing_scripts.indexOf(url) !== -1) {\n\tif (!window.requirejs) {\n\t on_load();\n\t}\n\tcontinue;\n }\n var element = document.createElement('script');\n element.onload = on_load;\n element.onerror = on_error;\n element.async = false;\n element.src = url;\n element.type = \"module\";\n console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n document.head.appendChild(element);\n }\n for (const name in js_exports) {\n var url = js_exports[name];\n if (skip.indexOf(url) >= 0 || root[name] != null) {\n\tif (!window.requirejs) {\n\t on_load();\n\t}\n\tcontinue;\n }\n var element = document.createElement('script');\n element.onerror = on_error;\n element.async = false;\n element.type = \"module\";\n console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n element.textContent = `\n import ${name} from \"${url}\"\n window.${name} = ${name}\n window._bokeh_on_load()\n `\n document.head.appendChild(element);\n }\n if (!js_urls.length && !js_modules.length) {\n on_load()\n }\n };\n\n function inject_raw_css(css) {\n const element = document.createElement(\"style\");\n element.appendChild(document.createTextNode(css));\n document.body.appendChild(element);\n }\n\n var js_urls = [\"https://cdn.bokeh.org/bokeh/release/bokeh-3.2.2.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-gl-3.2.2.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-widgets-3.2.2.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-tables-3.2.2.min.js\", \"https://cdn.holoviz.org/panel/1.2.3/dist/panel.min.js\", \"https://cdn.jsdelivr.net/npm/@holoviz/geoviews@1.11.0/dist/geoviews.min.js\"];\n var js_modules = [];\n var js_exports = {};\n var css_urls = [];\n var inline_js = [ function(Bokeh) {\n Bokeh.set_log_level(\"info\");\n },\nfunction(Bokeh) {} // ensure no trailing comma for IE\n ];\n\n function run_inline_js() {\n if ((root.Bokeh !== undefined) || (force === true)) {\n for (var i = 0; i < inline_js.length; i++) {\n inline_js[i].call(root, root.Bokeh);\n }\n // Cache old bokeh versions\n if (Bokeh != undefined && !reloading) {\n\tvar NewBokeh = root.Bokeh;\n\tif (Bokeh.versions === undefined) {\n\t Bokeh.versions = new Map();\n\t}\n\tif (NewBokeh.version !== Bokeh.version) {\n\t Bokeh.versions.set(NewBokeh.version, NewBokeh)\n\t}\n\troot.Bokeh = Bokeh;\n }} else if (Date.now() < root._bokeh_timeout) {\n setTimeout(run_inline_js, 100);\n } else if (!root._bokeh_failed_load) {\n console.log(\"Bokeh: BokehJS failed to load within specified timeout.\");\n root._bokeh_failed_load = true;\n }\n root._bokeh_is_initializing = false\n }\n\n function load_or_wait() {\n // Implement a backoff loop that tries to ensure we do not load multiple\n // versions of Bokeh and its dependencies at the same time.\n // In recent versions we use the root._bokeh_is_initializing flag\n // to determine whether there is an ongoing attempt to initialize\n // bokeh, however for backward compatibility we also try to ensure\n // that we do not start loading a newer (Panel>=1.0 and Bokeh>3) version\n // before older versions are fully initialized.\n if (root._bokeh_is_initializing && Date.now() > root._bokeh_timeout) {\n root._bokeh_is_initializing = false;\n root._bokeh_onload_callbacks = undefined;\n console.log(\"Bokeh: BokehJS was loaded multiple times but one version failed to initialize.\");\n load_or_wait();\n } else if (root._bokeh_is_initializing || (typeof root._bokeh_is_initializing === \"undefined\" && root._bokeh_onload_callbacks !== undefined)) {\n setTimeout(load_or_wait, 100);\n } else {\n Bokeh = root.Bokeh;\n bokeh_loaded = Bokeh != null && (Bokeh.version === py_version || (Bokeh.versions !== undefined && Bokeh.versions.has(py_version)));\n root._bokeh_is_initializing = true\n root._bokeh_onload_callbacks = []\n if (!reloading && (!bokeh_loaded || is_dev)) {\n\troot.Bokeh = undefined;\n }\n load_libs(css_urls, js_urls, js_modules, js_exports, function() {\n\tconsole.debug(\"Bokeh: BokehJS plotting callback run at\", now());\n\trun_inline_js();\n });\n }\n }\n // Give older versions of the autoload script a head-start to ensure\n // they initialize before we start loading newer version.\n setTimeout(load_or_wait, 100)\n}(window));" + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/javascript": [ + "\n", + "if ((window.PyViz === undefined) || (window.PyViz instanceof HTMLElement)) {\n", + " window.PyViz = {comms: {}, comm_status:{}, kernels:{}, receivers: {}, plot_index: []}\n", + "}\n", + "\n", + "\n", + " function JupyterCommManager() {\n", + " }\n", + "\n", + " JupyterCommManager.prototype.register_target = function(plot_id, comm_id, msg_handler) {\n", + " if (window.comm_manager || ((window.Jupyter !== undefined) && (Jupyter.notebook.kernel != null))) {\n", + " var comm_manager = window.comm_manager || Jupyter.notebook.kernel.comm_manager;\n", + " comm_manager.register_target(comm_id, function(comm) {\n", + " comm.on_msg(msg_handler);\n", + " });\n", + " } else if ((plot_id in window.PyViz.kernels) && (window.PyViz.kernels[plot_id])) {\n", + " window.PyViz.kernels[plot_id].registerCommTarget(comm_id, function(comm) {\n", + " comm.onMsg = msg_handler;\n", + " });\n", + " } else if (typeof google != 'undefined' && google.colab.kernel != null) {\n", + " google.colab.kernel.comms.registerTarget(comm_id, (comm) => {\n", + " var messages = comm.messages[Symbol.asyncIterator]();\n", + " function processIteratorResult(result) {\n", + " var message = result.value;\n", + " console.log(message)\n", + " var content = {data: message.data, comm_id};\n", + " var buffers = []\n", + " for (var buffer of message.buffers || []) {\n", + " buffers.push(new DataView(buffer))\n", + " }\n", + " var metadata = message.metadata || {};\n", + " var msg = {content, buffers, metadata}\n", + " msg_handler(msg);\n", + " return messages.next().then(processIteratorResult);\n", + " }\n", + " return messages.next().then(processIteratorResult);\n", + " })\n", + " }\n", + " }\n", + "\n", + " JupyterCommManager.prototype.get_client_comm = function(plot_id, comm_id, msg_handler) {\n", + " if (comm_id in window.PyViz.comms) {\n", + " return window.PyViz.comms[comm_id];\n", + " } else if (window.comm_manager || ((window.Jupyter !== undefined) && (Jupyter.notebook.kernel != null))) {\n", + " var comm_manager = window.comm_manager || Jupyter.notebook.kernel.comm_manager;\n", + " var comm = comm_manager.new_comm(comm_id, {}, {}, {}, comm_id);\n", + " if (msg_handler) {\n", + " comm.on_msg(msg_handler);\n", + " }\n", + " } else if ((plot_id in window.PyViz.kernels) && (window.PyViz.kernels[plot_id])) {\n", + " var comm = window.PyViz.kernels[plot_id].connectToComm(comm_id);\n", + " comm.open();\n", + " if (msg_handler) {\n", + " comm.onMsg = msg_handler;\n", + " }\n", + " } else if (typeof google != 'undefined' && google.colab.kernel != null) {\n", + " var comm_promise = google.colab.kernel.comms.open(comm_id)\n", + " comm_promise.then((comm) => {\n", + " window.PyViz.comms[comm_id] = comm;\n", + " if (msg_handler) {\n", + " var messages = comm.messages[Symbol.asyncIterator]();\n", + " function processIteratorResult(result) {\n", + " var message = result.value;\n", + " var content = {data: message.data};\n", + " var metadata = message.metadata || {comm_id};\n", + " var msg = {content, metadata}\n", + " msg_handler(msg);\n", + " return messages.next().then(processIteratorResult);\n", + " }\n", + " return messages.next().then(processIteratorResult);\n", + " }\n", + " }) \n", + " var sendClosure = (data, metadata, buffers, disposeOnDone) => {\n", + " return comm_promise.then((comm) => {\n", + " comm.send(data, metadata, buffers, disposeOnDone);\n", + " });\n", + " };\n", + " var comm = {\n", + " send: sendClosure\n", + " };\n", + " }\n", + " window.PyViz.comms[comm_id] = comm;\n", + " return comm;\n", + " }\n", + " window.PyViz.comm_manager = new JupyterCommManager();\n", + " \n", + "\n", + "\n", + "var JS_MIME_TYPE = 'application/javascript';\n", + "var HTML_MIME_TYPE = 'text/html';\n", + "var EXEC_MIME_TYPE = 'application/vnd.holoviews_exec.v0+json';\n", + "var CLASS_NAME = 'output';\n", + "\n", + "/**\n", + " * Render data to the DOM node\n", + " */\n", + "function render(props, node) {\n", + " var div = document.createElement(\"div\");\n", + " var script = document.createElement(\"script\");\n", + " node.appendChild(div);\n", + " node.appendChild(script);\n", + "}\n", + "\n", + "/**\n", + " * Handle when a new output is added\n", + " */\n", + "function handle_add_output(event, handle) {\n", + " var output_area = handle.output_area;\n", + " var output = handle.output;\n", + " if ((output.data == undefined) || (!output.data.hasOwnProperty(EXEC_MIME_TYPE))) {\n", + " return\n", + " }\n", + " var id = output.metadata[EXEC_MIME_TYPE][\"id\"];\n", + " var toinsert = output_area.element.find(\".\" + CLASS_NAME.split(' ')[0]);\n", + " if (id !== undefined) {\n", + " var nchildren = toinsert.length;\n", + " var html_node = toinsert[nchildren-1].children[0];\n", + " html_node.innerHTML = output.data[HTML_MIME_TYPE];\n", + " var scripts = [];\n", + " var nodelist = html_node.querySelectorAll(\"script\");\n", + " for (var i in nodelist) {\n", + " if (nodelist.hasOwnProperty(i)) {\n", + " scripts.push(nodelist[i])\n", + " }\n", + " }\n", + "\n", + " scripts.forEach( function (oldScript) {\n", + " var newScript = document.createElement(\"script\");\n", + " var attrs = [];\n", + " var nodemap = oldScript.attributes;\n", + " for (var j in nodemap) {\n", + " if (nodemap.hasOwnProperty(j)) {\n", + " attrs.push(nodemap[j])\n", + " }\n", + " }\n", + " attrs.forEach(function(attr) { newScript.setAttribute(attr.name, attr.value) });\n", + " newScript.appendChild(document.createTextNode(oldScript.innerHTML));\n", + " oldScript.parentNode.replaceChild(newScript, oldScript);\n", + " });\n", + " if (JS_MIME_TYPE in output.data) {\n", + " toinsert[nchildren-1].children[1].textContent = output.data[JS_MIME_TYPE];\n", + " }\n", + " output_area._hv_plot_id = id;\n", + " if ((window.Bokeh !== undefined) && (id in Bokeh.index)) {\n", + " window.PyViz.plot_index[id] = Bokeh.index[id];\n", + " } else {\n", + " window.PyViz.plot_index[id] = null;\n", + " }\n", + " } else if (output.metadata[EXEC_MIME_TYPE][\"server_id\"] !== undefined) {\n", + " var bk_div = document.createElement(\"div\");\n", + " bk_div.innerHTML = output.data[HTML_MIME_TYPE];\n", + " var script_attrs = bk_div.children[0].attributes;\n", + " for (var i = 0; i < script_attrs.length; i++) {\n", + " toinsert[toinsert.length - 1].childNodes[1].setAttribute(script_attrs[i].name, script_attrs[i].value);\n", + " }\n", + " // store reference to server id on output_area\n", + " output_area._bokeh_server_id = output.metadata[EXEC_MIME_TYPE][\"server_id\"];\n", + " }\n", + "}\n", + "\n", + "/**\n", + " * Handle when an output is cleared or removed\n", + " */\n", + "function handle_clear_output(event, handle) {\n", + " var id = handle.cell.output_area._hv_plot_id;\n", + " var server_id = handle.cell.output_area._bokeh_server_id;\n", + " if (((id === undefined) || !(id in PyViz.plot_index)) && (server_id !== undefined)) { return; }\n", + " var comm = window.PyViz.comm_manager.get_client_comm(\"hv-extension-comm\", \"hv-extension-comm\", function () {});\n", + " if (server_id !== null) {\n", + " comm.send({event_type: 'server_delete', 'id': server_id});\n", + " return;\n", + " } else if (comm !== null) {\n", + " comm.send({event_type: 'delete', 'id': id});\n", + " }\n", + " delete PyViz.plot_index[id];\n", + " if ((window.Bokeh !== undefined) & (id in window.Bokeh.index)) {\n", + " var doc = window.Bokeh.index[id].model.document\n", + " doc.clear();\n", + " const i = window.Bokeh.documents.indexOf(doc);\n", + " if (i > -1) {\n", + " window.Bokeh.documents.splice(i, 1);\n", + " }\n", + " }\n", + "}\n", + "\n", + "/**\n", + " * Handle kernel restart event\n", + " */\n", + "function handle_kernel_cleanup(event, handle) {\n", + " delete PyViz.comms[\"hv-extension-comm\"];\n", + " window.PyViz.plot_index = {}\n", + "}\n", + "\n", + "/**\n", + " * Handle update_display_data messages\n", + " */\n", + "function handle_update_output(event, handle) {\n", + " handle_clear_output(event, {cell: {output_area: handle.output_area}})\n", + " handle_add_output(event, handle)\n", + "}\n", + "\n", + "function register_renderer(events, OutputArea) {\n", + " function append_mime(data, metadata, element) {\n", + " // create a DOM node to render to\n", + " var toinsert = this.create_output_subarea(\n", + " metadata,\n", + " CLASS_NAME,\n", + " EXEC_MIME_TYPE\n", + " );\n", + " this.keyboard_manager.register_events(toinsert);\n", + " // Render to node\n", + " var props = {data: data, metadata: metadata[EXEC_MIME_TYPE]};\n", + " render(props, toinsert[0]);\n", + " element.append(toinsert);\n", + " return toinsert\n", + " }\n", + "\n", + " events.on('output_added.OutputArea', handle_add_output);\n", + " events.on('output_updated.OutputArea', handle_update_output);\n", + " events.on('clear_output.CodeCell', handle_clear_output);\n", + " events.on('delete.Cell', handle_clear_output);\n", + " events.on('kernel_ready.Kernel', handle_kernel_cleanup);\n", + "\n", + " OutputArea.prototype.register_mime_type(EXEC_MIME_TYPE, append_mime, {\n", + " safe: true,\n", + " index: 0\n", + " });\n", + "}\n", + "\n", + "if (window.Jupyter !== undefined) {\n", + " try {\n", + " var events = require('base/js/events');\n", + " var OutputArea = require('notebook/js/outputarea').OutputArea;\n", + " if (OutputArea.prototype.mime_types().indexOf(EXEC_MIME_TYPE) == -1) {\n", + " register_renderer(events, OutputArea);\n", + " }\n", + " } catch(err) {\n", + " }\n", + "}\n" + ], + "application/vnd.holoviews_load.v0+json": "\nif ((window.PyViz === undefined) || (window.PyViz instanceof HTMLElement)) {\n window.PyViz = {comms: {}, comm_status:{}, kernels:{}, receivers: {}, plot_index: []}\n}\n\n\n function JupyterCommManager() {\n }\n\n JupyterCommManager.prototype.register_target = function(plot_id, comm_id, msg_handler) {\n if (window.comm_manager || ((window.Jupyter !== undefined) && (Jupyter.notebook.kernel != null))) {\n var comm_manager = window.comm_manager || Jupyter.notebook.kernel.comm_manager;\n comm_manager.register_target(comm_id, function(comm) {\n comm.on_msg(msg_handler);\n });\n } else if ((plot_id in window.PyViz.kernels) && (window.PyViz.kernels[plot_id])) {\n window.PyViz.kernels[plot_id].registerCommTarget(comm_id, function(comm) {\n comm.onMsg = msg_handler;\n });\n } else if (typeof google != 'undefined' && google.colab.kernel != null) {\n google.colab.kernel.comms.registerTarget(comm_id, (comm) => {\n var messages = comm.messages[Symbol.asyncIterator]();\n function processIteratorResult(result) {\n var message = result.value;\n console.log(message)\n var content = {data: message.data, comm_id};\n var buffers = []\n for (var buffer of message.buffers || []) {\n buffers.push(new DataView(buffer))\n }\n var metadata = message.metadata || {};\n var msg = {content, buffers, metadata}\n msg_handler(msg);\n return messages.next().then(processIteratorResult);\n }\n return messages.next().then(processIteratorResult);\n })\n }\n }\n\n JupyterCommManager.prototype.get_client_comm = function(plot_id, comm_id, msg_handler) {\n if (comm_id in window.PyViz.comms) {\n return window.PyViz.comms[comm_id];\n } else if (window.comm_manager || ((window.Jupyter !== undefined) && (Jupyter.notebook.kernel != null))) {\n var comm_manager = window.comm_manager || Jupyter.notebook.kernel.comm_manager;\n var comm = comm_manager.new_comm(comm_id, {}, {}, {}, comm_id);\n if (msg_handler) {\n comm.on_msg(msg_handler);\n }\n } else if ((plot_id in window.PyViz.kernels) && (window.PyViz.kernels[plot_id])) {\n var comm = window.PyViz.kernels[plot_id].connectToComm(comm_id);\n comm.open();\n if (msg_handler) {\n comm.onMsg = msg_handler;\n }\n } else if (typeof google != 'undefined' && google.colab.kernel != null) {\n var comm_promise = google.colab.kernel.comms.open(comm_id)\n comm_promise.then((comm) => {\n window.PyViz.comms[comm_id] = comm;\n if (msg_handler) {\n var messages = comm.messages[Symbol.asyncIterator]();\n function processIteratorResult(result) {\n var message = result.value;\n var content = {data: message.data};\n var metadata = message.metadata || {comm_id};\n var msg = {content, metadata}\n msg_handler(msg);\n return messages.next().then(processIteratorResult);\n }\n return messages.next().then(processIteratorResult);\n }\n }) \n var sendClosure = (data, metadata, buffers, disposeOnDone) => {\n return comm_promise.then((comm) => {\n comm.send(data, metadata, buffers, disposeOnDone);\n });\n };\n var comm = {\n send: sendClosure\n };\n }\n window.PyViz.comms[comm_id] = comm;\n return comm;\n }\n window.PyViz.comm_manager = new JupyterCommManager();\n \n\n\nvar JS_MIME_TYPE = 'application/javascript';\nvar HTML_MIME_TYPE = 'text/html';\nvar EXEC_MIME_TYPE = 'application/vnd.holoviews_exec.v0+json';\nvar CLASS_NAME = 'output';\n\n/**\n * Render data to the DOM node\n */\nfunction render(props, node) {\n var div = document.createElement(\"div\");\n var script = document.createElement(\"script\");\n node.appendChild(div);\n node.appendChild(script);\n}\n\n/**\n * Handle when a new output is added\n */\nfunction handle_add_output(event, handle) {\n var output_area = handle.output_area;\n var output = handle.output;\n if ((output.data == undefined) || (!output.data.hasOwnProperty(EXEC_MIME_TYPE))) {\n return\n }\n var id = output.metadata[EXEC_MIME_TYPE][\"id\"];\n var toinsert = output_area.element.find(\".\" + CLASS_NAME.split(' ')[0]);\n if (id !== undefined) {\n var nchildren = toinsert.length;\n var html_node = toinsert[nchildren-1].children[0];\n html_node.innerHTML = output.data[HTML_MIME_TYPE];\n var scripts = [];\n var nodelist = html_node.querySelectorAll(\"script\");\n for (var i in nodelist) {\n if (nodelist.hasOwnProperty(i)) {\n scripts.push(nodelist[i])\n }\n }\n\n scripts.forEach( function (oldScript) {\n var newScript = document.createElement(\"script\");\n var attrs = [];\n var nodemap = oldScript.attributes;\n for (var j in nodemap) {\n if (nodemap.hasOwnProperty(j)) {\n attrs.push(nodemap[j])\n }\n }\n attrs.forEach(function(attr) { newScript.setAttribute(attr.name, attr.value) });\n newScript.appendChild(document.createTextNode(oldScript.innerHTML));\n oldScript.parentNode.replaceChild(newScript, oldScript);\n });\n if (JS_MIME_TYPE in output.data) {\n toinsert[nchildren-1].children[1].textContent = output.data[JS_MIME_TYPE];\n }\n output_area._hv_plot_id = id;\n if ((window.Bokeh !== undefined) && (id in Bokeh.index)) {\n window.PyViz.plot_index[id] = Bokeh.index[id];\n } else {\n window.PyViz.plot_index[id] = null;\n }\n } else if (output.metadata[EXEC_MIME_TYPE][\"server_id\"] !== undefined) {\n var bk_div = document.createElement(\"div\");\n bk_div.innerHTML = output.data[HTML_MIME_TYPE];\n var script_attrs = bk_div.children[0].attributes;\n for (var i = 0; i < script_attrs.length; i++) {\n toinsert[toinsert.length - 1].childNodes[1].setAttribute(script_attrs[i].name, script_attrs[i].value);\n }\n // store reference to server id on output_area\n output_area._bokeh_server_id = output.metadata[EXEC_MIME_TYPE][\"server_id\"];\n }\n}\n\n/**\n * Handle when an output is cleared or removed\n */\nfunction handle_clear_output(event, handle) {\n var id = handle.cell.output_area._hv_plot_id;\n var server_id = handle.cell.output_area._bokeh_server_id;\n if (((id === undefined) || !(id in PyViz.plot_index)) && (server_id !== undefined)) { return; }\n var comm = window.PyViz.comm_manager.get_client_comm(\"hv-extension-comm\", \"hv-extension-comm\", function () {});\n if (server_id !== null) {\n comm.send({event_type: 'server_delete', 'id': server_id});\n return;\n } else if (comm !== null) {\n comm.send({event_type: 'delete', 'id': id});\n }\n delete PyViz.plot_index[id];\n if ((window.Bokeh !== undefined) & (id in window.Bokeh.index)) {\n var doc = window.Bokeh.index[id].model.document\n doc.clear();\n const i = window.Bokeh.documents.indexOf(doc);\n if (i > -1) {\n window.Bokeh.documents.splice(i, 1);\n }\n }\n}\n\n/**\n * Handle kernel restart event\n */\nfunction handle_kernel_cleanup(event, handle) {\n delete PyViz.comms[\"hv-extension-comm\"];\n window.PyViz.plot_index = {}\n}\n\n/**\n * Handle update_display_data messages\n */\nfunction handle_update_output(event, handle) {\n handle_clear_output(event, {cell: {output_area: handle.output_area}})\n handle_add_output(event, handle)\n}\n\nfunction register_renderer(events, OutputArea) {\n function append_mime(data, metadata, element) {\n // create a DOM node to render to\n var toinsert = this.create_output_subarea(\n metadata,\n CLASS_NAME,\n EXEC_MIME_TYPE\n );\n this.keyboard_manager.register_events(toinsert);\n // Render to node\n var props = {data: data, metadata: metadata[EXEC_MIME_TYPE]};\n render(props, toinsert[0]);\n element.append(toinsert);\n return toinsert\n }\n\n events.on('output_added.OutputArea', handle_add_output);\n events.on('output_updated.OutputArea', handle_update_output);\n events.on('clear_output.CodeCell', handle_clear_output);\n events.on('delete.Cell', handle_clear_output);\n events.on('kernel_ready.Kernel', handle_kernel_cleanup);\n\n OutputArea.prototype.register_mime_type(EXEC_MIME_TYPE, append_mime, {\n safe: true,\n index: 0\n });\n}\n\nif (window.Jupyter !== undefined) {\n try {\n var events = require('base/js/events');\n var OutputArea = require('notebook/js/outputarea').OutputArea;\n if (OutputArea.prototype.mime_types().indexOf(EXEC_MIME_TYPE) == -1) {\n register_renderer(events, OutputArea);\n }\n } catch(err) {\n }\n}\n" + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "\n", + "
\n", + "\n", + "\n", + "\n", + " \n", + " \n", + "\n", + "\n", + " \n", + " \n", + "\n", + "\n", + "
\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ "import os\n", "import h5py\n", @@ -103,7 +803,9 @@ "from shapely.geometry import Point\n", "import warnings\n", "from shapely.errors import ShapelyDeprecationWarning\n", - "warnings.filterwarnings(\"ignore\", category=ShapelyDeprecationWarning) " + "warnings.filterwarnings(\"ignore\", category=ShapelyDeprecationWarning) \n", + "\n", + "os.chdir('../../') " ] }, { @@ -116,32 +818,102 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 2, "metadata": {}, "outputs": [], "source": [ - "inDir = os.getcwd().split(\"python\")[0] # Set input directory to the current working directory\n", - "os.chdir(inDir)\n" + "inDir = os.getcwd()\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "#### In this section, the GEDI .h5 file has been downloaded to the `inDir` defined above. You will need to download the file directly from the LP DAAC Data Pool in order to execute this tutorial.\n", + "#### You will need to download the file in order to execute this tutorial. Make sure to download the file into the `data` directory defined above.\n", "### Direct Link to file:\n", - " - https://e4ftl01.cr.usgs.gov/GEDI/GEDI01_B.002/2019.06.19/GEDI01_B_2019170155833_O02932_02_T02267_02_005_01_V002.h5 \n", - " \n", - "#### Make sure to download the file into the `inDir` directory defined above." + " - https://data.lpdaac.earthdatacloud.nasa.gov/lp-prod-protected/GEDI01_B.002/GEDI01_B_2019170155833_O02932_02_T02267_02_005_01_V002/GEDI01_B_2019170155833_O02932_02_T02267_02_005_01_V002.h5 \n", + "\n", + "Alternatively, you can use `earthaccess` package to download the data. \n" ] }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "gediFiles = [g for g in os.listdir(inDir+\"\\\\data\") if g.startswith('GEDI01_B') and g.endswith('.h5')] # List all GEDI L1B .h5 files in inDir\n", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "ea57d2d500b041a9ae6c6e6b7ba76ee3", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "QUEUEING TASKS | : 0%| | 0/1 [00:00" ] }, @@ -163,11 +935,22 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "L1B = f'data\\\\{gediFiles[0]}'\n", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'data/GEDI01_B_2019170155833_O02932_02_T02267_02_005_01_V002.h5'" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "L1B = f'data/{gediFiles[0]}'\n", "L1B" ] }, @@ -196,7 +979,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 7, "metadata": {}, "outputs": [], "source": [ @@ -212,9 +995,28 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['BEAM0000',\n", + " 'BEAM0001',\n", + " 'BEAM0010',\n", + " 'BEAM0011',\n", + " 'BEAM0101',\n", + " 'BEAM0110',\n", + " 'BEAM1000',\n", + " 'BEAM1011',\n", + " 'METADATA']" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "list(gediL1B.keys())" ] @@ -229,9 +1031,20 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['DatasetIdentification']" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "list(gediL1B['METADATA'])" ] @@ -245,18 +1058,48 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "PGEVersion\n", + "VersionID\n", + "abstract\n", + "characterSet\n", + "creationDate\n", + "credit\n", + "fileName\n", + "language\n", + "originatorOrganizationName\n", + "purpose\n", + "shortName\n", + "spatialRepresentationType\n", + "status\n", + "topicCategory\n", + "uuid\n" + ] + } + ], "source": [ "for g in gediL1B['METADATA']['DatasetIdentification'].attrs: print(g) " ] }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The purpose of the L1B dataset is to provide geolocated waveforms and supporting datasets for each laser shot for all eight GEDI beams. This includes corrected and smoothed waveforms, geolocation parameters, and geophysical corrections.\n" + ] + } + ], "source": [ "print(gediL1B['METADATA']['DatasetIdentification'].attrs['purpose'])" ] @@ -277,9 +1120,27 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['BEAM0000',\n", + " 'BEAM0001',\n", + " 'BEAM0010',\n", + " 'BEAM0011',\n", + " 'BEAM0101',\n", + " 'BEAM0110',\n", + " 'BEAM1000',\n", + " 'BEAM1011']" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "beamNames = [g for g in gediL1B.keys() if g.startswith('BEAM')]\n", "beamNames" @@ -294,18 +1155,43 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "description\n", + "wp-l1a-to-l1b_githash\n", + "wp-l1a-to-l1b_version\n" + ] + } + ], "source": [ "for g in gediL1B['BEAM0000'].attrs: print(g)" ] }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "BEAM0000 is a Coverage beam\n", + "BEAM0001 is a Coverage beam\n", + "BEAM0010 is a Coverage beam\n", + "BEAM0011 is a Coverage beam\n", + "BEAM0101 is a Full power beam\n", + "BEAM0110 is a Full power beam\n", + "BEAM1000 is a Full power beam\n", + "BEAM1011 is a Full power beam\n" + ] + } + ], "source": [ "for b in beamNames: \n", " print(f\"{b} is a {gediL1B[b].attrs['description']}\")" @@ -320,7 +1206,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 15, "metadata": {}, "outputs": [], "source": [ @@ -337,9 +1223,29 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 16, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['BEAM0110/all_samples_sum',\n", + " 'BEAM0110/ancillary/master_time_epoch',\n", + " 'BEAM0110/ancillary/mean_samples',\n", + " 'BEAM0110/ancillary/smoothing_width',\n", + " 'BEAM0110/beam',\n", + " 'BEAM0110/channel',\n", + " 'BEAM0110/geolocation/altitude_instrument',\n", + " 'BEAM0110/geolocation/altitude_instrument_error',\n", + " 'BEAM0110/geolocation/bounce_time_offset_bin0',\n", + " 'BEAM0110/geolocation/bounce_time_offset_bin0_error']" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "gediL1B_objs = []\n", "gediL1B.visit(gediL1B_objs.append) # Retrieve list of datasets\n", @@ -351,7 +1257,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "---\n", + "\n", "# 3. Visualize a GEDI Orbit \n", "#### In the section below, import GEDI L1B SDS layers into a `GeoPandas` GeoDataFrame for the beam specified above. \n", "#### Use the `latitude_bin0` and `longitude_bin0` to create a `shapely` point for each GEDI shot location. " @@ -373,9 +1279,178 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 17, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
BeamShot NumberLongitudeLatitudeStale Return FlagDegrade
0BEAM011029320600200419869-142.75569226.92389610
1BEAM011029320600200419969-142.73656726.94324210
2BEAM011029320600200420069-142.71743326.96256910
3BEAM011029320600200420169-142.69829426.98189110
4BEAM011029320600200420269-142.67913627.00118710
.....................
1066BEAM011029320600200526469-80.19845251.796858180
1067BEAM011029320600200526569-80.11467751.797029180
1068BEAM011029320600200526669-80.03217351.797173080
1069BEAM011029320600200526769-79.94857151.797245180
1070BEAM011029320600200526869-79.86569851.797246180
\n", + "

1071 rows × 6 columns

\n", + "
" + ], + "text/plain": [ + " Beam Shot Number Longitude Latitude Stale Return Flag \\\n", + "0 BEAM0110 29320600200419869 -142.755692 26.923896 1 \n", + "1 BEAM0110 29320600200419969 -142.736567 26.943242 1 \n", + "2 BEAM0110 29320600200420069 -142.717433 26.962569 1 \n", + "3 BEAM0110 29320600200420169 -142.698294 26.981891 1 \n", + "4 BEAM0110 29320600200420269 -142.679136 27.001187 1 \n", + "... ... ... ... ... ... \n", + "1066 BEAM0110 29320600200526469 -80.198452 51.796858 1 \n", + "1067 BEAM0110 29320600200526569 -80.114677 51.797029 1 \n", + "1068 BEAM0110 29320600200526669 -80.032173 51.797173 0 \n", + "1069 BEAM0110 29320600200526769 -79.948571 51.797245 1 \n", + "1070 BEAM0110 29320600200526869 -79.865698 51.797246 1 \n", + "\n", + " Degrade \n", + "0 0 \n", + "1 0 \n", + "2 0 \n", + "3 0 \n", + "4 0 \n", + "... ... \n", + "1066 80 \n", + "1067 80 \n", + "1068 80 \n", + "1069 80 \n", + "1070 80 \n", + "\n", + "[1071 rows x 6 columns]" + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "lonSample, latSample, shotSample, srfSample, degradeSample, beamSample = [], [], [], [], [], [] # Set up lists to store data\n", "\n", @@ -411,7 +1486,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 18, "metadata": {}, "outputs": [], "source": [ @@ -428,7 +1503,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 19, "metadata": {}, "outputs": [], "source": [ @@ -445,9 +1520,31 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 20, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "0 POINT (-142.75569 26.92390)\n", + "1 POINT (-142.73657 26.94324)\n", + "2 POINT (-142.71743 26.96257)\n", + "3 POINT (-142.69829 26.98189)\n", + "4 POINT (-142.67914 27.00119)\n", + " ... \n", + "1066 POINT (-80.19845 51.79686)\n", + "1067 POINT (-80.11468 51.79703)\n", + "1068 POINT (-80.03217 51.79717)\n", + "1069 POINT (-79.94857 51.79725)\n", + "1070 POINT (-79.86570 51.79725)\n", + "Name: geometry, Length: 1071, dtype: geometry" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "# Convert to a Geodataframe\n", "latslons = gp.GeoDataFrame(latslons)\n", @@ -464,11 +1561,25 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 21, "metadata": { "scrolled": true }, - "outputs": [], + "outputs": [ + { + "data": { + "image/svg+xml": [ + "" + ], + "text/plain": [ + "" + ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "latslons['geometry'][0]" ] @@ -483,7 +1594,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 22, "metadata": {}, "outputs": [], "source": [ @@ -503,27 +1614,108 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 23, "metadata": {}, "outputs": [], "source": [ - "redwoodNP = gp.GeoDataFrame.from_file('Data\\\\RedwoodNP.geojson') # Import GeoJSON as GeoDataFrame" + "redwoodNP = gp.GeoDataFrame.from_file('data/RedwoodNP.geojson') # Import GeoJSON as GeoDataFrame" ] }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 24, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
GIS_LOC_IDUNIT_CODEGROUP_CODEUNIT_NAMEUNIT_TYPEMETA_MIDFLANDS_CODEDATE_EDITGIS_NOTESgeometry
0NoneREDWNoneRedwoodNational ParkNoneNoneNoneShifted 0.06 milesMULTIPOLYGON (((-124.01829 41.44539, -124.0184...
\n", + "
" + ], + "text/plain": [ + " GIS_LOC_ID UNIT_CODE GROUP_CODE UNIT_NAME UNIT_TYPE META_MIDF \\\n", + "0 None REDW None Redwood National Park None \n", + "\n", + " LANDS_CODE DATE_EDIT GIS_NOTES \\\n", + "0 None None Shifted 0.06 miles \n", + "\n", + " geometry \n", + "0 MULTIPOLYGON (((-124.01829 41.44539, -124.0184... " + ] + }, + "execution_count": 24, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "redwoodNP" ] }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 25, + "metadata": {}, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "" + ], + "text/plain": [ + "" + ] + }, + "execution_count": 25, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "redwoodNP['geometry'][0] # Plot GeoDataFrame" ] @@ -537,9 +1729,20 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 26, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['Beam', 'Shot Number', 'Stale Return Flag', 'Degrade']" + ] + }, + "execution_count": 26, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "# Create a list of geodataframe columns to be included as attributes in the output map\n", "vdims = []\n", @@ -558,9 +1761,101 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 27, + "metadata": {}, + "outputs": [ + { + "data": {}, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.holoviews_exec.v0+json": "", + "text/html": [ + "
\n", + "
\n", + "
\n", + "" + ], + "text/plain": [ + ":Overlay\n", + " .Polygons.I :Polygons [Longitude,Latitude]\n", + " .WMTS.I :WMTS [Longitude,Latitude]\n", + " .Points.I :Points [Longitude,Latitude] (Beam,Shot Number,Stale Return Flag,Degrade)" + ] + }, + "execution_count": 27, + "metadata": { + "application/vnd.holoviews_exec.v0+json": { + "id": "p1005" + } + }, + "output_type": "execute_result" + } + ], "source": [ "# Call the function for plotting the GEDI points\n", "gv.Polygons(redwoodNP['geometry']).opts(line_color='red', color=None) * pointVisual(latslons, vdims = vdims)" @@ -591,9 +1886,18 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 28, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "stale_return_flag: Indicates that a \"stale\" cue point from the coarse search algorithm is being used.\n", + "degrade: Non-zero values indicate the shot occured during a degraded period. A non-zero tens digit indicates degraded attitude, a non-zero ones digit indicates a degraded trajectory. 3X=ADF CHU solution unavailable (ST-2); 4X=Platform attitude; 5X=Poor solution (filter covariance large); 6X=Data outage (platform attitude gap also); 7X=ST 1+2 unavailable (similar boresight FOV); 8X=ST 1+2+3 unavailable; 9X=ST 1+2+3 and ISS unavailable; X1=Maneuver; X2=GPS data gap; X3=ST blinding; X4=Other; X5=GPS receiver clock drift; X6=X5+X1; X7=X5+X2; X8=X5+X3; X9=X5+X4.\n" + ] + } + ], "source": [ "print(f\"stale_return_flag: {gediL1B[b]['stale_return_flag'].attrs['description']}\")\n", "print(f\"degrade: {gediL1B[b]['geolocation']['degrade'].attrs['description']}\")" @@ -601,18 +1905,37 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 29, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 29, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "gediL1B[b]['stale_return_flag'].attrs.keys()" ] }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 30, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "stale_return_flag: Indicates that a \"stale\" cue point from the coarse search algorithm is being used.\n" + ] + } + ], "source": [ "print(f\"stale_return_flag: {gediL1B[b]['stale_return_flag'].attrs['description']}\")" ] @@ -647,7 +1970,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 31, "metadata": {}, "outputs": [], "source": [ @@ -656,9 +1979,20 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 32, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "45732" + ] + }, + "execution_count": 32, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "index = np.where(gediL1B[f'{beamNames[0]}/shot_number'][()]==shot)[0][0] # Set the index for the shot identified above\n", "index" @@ -666,7 +2000,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 33, "metadata": {}, "outputs": [], "source": [ @@ -677,7 +2011,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "---\n", + "\n", "# 4. Subset and Visualize Waveforms \n", "#### In this section, learn how to extract and subset specific waveforms and plot them using `holoviews`. " ] @@ -698,7 +2032,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 34, "metadata": {}, "outputs": [], "source": [ @@ -717,9 +2051,17 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 35, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "rxwaveform is The corrected receive (RX) waveforms. Use rx_sample_count and rx_sample_start_index to identify the location of each waveform.\n" + ] + } + ], "source": [ "print(f\"rxwaveform is {gediL1B[sdsWaveform].attrs['description']}\")" ] @@ -733,9 +2075,18 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 36, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "rx_sample_count is The number of sample intervals (elements) in each RX waveform.\n", + "rx_sample_start_index is The index in the rxwaveform dataset of the first element of each RX waveform. The indices start at 1.\n" + ] + } + ], "source": [ "print(f\"rx_sample_count is {sdsCount.attrs['description']}\")\n", "print(f\"rx_sample_start_index is {sdsStart.attrs['description']}\")" @@ -750,7 +2101,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 37, "metadata": {}, "outputs": [], "source": [ @@ -767,7 +2118,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 38, "metadata": { "scrolled": true }, @@ -787,11 +2138,19 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 39, "metadata": { "scrolled": true }, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The waveform located at: 41.28541314606824, -124.03007585226648 (shot ID: 29320600200465601, index 45732) is from beam BEAM0110 and is stored in rxwaveform beginning at index 64939440 and ending at index 64940252\n" + ] + } + ], "source": [ "print(f\"The waveform located at: {str(wfLat)}, {str(wfLon)} (shot ID: {wfShot}, index {index}) is from beam {beamNames[0]} \\\n", " and is stored in rxwaveform beginning at index {wfStart} and ending at index {wfStart + wfCount}\")" @@ -806,7 +2165,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 40, "metadata": {}, "outputs": [], "source": [ @@ -825,16 +2184,24 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 41, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "151,959,880\n" + ] + } + ], "source": [ "print(\"{:,}\".format(gediL1B[sdsWaveform].shape[0]))" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 42, "metadata": {}, "outputs": [], "source": [ @@ -852,7 +2219,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 43, "metadata": {}, "outputs": [], "source": [ @@ -862,9 +2229,117 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 44, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
Amplitude (DN)Elevation (m)
0226.00996437.354491
1225.36068737.204811
2225.37399337.055132
3226.16575636.905453
4227.48011836.755773
.........
807231.473907-83.436681
808230.386887-83.586361
809228.637268-83.736040
810226.949203-83.885719
811226.124496-84.035399
\n", + "

812 rows × 2 columns

\n", + "
" + ], + "text/plain": [ + " Amplitude (DN) Elevation (m)\n", + "0 226.009964 37.354491\n", + "1 225.360687 37.204811\n", + "2 225.373993 37.055132\n", + "3 226.165756 36.905453\n", + "4 227.480118 36.755773\n", + ".. ... ...\n", + "807 231.473907 -83.436681\n", + "808 230.386887 -83.586361\n", + "809 228.637268 -83.736040\n", + "810 226.949203 -83.885719\n", + "811 226.124496 -84.035399\n", + "\n", + "[812 rows x 2 columns]" + ] + }, + "execution_count": 44, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "# match the waveform amplitude values with the elevation and convert to Pandas df\n", "wvDF = pd.DataFrame({'Amplitude (DN)': waveform, 'Elevation (m)': zStretch})\n", @@ -880,9 +2355,98 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 45, + "metadata": {}, + "outputs": [ + { + "data": {}, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.holoviews_exec.v0+json": "", + "text/html": [ + "
\n", + "
\n", + "
\n", + "" + ], + "text/plain": [ + ":Curve [Amplitude (DN)] (Elevation (m))" + ] + }, + "execution_count": 45, + "metadata": { + "application/vnd.holoviews_exec.v0+json": { + "id": "p1112" + } + }, + "output_type": "execute_result" + } + ], "source": [ "hv.Curve(wvDF) # Basic line graph plotting the waveform" ] @@ -897,9 +2461,98 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 46, + "metadata": {}, + "outputs": [ + { + "data": {}, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.holoviews_exec.v0+json": "", + "text/html": [ + "
\n", + "
\n", + "
\n", + "" + ], + "text/plain": [ + ":Curve [Amplitude (DN)] (Elevation (m))" + ] + }, + "execution_count": 46, + "metadata": { + "application/vnd.holoviews_exec.v0+json": { + "id": "p1167" + } + }, + "output_type": "execute_result" + } + ], "source": [ "# Create a holoviews interactive Curve plot with additional parameters defining the plot aesthetics \n", "wfVis = hv.Curve(wvDF).opts(color='darkgreen', tools=['hover'], height=500, width=400,\n", @@ -919,7 +2572,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 47, "metadata": {}, "outputs": [], "source": [ @@ -935,7 +2588,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 48, "metadata": {}, "outputs": [], "source": [ @@ -945,7 +2598,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 49, "metadata": {}, "outputs": [], "source": [ @@ -967,7 +2620,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 50, "metadata": {}, "outputs": [], "source": [ @@ -980,7 +2633,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 51, "metadata": {}, "outputs": [], "source": [ @@ -993,9 +2646,98 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 52, + "metadata": {}, + "outputs": [ + { + "data": {}, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.holoviews_exec.v0+json": "", + "text/html": [ + "
\n", + "
\n", + "
\n", + "" + ], + "text/plain": [ + ":Curve [Amplitude (DN)] (Elevation (m))" + ] + }, + "execution_count": 52, + "metadata": { + "application/vnd.holoviews_exec.v0+json": { + "id": "p1225" + } + }, + "output_type": "execute_result" + } + ], "source": [ "# Create a holoviews interactive Curve plot with additional parameters defining the plot aesthetics \n", "visL1B1 = hv.Curve(wvDF).opts(color='darkgreen', tools=['hover'], height=500, width=400,\n", @@ -1013,7 +2755,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 53, "metadata": {}, "outputs": [], "source": [ @@ -1023,7 +2765,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 54, "metadata": {}, "outputs": [], "source": [ @@ -1045,7 +2787,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 55, "metadata": {}, "outputs": [], "source": [ @@ -1058,7 +2800,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 56, "metadata": {}, "outputs": [], "source": [ @@ -1071,9 +2813,98 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 57, + "metadata": {}, + "outputs": [ + { + "data": {}, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.holoviews_exec.v0+json": "", + "text/html": [ + "
\n", + "
\n", + "
\n", + "" + ], + "text/plain": [ + ":Curve [Amplitude (DN)] (Elevation (m))" + ] + }, + "execution_count": 57, + "metadata": { + "application/vnd.holoviews_exec.v0+json": { + "id": "p1283" + } + }, + "output_type": "execute_result" + } + ], "source": [ "# Create a holoviews interactive Curve plot with additional parameters defining the plot aesthetics \n", "visL1B2 = hv.Curve(wvDF).opts(color='darkgreen', tools=['hover'], height=500, width=400,\n", @@ -1084,7 +2915,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 58, "metadata": {}, "outputs": [], "source": [ @@ -1094,7 +2925,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 59, "metadata": {}, "outputs": [], "source": [ @@ -1116,7 +2947,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 60, "metadata": {}, "outputs": [], "source": [ @@ -1129,7 +2960,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 61, "metadata": {}, "outputs": [], "source": [ @@ -1142,9 +2973,98 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 62, + "metadata": {}, + "outputs": [ + { + "data": {}, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.holoviews_exec.v0+json": "", + "text/html": [ + "
\n", + "
\n", + "
\n", + "" + ], + "text/plain": [ + ":Curve [Amplitude (DN)] (Elevation (m))" + ] + }, + "execution_count": 62, + "metadata": { + "application/vnd.holoviews_exec.v0+json": { + "id": "p1341" + } + }, + "output_type": "execute_result" + } + ], "source": [ "# Create a holoviews interactive Curve plot with additional parameters defining the plot aesthetics \n", "visL1B3 = hv.Curve(wvDF).opts(color='darkgreen', tools=['hover'], height=500, width=400,\n", @@ -1162,9 +3082,112 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 63, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/srv/conda/envs/notebook/lib/python3.10/site-packages/holoviews/plotting/bokeh/plot.py:959: UserWarning: found multiple competing values for 'toolbar.active_drag' property; using the latest value\n", + " layout_plot = gridplot(\n", + "/srv/conda/envs/notebook/lib/python3.10/site-packages/holoviews/plotting/bokeh/plot.py:959: UserWarning: found multiple competing values for 'toolbar.active_scroll' property; using the latest value\n", + " layout_plot = gridplot(\n" + ] + }, + { + "data": {}, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.holoviews_exec.v0+json": "", + "text/html": [ + "
\n", + "
\n", + "
\n", + "" + ], + "text/plain": [ + ":Layout\n", + " .Curve.I :Curve [Amplitude (DN)] (Elevation (m))\n", + " .Curve.II :Curve [Amplitude (DN)] (Elevation (m))\n", + " .Curve.III :Curve [Amplitude (DN)] (Elevation (m))\n", + " .Curve.IV :Curve [Amplitude (DN)] (Elevation (m))" + ] + }, + "execution_count": 63, + "metadata": { + "application/vnd.holoviews_exec.v0+json": { + "id": "p1399" + } + }, + "output_type": "execute_result" + } + ], "source": [ "# The \"+\" symbol will plot multiple saved holoviews plots together\n", "visL1B1.opts(width=240) + visL1B2.opts(width=240, labelled=[]) + visL1B3.opts(width=240, labelled=[]) + \\\n", @@ -1187,7 +3210,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 64, "metadata": {}, "outputs": [], "source": [ @@ -1199,7 +3222,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 65, "metadata": {}, "outputs": [], "source": [ @@ -1208,18 +3231,121 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 66, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['Beam', 'Shot Number', 'Stale Return Flag', 'Degrade']" + ] + }, + "execution_count": 66, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "vdims" ] }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 67, + "metadata": {}, + "outputs": [ + { + "data": {}, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.holoviews_exec.v0+json": "", + "text/html": [ + "
\n", + "
\n", + "
\n", + "" + ], + "text/plain": [ + ":Overlay\n", + " .Polygons.I :Polygons [Longitude,Latitude]\n", + " .WMTS.I :WMTS [Longitude,Latitude]\n", + " .Points.I :Points [Longitude,Latitude] (Shot Number,Stale Return Flag,Degrade)" + ] + }, + "execution_count": 67, + "metadata": { + "application/vnd.holoviews_exec.v0+json": { + "id": "p1611" + } + }, + "output_type": "execute_result" + } + ], "source": [ "# Create a list of geodataframe columns to be included as attributes in the output map\n", "vdims = []\n", @@ -1250,7 +3376,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 68, "metadata": {}, "outputs": [], "source": [ @@ -1261,7 +3387,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "---\n", + "\n", "# 5. Quality Filtering \n", "#### Now that you have the desired layers imported as a dataframe for the selected shots, let's perform quality filtering.\n", "#### Below, remove any shots where the `stale_return_flag` and `degrade` flag is not set to 0. \n", @@ -1270,7 +3396,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 69, "metadata": { "scrolled": true }, @@ -1288,7 +3414,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 70, "metadata": {}, "outputs": [], "source": [ @@ -1304,7 +3430,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 71, "metadata": {}, "outputs": [], "source": [ @@ -1313,9 +3439,17 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 72, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Quality filtering complete, 4 high quality shots remaining.\n" + ] + } + ], "source": [ "print(f\"Quality filtering complete, {len(latlons)} high quality shots remaining.\")" ] @@ -1329,7 +3463,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 73, "metadata": {}, "outputs": [], "source": [ @@ -1340,7 +3474,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "---\n", + "\n", "# 6. Plot Profile Transects \n", "#### In this section, plot a transect subset using waveforms." ] @@ -1361,16 +3495,24 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 74, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "45731\n" + ] + } + ], "source": [ "print(index)" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 75, "metadata": {}, "outputs": [], "source": [ @@ -1396,7 +3538,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 76, "metadata": {}, "outputs": [], "source": [ @@ -1413,7 +3555,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 77, "metadata": {}, "outputs": [], "source": [ @@ -1446,9 +3588,98 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 78, + "metadata": {}, + "outputs": [ + { + "data": {}, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.holoviews_exec.v0+json": "", + "text/html": [ + "
\n", + "
\n", + "
\n", + "" + ], + "text/plain": [ + ":Path [x,y] (Amplitude)" + ] + }, + "execution_count": 78, + "metadata": { + "application/vnd.holoviews_exec.v0+json": { + "id": "p1718" + } + }, + "output_type": "execute_result" + } + ], "source": [ "import warnings\n", "warnings.filterwarnings('ignore')\n", @@ -1472,7 +3703,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 79, "metadata": {}, "outputs": [], "source": [ @@ -1483,7 +3714,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "---\n", + "\n", "# 7. Spatial Visualization\n", "#### Section 7 combines many of the techniques learned above including how to import GEDI datasets, perform quality filtering, spatial subsetting, and visualization. " ] @@ -1504,7 +3735,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 80, "metadata": {}, "outputs": [], "source": [ @@ -1513,9 +3744,27 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 81, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['BEAM0000',\n", + " 'BEAM0001',\n", + " 'BEAM0010',\n", + " 'BEAM0011',\n", + " 'BEAM0101',\n", + " 'BEAM0110',\n", + " 'BEAM1000',\n", + " 'BEAM1011']" + ] + }, + "execution_count": 81, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "beamNames" ] @@ -1529,7 +3778,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 82, "metadata": {}, "outputs": [], "source": [ @@ -1539,7 +3788,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 83, "metadata": {}, "outputs": [], "source": [ @@ -1567,7 +3816,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 84, "metadata": {}, "outputs": [], "source": [ @@ -1579,7 +3828,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 85, "metadata": {}, "outputs": [], "source": [ @@ -1596,9 +3845,20 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 86, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "790135" + ] + }, + "execution_count": 86, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "len(allDF)" ] @@ -1612,16 +3872,30 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 87, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(-124.16015705494489,\n", + " 41.080601363502545,\n", + " -123.84950230520286,\n", + " 41.83981133687605)" + ] + }, + "execution_count": 87, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "redwoodNP.envelope[0].bounds" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 88, "metadata": {}, "outputs": [], "source": [ @@ -1637,7 +3911,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 89, "metadata": {}, "outputs": [], "source": [ @@ -1656,7 +3930,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 90, "metadata": {}, "outputs": [], "source": [ @@ -1665,9 +3939,20 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 91, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "4477" + ] + }, + "execution_count": 91, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "len(allDF)" ] @@ -1681,9 +3966,20 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 92, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "4475" + ] + }, + "execution_count": 92, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "# Set any poor quality returns to NaN\n", "allDF = allDF.where(allDF['Stale Return Flag'].ne(1))\n", @@ -1701,7 +3997,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 93, "metadata": {}, "outputs": [], "source": [ @@ -1711,7 +4007,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 94, "metadata": {}, "outputs": [], "source": [ @@ -1736,9 +4032,101 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 95, + "metadata": {}, + "outputs": [ + { + "data": {}, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.holoviews_exec.v0+json": "", + "text/html": [ + "
\n", + "
\n", + "
\n", + "" + ], + "text/plain": [ + ":Overlay\n", + " .WMTS.I :WMTS [Longitude,Latitude]\n", + " .Points.I :Points [Longitude,Latitude] (Shot Number,Beam,Tandem-X DEM,Elevation bin 0 (m),Elevation last bin (m),Stale Return Flag,Degrade Flag,rx Start Index,rx Sample Count)\n", + " .Polygons.I :Polygons [Longitude,Latitude]" + ] + }, + "execution_count": 95, + "metadata": { + "application/vnd.holoviews_exec.v0+json": { + "id": "p235592" + } + }, + "output_type": "execute_result" + } + ], "source": [ "allDF['Shot Number'] = allDF['Shot Number'].astype(str) # Convert shot number to string\n", "\n", @@ -1767,9 +4155,100 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 96, + "metadata": {}, + "outputs": [ + { + "data": {}, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.holoviews_exec.v0+json": "", + "text/html": [ + "
\n", + "
\n", + "
\n", + "" + ], + "text/plain": [ + ":Overlay\n", + " .WMTS.I :WMTS [Longitude,Latitude]\n", + " .Points.I :Points [Longitude,Latitude] (Shot Number,Beam,Tandem-X DEM,Elevation bin 0 (m),Elevation last bin (m),Stale Return Flag,Degrade Flag,rx Start Index,rx Sample Count)" + ] + }, + "execution_count": 96, + "metadata": { + "application/vnd.holoviews_exec.v0+json": { + "id": "p235704" + } + }, + "output_type": "execute_result" + } + ], "source": [ "(gvts.EsriImagery * gv.Points(allDF, vdims=vdims).options(color='Elevation bin 0 (m)',cmap='terrain', size=3, tools=['hover'],\n", " clim=(min(allDF['Elevation bin 0 (m)']), \n", @@ -1784,7 +4263,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "---\n", + "\n", "# 8. Export Subsets as GeoJSON Files\n", "#### In this section, export the GeoDataFrame as a `.geojson` file that can be easily opened in your favorite remote sensing and/or GIS software and will include an attribute table with all of the shots/values for each of the SDS layers in the dataframe." ] @@ -1798,7 +4277,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 97, "metadata": {}, "outputs": [], "source": [ @@ -1818,18 +4297,40 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 98, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'data/GEDI01_B_2019170155833_O02932_02_T02267_02_005_01_V002.h5'" + ] + }, + "execution_count": 98, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "gediL1B.filename # L1B Filename" ] }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 99, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'data/GEDI01_B_2019170155833_O02932_02_T02267_02_005_01_V002.json'" + ] + }, + "execution_count": 99, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "outName = gediL1B.filename.replace('.h5', '.json') # Create an output file name using the input file name\n", "outName" @@ -1837,7 +4338,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 100, "metadata": {}, "outputs": [], "source": [ @@ -1846,7 +4347,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 101, "metadata": {}, "outputs": [], "source": [ @@ -1864,21 +4365,16 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "
\n", - "

Contact Information

\n", - "

Material written by LP DAAC1

\n", - "
    \n", - " Contact: LPDAAC@usgs.gov
    \n", - " Voice: +1-605-594-6116
    \n", - " Organization: Land Processes Distributed Active Archive Center (LP DAAC)
    \n", - " Website: https://lpdaac.usgs.gov/
    \n", - " Date last modified: 03-27-2023
    \n", - "
\n", - " \n", - "1KBR Inc., contractor to the U.S. Geological Survey, Earth Resources Observation and Science (EROS) Center, Sioux Falls, South Dakota, 57198-001, USA. Work performed under USGS contract G15PD00467 for LP DAAC2.\n", "\n", - "2LP DAAC Work performed under NASA contract NNG14HH33I.\n", - "
" + "## Contact Info: \n", + "\n", + "Email: LPDAAC@usgs.gov \n", + "Voice: +1-866-573-3222 \n", + "Organization: Land Processes Distributed Active Archive Center (LP DAAC)¹ \n", + "Website: \n", + "Date last modified: 02-20-2024 \n", + "\n", + "¹Work performed under USGS contract G15PD00467 for NASA contract NNG14HH33I. " ] } ], @@ -1898,7 +4394,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.7" + "version": "3.10.13" } }, "nbformat": 4, diff --git a/python/tutorials/GEDI_L2A_V2_Tutorial.ipynb b/python/tutorials/GEDI_L2A_V2_Tutorial.ipynb index b86896e..af4fb95 100644 --- a/python/tutorials/GEDI_L2A_V2_Tutorial.ipynb +++ b/python/tutorials/GEDI_L2A_V2_Tutorial.ipynb @@ -76,7 +76,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "---\n", + "\n", "# 1. Get Started " ] }, @@ -90,9 +90,709 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "data": { + "application/javascript": [ + "(function(root) {\n", + " function now() {\n", + " return new Date();\n", + " }\n", + "\n", + " var force = true;\n", + " var py_version = '3.2.2'.replace('rc', '-rc.').replace('.dev', '-dev.');\n", + " var is_dev = py_version.indexOf(\"+\") !== -1 || py_version.indexOf(\"-\") !== -1;\n", + " var reloading = false;\n", + " var Bokeh = root.Bokeh;\n", + " var bokeh_loaded = Bokeh != null && (Bokeh.version === py_version || (Bokeh.versions !== undefined && Bokeh.versions.has(py_version)));\n", + "\n", + " if (typeof (root._bokeh_timeout) === \"undefined\" || force) {\n", + " root._bokeh_timeout = Date.now() + 5000;\n", + " root._bokeh_failed_load = false;\n", + " }\n", + "\n", + " function run_callbacks() {\n", + " try {\n", + " root._bokeh_onload_callbacks.forEach(function(callback) {\n", + " if (callback != null)\n", + " callback();\n", + " });\n", + " } finally {\n", + " delete root._bokeh_onload_callbacks;\n", + " }\n", + " console.debug(\"Bokeh: all callbacks have finished\");\n", + " }\n", + "\n", + " function load_libs(css_urls, js_urls, js_modules, js_exports, callback) {\n", + " if (css_urls == null) css_urls = [];\n", + " if (js_urls == null) js_urls = [];\n", + " if (js_modules == null) js_modules = [];\n", + " if (js_exports == null) js_exports = {};\n", + "\n", + " root._bokeh_onload_callbacks.push(callback);\n", + "\n", + " if (root._bokeh_is_loading > 0) {\n", + " console.debug(\"Bokeh: BokehJS is being loaded, scheduling callback at\", now());\n", + " return null;\n", + " }\n", + " if (js_urls.length === 0 && js_modules.length === 0 && Object.keys(js_exports).length === 0) {\n", + " run_callbacks();\n", + " return null;\n", + " }\n", + " if (!reloading) {\n", + " console.debug(\"Bokeh: BokehJS not loaded, scheduling load and callback at\", now());\n", + " }\n", + "\n", + " function on_load() {\n", + " root._bokeh_is_loading--;\n", + " if (root._bokeh_is_loading === 0) {\n", + " console.debug(\"Bokeh: all BokehJS libraries/stylesheets loaded\");\n", + " run_callbacks()\n", + " }\n", + " }\n", + " window._bokeh_on_load = on_load\n", + "\n", + " function on_error() {\n", + " console.error(\"failed to load \" + url);\n", + " }\n", + "\n", + " var skip = [];\n", + " if (window.requirejs) {\n", + " window.requirejs.config({'packages': {}, 'paths': {'jspanel': 'https://cdn.jsdelivr.net/npm/jspanel4@4.12.0/dist/jspanel', 'jspanel-modal': 'https://cdn.jsdelivr.net/npm/jspanel4@4.12.0/dist/extensions/modal/jspanel.modal', 'jspanel-tooltip': 'https://cdn.jsdelivr.net/npm/jspanel4@4.12.0/dist/extensions/tooltip/jspanel.tooltip', 'jspanel-hint': 'https://cdn.jsdelivr.net/npm/jspanel4@4.12.0/dist/extensions/hint/jspanel.hint', 'jspanel-layout': 'https://cdn.jsdelivr.net/npm/jspanel4@4.12.0/dist/extensions/layout/jspanel.layout', 'jspanel-contextmenu': 'https://cdn.jsdelivr.net/npm/jspanel4@4.12.0/dist/extensions/contextmenu/jspanel.contextmenu', 'jspanel-dock': 'https://cdn.jsdelivr.net/npm/jspanel4@4.12.0/dist/extensions/dock/jspanel.dock', 'gridstack': 'https://cdn.jsdelivr.net/npm/gridstack@7.2.3/dist/gridstack-all', 'notyf': 'https://cdn.jsdelivr.net/npm/notyf@3/notyf.min'}, 'shim': {'jspanel': {'exports': 'jsPanel'}, 'gridstack': {'exports': 'GridStack'}}});\n", + " require([\"jspanel\"], function(jsPanel) {\n", + "\twindow.jsPanel = jsPanel\n", + "\ton_load()\n", + " })\n", + " require([\"jspanel-modal\"], function() {\n", + "\ton_load()\n", + " })\n", + " require([\"jspanel-tooltip\"], function() {\n", + "\ton_load()\n", + " })\n", + " require([\"jspanel-hint\"], function() {\n", + "\ton_load()\n", + " })\n", + " require([\"jspanel-layout\"], function() {\n", + "\ton_load()\n", + " })\n", + " require([\"jspanel-contextmenu\"], function() {\n", + "\ton_load()\n", + " })\n", + " require([\"jspanel-dock\"], function() {\n", + "\ton_load()\n", + " })\n", + " require([\"gridstack\"], function(GridStack) {\n", + "\twindow.GridStack = GridStack\n", + "\ton_load()\n", + " })\n", + " require([\"notyf\"], function() {\n", + "\ton_load()\n", + " })\n", + " root._bokeh_is_loading = css_urls.length + 9;\n", + " } else {\n", + " root._bokeh_is_loading = css_urls.length + js_urls.length + js_modules.length + Object.keys(js_exports).length;\n", + " }\n", + "\n", + " var existing_stylesheets = []\n", + " var links = document.getElementsByTagName('link')\n", + " for (var i = 0; i < links.length; i++) {\n", + " var link = links[i]\n", + " if (link.href != null) {\n", + "\texisting_stylesheets.push(link.href)\n", + " }\n", + " }\n", + " for (var i = 0; i < css_urls.length; i++) {\n", + " var url = css_urls[i];\n", + " if (existing_stylesheets.indexOf(url) !== -1) {\n", + "\ton_load()\n", + "\tcontinue;\n", + " }\n", + " const element = document.createElement(\"link\");\n", + " element.onload = on_load;\n", + " element.onerror = on_error;\n", + " element.rel = \"stylesheet\";\n", + " element.type = \"text/css\";\n", + " element.href = url;\n", + " console.debug(\"Bokeh: injecting link tag for BokehJS stylesheet: \", url);\n", + " document.body.appendChild(element);\n", + " } if (((window['jsPanel'] !== undefined) && (!(window['jsPanel'] instanceof HTMLElement))) || window.requirejs) {\n", + " var urls = ['https://cdn.holoviz.org/panel/1.2.3/dist/bundled/floatpanel/jspanel4@4.12.0/dist/jspanel.js', 'https://cdn.holoviz.org/panel/1.2.3/dist/bundled/floatpanel/jspanel4@4.12.0/dist/extensions/modal/jspanel.modal.js', 'https://cdn.holoviz.org/panel/1.2.3/dist/bundled/floatpanel/jspanel4@4.12.0/dist/extensions/tooltip/jspanel.tooltip.js', 'https://cdn.holoviz.org/panel/1.2.3/dist/bundled/floatpanel/jspanel4@4.12.0/dist/extensions/hint/jspanel.hint.js', 'https://cdn.holoviz.org/panel/1.2.3/dist/bundled/floatpanel/jspanel4@4.12.0/dist/extensions/layout/jspanel.layout.js', 'https://cdn.holoviz.org/panel/1.2.3/dist/bundled/floatpanel/jspanel4@4.12.0/dist/extensions/contextmenu/jspanel.contextmenu.js', 'https://cdn.holoviz.org/panel/1.2.3/dist/bundled/floatpanel/jspanel4@4.12.0/dist/extensions/dock/jspanel.dock.js'];\n", + " for (var i = 0; i < urls.length; i++) {\n", + " skip.push(urls[i])\n", + " }\n", + " } if (((window['GridStack'] !== undefined) && (!(window['GridStack'] instanceof HTMLElement))) || window.requirejs) {\n", + " var urls = ['https://cdn.holoviz.org/panel/1.2.3/dist/bundled/gridstack/gridstack@7.2.3/dist/gridstack-all.js'];\n", + " for (var i = 0; i < urls.length; i++) {\n", + " skip.push(urls[i])\n", + " }\n", + " } if (((window['Notyf'] !== undefined) && (!(window['Notyf'] instanceof HTMLElement))) || window.requirejs) {\n", + " var urls = ['https://cdn.holoviz.org/panel/1.2.3/dist/bundled/notificationarea/notyf@3/notyf.min.js'];\n", + " for (var i = 0; i < urls.length; i++) {\n", + " skip.push(urls[i])\n", + " }\n", + " } var existing_scripts = []\n", + " var scripts = document.getElementsByTagName('script')\n", + " for (var i = 0; i < scripts.length; i++) {\n", + " var script = scripts[i]\n", + " if (script.src != null) {\n", + "\texisting_scripts.push(script.src)\n", + " }\n", + " }\n", + " for (var i = 0; i < js_urls.length; i++) {\n", + " var url = js_urls[i];\n", + " if (skip.indexOf(url) !== -1 || existing_scripts.indexOf(url) !== -1) {\n", + "\tif (!window.requirejs) {\n", + "\t on_load();\n", + "\t}\n", + "\tcontinue;\n", + " }\n", + " var element = document.createElement('script');\n", + " element.onload = on_load;\n", + " element.onerror = on_error;\n", + " element.async = false;\n", + " element.src = url;\n", + " console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n", + " document.head.appendChild(element);\n", + " }\n", + " for (var i = 0; i < js_modules.length; i++) {\n", + " var url = js_modules[i];\n", + " if (skip.indexOf(url) !== -1 || existing_scripts.indexOf(url) !== -1) {\n", + "\tif (!window.requirejs) {\n", + "\t on_load();\n", + "\t}\n", + "\tcontinue;\n", + " }\n", + " var element = document.createElement('script');\n", + " element.onload = on_load;\n", + " element.onerror = on_error;\n", + " element.async = false;\n", + " element.src = url;\n", + " element.type = \"module\";\n", + " console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n", + " document.head.appendChild(element);\n", + " }\n", + " for (const name in js_exports) {\n", + " var url = js_exports[name];\n", + " if (skip.indexOf(url) >= 0 || root[name] != null) {\n", + "\tif (!window.requirejs) {\n", + "\t on_load();\n", + "\t}\n", + "\tcontinue;\n", + " }\n", + " var element = document.createElement('script');\n", + " element.onerror = on_error;\n", + " element.async = false;\n", + " element.type = \"module\";\n", + " console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n", + " element.textContent = `\n", + " import ${name} from \"${url}\"\n", + " window.${name} = ${name}\n", + " window._bokeh_on_load()\n", + " `\n", + " document.head.appendChild(element);\n", + " }\n", + " if (!js_urls.length && !js_modules.length) {\n", + " on_load()\n", + " }\n", + " };\n", + "\n", + " function inject_raw_css(css) {\n", + " const element = document.createElement(\"style\");\n", + " element.appendChild(document.createTextNode(css));\n", + " document.body.appendChild(element);\n", + " }\n", + "\n", + " var js_urls = [\"https://cdn.bokeh.org/bokeh/release/bokeh-3.2.2.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-gl-3.2.2.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-widgets-3.2.2.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-tables-3.2.2.min.js\", \"https://cdn.holoviz.org/panel/1.2.3/dist/panel.min.js\", \"https://cdn.jsdelivr.net/npm/@holoviz/geoviews@1.11.0/dist/geoviews.min.js\"];\n", + " var js_modules = [];\n", + " var js_exports = {};\n", + " var css_urls = [];\n", + " var inline_js = [ function(Bokeh) {\n", + " Bokeh.set_log_level(\"info\");\n", + " },\n", + "function(Bokeh) {} // ensure no trailing comma for IE\n", + " ];\n", + "\n", + " function run_inline_js() {\n", + " if ((root.Bokeh !== undefined) || (force === true)) {\n", + " for (var i = 0; i < inline_js.length; i++) {\n", + " inline_js[i].call(root, root.Bokeh);\n", + " }\n", + " // Cache old bokeh versions\n", + " if (Bokeh != undefined && !reloading) {\n", + "\tvar NewBokeh = root.Bokeh;\n", + "\tif (Bokeh.versions === undefined) {\n", + "\t Bokeh.versions = new Map();\n", + "\t}\n", + "\tif (NewBokeh.version !== Bokeh.version) {\n", + "\t Bokeh.versions.set(NewBokeh.version, NewBokeh)\n", + "\t}\n", + "\troot.Bokeh = Bokeh;\n", + " }} else if (Date.now() < root._bokeh_timeout) {\n", + " setTimeout(run_inline_js, 100);\n", + " } else if (!root._bokeh_failed_load) {\n", + " console.log(\"Bokeh: BokehJS failed to load within specified timeout.\");\n", + " root._bokeh_failed_load = true;\n", + " }\n", + " root._bokeh_is_initializing = false\n", + " }\n", + "\n", + " function load_or_wait() {\n", + " // Implement a backoff loop that tries to ensure we do not load multiple\n", + " // versions of Bokeh and its dependencies at the same time.\n", + " // In recent versions we use the root._bokeh_is_initializing flag\n", + " // to determine whether there is an ongoing attempt to initialize\n", + " // bokeh, however for backward compatibility we also try to ensure\n", + " // that we do not start loading a newer (Panel>=1.0 and Bokeh>3) version\n", + " // before older versions are fully initialized.\n", + " if (root._bokeh_is_initializing && Date.now() > root._bokeh_timeout) {\n", + " root._bokeh_is_initializing = false;\n", + " root._bokeh_onload_callbacks = undefined;\n", + " console.log(\"Bokeh: BokehJS was loaded multiple times but one version failed to initialize.\");\n", + " load_or_wait();\n", + " } else if (root._bokeh_is_initializing || (typeof root._bokeh_is_initializing === \"undefined\" && root._bokeh_onload_callbacks !== undefined)) {\n", + " setTimeout(load_or_wait, 100);\n", + " } else {\n", + " Bokeh = root.Bokeh;\n", + " bokeh_loaded = Bokeh != null && (Bokeh.version === py_version || (Bokeh.versions !== undefined && Bokeh.versions.has(py_version)));\n", + " root._bokeh_is_initializing = true\n", + " root._bokeh_onload_callbacks = []\n", + " if (!reloading && (!bokeh_loaded || is_dev)) {\n", + "\troot.Bokeh = undefined;\n", + " }\n", + " load_libs(css_urls, js_urls, js_modules, js_exports, function() {\n", + "\tconsole.debug(\"Bokeh: BokehJS plotting callback run at\", now());\n", + "\trun_inline_js();\n", + " });\n", + " }\n", + " }\n", + " // Give older versions of the autoload script a head-start to ensure\n", + " // they initialize before we start loading newer version.\n", + " setTimeout(load_or_wait, 100)\n", + "}(window));" + ], + "application/vnd.holoviews_load.v0+json": "(function(root) {\n function now() {\n return new Date();\n }\n\n var force = true;\n var py_version = '3.2.2'.replace('rc', '-rc.').replace('.dev', '-dev.');\n var is_dev = py_version.indexOf(\"+\") !== -1 || py_version.indexOf(\"-\") !== -1;\n var reloading = false;\n var Bokeh = root.Bokeh;\n var bokeh_loaded = Bokeh != null && (Bokeh.version === py_version || (Bokeh.versions !== undefined && Bokeh.versions.has(py_version)));\n\n if (typeof (root._bokeh_timeout) === \"undefined\" || force) {\n root._bokeh_timeout = Date.now() + 5000;\n root._bokeh_failed_load = false;\n }\n\n function run_callbacks() {\n try {\n root._bokeh_onload_callbacks.forEach(function(callback) {\n if (callback != null)\n callback();\n });\n } finally {\n delete root._bokeh_onload_callbacks;\n }\n console.debug(\"Bokeh: all callbacks have finished\");\n }\n\n function load_libs(css_urls, js_urls, js_modules, js_exports, callback) {\n if (css_urls == null) css_urls = [];\n if (js_urls == null) js_urls = [];\n if (js_modules == null) js_modules = [];\n if (js_exports == null) js_exports = {};\n\n root._bokeh_onload_callbacks.push(callback);\n\n if (root._bokeh_is_loading > 0) {\n console.debug(\"Bokeh: BokehJS is being loaded, scheduling callback at\", now());\n return null;\n }\n if (js_urls.length === 0 && js_modules.length === 0 && Object.keys(js_exports).length === 0) {\n run_callbacks();\n return null;\n }\n if (!reloading) {\n console.debug(\"Bokeh: BokehJS not loaded, scheduling load and callback at\", now());\n }\n\n function on_load() {\n root._bokeh_is_loading--;\n if (root._bokeh_is_loading === 0) {\n console.debug(\"Bokeh: all BokehJS libraries/stylesheets loaded\");\n run_callbacks()\n }\n }\n window._bokeh_on_load = on_load\n\n function on_error() {\n console.error(\"failed to load \" + url);\n }\n\n var skip = [];\n if (window.requirejs) {\n window.requirejs.config({'packages': {}, 'paths': {'jspanel': 'https://cdn.jsdelivr.net/npm/jspanel4@4.12.0/dist/jspanel', 'jspanel-modal': 'https://cdn.jsdelivr.net/npm/jspanel4@4.12.0/dist/extensions/modal/jspanel.modal', 'jspanel-tooltip': 'https://cdn.jsdelivr.net/npm/jspanel4@4.12.0/dist/extensions/tooltip/jspanel.tooltip', 'jspanel-hint': 'https://cdn.jsdelivr.net/npm/jspanel4@4.12.0/dist/extensions/hint/jspanel.hint', 'jspanel-layout': 'https://cdn.jsdelivr.net/npm/jspanel4@4.12.0/dist/extensions/layout/jspanel.layout', 'jspanel-contextmenu': 'https://cdn.jsdelivr.net/npm/jspanel4@4.12.0/dist/extensions/contextmenu/jspanel.contextmenu', 'jspanel-dock': 'https://cdn.jsdelivr.net/npm/jspanel4@4.12.0/dist/extensions/dock/jspanel.dock', 'gridstack': 'https://cdn.jsdelivr.net/npm/gridstack@7.2.3/dist/gridstack-all', 'notyf': 'https://cdn.jsdelivr.net/npm/notyf@3/notyf.min'}, 'shim': {'jspanel': {'exports': 'jsPanel'}, 'gridstack': {'exports': 'GridStack'}}});\n require([\"jspanel\"], function(jsPanel) {\n\twindow.jsPanel = jsPanel\n\ton_load()\n })\n require([\"jspanel-modal\"], function() {\n\ton_load()\n })\n require([\"jspanel-tooltip\"], function() {\n\ton_load()\n })\n require([\"jspanel-hint\"], function() {\n\ton_load()\n })\n require([\"jspanel-layout\"], function() {\n\ton_load()\n })\n require([\"jspanel-contextmenu\"], function() {\n\ton_load()\n })\n require([\"jspanel-dock\"], function() {\n\ton_load()\n })\n require([\"gridstack\"], function(GridStack) {\n\twindow.GridStack = GridStack\n\ton_load()\n })\n require([\"notyf\"], function() {\n\ton_load()\n })\n root._bokeh_is_loading = css_urls.length + 9;\n } else {\n root._bokeh_is_loading = css_urls.length + js_urls.length + js_modules.length + Object.keys(js_exports).length;\n }\n\n var existing_stylesheets = []\n var links = document.getElementsByTagName('link')\n for (var i = 0; i < links.length; i++) {\n var link = links[i]\n if (link.href != null) {\n\texisting_stylesheets.push(link.href)\n }\n }\n for (var i = 0; i < css_urls.length; i++) {\n var url = css_urls[i];\n if (existing_stylesheets.indexOf(url) !== -1) {\n\ton_load()\n\tcontinue;\n }\n const element = document.createElement(\"link\");\n element.onload = on_load;\n element.onerror = on_error;\n element.rel = \"stylesheet\";\n element.type = \"text/css\";\n element.href = url;\n console.debug(\"Bokeh: injecting link tag for BokehJS stylesheet: \", url);\n document.body.appendChild(element);\n } if (((window['jsPanel'] !== undefined) && (!(window['jsPanel'] instanceof HTMLElement))) || window.requirejs) {\n var urls = ['https://cdn.holoviz.org/panel/1.2.3/dist/bundled/floatpanel/jspanel4@4.12.0/dist/jspanel.js', 'https://cdn.holoviz.org/panel/1.2.3/dist/bundled/floatpanel/jspanel4@4.12.0/dist/extensions/modal/jspanel.modal.js', 'https://cdn.holoviz.org/panel/1.2.3/dist/bundled/floatpanel/jspanel4@4.12.0/dist/extensions/tooltip/jspanel.tooltip.js', 'https://cdn.holoviz.org/panel/1.2.3/dist/bundled/floatpanel/jspanel4@4.12.0/dist/extensions/hint/jspanel.hint.js', 'https://cdn.holoviz.org/panel/1.2.3/dist/bundled/floatpanel/jspanel4@4.12.0/dist/extensions/layout/jspanel.layout.js', 'https://cdn.holoviz.org/panel/1.2.3/dist/bundled/floatpanel/jspanel4@4.12.0/dist/extensions/contextmenu/jspanel.contextmenu.js', 'https://cdn.holoviz.org/panel/1.2.3/dist/bundled/floatpanel/jspanel4@4.12.0/dist/extensions/dock/jspanel.dock.js'];\n for (var i = 0; i < urls.length; i++) {\n skip.push(urls[i])\n }\n } if (((window['GridStack'] !== undefined) && (!(window['GridStack'] instanceof HTMLElement))) || window.requirejs) {\n var urls = ['https://cdn.holoviz.org/panel/1.2.3/dist/bundled/gridstack/gridstack@7.2.3/dist/gridstack-all.js'];\n for (var i = 0; i < urls.length; i++) {\n skip.push(urls[i])\n }\n } if (((window['Notyf'] !== undefined) && (!(window['Notyf'] instanceof HTMLElement))) || window.requirejs) {\n var urls = ['https://cdn.holoviz.org/panel/1.2.3/dist/bundled/notificationarea/notyf@3/notyf.min.js'];\n for (var i = 0; i < urls.length; i++) {\n skip.push(urls[i])\n }\n } var existing_scripts = []\n var scripts = document.getElementsByTagName('script')\n for (var i = 0; i < scripts.length; i++) {\n var script = scripts[i]\n if (script.src != null) {\n\texisting_scripts.push(script.src)\n }\n }\n for (var i = 0; i < js_urls.length; i++) {\n var url = js_urls[i];\n if (skip.indexOf(url) !== -1 || existing_scripts.indexOf(url) !== -1) {\n\tif (!window.requirejs) {\n\t on_load();\n\t}\n\tcontinue;\n }\n var element = document.createElement('script');\n element.onload = on_load;\n element.onerror = on_error;\n element.async = false;\n element.src = url;\n console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n document.head.appendChild(element);\n }\n for (var i = 0; i < js_modules.length; i++) {\n var url = js_modules[i];\n if (skip.indexOf(url) !== -1 || existing_scripts.indexOf(url) !== -1) {\n\tif (!window.requirejs) {\n\t on_load();\n\t}\n\tcontinue;\n }\n var element = document.createElement('script');\n element.onload = on_load;\n element.onerror = on_error;\n element.async = false;\n element.src = url;\n element.type = \"module\";\n console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n document.head.appendChild(element);\n }\n for (const name in js_exports) {\n var url = js_exports[name];\n if (skip.indexOf(url) >= 0 || root[name] != null) {\n\tif (!window.requirejs) {\n\t on_load();\n\t}\n\tcontinue;\n }\n var element = document.createElement('script');\n element.onerror = on_error;\n element.async = false;\n element.type = \"module\";\n console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n element.textContent = `\n import ${name} from \"${url}\"\n window.${name} = ${name}\n window._bokeh_on_load()\n `\n document.head.appendChild(element);\n }\n if (!js_urls.length && !js_modules.length) {\n on_load()\n }\n };\n\n function inject_raw_css(css) {\n const element = document.createElement(\"style\");\n element.appendChild(document.createTextNode(css));\n document.body.appendChild(element);\n }\n\n var js_urls = [\"https://cdn.bokeh.org/bokeh/release/bokeh-3.2.2.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-gl-3.2.2.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-widgets-3.2.2.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-tables-3.2.2.min.js\", \"https://cdn.holoviz.org/panel/1.2.3/dist/panel.min.js\", \"https://cdn.jsdelivr.net/npm/@holoviz/geoviews@1.11.0/dist/geoviews.min.js\"];\n var js_modules = [];\n var js_exports = {};\n var css_urls = [];\n var inline_js = [ function(Bokeh) {\n Bokeh.set_log_level(\"info\");\n },\nfunction(Bokeh) {} // ensure no trailing comma for IE\n ];\n\n function run_inline_js() {\n if ((root.Bokeh !== undefined) || (force === true)) {\n for (var i = 0; i < inline_js.length; i++) {\n inline_js[i].call(root, root.Bokeh);\n }\n // Cache old bokeh versions\n if (Bokeh != undefined && !reloading) {\n\tvar NewBokeh = root.Bokeh;\n\tif (Bokeh.versions === undefined) {\n\t Bokeh.versions = new Map();\n\t}\n\tif (NewBokeh.version !== Bokeh.version) {\n\t Bokeh.versions.set(NewBokeh.version, NewBokeh)\n\t}\n\troot.Bokeh = Bokeh;\n }} else if (Date.now() < root._bokeh_timeout) {\n setTimeout(run_inline_js, 100);\n } else if (!root._bokeh_failed_load) {\n console.log(\"Bokeh: BokehJS failed to load within specified timeout.\");\n root._bokeh_failed_load = true;\n }\n root._bokeh_is_initializing = false\n }\n\n function load_or_wait() {\n // Implement a backoff loop that tries to ensure we do not load multiple\n // versions of Bokeh and its dependencies at the same time.\n // In recent versions we use the root._bokeh_is_initializing flag\n // to determine whether there is an ongoing attempt to initialize\n // bokeh, however for backward compatibility we also try to ensure\n // that we do not start loading a newer (Panel>=1.0 and Bokeh>3) version\n // before older versions are fully initialized.\n if (root._bokeh_is_initializing && Date.now() > root._bokeh_timeout) {\n root._bokeh_is_initializing = false;\n root._bokeh_onload_callbacks = undefined;\n console.log(\"Bokeh: BokehJS was loaded multiple times but one version failed to initialize.\");\n load_or_wait();\n } else if (root._bokeh_is_initializing || (typeof root._bokeh_is_initializing === \"undefined\" && root._bokeh_onload_callbacks !== undefined)) {\n setTimeout(load_or_wait, 100);\n } else {\n Bokeh = root.Bokeh;\n bokeh_loaded = Bokeh != null && (Bokeh.version === py_version || (Bokeh.versions !== undefined && Bokeh.versions.has(py_version)));\n root._bokeh_is_initializing = true\n root._bokeh_onload_callbacks = []\n if (!reloading && (!bokeh_loaded || is_dev)) {\n\troot.Bokeh = undefined;\n }\n load_libs(css_urls, js_urls, js_modules, js_exports, function() {\n\tconsole.debug(\"Bokeh: BokehJS plotting callback run at\", now());\n\trun_inline_js();\n });\n }\n }\n // Give older versions of the autoload script a head-start to ensure\n // they initialize before we start loading newer version.\n setTimeout(load_or_wait, 100)\n}(window));" + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/javascript": [ + "\n", + "if ((window.PyViz === undefined) || (window.PyViz instanceof HTMLElement)) {\n", + " window.PyViz = {comms: {}, comm_status:{}, kernels:{}, receivers: {}, plot_index: []}\n", + "}\n", + "\n", + "\n", + " function JupyterCommManager() {\n", + " }\n", + "\n", + " JupyterCommManager.prototype.register_target = function(plot_id, comm_id, msg_handler) {\n", + " if (window.comm_manager || ((window.Jupyter !== undefined) && (Jupyter.notebook.kernel != null))) {\n", + " var comm_manager = window.comm_manager || Jupyter.notebook.kernel.comm_manager;\n", + " comm_manager.register_target(comm_id, function(comm) {\n", + " comm.on_msg(msg_handler);\n", + " });\n", + " } else if ((plot_id in window.PyViz.kernels) && (window.PyViz.kernels[plot_id])) {\n", + " window.PyViz.kernels[plot_id].registerCommTarget(comm_id, function(comm) {\n", + " comm.onMsg = msg_handler;\n", + " });\n", + " } else if (typeof google != 'undefined' && google.colab.kernel != null) {\n", + " google.colab.kernel.comms.registerTarget(comm_id, (comm) => {\n", + " var messages = comm.messages[Symbol.asyncIterator]();\n", + " function processIteratorResult(result) {\n", + " var message = result.value;\n", + " console.log(message)\n", + " var content = {data: message.data, comm_id};\n", + " var buffers = []\n", + " for (var buffer of message.buffers || []) {\n", + " buffers.push(new DataView(buffer))\n", + " }\n", + " var metadata = message.metadata || {};\n", + " var msg = {content, buffers, metadata}\n", + " msg_handler(msg);\n", + " return messages.next().then(processIteratorResult);\n", + " }\n", + " return messages.next().then(processIteratorResult);\n", + " })\n", + " }\n", + " }\n", + "\n", + " JupyterCommManager.prototype.get_client_comm = function(plot_id, comm_id, msg_handler) {\n", + " if (comm_id in window.PyViz.comms) {\n", + " return window.PyViz.comms[comm_id];\n", + " } else if (window.comm_manager || ((window.Jupyter !== undefined) && (Jupyter.notebook.kernel != null))) {\n", + " var comm_manager = window.comm_manager || Jupyter.notebook.kernel.comm_manager;\n", + " var comm = comm_manager.new_comm(comm_id, {}, {}, {}, comm_id);\n", + " if (msg_handler) {\n", + " comm.on_msg(msg_handler);\n", + " }\n", + " } else if ((plot_id in window.PyViz.kernels) && (window.PyViz.kernels[plot_id])) {\n", + " var comm = window.PyViz.kernels[plot_id].connectToComm(comm_id);\n", + " comm.open();\n", + " if (msg_handler) {\n", + " comm.onMsg = msg_handler;\n", + " }\n", + " } else if (typeof google != 'undefined' && google.colab.kernel != null) {\n", + " var comm_promise = google.colab.kernel.comms.open(comm_id)\n", + " comm_promise.then((comm) => {\n", + " window.PyViz.comms[comm_id] = comm;\n", + " if (msg_handler) {\n", + " var messages = comm.messages[Symbol.asyncIterator]();\n", + " function processIteratorResult(result) {\n", + " var message = result.value;\n", + " var content = {data: message.data};\n", + " var metadata = message.metadata || {comm_id};\n", + " var msg = {content, metadata}\n", + " msg_handler(msg);\n", + " return messages.next().then(processIteratorResult);\n", + " }\n", + " return messages.next().then(processIteratorResult);\n", + " }\n", + " }) \n", + " var sendClosure = (data, metadata, buffers, disposeOnDone) => {\n", + " return comm_promise.then((comm) => {\n", + " comm.send(data, metadata, buffers, disposeOnDone);\n", + " });\n", + " };\n", + " var comm = {\n", + " send: sendClosure\n", + " };\n", + " }\n", + " window.PyViz.comms[comm_id] = comm;\n", + " return comm;\n", + " }\n", + " window.PyViz.comm_manager = new JupyterCommManager();\n", + " \n", + "\n", + "\n", + "var JS_MIME_TYPE = 'application/javascript';\n", + "var HTML_MIME_TYPE = 'text/html';\n", + "var EXEC_MIME_TYPE = 'application/vnd.holoviews_exec.v0+json';\n", + "var CLASS_NAME = 'output';\n", + "\n", + "/**\n", + " * Render data to the DOM node\n", + " */\n", + "function render(props, node) {\n", + " var div = document.createElement(\"div\");\n", + " var script = document.createElement(\"script\");\n", + " node.appendChild(div);\n", + " node.appendChild(script);\n", + "}\n", + "\n", + "/**\n", + " * Handle when a new output is added\n", + " */\n", + "function handle_add_output(event, handle) {\n", + " var output_area = handle.output_area;\n", + " var output = handle.output;\n", + " if ((output.data == undefined) || (!output.data.hasOwnProperty(EXEC_MIME_TYPE))) {\n", + " return\n", + " }\n", + " var id = output.metadata[EXEC_MIME_TYPE][\"id\"];\n", + " var toinsert = output_area.element.find(\".\" + CLASS_NAME.split(' ')[0]);\n", + " if (id !== undefined) {\n", + " var nchildren = toinsert.length;\n", + " var html_node = toinsert[nchildren-1].children[0];\n", + " html_node.innerHTML = output.data[HTML_MIME_TYPE];\n", + " var scripts = [];\n", + " var nodelist = html_node.querySelectorAll(\"script\");\n", + " for (var i in nodelist) {\n", + " if (nodelist.hasOwnProperty(i)) {\n", + " scripts.push(nodelist[i])\n", + " }\n", + " }\n", + "\n", + " scripts.forEach( function (oldScript) {\n", + " var newScript = document.createElement(\"script\");\n", + " var attrs = [];\n", + " var nodemap = oldScript.attributes;\n", + " for (var j in nodemap) {\n", + " if (nodemap.hasOwnProperty(j)) {\n", + " attrs.push(nodemap[j])\n", + " }\n", + " }\n", + " attrs.forEach(function(attr) { newScript.setAttribute(attr.name, attr.value) });\n", + " newScript.appendChild(document.createTextNode(oldScript.innerHTML));\n", + " oldScript.parentNode.replaceChild(newScript, oldScript);\n", + " });\n", + " if (JS_MIME_TYPE in output.data) {\n", + " toinsert[nchildren-1].children[1].textContent = output.data[JS_MIME_TYPE];\n", + " }\n", + " output_area._hv_plot_id = id;\n", + " if ((window.Bokeh !== undefined) && (id in Bokeh.index)) {\n", + " window.PyViz.plot_index[id] = Bokeh.index[id];\n", + " } else {\n", + " window.PyViz.plot_index[id] = null;\n", + " }\n", + " } else if (output.metadata[EXEC_MIME_TYPE][\"server_id\"] !== undefined) {\n", + " var bk_div = document.createElement(\"div\");\n", + " bk_div.innerHTML = output.data[HTML_MIME_TYPE];\n", + " var script_attrs = bk_div.children[0].attributes;\n", + " for (var i = 0; i < script_attrs.length; i++) {\n", + " toinsert[toinsert.length - 1].childNodes[1].setAttribute(script_attrs[i].name, script_attrs[i].value);\n", + " }\n", + " // store reference to server id on output_area\n", + " output_area._bokeh_server_id = output.metadata[EXEC_MIME_TYPE][\"server_id\"];\n", + " }\n", + "}\n", + "\n", + "/**\n", + " * Handle when an output is cleared or removed\n", + " */\n", + "function handle_clear_output(event, handle) {\n", + " var id = handle.cell.output_area._hv_plot_id;\n", + " var server_id = handle.cell.output_area._bokeh_server_id;\n", + " if (((id === undefined) || !(id in PyViz.plot_index)) && (server_id !== undefined)) { return; }\n", + " var comm = window.PyViz.comm_manager.get_client_comm(\"hv-extension-comm\", \"hv-extension-comm\", function () {});\n", + " if (server_id !== null) {\n", + " comm.send({event_type: 'server_delete', 'id': server_id});\n", + " return;\n", + " } else if (comm !== null) {\n", + " comm.send({event_type: 'delete', 'id': id});\n", + " }\n", + " delete PyViz.plot_index[id];\n", + " if ((window.Bokeh !== undefined) & (id in window.Bokeh.index)) {\n", + " var doc = window.Bokeh.index[id].model.document\n", + " doc.clear();\n", + " const i = window.Bokeh.documents.indexOf(doc);\n", + " if (i > -1) {\n", + " window.Bokeh.documents.splice(i, 1);\n", + " }\n", + " }\n", + "}\n", + "\n", + "/**\n", + " * Handle kernel restart event\n", + " */\n", + "function handle_kernel_cleanup(event, handle) {\n", + " delete PyViz.comms[\"hv-extension-comm\"];\n", + " window.PyViz.plot_index = {}\n", + "}\n", + "\n", + "/**\n", + " * Handle update_display_data messages\n", + " */\n", + "function handle_update_output(event, handle) {\n", + " handle_clear_output(event, {cell: {output_area: handle.output_area}})\n", + " handle_add_output(event, handle)\n", + "}\n", + "\n", + "function register_renderer(events, OutputArea) {\n", + " function append_mime(data, metadata, element) {\n", + " // create a DOM node to render to\n", + " var toinsert = this.create_output_subarea(\n", + " metadata,\n", + " CLASS_NAME,\n", + " EXEC_MIME_TYPE\n", + " );\n", + " this.keyboard_manager.register_events(toinsert);\n", + " // Render to node\n", + " var props = {data: data, metadata: metadata[EXEC_MIME_TYPE]};\n", + " render(props, toinsert[0]);\n", + " element.append(toinsert);\n", + " return toinsert\n", + " }\n", + "\n", + " events.on('output_added.OutputArea', handle_add_output);\n", + " events.on('output_updated.OutputArea', handle_update_output);\n", + " events.on('clear_output.CodeCell', handle_clear_output);\n", + " events.on('delete.Cell', handle_clear_output);\n", + " events.on('kernel_ready.Kernel', handle_kernel_cleanup);\n", + "\n", + " OutputArea.prototype.register_mime_type(EXEC_MIME_TYPE, append_mime, {\n", + " safe: true,\n", + " index: 0\n", + " });\n", + "}\n", + "\n", + "if (window.Jupyter !== undefined) {\n", + " try {\n", + " var events = require('base/js/events');\n", + " var OutputArea = require('notebook/js/outputarea').OutputArea;\n", + " if (OutputArea.prototype.mime_types().indexOf(EXEC_MIME_TYPE) == -1) {\n", + " register_renderer(events, OutputArea);\n", + " }\n", + " } catch(err) {\n", + " }\n", + "}\n" + ], + "application/vnd.holoviews_load.v0+json": "\nif ((window.PyViz === undefined) || (window.PyViz instanceof HTMLElement)) {\n window.PyViz = {comms: {}, comm_status:{}, kernels:{}, receivers: {}, plot_index: []}\n}\n\n\n function JupyterCommManager() {\n }\n\n JupyterCommManager.prototype.register_target = function(plot_id, comm_id, msg_handler) {\n if (window.comm_manager || ((window.Jupyter !== undefined) && (Jupyter.notebook.kernel != null))) {\n var comm_manager = window.comm_manager || Jupyter.notebook.kernel.comm_manager;\n comm_manager.register_target(comm_id, function(comm) {\n comm.on_msg(msg_handler);\n });\n } else if ((plot_id in window.PyViz.kernels) && (window.PyViz.kernels[plot_id])) {\n window.PyViz.kernels[plot_id].registerCommTarget(comm_id, function(comm) {\n comm.onMsg = msg_handler;\n });\n } else if (typeof google != 'undefined' && google.colab.kernel != null) {\n google.colab.kernel.comms.registerTarget(comm_id, (comm) => {\n var messages = comm.messages[Symbol.asyncIterator]();\n function processIteratorResult(result) {\n var message = result.value;\n console.log(message)\n var content = {data: message.data, comm_id};\n var buffers = []\n for (var buffer of message.buffers || []) {\n buffers.push(new DataView(buffer))\n }\n var metadata = message.metadata || {};\n var msg = {content, buffers, metadata}\n msg_handler(msg);\n return messages.next().then(processIteratorResult);\n }\n return messages.next().then(processIteratorResult);\n })\n }\n }\n\n JupyterCommManager.prototype.get_client_comm = function(plot_id, comm_id, msg_handler) {\n if (comm_id in window.PyViz.comms) {\n return window.PyViz.comms[comm_id];\n } else if (window.comm_manager || ((window.Jupyter !== undefined) && (Jupyter.notebook.kernel != null))) {\n var comm_manager = window.comm_manager || Jupyter.notebook.kernel.comm_manager;\n var comm = comm_manager.new_comm(comm_id, {}, {}, {}, comm_id);\n if (msg_handler) {\n comm.on_msg(msg_handler);\n }\n } else if ((plot_id in window.PyViz.kernels) && (window.PyViz.kernels[plot_id])) {\n var comm = window.PyViz.kernels[plot_id].connectToComm(comm_id);\n comm.open();\n if (msg_handler) {\n comm.onMsg = msg_handler;\n }\n } else if (typeof google != 'undefined' && google.colab.kernel != null) {\n var comm_promise = google.colab.kernel.comms.open(comm_id)\n comm_promise.then((comm) => {\n window.PyViz.comms[comm_id] = comm;\n if (msg_handler) {\n var messages = comm.messages[Symbol.asyncIterator]();\n function processIteratorResult(result) {\n var message = result.value;\n var content = {data: message.data};\n var metadata = message.metadata || {comm_id};\n var msg = {content, metadata}\n msg_handler(msg);\n return messages.next().then(processIteratorResult);\n }\n return messages.next().then(processIteratorResult);\n }\n }) \n var sendClosure = (data, metadata, buffers, disposeOnDone) => {\n return comm_promise.then((comm) => {\n comm.send(data, metadata, buffers, disposeOnDone);\n });\n };\n var comm = {\n send: sendClosure\n };\n }\n window.PyViz.comms[comm_id] = comm;\n return comm;\n }\n window.PyViz.comm_manager = new JupyterCommManager();\n \n\n\nvar JS_MIME_TYPE = 'application/javascript';\nvar HTML_MIME_TYPE = 'text/html';\nvar EXEC_MIME_TYPE = 'application/vnd.holoviews_exec.v0+json';\nvar CLASS_NAME = 'output';\n\n/**\n * Render data to the DOM node\n */\nfunction render(props, node) {\n var div = document.createElement(\"div\");\n var script = document.createElement(\"script\");\n node.appendChild(div);\n node.appendChild(script);\n}\n\n/**\n * Handle when a new output is added\n */\nfunction handle_add_output(event, handle) {\n var output_area = handle.output_area;\n var output = handle.output;\n if ((output.data == undefined) || (!output.data.hasOwnProperty(EXEC_MIME_TYPE))) {\n return\n }\n var id = output.metadata[EXEC_MIME_TYPE][\"id\"];\n var toinsert = output_area.element.find(\".\" + CLASS_NAME.split(' ')[0]);\n if (id !== undefined) {\n var nchildren = toinsert.length;\n var html_node = toinsert[nchildren-1].children[0];\n html_node.innerHTML = output.data[HTML_MIME_TYPE];\n var scripts = [];\n var nodelist = html_node.querySelectorAll(\"script\");\n for (var i in nodelist) {\n if (nodelist.hasOwnProperty(i)) {\n scripts.push(nodelist[i])\n }\n }\n\n scripts.forEach( function (oldScript) {\n var newScript = document.createElement(\"script\");\n var attrs = [];\n var nodemap = oldScript.attributes;\n for (var j in nodemap) {\n if (nodemap.hasOwnProperty(j)) {\n attrs.push(nodemap[j])\n }\n }\n attrs.forEach(function(attr) { newScript.setAttribute(attr.name, attr.value) });\n newScript.appendChild(document.createTextNode(oldScript.innerHTML));\n oldScript.parentNode.replaceChild(newScript, oldScript);\n });\n if (JS_MIME_TYPE in output.data) {\n toinsert[nchildren-1].children[1].textContent = output.data[JS_MIME_TYPE];\n }\n output_area._hv_plot_id = id;\n if ((window.Bokeh !== undefined) && (id in Bokeh.index)) {\n window.PyViz.plot_index[id] = Bokeh.index[id];\n } else {\n window.PyViz.plot_index[id] = null;\n }\n } else if (output.metadata[EXEC_MIME_TYPE][\"server_id\"] !== undefined) {\n var bk_div = document.createElement(\"div\");\n bk_div.innerHTML = output.data[HTML_MIME_TYPE];\n var script_attrs = bk_div.children[0].attributes;\n for (var i = 0; i < script_attrs.length; i++) {\n toinsert[toinsert.length - 1].childNodes[1].setAttribute(script_attrs[i].name, script_attrs[i].value);\n }\n // store reference to server id on output_area\n output_area._bokeh_server_id = output.metadata[EXEC_MIME_TYPE][\"server_id\"];\n }\n}\n\n/**\n * Handle when an output is cleared or removed\n */\nfunction handle_clear_output(event, handle) {\n var id = handle.cell.output_area._hv_plot_id;\n var server_id = handle.cell.output_area._bokeh_server_id;\n if (((id === undefined) || !(id in PyViz.plot_index)) && (server_id !== undefined)) { return; }\n var comm = window.PyViz.comm_manager.get_client_comm(\"hv-extension-comm\", \"hv-extension-comm\", function () {});\n if (server_id !== null) {\n comm.send({event_type: 'server_delete', 'id': server_id});\n return;\n } else if (comm !== null) {\n comm.send({event_type: 'delete', 'id': id});\n }\n delete PyViz.plot_index[id];\n if ((window.Bokeh !== undefined) & (id in window.Bokeh.index)) {\n var doc = window.Bokeh.index[id].model.document\n doc.clear();\n const i = window.Bokeh.documents.indexOf(doc);\n if (i > -1) {\n window.Bokeh.documents.splice(i, 1);\n }\n }\n}\n\n/**\n * Handle kernel restart event\n */\nfunction handle_kernel_cleanup(event, handle) {\n delete PyViz.comms[\"hv-extension-comm\"];\n window.PyViz.plot_index = {}\n}\n\n/**\n * Handle update_display_data messages\n */\nfunction handle_update_output(event, handle) {\n handle_clear_output(event, {cell: {output_area: handle.output_area}})\n handle_add_output(event, handle)\n}\n\nfunction register_renderer(events, OutputArea) {\n function append_mime(data, metadata, element) {\n // create a DOM node to render to\n var toinsert = this.create_output_subarea(\n metadata,\n CLASS_NAME,\n EXEC_MIME_TYPE\n );\n this.keyboard_manager.register_events(toinsert);\n // Render to node\n var props = {data: data, metadata: metadata[EXEC_MIME_TYPE]};\n render(props, toinsert[0]);\n element.append(toinsert);\n return toinsert\n }\n\n events.on('output_added.OutputArea', handle_add_output);\n events.on('output_updated.OutputArea', handle_update_output);\n events.on('clear_output.CodeCell', handle_clear_output);\n events.on('delete.Cell', handle_clear_output);\n events.on('kernel_ready.Kernel', handle_kernel_cleanup);\n\n OutputArea.prototype.register_mime_type(EXEC_MIME_TYPE, append_mime, {\n safe: true,\n index: 0\n });\n}\n\nif (window.Jupyter !== undefined) {\n try {\n var events = require('base/js/events');\n var OutputArea = require('notebook/js/outputarea').OutputArea;\n if (OutputArea.prototype.mime_types().indexOf(EXEC_MIME_TYPE) == -1) {\n register_renderer(events, OutputArea);\n }\n } catch(err) {\n }\n}\n" + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "\n", + "
\n", + "\n", + "\n", + "\n", + " \n", + " \n", + "\n", + "\n", + " \n", + " \n", + "\n", + "\n", + "
\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ "import os\n", "import h5py\n", @@ -107,7 +807,9 @@ "import shapely\n", "import warnings\n", "from shapely.errors import ShapelyDeprecationWarning\n", - "warnings.filterwarnings(\"ignore\", category=ShapelyDeprecationWarning) " + "warnings.filterwarnings(\"ignore\", category=ShapelyDeprecationWarning)\n", + "\n", + "os.chdir('../../') " ] }, { @@ -120,32 +822,109 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 2, "metadata": {}, "outputs": [], "source": [ - "inDir = os.getcwd().split(\"python\")[0] # Set input directory to the current working directory\n", - "os.chdir(inDir)" + "inDir = os.getcwd()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "#### In this section, a Version 2 GEDI L2A .h5 file has been downloaded to the `inDir` defined above. You will need to download the file directly from the LP DAAC Data Pool in order to execute this tutorial.\n", + "#### You will need to download the file in order to execute this tutorial. Make sure to download the file into the `data` directory defined above.\n", "### Direct Link to file:\n", - " - https://e4ftl01.cr.usgs.gov/GEDI/GEDI02_A.002/2019.06.19/GEDI02_A_2019170155833_O02932_02_T02267_02_003_01_V002.h5 \n", + " - https://data.lpdaac.earthdatacloud.nasa.gov/lp-prod-protected/GEDI02_A.002/GEDI02_A_2019170155833_O02932_02_T02267_02_003_01_V002/GEDI02_A_2019170155833_O02932_02_T02267_02_003_01_V002.h5 \n", "\n", - "#### Make sure to download the file into the `inDir` directory defined above." + "Alternatively, you can use `earthaccess` package to download the data. " ] }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "9c5dd4f7797d454d87a996c4a467701c", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "QUEUEING TASKS | : 0%| | 0/1 [00:00\n", "#### In this section, begin working with the [GEDI Level 2A Elevation and Height Metrics Data](https://doi.org/10.5067/GEDI/GEDI02_A.002). Begin by checking out the example L2A file metadata." ] @@ -168,11 +947,22 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'data/GEDI02_A_2019170155833_O02932_02_T02267_02_003_01_V002.h5'" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ - "L2A = f'data\\\\{gediFiles[0]}'\n", + "L2A = f'data/{gediFiles[0]}'\n", "L2A" ] }, @@ -201,7 +991,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 6, "metadata": {}, "outputs": [], "source": [ @@ -217,9 +1007,28 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['BEAM0000',\n", + " 'BEAM0001',\n", + " 'BEAM0010',\n", + " 'BEAM0011',\n", + " 'BEAM0101',\n", + " 'BEAM0110',\n", + " 'BEAM1000',\n", + " 'BEAM1011',\n", + " 'METADATA']" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "list(gediL2A.keys())" ] @@ -234,9 +1043,20 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['DatasetIdentification']" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "list(gediL2A['METADATA'])" ] @@ -250,18 +1070,48 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "PGEVersion\n", + "VersionID\n", + "abstract\n", + "characterSet\n", + "creationDate\n", + "credit\n", + "fileName\n", + "language\n", + "originatorOrganizationName\n", + "purpose\n", + "shortName\n", + "spatialRepresentationType\n", + "status\n", + "topicCategory\n", + "uuid\n" + ] + } + ], "source": [ "for g in gediL2A['METADATA']['DatasetIdentification'].attrs: print(g) " ] }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The purpose of the L2A dataset is to provide waveform interpretation and extracted products from each GEDI waveform. This includes ground elevation, canopy top height, relative return energy metrics (describing canopy vertical structure, for example), and many other interpreted products from the return waveforms.\n" + ] + } + ], "source": [ "print(gediL2A['METADATA']['DatasetIdentification'].attrs['purpose'])" ] @@ -282,9 +1132,27 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['BEAM0000',\n", + " 'BEAM0001',\n", + " 'BEAM0010',\n", + " 'BEAM0011',\n", + " 'BEAM0101',\n", + " 'BEAM0110',\n", + " 'BEAM1000',\n", + " 'BEAM1011']" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "beamNames = [g for g in gediL2A.keys() if g.startswith('BEAM')]\n", "beamNames" @@ -299,18 +1167,41 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "description\n" + ] + } + ], "source": [ "for g in gediL2A['BEAM0000'].attrs: print(g)" ] }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "BEAM0000 is a Coverage beam\n", + "BEAM0001 is a Coverage beam\n", + "BEAM0010 is a Coverage beam\n", + "BEAM0011 is a Coverage beam\n", + "BEAM0101 is a Full power beam\n", + "BEAM0110 is a Full power beam\n", + "BEAM1000 is a Full power beam\n", + "BEAM1011 is a Full power beam\n" + ] + } + ], "source": [ "for b in beamNames: \n", " print(f\"{b} is a {gediL2A[b].attrs['description']}\")" @@ -325,7 +1216,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 14, "metadata": {}, "outputs": [], "source": [ @@ -342,9 +1233,29 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['BEAM0110/ancillary/l2a_alg_count',\n", + " 'BEAM0110/beam',\n", + " 'BEAM0110/channel',\n", + " 'BEAM0110/degrade_flag',\n", + " 'BEAM0110/delta_time',\n", + " 'BEAM0110/digital_elevation_model',\n", + " 'BEAM0110/digital_elevation_model_srtm',\n", + " 'BEAM0110/elev_highestreturn',\n", + " 'BEAM0110/elev_lowestmode',\n", + " 'BEAM0110/elevation_bias_flag']" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "gediL2A_objs = []\n", "gediL2A.visit(gediL2A_objs.append) # Retrieve list of datasets\n", @@ -356,7 +1267,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "---\n", + "\n", "# 3. Visualize a GEDI Orbit \n", "#### In the section below, import GEDI L2A SDS layers into a `GeoPandas` GeoDataFrame for the beam specified above. \n", "#### Use the `lat_lowestmode` and `lon_lowestmode` to create a `shapely` point for each GEDI shot location. " @@ -378,9 +1289,153 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 16, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
BeamShot NumberLongitudeLatitudeQuality Flag
0BEAM011029320600200419869-142.75569226.9238960
1BEAM011029320600200419969-142.73656726.9432420
2BEAM011029320600200420069-142.71743326.9625690
3BEAM011029320600200420169-142.69829426.9818910
4BEAM011029320600200420269-142.67913627.0011870
..................
1066BEAM011029320600200526469-80.19845251.7968580
1067BEAM011029320600200526569-80.11467751.7970290
1068BEAM011029320600200526669-80.03217351.7971730
1069BEAM011029320600200526769-79.94857151.7972450
1070BEAM011029320600200526869-79.86569851.7972460
\n", + "

1071 rows × 5 columns

\n", + "
" + ], + "text/plain": [ + " Beam Shot Number Longitude Latitude Quality Flag\n", + "0 BEAM0110 29320600200419869 -142.755692 26.923896 0\n", + "1 BEAM0110 29320600200419969 -142.736567 26.943242 0\n", + "2 BEAM0110 29320600200420069 -142.717433 26.962569 0\n", + "3 BEAM0110 29320600200420169 -142.698294 26.981891 0\n", + "4 BEAM0110 29320600200420269 -142.679136 27.001187 0\n", + "... ... ... ... ... ...\n", + "1066 BEAM0110 29320600200526469 -80.198452 51.796858 0\n", + "1067 BEAM0110 29320600200526569 -80.114677 51.797029 0\n", + "1068 BEAM0110 29320600200526669 -80.032173 51.797173 0\n", + "1069 BEAM0110 29320600200526769 -79.948571 51.797245 0\n", + "1070 BEAM0110 29320600200526869 -79.865698 51.797246 0\n", + "\n", + "[1071 rows x 5 columns]" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "lonSample, latSample, shotSample, qualitySample, beamSample = [], [], [], [], [] # Set up lists to store data\n", "\n", @@ -414,7 +1469,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 17, "metadata": {}, "outputs": [], "source": [ @@ -431,7 +1486,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 18, "metadata": {}, "outputs": [], "source": [ @@ -448,9 +1503,31 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 19, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "0 POINT (-142.75569 26.92390)\n", + "1 POINT (-142.73657 26.94324)\n", + "2 POINT (-142.71743 26.96257)\n", + "3 POINT (-142.69829 26.98189)\n", + "4 POINT (-142.67914 27.00119)\n", + " ... \n", + "1066 POINT (-80.19845 51.79686)\n", + "1067 POINT (-80.11468 51.79703)\n", + "1068 POINT (-80.03217 51.79717)\n", + "1069 POINT (-79.94857 51.79725)\n", + "1070 POINT (-79.86570 51.79725)\n", + "Name: geometry, Length: 1071, dtype: geometry" + ] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "# Convert to a Geodataframe\n", "latslons = gp.GeoDataFrame(latslons)\n", @@ -467,11 +1544,25 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 20, "metadata": { "scrolled": true }, - "outputs": [], + "outputs": [ + { + "data": { + "image/svg+xml": [ + "" + ], + "text/plain": [ + "" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "latslons['geometry'][0]" ] @@ -486,7 +1577,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 21, "metadata": {}, "outputs": [], "source": [ @@ -506,27 +1597,108 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 22, "metadata": {}, "outputs": [], "source": [ - "redwoodNP = gp.GeoDataFrame.from_file('Data\\\\RedwoodNP.geojson') # Import GeoJSON as GeoDataFrame" + "redwoodNP = gp.GeoDataFrame.from_file('data/RedwoodNP.geojson') # Import GeoJSON as GeoDataFrame" ] }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 23, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
GIS_LOC_IDUNIT_CODEGROUP_CODEUNIT_NAMEUNIT_TYPEMETA_MIDFLANDS_CODEDATE_EDITGIS_NOTESgeometry
0NoneREDWNoneRedwoodNational ParkNoneNoneNoneShifted 0.06 milesMULTIPOLYGON (((-124.01829 41.44539, -124.0184...
\n", + "
" + ], + "text/plain": [ + " GIS_LOC_ID UNIT_CODE GROUP_CODE UNIT_NAME UNIT_TYPE META_MIDF \\\n", + "0 None REDW None Redwood National Park None \n", + "\n", + " LANDS_CODE DATE_EDIT GIS_NOTES \\\n", + "0 None None Shifted 0.06 miles \n", + "\n", + " geometry \n", + "0 MULTIPOLYGON (((-124.01829 41.44539, -124.0184... " + ] + }, + "execution_count": 23, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "redwoodNP" ] }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 24, + "metadata": {}, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "" + ], + "text/plain": [ + "" + ] + }, + "execution_count": 24, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "redwoodNP['geometry'][0] # Plot GeoDataFrame" ] @@ -540,9 +1712,20 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 25, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['Beam', 'Shot Number', 'Quality Flag']" + ] + }, + "execution_count": 25, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "# Create a list of geodataframe columns to be included as attributes in the output map\n", "vdims = []\n", @@ -561,9 +1744,101 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 26, + "metadata": {}, + "outputs": [ + { + "data": {}, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.holoviews_exec.v0+json": "", + "text/html": [ + "
\n", + "
\n", + "
\n", + "" + ], + "text/plain": [ + ":Overlay\n", + " .Polygons.I :Polygons [Longitude,Latitude]\n", + " .WMTS.I :WMTS [Longitude,Latitude]\n", + " .Points.I :Points [Longitude,Latitude] (Beam,Shot Number,Quality Flag)" + ] + }, + "execution_count": 26, + "metadata": { + "application/vnd.holoviews_exec.v0+json": { + "id": "p1005" + } + }, + "output_type": "execute_result" + } + ], "source": [ "# Call the function for plotting the GEDI points\n", "gv.Polygons(redwoodNP['geometry']).opts(line_color='red', color=None) * pointVisual(latslons, vdims = vdims)" @@ -599,9 +1874,17 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 27, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Quality Flag: Flag simpilfying selection of most useful data\n" + ] + } + ], "source": [ "print(f\"Quality Flag: {gediL2A[b]['quality_flag'].attrs['description']}\")" ] @@ -636,7 +1919,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 28, "metadata": {}, "outputs": [], "source": [ @@ -647,7 +1930,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "---\n", + "\n", "# 4. Work with GEDI L2A Data\n", "### Interpretation of RH Metrics\n", "#### The GEDI L2A data product provides relative height (RH) metrics, which are “lidar perceived” metrics that have the following characteristics:\n", @@ -668,18 +1951,40 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 29, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "4320" + ] + }, + "execution_count": 29, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "len(gediSDS)" ] }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 30, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['BEAM0110']" + ] + }, + "execution_count": 30, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "beamNames" ] @@ -693,11 +1998,22 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 31, "metadata": { "scrolled": true }, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "540" + ] + }, + "execution_count": 31, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "beamSDS = [g for g in gediSDS if beamNames[0] in g] # Subset to a single beam\n", "len(beamSDS)" @@ -712,7 +2028,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 32, "metadata": {}, "outputs": [], "source": [ @@ -721,9 +2037,20 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 33, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "45732" + ] + }, + "execution_count": 33, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "index = np.where(gediL2A[f'{beamNames[0]}/shot_number'][()]==shot)[0][0] # Set the index for the shot identified above\n", "index" @@ -739,7 +2066,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 34, "metadata": {}, "outputs": [], "source": [ @@ -755,9 +2082,17 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 35, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "rh is Relative height metrics at 1 % interval\n" + ] + } + ], "source": [ "print(f\"rh is {rh.attrs['description']}\")" ] @@ -773,9 +2108,17 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 36, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "selected_algorithm is ID of algorithm selected as identifying the lowest non-noise mode\n" + ] + } + ], "source": [ "algo = gediL2A[f'{beamNames[0]}/selected_algorithm'] # selected algorithm\n", "print(f\"selected_algorithm is {algo.attrs['description']}\")" @@ -790,7 +2133,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 37, "metadata": {}, "outputs": [], "source": [ @@ -808,7 +2151,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 38, "metadata": {}, "outputs": [], "source": [ @@ -827,11 +2170,20 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 39, "metadata": { "scrolled": true }, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The shot is located at: 41.28541681552604, -124.03006394248702 (shot ID: 29320600200465601, index 45732) and is from beam BEAM0110.\n", + "The selected algorithm is Algorithm Setting Group 1.\n" + ] + } + ], "source": [ "print(f\"The shot is located at: {str(rhLat)}, {str(rhLon)} (shot ID: {shot}, index {index}) and is from beam {beamNames[0]}.\")\n", "print(f\"The selected algorithm is Algorithm Setting Group {str(algoShot1)}.\")" @@ -846,7 +2198,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 40, "metadata": {}, "outputs": [], "source": [ @@ -864,7 +2216,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 41, "metadata": {}, "outputs": [], "source": [ @@ -883,9 +2235,98 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 42, + "metadata": {}, + "outputs": [ + { + "data": {}, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.holoviews_exec.v0+json": "", + "text/html": [ + "
\n", + "
\n", + "
\n", + "" + ], + "text/plain": [ + ":Curve [x] (y)" + ] + }, + "execution_count": 42, + "metadata": { + "application/vnd.holoviews_exec.v0+json": { + "id": "p1111" + } + }, + "output_type": "execute_result" + } + ], "source": [ "rhVis = hv.Curve(rhShot, label=f'Selected Algorithm (a{str(algoShot1)})')\n", "rhVis = rhVis.opts(color='black', tools=['hover'], height=500, width=400, title='GEDI L2A Relative Height Metrics', \n", @@ -911,7 +2352,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 43, "metadata": {}, "outputs": [], "source": [ @@ -930,9 +2371,104 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 44, + "metadata": {}, + "outputs": [ + { + "data": {}, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.holoviews_exec.v0+json": "", + "text/html": [ + "
\n", + "
\n", + "
\n", + "" + ], + "text/plain": [ + ":Overlay\n", + " .Curve.Selected_Algorithm_left_parenthesis_a1_right_parenthesis :Curve [x] (y)\n", + " .Curve.Ground_Return :Curve [x] (y)\n", + " .Curve.RH100 :Curve [x] (y)\n", + " .Curve.RH25 :Curve [x] (y)\n", + " .Curve.RH50 :Curve [x] (y)\n", + " .Curve.RH75 :Curve [x] (y)" + ] + }, + "execution_count": 44, + "metadata": { + "application/vnd.holoviews_exec.v0+json": { + "id": "p1167" + } + }, + "output_type": "execute_result" + } + ], "source": [ "# Plot all of the metrics together\n", "l2aVis = rhVis * zVis * ztVis * rh25Vis * rh50Vis * rh75Vis\n", @@ -971,18 +2507,126 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 45, "metadata": {}, "outputs": [], "source": [ - "wvDF = pd.read_csv('data\\\\waveform.csv')" + "wvDF = pd.read_csv('data/waveform.csv')" ] }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 46, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
Amplitude (DN)Elevation (m)
0227.00992111.252897
1226.57410111.103165
2226.66390110.953434
3227.26736110.803702
4228.11644110.653970
.........
1241230.74078-74.564040
1242230.04378-74.713771
1243229.24507-74.863503
1244228.71117-75.013235
1245228.53528-75.162966
\n", + "

1246 rows × 2 columns

\n", + "
" + ], + "text/plain": [ + " Amplitude (DN) Elevation (m)\n", + "0 227.00992 111.252897\n", + "1 226.57410 111.103165\n", + "2 226.66390 110.953434\n", + "3 227.26736 110.803702\n", + "4 228.11644 110.653970\n", + "... ... ...\n", + "1241 230.74078 -74.564040\n", + "1242 230.04378 -74.713771\n", + "1243 229.24507 -74.863503\n", + "1244 228.71117 -75.013235\n", + "1245 228.53528 -75.162966\n", + "\n", + "[1246 rows x 2 columns]" + ] + }, + "execution_count": 46, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "wvDF" ] @@ -996,9 +2640,98 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 47, + "metadata": {}, + "outputs": [ + { + "data": {}, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.holoviews_exec.v0+json": "", + "text/html": [ + "
\n", + "
\n", + "
\n", + "" + ], + "text/plain": [ + ":Curve [Amplitude (DN)] (Elevation (m))" + ] + }, + "execution_count": 47, + "metadata": { + "application/vnd.holoviews_exec.v0+json": { + "id": "p1313" + } + }, + "output_type": "execute_result" + } + ], "source": [ "# Create a holoviews interactive Curve plot with additional parameters defining the plot aesthetics \n", "visL1B = hv.Curve(wvDF).opts(color='darkgreen', tools=['hover'], height=600, width=400,\n", @@ -1017,9 +2750,116 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 48, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/srv/conda/envs/notebook/lib/python3.10/site-packages/holoviews/plotting/bokeh/plot.py:959: UserWarning: found multiple competing values for 'toolbar.active_drag' property; using the latest value\n", + " layout_plot = gridplot(\n", + "/srv/conda/envs/notebook/lib/python3.10/site-packages/holoviews/plotting/bokeh/plot.py:959: UserWarning: found multiple competing values for 'toolbar.active_scroll' property; using the latest value\n", + " layout_plot = gridplot(\n" + ] + }, + { + "data": {}, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.holoviews_exec.v0+json": "", + "text/html": [ + "
\n", + "
\n", + "
\n", + "" + ], + "text/plain": [ + ":Layout\n", + " .Curve.I :Curve [Amplitude (DN)] (Elevation (m))\n", + " .Overlay.I :Overlay\n", + " .Curve.Selected_Algorithm_left_parenthesis_a1_right_parenthesis :Curve [x] (y)\n", + " .Curve.Ground_Return :Curve [x] (y)\n", + " .Curve.RH100 :Curve [x] (y)\n", + " .Curve.RH25 :Curve [x] (y)\n", + " .Curve.RH50 :Curve [x] (y)\n", + " .Curve.RH75 :Curve [x] (y)" + ] + }, + "execution_count": 48, + "metadata": { + "application/vnd.holoviews_exec.v0+json": { + "id": "p1371" + } + }, + "output_type": "execute_result" + } + ], "source": [ "visL1B.opts(height=600, width=400, ylim=(np.min(rhShot), np.max(rhShot)+5), ylabel='Elevation (m)', xlabel='Amplitude (DN)') \\\n", "+ l2aVis.opts(height=600, width=400, ylim=(np.min(rhShot), np.max(rhShot)+5))" @@ -1053,7 +2893,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 49, "metadata": {}, "outputs": [], "source": [ @@ -1069,9 +2909,36 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 50, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['BEAM0110/geolocation/elev_highestreturn_a3',\n", + " 'BEAM0110/geolocation/elev_lowestmode_a3',\n", + " 'BEAM0110/geolocation/elev_lowestreturn_a3',\n", + " 'BEAM0110/geolocation/elevs_allmodes_a3',\n", + " 'BEAM0110/geolocation/energy_lowestmode_a3',\n", + " 'BEAM0110/geolocation/lat_highestreturn_a3',\n", + " 'BEAM0110/geolocation/lat_lowestmode_a3',\n", + " 'BEAM0110/geolocation/lat_lowestreturn_a3',\n", + " 'BEAM0110/geolocation/lats_allmodes_a3',\n", + " 'BEAM0110/geolocation/lon_highestreturn_a3',\n", + " 'BEAM0110/geolocation/lon_lowestmode_a3',\n", + " 'BEAM0110/geolocation/lon_lowestreturn_a3',\n", + " 'BEAM0110/geolocation/lons_allmodes_a3',\n", + " 'BEAM0110/geolocation/num_detectedmodes_a3',\n", + " 'BEAM0110/geolocation/quality_flag_a3',\n", + " 'BEAM0110/geolocation/rh_a3',\n", + " 'BEAM0110/geolocation/sensitivity_a3']" + ] + }, + "execution_count": 50, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "a3SDS = [g for g in geolocationSDS if g.endswith('a3')] # Select algorithm 3 datasets\n", "a3SDS" @@ -1086,7 +2953,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 51, "metadata": {}, "outputs": [], "source": [ @@ -1108,9 +2975,98 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 52, + "metadata": {}, + "outputs": [ + { + "data": {}, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.holoviews_exec.v0+json": "", + "text/html": [ + "
\n", + "
\n", + "
\n", + "" + ], + "text/plain": [ + ":Curve [x] (y)" + ] + }, + "execution_count": 52, + "metadata": { + "application/vnd.holoviews_exec.v0+json": { + "id": "p1580" + } + }, + "output_type": "execute_result" + } + ], "source": [ "rhVisA3 = hv.Curve(rhShota3, label='Algorithm Setting Group 3 (a3)')\n", "rhVisA3 = rhVisA3.opts(color='green', tools=['hover'], height=500, width=400, title='GEDI L2A Relative Height Metrics (a3)', \n", @@ -1128,9 +3084,100 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 53, + "metadata": {}, + "outputs": [ + { + "data": {}, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.holoviews_exec.v0+json": "", + "text/html": [ + "
\n", + "
\n", + "
\n", + "" + ], + "text/plain": [ + ":Overlay\n", + " .Curve.Selected_Algorithm_left_parenthesis_a1_right_parenthesis :Curve [x] (y)\n", + " .Curve.Algorithm_Setting_Group_3_left_parenthesis_a3_right_parenthesis :Curve [x] (y)" + ] + }, + "execution_count": 53, + "metadata": { + "application/vnd.holoviews_exec.v0+json": { + "id": "p1636" + } + }, + "output_type": "execute_result" + } + ], "source": [ "(rhVis * rhVisA3).opts(show_legend=True, legend_position='bottom_right', title='GEDI L2A Relative Height Metrics by Algorithm',\n", " ylabel='Elevation (m)', xlabel='Percent Energy Returned', xlim=(0, 100), \n", @@ -1149,7 +3196,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "---\n", + "\n", "# 5. Plot Transects " ] }, @@ -1162,7 +3209,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 54, "metadata": {}, "outputs": [], "source": [ @@ -1186,7 +3233,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 55, "metadata": {}, "outputs": [], "source": [ @@ -1195,7 +3242,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 56, "metadata": {}, "outputs": [], "source": [ @@ -1219,9 +3266,275 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 57, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
Shot IndexShot NumberLatitudeLongitudeTandem-X DEMSRTM DEMElevation (m)Canopy Elevation (m)Canopy Height (rh100)Quality FlagDegrade FlagSensitivitySelected Algorithm
002932060020041986926.923896-142.755692-999999.000000-999999.03693.5039063693.5039060.0005.0299311
112932060020041987026.924089-142.755501-999999.000000-999999.03693.5183113693.5183110.000-4.6206631
222932060020041987126.924283-142.755309-999999.000000-999999.03693.5327153693.5327150.0006.6137961
332932060020041987226.924477-142.755118-999999.000000-999999.03693.5471193693.5471190.000-0.7887311
442932060020041987326.924671-142.754927-999999.000000-999999.03693.5615233693.5615230.000-64.3275151
..........................................
1070091070092932060020052687851.797246-79.858241-36.021603-999999.04535.9052734535.9052730.00800.4461881
1070101070102932060020052687951.797246-79.857414-999999.000000-999999.04543.5742194543.5742190.00800.7098041
1070111070112932060020052688051.797245-79.856577-999999.000000-999999.04515.9028324515.9028320.0080-0.8169401
1070121070122932060020052688151.797245-79.855748-999999.000000-999999.04516.1020514516.1020510.0080-43.3016511
1070131070132932060020052688251.797245-79.854919-41.067085-999999.04515.5693364515.5693360.00800.4082801
\n", + "

107014 rows × 13 columns

\n", + "
" + ], + "text/plain": [ + " Shot Index Shot Number Latitude Longitude Tandem-X DEM \\\n", + "0 0 29320600200419869 26.923896 -142.755692 -999999.000000 \n", + "1 1 29320600200419870 26.924089 -142.755501 -999999.000000 \n", + "2 2 29320600200419871 26.924283 -142.755309 -999999.000000 \n", + "3 3 29320600200419872 26.924477 -142.755118 -999999.000000 \n", + "4 4 29320600200419873 26.924671 -142.754927 -999999.000000 \n", + "... ... ... ... ... ... \n", + "107009 107009 29320600200526878 51.797246 -79.858241 -36.021603 \n", + "107010 107010 29320600200526879 51.797246 -79.857414 -999999.000000 \n", + "107011 107011 29320600200526880 51.797245 -79.856577 -999999.000000 \n", + "107012 107012 29320600200526881 51.797245 -79.855748 -999999.000000 \n", + "107013 107013 29320600200526882 51.797245 -79.854919 -41.067085 \n", + "\n", + " SRTM DEM Elevation (m) Canopy Elevation (m) Canopy Height (rh100) \\\n", + "0 -999999.0 3693.503906 3693.503906 0.0 \n", + "1 -999999.0 3693.518311 3693.518311 0.0 \n", + "2 -999999.0 3693.532715 3693.532715 0.0 \n", + "3 -999999.0 3693.547119 3693.547119 0.0 \n", + "4 -999999.0 3693.561523 3693.561523 0.0 \n", + "... ... ... ... ... \n", + "107009 -999999.0 4535.905273 4535.905273 0.0 \n", + "107010 -999999.0 4543.574219 4543.574219 0.0 \n", + "107011 -999999.0 4515.902832 4515.902832 0.0 \n", + "107012 -999999.0 4516.102051 4516.102051 0.0 \n", + "107013 -999999.0 4515.569336 4515.569336 0.0 \n", + "\n", + " Quality Flag Degrade Flag Sensitivity Selected Algorithm \n", + "0 0 0 5.029931 1 \n", + "1 0 0 -4.620663 1 \n", + "2 0 0 6.613796 1 \n", + "3 0 0 -0.788731 1 \n", + "4 0 0 -64.327515 1 \n", + "... ... ... ... ... \n", + "107009 0 80 0.446188 1 \n", + "107010 0 80 0.709804 1 \n", + "107011 0 80 -0.816940 1 \n", + "107012 0 80 -43.301651 1 \n", + "107013 0 80 0.408280 1 \n", + "\n", + "[107014 rows x 13 columns]" + ] + }, + "execution_count": 57, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "transectDF" ] @@ -1236,9 +3549,98 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 58, + "metadata": {}, + "outputs": [ + { + "data": {}, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.holoviews_exec.v0+json": "", + "text/html": [ + "
\n", + "
\n", + "
\n", + "" + ], + "text/plain": [ + ":Scatter [x] (y)" + ] + }, + "execution_count": 58, + "metadata": { + "application/vnd.holoviews_exec.v0+json": { + "id": "p1714" + } + }, + "output_type": "execute_result" + } + ], "source": [ "# Plot Canopy Height\n", "canopyVis = hv.Scatter((transectDF['Shot Index'], transectDF['Canopy Height (rh100)']))\n", @@ -1255,7 +3657,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 59, "metadata": {}, "outputs": [], "source": [ @@ -1274,7 +3676,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 60, "metadata": {}, "outputs": [], "source": [ @@ -1283,9 +3685,275 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 61, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
Shot IndexShot NumberLatitudeLongitudeTandem-X DEMSRTM DEMElevation (m)Canopy Elevation (m)Canopy Height (rh100)Quality FlagDegrade FlagSensitivitySelected Algorithm
0NaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaN
1NaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaN
2NaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaN
3NaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaN
4NaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaN
..........................................
107009NaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaN
107010NaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaN
107011NaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaN
107012NaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaN
107013NaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaN
\n", + "

107014 rows × 13 columns

\n", + "
" + ], + "text/plain": [ + " Shot Index Shot Number Latitude Longitude Tandem-X DEM SRTM DEM \\\n", + "0 NaN NaN NaN NaN NaN NaN \n", + "1 NaN NaN NaN NaN NaN NaN \n", + "2 NaN NaN NaN NaN NaN NaN \n", + "3 NaN NaN NaN NaN NaN NaN \n", + "4 NaN NaN NaN NaN NaN NaN \n", + "... ... ... ... ... ... ... \n", + "107009 NaN NaN NaN NaN NaN NaN \n", + "107010 NaN NaN NaN NaN NaN NaN \n", + "107011 NaN NaN NaN NaN NaN NaN \n", + "107012 NaN NaN NaN NaN NaN NaN \n", + "107013 NaN NaN NaN NaN NaN NaN \n", + "\n", + " Elevation (m) Canopy Elevation (m) Canopy Height (rh100) \\\n", + "0 NaN NaN NaN \n", + "1 NaN NaN NaN \n", + "2 NaN NaN NaN \n", + "3 NaN NaN NaN \n", + "4 NaN NaN NaN \n", + "... ... ... ... \n", + "107009 NaN NaN NaN \n", + "107010 NaN NaN NaN \n", + "107011 NaN NaN NaN \n", + "107012 NaN NaN NaN \n", + "107013 NaN NaN NaN \n", + "\n", + " Quality Flag Degrade Flag Sensitivity Selected Algorithm \n", + "0 NaN NaN NaN NaN \n", + "1 NaN NaN NaN NaN \n", + "2 NaN NaN NaN NaN \n", + "3 NaN NaN NaN NaN \n", + "4 NaN NaN NaN NaN \n", + "... ... ... ... ... \n", + "107009 NaN NaN NaN NaN \n", + "107010 NaN NaN NaN NaN \n", + "107011 NaN NaN NaN NaN \n", + "107012 NaN NaN NaN NaN \n", + "107013 NaN NaN NaN NaN \n", + "\n", + "[107014 rows x 13 columns]" + ] + }, + "execution_count": 61, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "transectDF" ] @@ -1307,7 +3975,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 62, "metadata": {}, "outputs": [], "source": [ @@ -1324,7 +3992,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 63, "metadata": {}, "outputs": [], "source": [ @@ -1333,9 +4001,17 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 64, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Quality filtering complete, 15551 high quality shots remaining.\n" + ] + } + ], "source": [ "print(f\"Quality filtering complete, {len(transectDF)} high quality shots remaining.\")" ] @@ -1350,7 +4026,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 65, "metadata": {}, "outputs": [], "source": [ @@ -1364,7 +4040,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 66, "metadata": {}, "outputs": [], "source": [ @@ -1378,7 +4054,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 67, "metadata": {}, "outputs": [], "source": [ @@ -1389,7 +4065,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 68, "metadata": {}, "outputs": [], "source": [ @@ -1401,9 +4077,102 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 69, + "metadata": {}, + "outputs": [ + { + "data": {}, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.holoviews_exec.v0+json": "", + "text/html": [ + "
\n", + "
\n", + "
\n", + "" + ], + "text/plain": [ + ":Overlay\n", + " .Scatter.Tandem_hyphen_minus_X_DEM :Scatter [x] (y)\n", + " .Scatter.SRTM_DEM :Scatter [x] (y)\n", + " .Scatter.GEDI_hyphen_minus_derived_Elevation :Scatter [x] (y)\n", + " .Scatter.Canopy_Top_Elevation :Scatter [x] (y)" + ] + }, + "execution_count": 69, + "metadata": { + "application/vnd.holoviews_exec.v0+json": { + "id": "p1769" + } + }, + "output_type": "execute_result" + } + ], "source": [ "# Combine all three scatterplots\n", "(demVis * srtmVis * zVis * rhVis).opts(show_legend=True, legend_position='top_left',fontsize={'title':14, 'xlabel':16, \n", @@ -1439,16 +4208,24 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 70, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "45732\n" + ] + } + ], "source": [ "print(index)" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 71, "metadata": {}, "outputs": [], "source": [ @@ -1459,11 +4236,19 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 72, "metadata": { "scrolled": true }, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The transect begins at (41.27021331597735, -124.05764284824794) and ends at (41.30056975532455, -124.00258112950078).\n" + ] + } + ], "source": [ "print(f\"The transect begins at ({transectDF['Latitude'][start]}, {transectDF['Longitude'][start]}) and ends at ({transectDF['Latitude'][end]}, {transectDF['Longitude'][end]}).\")" ] @@ -1477,7 +4262,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 73, "metadata": {}, "outputs": [], "source": [ @@ -1500,7 +4285,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 74, "metadata": {}, "outputs": [], "source": [ @@ -1518,7 +4303,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 75, "metadata": {}, "outputs": [], "source": [ @@ -1542,9 +4327,98 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 76, + "metadata": {}, + "outputs": [ + { + "data": {}, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.holoviews_exec.v0+json": "", + "text/html": [ + "
\n", + "
\n", + "
\n", + "" + ], + "text/plain": [ + ":Scatter [x] (y)" + ] + }, + "execution_count": 76, + "metadata": { + "application/vnd.holoviews_exec.v0+json": { + "id": "p1878" + } + }, + "output_type": "execute_result" + } + ], "source": [ "title = 'Relative Height Metrics (0-100) across Redwood National Park: June 19, 2019'\n", "hv.Scatter((distList,rhList)).opts(color='darkgreen', alpha=0.1, height=500, width=900, size=7.5, \n", @@ -1561,7 +4435,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 77, "metadata": {}, "outputs": [], "source": [ @@ -1578,9 +4452,100 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 78, + "metadata": {}, + "outputs": [ + { + "data": {}, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.holoviews_exec.v0+json": "", + "text/html": [ + "
\n", + "
\n", + "
\n", + "" + ], + "text/plain": [ + ":Overlay\n", + " .Curve.Ground_Elevation :Curve [x] (y)\n", + " .Scatter.RH_0_hyphen_minus_100 :Scatter [x] (y)" + ] + }, + "execution_count": 78, + "metadata": { + "application/vnd.holoviews_exec.v0+json": { + "id": "p1933" + } + }, + "output_type": "execute_result" + } + ], "source": [ "# Plot GEDI-Retrieved Elevation\n", "demVis = hv.Curve((transectDF['Distance'], transectDF['Elevation (m)']), label='Ground Elevation')\n", @@ -1601,7 +4566,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 79, "metadata": {}, "outputs": [], "source": [ @@ -1610,7 +4575,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 80, "metadata": {}, "outputs": [], "source": [ @@ -1628,7 +4593,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "---\n", + "\n", "# 6. Spatial Visualization\n", "#### Section 6 combines many of the techniques learned above including how to import GEDI datasets, perform quality filtering, spatial subsetting, and visualization. " ] @@ -1649,7 +4614,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 81, "metadata": {}, "outputs": [], "source": [ @@ -1658,9 +4623,27 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 82, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['BEAM0000',\n", + " 'BEAM0001',\n", + " 'BEAM0010',\n", + " 'BEAM0011',\n", + " 'BEAM0101',\n", + " 'BEAM0110',\n", + " 'BEAM1000',\n", + " 'BEAM1011']" + ] + }, + "execution_count": 82, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "beamNames" ] @@ -1674,7 +4657,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 83, "metadata": {}, "outputs": [], "source": [ @@ -1691,7 +4674,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 84, "metadata": {}, "outputs": [], "source": [ @@ -1722,7 +4705,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 85, "metadata": {}, "outputs": [], "source": [ @@ -1747,7 +4730,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 86, "metadata": {}, "outputs": [], "source": [ @@ -1764,9 +4747,20 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 87, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "790135" + ] + }, + "execution_count": 87, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "len(allDF)" ] @@ -1780,16 +4774,30 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 88, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(-124.16015705494489,\n", + " 41.080601363502545,\n", + " -123.84950230520286,\n", + " 41.83981133687605)" + ] + }, + "execution_count": 88, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "redwoodNP.envelope[0].bounds" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 89, "metadata": {}, "outputs": [], "source": [ @@ -1805,7 +4813,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 90, "metadata": {}, "outputs": [], "source": [ @@ -1824,7 +4832,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 91, "metadata": {}, "outputs": [], "source": [ @@ -1833,9 +4841,20 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 92, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "4477" + ] + }, + "execution_count": 92, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "len(allDF)" ] @@ -1849,9 +4868,20 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 93, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "3079" + ] + }, + "execution_count": 93, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "# Set any poor quality returns to NaN\n", "allDF = allDF.where(allDF['Quality Flag'].ne(0))\n", @@ -1870,7 +4900,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 94, "metadata": {}, "outputs": [], "source": [ @@ -1880,7 +4910,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 95, "metadata": {}, "outputs": [], "source": [ @@ -1905,9 +4935,101 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 96, + "metadata": {}, + "outputs": [ + { + "data": {}, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.holoviews_exec.v0+json": "", + "text/html": [ + "
\n", + "
\n", + "
\n", + "" + ], + "text/plain": [ + ":Overlay\n", + " .WMTS.I :WMTS [Longitude,Latitude]\n", + " .Points.I :Points [Longitude,Latitude] (Shot Number,Beam,Tandem-X DEM,Elevation (m),Canopy Elevation (m),Canopy Height (rh100),RH 98,RH 25,Quality Flag,Degrade Flag,Sensitivity,Selected Algorithm)\n", + " .Polygons.I :Polygons [Longitude,Latitude]" + ] + }, + "execution_count": 96, + "metadata": { + "application/vnd.holoviews_exec.v0+json": { + "id": "p2009" + } + }, + "output_type": "execute_result" + } + ], "source": [ "allDF['Shot Number'] = allDF['Shot Number'].astype(str) # Convert shot number to string\n", "\n", @@ -1936,9 +5058,100 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 97, + "metadata": {}, + "outputs": [ + { + "data": {}, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.holoviews_exec.v0+json": "", + "text/html": [ + "
\n", + "
\n", + "
\n", + "" + ], + "text/plain": [ + ":Overlay\n", + " .WMTS.I :WMTS [Longitude,Latitude]\n", + " .Points.I :Points [Longitude,Latitude] (Shot Number,Beam,Tandem-X DEM,Elevation (m),Canopy Elevation (m),Canopy Height (rh100),RH 98,RH 25,Quality Flag,Degrade Flag,Sensitivity,Selected Algorithm)" + ] + }, + "execution_count": 97, + "metadata": { + "application/vnd.holoviews_exec.v0+json": { + "id": "p2124" + } + }, + "output_type": "execute_result" + } + ], "source": [ "# Plot the basemap and geoviews Points, defining the color as the Canopy Height for each shot\n", "(gvts.EsriImagery * gv.Points(allDF, vdims=vdims).options(color='Canopy Height (rh100)',cmap='plasma', size=3, tools=['hover'],\n", @@ -1971,9 +5184,100 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 98, + "metadata": {}, + "outputs": [ + { + "data": {}, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.holoviews_exec.v0+json": "", + "text/html": [ + "
\n", + "
\n", + "
\n", + "" + ], + "text/plain": [ + ":Overlay\n", + " .WMTS.I :WMTS [Longitude,Latitude]\n", + " .Points.I :Points [Longitude,Latitude] (Shot Number,Beam,Tandem-X DEM,Elevation (m),Canopy Elevation (m),Canopy Height (rh100),RH 98,RH 25,Quality Flag,Degrade Flag,Sensitivity,Selected Algorithm)" + ] + }, + "execution_count": 98, + "metadata": { + "application/vnd.holoviews_exec.v0+json": { + "id": "p2222" + } + }, + "output_type": "execute_result" + } + ], "source": [ "(gvts.EsriImagery * gv.Points(allDF, vdims=vdims).options(color='Elevation (m)',cmap='terrain', size=3, tools=['hover'],\n", " clim=(min(allDF['Elevation (m)']), max(allDF['Elevation (m)'])),\n", @@ -1995,25 +5299,47 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "---\n", + "\n", "# 7. Export Subsets as GeoJSON Files\n", "#### In this section, export the GeoDataFrame as a `.geojson` file that can be easily opened in your favorite remote sensing and/or GIS software and will include an attribute table with all of the shots/values for each of the SDS layers in the dataframe." ] }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 99, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'data/GEDI02_A_2019170155833_O02932_02_T02267_02_003_01_V002.h5'" + ] + }, + "execution_count": 99, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "gediL2A.filename # L2A Filename" ] }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 100, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'data/GEDI02_A_2019170155833_O02932_02_T02267_02_003_01_V002.json'" + ] + }, + "execution_count": 100, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "outName = gediL2A.filename.replace('.h5', '.json') # Create an output file name using the input file name\n", "outName" @@ -2021,7 +5347,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 101, "metadata": {}, "outputs": [], "source": [ @@ -2030,7 +5356,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 102, "metadata": {}, "outputs": [], "source": [ @@ -2044,21 +5370,16 @@ "tags": [] }, "source": [ - "
\n", - "

Contact Information

\n", - "

Material written by LP DAAC1

\n", - "
    \n", - " Contact: LPDAAC@usgs.gov
    \n", - " Voice: +1-605-594-6116
    \n", - " Organization: Land Processes Distributed Active Archive Center (LP DAAC)
    \n", - " Website: https://lpdaac.usgs.gov/
    \n", - " Date last modified: 03-27-2023
    \n", - "
\n", - " \n", - "1KBR Inc., contractor to the U.S. Geological Survey, Earth Resources Observation and Science (EROS) Center, Sioux Falls, South Dakota, 57198-001, USA. Work performed under USGS contract G15PD00467 for LP DAAC2.\n", "\n", - "2LP DAAC Work performed under NASA contract NNG14HH33I.\n", - "
" + "## Contact Info: \n", + "\n", + "Email: LPDAAC@usgs.gov \n", + "Voice: +1-866-573-3222 \n", + "Organization: Land Processes Distributed Active Archive Center (LP DAAC)¹ \n", + "Website: \n", + "Date last modified: 02-20-2024 \n", + "\n", + "¹Work performed under USGS contract G15PD00467 for NASA contract NNG14HH33I. " ] } ], @@ -2078,7 +5399,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.7" + "version": "3.10.13" }, "vscode": { "interpreter": { diff --git a/python/tutorials/GEDI_L2B_V2_Tutorial.ipynb b/python/tutorials/GEDI_L2B_V2_Tutorial.ipynb index ba0368c..28c81a5 100644 --- a/python/tutorials/GEDI_L2B_V2_Tutorial.ipynb +++ b/python/tutorials/GEDI_L2B_V2_Tutorial.ipynb @@ -92,9 +92,709 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "data": { + "application/javascript": [ + "(function(root) {\n", + " function now() {\n", + " return new Date();\n", + " }\n", + "\n", + " var force = true;\n", + " var py_version = '3.2.2'.replace('rc', '-rc.').replace('.dev', '-dev.');\n", + " var is_dev = py_version.indexOf(\"+\") !== -1 || py_version.indexOf(\"-\") !== -1;\n", + " var reloading = false;\n", + " var Bokeh = root.Bokeh;\n", + " var bokeh_loaded = Bokeh != null && (Bokeh.version === py_version || (Bokeh.versions !== undefined && Bokeh.versions.has(py_version)));\n", + "\n", + " if (typeof (root._bokeh_timeout) === \"undefined\" || force) {\n", + " root._bokeh_timeout = Date.now() + 5000;\n", + " root._bokeh_failed_load = false;\n", + " }\n", + "\n", + " function run_callbacks() {\n", + " try {\n", + " root._bokeh_onload_callbacks.forEach(function(callback) {\n", + " if (callback != null)\n", + " callback();\n", + " });\n", + " } finally {\n", + " delete root._bokeh_onload_callbacks;\n", + " }\n", + " console.debug(\"Bokeh: all callbacks have finished\");\n", + " }\n", + "\n", + " function load_libs(css_urls, js_urls, js_modules, js_exports, callback) {\n", + " if (css_urls == null) css_urls = [];\n", + " if (js_urls == null) js_urls = [];\n", + " if (js_modules == null) js_modules = [];\n", + " if (js_exports == null) js_exports = {};\n", + "\n", + " root._bokeh_onload_callbacks.push(callback);\n", + "\n", + " if (root._bokeh_is_loading > 0) {\n", + " console.debug(\"Bokeh: BokehJS is being loaded, scheduling callback at\", now());\n", + " return null;\n", + " }\n", + " if (js_urls.length === 0 && js_modules.length === 0 && Object.keys(js_exports).length === 0) {\n", + " run_callbacks();\n", + " return null;\n", + " }\n", + " if (!reloading) {\n", + " console.debug(\"Bokeh: BokehJS not loaded, scheduling load and callback at\", now());\n", + " }\n", + "\n", + " function on_load() {\n", + " root._bokeh_is_loading--;\n", + " if (root._bokeh_is_loading === 0) {\n", + " console.debug(\"Bokeh: all BokehJS libraries/stylesheets loaded\");\n", + " run_callbacks()\n", + " }\n", + " }\n", + " window._bokeh_on_load = on_load\n", + "\n", + " function on_error() {\n", + " console.error(\"failed to load \" + url);\n", + " }\n", + "\n", + " var skip = [];\n", + " if (window.requirejs) {\n", + " window.requirejs.config({'packages': {}, 'paths': {'jspanel': 'https://cdn.jsdelivr.net/npm/jspanel4@4.12.0/dist/jspanel', 'jspanel-modal': 'https://cdn.jsdelivr.net/npm/jspanel4@4.12.0/dist/extensions/modal/jspanel.modal', 'jspanel-tooltip': 'https://cdn.jsdelivr.net/npm/jspanel4@4.12.0/dist/extensions/tooltip/jspanel.tooltip', 'jspanel-hint': 'https://cdn.jsdelivr.net/npm/jspanel4@4.12.0/dist/extensions/hint/jspanel.hint', 'jspanel-layout': 'https://cdn.jsdelivr.net/npm/jspanel4@4.12.0/dist/extensions/layout/jspanel.layout', 'jspanel-contextmenu': 'https://cdn.jsdelivr.net/npm/jspanel4@4.12.0/dist/extensions/contextmenu/jspanel.contextmenu', 'jspanel-dock': 'https://cdn.jsdelivr.net/npm/jspanel4@4.12.0/dist/extensions/dock/jspanel.dock', 'gridstack': 'https://cdn.jsdelivr.net/npm/gridstack@7.2.3/dist/gridstack-all', 'notyf': 'https://cdn.jsdelivr.net/npm/notyf@3/notyf.min'}, 'shim': {'jspanel': {'exports': 'jsPanel'}, 'gridstack': {'exports': 'GridStack'}}});\n", + " require([\"jspanel\"], function(jsPanel) {\n", + "\twindow.jsPanel = jsPanel\n", + "\ton_load()\n", + " })\n", + " require([\"jspanel-modal\"], function() {\n", + "\ton_load()\n", + " })\n", + " require([\"jspanel-tooltip\"], function() {\n", + "\ton_load()\n", + " })\n", + " require([\"jspanel-hint\"], function() {\n", + "\ton_load()\n", + " })\n", + " require([\"jspanel-layout\"], function() {\n", + "\ton_load()\n", + " })\n", + " require([\"jspanel-contextmenu\"], function() {\n", + "\ton_load()\n", + " })\n", + " require([\"jspanel-dock\"], function() {\n", + "\ton_load()\n", + " })\n", + " require([\"gridstack\"], function(GridStack) {\n", + "\twindow.GridStack = GridStack\n", + "\ton_load()\n", + " })\n", + " require([\"notyf\"], function() {\n", + "\ton_load()\n", + " })\n", + " root._bokeh_is_loading = css_urls.length + 9;\n", + " } else {\n", + " root._bokeh_is_loading = css_urls.length + js_urls.length + js_modules.length + Object.keys(js_exports).length;\n", + " }\n", + "\n", + " var existing_stylesheets = []\n", + " var links = document.getElementsByTagName('link')\n", + " for (var i = 0; i < links.length; i++) {\n", + " var link = links[i]\n", + " if (link.href != null) {\n", + "\texisting_stylesheets.push(link.href)\n", + " }\n", + " }\n", + " for (var i = 0; i < css_urls.length; i++) {\n", + " var url = css_urls[i];\n", + " if (existing_stylesheets.indexOf(url) !== -1) {\n", + "\ton_load()\n", + "\tcontinue;\n", + " }\n", + " const element = document.createElement(\"link\");\n", + " element.onload = on_load;\n", + " element.onerror = on_error;\n", + " element.rel = \"stylesheet\";\n", + " element.type = \"text/css\";\n", + " element.href = url;\n", + " console.debug(\"Bokeh: injecting link tag for BokehJS stylesheet: \", url);\n", + " document.body.appendChild(element);\n", + " } if (((window['jsPanel'] !== undefined) && (!(window['jsPanel'] instanceof HTMLElement))) || window.requirejs) {\n", + " var urls = ['https://cdn.holoviz.org/panel/1.2.3/dist/bundled/floatpanel/jspanel4@4.12.0/dist/jspanel.js', 'https://cdn.holoviz.org/panel/1.2.3/dist/bundled/floatpanel/jspanel4@4.12.0/dist/extensions/modal/jspanel.modal.js', 'https://cdn.holoviz.org/panel/1.2.3/dist/bundled/floatpanel/jspanel4@4.12.0/dist/extensions/tooltip/jspanel.tooltip.js', 'https://cdn.holoviz.org/panel/1.2.3/dist/bundled/floatpanel/jspanel4@4.12.0/dist/extensions/hint/jspanel.hint.js', 'https://cdn.holoviz.org/panel/1.2.3/dist/bundled/floatpanel/jspanel4@4.12.0/dist/extensions/layout/jspanel.layout.js', 'https://cdn.holoviz.org/panel/1.2.3/dist/bundled/floatpanel/jspanel4@4.12.0/dist/extensions/contextmenu/jspanel.contextmenu.js', 'https://cdn.holoviz.org/panel/1.2.3/dist/bundled/floatpanel/jspanel4@4.12.0/dist/extensions/dock/jspanel.dock.js'];\n", + " for (var i = 0; i < urls.length; i++) {\n", + " skip.push(urls[i])\n", + " }\n", + " } if (((window['GridStack'] !== undefined) && (!(window['GridStack'] instanceof HTMLElement))) || window.requirejs) {\n", + " var urls = ['https://cdn.holoviz.org/panel/1.2.3/dist/bundled/gridstack/gridstack@7.2.3/dist/gridstack-all.js'];\n", + " for (var i = 0; i < urls.length; i++) {\n", + " skip.push(urls[i])\n", + " }\n", + " } if (((window['Notyf'] !== undefined) && (!(window['Notyf'] instanceof HTMLElement))) || window.requirejs) {\n", + " var urls = ['https://cdn.holoviz.org/panel/1.2.3/dist/bundled/notificationarea/notyf@3/notyf.min.js'];\n", + " for (var i = 0; i < urls.length; i++) {\n", + " skip.push(urls[i])\n", + " }\n", + " } var existing_scripts = []\n", + " var scripts = document.getElementsByTagName('script')\n", + " for (var i = 0; i < scripts.length; i++) {\n", + " var script = scripts[i]\n", + " if (script.src != null) {\n", + "\texisting_scripts.push(script.src)\n", + " }\n", + " }\n", + " for (var i = 0; i < js_urls.length; i++) {\n", + " var url = js_urls[i];\n", + " if (skip.indexOf(url) !== -1 || existing_scripts.indexOf(url) !== -1) {\n", + "\tif (!window.requirejs) {\n", + "\t on_load();\n", + "\t}\n", + "\tcontinue;\n", + " }\n", + " var element = document.createElement('script');\n", + " element.onload = on_load;\n", + " element.onerror = on_error;\n", + " element.async = false;\n", + " element.src = url;\n", + " console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n", + " document.head.appendChild(element);\n", + " }\n", + " for (var i = 0; i < js_modules.length; i++) {\n", + " var url = js_modules[i];\n", + " if (skip.indexOf(url) !== -1 || existing_scripts.indexOf(url) !== -1) {\n", + "\tif (!window.requirejs) {\n", + "\t on_load();\n", + "\t}\n", + "\tcontinue;\n", + " }\n", + " var element = document.createElement('script');\n", + " element.onload = on_load;\n", + " element.onerror = on_error;\n", + " element.async = false;\n", + " element.src = url;\n", + " element.type = \"module\";\n", + " console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n", + " document.head.appendChild(element);\n", + " }\n", + " for (const name in js_exports) {\n", + " var url = js_exports[name];\n", + " if (skip.indexOf(url) >= 0 || root[name] != null) {\n", + "\tif (!window.requirejs) {\n", + "\t on_load();\n", + "\t}\n", + "\tcontinue;\n", + " }\n", + " var element = document.createElement('script');\n", + " element.onerror = on_error;\n", + " element.async = false;\n", + " element.type = \"module\";\n", + " console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n", + " element.textContent = `\n", + " import ${name} from \"${url}\"\n", + " window.${name} = ${name}\n", + " window._bokeh_on_load()\n", + " `\n", + " document.head.appendChild(element);\n", + " }\n", + " if (!js_urls.length && !js_modules.length) {\n", + " on_load()\n", + " }\n", + " };\n", + "\n", + " function inject_raw_css(css) {\n", + " const element = document.createElement(\"style\");\n", + " element.appendChild(document.createTextNode(css));\n", + " document.body.appendChild(element);\n", + " }\n", + "\n", + " var js_urls = [\"https://cdn.bokeh.org/bokeh/release/bokeh-3.2.2.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-gl-3.2.2.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-widgets-3.2.2.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-tables-3.2.2.min.js\", \"https://cdn.holoviz.org/panel/1.2.3/dist/panel.min.js\", \"https://cdn.jsdelivr.net/npm/@holoviz/geoviews@1.11.0/dist/geoviews.min.js\"];\n", + " var js_modules = [];\n", + " var js_exports = {};\n", + " var css_urls = [];\n", + " var inline_js = [ function(Bokeh) {\n", + " Bokeh.set_log_level(\"info\");\n", + " },\n", + "function(Bokeh) {} // ensure no trailing comma for IE\n", + " ];\n", + "\n", + " function run_inline_js() {\n", + " if ((root.Bokeh !== undefined) || (force === true)) {\n", + " for (var i = 0; i < inline_js.length; i++) {\n", + " inline_js[i].call(root, root.Bokeh);\n", + " }\n", + " // Cache old bokeh versions\n", + " if (Bokeh != undefined && !reloading) {\n", + "\tvar NewBokeh = root.Bokeh;\n", + "\tif (Bokeh.versions === undefined) {\n", + "\t Bokeh.versions = new Map();\n", + "\t}\n", + "\tif (NewBokeh.version !== Bokeh.version) {\n", + "\t Bokeh.versions.set(NewBokeh.version, NewBokeh)\n", + "\t}\n", + "\troot.Bokeh = Bokeh;\n", + " }} else if (Date.now() < root._bokeh_timeout) {\n", + " setTimeout(run_inline_js, 100);\n", + " } else if (!root._bokeh_failed_load) {\n", + " console.log(\"Bokeh: BokehJS failed to load within specified timeout.\");\n", + " root._bokeh_failed_load = true;\n", + " }\n", + " root._bokeh_is_initializing = false\n", + " }\n", + "\n", + " function load_or_wait() {\n", + " // Implement a backoff loop that tries to ensure we do not load multiple\n", + " // versions of Bokeh and its dependencies at the same time.\n", + " // In recent versions we use the root._bokeh_is_initializing flag\n", + " // to determine whether there is an ongoing attempt to initialize\n", + " // bokeh, however for backward compatibility we also try to ensure\n", + " // that we do not start loading a newer (Panel>=1.0 and Bokeh>3) version\n", + " // before older versions are fully initialized.\n", + " if (root._bokeh_is_initializing && Date.now() > root._bokeh_timeout) {\n", + " root._bokeh_is_initializing = false;\n", + " root._bokeh_onload_callbacks = undefined;\n", + " console.log(\"Bokeh: BokehJS was loaded multiple times but one version failed to initialize.\");\n", + " load_or_wait();\n", + " } else if (root._bokeh_is_initializing || (typeof root._bokeh_is_initializing === \"undefined\" && root._bokeh_onload_callbacks !== undefined)) {\n", + " setTimeout(load_or_wait, 100);\n", + " } else {\n", + " Bokeh = root.Bokeh;\n", + " bokeh_loaded = Bokeh != null && (Bokeh.version === py_version || (Bokeh.versions !== undefined && Bokeh.versions.has(py_version)));\n", + " root._bokeh_is_initializing = true\n", + " root._bokeh_onload_callbacks = []\n", + " if (!reloading && (!bokeh_loaded || is_dev)) {\n", + "\troot.Bokeh = undefined;\n", + " }\n", + " load_libs(css_urls, js_urls, js_modules, js_exports, function() {\n", + "\tconsole.debug(\"Bokeh: BokehJS plotting callback run at\", now());\n", + "\trun_inline_js();\n", + " });\n", + " }\n", + " }\n", + " // Give older versions of the autoload script a head-start to ensure\n", + " // they initialize before we start loading newer version.\n", + " setTimeout(load_or_wait, 100)\n", + "}(window));" + ], + "application/vnd.holoviews_load.v0+json": "(function(root) {\n function now() {\n return new Date();\n }\n\n var force = true;\n var py_version = '3.2.2'.replace('rc', '-rc.').replace('.dev', '-dev.');\n var is_dev = py_version.indexOf(\"+\") !== -1 || py_version.indexOf(\"-\") !== -1;\n var reloading = false;\n var Bokeh = root.Bokeh;\n var bokeh_loaded = Bokeh != null && (Bokeh.version === py_version || (Bokeh.versions !== undefined && Bokeh.versions.has(py_version)));\n\n if (typeof (root._bokeh_timeout) === \"undefined\" || force) {\n root._bokeh_timeout = Date.now() + 5000;\n root._bokeh_failed_load = false;\n }\n\n function run_callbacks() {\n try {\n root._bokeh_onload_callbacks.forEach(function(callback) {\n if (callback != null)\n callback();\n });\n } finally {\n delete root._bokeh_onload_callbacks;\n }\n console.debug(\"Bokeh: all callbacks have finished\");\n }\n\n function load_libs(css_urls, js_urls, js_modules, js_exports, callback) {\n if (css_urls == null) css_urls = [];\n if (js_urls == null) js_urls = [];\n if (js_modules == null) js_modules = [];\n if (js_exports == null) js_exports = {};\n\n root._bokeh_onload_callbacks.push(callback);\n\n if (root._bokeh_is_loading > 0) {\n console.debug(\"Bokeh: BokehJS is being loaded, scheduling callback at\", now());\n return null;\n }\n if (js_urls.length === 0 && js_modules.length === 0 && Object.keys(js_exports).length === 0) {\n run_callbacks();\n return null;\n }\n if (!reloading) {\n console.debug(\"Bokeh: BokehJS not loaded, scheduling load and callback at\", now());\n }\n\n function on_load() {\n root._bokeh_is_loading--;\n if (root._bokeh_is_loading === 0) {\n console.debug(\"Bokeh: all BokehJS libraries/stylesheets loaded\");\n run_callbacks()\n }\n }\n window._bokeh_on_load = on_load\n\n function on_error() {\n console.error(\"failed to load \" + url);\n }\n\n var skip = [];\n if (window.requirejs) {\n window.requirejs.config({'packages': {}, 'paths': {'jspanel': 'https://cdn.jsdelivr.net/npm/jspanel4@4.12.0/dist/jspanel', 'jspanel-modal': 'https://cdn.jsdelivr.net/npm/jspanel4@4.12.0/dist/extensions/modal/jspanel.modal', 'jspanel-tooltip': 'https://cdn.jsdelivr.net/npm/jspanel4@4.12.0/dist/extensions/tooltip/jspanel.tooltip', 'jspanel-hint': 'https://cdn.jsdelivr.net/npm/jspanel4@4.12.0/dist/extensions/hint/jspanel.hint', 'jspanel-layout': 'https://cdn.jsdelivr.net/npm/jspanel4@4.12.0/dist/extensions/layout/jspanel.layout', 'jspanel-contextmenu': 'https://cdn.jsdelivr.net/npm/jspanel4@4.12.0/dist/extensions/contextmenu/jspanel.contextmenu', 'jspanel-dock': 'https://cdn.jsdelivr.net/npm/jspanel4@4.12.0/dist/extensions/dock/jspanel.dock', 'gridstack': 'https://cdn.jsdelivr.net/npm/gridstack@7.2.3/dist/gridstack-all', 'notyf': 'https://cdn.jsdelivr.net/npm/notyf@3/notyf.min'}, 'shim': {'jspanel': {'exports': 'jsPanel'}, 'gridstack': {'exports': 'GridStack'}}});\n require([\"jspanel\"], function(jsPanel) {\n\twindow.jsPanel = jsPanel\n\ton_load()\n })\n require([\"jspanel-modal\"], function() {\n\ton_load()\n })\n require([\"jspanel-tooltip\"], function() {\n\ton_load()\n })\n require([\"jspanel-hint\"], function() {\n\ton_load()\n })\n require([\"jspanel-layout\"], function() {\n\ton_load()\n })\n require([\"jspanel-contextmenu\"], function() {\n\ton_load()\n })\n require([\"jspanel-dock\"], function() {\n\ton_load()\n })\n require([\"gridstack\"], function(GridStack) {\n\twindow.GridStack = GridStack\n\ton_load()\n })\n require([\"notyf\"], function() {\n\ton_load()\n })\n root._bokeh_is_loading = css_urls.length + 9;\n } else {\n root._bokeh_is_loading = css_urls.length + js_urls.length + js_modules.length + Object.keys(js_exports).length;\n }\n\n var existing_stylesheets = []\n var links = document.getElementsByTagName('link')\n for (var i = 0; i < links.length; i++) {\n var link = links[i]\n if (link.href != null) {\n\texisting_stylesheets.push(link.href)\n }\n }\n for (var i = 0; i < css_urls.length; i++) {\n var url = css_urls[i];\n if (existing_stylesheets.indexOf(url) !== -1) {\n\ton_load()\n\tcontinue;\n }\n const element = document.createElement(\"link\");\n element.onload = on_load;\n element.onerror = on_error;\n element.rel = \"stylesheet\";\n element.type = \"text/css\";\n element.href = url;\n console.debug(\"Bokeh: injecting link tag for BokehJS stylesheet: \", url);\n document.body.appendChild(element);\n } if (((window['jsPanel'] !== undefined) && (!(window['jsPanel'] instanceof HTMLElement))) || window.requirejs) {\n var urls = ['https://cdn.holoviz.org/panel/1.2.3/dist/bundled/floatpanel/jspanel4@4.12.0/dist/jspanel.js', 'https://cdn.holoviz.org/panel/1.2.3/dist/bundled/floatpanel/jspanel4@4.12.0/dist/extensions/modal/jspanel.modal.js', 'https://cdn.holoviz.org/panel/1.2.3/dist/bundled/floatpanel/jspanel4@4.12.0/dist/extensions/tooltip/jspanel.tooltip.js', 'https://cdn.holoviz.org/panel/1.2.3/dist/bundled/floatpanel/jspanel4@4.12.0/dist/extensions/hint/jspanel.hint.js', 'https://cdn.holoviz.org/panel/1.2.3/dist/bundled/floatpanel/jspanel4@4.12.0/dist/extensions/layout/jspanel.layout.js', 'https://cdn.holoviz.org/panel/1.2.3/dist/bundled/floatpanel/jspanel4@4.12.0/dist/extensions/contextmenu/jspanel.contextmenu.js', 'https://cdn.holoviz.org/panel/1.2.3/dist/bundled/floatpanel/jspanel4@4.12.0/dist/extensions/dock/jspanel.dock.js'];\n for (var i = 0; i < urls.length; i++) {\n skip.push(urls[i])\n }\n } if (((window['GridStack'] !== undefined) && (!(window['GridStack'] instanceof HTMLElement))) || window.requirejs) {\n var urls = ['https://cdn.holoviz.org/panel/1.2.3/dist/bundled/gridstack/gridstack@7.2.3/dist/gridstack-all.js'];\n for (var i = 0; i < urls.length; i++) {\n skip.push(urls[i])\n }\n } if (((window['Notyf'] !== undefined) && (!(window['Notyf'] instanceof HTMLElement))) || window.requirejs) {\n var urls = ['https://cdn.holoviz.org/panel/1.2.3/dist/bundled/notificationarea/notyf@3/notyf.min.js'];\n for (var i = 0; i < urls.length; i++) {\n skip.push(urls[i])\n }\n } var existing_scripts = []\n var scripts = document.getElementsByTagName('script')\n for (var i = 0; i < scripts.length; i++) {\n var script = scripts[i]\n if (script.src != null) {\n\texisting_scripts.push(script.src)\n }\n }\n for (var i = 0; i < js_urls.length; i++) {\n var url = js_urls[i];\n if (skip.indexOf(url) !== -1 || existing_scripts.indexOf(url) !== -1) {\n\tif (!window.requirejs) {\n\t on_load();\n\t}\n\tcontinue;\n }\n var element = document.createElement('script');\n element.onload = on_load;\n element.onerror = on_error;\n element.async = false;\n element.src = url;\n console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n document.head.appendChild(element);\n }\n for (var i = 0; i < js_modules.length; i++) {\n var url = js_modules[i];\n if (skip.indexOf(url) !== -1 || existing_scripts.indexOf(url) !== -1) {\n\tif (!window.requirejs) {\n\t on_load();\n\t}\n\tcontinue;\n }\n var element = document.createElement('script');\n element.onload = on_load;\n element.onerror = on_error;\n element.async = false;\n element.src = url;\n element.type = \"module\";\n console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n document.head.appendChild(element);\n }\n for (const name in js_exports) {\n var url = js_exports[name];\n if (skip.indexOf(url) >= 0 || root[name] != null) {\n\tif (!window.requirejs) {\n\t on_load();\n\t}\n\tcontinue;\n }\n var element = document.createElement('script');\n element.onerror = on_error;\n element.async = false;\n element.type = \"module\";\n console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n element.textContent = `\n import ${name} from \"${url}\"\n window.${name} = ${name}\n window._bokeh_on_load()\n `\n document.head.appendChild(element);\n }\n if (!js_urls.length && !js_modules.length) {\n on_load()\n }\n };\n\n function inject_raw_css(css) {\n const element = document.createElement(\"style\");\n element.appendChild(document.createTextNode(css));\n document.body.appendChild(element);\n }\n\n var js_urls = [\"https://cdn.bokeh.org/bokeh/release/bokeh-3.2.2.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-gl-3.2.2.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-widgets-3.2.2.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-tables-3.2.2.min.js\", \"https://cdn.holoviz.org/panel/1.2.3/dist/panel.min.js\", \"https://cdn.jsdelivr.net/npm/@holoviz/geoviews@1.11.0/dist/geoviews.min.js\"];\n var js_modules = [];\n var js_exports = {};\n var css_urls = [];\n var inline_js = [ function(Bokeh) {\n Bokeh.set_log_level(\"info\");\n },\nfunction(Bokeh) {} // ensure no trailing comma for IE\n ];\n\n function run_inline_js() {\n if ((root.Bokeh !== undefined) || (force === true)) {\n for (var i = 0; i < inline_js.length; i++) {\n inline_js[i].call(root, root.Bokeh);\n }\n // Cache old bokeh versions\n if (Bokeh != undefined && !reloading) {\n\tvar NewBokeh = root.Bokeh;\n\tif (Bokeh.versions === undefined) {\n\t Bokeh.versions = new Map();\n\t}\n\tif (NewBokeh.version !== Bokeh.version) {\n\t Bokeh.versions.set(NewBokeh.version, NewBokeh)\n\t}\n\troot.Bokeh = Bokeh;\n }} else if (Date.now() < root._bokeh_timeout) {\n setTimeout(run_inline_js, 100);\n } else if (!root._bokeh_failed_load) {\n console.log(\"Bokeh: BokehJS failed to load within specified timeout.\");\n root._bokeh_failed_load = true;\n }\n root._bokeh_is_initializing = false\n }\n\n function load_or_wait() {\n // Implement a backoff loop that tries to ensure we do not load multiple\n // versions of Bokeh and its dependencies at the same time.\n // In recent versions we use the root._bokeh_is_initializing flag\n // to determine whether there is an ongoing attempt to initialize\n // bokeh, however for backward compatibility we also try to ensure\n // that we do not start loading a newer (Panel>=1.0 and Bokeh>3) version\n // before older versions are fully initialized.\n if (root._bokeh_is_initializing && Date.now() > root._bokeh_timeout) {\n root._bokeh_is_initializing = false;\n root._bokeh_onload_callbacks = undefined;\n console.log(\"Bokeh: BokehJS was loaded multiple times but one version failed to initialize.\");\n load_or_wait();\n } else if (root._bokeh_is_initializing || (typeof root._bokeh_is_initializing === \"undefined\" && root._bokeh_onload_callbacks !== undefined)) {\n setTimeout(load_or_wait, 100);\n } else {\n Bokeh = root.Bokeh;\n bokeh_loaded = Bokeh != null && (Bokeh.version === py_version || (Bokeh.versions !== undefined && Bokeh.versions.has(py_version)));\n root._bokeh_is_initializing = true\n root._bokeh_onload_callbacks = []\n if (!reloading && (!bokeh_loaded || is_dev)) {\n\troot.Bokeh = undefined;\n }\n load_libs(css_urls, js_urls, js_modules, js_exports, function() {\n\tconsole.debug(\"Bokeh: BokehJS plotting callback run at\", now());\n\trun_inline_js();\n });\n }\n }\n // Give older versions of the autoload script a head-start to ensure\n // they initialize before we start loading newer version.\n setTimeout(load_or_wait, 100)\n}(window));" + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/javascript": [ + "\n", + "if ((window.PyViz === undefined) || (window.PyViz instanceof HTMLElement)) {\n", + " window.PyViz = {comms: {}, comm_status:{}, kernels:{}, receivers: {}, plot_index: []}\n", + "}\n", + "\n", + "\n", + " function JupyterCommManager() {\n", + " }\n", + "\n", + " JupyterCommManager.prototype.register_target = function(plot_id, comm_id, msg_handler) {\n", + " if (window.comm_manager || ((window.Jupyter !== undefined) && (Jupyter.notebook.kernel != null))) {\n", + " var comm_manager = window.comm_manager || Jupyter.notebook.kernel.comm_manager;\n", + " comm_manager.register_target(comm_id, function(comm) {\n", + " comm.on_msg(msg_handler);\n", + " });\n", + " } else if ((plot_id in window.PyViz.kernels) && (window.PyViz.kernels[plot_id])) {\n", + " window.PyViz.kernels[plot_id].registerCommTarget(comm_id, function(comm) {\n", + " comm.onMsg = msg_handler;\n", + " });\n", + " } else if (typeof google != 'undefined' && google.colab.kernel != null) {\n", + " google.colab.kernel.comms.registerTarget(comm_id, (comm) => {\n", + " var messages = comm.messages[Symbol.asyncIterator]();\n", + " function processIteratorResult(result) {\n", + " var message = result.value;\n", + " console.log(message)\n", + " var content = {data: message.data, comm_id};\n", + " var buffers = []\n", + " for (var buffer of message.buffers || []) {\n", + " buffers.push(new DataView(buffer))\n", + " }\n", + " var metadata = message.metadata || {};\n", + " var msg = {content, buffers, metadata}\n", + " msg_handler(msg);\n", + " return messages.next().then(processIteratorResult);\n", + " }\n", + " return messages.next().then(processIteratorResult);\n", + " })\n", + " }\n", + " }\n", + "\n", + " JupyterCommManager.prototype.get_client_comm = function(plot_id, comm_id, msg_handler) {\n", + " if (comm_id in window.PyViz.comms) {\n", + " return window.PyViz.comms[comm_id];\n", + " } else if (window.comm_manager || ((window.Jupyter !== undefined) && (Jupyter.notebook.kernel != null))) {\n", + " var comm_manager = window.comm_manager || Jupyter.notebook.kernel.comm_manager;\n", + " var comm = comm_manager.new_comm(comm_id, {}, {}, {}, comm_id);\n", + " if (msg_handler) {\n", + " comm.on_msg(msg_handler);\n", + " }\n", + " } else if ((plot_id in window.PyViz.kernels) && (window.PyViz.kernels[plot_id])) {\n", + " var comm = window.PyViz.kernels[plot_id].connectToComm(comm_id);\n", + " comm.open();\n", + " if (msg_handler) {\n", + " comm.onMsg = msg_handler;\n", + " }\n", + " } else if (typeof google != 'undefined' && google.colab.kernel != null) {\n", + " var comm_promise = google.colab.kernel.comms.open(comm_id)\n", + " comm_promise.then((comm) => {\n", + " window.PyViz.comms[comm_id] = comm;\n", + " if (msg_handler) {\n", + " var messages = comm.messages[Symbol.asyncIterator]();\n", + " function processIteratorResult(result) {\n", + " var message = result.value;\n", + " var content = {data: message.data};\n", + " var metadata = message.metadata || {comm_id};\n", + " var msg = {content, metadata}\n", + " msg_handler(msg);\n", + " return messages.next().then(processIteratorResult);\n", + " }\n", + " return messages.next().then(processIteratorResult);\n", + " }\n", + " }) \n", + " var sendClosure = (data, metadata, buffers, disposeOnDone) => {\n", + " return comm_promise.then((comm) => {\n", + " comm.send(data, metadata, buffers, disposeOnDone);\n", + " });\n", + " };\n", + " var comm = {\n", + " send: sendClosure\n", + " };\n", + " }\n", + " window.PyViz.comms[comm_id] = comm;\n", + " return comm;\n", + " }\n", + " window.PyViz.comm_manager = new JupyterCommManager();\n", + " \n", + "\n", + "\n", + "var JS_MIME_TYPE = 'application/javascript';\n", + "var HTML_MIME_TYPE = 'text/html';\n", + "var EXEC_MIME_TYPE = 'application/vnd.holoviews_exec.v0+json';\n", + "var CLASS_NAME = 'output';\n", + "\n", + "/**\n", + " * Render data to the DOM node\n", + " */\n", + "function render(props, node) {\n", + " var div = document.createElement(\"div\");\n", + " var script = document.createElement(\"script\");\n", + " node.appendChild(div);\n", + " node.appendChild(script);\n", + "}\n", + "\n", + "/**\n", + " * Handle when a new output is added\n", + " */\n", + "function handle_add_output(event, handle) {\n", + " var output_area = handle.output_area;\n", + " var output = handle.output;\n", + " if ((output.data == undefined) || (!output.data.hasOwnProperty(EXEC_MIME_TYPE))) {\n", + " return\n", + " }\n", + " var id = output.metadata[EXEC_MIME_TYPE][\"id\"];\n", + " var toinsert = output_area.element.find(\".\" + CLASS_NAME.split(' ')[0]);\n", + " if (id !== undefined) {\n", + " var nchildren = toinsert.length;\n", + " var html_node = toinsert[nchildren-1].children[0];\n", + " html_node.innerHTML = output.data[HTML_MIME_TYPE];\n", + " var scripts = [];\n", + " var nodelist = html_node.querySelectorAll(\"script\");\n", + " for (var i in nodelist) {\n", + " if (nodelist.hasOwnProperty(i)) {\n", + " scripts.push(nodelist[i])\n", + " }\n", + " }\n", + "\n", + " scripts.forEach( function (oldScript) {\n", + " var newScript = document.createElement(\"script\");\n", + " var attrs = [];\n", + " var nodemap = oldScript.attributes;\n", + " for (var j in nodemap) {\n", + " if (nodemap.hasOwnProperty(j)) {\n", + " attrs.push(nodemap[j])\n", + " }\n", + " }\n", + " attrs.forEach(function(attr) { newScript.setAttribute(attr.name, attr.value) });\n", + " newScript.appendChild(document.createTextNode(oldScript.innerHTML));\n", + " oldScript.parentNode.replaceChild(newScript, oldScript);\n", + " });\n", + " if (JS_MIME_TYPE in output.data) {\n", + " toinsert[nchildren-1].children[1].textContent = output.data[JS_MIME_TYPE];\n", + " }\n", + " output_area._hv_plot_id = id;\n", + " if ((window.Bokeh !== undefined) && (id in Bokeh.index)) {\n", + " window.PyViz.plot_index[id] = Bokeh.index[id];\n", + " } else {\n", + " window.PyViz.plot_index[id] = null;\n", + " }\n", + " } else if (output.metadata[EXEC_MIME_TYPE][\"server_id\"] !== undefined) {\n", + " var bk_div = document.createElement(\"div\");\n", + " bk_div.innerHTML = output.data[HTML_MIME_TYPE];\n", + " var script_attrs = bk_div.children[0].attributes;\n", + " for (var i = 0; i < script_attrs.length; i++) {\n", + " toinsert[toinsert.length - 1].childNodes[1].setAttribute(script_attrs[i].name, script_attrs[i].value);\n", + " }\n", + " // store reference to server id on output_area\n", + " output_area._bokeh_server_id = output.metadata[EXEC_MIME_TYPE][\"server_id\"];\n", + " }\n", + "}\n", + "\n", + "/**\n", + " * Handle when an output is cleared or removed\n", + " */\n", + "function handle_clear_output(event, handle) {\n", + " var id = handle.cell.output_area._hv_plot_id;\n", + " var server_id = handle.cell.output_area._bokeh_server_id;\n", + " if (((id === undefined) || !(id in PyViz.plot_index)) && (server_id !== undefined)) { return; }\n", + " var comm = window.PyViz.comm_manager.get_client_comm(\"hv-extension-comm\", \"hv-extension-comm\", function () {});\n", + " if (server_id !== null) {\n", + " comm.send({event_type: 'server_delete', 'id': server_id});\n", + " return;\n", + " } else if (comm !== null) {\n", + " comm.send({event_type: 'delete', 'id': id});\n", + " }\n", + " delete PyViz.plot_index[id];\n", + " if ((window.Bokeh !== undefined) & (id in window.Bokeh.index)) {\n", + " var doc = window.Bokeh.index[id].model.document\n", + " doc.clear();\n", + " const i = window.Bokeh.documents.indexOf(doc);\n", + " if (i > -1) {\n", + " window.Bokeh.documents.splice(i, 1);\n", + " }\n", + " }\n", + "}\n", + "\n", + "/**\n", + " * Handle kernel restart event\n", + " */\n", + "function handle_kernel_cleanup(event, handle) {\n", + " delete PyViz.comms[\"hv-extension-comm\"];\n", + " window.PyViz.plot_index = {}\n", + "}\n", + "\n", + "/**\n", + " * Handle update_display_data messages\n", + " */\n", + "function handle_update_output(event, handle) {\n", + " handle_clear_output(event, {cell: {output_area: handle.output_area}})\n", + " handle_add_output(event, handle)\n", + "}\n", + "\n", + "function register_renderer(events, OutputArea) {\n", + " function append_mime(data, metadata, element) {\n", + " // create a DOM node to render to\n", + " var toinsert = this.create_output_subarea(\n", + " metadata,\n", + " CLASS_NAME,\n", + " EXEC_MIME_TYPE\n", + " );\n", + " this.keyboard_manager.register_events(toinsert);\n", + " // Render to node\n", + " var props = {data: data, metadata: metadata[EXEC_MIME_TYPE]};\n", + " render(props, toinsert[0]);\n", + " element.append(toinsert);\n", + " return toinsert\n", + " }\n", + "\n", + " events.on('output_added.OutputArea', handle_add_output);\n", + " events.on('output_updated.OutputArea', handle_update_output);\n", + " events.on('clear_output.CodeCell', handle_clear_output);\n", + " events.on('delete.Cell', handle_clear_output);\n", + " events.on('kernel_ready.Kernel', handle_kernel_cleanup);\n", + "\n", + " OutputArea.prototype.register_mime_type(EXEC_MIME_TYPE, append_mime, {\n", + " safe: true,\n", + " index: 0\n", + " });\n", + "}\n", + "\n", + "if (window.Jupyter !== undefined) {\n", + " try {\n", + " var events = require('base/js/events');\n", + " var OutputArea = require('notebook/js/outputarea').OutputArea;\n", + " if (OutputArea.prototype.mime_types().indexOf(EXEC_MIME_TYPE) == -1) {\n", + " register_renderer(events, OutputArea);\n", + " }\n", + " } catch(err) {\n", + " }\n", + "}\n" + ], + "application/vnd.holoviews_load.v0+json": "\nif ((window.PyViz === undefined) || (window.PyViz instanceof HTMLElement)) {\n window.PyViz = {comms: {}, comm_status:{}, kernels:{}, receivers: {}, plot_index: []}\n}\n\n\n function JupyterCommManager() {\n }\n\n JupyterCommManager.prototype.register_target = function(plot_id, comm_id, msg_handler) {\n if (window.comm_manager || ((window.Jupyter !== undefined) && (Jupyter.notebook.kernel != null))) {\n var comm_manager = window.comm_manager || Jupyter.notebook.kernel.comm_manager;\n comm_manager.register_target(comm_id, function(comm) {\n comm.on_msg(msg_handler);\n });\n } else if ((plot_id in window.PyViz.kernels) && (window.PyViz.kernels[plot_id])) {\n window.PyViz.kernels[plot_id].registerCommTarget(comm_id, function(comm) {\n comm.onMsg = msg_handler;\n });\n } else if (typeof google != 'undefined' && google.colab.kernel != null) {\n google.colab.kernel.comms.registerTarget(comm_id, (comm) => {\n var messages = comm.messages[Symbol.asyncIterator]();\n function processIteratorResult(result) {\n var message = result.value;\n console.log(message)\n var content = {data: message.data, comm_id};\n var buffers = []\n for (var buffer of message.buffers || []) {\n buffers.push(new DataView(buffer))\n }\n var metadata = message.metadata || {};\n var msg = {content, buffers, metadata}\n msg_handler(msg);\n return messages.next().then(processIteratorResult);\n }\n return messages.next().then(processIteratorResult);\n })\n }\n }\n\n JupyterCommManager.prototype.get_client_comm = function(plot_id, comm_id, msg_handler) {\n if (comm_id in window.PyViz.comms) {\n return window.PyViz.comms[comm_id];\n } else if (window.comm_manager || ((window.Jupyter !== undefined) && (Jupyter.notebook.kernel != null))) {\n var comm_manager = window.comm_manager || Jupyter.notebook.kernel.comm_manager;\n var comm = comm_manager.new_comm(comm_id, {}, {}, {}, comm_id);\n if (msg_handler) {\n comm.on_msg(msg_handler);\n }\n } else if ((plot_id in window.PyViz.kernels) && (window.PyViz.kernels[plot_id])) {\n var comm = window.PyViz.kernels[plot_id].connectToComm(comm_id);\n comm.open();\n if (msg_handler) {\n comm.onMsg = msg_handler;\n }\n } else if (typeof google != 'undefined' && google.colab.kernel != null) {\n var comm_promise = google.colab.kernel.comms.open(comm_id)\n comm_promise.then((comm) => {\n window.PyViz.comms[comm_id] = comm;\n if (msg_handler) {\n var messages = comm.messages[Symbol.asyncIterator]();\n function processIteratorResult(result) {\n var message = result.value;\n var content = {data: message.data};\n var metadata = message.metadata || {comm_id};\n var msg = {content, metadata}\n msg_handler(msg);\n return messages.next().then(processIteratorResult);\n }\n return messages.next().then(processIteratorResult);\n }\n }) \n var sendClosure = (data, metadata, buffers, disposeOnDone) => {\n return comm_promise.then((comm) => {\n comm.send(data, metadata, buffers, disposeOnDone);\n });\n };\n var comm = {\n send: sendClosure\n };\n }\n window.PyViz.comms[comm_id] = comm;\n return comm;\n }\n window.PyViz.comm_manager = new JupyterCommManager();\n \n\n\nvar JS_MIME_TYPE = 'application/javascript';\nvar HTML_MIME_TYPE = 'text/html';\nvar EXEC_MIME_TYPE = 'application/vnd.holoviews_exec.v0+json';\nvar CLASS_NAME = 'output';\n\n/**\n * Render data to the DOM node\n */\nfunction render(props, node) {\n var div = document.createElement(\"div\");\n var script = document.createElement(\"script\");\n node.appendChild(div);\n node.appendChild(script);\n}\n\n/**\n * Handle when a new output is added\n */\nfunction handle_add_output(event, handle) {\n var output_area = handle.output_area;\n var output = handle.output;\n if ((output.data == undefined) || (!output.data.hasOwnProperty(EXEC_MIME_TYPE))) {\n return\n }\n var id = output.metadata[EXEC_MIME_TYPE][\"id\"];\n var toinsert = output_area.element.find(\".\" + CLASS_NAME.split(' ')[0]);\n if (id !== undefined) {\n var nchildren = toinsert.length;\n var html_node = toinsert[nchildren-1].children[0];\n html_node.innerHTML = output.data[HTML_MIME_TYPE];\n var scripts = [];\n var nodelist = html_node.querySelectorAll(\"script\");\n for (var i in nodelist) {\n if (nodelist.hasOwnProperty(i)) {\n scripts.push(nodelist[i])\n }\n }\n\n scripts.forEach( function (oldScript) {\n var newScript = document.createElement(\"script\");\n var attrs = [];\n var nodemap = oldScript.attributes;\n for (var j in nodemap) {\n if (nodemap.hasOwnProperty(j)) {\n attrs.push(nodemap[j])\n }\n }\n attrs.forEach(function(attr) { newScript.setAttribute(attr.name, attr.value) });\n newScript.appendChild(document.createTextNode(oldScript.innerHTML));\n oldScript.parentNode.replaceChild(newScript, oldScript);\n });\n if (JS_MIME_TYPE in output.data) {\n toinsert[nchildren-1].children[1].textContent = output.data[JS_MIME_TYPE];\n }\n output_area._hv_plot_id = id;\n if ((window.Bokeh !== undefined) && (id in Bokeh.index)) {\n window.PyViz.plot_index[id] = Bokeh.index[id];\n } else {\n window.PyViz.plot_index[id] = null;\n }\n } else if (output.metadata[EXEC_MIME_TYPE][\"server_id\"] !== undefined) {\n var bk_div = document.createElement(\"div\");\n bk_div.innerHTML = output.data[HTML_MIME_TYPE];\n var script_attrs = bk_div.children[0].attributes;\n for (var i = 0; i < script_attrs.length; i++) {\n toinsert[toinsert.length - 1].childNodes[1].setAttribute(script_attrs[i].name, script_attrs[i].value);\n }\n // store reference to server id on output_area\n output_area._bokeh_server_id = output.metadata[EXEC_MIME_TYPE][\"server_id\"];\n }\n}\n\n/**\n * Handle when an output is cleared or removed\n */\nfunction handle_clear_output(event, handle) {\n var id = handle.cell.output_area._hv_plot_id;\n var server_id = handle.cell.output_area._bokeh_server_id;\n if (((id === undefined) || !(id in PyViz.plot_index)) && (server_id !== undefined)) { return; }\n var comm = window.PyViz.comm_manager.get_client_comm(\"hv-extension-comm\", \"hv-extension-comm\", function () {});\n if (server_id !== null) {\n comm.send({event_type: 'server_delete', 'id': server_id});\n return;\n } else if (comm !== null) {\n comm.send({event_type: 'delete', 'id': id});\n }\n delete PyViz.plot_index[id];\n if ((window.Bokeh !== undefined) & (id in window.Bokeh.index)) {\n var doc = window.Bokeh.index[id].model.document\n doc.clear();\n const i = window.Bokeh.documents.indexOf(doc);\n if (i > -1) {\n window.Bokeh.documents.splice(i, 1);\n }\n }\n}\n\n/**\n * Handle kernel restart event\n */\nfunction handle_kernel_cleanup(event, handle) {\n delete PyViz.comms[\"hv-extension-comm\"];\n window.PyViz.plot_index = {}\n}\n\n/**\n * Handle update_display_data messages\n */\nfunction handle_update_output(event, handle) {\n handle_clear_output(event, {cell: {output_area: handle.output_area}})\n handle_add_output(event, handle)\n}\n\nfunction register_renderer(events, OutputArea) {\n function append_mime(data, metadata, element) {\n // create a DOM node to render to\n var toinsert = this.create_output_subarea(\n metadata,\n CLASS_NAME,\n EXEC_MIME_TYPE\n );\n this.keyboard_manager.register_events(toinsert);\n // Render to node\n var props = {data: data, metadata: metadata[EXEC_MIME_TYPE]};\n render(props, toinsert[0]);\n element.append(toinsert);\n return toinsert\n }\n\n events.on('output_added.OutputArea', handle_add_output);\n events.on('output_updated.OutputArea', handle_update_output);\n events.on('clear_output.CodeCell', handle_clear_output);\n events.on('delete.Cell', handle_clear_output);\n events.on('kernel_ready.Kernel', handle_kernel_cleanup);\n\n OutputArea.prototype.register_mime_type(EXEC_MIME_TYPE, append_mime, {\n safe: true,\n index: 0\n });\n}\n\nif (window.Jupyter !== undefined) {\n try {\n var events = require('base/js/events');\n var OutputArea = require('notebook/js/outputarea').OutputArea;\n if (OutputArea.prototype.mime_types().indexOf(EXEC_MIME_TYPE) == -1) {\n register_renderer(events, OutputArea);\n }\n } catch(err) {\n }\n}\n" + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "\n", + "
\n", + "\n", + "\n", + "\n", + " \n", + " \n", + "\n", + "\n", + " \n", + " \n", + "\n", + "\n", + "
\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ "import os\n", "import h5py\n", @@ -109,7 +809,9 @@ "import shapely\n", "import warnings\n", "from shapely.errors import ShapelyDeprecationWarning\n", - "warnings.filterwarnings(\"ignore\", category=ShapelyDeprecationWarning) " + "warnings.filterwarnings(\"ignore\", category=ShapelyDeprecationWarning) \n", + "\n", + "os.chdir('../../') " ] }, { @@ -122,32 +824,121 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'/home/jovyan/GEDI-Data-Resources'" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ - "inDir = os.getcwd().split(\"python\")[0] # Set input directory to the current working directory\n", - "os.chdir(inDir)" + "inDir = os.getcwd()\n", + "inDir" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "#### In this section, a GEDI .h5 file has been downloaded to the `inDir` defined above. You will need to download the file directly from the LP DAAC Data Pool in order to execute this tutorial.\n", - "### Direct Link to file: \n", - " - https://e4ftl01.cr.usgs.gov/GEDI/GEDI02_B.002/2019.06.19/GEDI02_B_2019170155833_O02932_02_T02267_02_003_01_V002.h5 \n", + "#### You will need to download the file in order to execute this tutorial. Make sure to download the file into the `data` directory defined above.\n", + "### Direct Link to file:\n", + " - https://data.lpdaac.earthdatacloud.nasa.gov/lp-prod-protected/GEDI02_B.002/GEDI02_B_2019170155833_O02932_02_T02267_02_003_01_V002/GEDI02_B_2019170155833_O02932_02_T02267_02_003_01_V002.h5\n", "\n", - "#### Make sure to download the files into the `inDir` directory defined above." + "Alternatively, you can use `earthaccess` package to download the data. " ] }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "3592cd57fc114b6ea65ae517fe097196", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "QUEUEING TASKS | : 0%| | 0/1 [00:00\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
BeamShot NumberLongitudeLatitudeQuality Flag
0BEAM011029320600200419869-142.75569226.9238960
1BEAM011029320600200419969-142.73656726.9432420
2BEAM011029320600200420069-142.71743326.9625690
3BEAM011029320600200420169-142.69829426.9818910
4BEAM011029320600200420269-142.67913627.0011870
..................
1066BEAM011029320600200526469-80.19845251.7968580
1067BEAM011029320600200526569-80.11467751.7970290
1068BEAM011029320600200526669-80.03217351.7971730
1069BEAM011029320600200526769-79.94857151.7972450
1070BEAM011029320600200526869-79.86569851.7972460
\n", + "

1071 rows × 5 columns

\n", + "" + ], + "text/plain": [ + " Beam Shot Number Longitude Latitude Quality Flag\n", + "0 BEAM0110 29320600200419869 -142.755692 26.923896 0\n", + "1 BEAM0110 29320600200419969 -142.736567 26.943242 0\n", + "2 BEAM0110 29320600200420069 -142.717433 26.962569 0\n", + "3 BEAM0110 29320600200420169 -142.698294 26.981891 0\n", + "4 BEAM0110 29320600200420269 -142.679136 27.001187 0\n", + "... ... ... ... ... ...\n", + "1066 BEAM0110 29320600200526469 -80.198452 51.796858 0\n", + "1067 BEAM0110 29320600200526569 -80.114677 51.797029 0\n", + "1068 BEAM0110 29320600200526669 -80.032173 51.797173 0\n", + "1069 BEAM0110 29320600200526769 -79.948571 51.797245 0\n", + "1070 BEAM0110 29320600200526869 -79.865698 51.797246 0\n", + "\n", + "[1071 rows x 5 columns]" + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "lonSample, latSample, shotSample, qualitySample, beamSample = [], [], [], [], [] # Set up lists to store data\n", "\n", @@ -416,7 +1485,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 18, "metadata": {}, "outputs": [], "source": [ @@ -433,7 +1502,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 19, "metadata": {}, "outputs": [], "source": [ @@ -450,9 +1519,31 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 20, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "0 POINT (-142.75569 26.92390)\n", + "1 POINT (-142.73657 26.94324)\n", + "2 POINT (-142.71743 26.96257)\n", + "3 POINT (-142.69829 26.98189)\n", + "4 POINT (-142.67914 27.00119)\n", + " ... \n", + "1066 POINT (-80.19845 51.79686)\n", + "1067 POINT (-80.11468 51.79703)\n", + "1068 POINT (-80.03217 51.79717)\n", + "1069 POINT (-79.94857 51.79725)\n", + "1070 POINT (-79.86570 51.79725)\n", + "Name: geometry, Length: 1071, dtype: geometry" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "# Convert to a Geodataframe\n", "latslons = gp.GeoDataFrame(latslons)\n", @@ -469,11 +1560,25 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 21, "metadata": { "scrolled": true }, - "outputs": [], + "outputs": [ + { + "data": { + "image/svg+xml": [ + "" + ], + "text/plain": [ + "" + ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "latslons['geometry'][0]" ] @@ -488,7 +1593,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 22, "metadata": {}, "outputs": [], "source": [ @@ -508,27 +1613,108 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 23, "metadata": {}, "outputs": [], "source": [ - "redwoodNP = gp.GeoDataFrame.from_file('Data\\\\RedwoodNP.geojson') # Import GeoJSON as GeoDataFrame" + "redwoodNP = gp.GeoDataFrame.from_file('data/RedwoodNP.geojson') # Import GeoJSON as GeoDataFrame" ] }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 24, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
GIS_LOC_IDUNIT_CODEGROUP_CODEUNIT_NAMEUNIT_TYPEMETA_MIDFLANDS_CODEDATE_EDITGIS_NOTESgeometry
0NoneREDWNoneRedwoodNational ParkNoneNoneNoneShifted 0.06 milesMULTIPOLYGON (((-124.01829 41.44539, -124.0184...
\n", + "
" + ], + "text/plain": [ + " GIS_LOC_ID UNIT_CODE GROUP_CODE UNIT_NAME UNIT_TYPE META_MIDF \\\n", + "0 None REDW None Redwood National Park None \n", + "\n", + " LANDS_CODE DATE_EDIT GIS_NOTES \\\n", + "0 None None Shifted 0.06 miles \n", + "\n", + " geometry \n", + "0 MULTIPOLYGON (((-124.01829 41.44539, -124.0184... " + ] + }, + "execution_count": 24, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "redwoodNP" ] }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 25, + "metadata": {}, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "" + ], + "text/plain": [ + "" + ] + }, + "execution_count": 25, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "redwoodNP['geometry'][0] # Plot GeoDataFrame" ] @@ -542,9 +1728,20 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 26, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['Beam', 'Shot Number', 'Quality Flag']" + ] + }, + "execution_count": 26, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "# Create a list of geodataframe columns to be included as attributes in the output map\n", "vdims = []\n", @@ -563,9 +1760,101 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 27, + "metadata": {}, + "outputs": [ + { + "data": {}, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.holoviews_exec.v0+json": "", + "text/html": [ + "
\n", + "
\n", + "
\n", + "" + ], + "text/plain": [ + ":Overlay\n", + " .Polygons.I :Polygons [Longitude,Latitude]\n", + " .WMTS.I :WMTS [Longitude,Latitude]\n", + " .Points.I :Points [Longitude,Latitude] (Beam,Shot Number,Quality Flag)" + ] + }, + "execution_count": 27, + "metadata": { + "application/vnd.holoviews_exec.v0+json": { + "id": "p1005" + } + }, + "output_type": "execute_result" + } + ], "source": [ "# Call the function for plotting the GEDI points\n", "gv.Polygons(redwoodNP['geometry']).opts(line_color='red', color=None) * pointVisual(latslons, vdims = vdims)" @@ -602,9 +1891,17 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 28, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Quality Flag: Flag simpilfying selection of most useful data for Level 2B\n" + ] + } + ], "source": [ "print(f\"Quality Flag: {gediL2B[b]['l2b_quality_flag'].attrs['description']}\")" ] @@ -639,7 +1936,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 29, "metadata": {}, "outputs": [], "source": [ @@ -667,27 +1964,60 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 30, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "1576" + ] + }, + "execution_count": 30, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "len(gediSDS)" ] }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 31, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['BEAM0110']" + ] + }, + "execution_count": 31, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "beamNames" ] }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 32, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "197" + ] + }, + "execution_count": 32, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "beamSDS = [g for g in gediSDS if beamNames[0] in g] # Subset to a single beam\n", "len(beamSDS)" @@ -702,7 +2032,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 33, "metadata": {}, "outputs": [], "source": [ @@ -711,9 +2041,20 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 34, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "45732" + ] + }, + "execution_count": 34, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "index = np.where(gediL2B[f'{beamNames[0]}/shot_number'][()]==shot)[0][0] # Set the index for the shot identified above\n", "index" @@ -729,7 +2070,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 35, "metadata": {}, "outputs": [], "source": [ @@ -745,9 +2086,17 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 36, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Plant Area Volume Density is Vertical Plant Area Volume Density profile with a vertical step size of dZ\n" + ] + } + ], "source": [ "print(f\"Plant Area Volume Density is {pavd.attrs['description']}\")" ] @@ -761,9 +2110,20 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 37, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "5.0" + ] + }, + "execution_count": 37, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "# Grab vertical step size \n", "dz = gediL2B[f'{beamNames[0]}/ancillary/dz'][0]\n", @@ -779,9 +2139,17 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 38, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The shape of PAVD is (107014, 30).\n" + ] + } + ], "source": [ "print(f\"The shape of PAVD is {pavd.shape}.\")" ] @@ -796,7 +2164,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 39, "metadata": {}, "outputs": [], "source": [ @@ -815,7 +2183,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 40, "metadata": {}, "outputs": [], "source": [ @@ -834,9 +2202,17 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 41, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The shot is located at: 41.28541681552604, -124.03006394248702 (shot ID: 29320600200465601, index 45732) and is from BEAM0110.\n" + ] + } + ], "source": [ "print(f\"The shot is located at: {str(shotLat)}, {str(shotLon)} (shot ID: {shot}, index {index}) and is from {beamNames[0]}.\")" ] @@ -850,7 +2226,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 42, "metadata": {}, "outputs": [], "source": [ @@ -872,9 +2248,98 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 43, + "metadata": {}, + "outputs": [ + { + "data": {}, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.holoviews_exec.v0+json": "", + "text/html": [ + "
\n", + "
\n", + "
\n", + "" + ], + "text/plain": [ + ":Path [x,y] (PAVD)" + ] + }, + "execution_count": 43, + "metadata": { + "application/vnd.holoviews_exec.v0+json": { + "id": "p1111" + } + }, + "output_type": "execute_result" + } + ], "source": [ "path1 = hv.Path(pavdAll, vdims='PAVD').options(color='PAVD', clim=(0,0.1), cmap='Greens', line_width=20, colorbar=True, \n", " width=700, height=550, clabel='PAVD', xlabel='Shot Number', \n", @@ -902,7 +2367,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 44, "metadata": {}, "outputs": [], "source": [ @@ -933,7 +2398,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 45, "metadata": {}, "outputs": [], "source": [ @@ -949,9 +2414,17 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 46, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The shape of Canopy Height is (107014,) vs. the shape of PAVD, which is (107014, 30).\n" + ] + } + ], "source": [ "print(f\"The shape of Canopy Height is {canopyHeight.shape} vs. the shape of PAVD, which is {pavd.shape}.\")" ] @@ -966,7 +2439,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 47, "metadata": {}, "outputs": [], "source": [ @@ -998,18 +2471,29 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 48, "metadata": { "scrolled": true }, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "107014" + ] + }, + "execution_count": 48, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "len(pavdA)" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 49, "metadata": {}, "outputs": [], "source": [ @@ -1033,9 +2517,288 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 50, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
Shot IndexShot NumberLatitudeLongitudeTandem-X DEMElevation (m)Canopy Elevation (m)Canopy Height (rh100)Quality FlagDegrade FlagPlant Area Volume DensitySensitivitySelected L2A Algorithm
002932060020041986926.923896-142.755692-999999.0000003693.5039063693.5039060.000[nan]5.0299311
112932060020041987026.924089-142.755501-999999.0000003693.5183113693.5183110.000[nan]-4.6206631
222932060020041987126.924283-142.755309-999999.0000003693.5327153693.5327150.000[nan]6.6137961
332932060020041987226.924477-142.755118-999999.0000003693.5471193693.5471190.000[nan]-0.7887311
442932060020041987326.924671-142.754927-999999.0000003693.5615233693.5615230.000[nan]-64.3275151
..........................................
1070091070092932060020052687851.797246-79.858241-36.0216034535.9052734535.9052730.0080[nan]0.4461881
1070101070102932060020052687951.797246-79.857414-999999.0000004543.5742194543.5742190.0080[nan]0.7098041
1070111070112932060020052688051.797245-79.856577-999999.0000004515.9028324515.9028320.0080[nan]-0.8169401
1070121070122932060020052688151.797245-79.855748-999999.0000004516.1020514516.1020510.0080[nan]-43.3016511
1070131070132932060020052688251.797245-79.854919-41.0670854515.5693364515.5693360.0080[nan]0.4082801
\n", + "

107014 rows × 13 columns

\n", + "
" + ], + "text/plain": [ + " Shot Index Shot Number Latitude Longitude Tandem-X DEM \\\n", + "0 0 29320600200419869 26.923896 -142.755692 -999999.000000 \n", + "1 1 29320600200419870 26.924089 -142.755501 -999999.000000 \n", + "2 2 29320600200419871 26.924283 -142.755309 -999999.000000 \n", + "3 3 29320600200419872 26.924477 -142.755118 -999999.000000 \n", + "4 4 29320600200419873 26.924671 -142.754927 -999999.000000 \n", + "... ... ... ... ... ... \n", + "107009 107009 29320600200526878 51.797246 -79.858241 -36.021603 \n", + "107010 107010 29320600200526879 51.797246 -79.857414 -999999.000000 \n", + "107011 107011 29320600200526880 51.797245 -79.856577 -999999.000000 \n", + "107012 107012 29320600200526881 51.797245 -79.855748 -999999.000000 \n", + "107013 107013 29320600200526882 51.797245 -79.854919 -41.067085 \n", + "\n", + " Elevation (m) Canopy Elevation (m) Canopy Height (rh100) \\\n", + "0 3693.503906 3693.503906 0.0 \n", + "1 3693.518311 3693.518311 0.0 \n", + "2 3693.532715 3693.532715 0.0 \n", + "3 3693.547119 3693.547119 0.0 \n", + "4 3693.561523 3693.561523 0.0 \n", + "... ... ... ... \n", + "107009 4535.905273 4535.905273 0.0 \n", + "107010 4543.574219 4543.574219 0.0 \n", + "107011 4515.902832 4515.902832 0.0 \n", + "107012 4516.102051 4516.102051 0.0 \n", + "107013 4515.569336 4515.569336 0.0 \n", + "\n", + " Quality Flag Degrade Flag Plant Area Volume Density Sensitivity \\\n", + "0 0 0 [nan] 5.029931 \n", + "1 0 0 [nan] -4.620663 \n", + "2 0 0 [nan] 6.613796 \n", + "3 0 0 [nan] -0.788731 \n", + "4 0 0 [nan] -64.327515 \n", + "... ... ... ... ... \n", + "107009 0 80 [nan] 0.446188 \n", + "107010 0 80 [nan] 0.709804 \n", + "107011 0 80 [nan] -0.816940 \n", + "107012 0 80 [nan] -43.301651 \n", + "107013 0 80 [nan] 0.408280 \n", + "\n", + " Selected L2A Algorithm \n", + "0 1 \n", + "1 1 \n", + "2 1 \n", + "3 1 \n", + "4 1 \n", + "... ... \n", + "107009 1 \n", + "107010 1 \n", + "107011 1 \n", + "107012 1 \n", + "107013 1 \n", + "\n", + "[107014 rows x 13 columns]" + ] + }, + "execution_count": 50, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "transectDF" ] @@ -1050,9 +2813,98 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 51, + "metadata": {}, + "outputs": [ + { + "data": {}, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.holoviews_exec.v0+json": "", + "text/html": [ + "
\n", + "
\n", + "
\n", + "" + ], + "text/plain": [ + ":Scatter [x] (y)" + ] + }, + "execution_count": 51, + "metadata": { + "application/vnd.holoviews_exec.v0+json": { + "id": "p1173" + } + }, + "output_type": "execute_result" + } + ], "source": [ "# Plot Canopy Height\n", "canopyVis = hv.Scatter((transectDF['Shot Index'], transectDF['Canopy Height (rh100)']))\n", @@ -1069,7 +2921,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 52, "metadata": {}, "outputs": [], "source": [ @@ -1088,7 +2940,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 53, "metadata": {}, "outputs": [], "source": [ @@ -1097,9 +2949,288 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 54, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
Shot IndexShot NumberLatitudeLongitudeTandem-X DEMElevation (m)Canopy Elevation (m)Canopy Height (rh100)Quality FlagDegrade FlagPlant Area Volume DensitySensitivitySelected L2A Algorithm
0NaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaN
1NaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaN
2NaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaN
3NaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaN
4NaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaN
..........................................
107009NaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaN
107010NaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaN
107011NaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaN
107012NaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaN
107013NaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaN
\n", + "

107014 rows × 13 columns

\n", + "
" + ], + "text/plain": [ + " Shot Index Shot Number Latitude Longitude Tandem-X DEM \\\n", + "0 NaN NaN NaN NaN NaN \n", + "1 NaN NaN NaN NaN NaN \n", + "2 NaN NaN NaN NaN NaN \n", + "3 NaN NaN NaN NaN NaN \n", + "4 NaN NaN NaN NaN NaN \n", + "... ... ... ... ... ... \n", + "107009 NaN NaN NaN NaN NaN \n", + "107010 NaN NaN NaN NaN NaN \n", + "107011 NaN NaN NaN NaN NaN \n", + "107012 NaN NaN NaN NaN NaN \n", + "107013 NaN NaN NaN NaN NaN \n", + "\n", + " Elevation (m) Canopy Elevation (m) Canopy Height (rh100) \\\n", + "0 NaN NaN NaN \n", + "1 NaN NaN NaN \n", + "2 NaN NaN NaN \n", + "3 NaN NaN NaN \n", + "4 NaN NaN NaN \n", + "... ... ... ... \n", + "107009 NaN NaN NaN \n", + "107010 NaN NaN NaN \n", + "107011 NaN NaN NaN \n", + "107012 NaN NaN NaN \n", + "107013 NaN NaN NaN \n", + "\n", + " Quality Flag Degrade Flag Plant Area Volume Density Sensitivity \\\n", + "0 NaN NaN NaN NaN \n", + "1 NaN NaN NaN NaN \n", + "2 NaN NaN NaN NaN \n", + "3 NaN NaN NaN NaN \n", + "4 NaN NaN NaN NaN \n", + "... ... ... ... ... \n", + "107009 NaN NaN NaN NaN \n", + "107010 NaN NaN NaN NaN \n", + "107011 NaN NaN NaN NaN \n", + "107012 NaN NaN NaN NaN \n", + "107013 NaN NaN NaN NaN \n", + "\n", + " Selected L2A Algorithm \n", + "0 NaN \n", + "1 NaN \n", + "2 NaN \n", + "3 NaN \n", + "4 NaN \n", + "... ... \n", + "107009 NaN \n", + "107010 NaN \n", + "107011 NaN \n", + "107012 NaN \n", + "107013 NaN \n", + "\n", + "[107014 rows x 13 columns]" + ] + }, + "execution_count": 54, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "transectDF" ] @@ -1113,7 +3244,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 55, "metadata": {}, "outputs": [], "source": [ @@ -1130,7 +3261,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 56, "metadata": {}, "outputs": [], "source": [ @@ -1139,9 +3270,17 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 57, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Quality filtering complete, 15364 high quality shots remaining.\n" + ] + } + ], "source": [ "print(f\"Quality filtering complete, {len(transectDF)} high quality shots remaining.\")" ] @@ -1156,7 +3295,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 58, "metadata": {}, "outputs": [], "source": [ @@ -1167,7 +3306,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 59, "metadata": {}, "outputs": [], "source": [ @@ -1178,7 +3317,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 60, "metadata": {}, "outputs": [], "source": [ @@ -1190,9 +3329,101 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 61, + "metadata": {}, + "outputs": [ + { + "data": {}, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.holoviews_exec.v0+json": "", + "text/html": [ + "
\n", + "
\n", + "
\n", + "" + ], + "text/plain": [ + ":Overlay\n", + " .Scatter.Tandem_hyphen_minus_X_DEM :Scatter [x] (y)\n", + " .Scatter.GEDI_hyphen_minus_derived_Elevation :Scatter [x] (y)\n", + " .Scatter.Canopy_Top_Elevation :Scatter [x] (y)" + ] + }, + "execution_count": 61, + "metadata": { + "application/vnd.holoviews_exec.v0+json": { + "id": "p1228" + } + }, + "output_type": "execute_result" + } + ], "source": [ "# Combine all three scatterplots\n", "(demVis * zVis * rhVis).opts(show_legend=True, legend_position='top_left',fontsize={'title':14, 'xlabel':16, 'ylabel': 16}, \n", @@ -1228,16 +3459,24 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 62, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "45732\n" + ] + } + ], "source": [ "print(index)" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 63, "metadata": {}, "outputs": [], "source": [ @@ -1248,11 +3487,19 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 64, "metadata": { "scrolled": true }, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The transect begins at (41.27021331597735, -124.05764284824794) and ends at (41.30056975532455, -124.00258112950078).\n" + ] + } + ], "source": [ "print(f\"The transect begins at ({transectDF['Latitude'][start]}, {transectDF['Longitude'][start]}) and ends at ({transectDF['Latitude'][end]}, {transectDF['Longitude'][end]}).\")" ] @@ -1266,7 +3513,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 65, "metadata": {}, "outputs": [], "source": [ @@ -1291,7 +3538,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 66, "metadata": {}, "outputs": [], "source": [ @@ -1316,7 +3563,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 67, "metadata": {}, "outputs": [], "source": [ @@ -1339,7 +3586,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 68, "metadata": { "scrolled": true }, @@ -1357,9 +3604,105 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 69, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "BokehUserWarning: ColumnDataSource's columns must be of the same length. Current lengths: ('color', 1371), ('xs', 1275), ('ys', 1275)\n" + ] + }, + { + "data": {}, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.holoviews_exec.v0+json": "", + "text/html": [ + "
\n", + "
\n", + "
\n", + "" + ], + "text/plain": [ + ":Path [x,y] (PAVD)" + ] + }, + "execution_count": 69, + "metadata": { + "application/vnd.holoviews_exec.v0+json": { + "id": "p1321" + } + }, + "output_type": "execute_result" + } + ], "source": [ "path1 = hv.Path(pavdAll, vdims='PAVD').options(color='PAVD', clim=(0,0.3), cmap='Greens', line_width=8, colorbar=True, \n", " width=950, height=500, clabel='PAVD', xlabel='Distance Along Transect (m)',\n", @@ -1378,7 +3721,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 70, "metadata": {}, "outputs": [], "source": [ @@ -1388,9 +3731,108 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 71, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "BokehUserWarning: ColumnDataSource's columns must be of the same length. Current lengths: ('color', 1371), ('xs', 1275), ('ys', 1275)\n" + ] + }, + { + "data": {}, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.holoviews_exec.v0+json": "", + "text/html": [ + "
\n", + "
\n", + "
\n", + "" + ], + "text/plain": [ + ":Overlay\n", + " .Path.I :Path [x,y] (PAVD)\n", + " .Curve.Ground_Elevation :Curve [x] (y)\n", + " .Curve.Canopy_Top_Elevation :Curve [x] (y)" + ] + }, + "execution_count": 71, + "metadata": { + "application/vnd.holoviews_exec.v0+json": { + "id": "p3929" + } + }, + "output_type": "execute_result" + } + ], "source": [ "# Plot all three together\n", "path = path1 * path2 * path3\n", @@ -1409,7 +3851,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 72, "metadata": {}, "outputs": [], "source": [ @@ -1448,7 +3890,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 73, "metadata": {}, "outputs": [], "source": [ @@ -1457,9 +3899,27 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 74, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['BEAM0000',\n", + " 'BEAM0001',\n", + " 'BEAM0010',\n", + " 'BEAM0011',\n", + " 'BEAM0101',\n", + " 'BEAM0110',\n", + " 'BEAM1000',\n", + " 'BEAM1011']" + ] + }, + "execution_count": 74, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "beamNames" ] @@ -1473,7 +3933,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 75, "metadata": {}, "outputs": [], "source": [ @@ -1483,7 +3943,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 76, "metadata": {}, "outputs": [], "source": [ @@ -1506,7 +3966,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 77, "metadata": {}, "outputs": [], "source": [ @@ -1530,7 +3990,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 78, "metadata": {}, "outputs": [], "source": [ @@ -1547,9 +4007,20 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 79, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "790135" + ] + }, + "execution_count": 79, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "len(allDF)" ] @@ -1563,16 +4034,30 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 80, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(-124.16015705494489,\n", + " 41.080601363502545,\n", + " -123.84950230520286,\n", + " 41.83981133687605)" + ] + }, + "execution_count": 80, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "redwoodNP.envelope[0].bounds" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 81, "metadata": {}, "outputs": [], "source": [ @@ -1588,7 +4073,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 82, "metadata": {}, "outputs": [], "source": [ @@ -1600,7 +4085,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 83, "metadata": {}, "outputs": [], "source": [ @@ -1609,9 +4094,20 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 84, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "4477" + ] + }, + "execution_count": 84, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "len(allDF)" ] @@ -1625,9 +4121,20 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 85, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "2979" + ] + }, + "execution_count": 85, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "# Set any poor quality returns to NaN\n", "allDF = allDF.where(allDF['Quality Flag'].ne(0))\n", @@ -1646,7 +4153,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 86, "metadata": {}, "outputs": [], "source": [ @@ -1656,7 +4163,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 87, "metadata": {}, "outputs": [], "source": [ @@ -1681,9 +4188,101 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 88, + "metadata": {}, + "outputs": [ + { + "data": {}, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.holoviews_exec.v0+json": "", + "text/html": [ + "
\n", + "
\n", + "
\n", + "" + ], + "text/plain": [ + ":Overlay\n", + " .WMTS.I :WMTS [Longitude,Latitude]\n", + " .Points.I :Points [Longitude,Latitude] (Shot Number,Beam,Tandem-X DEM,Elevation (m),Canopy Elevation (m),Canopy Height (rh100),Quality Flag,Plant Area Index,Degrade Flag,Sensitivity,Selected L2A Algorithm)\n", + " .Polygons.I :Polygons [Longitude,Latitude]" + ] + }, + "execution_count": 88, + "metadata": { + "application/vnd.holoviews_exec.v0+json": { + "id": "p6573" + } + }, + "output_type": "execute_result" + } + ], "source": [ "allDF['Shot Number'] = allDF['Shot Number'].astype(str) # Convert shot number to string\n", "\n", @@ -1712,7 +4311,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 89, "metadata": { "scrolled": true }, @@ -1723,9 +4322,100 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 90, + "metadata": {}, + "outputs": [ + { + "data": {}, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.holoviews_exec.v0+json": "", + "text/html": [ + "
\n", + "
\n", + "
\n", + "" + ], + "text/plain": [ + ":Overlay\n", + " .WMTS.I :WMTS [Longitude,Latitude]\n", + " .Points.I :Points [Longitude,Latitude] (Shot Number,Beam,Tandem-X DEM,Elevation (m),Canopy Elevation (m),Canopy Height (rh100),Quality Flag,Plant Area Index,Degrade Flag,Sensitivity,Selected L2A Algorithm)" + ] + }, + "execution_count": 90, + "metadata": { + "application/vnd.holoviews_exec.v0+json": { + "id": "p6687" + } + }, + "output_type": "execute_result" + } + ], "source": [ "# Plot the basemap and geoviews Points, defining the color as the Canopy Height for each shot\n", "(gvts.EsriImagery * gv.Points(allDF, vdims=vdims).options(color='Canopy Height (rh100)',cmap='plasma', size=3, tools=['hover'],\n", @@ -1758,9 +4448,100 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 91, + "metadata": {}, + "outputs": [ + { + "data": {}, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.holoviews_exec.v0+json": "", + "text/html": [ + "
\n", + "
\n", + "
\n", + "" + ], + "text/plain": [ + ":Overlay\n", + " .WMTS.I :WMTS [Longitude,Latitude]\n", + " .Points.I :Points [Longitude,Latitude] (Shot Number,Beam,Tandem-X DEM,Elevation (m),Canopy Elevation (m),Canopy Height (rh100),Quality Flag,Plant Area Index,Degrade Flag,Sensitivity,Selected L2A Algorithm)" + ] + }, + "execution_count": 91, + "metadata": { + "application/vnd.holoviews_exec.v0+json": { + "id": "p6784" + } + }, + "output_type": "execute_result" + } + ], "source": [ "(gvts.EsriImagery * gv.Points(allDF, vdims=vdims).options(color='Elevation (m)',cmap='terrain', size=3, tools=['hover'],\n", " clim=(min(allDF['Elevation (m)']), max(allDF['Elevation (m)'])),\n", @@ -1780,9 +4561,100 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 92, + "metadata": {}, + "outputs": [ + { + "data": {}, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.holoviews_exec.v0+json": "", + "text/html": [ + "
\n", + "
\n", + "
\n", + "" + ], + "text/plain": [ + ":Overlay\n", + " .WMTS.I :WMTS [Longitude,Latitude]\n", + " .Points.I :Points [Longitude,Latitude] (Shot Number,Beam,Tandem-X DEM,Elevation (m),Canopy Elevation (m),Canopy Height (rh100),Quality Flag,Plant Area Index,Degrade Flag,Sensitivity,Selected L2A Algorithm)" + ] + }, + "execution_count": 92, + "metadata": { + "application/vnd.holoviews_exec.v0+json": { + "id": "p6881" + } + }, + "output_type": "execute_result" + } + ], "source": [ "(gvts.EsriImagery * gv.Points(allDF, vdims=vdims).options(color='Plant Area Index',cmap='Greens', size=3, tools=['hover'],\n", " clim=(0,1), colorbar=True, clabel='m2/m2',\n", @@ -1803,25 +4675,47 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "---\n", + "\n", "# 8. Export Subsets as GeoJSON Files\n", "#### In this section, export the GeoDataFrame as a `.geojson` file that can be easily opened in your favorite remote sensing and/or GIS software and will include an attribute table with all of the shots/values for each of the SDS layers in the dataframe." ] }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 93, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'data/GEDI02_B_2019170155833_O02932_02_T02267_02_003_01_V002.h5'" + ] + }, + "execution_count": 93, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "gediL2B.filename # L2B Filename" ] }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 94, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'data/GEDI02_B_2019170155833_O02932_02_T02267_02_003_01_V002.json'" + ] + }, + "execution_count": 94, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "outName = gediL2B.filename.replace('.h5', '.json') # Create an output file name using the input file name\n", "outName" @@ -1829,7 +4723,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 95, "metadata": {}, "outputs": [], "source": [ @@ -1838,7 +4732,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 96, "metadata": {}, "outputs": [], "source": [ @@ -1849,21 +4743,16 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "
\n", - "

Contact Information

\n", - "

Material written by LP DAAC1

\n", - "
    \n", - " Contact: LPDAAC@usgs.gov
    \n", - " Voice: +1-605-594-6116
    \n", - " Organization: Land Processes Distributed Active Archive Center (LP DAAC)
    \n", - " Website: https://lpdaac.usgs.gov/
    \n", - " Date last modified: 03-27-2023
    \n", - "
\n", - " \n", - "1KBR Inc., contractor to the U.S. Geological Survey, Earth Resources Observation and Science (EROS) Center, Sioux Falls, South Dakota, 57198-001, USA. Work performed under USGS contract G15PD00467 for LP DAAC2.\n", "\n", - "2LP DAAC Work performed under NASA contract NNG14HH33I.\n", - "
" + "## Contact Info: \n", + "\n", + "Email: LPDAAC@usgs.gov \n", + "Voice: +1-866-573-3222 \n", + "Organization: Land Processes Distributed Active Archive Center (LP DAAC)¹ \n", + "Website: \n", + "Date last modified: 02-20-2024 \n", + "\n", + "¹Work performed under USGS contract G15PD00467 for NASA contract NNG14HH33I. " ] } ], @@ -1883,7 +4772,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.7" + "version": "3.10.13" }, "vscode": { "interpreter": { diff --git a/r/GEDI_Finder_Tutorial_R.rmd b/r/GEDI_Finder_Tutorial_R.rmd index dd117c1..b100592 100644 --- a/r/GEDI_Finder_Tutorial_R.rmd +++ b/r/GEDI_Finder_Tutorial_R.rmd @@ -74,12 +74,13 @@ Lower Left Longitude, Lower Left Latitude, Upper Right Longitude, Upper Right La gedi_finder <- function(product, bbox) { # Define the base CMR granule search url, including LPDAAC provider name and max page size (2000 is the max allowed) -cmr <- "https://cmr.earthdata.nasa.gov/search/granules.json?pretty=true&provider=LPDAAC_ECS&page_size=2000&concept_id=" +cmr <- "https://cmr.earthdata.nasa.gov/search/granules.json?pretty=true&provider=LPCLOUD&page_size=2000&concept_id=" # Set up list where key is GEDI shortname + version and value is CMR Concept ID - concept_ids <- list('GEDI01_B.002'='C1908344278-LPDAAC_ECS', - 'GEDI02_A.002'='C1908348134-LPDAAC_ECS', - 'GEDI02_B.002'='C1908350066-LPDAAC_ECS') + concept_ids <- list('GEDI01_B.002'='C2142749196-LPCLOUD', + 'GEDI02_A.002'='C2142771958-LPCLOUD', + 'GEDI02_B.002'='C2142776747-LPCLOUD') + # CMR uses pagination for queries with more features returned than the page size page <- 1 @@ -152,38 +153,3 @@ outName <- sprintf("%s_GranuleList_%s.txt", sub('.002', '_002', product), format write.table(granules, outName, row.names = FALSE, col.names = FALSE, quote = FALSE, sep='\n') print(sprintf("File containing links to intersecting %s Version 2 data has been saved to: %s/%s", product, getwd(), outName)) -``` - -## Additional Resources -Looking to bulk download the intersecting GEDI V2 files from your request? Check out the following LP DAAC resources to get you started: -1. [How to Access LP DAAC Data from the Command Line](https://lpdaac.usgs.gov/resources/e-learning/how-access-lp-daac-data-command-line/) -2. [How to Access the LP DAAC Data Pool with Python](https://git.earthdata.nasa.gov/projects/LPDUR/repos/daac_data_download_python/browse) -3. [How to Access the LP DAAC Data Pool with R](https://git.earthdata.nasa.gov/projects/LPDUR/repos/daac_data_download_r/browse) - -Also be sure to check out the following GEDI Resources: - -1. [GEDI Spatial Querying and Subsetting Quick Guide V2](https://lpdaac.usgs.gov/documents/989/GEDI_Quick_Guide_V2.pdf) - - - Explains how to perform spatial querying and subsetting of GEDI V2 data directly in NASA's Earthdata Search Client - -2. [GEDI Spatial and Band/layer Subsetting and Export to GeoJSON (GEDI Subsetter) Script](https://git.earthdata.nasa.gov/projects/LPDUR/repos/gedi-subsetter/browse?_ga=2.194415528.402062677.1620050832-1162630485.1615217900) - - - Allows you to subset GEDI V2 data by band/layer and region of interest - -3. [Getting Started with GEDI L1B, L2A, and L2B V2 Data in Python Tutorial Series](https://git.earthdata.nasa.gov/projects/LPDUR/repos/gedi-v2-tutorials/browse) - - - Includes a series of tutorials that demonstrate how to start working with GEDI V2 data in Python. - -
-Contact Information -Material written by Cole Krehbiel$^{1}$ -Contact: LPDAAC@usgs.gov -Voice: +1-605-594-6116 -Organization: Land Processes Distributed Active Archive Center (LP DAAC) -Website: https://lpdaac.usgs.gov/ -Date last modified: 05-10-2021 - -$^{1}$KBR Inc., contractor to the U.S. Geological Survey, Earth Resources Observation and Science (EROS) Center, Sioux Falls, South Dakota, 57198-001, USA. Work performed under USGS contract G15PD00467 for LP DAAC$^{2}$. - -$^{2}$LP DAAC Work performed under NASA contract NNG14HH33I. -