Source code for icesat2_toolkit.io.ATL11

#!/usr/bin/env python
u"""
ATL11.py (05/2024)
Read ICESat-2 ATL11 (Annual Land Ice Height) data files

OPTIONS:
    GROUPS: HDF5 groups to read for each beam pair
    ATTRIBUTES: read HDF5 attributes for groups and variables
    REFERENCE: read ATL11 reference surface variables
    CROSSOVERS: read ATL11 crossover height variables
    SUBSETTING: read ATL11 subsetting variables

PYTHON DEPENDENCIES:
    numpy: Scientific Computing Tools For Python
        https://numpy.org
        https://numpy.org/doc/stable/user/numpy-for-matlab-users.html
    h5py: Python interface for Hierarchal Data Format 5 (HDF5)
        https://www.h5py.org/

UPDATE HISTORY:
    Updated 05/2024: use wrapper to importlib for optional dependencies
        check if input filename is an open HDF5 file object
    Updated 03/2024: use pathlib to define and operate on paths
    Updated 11/2023: drop DIMENSION_LIST, CLASS and NAME attributes
    Updated 05/2023: extract more ancillary data from ATL11 files
    Updated 12/2022: place some imports behind try/except statements
        refactor ICESat-2 data product read programs under io
    Updated 04/2022: updated docstrings to numpy documentation format
    Updated 10/2021: using python logging for handling verbose output
    Updated 03/2021: added function for reading only pair level variables
        simplified group reads to iterate within a try/except statement
        added keyword argument to explicitly define groups to read
    Updated 02/2021: add check if input streaming from bytes
        add small function to find valid beam pair groups
    Written 01/2021
"""
from __future__ import print_function

import io
import re
import logging
import pathlib
import warnings
import numpy as np
from icesat2_toolkit.utilities import import_dependency

# attempt imports
h5py = import_dependency('h5py')

# PURPOSE: read ICESat-2 ATL11 HDF5 data files
[docs]def read_granule(FILENAME, GROUPS=['cycle_stats'], ATTRIBUTES=False, REFERENCE=False, CROSSOVERS=False, SUBSETTING=False, KEEP=False, **kwargs): """ Reads ICESat-2 ATL11 (Annual Land Ice Height) data files Parameters ---------- FILENAME: str full path to ATL11 file GROUPS: list, default ['cycle_stats'] HDF5 groups to read for each beam pair ATTRIBUTES: bool, default False read HDF5 attributes for groups and variables REFERENCE: bool, default False read ATL11 reference surface variables CROSSOVERS: bool, default False read ATL11 crossover height variables SUBSETTING: bool, default False read ATL11 subsetting variables KEEP: bool, default False keep file object open Returns ------- IS2_atl11_mds: ATL11 variables IS2_atl11_attrs: ATL11 attributes IS2_atl11_pairs: list valid ICESat-2 beam pairs within ATL11 file """ # Open the HDF5 file for reading if isinstance(FILENAME, io.IOBase): fileID = h5py.File(FILENAME, 'r') elif isinstance(FILENAME, h5py.File): fileID = FILENAME else: FILENAME = pathlib.Path(FILENAME).expanduser().absolute() fileID = h5py.File(FILENAME, 'r') # Output HDF5 file information logging.info(fileID.filename) logging.info(list(fileID.keys())) # allocate python dictionaries for ICESat-2 ATL11 variables and attributes IS2_atl11_mds = {} IS2_atl11_attrs = {} # read each input beam pair within the file IS2_atl11_pairs = [] for ptx in [k for k in fileID.keys() if bool(re.match(r'pt\d',k))]: # check if subsetted beam contains reference points try: fileID[ptx]['ref_pt'] except KeyError: pass else: IS2_atl11_pairs.append(ptx) # groups to read from ATL11 file # ATL11 ref_surf group if REFERENCE: GROUPS.append('ref_surf') # ATL11 crossing_track_data group if CROSSOVERS: GROUPS.append('crossing_track_data') # ATL11 subsetting group if SUBSETTING: GROUPS.append('subsetting') # read each input pair track within the file for ptx in IS2_atl11_pairs: IS2_atl11_mds[ptx] = {} # get each main level HDF5 variable for key,val in fileID[ptx].items(): if isinstance(val, h5py.Dataset): IS2_atl11_mds[ptx][key] = val[:] # get each HDF5 variable within named groups for group in GROUPS: try: IS2_atl11_mds[ptx][group] = {} for key,val in fileID[ptx][group].items(): IS2_atl11_mds[ptx][group][key] = val[:] except: pass # Getting attributes of included variables if ATTRIBUTES: # Getting attributes of ICESat-2 ATL11 main level variables IS2_atl11_attrs[ptx] = {} # Global Group Attributes for ATL11 beam pair for att_name,att_val in fileID[ptx].attrs.items(): if att_name not in ('DIMENSION_LIST','CLASS','NAME'): IS2_atl11_attrs[ptx][att_name] = att_val # getting attributes of main level ATL11 variables for key,val in fileID[ptx].items(): IS2_atl11_attrs[ptx][key] = {} for att_name,att_val in val.attrs.items(): if att_name not in ('DIMENSION_LIST','CLASS','NAME'): IS2_atl11_attrs[ptx][key][att_name] = att_val # get fill value attributes if applicable if hasattr(val,'fillvalue'): IS2_atl11_attrs[ptx][key]['_FillValue'] = \ getattr(val,'fillvalue') # getting attributes of variables within named groups for group in GROUPS: try: IS2_atl11_attrs[ptx][group] = {} for key,val in fileID[ptx][group].items(): IS2_atl11_attrs[ptx][group][key] = {} for att_name,att_val in val.attrs.items(): if att_name not in ('DIMENSION_LIST','CLASS','NAME'): IS2_atl11_attrs[ptx][group][key][att_name] = att_val # get fill value attributes if applicable if hasattr(val,'fillvalue'): IS2_atl11_attrs[ptx][group][key]['_FillValue'] = \ getattr(val,'fillvalue') except: pass # ICESat-2 orbit_info Group IS2_atl11_mds['orbit_info'] = {} for key,val in fileID['orbit_info'].items(): IS2_atl11_mds['orbit_info'][key] = val[:] # ICESat-2 quality_assessment Group IS2_atl11_mds['quality_assessment'] = {} for key,val in fileID['quality_assessment'].items(): IS2_atl11_mds['quality_assessment'][key] = val[:] # number of GPS seconds between the GPS epoch (1980-01-06T00:00:00Z UTC) # and ATLAS Standard Data Product (SDP) epoch (2018-01-01:T00:00:00Z UTC) # Add this value to delta time parameters to compute full gps_seconds # could alternatively use the Julian day of the ATLAS SDP epoch: 2458119.5 # and add leap seconds since 2018-01-01:T00:00:00Z UTC (ATLAS SDP epoch) IS2_atl11_mds['ancillary_data'] = {} IS2_atl11_attrs['ancillary_data'] = {} ancillary_keys = ['atlas_sdp_gps_epoch','data_end_utc','data_start_utc', 'end_cycle','end_geoseg','end_gpssow','end_gpsweek','end_orbit', 'end_region','end_rgt','granule_end_utc','granule_start_utc','release', 'start_cycle','start_geoseg','start_gpssow','start_gpsweek', 'start_orbit','start_region','start_rgt','version'] for key in ancillary_keys: # get each HDF5 variable IS2_atl11_mds['ancillary_data'][key] = fileID['ancillary_data'][key][:] # Getting attributes of group and included variables if ATTRIBUTES: # Variable Attributes IS2_atl11_attrs['ancillary_data'][key] = {} for att_name,att_val in fileID['ancillary_data'][key].attrs.items(): if att_name not in ('DIMENSION_LIST','CLASS','NAME'): IS2_atl11_attrs['ancillary_data'][key][att_name] = att_val # get each global attribute and the attributes for orbit and quality if ATTRIBUTES: # ICESat-2 HDF5 global attributes for att_name,att_val in fileID.attrs.items(): if att_name not in ('DIMENSION_LIST','CLASS','NAME'): IS2_atl11_attrs[att_name] = att_name # ICESat-2 orbit_info Group IS2_atl11_attrs['orbit_info'] = {} for key,val in fileID['orbit_info'].items(): IS2_atl11_attrs['orbit_info'][key] = {} for att_name,att_val in val.attrs.items(): if att_name not in ('DIMENSION_LIST','CLASS','NAME'): IS2_atl11_attrs['orbit_info'][key][att_name]= att_val # ICESat-2 quality_assessment Group IS2_atl11_attrs['quality_assessment'] = {} for key,val in fileID['quality_assessment'].items(): IS2_atl11_attrs['quality_assessment'][key] = {} for att_name,att_val in val.attrs.items(): if att_name not in ('DIMENSION_LIST','CLASS','NAME'): IS2_atl11_attrs['quality_assessment'][key][att_name]= att_val # Closing the HDF5 file if not KEEP: fileID.close() # Return the datasets and variables return (IS2_atl11_mds, IS2_atl11_attrs, IS2_atl11_pairs)
# PURPOSE: find valid beam pair groups within ICESat-2 ATL11 HDF5 data files
[docs]def find_pairs(FILENAME, KEEP=False, **kwargs): """ Find valid beam pair groups within ICESat-2 ATL11 (Annual Land Ice Height) data files Parameters ---------- FILENAME: str full path to ATL11 file KEEP: bool, default False keep file object open Returns ------- IS2_atl11_pairs: list valid ICESat-2 beam pairs within ATL11 file """ # Open the HDF5 file for reading if isinstance(FILENAME, io.IOBase): fileID = h5py.File(FILENAME, 'r') elif isinstance(FILENAME, h5py.File): fileID = FILENAME else: FILENAME = pathlib.Path(FILENAME).expanduser().absolute() fileID = h5py.File(FILENAME, 'r') # read each input beam pair within the file IS2_atl11_pairs = [] for ptx in [k for k in fileID.keys() if bool(re.match(r'pt\d',k))]: # check if subsetted beam contains reference points try: fileID[ptx]['ref_pt'] except KeyError: pass else: IS2_atl11_pairs.append(ptx) # Closing the HDF5 file if not KEEP: fileID.close() # return the list of beam pairs return IS2_atl11_pairs
# PURPOSE: read ICESat-2 ATL11 HDF5 data files for a specific beam pair
[docs]def read_pair(FILENAME, ptx, GROUPS=['cycle_stats'], ATTRIBUTES=False, REFERENCE=False, CROSSOVERS=False, SUBSETTING=False, KEEP=False, **kwargs): """ Reads ICESat-2 ATL11 (Annual Land Ice Height) data files for a specific beam pair Parameters ---------- FILENAME: str full path to ATL11 file ptx: str beam pair name - ``'pt1'`` - ``'pt2'`` - ``'pt3'`` GROUPS: list, default ['cycle_stats'] HDF5 groups to read for each beam pair ATTRIBUTES: bool, default False read HDF5 attributes for groups and variables REFERENCE: bool, default False read ATL11 reference surface variables CROSSOVERS: bool, default False read ATL11 crossover height variables SUBSETTING: bool, default False read ATL11 subsetting variables KEEP: bool, default False keep file object open Returns ------- IS2_atl11_mds: dict ATL11 variables IS2_atl11_attrs: dict ATL11 attributes """ # Open the HDF5 file for reading if isinstance(FILENAME, io.IOBase): fileID = h5py.File(FILENAME, 'r') elif isinstance(FILENAME, h5py.File): fileID = FILENAME else: FILENAME = pathlib.Path(FILENAME).expanduser().absolute() fileID = h5py.File(FILENAME, 'r') # Output HDF5 file information logging.info(fileID.filename) logging.info(list(fileID.keys())) # allocate python dictionaries for ICESat-2 ATL11 variables and attributes IS2_atl11_mds = {} IS2_atl11_attrs = {} # groups to read from ATL11 file # ATL11 ref_surf group if REFERENCE: GROUPS.append('ref_surf') # ATL11 crossing_track_data group if CROSSOVERS: GROUPS.append('crossing_track_data') # ATL11 subsetting group if SUBSETTING: GROUPS.append('subsetting') # read input pair track within the file IS2_atl11_mds[ptx] = {} # get each main level HDF5 variable for key,val in fileID[ptx].items(): if isinstance(val, h5py.Dataset): IS2_atl11_mds[ptx][key] = val[:] # get each cycle_stats HDF5 variable for group in GROUPS: try: IS2_atl11_mds[ptx][group] = {} for key,val in fileID[ptx][group].items(): IS2_atl11_mds[ptx][group][key] = val[:] except: pass # Getting attributes of included variables if ATTRIBUTES: # Getting attributes of ICESat-2 ATL11 main level variables IS2_atl11_attrs[ptx] = {} # Global Group Attributes for ATL11 beam pair for att_name,att_val in fileID[ptx].attrs.items(): if att_name not in ('DIMENSION_LIST','CLASS','NAME'): IS2_atl11_attrs[ptx][att_name] = att_val # getting attributes of main level ATL11 variables for key,val in fileID[ptx].items(): IS2_atl11_attrs[ptx][key] = {} for att_name,att_val in val.attrs.items(): if att_name not in ('DIMENSION_LIST','CLASS','NAME'): IS2_atl11_attrs[ptx][key][att_name] = att_val # get fill value attributes if applicable if hasattr(val,'fillvalue'): IS2_atl11_attrs[ptx][key]['_FillValue'] = \ getattr(val,'fillvalue') # getting attributes of variables within named groups for group in GROUPS: try: IS2_atl11_attrs[ptx][group] = {} for key,val in fileID[ptx][group].items(): IS2_atl11_attrs[ptx][group][key] = {} for att_name,att_val in val.attrs.items(): if att_name not in ('DIMENSION_LIST','CLASS','NAME'): IS2_atl11_attrs[ptx][group][key][att_name] = att_val # get fill value attributes if applicable if hasattr(val,'fillvalue'): IS2_atl11_attrs[ptx][group][key]['_FillValue'] = \ getattr(val,'fillvalue') except: pass # Closing the HDF5 file if not KEEP: fileID.close() # Return the datasets and variables return (IS2_atl11_mds, IS2_atl11_attrs)