Source code for gedih3.cli.gh3_download

#! python

# Copyright (C) 2026, University of Maryland. All Rights Reserved.
# Authors: Tiago de Conto, Amelia Grace Holcomb
# For commercial licensing inquiries, contact UM Ventures at umdtechtransfer@umd.edu

import argparse

[docs] def get_cmd_args(): from gedih3.cliutils import add_dask_args, add_verbosity_args, add_product_args p = argparse.ArgumentParser(description="Download GEDI data from NASA's DAAC") # Spatial/temporal filtering p.add_argument("-r", "--region", dest="region", type=str, default=None, help="vector file, bbox 'W,S,E,N', or ISO3 code") p.add_argument("-t0", "--time-start", dest="time_start", type=str, default=None, help="start date [YYYY-MM-DD]") p.add_argument("-t1", "--time-end", dest="time_end", type=str, default=None, help="end date [YYYY-MM-DD]") # GEDI product variables add_product_args(p) # Output options p.add_argument("-o", "--output", dest="output", type=str, default=None, help="output directory for downloaded files") p.add_argument("-s3", "--s3", dest="s3", action='store_true', help="use S3 ETL mode: stream and subset from NASA S3. Best when the subset is narrow (<~10%% of the granule) or bandwidth is constrained. On fast links with broad subsets (e.g. L2A default including rh), plain DAAC download is usually faster") p.add_argument("--gedi-version", dest="version", type=int, default=None, help="GEDI data version [default=latest available]") # Dask and verbosity add_dask_args(p, profile='build') add_verbosity_args(p) return p.parse_args()
[docs] def main(): import os import sys args = get_cmd_args() from gedih3.config import GH3_DEFAULT_SOC_DIR from gedih3.cliutils import parse_gedi_args, parse_dask_args, parse_region, setup_logging, print_banner, print_success, resolve_path_args from gedih3.gh3builder import download_soc, s3_etl_subset from gedih3.logger import SOCDownloadLogger from dask.distributed import Client # Setup logging and print banner logger = setup_logging(args, __name__) print_banner("GEDI Data Download Tool", logger=logger) if args.output is None: args.output = GH3_DEFAULT_SOC_DIR resolve_path_args(args, ['output'], logger=logger) os.makedirs(args.output, exist_ok=True) product_vars = parse_gedi_args(args) spatial = parse_region(args.region) if args.region is not None else None temporal = None if args.time_start or args.time_end: temporal = (args.time_start, args.time_end) soc_logger = SOCDownloadLogger( product_vars=product_vars, spatial=spatial, temporal=temporal, version=args.version, dir=args.output ) if args.s3: soc_logger.s3_access = True if not soc_logger.product_vars and not soc_logger.updating: raise ValueError( "No GEDI product selected for download - please select at least one of " "--l2a, --l2b, --l4a, --l4c (or use --detail-level for all four), " "and/or --l1b for waveform data" ) if soc_logger.get_spatial() is None: logger.warning("No spatial filter provided - downloading global data") if soc_logger.get_temporal() is None: logger.warning("No temporal filter provided - downloading data from all available dates") if soc_logger.updating: logger.info("Download log exists, resuming downloads") if soc_logger.new_spatial is not None: logger.info("Spatial filter updated") if soc_logger.new_temporal is not None: logger.info("Temporal filter updated") if soc_logger.new_product_vars is not None: logger.info("Product variables updated") source_label = "S3 ETL" if args.s3 else "DAAC download" logger.info(f"Downloading GEDI data to {args.output} ({source_label})") soc_logger.save_log('DOWNLOADING') dask_kwargs = parse_dask_args(args) try: with Client(**dask_kwargs) as client: logger.info(f"Dask dashboard available at: {client.dashboard_link}") try: if args.s3: # S3 ETL mode: stream and subset from NASA S3 to permanent output dir s3_etl_subset( product_vars=soc_logger.product_vars, spatial=soc_logger.get_spatial(), temporal=soc_logger.get_temporal(), version=args.version, odir=args.output, ensure_l2a=True, ) else: # Standard DAAC download mode download_soc( product_vars=soc_logger.product_vars, spatial=soc_logger.get_spatial(), temporal=soc_logger.get_temporal(), direct_access=False, update=True, version=args.version, odir=args.output ) soc_logger.set_post_download_info() soc_logger.save_log('COMPLETED') # set_post_download_info already walked the SOC tree (and # refreshed _soc_manifest.txt); read the count it cached # instead of crawling GPFS a second time. n_files = getattr(soc_logger, 'n_files', 0) print_success(f"{n_files} files downloaded to {args.output} ({source_label})", logger=logger) except Exception as e: soc_logger.save_log('FAILED') logger.error(f"Download failed: {e}") raise e except KeyboardInterrupt: logger.warning("\nDownload interrupted by user") soc_logger.save_log('INTERRUPTED') sys.exit(130) except Exception as e: from gedih3.exceptions import ( GediDownloadError, GediAuthenticationError, GediNetworkError, GediError ) if isinstance(e, GediAuthenticationError): logger.error(f"Authentication error: {e}") logger.info("Please check your NASA Earthdata credentials at ~/.netrc") sys.exit(2) elif isinstance(e, GediDownloadError): logger.error(f"Download error: {e}") sys.exit(3) elif isinstance(e, GediNetworkError): logger.error(f"Network error: {e}") logger.info("Check your internet connection and try again") sys.exit(4) elif isinstance(e, GediError): logger.error(f"GEDI error: {e}") sys.exit(1) else: logger.error(f"Unexpected error: {type(e).__name__}: {e}") if args.verbose >= 2: import traceback traceback.print_exc() sys.exit(1)
if __name__ == "__main__": main()