From 9d09ff9658c4fdf958a80253d60bfb1e8cbfa365 Mon Sep 17 00:00:00 2001 From: Rachel Wegener Date: Tue, 1 Aug 2023 17:07:58 +0000 Subject: [PATCH 01/55] mvp remove intake from Read --- icepyx/core/read.py | 51 ++++++++++++++++++++++++--------------------- 1 file changed, 27 insertions(+), 24 deletions(-) diff --git a/icepyx/core/read.py b/icepyx/core/read.py index 5a497279a..5860e32dc 100644 --- a/icepyx/core/read.py +++ b/icepyx/core/read.py @@ -4,6 +4,7 @@ import numpy as np import xarray as xr +import h5py import icepyx.core.is2cat as is2cat import icepyx.core.is2ref as is2ref @@ -258,7 +259,7 @@ def __init__( catalog=None, out_obj_type=None, # xr.Dataset, ): - + # Note: maybe just don't add default values, so that Python enforces their existence? if data_source is None: raise ValueError("Please provide a data source.") else: @@ -271,10 +272,16 @@ def __init__( ) else: self._prod = is2ref._validate_product(product) - + + # TODO delete? seems like it just validates the pattern + # Does Read accept a directory right now? Why would there be multiple files in the list? + # seems like yes, it does accept a directory + # does it check, then, that all the files have the same version and product? pattern_ck, filelist = Read._check_source_for_pattern( data_source, filename_pattern ) + print('pattern_ck', pattern_ck) + print('filelist', filelist) assert pattern_ck # Note: need to check if this works for subset and non-subset NSIDC files (processed_ prepends the former) self._pattern = filename_pattern @@ -282,7 +289,8 @@ def __init__( # this is a first pass at getting rid of mixed product types and warning the user. # it takes an approach assuming the product name is in the filename, but needs reworking if we let multiple products be loaded # one way to handle this would be bring in the product info during the loading step and fill in product there instead of requiring it from the user - filtered_filelist = [file for file in filelist if self._prod in file] + filtered_filelist = [file for file in filelist if self._prod in Read._get_product_and_version(file)] + print('filtered', filtered_filelist) if len(filtered_filelist) == 0: warnings.warn( "Your filenames do not contain a product identifier (e.g. ATL06). " @@ -665,6 +673,13 @@ def _build_dataset_template(self, file): attrs=dict(data_product=self._prod), ) return is2ds + + def _get_product_and_version(filepath): + # TODO either persist this info or remove 'version', since it isn't necessary right now + with h5py.File(filepath, 'r') as f: + product = f['METADATA']['DatasetIdentification'].attrs['shortName'].decode() + version = f['METADATA']['DatasetIdentification'].attrs['VersionID'].decode() + return product, version def _read_single_grp(self, file, grp_path): """ @@ -684,25 +699,10 @@ def _read_single_grp(self, file, grp_path): Xarray dataset with the specified group. """ - - try: - grpcat = is2cat.build_catalog( - file, self._pattern, self._source_type, grp_paths=grp_path - ) - ds = grpcat[self._source_type].read() - - # NOTE: could also do this with h5py, but then would have to read in each variable in the group separately - except ValueError: - grpcat = is2cat.build_catalog( - file, - self._pattern, - self._source_type, - grp_paths=grp_path, - extra_engine_kwargs={"phony_dims": "access"}, - ) - ds = grpcat[self._source_type].read() - - return ds + # I think this would fail if a group that has too high of a level of nesting + # is given. Consider this. + # TODO: update docstring + return xr.open_dataset(file, group=grp_path) def _build_single_file_dataset(self, file, groups_list): """ @@ -722,8 +722,11 @@ def _build_single_file_dataset(self, file, groups_list): ------- Xarray Dataset """ - - file_product = self._read_single_grp(file, "/").attrs["identifier_product_type"] + # why do we do get the product twice? is it important to us that the user tells us + # correctly their product? Do we trust the metadata or the filename more? + # Also revisit the semantics of this. Not sure if it makes semantic sense for this + # to be a class method + file_product, _ = Read._get_product_and_version(file) assert ( file_product == self._prod ), "Your product specification does not match the product specification within your files." From 24f6a42df3b06bf93e2f16e90434a70aef5d4b1c Mon Sep 17 00:00:00 2001 From: Rachel Wegener Date: Tue, 29 Aug 2023 15:58:03 +0000 Subject: [PATCH 02/55] delete is2cat and references --- icepyx/core/is2cat.py | 178 ------------------------------------------ icepyx/core/read.py | 78 +++++++++++------- 2 files changed, 51 insertions(+), 205 deletions(-) delete mode 100644 icepyx/core/is2cat.py diff --git a/icepyx/core/is2cat.py b/icepyx/core/is2cat.py deleted file mode 100644 index f4e66a7bf..000000000 --- a/icepyx/core/is2cat.py +++ /dev/null @@ -1,178 +0,0 @@ -from intake.catalog import Catalog - -# Need to post on intake's page to see if this would be a useful contribution... -# https://github.com/intake/intake/blob/0.6.4/intake/source/utils.py#L216 -def _pattern_to_glob(pattern): - """ - Adapted from intake.source.utils.path_to_glob to convert a path as pattern into a glob style path - that uses the pattern's indicated number of '?' instead of '*' where an int was specified. - - Returns pattern if pattern is not a string. - - Parameters - ---------- - pattern : str - Path as pattern optionally containing format_strings - - Returns - ------- - glob_path : str - Path with int format strings replaced with the proper number of '?' and '*' otherwise. - - Examples - -------- - >>> _pattern_to_glob('{year}/{month}/{day}.csv') - '*/*/*.csv' - >>> _pattern_to_glob('{year:4}/{month:2}/{day:2}.csv') - '????/??/??.csv' - >>> _pattern_to_glob('data/{year:4}{month:02}{day:02}.csv') - 'data/????????.csv' - >>> _pattern_to_glob('data/*.csv') - 'data/*.csv' - """ - from string import Formatter - - if not isinstance(pattern, str): - return pattern - - fmt = Formatter() - glob_path = "" - # prev_field_name = None - for literal_text, field_name, format_specs, _ in fmt.parse(format_string=pattern): - glob_path += literal_text - if field_name and (glob_path != "*"): - try: - glob_path += "?" * int(format_specs) - except ValueError: - glob_path += "*" - # alternatively, you could use bits=utils._get_parts_of_format_string(resolved_string, literal_texts, format_specs) - # and then use len(bits[i]) to get the length of each format_spec - # print(glob_path) - return glob_path - - -def build_catalog( - data_source, - path_pattern, - source_type, - grp_paths=None, - grp_path_params=None, - extra_engine_kwargs=None, - **kwargs -): - """ - Build a general Intake catalog for reading in ICESat-2 data. - This function is used by the read class object to create catalogs from lists of ICESat-2 variables. - - Parameters - ---------- - data_source : string - A string with a full file path or full directory path to ICESat-2 hdf5 (.h5) format files. - Files within a directory must have a consistent filename pattern that includes the "ATL??" data product name. - Files must all be within a single directory. - - path_pattern : string - String that shows the filename pattern as required for Intake's path_as_pattern argument. - - source_type : string - String to use as the Local Catalog Entry name. - - grp_paths : str, default None - Variable paths to load. - Can include general parameter names, which must be contained within double curly brackets and further - described in `grp_path_params`. - Default list based on data product of provided files. - If multiple data products are included in the files, the default list will be for the product of the first file. - This may result in errors during read-in if all files do not have the same variable paths. - - grp_path_params : [dict], default None - List of dictionaries with a keyword for each parameter name specified in the `grp_paths` string. - Each parameter keyword should contain a dictionary with the acceptable keyword-value pairs for the driver being used. - - **kwargs : - Keyword arguments to be passed through to `intake.catalog.Catalog.from_dict()`. - Keywords needed to override default inputs include: - - `source_args_dict` # highest level source information; keys include: "urlpath", "path_as_pattern", driver-specific ("xarray_kwargs" is default) - - `metadata_dict` - - `source_dict` # individual source entry information (default is supplied by data object; "name", "description", "driver", "args") - - `defaults_dict` # catalog "name", "description", "metadata", "entries", etc. - - Returns - ------- - intake.catalog.Catalog object - - See Also - -------- - read.Read - - """ - from intake.catalog.local import LocalCatalogEntry, UserParameter - import intake_xarray - - import icepyx.core.APIformatting as apifmt - - assert ( - grp_paths - ), "You must enter a variable path or you will not be able to read in any data." - - # generalize this/make it so the [engine] values can be entered as kwargs... - engine_key = "xarray_kwargs" - xarray_kwargs_dict = {"engine": "h5netcdf", "group": grp_paths} - if extra_engine_kwargs: - for key in extra_engine_kwargs.keys(): - xarray_kwargs_dict[key] = extra_engine_kwargs[key] - - source_args_dict = { - "urlpath": data_source, - "path_as_pattern": path_pattern, - engine_key: xarray_kwargs_dict, - } - - metadata_dict = {"version": 1} - - source_dict = { - "name": source_type, - "description": "", - "driver": "intake_xarray.netcdf.NetCDFSource", # NOTE: this must be a string or the catalog cannot be imported after saving - "args": source_args_dict, - } - - if grp_path_params: - source_dict = apifmt.combine_params( - source_dict, - {"parameters": [UserParameter(**params) for params in grp_path_params]}, - ) - - # NOTE: LocalCatalogEntry has some required positional args (name, description, driver) - # I tried doing this generally with *source_dict after the positional args (instead of as part of the if) - # but apparently I don't quite get something about passing dicts with * and ** and couldn't make it work - local_cat_source = { - source_type: LocalCatalogEntry( - name=source_dict.pop("name"), - description=source_dict.pop("description"), - driver=source_dict.pop("driver"), - parameters=source_dict.pop("parameters"), - args=source_dict.pop("args"), - ) - } - - else: - local_cat_source = { - source_type: LocalCatalogEntry( - name=source_dict.pop("name"), - description=source_dict.pop("description"), - driver=source_dict.pop("driver"), - args=source_dict.pop("args"), - ) - } - - defaults_dict = { - "name": "IS2-hdf5-icepyx-intake-catalog", - "description": "an icepyx-generated catalog for creating local ICESat-2 intake entries", - "metadata": metadata_dict, - "entries": local_cat_source, - } - - build_cat_dict = apifmt.combine_params(defaults_dict, kwargs) - - return Catalog.from_dict(**build_cat_dict) diff --git a/icepyx/core/read.py b/icepyx/core/read.py index 5860e32dc..c4c10b296 100644 --- a/icepyx/core/read.py +++ b/icepyx/core/read.py @@ -6,7 +6,6 @@ import xarray as xr import h5py -import icepyx.core.is2cat as is2cat import icepyx.core.is2ref as is2ref from icepyx.core.variables import Variables as Variables from icepyx.core.variables import list_of_dict_vals @@ -207,6 +206,56 @@ def _run_fast_scandir(dir, fn_glob): return subfolders, files +# Need to post on intake's page to see if this would be a useful contribution... +# https://github.com/intake/intake/blob/0.6.4/intake/source/utils.py#L216 +def _pattern_to_glob(pattern): + """ + Adapted from intake.source.utils.path_to_glob to convert a path as pattern into a glob style path + that uses the pattern's indicated number of '?' instead of '*' where an int was specified. + + Returns pattern if pattern is not a string. + + Parameters + ---------- + pattern : str + Path as pattern optionally containing format_strings + + Returns + ------- + glob_path : str + Path with int format strings replaced with the proper number of '?' and '*' otherwise. + + Examples + -------- + >>> _pattern_to_glob('{year}/{month}/{day}.csv') + '*/*/*.csv' + >>> _pattern_to_glob('{year:4}/{month:2}/{day:2}.csv') + '????/??/??.csv' + >>> _pattern_to_glob('data/{year:4}{month:02}{day:02}.csv') + 'data/????????.csv' + >>> _pattern_to_glob('data/*.csv') + 'data/*.csv' + """ + from string import Formatter + + if not isinstance(pattern, str): + return pattern + + fmt = Formatter() + glob_path = "" + # prev_field_name = None + for literal_text, field_name, format_specs, _ in fmt.parse(format_string=pattern): + glob_path += literal_text + if field_name and (glob_path != "*"): + try: + glob_path += "?" * int(format_specs) + except ValueError: + glob_path += "*" + # alternatively, you could use bits=utils._get_parts_of_format_string(resolved_string, literal_texts, format_specs) + # and then use len(bits[i]) to get the length of each format_spec + # print(glob_path) + return glob_path + # To do: test this class and functions therein class Read: @@ -322,28 +371,6 @@ def __init__( # ---------------------------------------------------------------------- # Properties - @property - def is2catalog(self): - """ - Print a generic ICESat-2 Intake catalog. - This catalog does not specify groups, so it cannot be used to read in data. - - """ - if not hasattr(self, "_is2catalog") and hasattr(self, "_catalog_path"): - from intake import open_catalog - - self._is2catalog = open_catalog(self._catalog_path) - - else: - self._is2catalog = is2cat.build_catalog( - self.data_source, - self._pattern, - self._source_type, - grp_paths="/paths/to/variables", - ) - - return self._is2catalog - # I cut and pasted this directly out of the Query class - going to need to reconcile the _source/file stuff there @property @@ -378,7 +405,7 @@ def _check_source_for_pattern(source, filename_pattern): """ Check that the entered data source contains files that match the input filename_pattern """ - glob_pattern = is2cat._pattern_to_glob(filename_pattern) + glob_pattern = _pattern_to_glob(filename_pattern) if os.path.isdir(source): _, filelist = _run_fast_scandir(source, glob_pattern) @@ -609,9 +636,6 @@ def load(self): All items in the wanted variables list will be loaded from the files into memory. If you do not provide a wanted variables list, a default one will be created for you. - - If you would like to use the Intake catalog you provided to read in a single data variable, - simply call Intake's `read()` function on the is2catalog property (e.g. `reader.is2catalog.read()`). """ # todo: From b13b8473644589445c79144f6e53a21b7447c383 Mon Sep 17 00:00:00 2001 From: Rachel Wegener Date: Wed, 30 Aug 2023 20:06:14 +0000 Subject: [PATCH 03/55] remove extra comments --- icepyx/core/read.py | 33 ++++++--------------------------- 1 file changed, 6 insertions(+), 27 deletions(-) diff --git a/icepyx/core/read.py b/icepyx/core/read.py index c4c10b296..f272a3aa4 100644 --- a/icepyx/core/read.py +++ b/icepyx/core/read.py @@ -305,7 +305,6 @@ def __init__( data_source=None, product=None, filename_pattern="ATL{product:2}_{datetime:%Y%m%d%H%M%S}_{rgt:4}{cycle:2}{orbitsegment:2}_{version:3}_{revision:2}.h5", - catalog=None, out_obj_type=None, # xr.Dataset, ): # Note: maybe just don't add default values, so that Python enforces their existence? @@ -322,15 +321,9 @@ def __init__( else: self._prod = is2ref._validate_product(product) - # TODO delete? seems like it just validates the pattern - # Does Read accept a directory right now? Why would there be multiple files in the list? - # seems like yes, it does accept a directory - # does it check, then, that all the files have the same version and product? pattern_ck, filelist = Read._check_source_for_pattern( data_source, filename_pattern ) - print('pattern_ck', pattern_ck) - print('filelist', filelist) assert pattern_ck # Note: need to check if this works for subset and non-subset NSIDC files (processed_ prepends the former) self._pattern = filename_pattern @@ -338,8 +331,7 @@ def __init__( # this is a first pass at getting rid of mixed product types and warning the user. # it takes an approach assuming the product name is in the filename, but needs reworking if we let multiple products be loaded # one way to handle this would be bring in the product info during the loading step and fill in product there instead of requiring it from the user - filtered_filelist = [file for file in filelist if self._prod in Read._get_product_and_version(file)] - print('filtered', filtered_filelist) + filtered_filelist = [file for file in filelist if self._prod in file] if len(filtered_filelist) == 0: warnings.warn( "Your filenames do not contain a product identifier (e.g. ATL06). " @@ -355,11 +347,6 @@ def __init__( self._filelist = filelist # after validation, use the notebook code and code outline to start implementing the rest of the class - if catalog is not None: - assert os.path.isfile( - catalog - ), f"Your catalog path '{catalog}' does not point to a valid file." - self._catalog_path = catalog if out_obj_type is not None: print( @@ -697,17 +684,10 @@ def _build_dataset_template(self, file): attrs=dict(data_product=self._prod), ) return is2ds - - def _get_product_and_version(filepath): - # TODO either persist this info or remove 'version', since it isn't necessary right now - with h5py.File(filepath, 'r') as f: - product = f['METADATA']['DatasetIdentification'].attrs['shortName'].decode() - version = f['METADATA']['DatasetIdentification'].attrs['VersionID'].decode() - return product, version def _read_single_grp(self, file, grp_path): """ - For a given file and variable group path, construct an Intake catalog and use it to read in the data. + For a given file and variable group path, construct an an xarray Dataset. Parameters ---------- @@ -723,10 +703,9 @@ def _read_single_grp(self, file, grp_path): Xarray dataset with the specified group. """ - # I think this would fail if a group that has too high of a level of nesting - # is given. Consider this. - # TODO: update docstring - return xr.open_dataset(file, group=grp_path) + + return xr.open_dataset(file, group=grp_path, engine='h5netcdf', + backend_kwargs={'phony_dims': 'access'}) def _build_single_file_dataset(self, file, groups_list): """ @@ -750,7 +729,7 @@ def _build_single_file_dataset(self, file, groups_list): # correctly their product? Do we trust the metadata or the filename more? # Also revisit the semantics of this. Not sure if it makes semantic sense for this # to be a class method - file_product, _ = Read._get_product_and_version(file) + file_product = self._read_single_grp(file, "/").attrs["identifier_product_type"] assert ( file_product == self._prod ), "Your product specification does not match the product specification within your files." From 0779b8017a98ac3f2e683fddcaaebc0ca413ceac Mon Sep 17 00:00:00 2001 From: Rachel Wegener Date: Wed, 30 Aug 2023 20:23:45 +0000 Subject: [PATCH 04/55] update doc strings --- icepyx/core/read.py | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/icepyx/core/read.py b/icepyx/core/read.py index f272a3aa4..d3ca0d82a 100644 --- a/icepyx/core/read.py +++ b/icepyx/core/read.py @@ -4,7 +4,6 @@ import numpy as np import xarray as xr -import h5py import icepyx.core.is2ref as is2ref from icepyx.core.variables import Variables as Variables @@ -260,7 +259,7 @@ def _pattern_to_glob(pattern): # To do: test this class and functions therein class Read: """ - Data object to create and use Intake catalogs to read ICESat-2 data into the specified formats. + Data object to read ICESat-2 data into the specified formats. Provides flexiblity for reading nested hdf5 files into common analysis formats. Parameters @@ -279,10 +278,6 @@ class Read: The default describes files downloaded directly from NSIDC (subsetted and non-subsetted) for most products (e.g. ATL06). The ATL11 filename pattern from NSIDC is: 'ATL{product:2}_{rgt:4}{orbitsegment:2}_{cycles:4}_{version:3}_{revision:2}.h5'. - catalog : string, default None - Full path to an Intake catalog for reading in data. - If you still need to create a catalog, leave as default. - out_obj_type : object, default xarray.Dataset The desired format for the data to be read in. Currently, only xarray.Dataset objects (default) are available. @@ -307,7 +302,6 @@ def __init__( filename_pattern="ATL{product:2}_{datetime:%Y%m%d%H%M%S}_{rgt:4}{cycle:2}{orbitsegment:2}_{version:3}_{revision:2}.h5", out_obj_type=None, # xr.Dataset, ): - # Note: maybe just don't add default values, so that Python enforces their existence? if data_source is None: raise ValueError("Please provide a data source.") else: @@ -320,7 +314,6 @@ def __init__( ) else: self._prod = is2ref._validate_product(product) - pattern_ck, filelist = Read._check_source_for_pattern( data_source, filename_pattern ) @@ -725,10 +718,6 @@ def _build_single_file_dataset(self, file, groups_list): ------- Xarray Dataset """ - # why do we do get the product twice? is it important to us that the user tells us - # correctly their product? Do we trust the metadata or the filename more? - # Also revisit the semantics of this. Not sure if it makes semantic sense for this - # to be a class method file_product = self._read_single_grp(file, "/").attrs["identifier_product_type"] assert ( file_product == self._prod From 1cfbf7208a8de80a1539d25e0c536f6831330c25 Mon Sep 17 00:00:00 2001 From: Rachel Wegener Date: Wed, 30 Aug 2023 20:24:00 +0000 Subject: [PATCH 05/55] update tests --- icepyx/tests/test_read.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/icepyx/tests/test_read.py b/icepyx/tests/test_read.py index 9748ae992..018435968 100644 --- a/icepyx/tests/test_read.py +++ b/icepyx/tests/test_read.py @@ -63,7 +63,6 @@ def test_validate_source_str_not_a_dir_or_file(): ), sorted( [ - "./icepyx/core/is2cat.py", "./icepyx/core/is2ref.py", "./icepyx/tests/is2class_query.py", ] @@ -73,7 +72,7 @@ def test_validate_source_str_not_a_dir_or_file(): ( "./icepyx/core", "is2*.py", - ([], ["./icepyx/core/is2cat.py", "./icepyx/core/is2ref.py"]), + ([], ["./icepyx/core/is2ref.py"]), ), ( "./icepyx", From de61d87e21ccb4e1feb0e289b83a66ffc1690566 Mon Sep 17 00:00:00 2001 From: Rachel Wegener Date: Wed, 30 Aug 2023 20:43:07 +0000 Subject: [PATCH 06/55] update documentation for removing intake --- .../example_notebooks/IS2_data_read-in.ipynb | 2268 +++++++++++++++-- doc/source/user_guide/documentation/read.rst | 1 - 2 files changed, 2052 insertions(+), 217 deletions(-) diff --git a/doc/source/example_notebooks/IS2_data_read-in.ipynb b/doc/source/example_notebooks/IS2_data_read-in.ipynb index dc9d8ed31..b8697b1d7 100644 --- a/doc/source/example_notebooks/IS2_data_read-in.ipynb +++ b/doc/source/example_notebooks/IS2_data_read-in.ipynb @@ -3,7 +3,9 @@ { "cell_type": "markdown", "id": "552e9ef9", - "metadata": {}, + "metadata": { + "user_expressions": [] + }, "source": [ "# Reading ICESat-2 Data in for Analysis\n", "This notebook ({nb-download}`download `) illustrates the use of icepyx for reading ICESat-2 data files, loading them into a data object.\n", @@ -36,7 +38,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 4, "id": "2b74b672", "metadata": {}, "outputs": [], @@ -47,7 +49,9 @@ { "cell_type": "markdown", "id": "1ffb9a0c", - "metadata": {}, + "metadata": { + "user_expressions": [] + }, "source": [ "---------------------------------\n", "\n", @@ -57,10 +61,18 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 5, "id": "c4390195", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "You have 6 files matching the filename pattern to be read in.\n" + ] + } + ], "source": [ "path_root = '/full/path/to/your/data/'\n", "pattern = \"processed_ATL{product:2}_{datetime:%Y%m%d%H%M%S}_{rgt:4}{cycle:2}{orbitsegment:2}_{version:3}_{revision:2}.h5\"\n", @@ -69,7 +81,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 6, "id": "2f46029d", "metadata": {}, "outputs": [], @@ -79,10 +91,603 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 7, "id": "c0439388", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:49: UserWarning: Converting non-nanosecond precision datetime values to nanosecond precision. This behavior can eventually be relaxed in xarray, as it is an artifact from pandas which is now beginning to support non-nanosecond precision values. This warning is caused by passing non-nanosecond np.datetime64 or np.timedelta64 values to the DataArray or Variable constructor; it can be silenced by converting the values to nanosecond precision ahead of time.\n", + " df.update({keyword: df[keyword].str[:-1].astype(np.datetime64)})\n", + "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:49: UserWarning: Converting non-nanosecond precision datetime values to nanosecond precision. This behavior can eventually be relaxed in xarray, as it is an artifact from pandas which is now beginning to support non-nanosecond precision values. This warning is caused by passing non-nanosecond np.datetime64 or np.timedelta64 values to the DataArray or Variable constructor; it can be silenced by converting the values to nanosecond precision ahead of time.\n", + " df.update({keyword: df[keyword].str[:-1].astype(np.datetime64)})\n", + "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:490: UserWarning: rename 'delta_time' to 'photon_idx' does not create an index anymore. Try using swap_dims instead or use set_index after rename to create an indexed coordinate.\n", + " .rename({\"delta_time\": \"photon_idx\"})\n", + "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:490: UserWarning: rename 'delta_time' to 'photon_idx' does not create an index anymore. Try using swap_dims instead or use set_index after rename to create an indexed coordinate.\n", + " .rename({\"delta_time\": \"photon_idx\"})\n", + "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:49: UserWarning: Converting non-nanosecond precision datetime values to nanosecond precision. This behavior can eventually be relaxed in xarray, as it is an artifact from pandas which is now beginning to support non-nanosecond precision values. This warning is caused by passing non-nanosecond np.datetime64 or np.timedelta64 values to the DataArray or Variable constructor; it can be silenced by converting the values to nanosecond precision ahead of time.\n", + " df.update({keyword: df[keyword].str[:-1].astype(np.datetime64)})\n", + "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:49: UserWarning: Converting non-nanosecond precision datetime values to nanosecond precision. This behavior can eventually be relaxed in xarray, as it is an artifact from pandas which is now beginning to support non-nanosecond precision values. This warning is caused by passing non-nanosecond np.datetime64 or np.timedelta64 values to the DataArray or Variable constructor; it can be silenced by converting the values to nanosecond precision ahead of time.\n", + " df.update({keyword: df[keyword].str[:-1].astype(np.datetime64)})\n", + "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:490: UserWarning: rename 'delta_time' to 'photon_idx' does not create an index anymore. Try using swap_dims instead or use set_index after rename to create an indexed coordinate.\n", + " .rename({\"delta_time\": \"photon_idx\"})\n", + "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:490: UserWarning: rename 'delta_time' to 'photon_idx' does not create an index anymore. Try using swap_dims instead or use set_index after rename to create an indexed coordinate.\n", + " .rename({\"delta_time\": \"photon_idx\"})\n", + "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:49: UserWarning: Converting non-nanosecond precision datetime values to nanosecond precision. This behavior can eventually be relaxed in xarray, as it is an artifact from pandas which is now beginning to support non-nanosecond precision values. This warning is caused by passing non-nanosecond np.datetime64 or np.timedelta64 values to the DataArray or Variable constructor; it can be silenced by converting the values to nanosecond precision ahead of time.\n", + " df.update({keyword: df[keyword].str[:-1].astype(np.datetime64)})\n", + "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:49: UserWarning: Converting non-nanosecond precision datetime values to nanosecond precision. This behavior can eventually be relaxed in xarray, as it is an artifact from pandas which is now beginning to support non-nanosecond precision values. This warning is caused by passing non-nanosecond np.datetime64 or np.timedelta64 values to the DataArray or Variable constructor; it can be silenced by converting the values to nanosecond precision ahead of time.\n", + " df.update({keyword: df[keyword].str[:-1].astype(np.datetime64)})\n", + "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:490: UserWarning: rename 'delta_time' to 'photon_idx' does not create an index anymore. Try using swap_dims instead or use set_index after rename to create an indexed coordinate.\n", + " .rename({\"delta_time\": \"photon_idx\"})\n", + "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:490: UserWarning: rename 'delta_time' to 'photon_idx' does not create an index anymore. Try using swap_dims instead or use set_index after rename to create an indexed coordinate.\n", + " .rename({\"delta_time\": \"photon_idx\"})\n", + "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:49: UserWarning: Converting non-nanosecond precision datetime values to nanosecond precision. This behavior can eventually be relaxed in xarray, as it is an artifact from pandas which is now beginning to support non-nanosecond precision values. This warning is caused by passing non-nanosecond np.datetime64 or np.timedelta64 values to the DataArray or Variable constructor; it can be silenced by converting the values to nanosecond precision ahead of time.\n", + " df.update({keyword: df[keyword].str[:-1].astype(np.datetime64)})\n", + "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:49: UserWarning: Converting non-nanosecond precision datetime values to nanosecond precision. This behavior can eventually be relaxed in xarray, as it is an artifact from pandas which is now beginning to support non-nanosecond precision values. This warning is caused by passing non-nanosecond np.datetime64 or np.timedelta64 values to the DataArray or Variable constructor; it can be silenced by converting the values to nanosecond precision ahead of time.\n", + " df.update({keyword: df[keyword].str[:-1].astype(np.datetime64)})\n", + "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:490: UserWarning: rename 'delta_time' to 'photon_idx' does not create an index anymore. Try using swap_dims instead or use set_index after rename to create an indexed coordinate.\n", + " .rename({\"delta_time\": \"photon_idx\"})\n", + "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:490: UserWarning: rename 'delta_time' to 'photon_idx' does not create an index anymore. Try using swap_dims instead or use set_index after rename to create an indexed coordinate.\n", + " .rename({\"delta_time\": \"photon_idx\"})\n", + "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:49: UserWarning: Converting non-nanosecond precision datetime values to nanosecond precision. This behavior can eventually be relaxed in xarray, as it is an artifact from pandas which is now beginning to support non-nanosecond precision values. This warning is caused by passing non-nanosecond np.datetime64 or np.timedelta64 values to the DataArray or Variable constructor; it can be silenced by converting the values to nanosecond precision ahead of time.\n", + " df.update({keyword: df[keyword].str[:-1].astype(np.datetime64)})\n", + "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:49: UserWarning: Converting non-nanosecond precision datetime values to nanosecond precision. This behavior can eventually be relaxed in xarray, as it is an artifact from pandas which is now beginning to support non-nanosecond precision values. This warning is caused by passing non-nanosecond np.datetime64 or np.timedelta64 values to the DataArray or Variable constructor; it can be silenced by converting the values to nanosecond precision ahead of time.\n", + " df.update({keyword: df[keyword].str[:-1].astype(np.datetime64)})\n", + "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:490: UserWarning: rename 'delta_time' to 'photon_idx' does not create an index anymore. Try using swap_dims instead or use set_index after rename to create an indexed coordinate.\n", + " .rename({\"delta_time\": \"photon_idx\"})\n", + "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:490: UserWarning: rename 'delta_time' to 'photon_idx' does not create an index anymore. Try using swap_dims instead or use set_index after rename to create an indexed coordinate.\n", + " .rename({\"delta_time\": \"photon_idx\"})\n", + "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:49: UserWarning: Converting non-nanosecond precision datetime values to nanosecond precision. This behavior can eventually be relaxed in xarray, as it is an artifact from pandas which is now beginning to support non-nanosecond precision values. This warning is caused by passing non-nanosecond np.datetime64 or np.timedelta64 values to the DataArray or Variable constructor; it can be silenced by converting the values to nanosecond precision ahead of time.\n", + " df.update({keyword: df[keyword].str[:-1].astype(np.datetime64)})\n", + "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:49: UserWarning: Converting non-nanosecond precision datetime values to nanosecond precision. This behavior can eventually be relaxed in xarray, as it is an artifact from pandas which is now beginning to support non-nanosecond precision values. This warning is caused by passing non-nanosecond np.datetime64 or np.timedelta64 values to the DataArray or Variable constructor; it can be silenced by converting the values to nanosecond precision ahead of time.\n", + " df.update({keyword: df[keyword].str[:-1].astype(np.datetime64)})\n", + "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:490: UserWarning: rename 'delta_time' to 'photon_idx' does not create an index anymore. Try using swap_dims instead or use set_index after rename to create an indexed coordinate.\n", + " .rename({\"delta_time\": \"photon_idx\"})\n", + "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:490: UserWarning: rename 'delta_time' to 'photon_idx' does not create an index anymore. Try using swap_dims instead or use set_index after rename to create an indexed coordinate.\n", + " .rename({\"delta_time\": \"photon_idx\"})\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
<xarray.Dataset>\n",
+       "Dimensions:              (photon_idx: 29027, spot: 2, gran_idx: 6)\n",
+       "Coordinates:\n",
+       "  * photon_idx           (photon_idx) int64 0 1 2 3 ... 29023 29024 29025 29026\n",
+       "  * spot                 (spot) uint8 2 5\n",
+       "  * gran_idx             (gran_idx) float64 8.49e+04 9.02e+04 ... 1.016e+05\n",
+       "    source_file          (gran_idx) <U72 '../../../../data/ATL06/processed_AT...\n",
+       "    delta_time           (gran_idx, photon_idx) datetime64[ns] 2019-02-22T01:...\n",
+       "Data variables:\n",
+       "    sc_orient            (gran_idx) int8 0 0 0 1 1 1\n",
+       "    cycle_number         (gran_idx) int8 2 2 2 5 5 5\n",
+       "    rgt                  (gran_idx) int16 849 902 910 986 1001 1016\n",
+       "    atlas_sdp_gps_epoch  (gran_idx) datetime64[ns] 2018-01-01T00:00:18 ... 20...\n",
+       "    data_start_utc       (gran_idx) datetime64[ns] 2019-02-22T01:03:44.199777...\n",
+       "    data_end_utc         (gran_idx) datetime64[ns] 2019-02-22T01:07:38.112326...\n",
+       "    h_li                 (spot, gran_idx, photon_idx) float32 nan nan ... nan\n",
+       "    latitude             (spot, gran_idx, photon_idx) float64 nan nan ... nan\n",
+       "    longitude            (spot, gran_idx, photon_idx) float64 nan nan ... nan\n",
+       "    gt                   (gran_idx, spot) <U4 'gt3r' 'gt1l' ... 'gt1l' 'gt3r'\n",
+       "Attributes:\n",
+       "    data_product:  ATL06\n",
+       "    Description:   The land_ice_height group contains the primary set of deri...\n",
+       "    data_rate:     Data within this group are sparse.  Data values are provid...
" + ], + "text/plain": [ + "\n", + "Dimensions: (photon_idx: 29027, spot: 2, gran_idx: 6)\n", + "Coordinates:\n", + " * photon_idx (photon_idx) int64 0 1 2 3 ... 29023 29024 29025 29026\n", + " * spot (spot) uint8 2 5\n", + " * gran_idx (gran_idx) float64 8.49e+04 9.02e+04 ... 1.016e+05\n", + " source_file (gran_idx) " + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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p6cDkJKSwvTp0x0u16GA59ixY7z44os8/PDDJCQkALBq1SoOHjxoVzlvvfUWUVFRLFiwgNatW1OzZk3uuusu6tSpY83zwgsvcPfdd/P222/TvHlzateuTe/evQkNDXWk6kIIIYT7lXB31o0yMjL48ssveeyxx2x2I9+wYQOhoaHUq1ePESNGWD/jAaKjo8nMzKR79+7WtMjISBo3bsyWLVucq9B1Xn/9dT7++GM++eQTm1WW27dvz+7dux0u1+6AZ+PGjTRp0oTt27fz/fffk5qaCsC+ffuYOnWqXWUtX76cVq1aMWDAAEJDQ2nevDmffPKJ9byu6/z000/Uq1ePHj16EBoaSps2bVi2bFm+Zaanp5OcnGxzCCGEEKWKi8bwKKVyfebltIgUZNmyZVy6dInhw4db03r16sXixYv59ddfmTlzJjt37uTOO++0lhcfH4+Pjw+VK1e2KSssLIz4+Hhn/ho2Dh06RMeOHXOlBwUFcenSJYfLtTvgef7553n99ddZu3YtPj4+1vQuXbqwdetWu8r6559/+Oijj6hbty6rV6/mqaeeYsyYMSxatAiAhIQEUlNTefPNN+nZsydr1qzhvvvuo3///rmWm84xY8YMTCaT9YiKirL3EoUQQogyIT4+3uYzz2QyMWPGjEIfN3/+fHr16kVkZKQ17cEHH6R37940btyYe+65h5UrV3L48GF++umnAstSStm0EjkrIiKCo0eP5krfvHkztWvXdrhcu6el79+/n6+++ipXetWqVblw4YJdZem6TqtWrax9cs2bN+fgwYN89NFHDB061LrgYb9+/Rg3bhwAzZo1Y8uWLXz88cc2/Y45pkyZwvjx462/JycnS9AjhBCiVHHF1hKagvDwcA4dOmST7uvrW+DjTpw4wbp16/j+++8LzBcREUGNGjU4cuQIYHmujIwMEhMTbVp5EhISaN++vYNXkdvIkSN59tln+eyzz9A0jdOnT7N161YmTpzIyy+/7HC5drfwVKpUiTNnzuRK37NnDzfddJNdZUVERNCoUSObtIYNG1pnYIWEhODl5VVgnhv5+voSFBRkcwghhBCljgvG8Gialuszr7CAZ8GCBYSGhtK7d+8C8124cIGTJ08SEREBQMuWLfH29rbO7gI4c+YMBw4ccGnAM3nyZO699166dOlCamoqHTt25IknnmDkyJGMHj3a4XLt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vbs1LTIyksaNG7Nlyxan6lPcpIVHCJGLpnnn+bMQwkVctHmoUork5GSbZF9fX3x9fQt+qFKMHz+e22+/ncaNG1vTe/XqxYABA6hRowYxMTG89NJL3HnnnURHR+Pr60t8fDw+Pj5UrlzZprywsDDi4+OdvKDiJQGPEEIIUUbFx8djMpls0qZOncq0adMKfNzo0aPZt28fmzdvtkl/8MEHrT83btyYVq1aUaNGDX766Sf69++fb3llYY89CXiEEEKIEuaqrSXCw8NzzbQqrHXnmWeeYfny5fz2229Uq1atwLwRERHUqFGDI0eOABAeHk5GRgaJiYk2rTwJCQm0b1/ALM9SQMbwCCGEEO7g9MKDlnF3QUFBNkd+AY9SitGjR/P999/z66+/UqtWrUKreOHCBU6ePElERAQALVu2xNvbm7Vr11rznDlzhgMHDpT6gEdaeIQQQoiSplPiu6U//fTTfPXVV/zwww9UrFjROubGZDLh7+9Pamoq06ZN4/777yciIoLjx4/z73//m5CQEO677z5r3scff5wJEyZQpUoVgoODmThxIk2aNLHO2iqtJOARQgghyoGPPvoIgM6dO9ukL1iwgOHDh2M0Gtm/fz+LFi3i0qVLRERE0KVLF7755hsqVqxozT979my8vLwYOHAgaWlp3HXXXSxcuBCjsXTP5HR7wBMXF8dzzz3HypUrSUtLo169esyfP5+WLVuSmZnJiy++yM8//8w///yDyWSia9euvPnmm0RGRrq76kIIIYRDNFwzhsceqpD8/v7+rF69utBy/Pz8eP/993n//fften53c+sYnsTERDp06IC3tzcrV67kzz//ZObMmVSqVAmAK1eusHv3bl566SV2797N999/z+HDh+nbt687qy2EEEI4xyUbh8pmWvZwawvPW2+9RVRUFAsWLLCm1axZ0/qzyWSyGRgF8P7779O6dWtiY2OpXr16SVVVCCGEcB2F87udS7xjF7e28CxfvpxWrVoxYMAAQkNDad68OZ988kmBj0lKSkLTNGsrkBBCCCFEYdwa8Pzzzz989NFH1K1bl9WrV/PUU08xZswYFi1alGf+q1ev8vzzzzNo0CCCgoLyzJOenk5ycrLNIYQQQpQqimsztRw9pIXHLm7t0tJ1nVatWjF9+nQAmjdvzsGDB/noo48YOnSoTd7MzEweeughdF3nww8/zLfM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rb7++us8y1i8eLHy8fHJld61a1c1cuTIYrmWd999V7Vv397anx4TE1Ngl8mWLVsUoHbt2mVXfZQqmdfG3uvJ8Z///EcFBQXle96Vr01xXo873juuvBZ3v2/suZ4c586dU4cOHVJr1qxRHTp0UHfffbfSdT1Xue547xTXteQo6feNKN08uoVn9OjRhc4cqFmzZp7pbdu2BeDo0aNUqVLFmv7nn39y5513MmLEiHy/NRSkRYsWeHt7c+TIEVq0aGHXY115PeHh4Wzfvt0mT2JiIpmZmbm+weYIDw8nIyODxMREm29ECQkJtG/f3o4rKfq1vP7662zbti3XcvGtWrVi8ODBfP755zbpn376Kc2aNaNly5Z21QdK5rWx93pytG3bluTkZM6ePZvn6+PK16a4rsdd7x1XXou73zf2XE+OkJAQQkJCqFevHg0bNiQqKopt27bRrl07m8e4471TXNeSo6TfN6KUc3fEVVr9+OOPClAnTpywph04cECFhoaqSZMmOVzu/v37FaA2btzoimoW2Y3XkzP48vTp09Y8S5YsKdLgy2+++caadvr06WId4HfixAm1f/9+67F69WoFqG+//VadPHnSJm9KSoqqUKFCobN/8lMSr40913O9999/X/n5+amrV6/med4dr41SRb+esvDeKcq1lJX3TX5iY2MVoNavX2+TXhbeOzfK71quV1rfN8I9JOBRlqbcWbNmqT179qh//vlHffPNNyoyMlL17dvXmienKX7w4ME2Ux4TEhKseU6dOqXq16+vtm/frpRS6ujRo+qVV15RO3fuVDExMeqnn35SDRo0UM2bNy/WKY9FuZ6c6bV33XWX2r17t1q3bp2qVq2azfTaG69HKcsUzmrVqql169ap3bt3qzvvvLNEp3AW1GXy6aefKj8/P3Xx4sVc50rLa3OjvK5n+fLlat68eWr//v3q6NGj6pNPPlFBQUFqzJgx+V6PUu5/bfK7nrL03insWsrS+2b79u3q/fffV3v27FHHjx9Xv/76q7r99ttVnTp1cgUApf29U5RrKcvvG1EyJOBRSkVHR6s2bdook8mk/Pz8VP369dXUqVPV5cuXrXny62uuUaOGNU/ODTLnG0dsbKzq2LGjCg4OVj4+PqpOnTpqzJgx6sKFC26/HqUs32h79+6t/P39VXBwsBo9erTNjfDG61FKqbS0NDV69GgVHBys/P39VZ8+fVRsbGyxXs/1Cgp42rVrpwYNGlTg49z92uRXr+uvZ+XKlapZs2aqQoUKKiAgQDVu3FjNmTNHZWZm5ns9Srn/tbm+XtdfT1l67xR2LUqVnffNvn37VJcuXVRwcLDy9fVVNWvWVE899ZQ6depUrryl/b1TlGspy+8bUTJkt3QhhBBCeDxZh0cIIYQQHk8CHiGEEEJ4PAl4hBBCCOHxJOARQgghhMeTgEcIIYQQHk8CHiGEEEJ4PAl4hBBCCOHxJOARQgghhMeTgEeIQhw/fhxN09i7d2+xlK9pGsuWLXP48Rs2bEDTNDRN49577y0wb+fOnRk7dqzDzyUKlvM6VKpUyd1VEULcQAIeUaoNHz680A/x4hYVFcWZM2do3LgxcC3AuHTpklvrdaNDhw6xcOFCd1ejXMjv3+WZM2eYM2dOiddHCFE4CXiEKITRaCQ8PBwvLy93V6VAoaGhpaJlITMz091VcJvw8HBMJpO7qyGEyIMEPKJM27hxI61bt8bX15eIiAief/55srKyrOc7d+7MmDFjmDx5MsHBwYSHhzNt2jSbMv7++29uv/12/Pz8aNSoEevWrbPpZrq+S+v48eN06dIFgMqVK6NpGsOHDwegZs2aub7dN2vWzOb5jhw5QseOHa3PtXbt2lzXFBcXx4MPPkjlypWpUqUK/fr14/jx43b/bS5fvszQoUOpUKECERERzJw5M1eejIwMJk+ezE033URgYCBt2rRhw4YNNnk++eQToqKiCAgI4L777mPWrFk2gdW0adNo1qwZn332GbVr18bX1xelFElJSTz55JOEhoYSFBTEnXfeyR9//GFT9o8//kjLli3x8/Ojdu3avPLKKzav37Rp06hevTq+vr5ERkYyZsyYIl17Ydd14cIFHn74YapVq0ZAQABNmjTh66+/tinj22+/pUmTJvj7+1OlShW6du3K5cuXmTZtGp9//jk//PCDtQvrxr+ZEKL0Kd1fWYUoQFxcHHfffTfDhw9n0aJF/P3334wYMQI/Pz+bIOPzzz9n/PjxbN++na1btzJ8+HA6dOhAt27d0HWde++9l+rVq7N9+3ZSUlKYMGFCvs8ZFRXFd999x/3338+hQ4cICgrC39+/SPXVdZ3+/fsTEhLCtm3bSE5OzjWe5sqVK3Tp0oU77riD3377DS8vL15//XV69uzJvn378PHxKfLfZ9KkSaxfv56lS5cSHh7Ov//9b6Kjo2nWrJk1z6OPPsrx48dZsmQJkZGRLF26lJ49e7J//37q1q3L77//zlNPPcVbb71F3759WbduHS+99FKu5zp69Cj//e9/+e677zAajQD07t2b4OBgfv75Z0wmE3PnzuWuu+7i8OHDBAcHs3r1ah555BHee+897rjjDo4dO8aTTz4JwNSpU/n222+ZPXs2S5Ys4ZZbbiE+Pj5XwJSfwq7r6tWrtGzZkueee46goCB++uknhgwZQu3atWnTpg1nzpzh4Ycf5u233+a+++4jJSWFTZs2oZRi4sSJ/PXXXyQnJ7NgwQIAgoODi/y6CCHcxL2btQtRsGHDhql+/frlee7f//63ql+/vtJ13Zr2f//3f6pChQrKbDYrpZTq1KmTuv32220ed9ttt6nnnntOKaXUypUrlZeXlzpz5oz1/Nq1axWgli5dqpRSKiYmRgFqz549Siml1q9frwCVmJhoU26NGjXU7NmzbdKaNm2qpk6dqpRSavXq1cpoNKqTJ09az69cudLmuebPn5/rmtLT05W/v79avXp1nn+HvOqTkpKifHx81JIlS6xpFy5cUP7+/urZZ59VSil19OhRpWmaiouLsynvrrvuUlOmTFFKKfXggw+q3r1725wfPHiwMplM1t+nTp2qvL29VUJCgjXtl19+UUFBQerq1as2j61Tp46aO3euUkqpO+64Q02fPt3m/BdffKEiIiKUUkrNnDlT1atXT2VkZOR53fkpynXl5e6771YTJkxQSikVHR2tAHX8+PE88xb073LBggU2fx8hROkgLTyizPrrr79o164dmqZZ0zp06EBqaiqnTp2ievXqANx66602j4uIiCAhIQGwDPSNiooiPDzcer5169bFVt/q1atTrVo1a1q7du1s8kRHR3P06FEqVqxok3716lWOHTtW5Oc6duwYGRkZNuUHBwdTv3596++7d+9GKUW9evVsHpuenk6VKlUAy9/nvvvusznfunVrVqxYYZNWo0YNqlatanMdqamp1nJypKWlWa8jOjqanTt38sYbb1jPm81mrl69ypUrVxgwYABz5syhdu3a9OzZk7vvvpt77rmn0LFURbkus9nMm2++yTfffENcXBzp6emkp6cTGBgIQNOmTbnrrrto0qQJPXr0oHv37jzwwANUrly5wOcWQpReEvCI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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ "ds.plot.scatter(x=\"longitude\", y=\"latitude\", hue=\"h_li\", vmin=-100, vmax=2000)" ] @@ -101,7 +727,9 @@ { "cell_type": "markdown", "id": "b8875936", - "metadata": {}, + "metadata": { + "user_expressions": [] + }, "source": [ "---------------------------------------\n", "## Key steps for loading (reading) ICESat-2 data\n", @@ -119,7 +747,9 @@ { "cell_type": "markdown", "id": "9bf6d38c", - "metadata": {}, + "metadata": { + "user_expressions": [] + }, "source": [ "### Step 0: Get some data if you haven't already\n", "Here are a few lines of code to get you set up with a few data files if you don't already have some on your local system." @@ -127,7 +757,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 9, "id": "63da2b3c", "metadata": {}, "outputs": [], @@ -138,10 +768,24 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 10, "id": "e6f7c047", "metadata": {}, - "outputs": [], + "outputs": [ + { + "ename": "AttributeError", + "evalue": "'Query' object has no attribute '_session'", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mAttributeError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[0;32mIn[10], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mregion_a\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mdownload_granules\u001b[49m\u001b[43m(\u001b[49m\u001b[43mpath\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mpath_root\u001b[49m\u001b[43m)\u001b[49m\n", + "File \u001b[0;32m~/envs/general/lib/python3.11/site-packages/icepyx/core/query.py:1129\u001b[0m, in \u001b[0;36mQuery.download_granules\u001b[0;34m(self, path, verbose, subset, restart, **kwargs)\u001b[0m\n\u001b[1;32m 1124\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m 1125\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m (\n\u001b[1;32m 1126\u001b[0m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28mhasattr\u001b[39m(\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_granules, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124morderIDs\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[1;32m 1127\u001b[0m \u001b[38;5;129;01mor\u001b[39;00m \u001b[38;5;28mlen\u001b[39m(\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_granules\u001b[38;5;241m.\u001b[39morderIDs) \u001b[38;5;241m==\u001b[39m \u001b[38;5;241m0\u001b[39m\n\u001b[1;32m 1128\u001b[0m ):\n\u001b[0;32m-> 1129\u001b[0m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43morder_granules\u001b[49m\u001b[43m(\u001b[49m\u001b[43mverbose\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mverbose\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43msubset\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43msubset\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 1131\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_granules\u001b[38;5;241m.\u001b[39mdownload(verbose, path, session\u001b[38;5;241m=\u001b[39m\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_session, restart\u001b[38;5;241m=\u001b[39mrestart)\n", + "File \u001b[0;32m~/envs/general/lib/python3.11/site-packages/icepyx/core/query.py:1065\u001b[0m, in \u001b[0;36mQuery.order_granules\u001b[0;34m(self, verbose, subset, email, **kwargs)\u001b[0m\n\u001b[1;32m 1048\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_granules\u001b[38;5;241m.\u001b[39mplace_order(\n\u001b[1;32m 1049\u001b[0m tempCMRparams,\n\u001b[1;32m 1050\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mreqparams,\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 1055\u001b[0m geom_filepath\u001b[38;5;241m=\u001b[39m\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_spatial\u001b[38;5;241m.\u001b[39m_geom_file,\n\u001b[1;32m 1056\u001b[0m )\n\u001b[1;32m 1058\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m 1059\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_granules\u001b[38;5;241m.\u001b[39mplace_order(\n\u001b[1;32m 1060\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mCMRparams,\n\u001b[1;32m 1061\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mreqparams,\n\u001b[1;32m 1062\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39msubsetparams(\u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs),\n\u001b[1;32m 1063\u001b[0m verbose,\n\u001b[1;32m 1064\u001b[0m subset,\n\u001b[0;32m-> 1065\u001b[0m session\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_session\u001b[49m,\n\u001b[1;32m 1066\u001b[0m geom_filepath\u001b[38;5;241m=\u001b[39m\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_spatial\u001b[38;5;241m.\u001b[39m_geom_file,\n\u001b[1;32m 1067\u001b[0m )\n", + "\u001b[0;31mAttributeError\u001b[0m: 'Query' object has no attribute '_session'" + ] + } + ], "source": [ "region_a.download_granules(path=path_root)" ] @@ -182,7 +826,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 11, "id": "9cde6679", "metadata": {}, "outputs": [], @@ -192,7 +836,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 13, "id": "8b6edf0c", "metadata": {}, "outputs": [], @@ -202,7 +846,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 14, "id": "e683ebf7", "metadata": {}, "outputs": [], @@ -213,7 +857,9 @@ { "cell_type": "markdown", "id": "92743496", - "metadata": {}, + "metadata": { + "user_expressions": [] + }, "source": [ "### Step 2: Create a filename pattern for your data files\n", "\n", @@ -227,7 +873,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 15, "id": "7318abd0", "metadata": {}, "outputs": [], @@ -238,7 +884,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 16, "id": "f43e8664", "metadata": {}, "outputs": [], @@ -248,7 +894,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 17, "id": "992a77fb", "metadata": {}, "outputs": [], @@ -258,7 +904,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 18, "id": "6aec1a70", "metadata": {}, "outputs": [], @@ -269,7 +915,9 @@ { "cell_type": "markdown", "id": "4275b04c", - "metadata": {}, + "metadata": { + "user_expressions": [] + }, "source": [ "### Step 3: Create an icepyx read object\n", "\n", @@ -277,29 +925,52 @@ "- `path` = a string with the full file path or full directory path to your hdf5 (.h5) format files.\n", "- `product` = the data product you're working with, also known as the \"short name\".\n", "\n", - "The `Read` object also accepts two optional keyword inputs:\n", - "- `pattern` = a formatted string indicating the filename pattern required for Intake's path_as_pattern argument.\n", - "- `catalog` = a string with the full path to an Intake catalog, for users who wish to use their own catalog (note this may have unintended consequenses if multiple granules are being combined)." + "The `Read` object also accepts the optional keyword input:\n", + "- `pattern` = a formatted string indicating the filename pattern required for Intake's path_as_pattern argument." ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 19, "id": "39bd7eb8", "metadata": { "scrolled": true }, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "You have 6 files matching the filename pattern to be read in.\n" + ] + } + ], "source": [ "reader = ipx.Read(data_source=path_root, product=\"ATL06\", filename_pattern=pattern) # or ipx.Read(filepath, \"ATLXX\") if your filenames match the default pattern" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 20, "id": "6c9ebc4a", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "['../../../../data/ATL06/processed_ATL06_20190226005526_09100205_006_02.h5',\n", + " '../../../../data/ATL06/processed_ATL06_20191201105502_10010505_006_01.h5',\n", + " '../../../../data/ATL06/processed_ATL06_20190225121032_09020203_006_02.h5',\n", + " '../../../../data/ATL06/processed_ATL06_20190222010344_08490205_006_02.h5',\n", + " '../../../../data/ATL06/processed_ATL06_20191130112041_09860505_006_01.h5',\n", + " '../../../../data/ATL06/processed_ATL06_20191202102922_10160505_006_01.h5']" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "reader._filelist" ] @@ -307,7 +978,9 @@ { "cell_type": "markdown", "id": "da8d8024", - "metadata": {}, + "metadata": { + "user_expressions": [] + }, "source": [ "### Step 4: Specify variables to be read in\n", "\n", @@ -320,12 +993,616 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 21, "id": "18f65f67", "metadata": { "scrolled": true }, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "['ancillary_data/atlas_sdp_gps_epoch',\n", + " 'ancillary_data/control',\n", + " 'ancillary_data/data_end_utc',\n", + " 'ancillary_data/data_start_utc',\n", + " 'ancillary_data/end_cycle',\n", + " 'ancillary_data/end_delta_time',\n", + " 'ancillary_data/end_geoseg',\n", + " 'ancillary_data/end_gpssow',\n", + " 'ancillary_data/end_gpsweek',\n", + " 'ancillary_data/end_orbit',\n", + " 'ancillary_data/end_region',\n", + " 'ancillary_data/end_rgt',\n", + " 'ancillary_data/granule_end_utc',\n", + " 'ancillary_data/granule_start_utc',\n", + " 'ancillary_data/land_ice/dt_hist',\n", + " 'ancillary_data/land_ice/fit_maxiter',\n", + " 'ancillary_data/land_ice/fpb_maxiter',\n", + " 'ancillary_data/land_ice/max_res_ids',\n", + " 'ancillary_data/land_ice/min_dist',\n", + " 'ancillary_data/land_ice/min_gain_th',\n", + " 'ancillary_data/land_ice/min_n_pe',\n", + " 'ancillary_data/land_ice/min_n_sel',\n", + " 'ancillary_data/land_ice/min_signal_conf',\n", + " 'ancillary_data/land_ice/n_hist',\n", + " 'ancillary_data/land_ice/n_sigmas',\n", + " 'ancillary_data/land_ice/nhist_bins',\n", + " 'ancillary_data/land_ice/proc_interval',\n", + " 'ancillary_data/land_ice/qs_lim_bsc',\n", + " 'ancillary_data/land_ice/qs_lim_hrs',\n", + " 'ancillary_data/land_ice/qs_lim_hsigma',\n", + " 'ancillary_data/land_ice/qs_lim_msw',\n", + " 'ancillary_data/land_ice/qs_lim_snr',\n", + " 'ancillary_data/land_ice/qs_lim_sss',\n", + " 'ancillary_data/land_ice/rbin_width',\n", + " 'ancillary_data/land_ice/sigma_beam',\n", + " 'ancillary_data/land_ice/sigma_tx',\n", + " 'ancillary_data/land_ice/t_dead',\n", + " 'ancillary_data/land_ice/txp_maxiter',\n", + " 'ancillary_data/qa_at_interval',\n", + " 'ancillary_data/release',\n", + " 'ancillary_data/start_cycle',\n", + " 'ancillary_data/start_delta_time',\n", + " 'ancillary_data/start_geoseg',\n", + " 'ancillary_data/start_gpssow',\n", + " 'ancillary_data/start_gpsweek',\n", + " 'ancillary_data/start_orbit',\n", + " 'ancillary_data/start_region',\n", + " 'ancillary_data/start_rgt',\n", + " 'ancillary_data/version',\n", + " 'gt1l/land_ice_segments/atl06_quality_summary',\n", + " 'gt1l/land_ice_segments/bias_correction/fpb_mean_corr',\n", + " 'gt1l/land_ice_segments/bias_correction/fpb_mean_corr_sigma',\n", + " 'gt1l/land_ice_segments/bias_correction/fpb_med_corr',\n", + " 'gt1l/land_ice_segments/bias_correction/fpb_med_corr_sigma',\n", + " 'gt1l/land_ice_segments/bias_correction/fpb_n_corr',\n", + " 'gt1l/land_ice_segments/bias_correction/med_r_fit',\n", + " 'gt1l/land_ice_segments/bias_correction/tx_mean_corr',\n", + " 'gt1l/land_ice_segments/bias_correction/tx_med_corr',\n", + " 'gt1l/land_ice_segments/delta_time',\n", + " 'gt1l/land_ice_segments/dem/dem_flag',\n", + " 'gt1l/land_ice_segments/dem/dem_h',\n", + " 'gt1l/land_ice_segments/dem/geoid_free2mean',\n", + " 'gt1l/land_ice_segments/dem/geoid_h',\n", + " 'gt1l/land_ice_segments/fit_statistics/dh_fit_dx',\n", + " 'gt1l/land_ice_segments/fit_statistics/dh_fit_dx_sigma',\n", + " 'gt1l/land_ice_segments/fit_statistics/dh_fit_dy',\n", + " 'gt1l/land_ice_segments/fit_statistics/h_expected_rms',\n", + " 'gt1l/land_ice_segments/fit_statistics/h_mean',\n", + " 'gt1l/land_ice_segments/fit_statistics/h_rms_misfit',\n", + " 'gt1l/land_ice_segments/fit_statistics/h_robust_sprd',\n", + " 'gt1l/land_ice_segments/fit_statistics/n_fit_photons',\n", + " 'gt1l/land_ice_segments/fit_statistics/n_seg_pulses',\n", + " 'gt1l/land_ice_segments/fit_statistics/sigma_h_mean',\n", + " 'gt1l/land_ice_segments/fit_statistics/signal_selection_source',\n", + " 'gt1l/land_ice_segments/fit_statistics/signal_selection_source_status',\n", + " 'gt1l/land_ice_segments/fit_statistics/snr',\n", + " 'gt1l/land_ice_segments/fit_statistics/snr_significance',\n", + " 'gt1l/land_ice_segments/fit_statistics/w_surface_window_final',\n", + " 'gt1l/land_ice_segments/geophysical/bckgrd',\n", + " 'gt1l/land_ice_segments/geophysical/bsnow_conf',\n", + " 'gt1l/land_ice_segments/geophysical/bsnow_h',\n", + " 'gt1l/land_ice_segments/geophysical/bsnow_od',\n", + " 'gt1l/land_ice_segments/geophysical/cloud_flg_asr',\n", + " 'gt1l/land_ice_segments/geophysical/cloud_flg_atm',\n", + " 'gt1l/land_ice_segments/geophysical/dac',\n", + " 'gt1l/land_ice_segments/geophysical/e_bckgrd',\n", + " 'gt1l/land_ice_segments/geophysical/layer_flag',\n", + " 'gt1l/land_ice_segments/geophysical/msw_flag',\n", + " 'gt1l/land_ice_segments/geophysical/neutat_delay_total',\n", + " 'gt1l/land_ice_segments/geophysical/r_eff',\n", + " 'gt1l/land_ice_segments/geophysical/solar_azimuth',\n", + " 'gt1l/land_ice_segments/geophysical/solar_elevation',\n", + " 'gt1l/land_ice_segments/geophysical/tide_earth',\n", + " 'gt1l/land_ice_segments/geophysical/tide_earth_free2mean',\n", + " 'gt1l/land_ice_segments/geophysical/tide_equilibrium',\n", + " 'gt1l/land_ice_segments/geophysical/tide_load',\n", + " 'gt1l/land_ice_segments/geophysical/tide_ocean',\n", + " 'gt1l/land_ice_segments/geophysical/tide_pole',\n", + " 'gt1l/land_ice_segments/ground_track/ref_azimuth',\n", + " 'gt1l/land_ice_segments/ground_track/ref_coelv',\n", + " 'gt1l/land_ice_segments/ground_track/seg_azimuth',\n", + " 'gt1l/land_ice_segments/ground_track/sigma_geo_at',\n", + " 'gt1l/land_ice_segments/ground_track/sigma_geo_r',\n", + " 'gt1l/land_ice_segments/ground_track/sigma_geo_xt',\n", + " 'gt1l/land_ice_segments/ground_track/x_atc',\n", + " 'gt1l/land_ice_segments/ground_track/y_atc',\n", + " 'gt1l/land_ice_segments/h_li',\n", + " 'gt1l/land_ice_segments/h_li_sigma',\n", + " 'gt1l/land_ice_segments/latitude',\n", + " 'gt1l/land_ice_segments/longitude',\n", + " 'gt1l/land_ice_segments/segment_id',\n", + " 'gt1l/land_ice_segments/sigma_geo_h',\n", + " 'gt1l/residual_histogram/bckgrd_per_m',\n", + " 'gt1l/residual_histogram/bin_top_h',\n", + " 'gt1l/residual_histogram/count',\n", + " 'gt1l/residual_histogram/delta_time',\n", + " 'gt1l/residual_histogram/ds_segment_id',\n", + " 'gt1l/residual_histogram/lat_mean',\n", + " 'gt1l/residual_histogram/lon_mean',\n", + " 'gt1l/residual_histogram/pulse_count',\n", + " 'gt1l/residual_histogram/segment_id_list',\n", + " 'gt1l/residual_histogram/x_atc_mean',\n", + " 'gt1l/segment_quality/delta_time',\n", + " 'gt1l/segment_quality/record_number',\n", + " 'gt1l/segment_quality/reference_pt_lat',\n", + " 'gt1l/segment_quality/reference_pt_lon',\n", + " 'gt1l/segment_quality/segment_id',\n", + " 'gt1l/segment_quality/signal_selection_source',\n", + " 'gt1l/segment_quality/signal_selection_status/signal_selection_status_all',\n", + " 'gt1l/segment_quality/signal_selection_status/signal_selection_status_backup',\n", + " 'gt1l/segment_quality/signal_selection_status/signal_selection_status_confident',\n", + " 'gt1r/land_ice_segments/atl06_quality_summary',\n", + " 'gt1r/land_ice_segments/bias_correction/fpb_mean_corr',\n", + " 'gt1r/land_ice_segments/bias_correction/fpb_mean_corr_sigma',\n", + " 'gt1r/land_ice_segments/bias_correction/fpb_med_corr',\n", + " 'gt1r/land_ice_segments/bias_correction/fpb_med_corr_sigma',\n", + " 'gt1r/land_ice_segments/bias_correction/fpb_n_corr',\n", + " 'gt1r/land_ice_segments/bias_correction/med_r_fit',\n", + " 'gt1r/land_ice_segments/bias_correction/tx_mean_corr',\n", + " 'gt1r/land_ice_segments/bias_correction/tx_med_corr',\n", + " 'gt1r/land_ice_segments/delta_time',\n", + " 'gt1r/land_ice_segments/dem/dem_flag',\n", + " 'gt1r/land_ice_segments/dem/dem_h',\n", + " 'gt1r/land_ice_segments/dem/geoid_free2mean',\n", + " 'gt1r/land_ice_segments/dem/geoid_h',\n", + " 'gt1r/land_ice_segments/fit_statistics/dh_fit_dx',\n", + " 'gt1r/land_ice_segments/fit_statistics/dh_fit_dx_sigma',\n", + " 'gt1r/land_ice_segments/fit_statistics/dh_fit_dy',\n", + " 'gt1r/land_ice_segments/fit_statistics/h_expected_rms',\n", + " 'gt1r/land_ice_segments/fit_statistics/h_mean',\n", + " 'gt1r/land_ice_segments/fit_statistics/h_rms_misfit',\n", + " 'gt1r/land_ice_segments/fit_statistics/h_robust_sprd',\n", + " 'gt1r/land_ice_segments/fit_statistics/n_fit_photons',\n", + " 'gt1r/land_ice_segments/fit_statistics/n_seg_pulses',\n", + " 'gt1r/land_ice_segments/fit_statistics/sigma_h_mean',\n", + " 'gt1r/land_ice_segments/fit_statistics/signal_selection_source',\n", + " 'gt1r/land_ice_segments/fit_statistics/signal_selection_source_status',\n", + " 'gt1r/land_ice_segments/fit_statistics/snr',\n", + " 'gt1r/land_ice_segments/fit_statistics/snr_significance',\n", + " 'gt1r/land_ice_segments/fit_statistics/w_surface_window_final',\n", + " 'gt1r/land_ice_segments/geophysical/bckgrd',\n", + " 'gt1r/land_ice_segments/geophysical/bsnow_conf',\n", + " 'gt1r/land_ice_segments/geophysical/bsnow_h',\n", + " 'gt1r/land_ice_segments/geophysical/bsnow_od',\n", + " 'gt1r/land_ice_segments/geophysical/cloud_flg_asr',\n", + " 'gt1r/land_ice_segments/geophysical/cloud_flg_atm',\n", + " 'gt1r/land_ice_segments/geophysical/dac',\n", + " 'gt1r/land_ice_segments/geophysical/e_bckgrd',\n", + " 'gt1r/land_ice_segments/geophysical/layer_flag',\n", + " 'gt1r/land_ice_segments/geophysical/msw_flag',\n", + " 'gt1r/land_ice_segments/geophysical/neutat_delay_total',\n", + " 'gt1r/land_ice_segments/geophysical/r_eff',\n", + " 'gt1r/land_ice_segments/geophysical/solar_azimuth',\n", + " 'gt1r/land_ice_segments/geophysical/solar_elevation',\n", + " 'gt1r/land_ice_segments/geophysical/tide_earth',\n", + " 'gt1r/land_ice_segments/geophysical/tide_earth_free2mean',\n", + " 'gt1r/land_ice_segments/geophysical/tide_equilibrium',\n", + " 'gt1r/land_ice_segments/geophysical/tide_load',\n", + " 'gt1r/land_ice_segments/geophysical/tide_ocean',\n", + " 'gt1r/land_ice_segments/geophysical/tide_pole',\n", + " 'gt1r/land_ice_segments/ground_track/ref_azimuth',\n", + " 'gt1r/land_ice_segments/ground_track/ref_coelv',\n", + " 'gt1r/land_ice_segments/ground_track/seg_azimuth',\n", + " 'gt1r/land_ice_segments/ground_track/sigma_geo_at',\n", + " 'gt1r/land_ice_segments/ground_track/sigma_geo_r',\n", + " 'gt1r/land_ice_segments/ground_track/sigma_geo_xt',\n", + " 'gt1r/land_ice_segments/ground_track/x_atc',\n", + " 'gt1r/land_ice_segments/ground_track/y_atc',\n", + " 'gt1r/land_ice_segments/h_li',\n", + " 'gt1r/land_ice_segments/h_li_sigma',\n", + " 'gt1r/land_ice_segments/latitude',\n", + " 'gt1r/land_ice_segments/longitude',\n", + " 'gt1r/land_ice_segments/segment_id',\n", + " 'gt1r/land_ice_segments/sigma_geo_h',\n", + " 'gt1r/residual_histogram/bckgrd_per_m',\n", + " 'gt1r/residual_histogram/bin_top_h',\n", + " 'gt1r/residual_histogram/count',\n", + " 'gt1r/residual_histogram/delta_time',\n", + " 'gt1r/residual_histogram/ds_segment_id',\n", + " 'gt1r/residual_histogram/lat_mean',\n", + " 'gt1r/residual_histogram/lon_mean',\n", + " 'gt1r/residual_histogram/pulse_count',\n", + " 'gt1r/residual_histogram/segment_id_list',\n", + " 'gt1r/residual_histogram/x_atc_mean',\n", + " 'gt1r/segment_quality/delta_time',\n", + " 'gt1r/segment_quality/record_number',\n", + " 'gt1r/segment_quality/reference_pt_lat',\n", + " 'gt1r/segment_quality/reference_pt_lon',\n", + " 'gt1r/segment_quality/segment_id',\n", + " 'gt1r/segment_quality/signal_selection_source',\n", + " 'gt1r/segment_quality/signal_selection_status/signal_selection_status_all',\n", + " 'gt1r/segment_quality/signal_selection_status/signal_selection_status_backup',\n", + " 'gt1r/segment_quality/signal_selection_status/signal_selection_status_confident',\n", + " 'gt2l/land_ice_segments/atl06_quality_summary',\n", + " 'gt2l/land_ice_segments/bias_correction/fpb_mean_corr',\n", + " 'gt2l/land_ice_segments/bias_correction/fpb_mean_corr_sigma',\n", + " 'gt2l/land_ice_segments/bias_correction/fpb_med_corr',\n", + " 'gt2l/land_ice_segments/bias_correction/fpb_med_corr_sigma',\n", + " 'gt2l/land_ice_segments/bias_correction/fpb_n_corr',\n", + " 'gt2l/land_ice_segments/bias_correction/med_r_fit',\n", + " 'gt2l/land_ice_segments/bias_correction/tx_mean_corr',\n", + " 'gt2l/land_ice_segments/bias_correction/tx_med_corr',\n", + " 'gt2l/land_ice_segments/delta_time',\n", + " 'gt2l/land_ice_segments/dem/dem_flag',\n", + " 'gt2l/land_ice_segments/dem/dem_h',\n", + " 'gt2l/land_ice_segments/dem/geoid_free2mean',\n", + " 'gt2l/land_ice_segments/dem/geoid_h',\n", + " 'gt2l/land_ice_segments/fit_statistics/dh_fit_dx',\n", + " 'gt2l/land_ice_segments/fit_statistics/dh_fit_dx_sigma',\n", + " 'gt2l/land_ice_segments/fit_statistics/dh_fit_dy',\n", + " 'gt2l/land_ice_segments/fit_statistics/h_expected_rms',\n", + " 'gt2l/land_ice_segments/fit_statistics/h_mean',\n", + " 'gt2l/land_ice_segments/fit_statistics/h_rms_misfit',\n", + " 'gt2l/land_ice_segments/fit_statistics/h_robust_sprd',\n", + " 'gt2l/land_ice_segments/fit_statistics/n_fit_photons',\n", + " 'gt2l/land_ice_segments/fit_statistics/n_seg_pulses',\n", + " 'gt2l/land_ice_segments/fit_statistics/sigma_h_mean',\n", + " 'gt2l/land_ice_segments/fit_statistics/signal_selection_source',\n", + " 'gt2l/land_ice_segments/fit_statistics/signal_selection_source_status',\n", + " 'gt2l/land_ice_segments/fit_statistics/snr',\n", + " 'gt2l/land_ice_segments/fit_statistics/snr_significance',\n", + " 'gt2l/land_ice_segments/fit_statistics/w_surface_window_final',\n", + " 'gt2l/land_ice_segments/geophysical/bckgrd',\n", + " 'gt2l/land_ice_segments/geophysical/bsnow_conf',\n", + " 'gt2l/land_ice_segments/geophysical/bsnow_h',\n", + " 'gt2l/land_ice_segments/geophysical/bsnow_od',\n", + " 'gt2l/land_ice_segments/geophysical/cloud_flg_asr',\n", + " 'gt2l/land_ice_segments/geophysical/cloud_flg_atm',\n", + " 'gt2l/land_ice_segments/geophysical/dac',\n", + " 'gt2l/land_ice_segments/geophysical/e_bckgrd',\n", + " 'gt2l/land_ice_segments/geophysical/layer_flag',\n", + " 'gt2l/land_ice_segments/geophysical/msw_flag',\n", + " 'gt2l/land_ice_segments/geophysical/neutat_delay_total',\n", + " 'gt2l/land_ice_segments/geophysical/r_eff',\n", + " 'gt2l/land_ice_segments/geophysical/solar_azimuth',\n", + " 'gt2l/land_ice_segments/geophysical/solar_elevation',\n", + " 'gt2l/land_ice_segments/geophysical/tide_earth',\n", + " 'gt2l/land_ice_segments/geophysical/tide_earth_free2mean',\n", + " 'gt2l/land_ice_segments/geophysical/tide_equilibrium',\n", + " 'gt2l/land_ice_segments/geophysical/tide_load',\n", + " 'gt2l/land_ice_segments/geophysical/tide_ocean',\n", + " 'gt2l/land_ice_segments/geophysical/tide_pole',\n", + " 'gt2l/land_ice_segments/ground_track/ref_azimuth',\n", + " 'gt2l/land_ice_segments/ground_track/ref_coelv',\n", + " 'gt2l/land_ice_segments/ground_track/seg_azimuth',\n", + " 'gt2l/land_ice_segments/ground_track/sigma_geo_at',\n", + " 'gt2l/land_ice_segments/ground_track/sigma_geo_r',\n", + " 'gt2l/land_ice_segments/ground_track/sigma_geo_xt',\n", + " 'gt2l/land_ice_segments/ground_track/x_atc',\n", + " 'gt2l/land_ice_segments/ground_track/y_atc',\n", + " 'gt2l/land_ice_segments/h_li',\n", + " 'gt2l/land_ice_segments/h_li_sigma',\n", + " 'gt2l/land_ice_segments/latitude',\n", + " 'gt2l/land_ice_segments/longitude',\n", + " 'gt2l/land_ice_segments/segment_id',\n", + " 'gt2l/land_ice_segments/sigma_geo_h',\n", + " 'gt2l/residual_histogram/bckgrd_per_m',\n", + " 'gt2l/residual_histogram/bin_top_h',\n", + " 'gt2l/residual_histogram/count',\n", + " 'gt2l/residual_histogram/delta_time',\n", + " 'gt2l/residual_histogram/ds_segment_id',\n", + " 'gt2l/residual_histogram/lat_mean',\n", + " 'gt2l/residual_histogram/lon_mean',\n", + " 'gt2l/residual_histogram/pulse_count',\n", + " 'gt2l/residual_histogram/segment_id_list',\n", + " 'gt2l/residual_histogram/x_atc_mean',\n", + " 'gt2l/segment_quality/delta_time',\n", + " 'gt2l/segment_quality/record_number',\n", + " 'gt2l/segment_quality/reference_pt_lat',\n", + " 'gt2l/segment_quality/reference_pt_lon',\n", + " 'gt2l/segment_quality/segment_id',\n", + " 'gt2l/segment_quality/signal_selection_source',\n", + " 'gt2l/segment_quality/signal_selection_status/signal_selection_status_all',\n", + " 'gt2l/segment_quality/signal_selection_status/signal_selection_status_backup',\n", + " 'gt2l/segment_quality/signal_selection_status/signal_selection_status_confident',\n", + " 'gt2r/land_ice_segments/atl06_quality_summary',\n", + " 'gt2r/land_ice_segments/bias_correction/fpb_mean_corr',\n", + " 'gt2r/land_ice_segments/bias_correction/fpb_mean_corr_sigma',\n", + " 'gt2r/land_ice_segments/bias_correction/fpb_med_corr',\n", + " 'gt2r/land_ice_segments/bias_correction/fpb_med_corr_sigma',\n", + " 'gt2r/land_ice_segments/bias_correction/fpb_n_corr',\n", + " 'gt2r/land_ice_segments/bias_correction/med_r_fit',\n", + " 'gt2r/land_ice_segments/bias_correction/tx_mean_corr',\n", + " 'gt2r/land_ice_segments/bias_correction/tx_med_corr',\n", + " 'gt2r/land_ice_segments/delta_time',\n", + " 'gt2r/land_ice_segments/dem/dem_flag',\n", + " 'gt2r/land_ice_segments/dem/dem_h',\n", + " 'gt2r/land_ice_segments/dem/geoid_free2mean',\n", + " 'gt2r/land_ice_segments/dem/geoid_h',\n", + " 'gt2r/land_ice_segments/fit_statistics/dh_fit_dx',\n", + " 'gt2r/land_ice_segments/fit_statistics/dh_fit_dx_sigma',\n", + " 'gt2r/land_ice_segments/fit_statistics/dh_fit_dy',\n", + " 'gt2r/land_ice_segments/fit_statistics/h_expected_rms',\n", + " 'gt2r/land_ice_segments/fit_statistics/h_mean',\n", + " 'gt2r/land_ice_segments/fit_statistics/h_rms_misfit',\n", + " 'gt2r/land_ice_segments/fit_statistics/h_robust_sprd',\n", + " 'gt2r/land_ice_segments/fit_statistics/n_fit_photons',\n", + " 'gt2r/land_ice_segments/fit_statistics/n_seg_pulses',\n", + " 'gt2r/land_ice_segments/fit_statistics/sigma_h_mean',\n", + " 'gt2r/land_ice_segments/fit_statistics/signal_selection_source',\n", + " 'gt2r/land_ice_segments/fit_statistics/signal_selection_source_status',\n", + " 'gt2r/land_ice_segments/fit_statistics/snr',\n", + " 'gt2r/land_ice_segments/fit_statistics/snr_significance',\n", + " 'gt2r/land_ice_segments/fit_statistics/w_surface_window_final',\n", + " 'gt2r/land_ice_segments/geophysical/bckgrd',\n", + " 'gt2r/land_ice_segments/geophysical/bsnow_conf',\n", + " 'gt2r/land_ice_segments/geophysical/bsnow_h',\n", + " 'gt2r/land_ice_segments/geophysical/bsnow_od',\n", + " 'gt2r/land_ice_segments/geophysical/cloud_flg_asr',\n", + " 'gt2r/land_ice_segments/geophysical/cloud_flg_atm',\n", + " 'gt2r/land_ice_segments/geophysical/dac',\n", + " 'gt2r/land_ice_segments/geophysical/e_bckgrd',\n", + " 'gt2r/land_ice_segments/geophysical/layer_flag',\n", + " 'gt2r/land_ice_segments/geophysical/msw_flag',\n", + " 'gt2r/land_ice_segments/geophysical/neutat_delay_total',\n", + " 'gt2r/land_ice_segments/geophysical/r_eff',\n", + " 'gt2r/land_ice_segments/geophysical/solar_azimuth',\n", + " 'gt2r/land_ice_segments/geophysical/solar_elevation',\n", + " 'gt2r/land_ice_segments/geophysical/tide_earth',\n", + " 'gt2r/land_ice_segments/geophysical/tide_earth_free2mean',\n", + " 'gt2r/land_ice_segments/geophysical/tide_equilibrium',\n", + " 'gt2r/land_ice_segments/geophysical/tide_load',\n", + " 'gt2r/land_ice_segments/geophysical/tide_ocean',\n", + " 'gt2r/land_ice_segments/geophysical/tide_pole',\n", + " 'gt2r/land_ice_segments/ground_track/ref_azimuth',\n", + " 'gt2r/land_ice_segments/ground_track/ref_coelv',\n", + " 'gt2r/land_ice_segments/ground_track/seg_azimuth',\n", + " 'gt2r/land_ice_segments/ground_track/sigma_geo_at',\n", + " 'gt2r/land_ice_segments/ground_track/sigma_geo_r',\n", + " 'gt2r/land_ice_segments/ground_track/sigma_geo_xt',\n", + " 'gt2r/land_ice_segments/ground_track/x_atc',\n", + " 'gt2r/land_ice_segments/ground_track/y_atc',\n", + " 'gt2r/land_ice_segments/h_li',\n", + " 'gt2r/land_ice_segments/h_li_sigma',\n", + " 'gt2r/land_ice_segments/latitude',\n", + " 'gt2r/land_ice_segments/longitude',\n", + " 'gt2r/land_ice_segments/segment_id',\n", + " 'gt2r/land_ice_segments/sigma_geo_h',\n", + " 'gt2r/residual_histogram/bckgrd_per_m',\n", + " 'gt2r/residual_histogram/bin_top_h',\n", + " 'gt2r/residual_histogram/count',\n", + " 'gt2r/residual_histogram/delta_time',\n", + " 'gt2r/residual_histogram/ds_segment_id',\n", + " 'gt2r/residual_histogram/lat_mean',\n", + " 'gt2r/residual_histogram/lon_mean',\n", + " 'gt2r/residual_histogram/pulse_count',\n", + " 'gt2r/residual_histogram/segment_id_list',\n", + " 'gt2r/residual_histogram/x_atc_mean',\n", + " 'gt2r/segment_quality/delta_time',\n", + " 'gt2r/segment_quality/record_number',\n", + " 'gt2r/segment_quality/reference_pt_lat',\n", + " 'gt2r/segment_quality/reference_pt_lon',\n", + " 'gt2r/segment_quality/segment_id',\n", + " 'gt2r/segment_quality/signal_selection_source',\n", + " 'gt2r/segment_quality/signal_selection_status/signal_selection_status_all',\n", + " 'gt2r/segment_quality/signal_selection_status/signal_selection_status_backup',\n", + " 'gt2r/segment_quality/signal_selection_status/signal_selection_status_confident',\n", + " 'gt3l/land_ice_segments/atl06_quality_summary',\n", + " 'gt3l/land_ice_segments/bias_correction/fpb_mean_corr',\n", + " 'gt3l/land_ice_segments/bias_correction/fpb_mean_corr_sigma',\n", + " 'gt3l/land_ice_segments/bias_correction/fpb_med_corr',\n", + " 'gt3l/land_ice_segments/bias_correction/fpb_med_corr_sigma',\n", + " 'gt3l/land_ice_segments/bias_correction/fpb_n_corr',\n", + " 'gt3l/land_ice_segments/bias_correction/med_r_fit',\n", + " 'gt3l/land_ice_segments/bias_correction/tx_mean_corr',\n", + " 'gt3l/land_ice_segments/bias_correction/tx_med_corr',\n", + " 'gt3l/land_ice_segments/delta_time',\n", + " 'gt3l/land_ice_segments/dem/dem_flag',\n", + " 'gt3l/land_ice_segments/dem/dem_h',\n", + " 'gt3l/land_ice_segments/dem/geoid_free2mean',\n", + " 'gt3l/land_ice_segments/dem/geoid_h',\n", + " 'gt3l/land_ice_segments/fit_statistics/dh_fit_dx',\n", + " 'gt3l/land_ice_segments/fit_statistics/dh_fit_dx_sigma',\n", + " 'gt3l/land_ice_segments/fit_statistics/dh_fit_dy',\n", + " 'gt3l/land_ice_segments/fit_statistics/h_expected_rms',\n", + " 'gt3l/land_ice_segments/fit_statistics/h_mean',\n", + " 'gt3l/land_ice_segments/fit_statistics/h_rms_misfit',\n", + " 'gt3l/land_ice_segments/fit_statistics/h_robust_sprd',\n", + " 'gt3l/land_ice_segments/fit_statistics/n_fit_photons',\n", + " 'gt3l/land_ice_segments/fit_statistics/n_seg_pulses',\n", + " 'gt3l/land_ice_segments/fit_statistics/sigma_h_mean',\n", + " 'gt3l/land_ice_segments/fit_statistics/signal_selection_source',\n", + " 'gt3l/land_ice_segments/fit_statistics/signal_selection_source_status',\n", + " 'gt3l/land_ice_segments/fit_statistics/snr',\n", + " 'gt3l/land_ice_segments/fit_statistics/snr_significance',\n", + " 'gt3l/land_ice_segments/fit_statistics/w_surface_window_final',\n", + " 'gt3l/land_ice_segments/geophysical/bckgrd',\n", + " 'gt3l/land_ice_segments/geophysical/bsnow_conf',\n", + " 'gt3l/land_ice_segments/geophysical/bsnow_h',\n", + " 'gt3l/land_ice_segments/geophysical/bsnow_od',\n", + " 'gt3l/land_ice_segments/geophysical/cloud_flg_asr',\n", + " 'gt3l/land_ice_segments/geophysical/cloud_flg_atm',\n", + " 'gt3l/land_ice_segments/geophysical/dac',\n", + " 'gt3l/land_ice_segments/geophysical/e_bckgrd',\n", + " 'gt3l/land_ice_segments/geophysical/layer_flag',\n", + " 'gt3l/land_ice_segments/geophysical/msw_flag',\n", + " 'gt3l/land_ice_segments/geophysical/neutat_delay_total',\n", + " 'gt3l/land_ice_segments/geophysical/r_eff',\n", + " 'gt3l/land_ice_segments/geophysical/solar_azimuth',\n", + " 'gt3l/land_ice_segments/geophysical/solar_elevation',\n", + " 'gt3l/land_ice_segments/geophysical/tide_earth',\n", + " 'gt3l/land_ice_segments/geophysical/tide_earth_free2mean',\n", + " 'gt3l/land_ice_segments/geophysical/tide_equilibrium',\n", + " 'gt3l/land_ice_segments/geophysical/tide_load',\n", + " 'gt3l/land_ice_segments/geophysical/tide_ocean',\n", + " 'gt3l/land_ice_segments/geophysical/tide_pole',\n", + " 'gt3l/land_ice_segments/ground_track/ref_azimuth',\n", + " 'gt3l/land_ice_segments/ground_track/ref_coelv',\n", + " 'gt3l/land_ice_segments/ground_track/seg_azimuth',\n", + " 'gt3l/land_ice_segments/ground_track/sigma_geo_at',\n", + " 'gt3l/land_ice_segments/ground_track/sigma_geo_r',\n", + " 'gt3l/land_ice_segments/ground_track/sigma_geo_xt',\n", + " 'gt3l/land_ice_segments/ground_track/x_atc',\n", + " 'gt3l/land_ice_segments/ground_track/y_atc',\n", + " 'gt3l/land_ice_segments/h_li',\n", + " 'gt3l/land_ice_segments/h_li_sigma',\n", + " 'gt3l/land_ice_segments/latitude',\n", + " 'gt3l/land_ice_segments/longitude',\n", + " 'gt3l/land_ice_segments/segment_id',\n", + " 'gt3l/land_ice_segments/sigma_geo_h',\n", + " 'gt3l/residual_histogram/bckgrd_per_m',\n", + " 'gt3l/residual_histogram/bin_top_h',\n", + " 'gt3l/residual_histogram/count',\n", + " 'gt3l/residual_histogram/delta_time',\n", + " 'gt3l/residual_histogram/ds_segment_id',\n", + " 'gt3l/residual_histogram/lat_mean',\n", + " 'gt3l/residual_histogram/lon_mean',\n", + " 'gt3l/residual_histogram/pulse_count',\n", + " 'gt3l/residual_histogram/segment_id_list',\n", + " 'gt3l/residual_histogram/x_atc_mean',\n", + " 'gt3l/segment_quality/delta_time',\n", + " 'gt3l/segment_quality/record_number',\n", + " 'gt3l/segment_quality/reference_pt_lat',\n", + " 'gt3l/segment_quality/reference_pt_lon',\n", + " 'gt3l/segment_quality/segment_id',\n", + " 'gt3l/segment_quality/signal_selection_source',\n", + " 'gt3l/segment_quality/signal_selection_status/signal_selection_status_all',\n", + " 'gt3l/segment_quality/signal_selection_status/signal_selection_status_backup',\n", + " 'gt3l/segment_quality/signal_selection_status/signal_selection_status_confident',\n", + " 'gt3r/land_ice_segments/atl06_quality_summary',\n", + " 'gt3r/land_ice_segments/bias_correction/fpb_mean_corr',\n", + " 'gt3r/land_ice_segments/bias_correction/fpb_mean_corr_sigma',\n", + " 'gt3r/land_ice_segments/bias_correction/fpb_med_corr',\n", + " 'gt3r/land_ice_segments/bias_correction/fpb_med_corr_sigma',\n", + " 'gt3r/land_ice_segments/bias_correction/fpb_n_corr',\n", + " 'gt3r/land_ice_segments/bias_correction/med_r_fit',\n", + " 'gt3r/land_ice_segments/bias_correction/tx_mean_corr',\n", + " 'gt3r/land_ice_segments/bias_correction/tx_med_corr',\n", + " 'gt3r/land_ice_segments/delta_time',\n", + " 'gt3r/land_ice_segments/dem/dem_flag',\n", + " 'gt3r/land_ice_segments/dem/dem_h',\n", + " 'gt3r/land_ice_segments/dem/geoid_free2mean',\n", + " 'gt3r/land_ice_segments/dem/geoid_h',\n", + " 'gt3r/land_ice_segments/fit_statistics/dh_fit_dx',\n", + " 'gt3r/land_ice_segments/fit_statistics/dh_fit_dx_sigma',\n", + " 'gt3r/land_ice_segments/fit_statistics/dh_fit_dy',\n", + " 'gt3r/land_ice_segments/fit_statistics/h_expected_rms',\n", + " 'gt3r/land_ice_segments/fit_statistics/h_mean',\n", + " 'gt3r/land_ice_segments/fit_statistics/h_rms_misfit',\n", + " 'gt3r/land_ice_segments/fit_statistics/h_robust_sprd',\n", + " 'gt3r/land_ice_segments/fit_statistics/n_fit_photons',\n", + " 'gt3r/land_ice_segments/fit_statistics/n_seg_pulses',\n", + " 'gt3r/land_ice_segments/fit_statistics/sigma_h_mean',\n", + " 'gt3r/land_ice_segments/fit_statistics/signal_selection_source',\n", + " 'gt3r/land_ice_segments/fit_statistics/signal_selection_source_status',\n", + " 'gt3r/land_ice_segments/fit_statistics/snr',\n", + " 'gt3r/land_ice_segments/fit_statistics/snr_significance',\n", + " 'gt3r/land_ice_segments/fit_statistics/w_surface_window_final',\n", + " 'gt3r/land_ice_segments/geophysical/bckgrd',\n", + " 'gt3r/land_ice_segments/geophysical/bsnow_conf',\n", + " 'gt3r/land_ice_segments/geophysical/bsnow_h',\n", + " 'gt3r/land_ice_segments/geophysical/bsnow_od',\n", + " 'gt3r/land_ice_segments/geophysical/cloud_flg_asr',\n", + " 'gt3r/land_ice_segments/geophysical/cloud_flg_atm',\n", + " 'gt3r/land_ice_segments/geophysical/dac',\n", + " 'gt3r/land_ice_segments/geophysical/e_bckgrd',\n", + " 'gt3r/land_ice_segments/geophysical/layer_flag',\n", + " 'gt3r/land_ice_segments/geophysical/msw_flag',\n", + " 'gt3r/land_ice_segments/geophysical/neutat_delay_total',\n", + " 'gt3r/land_ice_segments/geophysical/r_eff',\n", + " 'gt3r/land_ice_segments/geophysical/solar_azimuth',\n", + " 'gt3r/land_ice_segments/geophysical/solar_elevation',\n", + " 'gt3r/land_ice_segments/geophysical/tide_earth',\n", + " 'gt3r/land_ice_segments/geophysical/tide_earth_free2mean',\n", + " 'gt3r/land_ice_segments/geophysical/tide_equilibrium',\n", + " 'gt3r/land_ice_segments/geophysical/tide_load',\n", + " 'gt3r/land_ice_segments/geophysical/tide_ocean',\n", + " 'gt3r/land_ice_segments/geophysical/tide_pole',\n", + " 'gt3r/land_ice_segments/ground_track/ref_azimuth',\n", + " 'gt3r/land_ice_segments/ground_track/ref_coelv',\n", + " 'gt3r/land_ice_segments/ground_track/seg_azimuth',\n", + " 'gt3r/land_ice_segments/ground_track/sigma_geo_at',\n", + " 'gt3r/land_ice_segments/ground_track/sigma_geo_r',\n", + " 'gt3r/land_ice_segments/ground_track/sigma_geo_xt',\n", + " 'gt3r/land_ice_segments/ground_track/x_atc',\n", + " 'gt3r/land_ice_segments/ground_track/y_atc',\n", + " 'gt3r/land_ice_segments/h_li',\n", + " 'gt3r/land_ice_segments/h_li_sigma',\n", + " 'gt3r/land_ice_segments/latitude',\n", + " 'gt3r/land_ice_segments/longitude',\n", + " 'gt3r/land_ice_segments/segment_id',\n", + " 'gt3r/land_ice_segments/sigma_geo_h',\n", + " 'gt3r/residual_histogram/bckgrd_per_m',\n", + " 'gt3r/residual_histogram/bin_top_h',\n", + " 'gt3r/residual_histogram/count',\n", + " 'gt3r/residual_histogram/delta_time',\n", + " 'gt3r/residual_histogram/ds_segment_id',\n", + " 'gt3r/residual_histogram/lat_mean',\n", + " 'gt3r/residual_histogram/lon_mean',\n", + " 'gt3r/residual_histogram/pulse_count',\n", + " 'gt3r/residual_histogram/segment_id_list',\n", + " 'gt3r/residual_histogram/x_atc_mean',\n", + " 'gt3r/segment_quality/delta_time',\n", + " 'gt3r/segment_quality/record_number',\n", + " 'gt3r/segment_quality/reference_pt_lat',\n", + " 'gt3r/segment_quality/reference_pt_lon',\n", + " 'gt3r/segment_quality/segment_id',\n", + " 'gt3r/segment_quality/signal_selection_source',\n", + " 'gt3r/segment_quality/signal_selection_status/signal_selection_status_all',\n", + " 'gt3r/segment_quality/signal_selection_status/signal_selection_status_backup',\n", + " 'gt3r/segment_quality/signal_selection_status/signal_selection_status_confident',\n", + " 'orbit_info/bounding_polygon_lat1',\n", + " 'orbit_info/bounding_polygon_lon1',\n", + " 'orbit_info/crossing_time',\n", + " 'orbit_info/cycle_number',\n", + " 'orbit_info/lan',\n", + " 'orbit_info/orbit_number',\n", + " 'orbit_info/rgt',\n", + " 'orbit_info/sc_orient',\n", + " 'orbit_info/sc_orient_time',\n", + " 'quality_assessment/gt1l/delta_time',\n", + " 'quality_assessment/gt1l/lat_mean',\n", + " 'quality_assessment/gt1l/lon_mean',\n", + " 'quality_assessment/gt1l/signal_selection_source_fraction_0',\n", + " 'quality_assessment/gt1l/signal_selection_source_fraction_1',\n", + " 'quality_assessment/gt1l/signal_selection_source_fraction_2',\n", + " 'quality_assessment/gt1l/signal_selection_source_fraction_3',\n", + " 'quality_assessment/gt1r/delta_time',\n", + " 'quality_assessment/gt1r/lat_mean',\n", + " 'quality_assessment/gt1r/lon_mean',\n", + " 'quality_assessment/gt1r/signal_selection_source_fraction_0',\n", + " 'quality_assessment/gt1r/signal_selection_source_fraction_1',\n", + " 'quality_assessment/gt1r/signal_selection_source_fraction_2',\n", + " 'quality_assessment/gt1r/signal_selection_source_fraction_3',\n", + " 'quality_assessment/gt2l/delta_time',\n", + " 'quality_assessment/gt2l/lat_mean',\n", + " 'quality_assessment/gt2l/lon_mean',\n", + " 'quality_assessment/gt2l/signal_selection_source_fraction_0',\n", + " 'quality_assessment/gt2l/signal_selection_source_fraction_1',\n", + " 'quality_assessment/gt2l/signal_selection_source_fraction_2',\n", + " 'quality_assessment/gt2l/signal_selection_source_fraction_3',\n", + " 'quality_assessment/gt2r/delta_time',\n", + " 'quality_assessment/gt2r/lat_mean',\n", + " 'quality_assessment/gt2r/lon_mean',\n", + " 'quality_assessment/gt2r/signal_selection_source_fraction_0',\n", + " 'quality_assessment/gt2r/signal_selection_source_fraction_1',\n", + " 'quality_assessment/gt2r/signal_selection_source_fraction_2',\n", + " 'quality_assessment/gt2r/signal_selection_source_fraction_3',\n", + " 'quality_assessment/gt3l/delta_time',\n", + " 'quality_assessment/gt3l/lat_mean',\n", + " 'quality_assessment/gt3l/lon_mean',\n", + " 'quality_assessment/gt3l/signal_selection_source_fraction_0',\n", + " 'quality_assessment/gt3l/signal_selection_source_fraction_1',\n", + " 'quality_assessment/gt3l/signal_selection_source_fraction_2',\n", + " 'quality_assessment/gt3l/signal_selection_source_fraction_3',\n", + " 'quality_assessment/gt3r/delta_time',\n", + " 'quality_assessment/gt3r/lat_mean',\n", + " 'quality_assessment/gt3r/lon_mean',\n", + " 'quality_assessment/gt3r/signal_selection_source_fraction_0',\n", + " 'quality_assessment/gt3r/signal_selection_source_fraction_1',\n", + " 'quality_assessment/gt3r/signal_selection_source_fraction_2',\n", + " 'quality_assessment/gt3r/signal_selection_source_fraction_3',\n", + " 'quality_assessment/qa_granule_fail_reason',\n", + " 'quality_assessment/qa_granule_pass_fail']" + ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "reader.vars.avail()" ] @@ -333,7 +1610,9 @@ { "cell_type": "markdown", "id": "b2449941", - "metadata": {}, + "metadata": { + "user_expressions": [] + }, "source": [ "To make things easier, you can use icepyx's built-in default list that loads commonly used variables for your non-gridded data product, or create your own list of variables to be read in.\n", "icepyx will determine what variables are available for you to read in by creating a list from one of your source files.\n", @@ -349,7 +1628,9 @@ { "cell_type": "markdown", "id": "55092d1b", - "metadata": {}, + "metadata": { + "user_expressions": [] + }, "source": [ "For a basic case, let's say we want to read in height, latitude, and longitude for all beam pairs.\n", "We create our variables list as" @@ -357,7 +1638,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 28, "id": "e3734e09", "metadata": {}, "outputs": [], @@ -368,17 +1649,53 @@ { "cell_type": "markdown", "id": "fff0bb19", - "metadata": {}, + "metadata": { + "user_expressions": [] + }, "source": [ "Then we can view a dictionary of the variables we'd like to read in." ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 29, "id": "e5456e36", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "{'sc_orient': ['orbit_info/sc_orient'],\n", + " 'atlas_sdp_gps_epoch': ['ancillary_data/atlas_sdp_gps_epoch'],\n", + " 'cycle_number': ['orbit_info/cycle_number'],\n", + " 'rgt': ['orbit_info/rgt'],\n", + " 'data_start_utc': ['ancillary_data/data_start_utc'],\n", + " 'data_end_utc': ['ancillary_data/data_end_utc'],\n", + " 'h_li': ['gt1l/land_ice_segments/h_li',\n", + " 'gt1r/land_ice_segments/h_li',\n", + " 'gt2l/land_ice_segments/h_li',\n", + " 'gt2r/land_ice_segments/h_li',\n", + " 'gt3l/land_ice_segments/h_li',\n", + " 'gt3r/land_ice_segments/h_li'],\n", + " 'latitude': ['gt1l/land_ice_segments/latitude',\n", + " 'gt1r/land_ice_segments/latitude',\n", + " 'gt2l/land_ice_segments/latitude',\n", + " 'gt2r/land_ice_segments/latitude',\n", + " 'gt3l/land_ice_segments/latitude',\n", + " 'gt3r/land_ice_segments/latitude'],\n", + " 'longitude': ['gt1l/land_ice_segments/longitude',\n", + " 'gt1r/land_ice_segments/longitude',\n", + " 'gt2l/land_ice_segments/longitude',\n", + " 'gt2r/land_ice_segments/longitude',\n", + " 'gt3l/land_ice_segments/longitude',\n", + " 'gt3r/land_ice_segments/longitude']}" + ] + }, + "execution_count": 29, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "reader.vars.wanted" ] @@ -386,14 +1703,16 @@ { "cell_type": "markdown", "id": "9d5b50b5", - "metadata": {}, + "metadata": { + "user_expressions": [] + }, "source": [ "Don't forget - if you need to start over, and re-generate your wanted variables list, it's easy!" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 24, "id": "69894391", "metadata": {}, "outputs": [], @@ -404,7 +1723,9 @@ { "cell_type": "markdown", "id": "473de4d7", - "metadata": {}, + "metadata": { + "user_expressions": [] + }, "source": [ "### Step 5: Loading your data\n", "\n", @@ -414,9 +1735,120 @@ { "cell_type": "code", "execution_count": null, - "id": "eaabc976", + "id": "4a66d889-8d2d-4b9a-821a-96a394ff8d66", "metadata": {}, "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": 30, + "id": "eaabc976", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:49: UserWarning: Converting non-nanosecond precision datetime values to nanosecond precision. This behavior can eventually be relaxed in xarray, as it is an artifact from pandas which is now beginning to support non-nanosecond precision values. This warning is caused by passing non-nanosecond np.datetime64 or np.timedelta64 values to the DataArray or Variable constructor; it can be silenced by converting the values to nanosecond precision ahead of time.\n", + " df.update({keyword: df[keyword].str[:-1].astype(np.datetime64)})\n", + "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:49: UserWarning: Converting non-nanosecond precision datetime values to nanosecond precision. This behavior can eventually be relaxed in xarray, as it is an artifact from pandas which is now beginning to support non-nanosecond precision values. This warning is caused by passing non-nanosecond np.datetime64 or np.timedelta64 values to the DataArray or Variable constructor; it can be silenced by converting the values to nanosecond precision ahead of time.\n", + " df.update({keyword: df[keyword].str[:-1].astype(np.datetime64)})\n", + "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:490: UserWarning: rename 'delta_time' to 'photon_idx' does not create an index anymore. Try using swap_dims instead or use set_index after rename to create an indexed coordinate.\n", + " .rename({\"delta_time\": \"photon_idx\"})\n", + "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:490: UserWarning: rename 'delta_time' to 'photon_idx' does not create an index anymore. Try using swap_dims instead or use set_index after rename to create an indexed coordinate.\n", + " .rename({\"delta_time\": \"photon_idx\"})\n", + "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:490: UserWarning: rename 'delta_time' to 'photon_idx' does not create an index anymore. Try using swap_dims instead or use set_index after rename to create an indexed coordinate.\n", + " .rename({\"delta_time\": \"photon_idx\"})\n", + "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:490: UserWarning: rename 'delta_time' to 'photon_idx' does not create an index anymore. Try using swap_dims instead or use set_index after rename to create an indexed coordinate.\n", + " .rename({\"delta_time\": \"photon_idx\"})\n", + "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:490: UserWarning: rename 'delta_time' to 'photon_idx' does not create an index anymore. Try using swap_dims instead or use set_index after rename to create an indexed coordinate.\n", + " .rename({\"delta_time\": \"photon_idx\"})\n", + "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:490: UserWarning: rename 'delta_time' to 'photon_idx' does not create an index anymore. Try using swap_dims instead or use set_index after rename to create an indexed coordinate.\n", + " .rename({\"delta_time\": \"photon_idx\"})\n", + "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:49: UserWarning: Converting non-nanosecond precision datetime values to nanosecond precision. This behavior can eventually be relaxed in xarray, as it is an artifact from pandas which is now beginning to support non-nanosecond precision values. This warning is caused by passing non-nanosecond np.datetime64 or np.timedelta64 values to the DataArray or Variable constructor; it can be silenced by converting the values to nanosecond precision ahead of time.\n", + " df.update({keyword: df[keyword].str[:-1].astype(np.datetime64)})\n", + "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:49: UserWarning: Converting non-nanosecond precision datetime values to nanosecond precision. This behavior can eventually be relaxed in xarray, as it is an artifact from pandas which is now beginning to support non-nanosecond precision values. This warning is caused by passing non-nanosecond np.datetime64 or np.timedelta64 values to the DataArray or Variable constructor; it can be silenced by converting the values to nanosecond precision ahead of time.\n", + " df.update({keyword: df[keyword].str[:-1].astype(np.datetime64)})\n", + "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:490: UserWarning: rename 'delta_time' to 'photon_idx' does not create an index anymore. Try using swap_dims instead or use set_index after rename to create an indexed coordinate.\n", + " .rename({\"delta_time\": \"photon_idx\"})\n", + "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:490: UserWarning: rename 'delta_time' to 'photon_idx' does not create an index anymore. Try using swap_dims instead or use set_index after rename to create an indexed coordinate.\n", + " .rename({\"delta_time\": \"photon_idx\"})\n", + "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:490: UserWarning: rename 'delta_time' to 'photon_idx' does not create an index anymore. Try using swap_dims instead or use set_index after rename to create an indexed coordinate.\n", + " .rename({\"delta_time\": \"photon_idx\"})\n", + "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:490: UserWarning: rename 'delta_time' to 'photon_idx' does not create an index anymore. Try using swap_dims instead or use set_index after rename to create an indexed coordinate.\n", + " .rename({\"delta_time\": \"photon_idx\"})\n", + "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:490: UserWarning: rename 'delta_time' to 'photon_idx' does not create an index anymore. Try using swap_dims instead or use set_index after rename to create an indexed coordinate.\n", + " .rename({\"delta_time\": \"photon_idx\"})\n", + "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:490: UserWarning: rename 'delta_time' to 'photon_idx' does not create an index anymore. Try using swap_dims instead or use set_index after rename to create an indexed coordinate.\n", + " .rename({\"delta_time\": \"photon_idx\"})\n", + "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:49: UserWarning: Converting non-nanosecond precision datetime values to nanosecond precision. This behavior can eventually be relaxed in xarray, as it is an artifact from pandas which is now beginning to support non-nanosecond precision values. This warning is caused by passing non-nanosecond np.datetime64 or np.timedelta64 values to the DataArray or Variable constructor; it can be silenced by converting the values to nanosecond precision ahead of time.\n", + " df.update({keyword: df[keyword].str[:-1].astype(np.datetime64)})\n", + "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:49: UserWarning: Converting non-nanosecond precision datetime values to nanosecond precision. This behavior can eventually be relaxed in xarray, as it is an artifact from pandas which is now beginning to support non-nanosecond precision values. This warning is caused by passing non-nanosecond np.datetime64 or np.timedelta64 values to the DataArray or Variable constructor; it can be silenced by converting the values to nanosecond precision ahead of time.\n", + " df.update({keyword: df[keyword].str[:-1].astype(np.datetime64)})\n", + "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:490: UserWarning: rename 'delta_time' to 'photon_idx' does not create an index anymore. Try using swap_dims instead or use set_index after rename to create an indexed coordinate.\n", + " .rename({\"delta_time\": \"photon_idx\"})\n", + "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:490: UserWarning: rename 'delta_time' to 'photon_idx' does not create an index anymore. Try using swap_dims instead or use set_index after rename to create an indexed coordinate.\n", + " .rename({\"delta_time\": \"photon_idx\"})\n", + "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:490: UserWarning: rename 'delta_time' to 'photon_idx' does not create an index anymore. Try using swap_dims instead or use set_index after rename to create an indexed coordinate.\n", + " .rename({\"delta_time\": \"photon_idx\"})\n", + "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:490: UserWarning: rename 'delta_time' to 'photon_idx' does not create an index anymore. Try using swap_dims instead or use set_index after rename to create an indexed coordinate.\n", + " .rename({\"delta_time\": \"photon_idx\"})\n", + "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:490: UserWarning: rename 'delta_time' to 'photon_idx' does not create an index anymore. Try using swap_dims instead or use set_index after rename to create an indexed coordinate.\n", + " .rename({\"delta_time\": \"photon_idx\"})\n", + "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:490: UserWarning: rename 'delta_time' to 'photon_idx' does not create an index anymore. Try using swap_dims instead or use set_index after rename to create an indexed coordinate.\n", + " .rename({\"delta_time\": \"photon_idx\"})\n", + "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:49: UserWarning: Converting non-nanosecond precision datetime values to nanosecond precision. This behavior can eventually be relaxed in xarray, as it is an artifact from pandas which is now beginning to support non-nanosecond precision values. This warning is caused by passing non-nanosecond np.datetime64 or np.timedelta64 values to the DataArray or Variable constructor; it can be silenced by converting the values to nanosecond precision ahead of time.\n", + " df.update({keyword: df[keyword].str[:-1].astype(np.datetime64)})\n", + "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:49: UserWarning: Converting non-nanosecond precision datetime values to nanosecond precision. This behavior can eventually be relaxed in xarray, as it is an artifact from pandas which is now beginning to support non-nanosecond precision values. This warning is caused by passing non-nanosecond np.datetime64 or np.timedelta64 values to the DataArray or Variable constructor; it can be silenced by converting the values to nanosecond precision ahead of time.\n", + " df.update({keyword: df[keyword].str[:-1].astype(np.datetime64)})\n", + "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:490: UserWarning: rename 'delta_time' to 'photon_idx' does not create an index anymore. Try using swap_dims instead or use set_index after rename to create an indexed coordinate.\n", + " .rename({\"delta_time\": \"photon_idx\"})\n", + "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:490: UserWarning: rename 'delta_time' to 'photon_idx' does not create an index anymore. Try using swap_dims instead or use set_index after rename to create an indexed coordinate.\n", + " .rename({\"delta_time\": \"photon_idx\"})\n", + "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:490: UserWarning: rename 'delta_time' to 'photon_idx' does not create an index anymore. Try using swap_dims instead or use set_index after rename to create an indexed coordinate.\n", + " .rename({\"delta_time\": \"photon_idx\"})\n", + "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:490: UserWarning: rename 'delta_time' to 'photon_idx' does not create an index anymore. Try using swap_dims instead or use set_index after rename to create an indexed coordinate.\n", + " .rename({\"delta_time\": \"photon_idx\"})\n", + "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:490: UserWarning: rename 'delta_time' to 'photon_idx' does not create an index anymore. Try using swap_dims instead or use set_index after rename to create an indexed coordinate.\n", + " .rename({\"delta_time\": \"photon_idx\"})\n", + "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:490: UserWarning: rename 'delta_time' to 'photon_idx' does not create an index anymore. Try using swap_dims instead or use set_index after rename to create an indexed coordinate.\n", + " .rename({\"delta_time\": \"photon_idx\"})\n", + "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:49: UserWarning: Converting non-nanosecond precision datetime values to nanosecond precision. This behavior can eventually be relaxed in xarray, as it is an artifact from pandas which is now beginning to support non-nanosecond precision values. This warning is caused by passing non-nanosecond np.datetime64 or np.timedelta64 values to the DataArray or Variable constructor; it can be silenced by converting the values to nanosecond precision ahead of time.\n", + " df.update({keyword: df[keyword].str[:-1].astype(np.datetime64)})\n", + "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:49: UserWarning: Converting non-nanosecond precision datetime values to nanosecond precision. This behavior can eventually be relaxed in xarray, as it is an artifact from pandas which is now beginning to support non-nanosecond precision values. This warning is caused by passing non-nanosecond np.datetime64 or np.timedelta64 values to the DataArray or Variable constructor; it can be silenced by converting the values to nanosecond precision ahead of time.\n", + " df.update({keyword: df[keyword].str[:-1].astype(np.datetime64)})\n", + "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:490: UserWarning: rename 'delta_time' to 'photon_idx' does not create an index anymore. Try using swap_dims instead or use set_index after rename to create an indexed coordinate.\n", + " .rename({\"delta_time\": \"photon_idx\"})\n", + "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:490: UserWarning: rename 'delta_time' to 'photon_idx' does not create an index anymore. Try using swap_dims instead or use set_index after rename to create an indexed coordinate.\n", + " .rename({\"delta_time\": \"photon_idx\"})\n", + "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:490: UserWarning: rename 'delta_time' to 'photon_idx' does not create an index anymore. Try using swap_dims instead or use set_index after rename to create an indexed coordinate.\n", + " .rename({\"delta_time\": \"photon_idx\"})\n", + "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:490: UserWarning: rename 'delta_time' to 'photon_idx' does not create an index anymore. Try using swap_dims instead or use set_index after rename to create an indexed coordinate.\n", + " .rename({\"delta_time\": \"photon_idx\"})\n", + "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:490: UserWarning: rename 'delta_time' to 'photon_idx' does not create an index anymore. Try using swap_dims instead or use set_index after rename to create an indexed coordinate.\n", + " .rename({\"delta_time\": \"photon_idx\"})\n", + "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:490: UserWarning: rename 'delta_time' to 'photon_idx' does not create an index anymore. Try using swap_dims instead or use set_index after rename to create an indexed coordinate.\n", + " .rename({\"delta_time\": \"photon_idx\"})\n", + "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:49: UserWarning: Converting non-nanosecond precision datetime values to nanosecond precision. This behavior can eventually be relaxed in xarray, as it is an artifact from pandas which is now beginning to support non-nanosecond precision values. This warning is caused by passing non-nanosecond np.datetime64 or np.timedelta64 values to the DataArray or Variable constructor; it can be silenced by converting the values to nanosecond precision ahead of time.\n", + " df.update({keyword: df[keyword].str[:-1].astype(np.datetime64)})\n", + "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:49: UserWarning: Converting non-nanosecond precision datetime values to nanosecond precision. This behavior can eventually be relaxed in xarray, as it is an artifact from pandas which is now beginning to support non-nanosecond precision values. This warning is caused by passing non-nanosecond np.datetime64 or np.timedelta64 values to the DataArray or Variable constructor; it can be silenced by converting the values to nanosecond precision ahead of time.\n", + " df.update({keyword: df[keyword].str[:-1].astype(np.datetime64)})\n", + "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:490: UserWarning: rename 'delta_time' to 'photon_idx' does not create an index anymore. Try using swap_dims instead or use set_index after rename to create an indexed coordinate.\n", + " .rename({\"delta_time\": \"photon_idx\"})\n", + "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:490: UserWarning: rename 'delta_time' to 'photon_idx' does not create an index anymore. Try using swap_dims instead or use set_index after rename to create an indexed coordinate.\n", + " .rename({\"delta_time\": \"photon_idx\"})\n", + "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:490: UserWarning: rename 'delta_time' to 'photon_idx' does not create an index anymore. Try using swap_dims instead or use set_index after rename to create an indexed coordinate.\n", + " .rename({\"delta_time\": \"photon_idx\"})\n", + "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:490: UserWarning: rename 'delta_time' to 'photon_idx' does not create an index anymore. Try using swap_dims instead or use set_index after rename to create an indexed coordinate.\n", + " .rename({\"delta_time\": \"photon_idx\"})\n", + "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:490: UserWarning: rename 'delta_time' to 'photon_idx' does not create an index anymore. Try using swap_dims instead or use set_index after rename to create an indexed coordinate.\n", + " .rename({\"delta_time\": \"photon_idx\"})\n", + "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:490: UserWarning: rename 'delta_time' to 'photon_idx' does not create an index anymore. Try using swap_dims instead or use set_index after rename to create an indexed coordinate.\n", + " .rename({\"delta_time\": \"photon_idx\"})\n" + ] + } + ], "source": [ "ds = reader.load()" ] @@ -424,7 +1856,9 @@ { "cell_type": "markdown", "id": "db6560f1", - "metadata": {}, + "metadata": { + "user_expressions": [] + }, "source": [ "Within a Jupyter Notebook, you can get a summary view of your data object.\n", "\n", @@ -435,10 +1869,549 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 26, "id": "723256f7", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
<xarray.Dataset>\n",
+       "Dimensions:              (photon_idx: 29027, spot: 2, gran_idx: 6)\n",
+       "Coordinates:\n",
+       "  * photon_idx           (photon_idx) int64 0 1 2 3 ... 29023 29024 29025 29026\n",
+       "  * spot                 (spot) uint8 2 5\n",
+       "  * gran_idx             (gran_idx) float64 8.49e+04 9.02e+04 ... 1.016e+05\n",
+       "    source_file          (gran_idx) <U72 '../../../../data/ATL06/processed_AT...\n",
+       "    delta_time           (gran_idx, photon_idx) datetime64[ns] 2019-02-22T01:...\n",
+       "Data variables:\n",
+       "    sc_orient            (gran_idx) int8 0 0 0 1 1 1\n",
+       "    cycle_number         (gran_idx) int8 2 2 2 5 5 5\n",
+       "    rgt                  (gran_idx) int16 849 902 910 986 1001 1016\n",
+       "    atlas_sdp_gps_epoch  (gran_idx) datetime64[ns] 2018-01-01T00:00:18 ... 20...\n",
+       "    data_start_utc       (gran_idx) datetime64[ns] 2019-02-22T01:03:44.199777...\n",
+       "    data_end_utc         (gran_idx) datetime64[ns] 2019-02-22T01:07:38.112326...\n",
+       "    h_li                 (spot, gran_idx, photon_idx) float32 nan nan ... nan\n",
+       "    latitude             (spot, gran_idx, photon_idx) float64 nan nan ... nan\n",
+       "    longitude            (spot, gran_idx, photon_idx) float64 nan nan ... nan\n",
+       "    gt                   (gran_idx, spot) <U4 'gt3r' 'gt1l' ... 'gt1l' 'gt3r'\n",
+       "Attributes:\n",
+       "    data_product:  ATL06\n",
+       "    Description:   The land_ice_height group contains the primary set of deri...\n",
+       "    data_rate:     Data within this group are sparse.  Data values are provid...
" + ], + "text/plain": [ + "\n", + "Dimensions: (photon_idx: 29027, spot: 2, gran_idx: 6)\n", + "Coordinates:\n", + " * photon_idx (photon_idx) int64 0 1 2 3 ... 29023 29024 29025 29026\n", + " * spot (spot) uint8 2 5\n", + " * gran_idx (gran_idx) float64 8.49e+04 9.02e+04 ... 1.016e+05\n", + " source_file (gran_idx) " + ] + }, + "execution_count": 27, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "ds.plot.scatter(x=\"longitude\", y=\"latitude\", hue=\"h_li\", vmin=-100, vmax=2000)" ] @@ -469,7 +2455,9 @@ { "cell_type": "markdown", "id": "6421f67c", - "metadata": {}, + "metadata": { + "user_expressions": [] + }, "source": [ "A developer note to users:\n", "our next steps will be to create an xarray extension with ICESat-2 aware functions (like \"get_strong_beams\", etc.).\n", @@ -478,191 +2466,39 @@ }, { "cell_type": "markdown", - "id": "6edfbb25", - "metadata": {}, - "source": [ - "### More on Intake catalogs and the read object\n", - "\n", - "As anyone familiar with ICESat-2 hdf5 files knows, one of the challenges to reading in data is looping through all of the beam pairs for each track.\n", - "The icepyx read module takes advantage of icepyx's variables module, which has some awareness of ICESat-2 data and uses that to save the user the trouble of having to loop through each beam pair.\n", - "The `reader.load()` function does this by automatically creating minimal Intake catalogs for each variable path, reading in the data, and merging each variable into a ready-to-analyze Xarray DataSet.\n", - "The Intake savvy user may wish to view the template catalog or use an existing catalog." - ] - }, - { - "cell_type": "markdown", - "id": "0f0076f9", - "metadata": {}, - "source": [ - "#### Viewing the template catalog\n", - "\n", - "You can access the ICESat-2 catalog template as an attribute of the read object.\n", - "\n", - "***NOTE: accessing `reader.is2catalog` creates a template with a placeholder in the 'group' parameter; thus, it will not work to actually read in data***" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "2de29fd8", + "id": "1b0cb477", "metadata": { - "scrolled": true + "user_expressions": [] }, - "outputs": [], - "source": [ - "reader.is2catalog" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "7a5deef8", - "metadata": {}, - "outputs": [], - "source": [ - "reader.is2catalog.gui" - ] - }, - { - "cell_type": "markdown", - "id": "fef43556", - "metadata": {}, - "source": [ - "#### Use an existing catalog\n", - "If you already have a catalog for your data, you can supply that when you create the read object." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "64986a60", - "metadata": {}, - "outputs": [], - "source": [ - "catpath = path_root + 'test_catalog.yml'\n", - "reader = ipx.Read(filepath, pattern, catpath)" - ] - }, - { - "cell_type": "markdown", - "id": "cf930e0a", - "metadata": {}, - "source": [ - "Then, you can use the catalog you supplied by calling intake's `read` directly to read in the specified data variable." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "dd0e086a", - "metadata": {}, - "outputs": [], - "source": [ - "ds = reader.is2catalog.read()" - ] - }, - { - "cell_type": "markdown", - "id": "60b1a304", - "metadata": {}, "source": [ - "***NOTE: this means that you will only be able to read in a single data variable!***\n", - "\n", - "To take advantage of icepyx's knowledge of ICESat-2 data nesting of beam pairs and read in multiple related variables at once, you must use the variable approach outlined earlier in this tutorial." + "#### Credits\n", + "* original notebook by: Jessica Scheick\n", + "* notebook contributors: Wei Ji and Tian\n", + "* templates for default ICESat-2 Intake catalogs from: [Wei Ji](https://github.com/icesat2py/icepyx/issues/106) and [Tian](https://github.com/icetianli/ICESat2_xarray)." ] }, { "cell_type": "code", "execution_count": null, - "id": "f5e3a221", - "metadata": {}, - "outputs": [], - "source": [ - "ds = reader.load()\n", - "ds" - ] - }, - { - "cell_type": "markdown", - "id": "d56fc41c", + "id": "aaf6f5a6-355b-456a-99fd-ce0b51045b58", "metadata": {}, - "source": [ - "### More customization options\n", - "\n", - "If you'd like to use the icepyx ICESat-2 Catalog template to create your own customized catalog, we recommend that you access the `build_catalog` function directly, which returns an Intake Catalog instance.\n", - "\n", - "You'll need to supply the required `data_source`, `path_pattern`, and `source_type` arguments. `data_source` and `path_pattern` are described in Steps 2 and 3 of this tutorial. `source_type` is the string you'd like to use for your Local Catalog entry.\n", - "\n", - "This function accepts as keyword input arguments (kwargs) dictionaries with appropriate keys (depending on the Intake driver you are using).\n", - "The simplest version of this is specifying the variable parameters and paths of interest.\n", - "`grp_paths` may contain \"variables\", each of which must then be further defined by `grp_path_params`.\n", - "You cannot use glob-like path syntax to access variables (so `grp_path = '/*/land_ice_segments'` is NOT VALID)." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f174f885", - "metadata": { - "scrolled": true - }, "outputs": [], - "source": [ - "import icepyx.core.is2cat as is2cat\n", - "\n", - "# build a custom ICESat-2 catalog with a group and parameter\n", - "cat = is2cat.build_catalog(data_source = path_root,\n", - " path_pattern = pattern,\n", - " source_type = \"manual_catalog\",\n", - " grp_paths = \"/{{gt}}/land_ice_segments\",\n", - " grp_path_params = [{\"name\": \"gt\",\n", - " \"description\": \"Ground track\",\n", - " \"type\": \"str\",\n", - " \"default\": \"gt1l\",\n", - " \"allowed\": [\"gt1l\", \"gt1r\", \"gt2l\", \"gt2r\", \"gt3l\", \"gt3r\"]\n", - " }]\n", - " )" - ] - }, - { - "cell_type": "markdown", - "id": "bab9c949", - "metadata": {}, - "source": [ - "#### Saving your catalog\n", - "If you create a highly customized ICESat-2 catalog, you can use Intake's `save` to export it as a .yml file.\n", - "\n", - "Don't forget you can easily use an existing catalog (such as this highly customized one you just made) to read in your data with `reader = ipx.Read(filepath, pattern, catalog)` (so it's as easy as re-creating your reader object with your modified catalog)." - ] + "source": [] }, { "cell_type": "code", "execution_count": null, - "id": "30f0122a", + "id": "8ea1987f-b6bf-44df-a869-949290f498cb", "metadata": {}, "outputs": [], - "source": [ - "catpath = path_root + 'test_catalog.yml'\n", - "cat.save(catpath)" - ] - }, - { - "cell_type": "markdown", - "id": "1b0cb477", - "metadata": {}, - "source": [ - "#### Credits\n", - "* original notebook by: Jessica Scheick\n", - "* notebook contributors: Wei Ji and Tian\n", - "* templates for default ICESat-2 Intake catalogs from: [Wei Ji](https://github.com/icesat2py/icepyx/issues/106) and [Tian](https://github.com/icetianli/ICESat2_xarray)." - ] + "source": [] } ], "metadata": { "kernelspec": { - "display_name": "Python 3 (ipykernel)", + "display_name": "general", "language": "python", - "name": "python3" + "name": "general" }, "language_info": { "codemirror_mode": { @@ -674,7 +2510,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.10.12" + "version": "3.11.4" } }, "nbformat": 4, diff --git a/doc/source/user_guide/documentation/read.rst b/doc/source/user_guide/documentation/read.rst index b076ef210..a5beedf4e 100644 --- a/doc/source/user_guide/documentation/read.rst +++ b/doc/source/user_guide/documentation/read.rst @@ -19,7 +19,6 @@ Attributes .. autosummary:: :toctree: ../../_icepyx/ - Read.is2catalog Read.vars From 9f066112c8b73200128e18eed8e6242d15329da6 Mon Sep 17 00:00:00 2001 From: Rachel Wegener Date: Wed, 30 Aug 2023 20:52:51 +0000 Subject: [PATCH 07/55] update approach paragraph --- doc/source/example_notebooks/IS2_data_read-in.ipynb | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/doc/source/example_notebooks/IS2_data_read-in.ipynb b/doc/source/example_notebooks/IS2_data_read-in.ipynb index b8697b1d7..c459b7c78 100644 --- a/doc/source/example_notebooks/IS2_data_read-in.ipynb +++ b/doc/source/example_notebooks/IS2_data_read-in.ipynb @@ -22,10 +22,7 @@ "Instead of needing to manually iterate through the beam pairs, you can provide a few options to the `Read` object and icepyx will do the heavy lifting for you (as detailed in this notebook).\n", "\n", "### Approach\n", - "If you're interested in what's happening under the hood: icepyx turns your instructions into something called a catalog, then uses the Intake library and the catalog to actually load the data into memory. Specifically, icepyx creates an [Intake](https://intake.readthedocs.io/en/latest/) data [catalog](https://intake.readthedocs.io/en/latest/catalog.html) for each requested variable and then merges the read-in data from each of the variables to create a single data object.\n", - "\n", - "Intake catalogs are powerful (and the tool we selected) because they can be saved, shared, modified, and reused to reproducibly read in a set of data files in a consistent way as part of an analysis workflow.\n", - "This approach streamlines the transition between data sources (local/downloaded files or, ultimately, cloud/bucket access) and data object types (e.g. [Xarray Dataset](http://xarray.pydata.org/en/stable/generated/xarray.Dataset.html) or [GeoPandas GeoDataFrame](https://geopandas.org/docs/reference/api/geopandas.GeoDataFrame.html))." + "If you're interested in what's happening under the hood: icepyx uses the [xarray](https://docs.xarray.dev/en/stable/) library to read in each of the requested variables of the dataset. icepyx formats each requested variable and then merges the read-in data from each of the variables to create a single data object. The use of xarray is powerful, because the returned data object can be used with relevant xarray processing tools." ] }, { From d019b9a2db2391882dbc69c4816f64eb03f389e3 Mon Sep 17 00:00:00 2001 From: Rachel Wegener Date: Wed, 30 Aug 2023 20:59:03 +0000 Subject: [PATCH 08/55] remove one more instance of catalog from the docs --- doc/source/example_notebooks/IS2_data_read-in.ipynb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/doc/source/example_notebooks/IS2_data_read-in.ipynb b/doc/source/example_notebooks/IS2_data_read-in.ipynb index c459b7c78..13b49cddb 100644 --- a/doc/source/example_notebooks/IS2_data_read-in.ipynb +++ b/doc/source/example_notebooks/IS2_data_read-in.ipynb @@ -2470,8 +2470,7 @@ "source": [ "#### Credits\n", "* original notebook by: Jessica Scheick\n", - "* notebook contributors: Wei Ji and Tian\n", - "* templates for default ICESat-2 Intake catalogs from: [Wei Ji](https://github.com/icesat2py/icepyx/issues/106) and [Tian](https://github.com/icetianli/ICESat2_xarray)." + "* notebook contributors: Wei Ji and Tian" ] }, { From 156ea89103f44dd4b7be680ac51eecb5e1b85b18 Mon Sep 17 00:00:00 2001 From: Rachel Wegener Date: Wed, 30 Aug 2023 21:01:36 +0000 Subject: [PATCH 09/55] clear jupyter history --- .../example_notebooks/IS2_data_read-in.ipynb | 2019 +---------------- 1 file changed, 34 insertions(+), 1985 deletions(-) diff --git a/doc/source/example_notebooks/IS2_data_read-in.ipynb b/doc/source/example_notebooks/IS2_data_read-in.ipynb index 13b49cddb..115c63044 100644 --- a/doc/source/example_notebooks/IS2_data_read-in.ipynb +++ b/doc/source/example_notebooks/IS2_data_read-in.ipynb @@ -35,7 +35,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": null, "id": "2b74b672", "metadata": {}, "outputs": [], @@ -58,18 +58,10 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": null, "id": "c4390195", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "You have 6 files matching the filename pattern to be read in.\n" - ] - } - ], + "outputs": [], "source": [ "path_root = '/full/path/to/your/data/'\n", "pattern = \"processed_ATL{product:2}_{datetime:%Y%m%d%H%M%S}_{rgt:4}{cycle:2}{orbitsegment:2}_{version:3}_{revision:2}.h5\"\n", @@ -78,7 +70,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": null, "id": "2f46029d", "metadata": {}, "outputs": [], @@ -88,603 +80,10 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": null, "id": "c0439388", "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:49: UserWarning: Converting non-nanosecond precision datetime values to nanosecond precision. This behavior can eventually be relaxed in xarray, as it is an artifact from pandas which is now beginning to support non-nanosecond precision values. This warning is caused by passing non-nanosecond np.datetime64 or np.timedelta64 values to the DataArray or Variable constructor; it can be silenced by converting the values to nanosecond precision ahead of time.\n", - " df.update({keyword: df[keyword].str[:-1].astype(np.datetime64)})\n", - "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:49: UserWarning: Converting non-nanosecond precision datetime values to nanosecond precision. This behavior can eventually be relaxed in xarray, as it is an artifact from pandas which is now beginning to support non-nanosecond precision values. This warning is caused by passing non-nanosecond np.datetime64 or np.timedelta64 values to the DataArray or Variable constructor; it can be silenced by converting the values to nanosecond precision ahead of time.\n", - " df.update({keyword: df[keyword].str[:-1].astype(np.datetime64)})\n", - "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:490: UserWarning: rename 'delta_time' to 'photon_idx' does not create an index anymore. Try using swap_dims instead or use set_index after rename to create an indexed coordinate.\n", - " .rename({\"delta_time\": \"photon_idx\"})\n", - "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:490: UserWarning: rename 'delta_time' to 'photon_idx' does not create an index anymore. Try using swap_dims instead or use set_index after rename to create an indexed coordinate.\n", - " .rename({\"delta_time\": \"photon_idx\"})\n", - "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:49: UserWarning: Converting non-nanosecond precision datetime values to nanosecond precision. This behavior can eventually be relaxed in xarray, as it is an artifact from pandas which is now beginning to support non-nanosecond precision values. This warning is caused by passing non-nanosecond np.datetime64 or np.timedelta64 values to the DataArray or Variable constructor; it can be silenced by converting the values to nanosecond precision ahead of time.\n", - " df.update({keyword: df[keyword].str[:-1].astype(np.datetime64)})\n", - "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:49: UserWarning: Converting non-nanosecond precision datetime values to nanosecond precision. This behavior can eventually be relaxed in xarray, as it is an artifact from pandas which is now beginning to support non-nanosecond precision values. This warning is caused by passing non-nanosecond np.datetime64 or np.timedelta64 values to the DataArray or Variable constructor; it can be silenced by converting the values to nanosecond precision ahead of time.\n", - " df.update({keyword: df[keyword].str[:-1].astype(np.datetime64)})\n", - "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:490: UserWarning: rename 'delta_time' to 'photon_idx' does not create an index anymore. Try using swap_dims instead or use set_index after rename to create an indexed coordinate.\n", - " .rename({\"delta_time\": \"photon_idx\"})\n", - "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:490: UserWarning: rename 'delta_time' to 'photon_idx' does not create an index anymore. Try using swap_dims instead or use set_index after rename to create an indexed coordinate.\n", - " .rename({\"delta_time\": \"photon_idx\"})\n", - "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:49: UserWarning: Converting non-nanosecond precision datetime values to nanosecond precision. This behavior can eventually be relaxed in xarray, as it is an artifact from pandas which is now beginning to support non-nanosecond precision values. This warning is caused by passing non-nanosecond np.datetime64 or np.timedelta64 values to the DataArray or Variable constructor; it can be silenced by converting the values to nanosecond precision ahead of time.\n", - " df.update({keyword: df[keyword].str[:-1].astype(np.datetime64)})\n", - "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:49: UserWarning: Converting non-nanosecond precision datetime values to nanosecond precision. This behavior can eventually be relaxed in xarray, as it is an artifact from pandas which is now beginning to support non-nanosecond precision values. This warning is caused by passing non-nanosecond np.datetime64 or np.timedelta64 values to the DataArray or Variable constructor; it can be silenced by converting the values to nanosecond precision ahead of time.\n", - " df.update({keyword: df[keyword].str[:-1].astype(np.datetime64)})\n", - "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:490: UserWarning: rename 'delta_time' to 'photon_idx' does not create an index anymore. Try using swap_dims instead or use set_index after rename to create an indexed coordinate.\n", - " .rename({\"delta_time\": \"photon_idx\"})\n", - "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:490: UserWarning: rename 'delta_time' to 'photon_idx' does not create an index anymore. Try using swap_dims instead or use set_index after rename to create an indexed coordinate.\n", - " .rename({\"delta_time\": \"photon_idx\"})\n", - "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:49: UserWarning: Converting non-nanosecond precision datetime values to nanosecond precision. This behavior can eventually be relaxed in xarray, as it is an artifact from pandas which is now beginning to support non-nanosecond precision values. This warning is caused by passing non-nanosecond np.datetime64 or np.timedelta64 values to the DataArray or Variable constructor; it can be silenced by converting the values to nanosecond precision ahead of time.\n", - " df.update({keyword: df[keyword].str[:-1].astype(np.datetime64)})\n", - "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:49: UserWarning: Converting non-nanosecond precision datetime values to nanosecond precision. This behavior can eventually be relaxed in xarray, as it is an artifact from pandas which is now beginning to support non-nanosecond precision values. This warning is caused by passing non-nanosecond np.datetime64 or np.timedelta64 values to the DataArray or Variable constructor; it can be silenced by converting the values to nanosecond precision ahead of time.\n", - " df.update({keyword: df[keyword].str[:-1].astype(np.datetime64)})\n", - "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:490: UserWarning: rename 'delta_time' to 'photon_idx' does not create an index anymore. Try using swap_dims instead or use set_index after rename to create an indexed coordinate.\n", - " .rename({\"delta_time\": \"photon_idx\"})\n", - "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:490: UserWarning: rename 'delta_time' to 'photon_idx' does not create an index anymore. Try using swap_dims instead or use set_index after rename to create an indexed coordinate.\n", - " .rename({\"delta_time\": \"photon_idx\"})\n", - "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:49: UserWarning: Converting non-nanosecond precision datetime values to nanosecond precision. This behavior can eventually be relaxed in xarray, as it is an artifact from pandas which is now beginning to support non-nanosecond precision values. This warning is caused by passing non-nanosecond np.datetime64 or np.timedelta64 values to the DataArray or Variable constructor; it can be silenced by converting the values to nanosecond precision ahead of time.\n", - " df.update({keyword: df[keyword].str[:-1].astype(np.datetime64)})\n", - "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:49: UserWarning: Converting non-nanosecond precision datetime values to nanosecond precision. This behavior can eventually be relaxed in xarray, as it is an artifact from pandas which is now beginning to support non-nanosecond precision values. This warning is caused by passing non-nanosecond np.datetime64 or np.timedelta64 values to the DataArray or Variable constructor; it can be silenced by converting the values to nanosecond precision ahead of time.\n", - " df.update({keyword: df[keyword].str[:-1].astype(np.datetime64)})\n", - "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:490: UserWarning: rename 'delta_time' to 'photon_idx' does not create an index anymore. Try using swap_dims instead or use set_index after rename to create an indexed coordinate.\n", - " .rename({\"delta_time\": \"photon_idx\"})\n", - "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:490: UserWarning: rename 'delta_time' to 'photon_idx' does not create an index anymore. Try using swap_dims instead or use set_index after rename to create an indexed coordinate.\n", - " .rename({\"delta_time\": \"photon_idx\"})\n", - "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:49: UserWarning: Converting non-nanosecond precision datetime values to nanosecond precision. This behavior can eventually be relaxed in xarray, as it is an artifact from pandas which is now beginning to support non-nanosecond precision values. This warning is caused by passing non-nanosecond np.datetime64 or np.timedelta64 values to the DataArray or Variable constructor; it can be silenced by converting the values to nanosecond precision ahead of time.\n", - " df.update({keyword: df[keyword].str[:-1].astype(np.datetime64)})\n", - "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:49: UserWarning: Converting non-nanosecond precision datetime values to nanosecond precision. This behavior can eventually be relaxed in xarray, as it is an artifact from pandas which is now beginning to support non-nanosecond precision values. This warning is caused by passing non-nanosecond np.datetime64 or np.timedelta64 values to the DataArray or Variable constructor; it can be silenced by converting the values to nanosecond precision ahead of time.\n", - " df.update({keyword: df[keyword].str[:-1].astype(np.datetime64)})\n", - "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:490: UserWarning: rename 'delta_time' to 'photon_idx' does not create an index anymore. Try using swap_dims instead or use set_index after rename to create an indexed coordinate.\n", - " .rename({\"delta_time\": \"photon_idx\"})\n", - "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:490: UserWarning: rename 'delta_time' to 'photon_idx' does not create an index anymore. Try using swap_dims instead or use set_index after rename to create an indexed coordinate.\n", - " .rename({\"delta_time\": \"photon_idx\"})\n" - ] - }, - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "
<xarray.Dataset>\n",
-       "Dimensions:              (photon_idx: 29027, spot: 2, gran_idx: 6)\n",
-       "Coordinates:\n",
-       "  * photon_idx           (photon_idx) int64 0 1 2 3 ... 29023 29024 29025 29026\n",
-       "  * spot                 (spot) uint8 2 5\n",
-       "  * gran_idx             (gran_idx) float64 8.49e+04 9.02e+04 ... 1.016e+05\n",
-       "    source_file          (gran_idx) <U72 '../../../../data/ATL06/processed_AT...\n",
-       "    delta_time           (gran_idx, photon_idx) datetime64[ns] 2019-02-22T01:...\n",
-       "Data variables:\n",
-       "    sc_orient            (gran_idx) int8 0 0 0 1 1 1\n",
-       "    cycle_number         (gran_idx) int8 2 2 2 5 5 5\n",
-       "    rgt                  (gran_idx) int16 849 902 910 986 1001 1016\n",
-       "    atlas_sdp_gps_epoch  (gran_idx) datetime64[ns] 2018-01-01T00:00:18 ... 20...\n",
-       "    data_start_utc       (gran_idx) datetime64[ns] 2019-02-22T01:03:44.199777...\n",
-       "    data_end_utc         (gran_idx) datetime64[ns] 2019-02-22T01:07:38.112326...\n",
-       "    h_li                 (spot, gran_idx, photon_idx) float32 nan nan ... nan\n",
-       "    latitude             (spot, gran_idx, photon_idx) float64 nan nan ... nan\n",
-       "    longitude            (spot, gran_idx, photon_idx) float64 nan nan ... nan\n",
-       "    gt                   (gran_idx, spot) <U4 'gt3r' 'gt1l' ... 'gt1l' 'gt3r'\n",
-       "Attributes:\n",
-       "    data_product:  ATL06\n",
-       "    Description:   The land_ice_height group contains the primary set of deri...\n",
-       "    data_rate:     Data within this group are sparse.  Data values are provid...
" - ], - "text/plain": [ - "\n", - "Dimensions: (photon_idx: 29027, spot: 2, gran_idx: 6)\n", - "Coordinates:\n", - " * photon_idx (photon_idx) int64 0 1 2 3 ... 29023 29024 29025 29026\n", - " * spot (spot) uint8 2 5\n", - " * gran_idx (gran_idx) float64 8.49e+04 9.02e+04 ... 1.016e+05\n", - " source_file (gran_idx) " - ] - }, - "execution_count": 8, - "metadata": {}, - "output_type": "execute_result" - }, - { - "data": { - "image/png": "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p6cDkJKSwvTp0x0u16GA59ixY7z44os8/PDDJCQkALBq1SoOHjxoVzlvvfUWUVFRLFiwgNatW1OzZk3uuusu6tSpY83zwgsvcPfdd/P222/TvHlzateuTe/evQkNDXWk6kIIIYT7lXB31o0yMjL48ssveeyxx2x2I9+wYQOhoaHUq1ePESNGWD/jAaKjo8nMzKR79+7WtMjISBo3bsyWLVucq9B1Xn/9dT7++GM++eQTm1WW27dvz+7dux0u1+6AZ+PGjTRp0oTt27fz/fffk5qaCsC+ffuYOnWqXWUtX76cVq1aMWDAAEJDQ2nevDmffPKJ9byu6/z000/Uq1ePHj16EBoaSps2bVi2bFm+Zaanp5OcnGxzCCGEEKWKi8bwKKVyfebltIgUZNmyZVy6dInhw4db03r16sXixYv59ddfmTlzJjt37uTOO++0lhcfH4+Pjw+VK1e2KSssLIz4+Hhn/ho2Dh06RMeOHXOlBwUFcenSJYfLtTvgef7553n99ddZu3YtPj4+1vQuXbqwdetWu8r6559/+Oijj6hbty6rV6/mqaeeYsyYMSxatAiAhIQEUlNTefPNN+nZsydr1qzhvvvuo3///rmWm84xY8YMTCaT9YiKirL3EoUQQogyIT4+3uYzz2QyMWPGjEIfN3/+fHr16kVkZKQ17cEHH6R37940btyYe+65h5UrV3L48GF++umnAstSStm0EjkrIiKCo0eP5krfvHkztWvXdrhcu6el79+/n6+++ipXetWqVblw4YJdZem6TqtWrax9cs2bN+fgwYN89NFHDB061LrgYb9+/Rg3bhwAzZo1Y8uWLXz88cc2/Y45pkyZwvjx462/JycnS9AjhBCiVHHF1hKagvDwcA4dOmST7uvrW+DjTpw4wbp16/j+++8LzBcREUGNGjU4cuQIYHmujIwMEhMTbVp5EhISaN++vYNXkdvIkSN59tln+eyzz9A0jdOnT7N161YmTpzIyy+/7HC5drfwVKpUiTNnzuRK37NnDzfddJNdZUVERNCoUSObtIYNG1pnYIWEhODl5VVgnhv5+voSFBRkcwghhBCljgvG8Gialuszr7CAZ8GCBYSGhtK7d+8C8124cIGTJ08SEREBQMuWLfH29rbO7gI4c+YMBw4ccGnAM3nyZO699166dOlCamoqHTt25IknnmDkyJGMHj3a4XLt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vbs1LTIyksaNG7Nlyxan6lPcpIVHCJGLpnnn+bMQwkVctHmoUork5GSbZF9fX3x9fQt+qFKMHz+e22+/ncaNG1vTe/XqxYABA6hRowYxMTG89NJL3HnnnURHR+Pr60t8fDw+Pj5UrlzZprywsDDi4+OdvKDiJQGPEEIIUUbFx8djMpls0qZOncq0adMKfNzo0aPZt28fmzdvtkl/8MEHrT83btyYVq1aUaNGDX766Sf69++fb3llYY89CXiEEEKIEuaqrSXCw8NzzbQqrHXnmWeeYfny5fz2229Uq1atwLwRERHUqFGDI0eOABAeHk5GRgaJiYk2rTwJCQm0b1/ALM9SQMbwCCGEEO7g9MKDlnF3QUFBNkd+AY9SitGjR/P999/z66+/UqtWrUKreOHCBU6ePElERAQALVu2xNvbm7Vr11rznDlzhgMHDpT6gEdaeIQQQoiSplPiu6U//fTTfPXVV/zwww9UrFjROubGZDLh7+9Pamoq06ZN4/777yciIoLjx4/z73//m5CQEO677z5r3scff5wJEyZQpUoVgoODmThxIk2aNLHO2iqtJOARQgghyoGPPvoIgM6dO9ukL1iwgOHDh2M0Gtm/fz+LFi3i0qVLRERE0KVLF7755hsqVqxozT979my8vLwYOHAgaWlp3HXXXSxcuBCjsXTP5HR7wBMXF8dzzz3HypUrSUtLo169esyfP5+WLVuSmZnJiy++yM8//8w///yDyWSia9euvPnmm0RGRrq76kIIIYRDNFwzhsceqpD8/v7+rF69utBy/Pz8eP/993n//fften53c+sYnsTERDp06IC3tzcrV67kzz//ZObMmVSqVAmAK1eusHv3bl566SV2797N999/z+HDh+nbt687qy2EEEI4xyUbh8pmWvZwawvPW2+9RVRUFAsWLLCm1axZ0/qzyWSyGRgF8P7779O6dWtiY2OpXr16SVVVCCGEcB2F87udS7xjF7e28CxfvpxWrVoxYMAAQkNDad68OZ988kmBj0lKSkLTNGsrkBBCCCFEYdwa8Pzzzz989NFH1K1bl9WrV/PUU08xZswYFi1alGf+q1ev8vzzzzNo0CCCgoLyzJOenk5ycrLNIYQQQpQqimsztRw9pIXHLm7t0tJ1nVatWjF9+nQAmjdvzsGDB/noo48YOnSoTd7MzEweeughdF3nww8/zLfM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rb7++us8y1i8eLHy8fHJld61a1c1cuTIYrmWd999V7Vv397anx4TE1Ngl8mWLVsUoHbt2mVXfZQqmdfG3uvJ8Z///EcFBQXle96Vr01xXo873juuvBZ3v2/suZ4c586dU4cOHVJr1qxRHTp0UHfffbfSdT1Xue547xTXteQo6feNKN08uoVn9OjRhc4cqFmzZp7pbdu2BeDo0aNUqVLFmv7nn39y5513MmLEiHy/NRSkRYsWeHt7c+TIEVq0aGHXY115PeHh4Wzfvt0mT2JiIpmZmbm+weYIDw8nIyODxMREm29ECQkJtG/f3o4rKfq1vP7662zbti3XcvGtWrVi8ODBfP755zbpn376Kc2aNaNly5Z21QdK5rWx93pytG3bluTkZM6ePZvn6+PK16a4rsdd7x1XXou73zf2XE+OkJAQQkJCqFevHg0bNiQqKopt27bRrl07m8e4471TXNeSo6TfN6KUc3fEVVr9+OOPClAnTpywph04cECFhoaqSZMmOVzu/v37FaA2btzoimoW2Y3XkzP48vTp09Y8S5YsKdLgy2+++caadvr06WId4HfixAm1f/9+67F69WoFqG+//VadPHnSJm9KSoqqUKFCobN/8lMSr40913O9999/X/n5+amrV6/med4dr41SRb+esvDeKcq1lJX3TX5iY2MVoNavX2+TXhbeOzfK71quV1rfN8I9JOBRlqbcWbNmqT179qh//vlHffPNNyoyMlL17dvXmienKX7w4ME2Ux4TEhKseU6dOqXq16+vtm/frpRS6ujRo+qVV15RO3fuVDExMeqnn35SDRo0UM2bNy/WKY9FuZ6c6bV33XWX2r17t1q3bp2qVq2azfTaG69HKcsUzmrVqql169ap3bt3qzvvvLNEp3AW1GXy6aefKj8/P3Xx4sVc50rLa3OjvK5n+fLlat68eWr//v3q6NGj6pNPPlFBQUFqzJgx+V6PUu5/bfK7nrL03insWsrS+2b79u3q/fffV3v27FHHjx9Xv/76q7r99ttVnTp1cgUApf29U5RrKcvvG1EyJOBRSkVHR6s2bdook8mk/Pz8VP369dXUqVPV5cuXrXny62uuUaOGNU/ODTLnG0dsbKzq2LGjCg4OVj4+PqpOnTpqzJgx6sKFC26/HqUs32h79+6t/P39VXBwsBo9erTNjfDG61FKqbS0NDV69GgVHBys/P39VZ8+fVRsbGyxXs/1Cgp42rVrpwYNGlTg49z92uRXr+uvZ+XKlapZs2aqQoUKKiAgQDVu3FjNmTNHZWZm5ns9Srn/tbm+XtdfT1l67xR2LUqVnffNvn37VJcuXVRwcLDy9fVVNWvWVE899ZQ6depUrryl/b1TlGspy+8bUTJkt3QhhBBCeDxZh0cIIYQQHk8CHiGEEEJ4PAl4hBBCCOHxJOARQgghhMeTgEcIIYQQHk8CHiGEEEJ4PAl4hBBCCOHxJOARQgghhMeTgEeIQhw/fhxN09i7d2+xlK9pGsuWLXP48Rs2bEDTNDRN49577y0wb+fOnRk7dqzDzyUKlvM6VKpUyd1VEULcQAIeUaoNHz680A/x4hYVFcWZM2do3LgxcC3AuHTpklvrdaNDhw6xcOFCd1ejXMjv3+WZM2eYM2dOiddHCFE4CXiEKITRaCQ8PBwvLy93V6VAoaGhpaJlITMz091VcJvw8HBMJpO7qyGEyIMEPKJM27hxI61bt8bX15eIiAief/55srKyrOc7d+7MmDFjmDx5MsHBwYSHhzNt2jSbMv7++29uv/12/Pz8aNSoEevWrbPpZrq+S+v48eN06dIFgMqVK6NpGsOHDwegZs2aub7dN2vWzOb5jhw5QseOHa3PtXbt2lzXFBcXx4MPPkjlypWpUqUK/fr14/jx43b/bS5fvszQoUOpUKECERERzJw5M1eejIwMJk+ezE033URgYCBt2rRhw4YNNnk++eQToqKiCAgI4L777mPWrFk2gdW0adNo1qwZn332GbVr18bX1xelFElJSTz55JOEhoYSFBTEnXfeyR9//GFT9o8//kjLli3x8/Ojdu3avPLKKzav37Rp06hevTq+vr5ERkYyZsyYIl17Ydd14cIFHn74YapVq0ZAQABNmjTh66+/tinj22+/pUmTJvj7+1OlShW6du3K5cuXmTZtGp9//jk//PCDtQvrxr+ZEKL0Kd1fWYUoQFxcHHfffTfDhw9n0aJF/P3334wYMQI/Pz+bIOPzzz9n/PjxbN++na1btzJ8+HA6dOhAt27d0HWde++9l+rVq7N9+3ZSUlKYMGFCvs8ZFRXFd999x/3338+hQ4cICgrC39+/SPXVdZ3+/fsTEhLCtm3bSE5OzjWe5sqVK3Tp0oU77riD3377DS8vL15//XV69uzJvn378PHxKfLfZ9KkSaxfv56lS5cSHh7Ov//9b6Kjo2nWrJk1z6OPPsrx48dZsmQJkZGRLF26lJ49e7J//37q1q3L77//zlNPPcVbb71F3759WbduHS+99FKu5zp69Cj//e9/+e677zAajQD07t2b4OBgfv75Z0wmE3PnzuWuu+7i8OHDBAcHs3r1ah555BHee+897rjjDo4dO8aTTz4JwNSpU/n222+ZPXs2S5Ys4ZZbbiE+Pj5XwJSfwq7r6tWrtGzZkueee46goCB++uknhgwZQu3atWnTpg1nzpzh4Ycf5u233+a+++4jJSWFTZs2oZRi4sSJ/PXXXyQnJ7NgwQIAgoODi/y6CCHcxL2btQtRsGHDhql+/frlee7f//63ql+/vtJ13Zr2f//3f6pChQrKbDYrpZTq1KmTuv32220ed9ttt6nnnntOKaXUypUrlZeXlzpz5oz1/Nq1axWgli5dqpRSKiYmRgFqz549Siml1q9frwCVmJhoU26NGjXU7NmzbdKaNm2qpk6dqpRSavXq1cpoNKqTJ09az69cudLmuebPn5/rmtLT05W/v79avXp1nn+HvOqTkpKifHx81JIlS6xpFy5cUP7+/urZZ59VSil19OhRpWmaiouLsynvrrvuUlOmTFFKKfXggw+q3r1725wfPHiwMplM1t+nTp2qvL29VUJCgjXtl19+UUFBQerq1as2j61Tp46aO3euUkqpO+64Q02fPt3m/BdffKEiIiKUUkrNnDlT1atXT2VkZOR53fkpynXl5e6771YTJkxQSikVHR2tAHX8+PE88xb073LBggU2fx8hROkgLTyizPrrr79o164dmqZZ0zp06EBqaiqnTp2ievXqANx66602j4uIiCAhIQGwDPSNiooiPDzcer5169bFVt/q1atTrVo1a1q7du1s8kRHR3P06FEqVqxok3716lWOHTtW5Oc6duwYGRkZNuUHBwdTv3596++7d+9GKUW9evVsHpuenk6VKlUAy9/nvvvusznfunVrVqxYYZNWo0YNqlatanMdqamp1nJypKWlWa8jOjqanTt38sYbb1jPm81mrl69ypUrVxgwYABz5syhdu3a9OzZk7vvvpt77rmn0LFURbkus9nMm2++yTfffENcXBzp6emkp6cTGBgIQNOmTbnrrrto0qQJPXr0oHv37jzwwANUrly5wOcWQpReEvCI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", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "ds.plot.scatter(x=\"longitude\", y=\"latitude\", hue=\"h_li\", vmin=-100, vmax=2000)" ] @@ -754,7 +132,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": null, "id": "63da2b3c", "metadata": {}, "outputs": [], @@ -765,24 +143,10 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": null, "id": "e6f7c047", "metadata": {}, - "outputs": [ - { - "ename": "AttributeError", - "evalue": "'Query' object has no attribute '_session'", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mAttributeError\u001b[0m Traceback (most recent call last)", - "Cell \u001b[0;32mIn[10], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mregion_a\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mdownload_granules\u001b[49m\u001b[43m(\u001b[49m\u001b[43mpath\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mpath_root\u001b[49m\u001b[43m)\u001b[49m\n", - "File \u001b[0;32m~/envs/general/lib/python3.11/site-packages/icepyx/core/query.py:1129\u001b[0m, in \u001b[0;36mQuery.download_granules\u001b[0;34m(self, path, verbose, subset, restart, **kwargs)\u001b[0m\n\u001b[1;32m 1124\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m 1125\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m (\n\u001b[1;32m 1126\u001b[0m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28mhasattr\u001b[39m(\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_granules, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124morderIDs\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[1;32m 1127\u001b[0m \u001b[38;5;129;01mor\u001b[39;00m \u001b[38;5;28mlen\u001b[39m(\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_granules\u001b[38;5;241m.\u001b[39morderIDs) \u001b[38;5;241m==\u001b[39m \u001b[38;5;241m0\u001b[39m\n\u001b[1;32m 1128\u001b[0m ):\n\u001b[0;32m-> 1129\u001b[0m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43morder_granules\u001b[49m\u001b[43m(\u001b[49m\u001b[43mverbose\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mverbose\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43msubset\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43msubset\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 1131\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_granules\u001b[38;5;241m.\u001b[39mdownload(verbose, path, session\u001b[38;5;241m=\u001b[39m\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_session, restart\u001b[38;5;241m=\u001b[39mrestart)\n", - "File \u001b[0;32m~/envs/general/lib/python3.11/site-packages/icepyx/core/query.py:1065\u001b[0m, in \u001b[0;36mQuery.order_granules\u001b[0;34m(self, verbose, subset, email, **kwargs)\u001b[0m\n\u001b[1;32m 1048\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_granules\u001b[38;5;241m.\u001b[39mplace_order(\n\u001b[1;32m 1049\u001b[0m tempCMRparams,\n\u001b[1;32m 1050\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mreqparams,\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 1055\u001b[0m geom_filepath\u001b[38;5;241m=\u001b[39m\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_spatial\u001b[38;5;241m.\u001b[39m_geom_file,\n\u001b[1;32m 1056\u001b[0m )\n\u001b[1;32m 1058\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m 1059\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_granules\u001b[38;5;241m.\u001b[39mplace_order(\n\u001b[1;32m 1060\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mCMRparams,\n\u001b[1;32m 1061\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mreqparams,\n\u001b[1;32m 1062\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39msubsetparams(\u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs),\n\u001b[1;32m 1063\u001b[0m verbose,\n\u001b[1;32m 1064\u001b[0m subset,\n\u001b[0;32m-> 1065\u001b[0m session\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_session\u001b[49m,\n\u001b[1;32m 1066\u001b[0m geom_filepath\u001b[38;5;241m=\u001b[39m\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_spatial\u001b[38;5;241m.\u001b[39m_geom_file,\n\u001b[1;32m 1067\u001b[0m )\n", - "\u001b[0;31mAttributeError\u001b[0m: 'Query' object has no attribute '_session'" - ] - } - ], + "outputs": [], "source": [ "region_a.download_granules(path=path_root)" ] @@ -823,7 +187,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": null, "id": "9cde6679", "metadata": {}, "outputs": [], @@ -833,7 +197,7 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": null, "id": "8b6edf0c", "metadata": {}, "outputs": [], @@ -843,7 +207,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": null, "id": "e683ebf7", "metadata": {}, "outputs": [], @@ -870,7 +234,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": null, "id": "7318abd0", "metadata": {}, "outputs": [], @@ -881,7 +245,7 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": null, "id": "f43e8664", "metadata": {}, "outputs": [], @@ -891,7 +255,7 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": null, "id": "992a77fb", "metadata": {}, "outputs": [], @@ -901,7 +265,7 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": null, "id": "6aec1a70", "metadata": {}, "outputs": [], @@ -928,46 +292,22 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": null, "id": "39bd7eb8", "metadata": { "scrolled": true }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "You have 6 files matching the filename pattern to be read in.\n" - ] - } - ], + "outputs": [], "source": [ "reader = ipx.Read(data_source=path_root, product=\"ATL06\", filename_pattern=pattern) # or ipx.Read(filepath, \"ATLXX\") if your filenames match the default pattern" ] }, { "cell_type": "code", - "execution_count": 20, + "execution_count": null, "id": "6c9ebc4a", "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "['../../../../data/ATL06/processed_ATL06_20190226005526_09100205_006_02.h5',\n", - " '../../../../data/ATL06/processed_ATL06_20191201105502_10010505_006_01.h5',\n", - " '../../../../data/ATL06/processed_ATL06_20190225121032_09020203_006_02.h5',\n", - " '../../../../data/ATL06/processed_ATL06_20190222010344_08490205_006_02.h5',\n", - " '../../../../data/ATL06/processed_ATL06_20191130112041_09860505_006_01.h5',\n", - " '../../../../data/ATL06/processed_ATL06_20191202102922_10160505_006_01.h5']" - ] - }, - "execution_count": 20, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "reader._filelist" ] @@ -990,616 +330,12 @@ }, { "cell_type": "code", - "execution_count": 21, + "execution_count": null, "id": "18f65f67", "metadata": { "scrolled": true }, - "outputs": [ - { - "data": { - "text/plain": [ - "['ancillary_data/atlas_sdp_gps_epoch',\n", - " 'ancillary_data/control',\n", - " 'ancillary_data/data_end_utc',\n", - " 'ancillary_data/data_start_utc',\n", - " 'ancillary_data/end_cycle',\n", - " 'ancillary_data/end_delta_time',\n", - " 'ancillary_data/end_geoseg',\n", - " 'ancillary_data/end_gpssow',\n", - " 'ancillary_data/end_gpsweek',\n", - " 'ancillary_data/end_orbit',\n", - " 'ancillary_data/end_region',\n", - " 'ancillary_data/end_rgt',\n", - " 'ancillary_data/granule_end_utc',\n", - " 'ancillary_data/granule_start_utc',\n", - " 'ancillary_data/land_ice/dt_hist',\n", - " 'ancillary_data/land_ice/fit_maxiter',\n", - " 'ancillary_data/land_ice/fpb_maxiter',\n", - " 'ancillary_data/land_ice/max_res_ids',\n", - " 'ancillary_data/land_ice/min_dist',\n", - " 'ancillary_data/land_ice/min_gain_th',\n", - " 'ancillary_data/land_ice/min_n_pe',\n", - " 'ancillary_data/land_ice/min_n_sel',\n", - " 'ancillary_data/land_ice/min_signal_conf',\n", - " 'ancillary_data/land_ice/n_hist',\n", - " 'ancillary_data/land_ice/n_sigmas',\n", - " 'ancillary_data/land_ice/nhist_bins',\n", - " 'ancillary_data/land_ice/proc_interval',\n", - " 'ancillary_data/land_ice/qs_lim_bsc',\n", - " 'ancillary_data/land_ice/qs_lim_hrs',\n", - " 'ancillary_data/land_ice/qs_lim_hsigma',\n", - " 'ancillary_data/land_ice/qs_lim_msw',\n", - " 'ancillary_data/land_ice/qs_lim_snr',\n", - " 'ancillary_data/land_ice/qs_lim_sss',\n", - " 'ancillary_data/land_ice/rbin_width',\n", - " 'ancillary_data/land_ice/sigma_beam',\n", - " 'ancillary_data/land_ice/sigma_tx',\n", - " 'ancillary_data/land_ice/t_dead',\n", - " 'ancillary_data/land_ice/txp_maxiter',\n", - " 'ancillary_data/qa_at_interval',\n", - " 'ancillary_data/release',\n", - " 'ancillary_data/start_cycle',\n", - " 'ancillary_data/start_delta_time',\n", - " 'ancillary_data/start_geoseg',\n", - " 'ancillary_data/start_gpssow',\n", - " 'ancillary_data/start_gpsweek',\n", - " 'ancillary_data/start_orbit',\n", - " 'ancillary_data/start_region',\n", - " 'ancillary_data/start_rgt',\n", - " 'ancillary_data/version',\n", - " 'gt1l/land_ice_segments/atl06_quality_summary',\n", - " 'gt1l/land_ice_segments/bias_correction/fpb_mean_corr',\n", - " 'gt1l/land_ice_segments/bias_correction/fpb_mean_corr_sigma',\n", - " 'gt1l/land_ice_segments/bias_correction/fpb_med_corr',\n", - " 'gt1l/land_ice_segments/bias_correction/fpb_med_corr_sigma',\n", - " 'gt1l/land_ice_segments/bias_correction/fpb_n_corr',\n", - " 'gt1l/land_ice_segments/bias_correction/med_r_fit',\n", - " 'gt1l/land_ice_segments/bias_correction/tx_mean_corr',\n", - " 'gt1l/land_ice_segments/bias_correction/tx_med_corr',\n", - " 'gt1l/land_ice_segments/delta_time',\n", - " 'gt1l/land_ice_segments/dem/dem_flag',\n", - " 'gt1l/land_ice_segments/dem/dem_h',\n", - " 'gt1l/land_ice_segments/dem/geoid_free2mean',\n", - " 'gt1l/land_ice_segments/dem/geoid_h',\n", - " 'gt1l/land_ice_segments/fit_statistics/dh_fit_dx',\n", - " 'gt1l/land_ice_segments/fit_statistics/dh_fit_dx_sigma',\n", - " 'gt1l/land_ice_segments/fit_statistics/dh_fit_dy',\n", - " 'gt1l/land_ice_segments/fit_statistics/h_expected_rms',\n", - " 'gt1l/land_ice_segments/fit_statistics/h_mean',\n", - " 'gt1l/land_ice_segments/fit_statistics/h_rms_misfit',\n", - " 'gt1l/land_ice_segments/fit_statistics/h_robust_sprd',\n", - " 'gt1l/land_ice_segments/fit_statistics/n_fit_photons',\n", - " 'gt1l/land_ice_segments/fit_statistics/n_seg_pulses',\n", - " 'gt1l/land_ice_segments/fit_statistics/sigma_h_mean',\n", - " 'gt1l/land_ice_segments/fit_statistics/signal_selection_source',\n", - " 'gt1l/land_ice_segments/fit_statistics/signal_selection_source_status',\n", - " 'gt1l/land_ice_segments/fit_statistics/snr',\n", - " 'gt1l/land_ice_segments/fit_statistics/snr_significance',\n", - " 'gt1l/land_ice_segments/fit_statistics/w_surface_window_final',\n", - " 'gt1l/land_ice_segments/geophysical/bckgrd',\n", - " 'gt1l/land_ice_segments/geophysical/bsnow_conf',\n", - " 'gt1l/land_ice_segments/geophysical/bsnow_h',\n", - " 'gt1l/land_ice_segments/geophysical/bsnow_od',\n", - " 'gt1l/land_ice_segments/geophysical/cloud_flg_asr',\n", - " 'gt1l/land_ice_segments/geophysical/cloud_flg_atm',\n", - " 'gt1l/land_ice_segments/geophysical/dac',\n", - " 'gt1l/land_ice_segments/geophysical/e_bckgrd',\n", - " 'gt1l/land_ice_segments/geophysical/layer_flag',\n", - " 'gt1l/land_ice_segments/geophysical/msw_flag',\n", - " 'gt1l/land_ice_segments/geophysical/neutat_delay_total',\n", - " 'gt1l/land_ice_segments/geophysical/r_eff',\n", - " 'gt1l/land_ice_segments/geophysical/solar_azimuth',\n", - " 'gt1l/land_ice_segments/geophysical/solar_elevation',\n", - " 'gt1l/land_ice_segments/geophysical/tide_earth',\n", - " 'gt1l/land_ice_segments/geophysical/tide_earth_free2mean',\n", - " 'gt1l/land_ice_segments/geophysical/tide_equilibrium',\n", - " 'gt1l/land_ice_segments/geophysical/tide_load',\n", - " 'gt1l/land_ice_segments/geophysical/tide_ocean',\n", - " 'gt1l/land_ice_segments/geophysical/tide_pole',\n", - " 'gt1l/land_ice_segments/ground_track/ref_azimuth',\n", - " 'gt1l/land_ice_segments/ground_track/ref_coelv',\n", - " 'gt1l/land_ice_segments/ground_track/seg_azimuth',\n", - " 'gt1l/land_ice_segments/ground_track/sigma_geo_at',\n", - " 'gt1l/land_ice_segments/ground_track/sigma_geo_r',\n", - " 'gt1l/land_ice_segments/ground_track/sigma_geo_xt',\n", - " 'gt1l/land_ice_segments/ground_track/x_atc',\n", - " 'gt1l/land_ice_segments/ground_track/y_atc',\n", - " 'gt1l/land_ice_segments/h_li',\n", - " 'gt1l/land_ice_segments/h_li_sigma',\n", - " 'gt1l/land_ice_segments/latitude',\n", - " 'gt1l/land_ice_segments/longitude',\n", - " 'gt1l/land_ice_segments/segment_id',\n", - " 'gt1l/land_ice_segments/sigma_geo_h',\n", - " 'gt1l/residual_histogram/bckgrd_per_m',\n", - " 'gt1l/residual_histogram/bin_top_h',\n", - " 'gt1l/residual_histogram/count',\n", - " 'gt1l/residual_histogram/delta_time',\n", - " 'gt1l/residual_histogram/ds_segment_id',\n", - " 'gt1l/residual_histogram/lat_mean',\n", - " 'gt1l/residual_histogram/lon_mean',\n", - " 'gt1l/residual_histogram/pulse_count',\n", - " 'gt1l/residual_histogram/segment_id_list',\n", - " 'gt1l/residual_histogram/x_atc_mean',\n", - " 'gt1l/segment_quality/delta_time',\n", - " 'gt1l/segment_quality/record_number',\n", - " 'gt1l/segment_quality/reference_pt_lat',\n", - " 'gt1l/segment_quality/reference_pt_lon',\n", - " 'gt1l/segment_quality/segment_id',\n", - " 'gt1l/segment_quality/signal_selection_source',\n", - " 'gt1l/segment_quality/signal_selection_status/signal_selection_status_all',\n", - " 'gt1l/segment_quality/signal_selection_status/signal_selection_status_backup',\n", - " 'gt1l/segment_quality/signal_selection_status/signal_selection_status_confident',\n", - " 'gt1r/land_ice_segments/atl06_quality_summary',\n", - " 'gt1r/land_ice_segments/bias_correction/fpb_mean_corr',\n", - " 'gt1r/land_ice_segments/bias_correction/fpb_mean_corr_sigma',\n", - " 'gt1r/land_ice_segments/bias_correction/fpb_med_corr',\n", - " 'gt1r/land_ice_segments/bias_correction/fpb_med_corr_sigma',\n", - " 'gt1r/land_ice_segments/bias_correction/fpb_n_corr',\n", - " 'gt1r/land_ice_segments/bias_correction/med_r_fit',\n", - " 'gt1r/land_ice_segments/bias_correction/tx_mean_corr',\n", - " 'gt1r/land_ice_segments/bias_correction/tx_med_corr',\n", - " 'gt1r/land_ice_segments/delta_time',\n", - " 'gt1r/land_ice_segments/dem/dem_flag',\n", - " 'gt1r/land_ice_segments/dem/dem_h',\n", - " 'gt1r/land_ice_segments/dem/geoid_free2mean',\n", - " 'gt1r/land_ice_segments/dem/geoid_h',\n", - " 'gt1r/land_ice_segments/fit_statistics/dh_fit_dx',\n", - " 'gt1r/land_ice_segments/fit_statistics/dh_fit_dx_sigma',\n", - " 'gt1r/land_ice_segments/fit_statistics/dh_fit_dy',\n", - " 'gt1r/land_ice_segments/fit_statistics/h_expected_rms',\n", - " 'gt1r/land_ice_segments/fit_statistics/h_mean',\n", - " 'gt1r/land_ice_segments/fit_statistics/h_rms_misfit',\n", - " 'gt1r/land_ice_segments/fit_statistics/h_robust_sprd',\n", - " 'gt1r/land_ice_segments/fit_statistics/n_fit_photons',\n", - " 'gt1r/land_ice_segments/fit_statistics/n_seg_pulses',\n", - " 'gt1r/land_ice_segments/fit_statistics/sigma_h_mean',\n", - " 'gt1r/land_ice_segments/fit_statistics/signal_selection_source',\n", - " 'gt1r/land_ice_segments/fit_statistics/signal_selection_source_status',\n", - " 'gt1r/land_ice_segments/fit_statistics/snr',\n", - " 'gt1r/land_ice_segments/fit_statistics/snr_significance',\n", - " 'gt1r/land_ice_segments/fit_statistics/w_surface_window_final',\n", - " 'gt1r/land_ice_segments/geophysical/bckgrd',\n", - " 'gt1r/land_ice_segments/geophysical/bsnow_conf',\n", - " 'gt1r/land_ice_segments/geophysical/bsnow_h',\n", - " 'gt1r/land_ice_segments/geophysical/bsnow_od',\n", - " 'gt1r/land_ice_segments/geophysical/cloud_flg_asr',\n", - " 'gt1r/land_ice_segments/geophysical/cloud_flg_atm',\n", - " 'gt1r/land_ice_segments/geophysical/dac',\n", - " 'gt1r/land_ice_segments/geophysical/e_bckgrd',\n", - " 'gt1r/land_ice_segments/geophysical/layer_flag',\n", - " 'gt1r/land_ice_segments/geophysical/msw_flag',\n", - " 'gt1r/land_ice_segments/geophysical/neutat_delay_total',\n", - " 'gt1r/land_ice_segments/geophysical/r_eff',\n", - " 'gt1r/land_ice_segments/geophysical/solar_azimuth',\n", - " 'gt1r/land_ice_segments/geophysical/solar_elevation',\n", - " 'gt1r/land_ice_segments/geophysical/tide_earth',\n", - " 'gt1r/land_ice_segments/geophysical/tide_earth_free2mean',\n", - " 'gt1r/land_ice_segments/geophysical/tide_equilibrium',\n", - " 'gt1r/land_ice_segments/geophysical/tide_load',\n", - " 'gt1r/land_ice_segments/geophysical/tide_ocean',\n", - " 'gt1r/land_ice_segments/geophysical/tide_pole',\n", - " 'gt1r/land_ice_segments/ground_track/ref_azimuth',\n", - " 'gt1r/land_ice_segments/ground_track/ref_coelv',\n", - " 'gt1r/land_ice_segments/ground_track/seg_azimuth',\n", - " 'gt1r/land_ice_segments/ground_track/sigma_geo_at',\n", - " 'gt1r/land_ice_segments/ground_track/sigma_geo_r',\n", - " 'gt1r/land_ice_segments/ground_track/sigma_geo_xt',\n", - " 'gt1r/land_ice_segments/ground_track/x_atc',\n", - " 'gt1r/land_ice_segments/ground_track/y_atc',\n", - " 'gt1r/land_ice_segments/h_li',\n", - " 'gt1r/land_ice_segments/h_li_sigma',\n", - " 'gt1r/land_ice_segments/latitude',\n", - " 'gt1r/land_ice_segments/longitude',\n", - " 'gt1r/land_ice_segments/segment_id',\n", - " 'gt1r/land_ice_segments/sigma_geo_h',\n", - " 'gt1r/residual_histogram/bckgrd_per_m',\n", - " 'gt1r/residual_histogram/bin_top_h',\n", - " 'gt1r/residual_histogram/count',\n", - " 'gt1r/residual_histogram/delta_time',\n", - " 'gt1r/residual_histogram/ds_segment_id',\n", - " 'gt1r/residual_histogram/lat_mean',\n", - " 'gt1r/residual_histogram/lon_mean',\n", - " 'gt1r/residual_histogram/pulse_count',\n", - " 'gt1r/residual_histogram/segment_id_list',\n", - " 'gt1r/residual_histogram/x_atc_mean',\n", - " 'gt1r/segment_quality/delta_time',\n", - " 'gt1r/segment_quality/record_number',\n", - " 'gt1r/segment_quality/reference_pt_lat',\n", - " 'gt1r/segment_quality/reference_pt_lon',\n", - " 'gt1r/segment_quality/segment_id',\n", - " 'gt1r/segment_quality/signal_selection_source',\n", - " 'gt1r/segment_quality/signal_selection_status/signal_selection_status_all',\n", - " 'gt1r/segment_quality/signal_selection_status/signal_selection_status_backup',\n", - " 'gt1r/segment_quality/signal_selection_status/signal_selection_status_confident',\n", - " 'gt2l/land_ice_segments/atl06_quality_summary',\n", - " 'gt2l/land_ice_segments/bias_correction/fpb_mean_corr',\n", - " 'gt2l/land_ice_segments/bias_correction/fpb_mean_corr_sigma',\n", - " 'gt2l/land_ice_segments/bias_correction/fpb_med_corr',\n", - " 'gt2l/land_ice_segments/bias_correction/fpb_med_corr_sigma',\n", - " 'gt2l/land_ice_segments/bias_correction/fpb_n_corr',\n", - " 'gt2l/land_ice_segments/bias_correction/med_r_fit',\n", - " 'gt2l/land_ice_segments/bias_correction/tx_mean_corr',\n", - " 'gt2l/land_ice_segments/bias_correction/tx_med_corr',\n", - " 'gt2l/land_ice_segments/delta_time',\n", - " 'gt2l/land_ice_segments/dem/dem_flag',\n", - " 'gt2l/land_ice_segments/dem/dem_h',\n", - " 'gt2l/land_ice_segments/dem/geoid_free2mean',\n", - " 'gt2l/land_ice_segments/dem/geoid_h',\n", - " 'gt2l/land_ice_segments/fit_statistics/dh_fit_dx',\n", - " 'gt2l/land_ice_segments/fit_statistics/dh_fit_dx_sigma',\n", - " 'gt2l/land_ice_segments/fit_statistics/dh_fit_dy',\n", - " 'gt2l/land_ice_segments/fit_statistics/h_expected_rms',\n", - " 'gt2l/land_ice_segments/fit_statistics/h_mean',\n", - " 'gt2l/land_ice_segments/fit_statistics/h_rms_misfit',\n", - " 'gt2l/land_ice_segments/fit_statistics/h_robust_sprd',\n", - " 'gt2l/land_ice_segments/fit_statistics/n_fit_photons',\n", - " 'gt2l/land_ice_segments/fit_statistics/n_seg_pulses',\n", - " 'gt2l/land_ice_segments/fit_statistics/sigma_h_mean',\n", - " 'gt2l/land_ice_segments/fit_statistics/signal_selection_source',\n", - " 'gt2l/land_ice_segments/fit_statistics/signal_selection_source_status',\n", - " 'gt2l/land_ice_segments/fit_statistics/snr',\n", - " 'gt2l/land_ice_segments/fit_statistics/snr_significance',\n", - " 'gt2l/land_ice_segments/fit_statistics/w_surface_window_final',\n", - " 'gt2l/land_ice_segments/geophysical/bckgrd',\n", - " 'gt2l/land_ice_segments/geophysical/bsnow_conf',\n", - " 'gt2l/land_ice_segments/geophysical/bsnow_h',\n", - " 'gt2l/land_ice_segments/geophysical/bsnow_od',\n", - " 'gt2l/land_ice_segments/geophysical/cloud_flg_asr',\n", - " 'gt2l/land_ice_segments/geophysical/cloud_flg_atm',\n", - " 'gt2l/land_ice_segments/geophysical/dac',\n", - " 'gt2l/land_ice_segments/geophysical/e_bckgrd',\n", - " 'gt2l/land_ice_segments/geophysical/layer_flag',\n", - " 'gt2l/land_ice_segments/geophysical/msw_flag',\n", - " 'gt2l/land_ice_segments/geophysical/neutat_delay_total',\n", - " 'gt2l/land_ice_segments/geophysical/r_eff',\n", - " 'gt2l/land_ice_segments/geophysical/solar_azimuth',\n", - " 'gt2l/land_ice_segments/geophysical/solar_elevation',\n", - " 'gt2l/land_ice_segments/geophysical/tide_earth',\n", - " 'gt2l/land_ice_segments/geophysical/tide_earth_free2mean',\n", - " 'gt2l/land_ice_segments/geophysical/tide_equilibrium',\n", - " 'gt2l/land_ice_segments/geophysical/tide_load',\n", - " 'gt2l/land_ice_segments/geophysical/tide_ocean',\n", - " 'gt2l/land_ice_segments/geophysical/tide_pole',\n", - " 'gt2l/land_ice_segments/ground_track/ref_azimuth',\n", - " 'gt2l/land_ice_segments/ground_track/ref_coelv',\n", - " 'gt2l/land_ice_segments/ground_track/seg_azimuth',\n", - " 'gt2l/land_ice_segments/ground_track/sigma_geo_at',\n", - " 'gt2l/land_ice_segments/ground_track/sigma_geo_r',\n", - " 'gt2l/land_ice_segments/ground_track/sigma_geo_xt',\n", - " 'gt2l/land_ice_segments/ground_track/x_atc',\n", - " 'gt2l/land_ice_segments/ground_track/y_atc',\n", - " 'gt2l/land_ice_segments/h_li',\n", - " 'gt2l/land_ice_segments/h_li_sigma',\n", - " 'gt2l/land_ice_segments/latitude',\n", - " 'gt2l/land_ice_segments/longitude',\n", - " 'gt2l/land_ice_segments/segment_id',\n", - " 'gt2l/land_ice_segments/sigma_geo_h',\n", - " 'gt2l/residual_histogram/bckgrd_per_m',\n", - " 'gt2l/residual_histogram/bin_top_h',\n", - " 'gt2l/residual_histogram/count',\n", - " 'gt2l/residual_histogram/delta_time',\n", - " 'gt2l/residual_histogram/ds_segment_id',\n", - " 'gt2l/residual_histogram/lat_mean',\n", - " 'gt2l/residual_histogram/lon_mean',\n", - " 'gt2l/residual_histogram/pulse_count',\n", - " 'gt2l/residual_histogram/segment_id_list',\n", - " 'gt2l/residual_histogram/x_atc_mean',\n", - " 'gt2l/segment_quality/delta_time',\n", - " 'gt2l/segment_quality/record_number',\n", - " 'gt2l/segment_quality/reference_pt_lat',\n", - " 'gt2l/segment_quality/reference_pt_lon',\n", - " 'gt2l/segment_quality/segment_id',\n", - " 'gt2l/segment_quality/signal_selection_source',\n", - " 'gt2l/segment_quality/signal_selection_status/signal_selection_status_all',\n", - " 'gt2l/segment_quality/signal_selection_status/signal_selection_status_backup',\n", - " 'gt2l/segment_quality/signal_selection_status/signal_selection_status_confident',\n", - " 'gt2r/land_ice_segments/atl06_quality_summary',\n", - " 'gt2r/land_ice_segments/bias_correction/fpb_mean_corr',\n", - " 'gt2r/land_ice_segments/bias_correction/fpb_mean_corr_sigma',\n", - " 'gt2r/land_ice_segments/bias_correction/fpb_med_corr',\n", - " 'gt2r/land_ice_segments/bias_correction/fpb_med_corr_sigma',\n", - " 'gt2r/land_ice_segments/bias_correction/fpb_n_corr',\n", - " 'gt2r/land_ice_segments/bias_correction/med_r_fit',\n", - " 'gt2r/land_ice_segments/bias_correction/tx_mean_corr',\n", - " 'gt2r/land_ice_segments/bias_correction/tx_med_corr',\n", - " 'gt2r/land_ice_segments/delta_time',\n", - " 'gt2r/land_ice_segments/dem/dem_flag',\n", - " 'gt2r/land_ice_segments/dem/dem_h',\n", - " 'gt2r/land_ice_segments/dem/geoid_free2mean',\n", - " 'gt2r/land_ice_segments/dem/geoid_h',\n", - " 'gt2r/land_ice_segments/fit_statistics/dh_fit_dx',\n", - " 'gt2r/land_ice_segments/fit_statistics/dh_fit_dx_sigma',\n", - " 'gt2r/land_ice_segments/fit_statistics/dh_fit_dy',\n", - " 'gt2r/land_ice_segments/fit_statistics/h_expected_rms',\n", - " 'gt2r/land_ice_segments/fit_statistics/h_mean',\n", - " 'gt2r/land_ice_segments/fit_statistics/h_rms_misfit',\n", - " 'gt2r/land_ice_segments/fit_statistics/h_robust_sprd',\n", - " 'gt2r/land_ice_segments/fit_statistics/n_fit_photons',\n", - " 'gt2r/land_ice_segments/fit_statistics/n_seg_pulses',\n", - " 'gt2r/land_ice_segments/fit_statistics/sigma_h_mean',\n", - " 'gt2r/land_ice_segments/fit_statistics/signal_selection_source',\n", - " 'gt2r/land_ice_segments/fit_statistics/signal_selection_source_status',\n", - " 'gt2r/land_ice_segments/fit_statistics/snr',\n", - " 'gt2r/land_ice_segments/fit_statistics/snr_significance',\n", - " 'gt2r/land_ice_segments/fit_statistics/w_surface_window_final',\n", - " 'gt2r/land_ice_segments/geophysical/bckgrd',\n", - " 'gt2r/land_ice_segments/geophysical/bsnow_conf',\n", - " 'gt2r/land_ice_segments/geophysical/bsnow_h',\n", - " 'gt2r/land_ice_segments/geophysical/bsnow_od',\n", - " 'gt2r/land_ice_segments/geophysical/cloud_flg_asr',\n", - " 'gt2r/land_ice_segments/geophysical/cloud_flg_atm',\n", - " 'gt2r/land_ice_segments/geophysical/dac',\n", - " 'gt2r/land_ice_segments/geophysical/e_bckgrd',\n", - " 'gt2r/land_ice_segments/geophysical/layer_flag',\n", - " 'gt2r/land_ice_segments/geophysical/msw_flag',\n", - " 'gt2r/land_ice_segments/geophysical/neutat_delay_total',\n", - " 'gt2r/land_ice_segments/geophysical/r_eff',\n", - " 'gt2r/land_ice_segments/geophysical/solar_azimuth',\n", - " 'gt2r/land_ice_segments/geophysical/solar_elevation',\n", - " 'gt2r/land_ice_segments/geophysical/tide_earth',\n", - " 'gt2r/land_ice_segments/geophysical/tide_earth_free2mean',\n", - " 'gt2r/land_ice_segments/geophysical/tide_equilibrium',\n", - " 'gt2r/land_ice_segments/geophysical/tide_load',\n", - " 'gt2r/land_ice_segments/geophysical/tide_ocean',\n", - " 'gt2r/land_ice_segments/geophysical/tide_pole',\n", - " 'gt2r/land_ice_segments/ground_track/ref_azimuth',\n", - " 'gt2r/land_ice_segments/ground_track/ref_coelv',\n", - " 'gt2r/land_ice_segments/ground_track/seg_azimuth',\n", - " 'gt2r/land_ice_segments/ground_track/sigma_geo_at',\n", - " 'gt2r/land_ice_segments/ground_track/sigma_geo_r',\n", - " 'gt2r/land_ice_segments/ground_track/sigma_geo_xt',\n", - " 'gt2r/land_ice_segments/ground_track/x_atc',\n", - " 'gt2r/land_ice_segments/ground_track/y_atc',\n", - " 'gt2r/land_ice_segments/h_li',\n", - " 'gt2r/land_ice_segments/h_li_sigma',\n", - " 'gt2r/land_ice_segments/latitude',\n", - " 'gt2r/land_ice_segments/longitude',\n", - " 'gt2r/land_ice_segments/segment_id',\n", - " 'gt2r/land_ice_segments/sigma_geo_h',\n", - " 'gt2r/residual_histogram/bckgrd_per_m',\n", - " 'gt2r/residual_histogram/bin_top_h',\n", - " 'gt2r/residual_histogram/count',\n", - " 'gt2r/residual_histogram/delta_time',\n", - " 'gt2r/residual_histogram/ds_segment_id',\n", - " 'gt2r/residual_histogram/lat_mean',\n", - " 'gt2r/residual_histogram/lon_mean',\n", - " 'gt2r/residual_histogram/pulse_count',\n", - " 'gt2r/residual_histogram/segment_id_list',\n", - " 'gt2r/residual_histogram/x_atc_mean',\n", - " 'gt2r/segment_quality/delta_time',\n", - " 'gt2r/segment_quality/record_number',\n", - " 'gt2r/segment_quality/reference_pt_lat',\n", - " 'gt2r/segment_quality/reference_pt_lon',\n", - " 'gt2r/segment_quality/segment_id',\n", - " 'gt2r/segment_quality/signal_selection_source',\n", - " 'gt2r/segment_quality/signal_selection_status/signal_selection_status_all',\n", - " 'gt2r/segment_quality/signal_selection_status/signal_selection_status_backup',\n", - " 'gt2r/segment_quality/signal_selection_status/signal_selection_status_confident',\n", - " 'gt3l/land_ice_segments/atl06_quality_summary',\n", - " 'gt3l/land_ice_segments/bias_correction/fpb_mean_corr',\n", - " 'gt3l/land_ice_segments/bias_correction/fpb_mean_corr_sigma',\n", - " 'gt3l/land_ice_segments/bias_correction/fpb_med_corr',\n", - " 'gt3l/land_ice_segments/bias_correction/fpb_med_corr_sigma',\n", - " 'gt3l/land_ice_segments/bias_correction/fpb_n_corr',\n", - " 'gt3l/land_ice_segments/bias_correction/med_r_fit',\n", - " 'gt3l/land_ice_segments/bias_correction/tx_mean_corr',\n", - " 'gt3l/land_ice_segments/bias_correction/tx_med_corr',\n", - " 'gt3l/land_ice_segments/delta_time',\n", - " 'gt3l/land_ice_segments/dem/dem_flag',\n", - " 'gt3l/land_ice_segments/dem/dem_h',\n", - " 'gt3l/land_ice_segments/dem/geoid_free2mean',\n", - " 'gt3l/land_ice_segments/dem/geoid_h',\n", - " 'gt3l/land_ice_segments/fit_statistics/dh_fit_dx',\n", - " 'gt3l/land_ice_segments/fit_statistics/dh_fit_dx_sigma',\n", - " 'gt3l/land_ice_segments/fit_statistics/dh_fit_dy',\n", - " 'gt3l/land_ice_segments/fit_statistics/h_expected_rms',\n", - " 'gt3l/land_ice_segments/fit_statistics/h_mean',\n", - " 'gt3l/land_ice_segments/fit_statistics/h_rms_misfit',\n", - " 'gt3l/land_ice_segments/fit_statistics/h_robust_sprd',\n", - " 'gt3l/land_ice_segments/fit_statistics/n_fit_photons',\n", - " 'gt3l/land_ice_segments/fit_statistics/n_seg_pulses',\n", - " 'gt3l/land_ice_segments/fit_statistics/sigma_h_mean',\n", - " 'gt3l/land_ice_segments/fit_statistics/signal_selection_source',\n", - " 'gt3l/land_ice_segments/fit_statistics/signal_selection_source_status',\n", - " 'gt3l/land_ice_segments/fit_statistics/snr',\n", - " 'gt3l/land_ice_segments/fit_statistics/snr_significance',\n", - " 'gt3l/land_ice_segments/fit_statistics/w_surface_window_final',\n", - " 'gt3l/land_ice_segments/geophysical/bckgrd',\n", - " 'gt3l/land_ice_segments/geophysical/bsnow_conf',\n", - " 'gt3l/land_ice_segments/geophysical/bsnow_h',\n", - " 'gt3l/land_ice_segments/geophysical/bsnow_od',\n", - " 'gt3l/land_ice_segments/geophysical/cloud_flg_asr',\n", - " 'gt3l/land_ice_segments/geophysical/cloud_flg_atm',\n", - " 'gt3l/land_ice_segments/geophysical/dac',\n", - " 'gt3l/land_ice_segments/geophysical/e_bckgrd',\n", - " 'gt3l/land_ice_segments/geophysical/layer_flag',\n", - " 'gt3l/land_ice_segments/geophysical/msw_flag',\n", - " 'gt3l/land_ice_segments/geophysical/neutat_delay_total',\n", - " 'gt3l/land_ice_segments/geophysical/r_eff',\n", - " 'gt3l/land_ice_segments/geophysical/solar_azimuth',\n", - " 'gt3l/land_ice_segments/geophysical/solar_elevation',\n", - " 'gt3l/land_ice_segments/geophysical/tide_earth',\n", - " 'gt3l/land_ice_segments/geophysical/tide_earth_free2mean',\n", - " 'gt3l/land_ice_segments/geophysical/tide_equilibrium',\n", - " 'gt3l/land_ice_segments/geophysical/tide_load',\n", - " 'gt3l/land_ice_segments/geophysical/tide_ocean',\n", - " 'gt3l/land_ice_segments/geophysical/tide_pole',\n", - " 'gt3l/land_ice_segments/ground_track/ref_azimuth',\n", - " 'gt3l/land_ice_segments/ground_track/ref_coelv',\n", - " 'gt3l/land_ice_segments/ground_track/seg_azimuth',\n", - " 'gt3l/land_ice_segments/ground_track/sigma_geo_at',\n", - " 'gt3l/land_ice_segments/ground_track/sigma_geo_r',\n", - " 'gt3l/land_ice_segments/ground_track/sigma_geo_xt',\n", - " 'gt3l/land_ice_segments/ground_track/x_atc',\n", - " 'gt3l/land_ice_segments/ground_track/y_atc',\n", - " 'gt3l/land_ice_segments/h_li',\n", - " 'gt3l/land_ice_segments/h_li_sigma',\n", - " 'gt3l/land_ice_segments/latitude',\n", - " 'gt3l/land_ice_segments/longitude',\n", - " 'gt3l/land_ice_segments/segment_id',\n", - " 'gt3l/land_ice_segments/sigma_geo_h',\n", - " 'gt3l/residual_histogram/bckgrd_per_m',\n", - " 'gt3l/residual_histogram/bin_top_h',\n", - " 'gt3l/residual_histogram/count',\n", - " 'gt3l/residual_histogram/delta_time',\n", - " 'gt3l/residual_histogram/ds_segment_id',\n", - " 'gt3l/residual_histogram/lat_mean',\n", - " 'gt3l/residual_histogram/lon_mean',\n", - " 'gt3l/residual_histogram/pulse_count',\n", - " 'gt3l/residual_histogram/segment_id_list',\n", - " 'gt3l/residual_histogram/x_atc_mean',\n", - " 'gt3l/segment_quality/delta_time',\n", - " 'gt3l/segment_quality/record_number',\n", - " 'gt3l/segment_quality/reference_pt_lat',\n", - " 'gt3l/segment_quality/reference_pt_lon',\n", - " 'gt3l/segment_quality/segment_id',\n", - " 'gt3l/segment_quality/signal_selection_source',\n", - " 'gt3l/segment_quality/signal_selection_status/signal_selection_status_all',\n", - " 'gt3l/segment_quality/signal_selection_status/signal_selection_status_backup',\n", - " 'gt3l/segment_quality/signal_selection_status/signal_selection_status_confident',\n", - " 'gt3r/land_ice_segments/atl06_quality_summary',\n", - " 'gt3r/land_ice_segments/bias_correction/fpb_mean_corr',\n", - " 'gt3r/land_ice_segments/bias_correction/fpb_mean_corr_sigma',\n", - " 'gt3r/land_ice_segments/bias_correction/fpb_med_corr',\n", - " 'gt3r/land_ice_segments/bias_correction/fpb_med_corr_sigma',\n", - " 'gt3r/land_ice_segments/bias_correction/fpb_n_corr',\n", - " 'gt3r/land_ice_segments/bias_correction/med_r_fit',\n", - " 'gt3r/land_ice_segments/bias_correction/tx_mean_corr',\n", - " 'gt3r/land_ice_segments/bias_correction/tx_med_corr',\n", - " 'gt3r/land_ice_segments/delta_time',\n", - " 'gt3r/land_ice_segments/dem/dem_flag',\n", - " 'gt3r/land_ice_segments/dem/dem_h',\n", - " 'gt3r/land_ice_segments/dem/geoid_free2mean',\n", - " 'gt3r/land_ice_segments/dem/geoid_h',\n", - " 'gt3r/land_ice_segments/fit_statistics/dh_fit_dx',\n", - " 'gt3r/land_ice_segments/fit_statistics/dh_fit_dx_sigma',\n", - " 'gt3r/land_ice_segments/fit_statistics/dh_fit_dy',\n", - " 'gt3r/land_ice_segments/fit_statistics/h_expected_rms',\n", - " 'gt3r/land_ice_segments/fit_statistics/h_mean',\n", - " 'gt3r/land_ice_segments/fit_statistics/h_rms_misfit',\n", - " 'gt3r/land_ice_segments/fit_statistics/h_robust_sprd',\n", - " 'gt3r/land_ice_segments/fit_statistics/n_fit_photons',\n", - " 'gt3r/land_ice_segments/fit_statistics/n_seg_pulses',\n", - " 'gt3r/land_ice_segments/fit_statistics/sigma_h_mean',\n", - " 'gt3r/land_ice_segments/fit_statistics/signal_selection_source',\n", - " 'gt3r/land_ice_segments/fit_statistics/signal_selection_source_status',\n", - " 'gt3r/land_ice_segments/fit_statistics/snr',\n", - " 'gt3r/land_ice_segments/fit_statistics/snr_significance',\n", - " 'gt3r/land_ice_segments/fit_statistics/w_surface_window_final',\n", - " 'gt3r/land_ice_segments/geophysical/bckgrd',\n", - " 'gt3r/land_ice_segments/geophysical/bsnow_conf',\n", - " 'gt3r/land_ice_segments/geophysical/bsnow_h',\n", - " 'gt3r/land_ice_segments/geophysical/bsnow_od',\n", - " 'gt3r/land_ice_segments/geophysical/cloud_flg_asr',\n", - " 'gt3r/land_ice_segments/geophysical/cloud_flg_atm',\n", - " 'gt3r/land_ice_segments/geophysical/dac',\n", - " 'gt3r/land_ice_segments/geophysical/e_bckgrd',\n", - " 'gt3r/land_ice_segments/geophysical/layer_flag',\n", - " 'gt3r/land_ice_segments/geophysical/msw_flag',\n", - " 'gt3r/land_ice_segments/geophysical/neutat_delay_total',\n", - " 'gt3r/land_ice_segments/geophysical/r_eff',\n", - " 'gt3r/land_ice_segments/geophysical/solar_azimuth',\n", - " 'gt3r/land_ice_segments/geophysical/solar_elevation',\n", - " 'gt3r/land_ice_segments/geophysical/tide_earth',\n", - " 'gt3r/land_ice_segments/geophysical/tide_earth_free2mean',\n", - " 'gt3r/land_ice_segments/geophysical/tide_equilibrium',\n", - " 'gt3r/land_ice_segments/geophysical/tide_load',\n", - " 'gt3r/land_ice_segments/geophysical/tide_ocean',\n", - " 'gt3r/land_ice_segments/geophysical/tide_pole',\n", - " 'gt3r/land_ice_segments/ground_track/ref_azimuth',\n", - " 'gt3r/land_ice_segments/ground_track/ref_coelv',\n", - " 'gt3r/land_ice_segments/ground_track/seg_azimuth',\n", - " 'gt3r/land_ice_segments/ground_track/sigma_geo_at',\n", - " 'gt3r/land_ice_segments/ground_track/sigma_geo_r',\n", - " 'gt3r/land_ice_segments/ground_track/sigma_geo_xt',\n", - " 'gt3r/land_ice_segments/ground_track/x_atc',\n", - " 'gt3r/land_ice_segments/ground_track/y_atc',\n", - " 'gt3r/land_ice_segments/h_li',\n", - " 'gt3r/land_ice_segments/h_li_sigma',\n", - " 'gt3r/land_ice_segments/latitude',\n", - " 'gt3r/land_ice_segments/longitude',\n", - " 'gt3r/land_ice_segments/segment_id',\n", - " 'gt3r/land_ice_segments/sigma_geo_h',\n", - " 'gt3r/residual_histogram/bckgrd_per_m',\n", - " 'gt3r/residual_histogram/bin_top_h',\n", - " 'gt3r/residual_histogram/count',\n", - " 'gt3r/residual_histogram/delta_time',\n", - " 'gt3r/residual_histogram/ds_segment_id',\n", - " 'gt3r/residual_histogram/lat_mean',\n", - " 'gt3r/residual_histogram/lon_mean',\n", - " 'gt3r/residual_histogram/pulse_count',\n", - " 'gt3r/residual_histogram/segment_id_list',\n", - " 'gt3r/residual_histogram/x_atc_mean',\n", - " 'gt3r/segment_quality/delta_time',\n", - " 'gt3r/segment_quality/record_number',\n", - " 'gt3r/segment_quality/reference_pt_lat',\n", - " 'gt3r/segment_quality/reference_pt_lon',\n", - " 'gt3r/segment_quality/segment_id',\n", - " 'gt3r/segment_quality/signal_selection_source',\n", - " 'gt3r/segment_quality/signal_selection_status/signal_selection_status_all',\n", - " 'gt3r/segment_quality/signal_selection_status/signal_selection_status_backup',\n", - " 'gt3r/segment_quality/signal_selection_status/signal_selection_status_confident',\n", - " 'orbit_info/bounding_polygon_lat1',\n", - " 'orbit_info/bounding_polygon_lon1',\n", - " 'orbit_info/crossing_time',\n", - " 'orbit_info/cycle_number',\n", - " 'orbit_info/lan',\n", - " 'orbit_info/orbit_number',\n", - " 'orbit_info/rgt',\n", - " 'orbit_info/sc_orient',\n", - " 'orbit_info/sc_orient_time',\n", - " 'quality_assessment/gt1l/delta_time',\n", - " 'quality_assessment/gt1l/lat_mean',\n", - " 'quality_assessment/gt1l/lon_mean',\n", - " 'quality_assessment/gt1l/signal_selection_source_fraction_0',\n", - " 'quality_assessment/gt1l/signal_selection_source_fraction_1',\n", - " 'quality_assessment/gt1l/signal_selection_source_fraction_2',\n", - " 'quality_assessment/gt1l/signal_selection_source_fraction_3',\n", - " 'quality_assessment/gt1r/delta_time',\n", - " 'quality_assessment/gt1r/lat_mean',\n", - " 'quality_assessment/gt1r/lon_mean',\n", - " 'quality_assessment/gt1r/signal_selection_source_fraction_0',\n", - " 'quality_assessment/gt1r/signal_selection_source_fraction_1',\n", - " 'quality_assessment/gt1r/signal_selection_source_fraction_2',\n", - " 'quality_assessment/gt1r/signal_selection_source_fraction_3',\n", - " 'quality_assessment/gt2l/delta_time',\n", - " 'quality_assessment/gt2l/lat_mean',\n", - " 'quality_assessment/gt2l/lon_mean',\n", - " 'quality_assessment/gt2l/signal_selection_source_fraction_0',\n", - " 'quality_assessment/gt2l/signal_selection_source_fraction_1',\n", - " 'quality_assessment/gt2l/signal_selection_source_fraction_2',\n", - " 'quality_assessment/gt2l/signal_selection_source_fraction_3',\n", - " 'quality_assessment/gt2r/delta_time',\n", - " 'quality_assessment/gt2r/lat_mean',\n", - " 'quality_assessment/gt2r/lon_mean',\n", - " 'quality_assessment/gt2r/signal_selection_source_fraction_0',\n", - " 'quality_assessment/gt2r/signal_selection_source_fraction_1',\n", - " 'quality_assessment/gt2r/signal_selection_source_fraction_2',\n", - " 'quality_assessment/gt2r/signal_selection_source_fraction_3',\n", - " 'quality_assessment/gt3l/delta_time',\n", - " 'quality_assessment/gt3l/lat_mean',\n", - " 'quality_assessment/gt3l/lon_mean',\n", - " 'quality_assessment/gt3l/signal_selection_source_fraction_0',\n", - " 'quality_assessment/gt3l/signal_selection_source_fraction_1',\n", - " 'quality_assessment/gt3l/signal_selection_source_fraction_2',\n", - " 'quality_assessment/gt3l/signal_selection_source_fraction_3',\n", - " 'quality_assessment/gt3r/delta_time',\n", - " 'quality_assessment/gt3r/lat_mean',\n", - " 'quality_assessment/gt3r/lon_mean',\n", - " 'quality_assessment/gt3r/signal_selection_source_fraction_0',\n", - " 'quality_assessment/gt3r/signal_selection_source_fraction_1',\n", - " 'quality_assessment/gt3r/signal_selection_source_fraction_2',\n", - " 'quality_assessment/gt3r/signal_selection_source_fraction_3',\n", - " 'quality_assessment/qa_granule_fail_reason',\n", - " 'quality_assessment/qa_granule_pass_fail']" - ] - }, - "execution_count": 21, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "reader.vars.avail()" ] @@ -1635,7 +371,7 @@ }, { "cell_type": "code", - "execution_count": 28, + "execution_count": null, "id": "e3734e09", "metadata": {}, "outputs": [], @@ -1655,44 +391,10 @@ }, { "cell_type": "code", - "execution_count": 29, + "execution_count": null, "id": "e5456e36", "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "{'sc_orient': ['orbit_info/sc_orient'],\n", - " 'atlas_sdp_gps_epoch': ['ancillary_data/atlas_sdp_gps_epoch'],\n", - " 'cycle_number': ['orbit_info/cycle_number'],\n", - " 'rgt': ['orbit_info/rgt'],\n", - " 'data_start_utc': ['ancillary_data/data_start_utc'],\n", - " 'data_end_utc': ['ancillary_data/data_end_utc'],\n", - " 'h_li': ['gt1l/land_ice_segments/h_li',\n", - " 'gt1r/land_ice_segments/h_li',\n", - " 'gt2l/land_ice_segments/h_li',\n", - " 'gt2r/land_ice_segments/h_li',\n", - " 'gt3l/land_ice_segments/h_li',\n", - " 'gt3r/land_ice_segments/h_li'],\n", - " 'latitude': ['gt1l/land_ice_segments/latitude',\n", - " 'gt1r/land_ice_segments/latitude',\n", - " 'gt2l/land_ice_segments/latitude',\n", - " 'gt2r/land_ice_segments/latitude',\n", - " 'gt3l/land_ice_segments/latitude',\n", - " 'gt3r/land_ice_segments/latitude'],\n", - " 'longitude': ['gt1l/land_ice_segments/longitude',\n", - " 'gt1r/land_ice_segments/longitude',\n", - " 'gt2l/land_ice_segments/longitude',\n", - " 'gt2r/land_ice_segments/longitude',\n", - " 'gt3l/land_ice_segments/longitude',\n", - " 'gt3r/land_ice_segments/longitude']}" - ] - }, - "execution_count": 29, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "reader.vars.wanted" ] @@ -1709,7 +411,7 @@ }, { "cell_type": "code", - "execution_count": 24, + "execution_count": null, "id": "69894391", "metadata": {}, "outputs": [], @@ -1739,113 +441,10 @@ }, { "cell_type": "code", - "execution_count": 30, + "execution_count": null, "id": "eaabc976", "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:49: UserWarning: Converting non-nanosecond precision datetime values to nanosecond precision. This behavior can eventually be relaxed in xarray, as it is an artifact from pandas which is now beginning to support non-nanosecond precision values. This warning is caused by passing non-nanosecond np.datetime64 or np.timedelta64 values to the DataArray or Variable constructor; it can be silenced by converting the values to nanosecond precision ahead of time.\n", - " df.update({keyword: df[keyword].str[:-1].astype(np.datetime64)})\n", - "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:49: UserWarning: Converting non-nanosecond precision datetime values to nanosecond precision. This behavior can eventually be relaxed in xarray, as it is an artifact from pandas which is now beginning to support non-nanosecond precision values. This warning is caused by passing non-nanosecond np.datetime64 or np.timedelta64 values to the DataArray or Variable constructor; it can be silenced by converting the values to nanosecond precision ahead of time.\n", - " df.update({keyword: df[keyword].str[:-1].astype(np.datetime64)})\n", - "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:490: UserWarning: rename 'delta_time' to 'photon_idx' does not create an index anymore. Try using swap_dims instead or use set_index after rename to create an indexed coordinate.\n", - " .rename({\"delta_time\": \"photon_idx\"})\n", - "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:490: UserWarning: rename 'delta_time' to 'photon_idx' does not create an index anymore. Try using swap_dims instead or use set_index after rename to create an indexed coordinate.\n", - " .rename({\"delta_time\": \"photon_idx\"})\n", - "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:490: UserWarning: rename 'delta_time' to 'photon_idx' does not create an index anymore. Try using swap_dims instead or use set_index after rename to create an indexed coordinate.\n", - " .rename({\"delta_time\": \"photon_idx\"})\n", - "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:490: UserWarning: rename 'delta_time' to 'photon_idx' does not create an index anymore. Try using swap_dims instead or use set_index after rename to create an indexed coordinate.\n", - " .rename({\"delta_time\": \"photon_idx\"})\n", - "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:490: UserWarning: rename 'delta_time' to 'photon_idx' does not create an index anymore. Try using swap_dims instead or use set_index after rename to create an indexed coordinate.\n", - " .rename({\"delta_time\": \"photon_idx\"})\n", - "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:490: UserWarning: rename 'delta_time' to 'photon_idx' does not create an index anymore. Try using swap_dims instead or use set_index after rename to create an indexed coordinate.\n", - " .rename({\"delta_time\": \"photon_idx\"})\n", - "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:49: UserWarning: Converting non-nanosecond precision datetime values to nanosecond precision. This behavior can eventually be relaxed in xarray, as it is an artifact from pandas which is now beginning to support non-nanosecond precision values. This warning is caused by passing non-nanosecond np.datetime64 or np.timedelta64 values to the DataArray or Variable constructor; it can be silenced by converting the values to nanosecond precision ahead of time.\n", - " df.update({keyword: df[keyword].str[:-1].astype(np.datetime64)})\n", - "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:49: UserWarning: Converting non-nanosecond precision datetime values to nanosecond precision. This behavior can eventually be relaxed in xarray, as it is an artifact from pandas which is now beginning to support non-nanosecond precision values. This warning is caused by passing non-nanosecond np.datetime64 or np.timedelta64 values to the DataArray or Variable constructor; it can be silenced by converting the values to nanosecond precision ahead of time.\n", - " df.update({keyword: df[keyword].str[:-1].astype(np.datetime64)})\n", - "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:490: UserWarning: rename 'delta_time' to 'photon_idx' does not create an index anymore. Try using swap_dims instead or use set_index after rename to create an indexed coordinate.\n", - " .rename({\"delta_time\": \"photon_idx\"})\n", - "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:490: UserWarning: rename 'delta_time' to 'photon_idx' does not create an index anymore. Try using swap_dims instead or use set_index after rename to create an indexed coordinate.\n", - " .rename({\"delta_time\": \"photon_idx\"})\n", - "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:490: UserWarning: rename 'delta_time' to 'photon_idx' does not create an index anymore. Try using swap_dims instead or use set_index after rename to create an indexed coordinate.\n", - " .rename({\"delta_time\": \"photon_idx\"})\n", - "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:490: UserWarning: rename 'delta_time' to 'photon_idx' does not create an index anymore. Try using swap_dims instead or use set_index after rename to create an indexed coordinate.\n", - " .rename({\"delta_time\": \"photon_idx\"})\n", - "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:490: UserWarning: rename 'delta_time' to 'photon_idx' does not create an index anymore. Try using swap_dims instead or use set_index after rename to create an indexed coordinate.\n", - " .rename({\"delta_time\": \"photon_idx\"})\n", - "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:490: UserWarning: rename 'delta_time' to 'photon_idx' does not create an index anymore. Try using swap_dims instead or use set_index after rename to create an indexed coordinate.\n", - " .rename({\"delta_time\": \"photon_idx\"})\n", - "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:49: UserWarning: Converting non-nanosecond precision datetime values to nanosecond precision. This behavior can eventually be relaxed in xarray, as it is an artifact from pandas which is now beginning to support non-nanosecond precision values. This warning is caused by passing non-nanosecond np.datetime64 or np.timedelta64 values to the DataArray or Variable constructor; it can be silenced by converting the values to nanosecond precision ahead of time.\n", - " df.update({keyword: df[keyword].str[:-1].astype(np.datetime64)})\n", - "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:49: UserWarning: Converting non-nanosecond precision datetime values to nanosecond precision. This behavior can eventually be relaxed in xarray, as it is an artifact from pandas which is now beginning to support non-nanosecond precision values. This warning is caused by passing non-nanosecond np.datetime64 or np.timedelta64 values to the DataArray or Variable constructor; it can be silenced by converting the values to nanosecond precision ahead of time.\n", - " df.update({keyword: df[keyword].str[:-1].astype(np.datetime64)})\n", - "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:490: UserWarning: rename 'delta_time' to 'photon_idx' does not create an index anymore. Try using swap_dims instead or use set_index after rename to create an indexed coordinate.\n", - " .rename({\"delta_time\": \"photon_idx\"})\n", - "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:490: UserWarning: rename 'delta_time' to 'photon_idx' does not create an index anymore. Try using swap_dims instead or use set_index after rename to create an indexed coordinate.\n", - " .rename({\"delta_time\": \"photon_idx\"})\n", - "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:490: UserWarning: rename 'delta_time' to 'photon_idx' does not create an index anymore. Try using swap_dims instead or use set_index after rename to create an indexed coordinate.\n", - " .rename({\"delta_time\": \"photon_idx\"})\n", - "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:490: UserWarning: rename 'delta_time' to 'photon_idx' does not create an index anymore. Try using swap_dims instead or use set_index after rename to create an indexed coordinate.\n", - " .rename({\"delta_time\": \"photon_idx\"})\n", - "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:490: UserWarning: rename 'delta_time' to 'photon_idx' does not create an index anymore. Try using swap_dims instead or use set_index after rename to create an indexed coordinate.\n", - " .rename({\"delta_time\": \"photon_idx\"})\n", - "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:490: UserWarning: rename 'delta_time' to 'photon_idx' does not create an index anymore. Try using swap_dims instead or use set_index after rename to create an indexed coordinate.\n", - " .rename({\"delta_time\": \"photon_idx\"})\n", - "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:49: UserWarning: Converting non-nanosecond precision datetime values to nanosecond precision. This behavior can eventually be relaxed in xarray, as it is an artifact from pandas which is now beginning to support non-nanosecond precision values. This warning is caused by passing non-nanosecond np.datetime64 or np.timedelta64 values to the DataArray or Variable constructor; it can be silenced by converting the values to nanosecond precision ahead of time.\n", - " df.update({keyword: df[keyword].str[:-1].astype(np.datetime64)})\n", - "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:49: UserWarning: Converting non-nanosecond precision datetime values to nanosecond precision. This behavior can eventually be relaxed in xarray, as it is an artifact from pandas which is now beginning to support non-nanosecond precision values. This warning is caused by passing non-nanosecond np.datetime64 or np.timedelta64 values to the DataArray or Variable constructor; it can be silenced by converting the values to nanosecond precision ahead of time.\n", - " df.update({keyword: df[keyword].str[:-1].astype(np.datetime64)})\n", - "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:490: UserWarning: rename 'delta_time' to 'photon_idx' does not create an index anymore. Try using swap_dims instead or use set_index after rename to create an indexed coordinate.\n", - " .rename({\"delta_time\": \"photon_idx\"})\n", - "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:490: UserWarning: rename 'delta_time' to 'photon_idx' does not create an index anymore. Try using swap_dims instead or use set_index after rename to create an indexed coordinate.\n", - " .rename({\"delta_time\": \"photon_idx\"})\n", - "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:490: UserWarning: rename 'delta_time' to 'photon_idx' does not create an index anymore. Try using swap_dims instead or use set_index after rename to create an indexed coordinate.\n", - " .rename({\"delta_time\": \"photon_idx\"})\n", - "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:490: UserWarning: rename 'delta_time' to 'photon_idx' does not create an index anymore. Try using swap_dims instead or use set_index after rename to create an indexed coordinate.\n", - " .rename({\"delta_time\": \"photon_idx\"})\n", - "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:490: UserWarning: rename 'delta_time' to 'photon_idx' does not create an index anymore. Try using swap_dims instead or use set_index after rename to create an indexed coordinate.\n", - " .rename({\"delta_time\": \"photon_idx\"})\n", - "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:490: UserWarning: rename 'delta_time' to 'photon_idx' does not create an index anymore. Try using swap_dims instead or use set_index after rename to create an indexed coordinate.\n", - " .rename({\"delta_time\": \"photon_idx\"})\n", - "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:49: UserWarning: Converting non-nanosecond precision datetime values to nanosecond precision. This behavior can eventually be relaxed in xarray, as it is an artifact from pandas which is now beginning to support non-nanosecond precision values. This warning is caused by passing non-nanosecond np.datetime64 or np.timedelta64 values to the DataArray or Variable constructor; it can be silenced by converting the values to nanosecond precision ahead of time.\n", - " df.update({keyword: df[keyword].str[:-1].astype(np.datetime64)})\n", - "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:49: UserWarning: Converting non-nanosecond precision datetime values to nanosecond precision. This behavior can eventually be relaxed in xarray, as it is an artifact from pandas which is now beginning to support non-nanosecond precision values. This warning is caused by passing non-nanosecond np.datetime64 or np.timedelta64 values to the DataArray or Variable constructor; it can be silenced by converting the values to nanosecond precision ahead of time.\n", - " df.update({keyword: df[keyword].str[:-1].astype(np.datetime64)})\n", - "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:490: UserWarning: rename 'delta_time' to 'photon_idx' does not create an index anymore. Try using swap_dims instead or use set_index after rename to create an indexed coordinate.\n", - " .rename({\"delta_time\": \"photon_idx\"})\n", - "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:490: UserWarning: rename 'delta_time' to 'photon_idx' does not create an index anymore. Try using swap_dims instead or use set_index after rename to create an indexed coordinate.\n", - " .rename({\"delta_time\": \"photon_idx\"})\n", - "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:490: UserWarning: rename 'delta_time' to 'photon_idx' does not create an index anymore. Try using swap_dims instead or use set_index after rename to create an indexed coordinate.\n", - " .rename({\"delta_time\": \"photon_idx\"})\n", - "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:490: UserWarning: rename 'delta_time' to 'photon_idx' does not create an index anymore. Try using swap_dims instead or use set_index after rename to create an indexed coordinate.\n", - " .rename({\"delta_time\": \"photon_idx\"})\n", - "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:490: UserWarning: rename 'delta_time' to 'photon_idx' does not create an index anymore. Try using swap_dims instead or use set_index after rename to create an indexed coordinate.\n", - " .rename({\"delta_time\": \"photon_idx\"})\n", - "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:490: UserWarning: rename 'delta_time' to 'photon_idx' does not create an index anymore. Try using swap_dims instead or use set_index after rename to create an indexed coordinate.\n", - " .rename({\"delta_time\": \"photon_idx\"})\n", - "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:49: UserWarning: Converting non-nanosecond precision datetime values to nanosecond precision. This behavior can eventually be relaxed in xarray, as it is an artifact from pandas which is now beginning to support non-nanosecond precision values. This warning is caused by passing non-nanosecond np.datetime64 or np.timedelta64 values to the DataArray or Variable constructor; it can be silenced by converting the values to nanosecond precision ahead of time.\n", - " df.update({keyword: df[keyword].str[:-1].astype(np.datetime64)})\n", - "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:49: UserWarning: Converting non-nanosecond precision datetime values to nanosecond precision. This behavior can eventually be relaxed in xarray, as it is an artifact from pandas which is now beginning to support non-nanosecond precision values. This warning is caused by passing non-nanosecond np.datetime64 or np.timedelta64 values to the DataArray or Variable constructor; it can be silenced by converting the values to nanosecond precision ahead of time.\n", - " df.update({keyword: df[keyword].str[:-1].astype(np.datetime64)})\n", - "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:490: UserWarning: rename 'delta_time' to 'photon_idx' does not create an index anymore. Try using swap_dims instead or use set_index after rename to create an indexed coordinate.\n", - " .rename({\"delta_time\": \"photon_idx\"})\n", - "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:490: UserWarning: rename 'delta_time' to 'photon_idx' does not create an index anymore. Try using swap_dims instead or use set_index after rename to create an indexed coordinate.\n", - " .rename({\"delta_time\": \"photon_idx\"})\n", - "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:490: UserWarning: rename 'delta_time' to 'photon_idx' does not create an index anymore. Try using swap_dims instead or use set_index after rename to create an indexed coordinate.\n", - " .rename({\"delta_time\": \"photon_idx\"})\n", - "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:490: UserWarning: rename 'delta_time' to 'photon_idx' does not create an index anymore. Try using swap_dims instead or use set_index after rename to create an indexed coordinate.\n", - " .rename({\"delta_time\": \"photon_idx\"})\n", - "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:490: UserWarning: rename 'delta_time' to 'photon_idx' does not create an index anymore. Try using swap_dims instead or use set_index after rename to create an indexed coordinate.\n", - " .rename({\"delta_time\": \"photon_idx\"})\n", - "/home/jovyan/envs/general/lib/python3.11/site-packages/icepyx/core/read.py:490: UserWarning: rename 'delta_time' to 'photon_idx' does not create an index anymore. Try using swap_dims instead or use set_index after rename to create an indexed coordinate.\n", - " .rename({\"delta_time\": \"photon_idx\"})\n" - ] - } - ], + "outputs": [], "source": [ "ds = reader.load()" ] @@ -1866,549 +465,10 @@ }, { "cell_type": "code", - "execution_count": 26, + "execution_count": null, "id": "723256f7", "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "
<xarray.Dataset>\n",
-       "Dimensions:              (photon_idx: 29027, spot: 2, gran_idx: 6)\n",
-       "Coordinates:\n",
-       "  * photon_idx           (photon_idx) int64 0 1 2 3 ... 29023 29024 29025 29026\n",
-       "  * spot                 (spot) uint8 2 5\n",
-       "  * gran_idx             (gran_idx) float64 8.49e+04 9.02e+04 ... 1.016e+05\n",
-       "    source_file          (gran_idx) <U72 '../../../../data/ATL06/processed_AT...\n",
-       "    delta_time           (gran_idx, photon_idx) datetime64[ns] 2019-02-22T01:...\n",
-       "Data variables:\n",
-       "    sc_orient            (gran_idx) int8 0 0 0 1 1 1\n",
-       "    cycle_number         (gran_idx) int8 2 2 2 5 5 5\n",
-       "    rgt                  (gran_idx) int16 849 902 910 986 1001 1016\n",
-       "    atlas_sdp_gps_epoch  (gran_idx) datetime64[ns] 2018-01-01T00:00:18 ... 20...\n",
-       "    data_start_utc       (gran_idx) datetime64[ns] 2019-02-22T01:03:44.199777...\n",
-       "    data_end_utc         (gran_idx) datetime64[ns] 2019-02-22T01:07:38.112326...\n",
-       "    h_li                 (spot, gran_idx, photon_idx) float32 nan nan ... nan\n",
-       "    latitude             (spot, gran_idx, photon_idx) float64 nan nan ... nan\n",
-       "    longitude            (spot, gran_idx, photon_idx) float64 nan nan ... nan\n",
-       "    gt                   (gran_idx, spot) <U4 'gt3r' 'gt1l' ... 'gt1l' 'gt3r'\n",
-       "Attributes:\n",
-       "    data_product:  ATL06\n",
-       "    Description:   The land_ice_height group contains the primary set of deri...\n",
-       "    data_rate:     Data within this group are sparse.  Data values are provid...
" - ], - "text/plain": [ - "\n", - "Dimensions: (photon_idx: 29027, spot: 2, gran_idx: 6)\n", - "Coordinates:\n", - " * photon_idx (photon_idx) int64 0 1 2 3 ... 29023 29024 29025 29026\n", - " * spot (spot) uint8 2 5\n", - " * gran_idx (gran_idx) float64 8.49e+04 9.02e+04 ... 1.016e+05\n", - " source_file (gran_idx) " - ] - }, - "execution_count": 27, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "ds.plot.scatter(x=\"longitude\", y=\"latitude\", hue=\"h_li\", vmin=-100, vmax=2000)" ] From b26ca4eb0ed54cf075bf6c53f31cd78114e5c182 Mon Sep 17 00:00:00 2001 From: Rachel Wegener <35503632+rwegener2@users.noreply.github.com> Date: Fri, 1 Sep 2023 13:38:37 -0400 Subject: [PATCH 10/55] Update icepyx/core/read.py Co-authored-by: Wei Ji <23487320+weiji14@users.noreply.github.com> --- icepyx/core/read.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/icepyx/core/read.py b/icepyx/core/read.py index d3ca0d82a..2ffe32cb7 100644 --- a/icepyx/core/read.py +++ b/icepyx/core/read.py @@ -680,7 +680,7 @@ def _build_dataset_template(self, file): def _read_single_grp(self, file, grp_path): """ - For a given file and variable group path, construct an an xarray Dataset. + For a given file and variable group path, construct an xarray Dataset. Parameters ---------- From ce1ca76b7e2d586eaba3695308fae0e0bcd4805f Mon Sep 17 00:00:00 2001 From: Rachel Wegener Date: Fri, 1 Sep 2023 17:40:10 +0000 Subject: [PATCH 11/55] remove intake and related modules --- requirements.txt | 2 -- 1 file changed, 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index 86618f108..06f4ad9a7 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,8 +7,6 @@ h5netcdf h5py holoviews hvplot -intake -intake-xarray matplotlib numpy requests From 431af78cd55d51fd08881e440d1a55681816db5b Mon Sep 17 00:00:00 2001 From: Rachel Wegener Date: Tue, 5 Sep 2023 16:27:50 +0000 Subject: [PATCH 12/55] mvp with new read parameters --- icepyx/core/read.py | 114 +++++++++++++++++++++++++++++--------------- 1 file changed, 75 insertions(+), 39 deletions(-) diff --git a/icepyx/core/read.py b/icepyx/core/read.py index 2ffe32cb7..c15957210 100644 --- a/icepyx/core/read.py +++ b/icepyx/core/read.py @@ -1,11 +1,14 @@ import fnmatch +import glob import os import warnings +import h5py import numpy as np import xarray as xr import icepyx.core.is2ref as is2ref +from icepyx.core.query import Query from icepyx.core.variables import Variables as Variables from icepyx.core.variables import list_of_dict_vals @@ -297,56 +300,79 @@ class Read: def __init__( self, - data_source=None, + path, # TODO how to deal with the fact that this is required in later versions + # but does not exist in past versions? + out_obj_type=None, # xr.Dataset, product=None, + data_source=None, filename_pattern="ATL{product:2}_{datetime:%Y%m%d%H%M%S}_{rgt:4}{cycle:2}{orbitsegment:2}_{version:3}_{revision:2}.h5", - out_obj_type=None, # xr.Dataset, ): - if data_source is None: - raise ValueError("Please provide a data source.") - else: - self._source_type = _check_datasource(data_source) - self.data_source = data_source - - if product is None: - raise ValueError( - "Please provide the ICESat-2 data product of your file(s)." - ) - else: - self._prod = is2ref._validate_product(product) - pattern_ck, filelist = Read._check_source_for_pattern( - data_source, filename_pattern - ) - assert pattern_ck - # Note: need to check if this works for subset and non-subset NSIDC files (processed_ prepends the former) - self._pattern = filename_pattern - - # this is a first pass at getting rid of mixed product types and warning the user. - # it takes an approach assuming the product name is in the filename, but needs reworking if we let multiple products be loaded - # one way to handle this would be bring in the product info during the loading step and fill in product there instead of requiring it from the user - filtered_filelist = [file for file in filelist if self._prod in file] - if len(filtered_filelist) == 0: - warnings.warn( - "Your filenames do not contain a product identifier (e.g. ATL06). " - "You will likely need to manually merge your dataframes." - ) - self._filelist = filelist - elif len(filtered_filelist) < len(filelist): - warnings.warn( - "Some files matching your filename pattern were removed as they were not the specified product." - ) - self._filelist = filtered_filelist + # Raise warnings for depreciated arguments + if data_source: + warnings.warn('The `data_source` argument is depreciated. Please use the path argument instead.') + # TODO this check doesn't work because default isn't None + if filename_pattern: + warnings.warn('The `filename_pattern` argument is depreciated. Instead please provide a glob string to the `path` argument') + + # CREATE THE FILELIST + # Create the filelist from the user `path` argument + if isinstance(path, list): + self._filelist = path + # Discussion: I think actually this parameter type will only exist for cloud? + # Unless we really want to abstract more stuff, i.e. the downloading + # elif isinstance(path, Query): + # self._filelist = path. + elif os.path.isdir(path): + path = os.path.join(path, '*') + # TODO better flow so ths glob doesn't happen twice + self._filelist = glob.glob(path) else: - self._filelist = filelist - + # Discussion: should we default to recursive or not? + # Could allow for glob kwargs, but at that point I think we should just tell + # the user to run glob themself to create the filelist. + self._filelist = glob.glob(path) + + # EXTRACT THE PRODUCT FOR EACH FILE + # Note for ticket: this logic got a little complex in the attempt to maintain a + # user-given product argument. If this gets depreciated we could depricate this + # Create a dictionary of the metadata extracted + product_dict = {} + for file_ in self._filelist: + product_dict[file_] = self._extract_product(file_) + # DEAL WITH MULTIPLE PRODUCTS IN THE LIST + # raise warning if there are multiple products present + if len(set(product_dict.values())) > 1: + # filter to only one product + if product: + warnings.warn(f'Multiple products found in list of files: {product_dict}. Files that do not match the user specified product will be removed from processing.') + # TODO thoughts on making filelist public read-only? It seems fair as I write + # all these warnings/error messages that reference a filelist. + self._filelist = [] + for key, value in product_dict.items(): + if value == product: + self._filelist.append(key) + product_dict.pop(key) + if len(self._filelist) == 0: + raise 'No files found in the file list matching the user-specified product type' + else: + raise TypeError(f'Multiple product types were found in the file list: {product_dict}. Please provide a valid `path` parameter indicating files of a single product') + # ASSIGN A PRODUCT TO THIS FILELIST + self.product = list(product_dict.values())[0] + if product and self.product != product: + warnings.warn(f'User specified product {product} does not match the product from the file metadata {self.product}') + + # Discussion: is this code meaningful to others? or can it be cleaned up? # after validation, use the notebook code and code outline to start implementing the rest of the class - + if out_obj_type is not None: print( "Output object type will be an xarray DataSet - " "no other output types are implemented yet" ) self._out_obj = xr.Dataset + + print('filelist:', self._filelist) + print('product', self.product) # ---------------------------------------------------------------------- # Properties @@ -379,6 +405,16 @@ def vars(self): # ---------------------------------------------------------------------- # Methods + + @staticmethod + def _extract_product(filepath): + with h5py.File(filepath, 'r') as f: + try: + product = f['METADATA']['DatasetIdentification'].attrs['shortName'].decode() + # TODO test that this is the proper error + except KeyError: + raise 'Unable to parse the product name from file metadata' + return product @staticmethod def _check_source_for_pattern(source, filename_pattern): From 612662e3ffc9395bc90557c4fdaba0a88500b068 Mon Sep 17 00:00:00 2001 From: Rachel Wegener Date: Tue, 5 Sep 2023 17:45:54 +0000 Subject: [PATCH 13/55] clean up remainder of file and remove extraneous comments --- icepyx/core/read.py | 137 +++++++++++++++++++++++++++----------------- 1 file changed, 83 insertions(+), 54 deletions(-) diff --git a/icepyx/core/read.py b/icepyx/core/read.py index c15957210..c8fe4f276 100644 --- a/icepyx/core/read.py +++ b/icepyx/core/read.py @@ -300,69 +300,83 @@ class Read: def __init__( self, - path, # TODO how to deal with the fact that this is required in later versions - # but does not exist in past versions? - out_obj_type=None, # xr.Dataset, + path=None, product=None, data_source=None, filename_pattern="ATL{product:2}_{datetime:%Y%m%d%H%M%S}_{rgt:4}{cycle:2}{orbitsegment:2}_{version:3}_{revision:2}.h5", + out_obj_type=None, # xr.Dataset, ): # Raise warnings for depreciated arguments if data_source: - warnings.warn('The `data_source` argument is depreciated. Please use the path argument instead.') - # TODO this check doesn't work because default isn't None - if filename_pattern: - warnings.warn('The `filename_pattern` argument is depreciated. Instead please provide a glob string to the `path` argument') + warnings.warn( + 'The `data_source` argument is depreciated. Please use the path argument ' + 'instead.' + ) + if filename_pattern != "ATL{product:2}_{datetime:%Y%m%d%H%M%S}_{rgt:4}{cycle:2}{orbitsegment:2}_{version:3}_{revision:2}.h5": + warnings.warn( + 'The `filename_pattern` argument is depreciated. Instead please provide a ' + 'glob string to the `path` argument' + ) + if product: + product = is2ref._validate_product(product) + warnings.warn( + 'The `product` argument is no longer required. If the `path` argument given ' + 'contains files with multiple products the `product` argument will be used ' + 'to filter that list. In all other cases the product argument is ignored. ' + 'The recommended approach is to not include a `product` argument and instead ' + 'provide a `path` with files of only a single product type`.' + ) - # CREATE THE FILELIST # Create the filelist from the user `path` argument if isinstance(path, list): self._filelist = path - # Discussion: I think actually this parameter type will only exist for cloud? - # Unless we really want to abstract more stuff, i.e. the downloading - # elif isinstance(path, Query): - # self._filelist = path. elif os.path.isdir(path): path = os.path.join(path, '*') - # TODO better flow so ths glob doesn't happen twice self._filelist = glob.glob(path) else: - # Discussion: should we default to recursive or not? - # Could allow for glob kwargs, but at that point I think we should just tell - # the user to run glob themself to create the filelist. self._filelist = glob.glob(path) + # Remove any directories from the list + self._filelist = [f for f in self._filelist if not os.path.isdir(f)] - # EXTRACT THE PRODUCT FOR EACH FILE - # Note for ticket: this logic got a little complex in the attempt to maintain a - # user-given product argument. If this gets depreciated we could depricate this - # Create a dictionary of the metadata extracted + # Create a dictionary of the products as read from the metadata product_dict = {} for file_ in self._filelist: product_dict[file_] = self._extract_product(file_) - # DEAL WITH MULTIPLE PRODUCTS IN THE LIST - # raise warning if there are multiple products present - if len(set(product_dict.values())) > 1: - # filter to only one product + + # Raise warnings or errors for muliple products or products not matching the user-specified product + all_products = list(set(product_dict.values())) + if len(all_products) > 1: if product: - warnings.warn(f'Multiple products found in list of files: {product_dict}. Files that do not match the user specified product will be removed from processing.') - # TODO thoughts on making filelist public read-only? It seems fair as I write - # all these warnings/error messages that reference a filelist. + warnings.warn( + f'Multiple products found in list of files: {product_dict}. Files that ' + 'do not match the user specified product will be removed from processing.' + ) self._filelist = [] for key, value in product_dict.items(): if value == product: self._filelist.append(key) - product_dict.pop(key) if len(self._filelist) == 0: - raise 'No files found in the file list matching the user-specified product type' + raise TypeError( + 'No files found in the file list matching the user-specified ' + 'product type' + ) + # Use the cleaned filelist to assign a product + self._product = product else: - raise TypeError(f'Multiple product types were found in the file list: {product_dict}. Please provide a valid `path` parameter indicating files of a single product') - # ASSIGN A PRODUCT TO THIS FILELIST - self.product = list(product_dict.values())[0] - if product and self.product != product: - warnings.warn(f'User specified product {product} does not match the product from the file metadata {self.product}') - - # Discussion: is this code meaningful to others? or can it be cleaned up? - # after validation, use the notebook code and code outline to start implementing the rest of the class + raise TypeError( + f'Multiple product types were found in the file list: {product_dict}.' + 'Please provide a valid `path` parameter indicating files of a single ' + 'product' + ) + else: + # Assign the identified product to the property + self._product = all_products[0] + # Raise a warning if the metadata-located product differs from the user-specified product + if product and self._product != product: + warnings.warn( + f'User specified product {product} does not match the product from the file' + ' metadata {self._product}' + ) if out_obj_type is not None: print( @@ -370,9 +384,6 @@ def __init__( "no other output types are implemented yet" ) self._out_obj = xr.Dataset - - print('filelist:', self._filelist) - print('product', self.product) # ---------------------------------------------------------------------- # Properties @@ -398,18 +409,45 @@ def vars(self): if not hasattr(self, "_read_vars"): self._read_vars = Variables( - "file", path=self._filelist[0], product=self._prod + "file", path=self.filelist[0], product=self.product ) return self._read_vars + + @property + def filelist(self): + """ + A read-only property for the user to view the list of files represented by this + Read object. + """ + return self._filelist + + @property + def num_files(self): + """ + Return the number of files that is being processed by the object + """ + return len(self.filelist) + + @property + def product(self): + """ + A read-only property for the user to view the product associated with the Read + object. + """ + return self._product # ---------------------------------------------------------------------- # Methods @staticmethod def _extract_product(filepath): + """ + Read the product type from the metadata of the file. Return the product as a string. + """ with h5py.File(filepath, 'r') as f: try: + # TODO consider: should we get this from the top level attrs instead? product = f['METADATA']['DatasetIdentification'].attrs['shortName'].decode() # TODO test that this is the proper error except KeyError: @@ -675,7 +713,7 @@ def load(self): # However, this led to errors when I tried to combine two identical datasets because the single dimension was equal. # In these situations, xarray recommends manually controlling the merge/concat process yourself. # While unlikely to be a broad issue, I've heard of multiple matching timestamps causing issues for combining multiple IS2 datasets. - for file in self._filelist: + for file in self.filelist: all_dss.append( self._build_single_file_dataset(file, groups_list) ) # wanted_groups, vgrp.keys())) @@ -710,7 +748,7 @@ def _build_dataset_template(self, file): gran_idx=[np.uint64(999999)], source_file=(["gran_idx"], [file]), ), - attrs=dict(data_product=self._prod), + attrs=dict(data_product=self.product), ) return is2ds @@ -754,20 +792,11 @@ def _build_single_file_dataset(self, file, groups_list): ------- Xarray Dataset """ - file_product = self._read_single_grp(file, "/").attrs["identifier_product_type"] - assert ( - file_product == self._prod - ), "Your product specification does not match the product specification within your files." - # I think the below method might NOT read the file into memory as the above might? - # import h5py - # with h5py.File(filepath,'r') as h5pt: - # prod_id = h5pt.attrs["identifier_product_type"] - # DEVNOTE: if and elif does not actually apply wanted variable list, and has not been tested for merging multiple files into one ds # if a gridded product # TODO: all products need to be tested, and quicklook products added or explicitly excluded # Level 3b, gridded (netcdf): ATL14, 15, 16, 17, 18, 19, 20, 21 - if self._prod in [ + if self.product in [ "ATL14", "ATL15", "ATL16", @@ -780,7 +809,7 @@ def _build_single_file_dataset(self, file, groups_list): is2ds = xr.open_dataset(file) # Level 3b, hdf5: ATL11 - elif self._prod in ["ATL11"]: + elif self.product in ["ATL11"]: is2ds = self._build_dataset_template(file) # returns the wanted groups as a single list of full group path strings From c16a00359034b5920780ce0f7719ea6c9000891d Mon Sep 17 00:00:00 2001 From: Rachel Wegener Date: Tue, 5 Sep 2023 21:06:39 +0000 Subject: [PATCH 14/55] maintain backward compatibility and combine arguments --- icepyx/core/is2ref.py | 5 +++-- icepyx/core/read.py | 51 +++++++++++++++++++++++-------------------- 2 files changed, 30 insertions(+), 26 deletions(-) diff --git a/icepyx/core/is2ref.py b/icepyx/core/is2ref.py index 883772a9e..6003d91b8 100644 --- a/icepyx/core/is2ref.py +++ b/icepyx/core/is2ref.py @@ -15,6 +15,7 @@ def _validate_product(product): """ Confirm a valid ICESat-2 product was specified """ + error_msg = "A valid product string was not provided. Check user input, if given, or file metadata." if isinstance(product, str): product = str.upper(product) assert product in [ @@ -39,9 +40,9 @@ def _validate_product(product): "ATL19", "ATL20", "ATL21", - ], "Please enter a valid product" + ], error_msg else: - raise TypeError("Please enter a product string") + raise TypeError(error_msg) return product diff --git a/icepyx/core/read.py b/icepyx/core/read.py index c8fe4f276..c900ed6c4 100644 --- a/icepyx/core/read.py +++ b/icepyx/core/read.py @@ -297,26 +297,23 @@ class Read: # ---------------------------------------------------------------------- # Constructors - + + # TODO -- update docstring + def __init__( self, - path=None, + data_source, product=None, - data_source=None, - filename_pattern="ATL{product:2}_{datetime:%Y%m%d%H%M%S}_{rgt:4}{cycle:2}{orbitsegment:2}_{version:3}_{revision:2}.h5", + filename_pattern=None, out_obj_type=None, # xr.Dataset, ): # Raise warnings for depreciated arguments - if data_source: - warnings.warn( - 'The `data_source` argument is depreciated. Please use the path argument ' - 'instead.' - ) - if filename_pattern != "ATL{product:2}_{datetime:%Y%m%d%H%M%S}_{rgt:4}{cycle:2}{orbitsegment:2}_{version:3}_{revision:2}.h5": + if filename_pattern: warnings.warn( 'The `filename_pattern` argument is depreciated. Instead please provide a ' - 'glob string to the `path` argument' + 'string, list, or glob string to the `data_source` argument.' ) + if product: product = is2ref._validate_product(product) warnings.warn( @@ -327,14 +324,21 @@ def __init__( 'provide a `path` with files of only a single product type`.' ) - # Create the filelist from the user `path` argument - if isinstance(path, list): - self._filelist = path - elif os.path.isdir(path): - path = os.path.join(path, '*') - self._filelist = glob.glob(path) + # Create the filelist from the `data_source` argument + if filename_pattern: + # maintained for backward compatibility + pattern_ck, filelist = Read._check_source_for_pattern( + data_source, filename_pattern + ) + assert pattern_ck + self._filelist = filelist + elif isinstance(data_source, list): + self._filelist = data_source + elif os.path.isdir(data_source): + data_source = os.path.join(data_source, '*') + self._filelist = glob.glob(data_source) else: - self._filelist = glob.glob(path) + self._filelist = glob.glob(data_source) # Remove any directories from the list self._filelist = [f for f in self._filelist if not os.path.isdir(f)] @@ -342,7 +346,7 @@ def __init__( product_dict = {} for file_ in self._filelist: product_dict[file_] = self._extract_product(file_) - + # Raise warnings or errors for muliple products or products not matching the user-specified product all_products = list(set(product_dict.values())) if len(all_products) > 1: @@ -417,23 +421,21 @@ def vars(self): @property def filelist(self): """ - A read-only property for the user to view the list of files represented by this - Read object. + A read-only property for viewing the list of files represented by this Read object. """ return self._filelist @property def num_files(self): """ - Return the number of files that is being processed by the object + Return the number of files that are being processed """ return len(self.filelist) @property def product(self): """ - A read-only property for the user to view the product associated with the Read - object. + A read-only property for the user to view the product associated with the Read object. """ return self._product @@ -449,6 +451,7 @@ def _extract_product(filepath): try: # TODO consider: should we get this from the top level attrs instead? product = f['METADATA']['DatasetIdentification'].attrs['shortName'].decode() + product = is2ref._validate_product(product) # TODO test that this is the proper error except KeyError: raise 'Unable to parse the product name from file metadata' From 76480783444165519b66590b31e99248b27ad35b Mon Sep 17 00:00:00 2001 From: Rachel Wegener Date: Tue, 5 Sep 2023 22:15:31 +0000 Subject: [PATCH 15/55] update to new error message --- icepyx/tests/test_is2ref.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/icepyx/tests/test_is2ref.py b/icepyx/tests/test_is2ref.py index c2ddf6e5e..7d1bba7bf 100644 --- a/icepyx/tests/test_is2ref.py +++ b/icepyx/tests/test_is2ref.py @@ -8,14 +8,14 @@ def test_num_product(): dsnum = 6 - ermsg = "Please enter a product string" + ermsg = "A valid product string was not provided. Check user input, if given, or file metadata." with pytest.raises(TypeError, match=ermsg): is2ref._validate_product(dsnum) def test_bad_product(): wrngds = "atl-6" - ermsg = "Please enter a valid product" + ermsg = "A valid product string was not provided. Check user input, if given, or file metadata." with pytest.raises(AssertionError, match=ermsg): is2ref._validate_product(wrngds) From 4cfbfdbd7cda2701ab3036cd381cd16f7f889c6c Mon Sep 17 00:00:00 2001 From: Rachel Wegener Date: Fri, 8 Sep 2023 14:16:25 +0000 Subject: [PATCH 16/55] update docs --- .../example_notebooks/IS2_data_read-in.ipynb | 180 +++++++++++++----- doc/source/user_guide/documentation/read.rst | 3 + icepyx/core/read.py | 44 +++-- 3 files changed, 165 insertions(+), 62 deletions(-) diff --git a/doc/source/example_notebooks/IS2_data_read-in.ipynb b/doc/source/example_notebooks/IS2_data_read-in.ipynb index 115c63044..9288e7da3 100644 --- a/doc/source/example_notebooks/IS2_data_read-in.ipynb +++ b/doc/source/example_notebooks/IS2_data_read-in.ipynb @@ -63,9 +63,8 @@ "metadata": {}, "outputs": [], "source": [ - "path_root = '/full/path/to/your/data/'\n", - "pattern = \"processed_ATL{product:2}_{datetime:%Y%m%d%H%M%S}_{rgt:4}{cycle:2}{orbitsegment:2}_{version:3}_{revision:2}.h5\"\n", - "reader = ipx.Read(path_root, \"ATL06\", pattern) # or ipx.Read(filepath, \"ATLXX\") if your filenames match the default pattern" + "path_root = '/full/path/to/your/ATL06_data/'\n", + "reader = ipx.Read(path_root)" ] }, { @@ -111,10 +110,9 @@ "\n", "Reading in ICESat-2 data with icepyx happens in a few simple steps:\n", "1. Let icepyx know where to find your data (this might be local files or urls to data in cloud storage)\n", - "2. Tell icepyx how to interpret the filename format\n", - "3. Create an icepyx `Read` object\n", - "4. Make a list of the variables you want to read in (does not apply for gridded products)\n", - "5. Load your data into memory (or read it in lazily, if you're using Dask)\n", + "2. Create an icepyx `Read` object\n", + "3. Make a list of the variables you want to read in (does not apply for gridded products)\n", + "4. Load your data into memory (or read it in lazily, if you're using Dask)\n", "\n", "We go through each of these steps in more detail in this notebook." ] @@ -168,17 +166,18 @@ { "cell_type": "markdown", "id": "e8da42c1", - "metadata": {}, + "metadata": { + "user_expressions": [] + }, "source": [ "### Step 1: Set data source path\n", "\n", "Provide a full path to the data to be read in (i.e. opened).\n", "Currently accepted inputs are:\n", - "* a directory\n", - "* a single file\n", - "\n", - "All files to be read in *must* have a consistent filename pattern.\n", - "If a directory is supplied as the data source, all files in any subdirectories that match the filename pattern will be included.\n", + "* a string path to directory - all files from the directory will be opened\n", + "* a string path to single file - one file will be opened\n", + "* a list of filepaths - all files in the list will be opened\n", + "* a glob string (see [glob](https://docs.python.org/3/library/glob.html)) - any files matching the glob pattern will be opened\n", "\n", "S3 bucket data access is currently under development, and requires you are registered with NSIDC as a beta tester for cloud-based ICESat-2 data.\n", "icepyx is working to ensure a smooth transition to working with remote files.\n", @@ -205,6 +204,17 @@ "# filepath = path_root + 'ATL06-20181214041627-Sample.h5'" ] }, + { + "cell_type": "code", + "execution_count": null, + "id": "fac636c2-e0eb-4e08-adaa-8f47623e46a1", + "metadata": {}, + "outputs": [], + "source": [ + "# list_of_files = ['/my/data/ATL06/processed_ATL06_20190226005526_09100205_006_02.h5', \n", + "# '/my/other/data/ATL06/processed_ATL06_20191202102922_10160505_006_01.h5']" + ] + }, { "cell_type": "code", "execution_count": null, @@ -217,77 +227,123 @@ }, { "cell_type": "markdown", - "id": "92743496", + "id": "ba3ebeb0-3091-4712-b0f7-559ddb95ca5a", "metadata": { "user_expressions": [] }, "source": [ - "### Step 2: Create a filename pattern for your data files\n", + "#### Glob Strings\n", + "\n", + "[glob](https://docs.python.org/3/library/glob.html) is a Python library which allows users to list files in their file systems whose paths match a given pattern. Icepyx uses the glob library to give users greater flexibility over their input file lists.\n", "\n", - "Files provided by NSIDC typically match the format `\"ATL{product:2}_{datetime:%Y%m%d%H%M%S}_{rgt:4}{cycle:2}{orbitsegment:2}_{version:3}_{revision:2}.h5\"` where the parameters in curly brackets indicate a parameter name (left of the colon) and character length or format (right of the colon).\n", - "Some of this information is used during data opening to help correctly read and label the data within the data structure, particularly when multiple files are opened simultaneously.\n", + "glob works using `*` and `?` as wildcard characters, where `*` matches any number of characters and `?` matches a single character. For example:\n", "\n", - "By default, icepyx will assume your filenames follow the default format.\n", - "However, you can easily read in other ICESat-2 data files by supplying your own filename pattern.\n", - "For instance, `pattern=\"ATL{product:2}-{datetime:%Y%m%d%H%M%S}-Sample.h5\"`. A few example patterns are provided below." + "* `/this/path/*.h5`: refers to all files `.h5` files in the `/this/path` folder\n", + "* `ATL??.h5`: refers to any `.h5` file that starts with `ATL` and then has any 2 characters after it\n", + "* `/this/path/ATL??/*.h5`: refers to all `.h5` files that are in a subfolder of `/this/path` which has a filename of `ATL` followed by any 2 characters\n", + "\n", + "See the glob documentation or other online explainer tutorials for more in depth explanation, or advanced glob paths such as character classes and ranges." ] }, { - "cell_type": "code", - "execution_count": null, - "id": "7318abd0", - "metadata": {}, - "outputs": [], + "cell_type": "markdown", + "id": "20286c76-5632-4420-b2c9-a5a6b1952672", + "metadata": { + "user_expressions": [] + }, "source": [ - "# pattern = 'ATL06-{datetime:%Y%m%d%H%M%S}-Sample.h5'\n", - "# pattern = 'ATL{product:2}-{datetime:%Y%m%d%H%M%S}-Sample.h5'" + "#### Recursive Directory Search" + ] + }, + { + "cell_type": "markdown", + "id": "632bd1ce-2397-4707-a63f-9d5d2fc02fbc", + "metadata": { + "user_expressions": [] + }, + "source": [ + "If specifying a directory, glob will not by default search all of the subdirectories for matching filepaths. If this is the search method you would like, you can achieve this by either:\n", + "1. passing the `recursive` argument into `glob_kwargs`\n", + "2. using glob directly to create a list of filepaths" + ] + }, + { + "cell_type": "markdown", + "id": "da0cacd8-9ddc-4c31-86b6-167d850b989e", + "metadata": { + "user_expressions": [] + }, + "source": [ + "Method 1: passing the `recursive` argument into `glob_kwargs`" ] }, { "cell_type": "code", "execution_count": null, - "id": "f43e8664", + "id": "be79b0dd-efcf-4d50-bdb0-8e3ae8e8e38c", "metadata": {}, "outputs": [], "source": [ - "# pattern = \"ATL{product:2}_{datetime:%Y%m%d%H%M%S}_{rgt:4}{cycle:2}{orbitsegment:2}_{version:3}_{revision:2}.h5\"" + "import glob" ] }, { "cell_type": "code", "execution_count": null, - "id": "992a77fb", - "metadata": {}, + "id": "5d088571-496d-479a-9fb7-833ed7e98676", + "metadata": { + "tags": [] + }, "outputs": [], "source": [ - "# grid_pattern = \"ATL{product:2}_GL_0311_{res:3}m_{version:3}_{revision:2}.nc\"" + "list_of_files = glob.glob('/path/to/my/folder', recursive=True)\n", + "ipx.Read(list_of_files)" + ] + }, + { + "cell_type": "markdown", + "id": "76de9539-710c-49f6-9e9e-238849382c33", + "metadata": { + "user_expressions": [] + }, + "source": [ + "Method 2: using glob directly to create a list of filepaths" ] }, { "cell_type": "code", "execution_count": null, - "id": "6aec1a70", + "id": "e276b876-9ec7-4991-8520-05c97824b896", "metadata": {}, "outputs": [], "source": [ - "pattern = \"processed_ATL{product:2}_{datetime:%Y%m%d%H%M%S}_{rgt:4}{cycle:2}{orbitsegment:2}_{version:3}_{revision:2}.h5\"" + "ipx.Read('/path/to/my/folder', glob_kwargs={'recursive': True})" ] }, { "cell_type": "markdown", - "id": "4275b04c", + "id": "08df2874-7c54-4670-8f37-9135ea296ff5", "metadata": { "user_expressions": [] }, "source": [ - "### Step 3: Create an icepyx read object\n", + "```{admonition} Read Module Update\n", + "Previously, icepyx required two additional things: 1) that you specify a `product` and 2) that your files either matched the default `filename_pattern` or that the user provided their own `filename_pattern`. These two requirements have been removed. `product` is not read directly from the file metadata (the root group's `short_name` attribute). Flexibility to specify multiple files via the `filename_pattern` has been replaced with [glob string](https://docs.python.org/3/library/glob.html) and allowing a list of filepaths as an argument.\n", "\n", - "The `Read` object has two required inputs:\n", - "- `path` = a string with the full file path or full directory path to your hdf5 (.h5) format files.\n", - "- `product` = the data product you're working with, also known as the \"short name\".\n", + "These arguments have been maintained for backward compatibility, but will be fully removed in icepyx version 1.0.0.\n", + "```" + ] + }, + { + "cell_type": "markdown", + "id": "4275b04c", + "metadata": { + "user_expressions": [] + }, + "source": [ + "### Step 2: Create an icepyx read object\n", "\n", - "The `Read` object also accepts the optional keyword input:\n", - "- `pattern` = a formatted string indicating the filename pattern required for Intake's path_as_pattern argument." + "Using the `data_source` described in Step 1, we can create our Read object." ] }, { @@ -299,7 +355,17 @@ }, "outputs": [], "source": [ - "reader = ipx.Read(data_source=path_root, product=\"ATL06\", filename_pattern=pattern) # or ipx.Read(filepath, \"ATLXX\") if your filenames match the default pattern" + "reader = ipx.Read(data_source=path_root)" + ] + }, + { + "cell_type": "markdown", + "id": "7b2acfdb-75eb-4c64-b583-2ab19326aaee", + "metadata": { + "user_expressions": [] + }, + "source": [ + "The Read object now contains the list of matching files that will eventually be loaded into Python. You can inspect its properties, such as the files that were located or the identified product, directly on the Read object." ] }, { @@ -309,7 +375,27 @@ "metadata": {}, "outputs": [], "source": [ - "reader._filelist" + "reader.filelist" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "248590ba-b468-4ca5-999f-4323b704008e", + "metadata": {}, + "outputs": [], + "source": [ + "reader.num_files" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7455ee3f-f9ab-486e-b4c7-2fa2314d4084", + "metadata": {}, + "outputs": [], + "source": [ + "reader.product" ] }, { @@ -319,7 +405,7 @@ "user_expressions": [] }, "source": [ - "### Step 4: Specify variables to be read in\n", + "### Step 3: Specify variables to be read in\n", "\n", "To load your data into memory or prepare it for analysis, icepyx needs to know which variables you'd like to read in.\n", "If you've used icepyx to download data from NSIDC with variable subsetting (which is the default), then you may already be familiar with the icepyx `Variables` module and how to create and modify lists of variables.\n", @@ -426,7 +512,7 @@ "user_expressions": [] }, "source": [ - "### Step 5: Loading your data\n", + "### Step 4: Loading your data\n", "\n", "Now that you've set up all the options, you're ready to read your ICESat-2 data into memory!" ] @@ -541,9 +627,9 @@ ], "metadata": { "kernelspec": { - "display_name": "general", + "display_name": "icepyx-dev", "language": "python", - "name": "general" + "name": "icepyx-dev" }, "language_info": { "codemirror_mode": { diff --git a/doc/source/user_guide/documentation/read.rst b/doc/source/user_guide/documentation/read.rst index a5beedf4e..892f98087 100644 --- a/doc/source/user_guide/documentation/read.rst +++ b/doc/source/user_guide/documentation/read.rst @@ -19,6 +19,9 @@ Attributes .. autosummary:: :toctree: ../../_icepyx/ + Read.filelist + Read.num_files + Read.product Read.vars diff --git a/icepyx/core/read.py b/icepyx/core/read.py index c900ed6c4..90bc6be9d 100644 --- a/icepyx/core/read.py +++ b/icepyx/core/read.py @@ -8,12 +8,9 @@ import xarray as xr import icepyx.core.is2ref as is2ref -from icepyx.core.query import Query from icepyx.core.variables import Variables as Variables from icepyx.core.variables import list_of_dict_vals -# from icepyx.core.query import Query - def _make_np_datetime(df, keyword): """ @@ -267,19 +264,19 @@ class Read: Parameters ---------- - data_source : string - A string with a full file path or full directory path to ICESat-2 hdf5 (.h5) format files. - Files within a directory must have a consistent filename pattern that includes the "ATL??" data product name. - Files must all be within a single directory. + data_source : string, List + A string or list which specifies the files to be read. The string can be either: 1) the path of a single file 2) the path to a directory or 3) a [glob string](https://docs.python.org/3/library/glob.html). product : string ICESat-2 data product ID, also known as "short name" (e.g. ATL03). Available data products can be found at: https://nsidc.org/data/icesat-2/data-sets + **Depreciation warning:** This argument is no longer required and will be depreciated in version 1.0.0. The dataset product is read from the file metadata. filename_pattern : string, default 'ATL{product:2}_{datetime:%Y%m%d%H%M%S}_{rgt:4}{cycle:2}{orbitsegment:2}_{version:3}_{revision:2}.h5' String that shows the filename pattern as required for Intake's path_as_pattern argument. The default describes files downloaded directly from NSIDC (subsetted and non-subsetted) for most products (e.g. ATL06). The ATL11 filename pattern from NSIDC is: 'ATL{product:2}_{rgt:4}{orbitsegment:2}_{cycles:4}_{version:3}_{revision:2}.h5'. + **Depreciation warning:** This argument is no longer required and will be depreciated in version 1.0.0. out_obj_type : object, default xarray.Dataset The desired format for the data to be read in. @@ -292,13 +289,31 @@ class Read: Examples -------- + Reading a single file + ``` + ipx.Read('/path/to/data/processed_ATL06_20190226005526_09100205_006_02.h5') # doctest: +SKIP + ``` + Reading all files in a directory + ``` + ipx.Read('/path/to/data/') # doctest: +SKIP + ``` + Reading files that match a particular pattern (here, all .h5 files that start with `processed_ATL06_`). + ``` + ipx.Read('/path/to/data/processed_ATL06_*.h5') # doctest: +SKIP + ``` + Reading a specific list of files + ``` + list_of_files = ['/path/to/data/processed_ATL06_20190226005526_09100205_006_02.h5', + '/path/to/more/data/processed_ATL06_20191202102922_10160505_006_01.h5'] + ipx.Read(list_of_files) # doctest: +SKIP + ``` """ # ---------------------------------------------------------------------- # Constructors - # TODO -- update docstring + # TODO -- what if user passes an empty list, or the glob string returns empty def __init__( self, @@ -317,11 +332,11 @@ def __init__( if product: product = is2ref._validate_product(product) warnings.warn( - 'The `product` argument is no longer required. If the `path` argument given ' + 'The `product` argument is no longer required. If the `data_source` argument given ' 'contains files with multiple products the `product` argument will be used ' 'to filter that list. In all other cases the product argument is ignored. ' 'The recommended approach is to not include a `product` argument and instead ' - 'provide a `path` with files of only a single product type`.' + 'provide a `data_source` with files of only a single product type`.' ) # Create the filelist from the `data_source` argument @@ -369,7 +384,7 @@ def __init__( else: raise TypeError( f'Multiple product types were found in the file list: {product_dict}.' - 'Please provide a valid `path` parameter indicating files of a single ' + 'Please provide a valid `data_source` parameter indicating files of a single ' 'product' ) else: @@ -448,15 +463,14 @@ def _extract_product(filepath): Read the product type from the metadata of the file. Return the product as a string. """ with h5py.File(filepath, 'r') as f: - try: - # TODO consider: should we get this from the top level attrs instead? - product = f['METADATA']['DatasetIdentification'].attrs['shortName'].decode() + try: + product = f.attrs['short_name'].decode() product = is2ref._validate_product(product) # TODO test that this is the proper error except KeyError: raise 'Unable to parse the product name from file metadata' return product - + @staticmethod def _check_source_for_pattern(source, filename_pattern): """ From f7f823b40674fee5e83329d34518403ae285a81f Mon Sep 17 00:00:00 2001 From: Rachel Wegener Date: Fri, 8 Sep 2023 14:32:45 +0000 Subject: [PATCH 17/55] glob kwargs and list error --- icepyx/core/read.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/icepyx/core/read.py b/icepyx/core/read.py index 90bc6be9d..9176e6cc4 100644 --- a/icepyx/core/read.py +++ b/icepyx/core/read.py @@ -277,6 +277,8 @@ class Read: The default describes files downloaded directly from NSIDC (subsetted and non-subsetted) for most products (e.g. ATL06). The ATL11 filename pattern from NSIDC is: 'ATL{product:2}_{rgt:4}{orbitsegment:2}_{cycles:4}_{version:3}_{revision:2}.h5'. **Depreciation warning:** This argument is no longer required and will be depreciated in version 1.0.0. + glob_kwargs : dict, default {} + Additional arguments to be passed into the [glob.glob()](https://docs.python.org/3/library/glob.html#glob.glob)function out_obj_type : object, default xarray.Dataset The desired format for the data to be read in. @@ -313,13 +315,12 @@ class Read: # ---------------------------------------------------------------------- # Constructors - # TODO -- what if user passes an empty list, or the glob string returns empty - def __init__( self, data_source, product=None, filename_pattern=None, + glob_kwargs = {}, out_obj_type=None, # xr.Dataset, ): # Raise warnings for depreciated arguments @@ -351,9 +352,9 @@ def __init__( self._filelist = data_source elif os.path.isdir(data_source): data_source = os.path.join(data_source, '*') - self._filelist = glob.glob(data_source) + self._filelist = glob.glob(data_source, **glob_kwargs) else: - self._filelist = glob.glob(data_source) + self._filelist = glob.glob(data_source, **glob_kwargs) # Remove any directories from the list self._filelist = [f for f in self._filelist if not os.path.isdir(f)] @@ -387,6 +388,11 @@ def __init__( 'Please provide a valid `data_source` parameter indicating files of a single ' 'product' ) + elif len(all_products) == 0: + raise TypeError( + 'No files found matching the specified `data_source`. Check your glob ' + 'string or file list.' + ) else: # Assign the identified product to the property self._product = all_products[0] From 203f3adafc085bce44d0fd993b5932f60366f33c Mon Sep 17 00:00:00 2001 From: Rachel Wegener Date: Fri, 8 Sep 2023 14:44:22 +0000 Subject: [PATCH 18/55] formatting updates --- .../example_notebooks/IS2_data_read-in.ipynb | 42 ++++++++++--------- icepyx/core/read.py | 18 ++++---- 2 files changed, 33 insertions(+), 27 deletions(-) diff --git a/doc/source/example_notebooks/IS2_data_read-in.ipynb b/doc/source/example_notebooks/IS2_data_read-in.ipynb index 9288e7da3..0ab4a4dfa 100644 --- a/doc/source/example_notebooks/IS2_data_read-in.ipynb +++ b/doc/source/example_notebooks/IS2_data_read-in.ipynb @@ -262,7 +262,9 @@ "user_expressions": [] }, "source": [ - "If specifying a directory, glob will not by default search all of the subdirectories for matching filepaths. If this is the search method you would like, you can achieve this by either:\n", + "glob will not by default search all of the subdirectories for matching filepaths, but it has the ability to do so. To search recursively you need to 1) use `/**/` in the filepath to match any level of nested folders and 2) use the `recursive=True` argument. \n", + "\n", + "If you would like to search recursively, you can achieve this by either:\n", "1. passing the `recursive` argument into `glob_kwargs`\n", "2. using glob directly to create a list of filepaths" ] @@ -280,24 +282,11 @@ { "cell_type": "code", "execution_count": null, - "id": "be79b0dd-efcf-4d50-bdb0-8e3ae8e8e38c", + "id": "e276b876-9ec7-4991-8520-05c97824b896", "metadata": {}, "outputs": [], "source": [ - "import glob" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "5d088571-496d-479a-9fb7-833ed7e98676", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "list_of_files = glob.glob('/path/to/my/folder', recursive=True)\n", - "ipx.Read(list_of_files)" + "ipx.Read('/path/to/**/folder', glob_kwargs={'recursive': True})" ] }, { @@ -313,11 +302,24 @@ { "cell_type": "code", "execution_count": null, - "id": "e276b876-9ec7-4991-8520-05c97824b896", + "id": "be79b0dd-efcf-4d50-bdb0-8e3ae8e8e38c", "metadata": {}, "outputs": [], "source": [ - "ipx.Read('/path/to/my/folder', glob_kwargs={'recursive': True})" + "import glob" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5d088571-496d-479a-9fb7-833ed7e98676", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "list_of_files = glob.glob('/path/to/**/folder', recursive=True)\n", + "ipx.Read(list_of_files)" ] }, { @@ -328,9 +330,9 @@ }, "source": [ "```{admonition} Read Module Update\n", - "Previously, icepyx required two additional things: 1) that you specify a `product` and 2) that your files either matched the default `filename_pattern` or that the user provided their own `filename_pattern`. These two requirements have been removed. `product` is not read directly from the file metadata (the root group's `short_name` attribute). Flexibility to specify multiple files via the `filename_pattern` has been replaced with [glob string](https://docs.python.org/3/library/glob.html) and allowing a list of filepaths as an argument.\n", + "Previously, icepyx required two additional conditions: 1) a `product` argument and 2) that your files either matched the default `filename_pattern` or that the user provided their own `filename_pattern`. These two requirements have been removed. `product` is now read directly from the file metadata (the root group's `short_name` attribute). Flexibility to specify multiple files via the `filename_pattern` has been replaced with the [glob string](https://docs.python.org/3/library/glob.html) feature, and by allowing a list of filepaths as an argument.\n", "\n", - "These arguments have been maintained for backward compatibility, but will be fully removed in icepyx version 1.0.0.\n", + "The `product` and `filename_pattern` arguments have been maintained for backwards compatibility, but will be fully removed in icepyx version 1.0.0.\n", "```" ] }, diff --git a/icepyx/core/read.py b/icepyx/core/read.py index 9176e6cc4..f5cb53f8f 100644 --- a/icepyx/core/read.py +++ b/icepyx/core/read.py @@ -327,7 +327,8 @@ def __init__( if filename_pattern: warnings.warn( 'The `filename_pattern` argument is depreciated. Instead please provide a ' - 'string, list, or glob string to the `data_source` argument.' + 'string, list, or glob string to the `data_source` argument.', + stacklevel=2, ) if product: @@ -337,7 +338,8 @@ def __init__( 'contains files with multiple products the `product` argument will be used ' 'to filter that list. In all other cases the product argument is ignored. ' 'The recommended approach is to not include a `product` argument and instead ' - 'provide a `data_source` with files of only a single product type`.' + 'provide a `data_source` with files of only a single product type`.', + stacklevel=2, ) # Create the filelist from the `data_source` argument @@ -369,7 +371,8 @@ def __init__( if product: warnings.warn( f'Multiple products found in list of files: {product_dict}. Files that ' - 'do not match the user specified product will be removed from processing.' + 'do not match the user specified product will be removed from processing.', + stacklevel=2, ) self._filelist = [] for key, value in product_dict.items(): @@ -400,7 +403,8 @@ def __init__( if product and self._product != product: warnings.warn( f'User specified product {product} does not match the product from the file' - ' metadata {self._product}' + ' metadata {self._product}', + stacklevel=2, ) if out_obj_type is not None: @@ -442,21 +446,21 @@ def vars(self): @property def filelist(self): """ - A read-only property for viewing the list of files represented by this Read object. + Return the list of files represented by this Read object. """ return self._filelist @property def num_files(self): """ - Return the number of files that are being processed + Return the number of files that are being processed. """ return len(self.filelist) @property def product(self): """ - A read-only property for the user to view the product associated with the Read object. + Return the product associated with the Read object. """ return self._product From 10d15910a1f82ec2b18ba61eff647e243d44651c Mon Sep 17 00:00:00 2001 From: Rachel Wegener <35503632+rwegener2@users.noreply.github.com> Date: Tue, 12 Sep 2023 09:18:22 -0400 Subject: [PATCH 19/55] Apply suggestions from code review Co-authored-by: Jessica Scheick --- icepyx/core/read.py | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/icepyx/core/read.py b/icepyx/core/read.py index f5cb53f8f..c45a72fa9 100644 --- a/icepyx/core/read.py +++ b/icepyx/core/read.py @@ -272,8 +272,8 @@ class Read: Available data products can be found at: https://nsidc.org/data/icesat-2/data-sets **Depreciation warning:** This argument is no longer required and will be depreciated in version 1.0.0. The dataset product is read from the file metadata. - filename_pattern : string, default 'ATL{product:2}_{datetime:%Y%m%d%H%M%S}_{rgt:4}{cycle:2}{orbitsegment:2}_{version:3}_{revision:2}.h5' - String that shows the filename pattern as required for Intake's path_as_pattern argument. + filename_pattern : string, default None + String that shows the filename pattern as previously required for Intake's path_as_pattern argument. The default describes files downloaded directly from NSIDC (subsetted and non-subsetted) for most products (e.g. ATL06). The ATL11 filename pattern from NSIDC is: 'ATL{product:2}_{rgt:4}{orbitsegment:2}_{cycles:4}_{version:3}_{revision:2}.h5'. **Depreciation warning:** This argument is no longer required and will be depreciated in version 1.0.0. @@ -292,23 +292,18 @@ class Read: Examples -------- Reading a single file - ``` - ipx.Read('/path/to/data/processed_ATL06_20190226005526_09100205_006_02.h5') # doctest: +SKIP - ``` + >>> ipx.Read('/path/to/data/processed_ATL06_20190226005526_09100205_006_02.h5') # doctest: +SKIP + Reading all files in a directory - ``` - ipx.Read('/path/to/data/') # doctest: +SKIP - ``` + >>> ipx.Read('/path/to/data/') # doctest: +SKIP + Reading files that match a particular pattern (here, all .h5 files that start with `processed_ATL06_`). - ``` - ipx.Read('/path/to/data/processed_ATL06_*.h5') # doctest: +SKIP - ``` + >>> ipx.Read('/path/to/data/processed_ATL06_*.h5') # doctest: +SKIP + Reading a specific list of files - ``` - list_of_files = ['/path/to/data/processed_ATL06_20190226005526_09100205_006_02.h5', + >>> list_of_files = ['/path/to/data/processed_ATL06_20190226005526_09100205_006_02.h5', '/path/to/more/data/processed_ATL06_20191202102922_10160505_006_01.h5'] - ipx.Read(list_of_files) # doctest: +SKIP - ``` + >>> ipx.Read(list_of_files) # doctest: +SKIP """ @@ -326,7 +321,7 @@ def __init__( # Raise warnings for depreciated arguments if filename_pattern: warnings.warn( - 'The `filename_pattern` argument is depreciated. Instead please provide a ' + 'The `filename_pattern` argument is deprecated. Instead please provide a ' 'string, list, or glob string to the `data_source` argument.', stacklevel=2, ) From 0b23d1e12808fb69afa85d1fa5168c17221f52f2 Mon Sep 17 00:00:00 2001 From: Rachel Wegener Date: Tue, 12 Sep 2023 13:24:45 +0000 Subject: [PATCH 20/55] remove num_files --- doc/source/user_guide/documentation/read.rst | 1 - icepyx/core/read.py | 7 ------- 2 files changed, 8 deletions(-) diff --git a/doc/source/user_guide/documentation/read.rst b/doc/source/user_guide/documentation/read.rst index 892f98087..68da03b1d 100644 --- a/doc/source/user_guide/documentation/read.rst +++ b/doc/source/user_guide/documentation/read.rst @@ -20,7 +20,6 @@ Attributes :toctree: ../../_icepyx/ Read.filelist - Read.num_files Read.product Read.vars diff --git a/icepyx/core/read.py b/icepyx/core/read.py index c45a72fa9..b139ca567 100644 --- a/icepyx/core/read.py +++ b/icepyx/core/read.py @@ -444,13 +444,6 @@ def filelist(self): Return the list of files represented by this Read object. """ return self._filelist - - @property - def num_files(self): - """ - Return the number of files that are being processed. - """ - return len(self.filelist) @property def product(self): From 6f5beadcb8bca332fbf9fbda79310e075bbd8af3 Mon Sep 17 00:00:00 2001 From: Rachel Wegener Date: Tue, 12 Sep 2023 13:28:03 +0000 Subject: [PATCH 21/55] fix docs test typo --- icepyx/core/read.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/icepyx/core/read.py b/icepyx/core/read.py index b139ca567..4300cebbc 100644 --- a/icepyx/core/read.py +++ b/icepyx/core/read.py @@ -301,8 +301,10 @@ class Read: >>> ipx.Read('/path/to/data/processed_ATL06_*.h5') # doctest: +SKIP Reading a specific list of files - >>> list_of_files = ['/path/to/data/processed_ATL06_20190226005526_09100205_006_02.h5', - '/path/to/more/data/processed_ATL06_20191202102922_10160505_006_01.h5'] + >>> list_of_files = [ + '/path/to/data/processed_ATL06_20190226005526_09100205_006_02.h5', + '/path/to/more/data/processed_ATL06_20191202102922_10160505_006_01.h5', + ] >>> ipx.Read(list_of_files) # doctest: +SKIP """ From 035ee5ab1e33dc43a471c36286a59c036d0cb292 Mon Sep 17 00:00:00 2001 From: Rachel Wegener Date: Tue, 12 Sep 2023 13:37:15 +0000 Subject: [PATCH 22/55] trying again to fix the build --- icepyx/core/read.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/icepyx/core/read.py b/icepyx/core/read.py index 4300cebbc..b9a6e672e 100644 --- a/icepyx/core/read.py +++ b/icepyx/core/read.py @@ -302,9 +302,9 @@ class Read: Reading a specific list of files >>> list_of_files = [ - '/path/to/data/processed_ATL06_20190226005526_09100205_006_02.h5', - '/path/to/more/data/processed_ATL06_20191202102922_10160505_006_01.h5', - ] + ... '/path/to/data/processed_ATL06_20190226005526_09100205_006_02.h5', + ... '/path/to/more/data/processed_ATL06_20191202102922_10160505_006_01.h5', + ... ] >>> ipx.Read(list_of_files) # doctest: +SKIP """ From 903c351007bc7245bf4d107b48b1f38c67acf900 Mon Sep 17 00:00:00 2001 From: Rachel Wegener Date: Tue, 12 Sep 2023 13:48:27 +0000 Subject: [PATCH 23/55] add feedback to docs page --- .../example_notebooks/IS2_data_read-in.ipynb | 42 +++++++------------ 1 file changed, 14 insertions(+), 28 deletions(-) diff --git a/doc/source/example_notebooks/IS2_data_read-in.ipynb b/doc/source/example_notebooks/IS2_data_read-in.ipynb index 0ab4a4dfa..4cdd3bf5a 100644 --- a/doc/source/example_notebooks/IS2_data_read-in.ipynb +++ b/doc/source/example_notebooks/IS2_data_read-in.ipynb @@ -177,11 +177,7 @@ "* a string path to directory - all files from the directory will be opened\n", "* a string path to single file - one file will be opened\n", "* a list of filepaths - all files in the list will be opened\n", - "* a glob string (see [glob](https://docs.python.org/3/library/glob.html)) - any files matching the glob pattern will be opened\n", - "\n", - "S3 bucket data access is currently under development, and requires you are registered with NSIDC as a beta tester for cloud-based ICESat-2 data.\n", - "icepyx is working to ensure a smooth transition to working with remote files.\n", - "We'd love your help exploring and testing these features as they become available!" + "* a glob string (see [glob](https://docs.python.org/3/library/glob.html)) - any files matching the glob pattern will be opened" ] }, { @@ -215,16 +211,6 @@ "# '/my/other/data/ATL06/processed_ATL06_20191202102922_10160505_006_01.h5']" ] }, - { - "cell_type": "code", - "execution_count": null, - "id": "e683ebf7", - "metadata": {}, - "outputs": [], - "source": [ - "# urlpath = 's3://nsidc-cumulus-prod-protected/ATLAS/ATL03/004/2019/11/30/ATL03_20191130221008_09930503_004_01.h5'" - ] - }, { "cell_type": "markdown", "id": "ba3ebeb0-3091-4712-b0f7-559ddb95ca5a", @@ -238,9 +224,9 @@ "\n", "glob works using `*` and `?` as wildcard characters, where `*` matches any number of characters and `?` matches a single character. For example:\n", "\n", - "* `/this/path/*.h5`: refers to all files `.h5` files in the `/this/path` folder\n", - "* `ATL??.h5`: refers to any `.h5` file that starts with `ATL` and then has any 2 characters after it\n", - "* `/this/path/ATL??/*.h5`: refers to all `.h5` files that are in a subfolder of `/this/path` which has a filename of `ATL` followed by any 2 characters\n", + "* `/this/path/*.h5`: refers to all `.h5` files in the `/this/path` folder (Example matches: \"/this/path/processed_ATL03_20191130221008_09930503_006_01.h5\" or \"/this/path/myfavoriteicsat-2file.h5\")\n", + "* `/this/path/*ATL07*.h5`: refers to all `.h5` files in the `/this/path` folder that have ATL07 in the filename. (Example matches: \"/this/path/ATL07-02_20221012220720_03391701_005_01.h5\" or \"/this/path/processed_ATL07.h5\")\n", + "* `/this/path/ATL??/*.h5`: refers to all `.h5` files that are in a subfolder of `/this/path` and a subdirectory of `ATL` followed by any 2 characters (Example matches: \"/this/path/ATL03/processed_ATL03_20191130221008_09930503_006_01.h5\", \"/this/path/ATL06/myfile.h5\")\n", "\n", "See the glob documentation or other online explainer tutorials for more in depth explanation, or advanced glob paths such as character classes and ranges." ] @@ -289,6 +275,16 @@ "ipx.Read('/path/to/**/folder', glob_kwargs={'recursive': True})" ] }, + { + "cell_type": "markdown", + "id": "f5a1e85e-fc4a-405f-9710-0cb61b827f2c", + "metadata": { + "user_expressions": [] + }, + "source": [ + "You can use `glob_kwargs` for any additional argument to Python's builtin `glob.glob` that you would like to pass in via icepyx." + ] + }, { "cell_type": "markdown", "id": "76de9539-710c-49f6-9e9e-238849382c33", @@ -380,16 +376,6 @@ "reader.filelist" ] }, - { - "cell_type": "code", - "execution_count": null, - "id": "248590ba-b468-4ca5-999f-4323b704008e", - "metadata": {}, - "outputs": [], - "source": [ - "reader.num_files" - ] - }, { "cell_type": "code", "execution_count": null, From 5e06de94da0c492b4b3cc94a5b4d834b0dc829a4 Mon Sep 17 00:00:00 2001 From: Rachel Wegener Date: Thu, 14 Sep 2023 13:32:52 +0000 Subject: [PATCH 24/55] fix typo --- icepyx/core/read.py | 1 - 1 file changed, 1 deletion(-) diff --git a/icepyx/core/read.py b/icepyx/core/read.py index baeba27f4..966df8d08 100644 --- a/icepyx/core/read.py +++ b/icepyx/core/read.py @@ -338,7 +338,6 @@ def __init__( if data_source is None: raise ValueError("data_source is a required arguemnt") - ) # Raise warnings for deprecated arguments if filename_pattern: From e3566f8765610b1f239ad46eb951d326c1666e07 Mon Sep 17 00:00:00 2001 From: Rachel Wegener Date: Mon, 18 Sep 2023 17:27:17 +0000 Subject: [PATCH 25/55] mvp for making a standalone variables class --- icepyx/core/query.py | 12 ++----- icepyx/core/read.py | 4 +-- icepyx/core/variables.py | 68 +++++++++++++++++++++++++--------------- 3 files changed, 46 insertions(+), 38 deletions(-) diff --git a/icepyx/core/query.py b/icepyx/core/query.py index e8f1d8e7c..44ecd1299 100644 --- a/icepyx/core/query.py +++ b/icepyx/core/query.py @@ -687,17 +687,14 @@ def order_vars(self): # DevGoal: check for active session here if hasattr(self, "_cust_options"): self._order_vars = Variables( - self._source, + self, auth = self.auth, - product=self.product, avail=self._cust_options["variables"], ) else: self._order_vars = Variables( - self._source, + self, auth=self.auth, - product=self.product, - version=self._version, ) # I think this is where property setters come in, and one should be used here? Right now order_vars.avail is only filled in @@ -728,10 +725,7 @@ def file_vars(self): if not hasattr(self, "_file_vars"): if self._source == "file": - self._file_vars = Variables(self._source, - auth=self.auth, - product=self.product, - ) + self._file_vars = Variables(self, auth=self.auth) return self._file_vars diff --git a/icepyx/core/read.py b/icepyx/core/read.py index 966df8d08..80064f4a8 100644 --- a/icepyx/core/read.py +++ b/icepyx/core/read.py @@ -453,9 +453,7 @@ def vars(self): """ if not hasattr(self, "_read_vars"): - self._read_vars = Variables( - "file", path=self.filelist[0], product=self.product - ) + self._read_vars = Variables(self.filelist[0]) return self._read_vars diff --git a/icepyx/core/variables.py b/icepyx/core/variables.py index d46561f46..ff770ed1b 100644 --- a/icepyx/core/variables.py +++ b/icepyx/core/variables.py @@ -1,9 +1,11 @@ import numpy as np import os import pprint +import warnings from icepyx.core.auth import EarthdataAuthMixin import icepyx.core.is2ref as is2ref +import icepyx.core as ipxc # DEVGOAL: use h5py to simplify some of these tasks, if possible! @@ -48,38 +50,54 @@ class Variables(EarthdataAuthMixin): def __init__( self, - vartype, + data_source, + # data_source is either 1) a dict (?) with product / version 2) a filepath (local for + # now) or 3) a Query object + vartype=None, avail=None, wanted=None, - product=None, - version=None, - path=None, + # product=None, + # version=None, + # path=None, auth=None, ): - - assert vartype in ["order", "file"], "Please submit a valid variables type flag" + if vartype or data_source in ['order', 'file']: + raise warnings.warn('Depreciation Warning', stacklevel=2) + # Make this an error + + # Set the product and version from the data_source + # These could be streamlined if we don't try to maintain backwards compat. + # Is this considered a user facing feature right now? If not maintaining compatibility + # isn't that important, because all we need to do is change the internal code. + if isinstance(data_source, ipxc.query.Query): + self.product = data_source.product + self.version = data_source.product_version + # TODO discussion: should a Read object be an option here? + # YES, but figure out what to do about version + elif isinstance(data_source, str): + self.path = data_source + # TODO set product and version? You don't really need to, but it maintains the + # conceptual consistency + elif isinstance(data_source, dict): + # TODO: assume latest version if not given? + # TODO: validate version is valid? + if 'product' not in data_source.keys(): + raise KeyError('message') + else: + self.product = is2ref._validate_product(data_source['product']) + # Set version if given, else set it to None + self.version = data_source.get('version', None) + else: + TypeError('message') # initialize authentication properties EarthdataAuthMixin.__init__(self, auth=auth) - self._vartype = vartype - self.product = product self._avail = avail self.wanted = wanted # DevGoal: put some more/robust checks here to assess validity of inputs - if self._vartype == "order": - if self._avail == None: - self._version = version - elif self._vartype == "file": - # DevGoal: check that the list or string are valid dir/files - self.path = path - - # @property - # def wanted(self): - # return self._wanted - def avail(self, options=False, internal=False): """ Get the list of available variables and variable paths from the input data product @@ -97,16 +115,14 @@ def avail(self, options=False, internal=False): . 'quality_assessment/gt3r/signal_selection_source_fraction_3'] """ - # if hasattr(self, '_avail'): - # return self._avail - # else: + if not hasattr(self, "_avail") or self._avail == None: - if self._vartype == "order": + if not hasattr(self, 'path'): self._avail = is2ref._get_custom_options( - self.session, self.product, self._version + self.session, self.product, self.version )["variables"] - - elif self._vartype == "file": + else: + # If a path was given, use that file to read the variables import h5py self._avail = [] From 44fd8ccc2109b579a47cbff546eb41e7eaeca096 Mon Sep 17 00:00:00 2001 From: Rachel Wegener Date: Tue, 3 Oct 2023 15:07:58 +0000 Subject: [PATCH 26/55] clean comments --- icepyx/core/variables.py | 41 ++++++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/icepyx/core/variables.py b/icepyx/core/variables.py index ff770ed1b..320795391 100644 --- a/icepyx/core/variables.py +++ b/icepyx/core/variables.py @@ -28,7 +28,12 @@ class Variables(EarthdataAuthMixin): Parameters ---------- + data_source: dictionary, string, icepyx.core.Query + Specification of which product and version to find available variables for. This + can be specified as either 1) a dictionary, with the keys `product` and `version`, + 2) a string containing a local filepath to an IceSat-2 file or 3) a Query object vartype : string + This argument is depreciated. The vartype will be inferred from data_source. One of ['order', 'file'] to indicate the source of the input variables. This field will be auto-populated when a variable object is created as an attribute of a query object. @@ -51,44 +56,44 @@ class Variables(EarthdataAuthMixin): def __init__( self, data_source, - # data_source is either 1) a dict (?) with product / version 2) a filepath (local for - # now) or 3) a Query object vartype=None, avail=None, wanted=None, - # product=None, - # version=None, - # path=None, + product=None, # Depreciated + version=None, # Depreciated + path=None, # Depreciated auth=None, ): + # Depreciation warnings if vartype or data_source in ['order', 'file']: - raise warnings.warn('Depreciation Warning', stacklevel=2) - # Make this an error - + raise warnings.warn('It is no longer required to specify the type of variable.', + stacklevel=2) + + if product or version: + raise warnings.warn('product and version argument are no longer required. Provide' \ + 'product via data_source dictionary', stacklevel=2) + data_source = {'product': product, 'version': version} + elif path: + raise warnings.warn('path argument is no longer required. Provide path as the' \ + 'data_source argument', stacklevel=2) + data_source = path + # Set the product and version from the data_source - # These could be streamlined if we don't try to maintain backwards compat. - # Is this considered a user facing feature right now? If not maintaining compatibility - # isn't that important, because all we need to do is change the internal code. if isinstance(data_source, ipxc.query.Query): self.product = data_source.product self.version = data_source.product_version - # TODO discussion: should a Read object be an option here? - # YES, but figure out what to do about version elif isinstance(data_source, str): self.path = data_source - # TODO set product and version? You don't really need to, but it maintains the - # conceptual consistency elif isinstance(data_source, dict): # TODO: assume latest version if not given? - # TODO: validate version is valid? if 'product' not in data_source.keys(): - raise KeyError('message') + raise KeyError('productd is a required key in data_source dictionary') else: self.product = is2ref._validate_product(data_source['product']) # Set version if given, else set it to None self.version = data_source.get('version', None) else: - TypeError('message') + raise TypeError('data_source must be of type Dict, string, or icepyx.core.Query') # initialize authentication properties EarthdataAuthMixin.__init__(self, auth=auth) From 69dce54b9f425de9e16301789a684264e01337dc Mon Sep 17 00:00:00 2001 From: Rachel Wegener Date: Mon, 16 Oct 2023 20:34:23 +0000 Subject: [PATCH 27/55] split data_source into seperate arguments --- icepyx/core/query.py | 11 +++-- icepyx/core/read.py | 3 +- icepyx/core/variables.py | 87 +++++++++++++++++++++++----------------- 3 files changed, 59 insertions(+), 42 deletions(-) diff --git a/icepyx/core/query.py b/icepyx/core/query.py index 44ecd1299..5c601859d 100644 --- a/icepyx/core/query.py +++ b/icepyx/core/query.py @@ -687,13 +687,15 @@ def order_vars(self): # DevGoal: check for active session here if hasattr(self, "_cust_options"): self._order_vars = Variables( - self, + product=self.product, + version = self.version, auth = self.auth, avail=self._cust_options["variables"], ) else: self._order_vars = Variables( - self, + product=self.product, + version=self._version, auth=self.auth, ) @@ -725,7 +727,10 @@ def file_vars(self): if not hasattr(self, "_file_vars"): if self._source == "file": - self._file_vars = Variables(self, auth=self.auth) + self._file_vars = Variables(auth=self.auth, + product=self.product, + version=self.version, + ) return self._file_vars diff --git a/icepyx/core/read.py b/icepyx/core/read.py index 80064f4a8..af70e65d5 100644 --- a/icepyx/core/read.py +++ b/icepyx/core/read.py @@ -453,8 +453,7 @@ def vars(self): """ if not hasattr(self, "_read_vars"): - self._read_vars = Variables(self.filelist[0]) - + self._read_vars = Variables(path=self.filelist[0]) return self._read_vars @property diff --git a/icepyx/core/variables.py b/icepyx/core/variables.py index 320795391..32f710a9b 100644 --- a/icepyx/core/variables.py +++ b/icepyx/core/variables.py @@ -5,6 +5,8 @@ from icepyx.core.auth import EarthdataAuthMixin import icepyx.core.is2ref as is2ref +from icepyx.core.exceptions import DeprecationError +import icepyx.core.validate_inputs as val import icepyx.core as ipxc # DEVGOAL: use h5py to simplify some of these tasks, if possible! @@ -28,10 +30,8 @@ class Variables(EarthdataAuthMixin): Parameters ---------- - data_source: dictionary, string, icepyx.core.Query - Specification of which product and version to find available variables for. This - can be specified as either 1) a dictionary, with the keys `product` and `version`, - 2) a string containing a local filepath to an IceSat-2 file or 3) a Query object + filepath: string + The path to a local Icesat-2 file. This is the ... TODO vartype : string This argument is depreciated. The vartype will be inferred from data_source. One of ['order', 'file'] to indicate the source of the input variables. @@ -55,46 +55,41 @@ class Variables(EarthdataAuthMixin): def __init__( self, - data_source, vartype=None, avail=None, wanted=None, - product=None, # Depreciated - version=None, # Depreciated - path=None, # Depreciated + product=None, + version=None, + path=None, auth=None, ): - # Depreciation warnings - if vartype or data_source in ['order', 'file']: - raise warnings.warn('It is no longer required to specify the type of variable.', - stacklevel=2) - - if product or version: - raise warnings.warn('product and version argument are no longer required. Provide' \ - 'product via data_source dictionary', stacklevel=2) - data_source = {'product': product, 'version': version} - elif path: - raise warnings.warn('path argument is no longer required. Provide path as the' \ - 'data_source argument', stacklevel=2) - data_source = path + # Deprecation error + if vartype in ['order', 'file']: + raise DeprecationError( + 'It is no longer required to specify the variable type `vartype`. Instead please ', + 'provide either the path to a local file (arg: `path`) or the product you would ', + 'like variables for (arg: `product`).' + ) - # Set the product and version from the data_source - if isinstance(data_source, ipxc.query.Query): - self.product = data_source.product - self.version = data_source.product_version - elif isinstance(data_source, str): - self.path = data_source - elif isinstance(data_source, dict): - # TODO: assume latest version if not given? - if 'product' not in data_source.keys(): - raise KeyError('productd is a required key in data_source dictionary') - else: - self.product = is2ref._validate_product(data_source['product']) - # Set version if given, else set it to None - self.version = data_source.get('version', None) - else: - raise TypeError('data_source must be of type Dict, string, or icepyx.core.Query') + if path and product: + raise TypeError( + 'Please provide either a filepath or a product. If a filepath is provided ', + 'variables will be read from the file. If a product is provided all available ', + 'variables for that product will be returned.' + ) + # Set the product and version from either the input args or the file + if path: + self.path = path + elif product: + # Check for valid product string + self.product = is2ref._validate_product(product) + # Check for valid version string + # If version is not specified by the user assume the most recent version + self.version = val.prod_version(self.latest_version(), version) + else: + raise TypeError('Either a filepath or a product need to be given as input arguments.') + # initialize authentication properties EarthdataAuthMixin.__init__(self, auth=auth) @@ -659,3 +654,21 @@ def remove(self, all=False, var_list=None, beam_list=None, keyword_list=None): del self.wanted[vkey] except KeyError: pass + + # DevNote: This is copied directly from the Query class. Is there a better way to share + # this functionality? + def latest_version(self): + """ + Determine the most recent version available for the given product. + + Examples + -------- + >>> reg_a = ipx.Query('ATL06',[-55, 68, -48, 71],['2019-02-20','2019-02-28']) + >>> reg_a.latest_version() + '006' + """ + if not hasattr(self, "_about_product"): + self._about_product = is2ref.about_product(self.product) + return max( + [entry["version_id"] for entry in self._about_product["feed"]["entry"]] + ) From 72e1e37d6823acbc6ddf68e7567853288ba2c6cb Mon Sep 17 00:00:00 2001 From: Rachel Wegener Date: Mon, 16 Oct 2023 20:38:27 +0000 Subject: [PATCH 28/55] clean dev notes --- icepyx/core/variables.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/icepyx/core/variables.py b/icepyx/core/variables.py index 32f710a9b..9056ec1df 100644 --- a/icepyx/core/variables.py +++ b/icepyx/core/variables.py @@ -86,7 +86,7 @@ def __init__( self.product = is2ref._validate_product(product) # Check for valid version string # If version is not specified by the user assume the most recent version - self.version = val.prod_version(self.latest_version(), version) + self.version = val.prod_version(self.get_latest_version(), version) else: raise TypeError('Either a filepath or a product need to be given as input arguments.') @@ -655,9 +655,8 @@ def remove(self, all=False, var_list=None, beam_list=None, keyword_list=None): except KeyError: pass - # DevNote: This is copied directly from the Query class. Is there a better way to share - # this functionality? - def latest_version(self): + # DevNote: This is a modified function from the Query class. + def get_latest_version(self): """ Determine the most recent version available for the given product. @@ -667,8 +666,7 @@ def latest_version(self): >>> reg_a.latest_version() '006' """ - if not hasattr(self, "_about_product"): - self._about_product = is2ref.about_product(self.product) + about_info = is2ref.about_product(self.product) return max( - [entry["version_id"] for entry in self._about_product["feed"]["entry"]] + [entry["version_id"] for entry in about_info["feed"]["entry"]] ) From 83d24fb3e40503c3d31f0429ebdc1cf018392fb0 Mon Sep 17 00:00:00 2001 From: Rachel Wegener Date: Mon, 16 Oct 2023 21:01:36 +0000 Subject: [PATCH 29/55] update docstrings --- icepyx/core/variables.py | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/icepyx/core/variables.py b/icepyx/core/variables.py index 9056ec1df..605f17e15 100644 --- a/icepyx/core/variables.py +++ b/icepyx/core/variables.py @@ -29,14 +29,21 @@ class Variables(EarthdataAuthMixin): contained in ICESat-2 products. Parameters - ---------- - filepath: string - The path to a local Icesat-2 file. This is the ... TODO + ---------- vartype : string This argument is depreciated. The vartype will be inferred from data_source. One of ['order', 'file'] to indicate the source of the input variables. This field will be auto-populated when a variable object is created as an attribute of a query object. + path : string, default None + The path to a local Icesat-2 file. The variables list will contain the variables + present in this file. Either path or product are required input arguments. + product : string, default None + Properly formatted string specifying a valid ICESat-2 product. The variables list will + contain all available variables for this product. Either product or path are required + input arguments. + version : string, default None + Properly formatted string specifying a valid version of the ICESat-2 product. avail : dictionary, default None Dictionary (key:values) of available variable names (keys) and paths (values). wanted : dictionary, default None @@ -45,22 +52,17 @@ class Variables(EarthdataAuthMixin): A session object authenticating the user to download data using their Earthdata login information. The session object will automatically be passed from the query object if you have successfully logged in there. - product : string, default None - Properly formatted string specifying a valid ICESat-2 product - version : string, default None - Properly formatted string specifying a valid version of the ICESat-2 product - path : string, default None - For vartype file, a path to a directory of or single input data file (not yet implemented) + """ def __init__( self, vartype=None, - avail=None, - wanted=None, + path=None, product=None, version=None, - path=None, + avail=None, + wanted=None, auth=None, ): # Deprecation error From a187328d3ff2640ca59071f8fdacc002dd8ac6e4 Mon Sep 17 00:00:00 2001 From: Rachel Wegener Date: Tue, 17 Oct 2023 13:48:23 +0000 Subject: [PATCH 30/55] little fixes --- icepyx/core/query.py | 4 ++-- icepyx/core/variables.py | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/icepyx/core/query.py b/icepyx/core/query.py index 5c601859d..3c451ff48 100644 --- a/icepyx/core/query.py +++ b/icepyx/core/query.py @@ -688,7 +688,7 @@ def order_vars(self): if hasattr(self, "_cust_options"): self._order_vars = Variables( product=self.product, - version = self.version, + version = self._version, auth = self.auth, avail=self._cust_options["variables"], ) @@ -729,7 +729,7 @@ def file_vars(self): if self._source == "file": self._file_vars = Variables(auth=self.auth, product=self.product, - version=self.version, + version=self._version, ) return self._file_vars diff --git a/icepyx/core/variables.py b/icepyx/core/variables.py index 605f17e15..20f9c1a2f 100644 --- a/icepyx/core/variables.py +++ b/icepyx/core/variables.py @@ -475,6 +475,7 @@ def append(self, defaults=False, var_list=None, beam_list=None, keyword_list=Non self._check_valid_lists(vgrp, allpaths, var_list, beam_list, keyword_list) # add the mandatory variables to the data object + # TODO QUESTION - why is this distinction made? How can we handle it wihtout vartype? if self._vartype == "order": nec_varlist = [ "sc_orient", From dce23f940846a0a15c74386c64af2f84574d7abf Mon Sep 17 00:00:00 2001 From: Rachel Wegener Date: Tue, 17 Oct 2023 14:43:07 +0000 Subject: [PATCH 31/55] upgrade Variables to an stand alone import --- icepyx/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/icepyx/__init__.py b/icepyx/__init__.py index 3d92e2e60..40ea9e1ec 100644 --- a/icepyx/__init__.py +++ b/icepyx/__init__.py @@ -1,5 +1,6 @@ from icepyx.core.query import Query, GenQuery from icepyx.core.read import Read from icepyx.quest.quest import Quest +from icepyx.core.variables import Variables from _icepyx_version import version as __version__ From 3561be8703c7f37585ef8a50109e1ce91d6af6f7 Mon Sep 17 00:00:00 2001 From: Rachel Wegener Date: Tue, 17 Oct 2023 14:44:10 +0000 Subject: [PATCH 32/55] update example notebooks --- .../IS2_data_access2-subsetting.ipynb | 42 ++- .../IS2_data_variables.ipynb | 355 ++++++++++++++++-- 2 files changed, 359 insertions(+), 38 deletions(-) diff --git a/doc/source/example_notebooks/IS2_data_access2-subsetting.ipynb b/doc/source/example_notebooks/IS2_data_access2-subsetting.ipynb index 89247de5f..3803b9fd6 100644 --- a/doc/source/example_notebooks/IS2_data_access2-subsetting.ipynb +++ b/doc/source/example_notebooks/IS2_data_access2-subsetting.ipynb @@ -51,7 +51,9 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "user_expressions": [] + }, "source": [ "Create a query object and log in to Earthdata\n", "\n", @@ -83,7 +85,9 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "user_expressions": [] + }, "source": [ "## Discover Subsetting Options\n", "\n", @@ -108,7 +112,9 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "user_expressions": [] + }, "source": [ "By default, spatial and temporal subsetting based on your initial inputs is applied to your order unless you specify `subset=False` to `order_granules()` or `download_granules()` (which calls `order_granules` under the hood if you have not already placed your order) functions.\n", "Additional subsetting options must be specified as keyword arguments to the order/download functions.\n", @@ -118,7 +124,9 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "user_expressions": [] + }, "source": [ "### _Why do I have to provide spatial bounds to icepyx even if I don't use them to subset my data order?_\n", "\n", @@ -132,7 +140,9 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "user_expressions": [] + }, "source": [ "## About Data Variables in a query object\n", "\n", @@ -145,7 +155,9 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "user_expressions": [] + }, "source": [ "### Determine what variables are available for your data product\n", "There are multiple ways to get a complete list of available variables.\n", @@ -159,7 +171,9 @@ { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "tags": [] + }, "outputs": [], "source": [ "region_a.order_vars.avail()" @@ -167,7 +181,9 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "user_expressions": [] + }, "source": [ "By passing the boolean `options=True` to the `avail` method, you can obtain lists of unique possible variable inputs (var_list inputs) and path subdirectory inputs (keyword_list and beam_list inputs) for your data product. These can be helpful for building your wanted variable list." ] @@ -175,7 +191,9 @@ { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "tags": [] + }, "outputs": [], "source": [ "region_a.order_vars.avail(options=True)" @@ -353,9 +371,9 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3 (ipykernel)", + "display_name": "icepyx-dev", "language": "python", - "name": "python3" + "name": "icepyx-dev" }, "language_info": { "codemirror_mode": { @@ -367,7 +385,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.10.12" + "version": "3.11.4" } }, "nbformat": 4, diff --git a/doc/source/example_notebooks/IS2_data_variables.ipynb b/doc/source/example_notebooks/IS2_data_variables.ipynb index 3ac1f99fe..3835bf9af 100644 --- a/doc/source/example_notebooks/IS2_data_variables.ipynb +++ b/doc/source/example_notebooks/IS2_data_variables.ipynb @@ -2,7 +2,9 @@ "cells": [ { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "user_expressions": [] + }, "source": [ "# ICESat-2's Nested Variables\n", "\n", @@ -13,10 +15,10 @@ "\n", "A given ICESat-2 product may have over 200 variable + path combinations.\n", "icepyx includes a custom `Variables` module that is \"aware\" of the ATLAS sensor and how the ICESat-2 data products are stored.\n", - "The module can be accessed independently, but is optimally used as a component of a `Query` object (Case 1) or `Read` object (Case 2).\n", + "The module can be accessed independently, and can also be accessed as a component of a `Query` object or `Read` object.\n", "\n", - "This notebook illustrates in detail how the `Variables` module behaves using a `Query` data access example.\n", - "However, module usage is analogous through an icepyx ICESat-2 `Read` object.\n", + "This notebook illustrates in detail how the `Variables` module behaves. We use the module independently and also show how powerful it is directly in the icepyx workflow using a `Query` data access example.\n", + "Module usage using `Query` is analogous through an icepyx ICESat-2 `Read` object.\n", "More detailed example workflows specifically for the [query](https://icepyx.readthedocs.io/en/latest/example_notebooks/IS2_data_access.html) and [read](https://icepyx.readthedocs.io/en/latest/example_notebooks/IS2_data_read-in.html) tools within icepyx are available as separate Jupyter Notebooks.\n", "\n", "Questions? Be sure to check out the FAQs throughout this notebook, indicated as italic headings." @@ -24,11 +26,15 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "user_expressions": [] + }, "source": [ "### _Why do ICESat-2 products need a custom variable manager?_\n", "\n", "_It can be confusing and cumbersome to comb through the 200+ variable and path combinations contained in ICESat-2 data products._\n", + "_An hdf5 file is built like a folder with files in it. Opening an ICESat-2 file can be like opening a new folder with over 200 files in it and manually searching for only ones you want!_\n", + "\n", "_The icepyx `Variables` module makes it easier for users to quickly find and extract the specific variables they would like to work with across multiple beams, keywords, and variables and provides reader-friendly formatting to browse variables._\n", "_A future development goal for `icepyx` includes developing an interactive widget to further improve the user experience._\n", "_For data read-in, additional tools are available to target specific beam characteristics (e.g. strong versus weak beams)._" @@ -38,35 +44,247 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "#### Some technical details about the Variables module\n", - "For those eager to push the limits or who want to know more implementation details...\n", + "Import packages, including icepyx" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import icepyx as ipx\n", + "from pprint import pprint" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "user_expressions": [] + }, + "source": [ + "## Creating or Accessing ICESat-2 Variables" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "user_expressions": [] + }, + "source": [ + "There are three ways to create or access an ICESat-2 Variables object in icepyx:\n", + "1. Access via the `.order_vars` property of a Query object\n", + "2. Access via the `.vars` property of a Read object\n", + "3. Create a stand-alone ICESat-2 Variables object using a local file or a product name\n", "\n", - "The only required input to the `Variables` module is `vartype`.\n", - "`vartype` has two acceptible string values, 'order' and 'file'.\n", - "If you use the module as shown in icepyx examples (namely through a `Read` or `Query` object), then this flag will be passed automatically.\n", - "It simply tells the software how to generate the list of possible variable values - either by pinging NSIDC for a list of available variables (`query`) or from the user-supplied file (`read`)." + "An example of each of these is shown below." ] }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "user_expressions": [] + }, "source": [ - "Import packages, including icepyx" + "### 1. Access `Variables` via the `.order_vars` property of a Query object" ] }, { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "tags": [] + }, "outputs": [], "source": [ - "import icepyx as ipx\n", - "from pprint import pprint" + "region_a = ipx.Query('ATL06',[-55, 68, -48, 71],['2019-02-22','2019-02-28'], \\\n", + " start_time='00:00:00', end_time='23:59:59')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# Accessing Variables\n", + "region_a.order_vars" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# Showing the variable paths\n", + "region_a.order_vars.avail()" ] }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "tags": [], + "user_expressions": [] + }, + "source": [ + "### 2. Access via the `.vars` property of a Read object" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "path_root = '../../../../data/ATL06/'\n", + "# path_root = '/full/path/to/your/data/'\n", + "reader = ipx.Read(path_root)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# Accessing Variables\n", + "reader.vars" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# Showing the variable paths\n", + "# reader.vars.avail()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "user_expressions": [] + }, + "source": [ + "### 3. Create a stand-alone Variables object\n", + "\n", + "You can also generate an independent Variables object. This can be done using either:\n", + "1. The filepath to a file you'd like a variables list for\n", + "2. The product name (and optionally version) of a an ICESat-2 product" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "user_expressions": [] + }, + "source": [ + "Create a variables object from a filepath:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "filepath = '/full/path/to/your/data.h5'\n", + "# v = ipx.Variables(path=path_root + 'processed_ATL03_20191130220138_09930502_006_01.h5')\n", + "v = ipx.Variables(path=filepath)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# v.avail()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "user_expressions": [] + }, + "source": [ + "Create a variables object from a product. The version argument is optional." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "v = ipx.Variables(product='ATL03')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# v.avail()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "v = ipx.Variables(product='ATL03', version='004')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# v.avail()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "user_expressions": [] + }, + "source": [ + "Now that you know how to create or access Variables the remainder of this notebook showcases the functions availble for building and modifying variables lists. Remember, the example shown below uses a Query object, but the same methods are available if you are using a Read object or a Variables object." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "user_expressions": [] + }, "source": [ "## Interacting with ICESat-2 Data Variables\n", "\n", @@ -88,7 +306,9 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "user_expressions": [] + }, "source": [ "Create a query object and log in to Earthdata\n", "\n", @@ -134,7 +354,9 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "user_expressions": [] + }, "source": [ "### ICESat-2 data variables\n", "\n", @@ -157,7 +379,9 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "user_expressions": [] + }, "source": [ "To increase readability, you can use built in functions to show the 200+ variable + path combinations as a dictionary where the keys are variable names and the values are the paths to that variable.\n", "`region_a.order_vars.parse_var_list(region_a.order_vars.avail())` will return a dictionary of variable:paths key:value pairs." @@ -174,7 +398,9 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "user_expressions": [] + }, "source": [ "By passing the boolean `options=True` to the `avail` method, you can obtain lists of unique possible variable inputs (var_list inputs) and path subdirectory inputs (keyword_list and beam_list inputs) for your data product. These can be helpful for building your wanted variable list." ] @@ -188,6 +414,30 @@ "region_a.order_vars.avail(options=True)" ] }, + { + "cell_type": "markdown", + "metadata": { + "user_expressions": [] + }, + "source": [ + "```{admonition} Remember\n", + "You can run these same methods no matter how you created or accessed your ICESat-2 Variables. So the methods in this section could be equivalently be accessed using a Read object, or by directly accessing a file on your computer:\n", + "\n", + "```\n", + "```python\n", + "# Using a Read object\n", + "reader.vars.avail()\n", + "reader.vars.parse_var_list(reader.vars.avail())\n", + "reader.vars.avail(options=True)\n", + "\n", + "# Using a file on your computer\n", + "v = Variables(path='/my/file.h5')\n", + "v.avail()\n", + "v.parse_var_list(v.avail())\n", + "v.avail(options=True)\n", + "```\n" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -228,7 +478,9 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "user_expressions": [] + }, "source": [ "The keywords available for this product are shown in the error message upon entering a blank keyword_list, as seen in the next cell." ] @@ -745,13 +997,64 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "user_expressions": [] + }, "source": [ "#### With a `Read` object\n", "Calling the `load()` method on your `Read` object will automatically look for your wanted variable list and use it.\n", "Please see the [read-in example Jupyter Notebook](https://icepyx.readthedocs.io/en/latest/example_notebooks/IS2_data_read-in.html) for a complete example of this usage.\n" ] }, + { + "cell_type": "markdown", + "metadata": { + "user_expressions": [] + }, + "source": [ + "#### With a local filepath\n", + "\n", + "One of the benefits of using a local filepath in variables is that it allows you to easily inspect the variables that are available in your file. Once you have a variable of interest from the `avail` list, you could read that variable in with another library, such as xarray. The example below demonstrates this assuming an ATL06 ICESat-2 file." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# filepath = '/full/path/to/my/ATL06_file.h5'\n", + "filepath = path_root + 'processed_ATL06_20190225121032_09020203_006_02.h5'\n", + "\n", + "v = ipx.Variables(path=filepath)\n", + "v.avail()\n", + "# Browse paths and decide you need `gt1l/land_ice_segments/`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "import xarray as xr\n", + "\n", + "xr.open_dataset(filepath, group='gt1l/land_ice_segments/', engine='h5netcdf')" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "user_expressions": [] + }, + "source": [ + "You'll notice in this workflow you are limited to viewing data only within a particular group. Icepyx also provides functionality for merging variables within or even across files. See the [read-in example Jupyter Notebook](https://icepyx.readthedocs.io/en/latest/example_notebooks/IS2_data_read-in.html) for more details about these features of icepyx." + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -763,9 +1066,9 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3 (ipykernel)", + "display_name": "icepyx-dev", "language": "python", - "name": "python3" + "name": "icepyx-dev" }, "language_info": { "codemirror_mode": { @@ -777,7 +1080,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.10.12" + "version": "3.11.4" } }, "nbformat": 4, From d13ac331a9f88122834895021afac757fb704b18 Mon Sep 17 00:00:00 2001 From: Rachel Wegener Date: Tue, 17 Oct 2023 14:49:09 +0000 Subject: [PATCH 33/55] hide get_latest_version --- icepyx/core/variables.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/icepyx/core/variables.py b/icepyx/core/variables.py index 20f9c1a2f..c7a313827 100644 --- a/icepyx/core/variables.py +++ b/icepyx/core/variables.py @@ -88,7 +88,7 @@ def __init__( self.product = is2ref._validate_product(product) # Check for valid version string # If version is not specified by the user assume the most recent version - self.version = val.prod_version(self.get_latest_version(), version) + self.version = val.prod_version(self._get_latest_version(), version) else: raise TypeError('Either a filepath or a product need to be given as input arguments.') @@ -659,7 +659,7 @@ def remove(self, all=False, var_list=None, beam_list=None, keyword_list=None): pass # DevNote: This is a modified function from the Query class. - def get_latest_version(self): + def _get_latest_version(self): """ Determine the most recent version available for the given product. From 593b9d19cc86052152c639929dae579991906a41 Mon Sep 17 00:00:00 2001 From: Rachel Wegener Date: Tue, 17 Oct 2023 14:50:36 +0000 Subject: [PATCH 34/55] update api docs --- .../user_guide/documentation/icepyx.rst | 1 + .../user_guide/documentation/variables.rst | 25 +++++++++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 doc/source/user_guide/documentation/variables.rst diff --git a/doc/source/user_guide/documentation/icepyx.rst b/doc/source/user_guide/documentation/icepyx.rst index 56ff7f496..a8a9a6f8e 100644 --- a/doc/source/user_guide/documentation/icepyx.rst +++ b/doc/source/user_guide/documentation/icepyx.rst @@ -23,4 +23,5 @@ Diagrams are updated automatically after a pull request (PR) is approved and bef query read quest + variables components diff --git a/doc/source/user_guide/documentation/variables.rst b/doc/source/user_guide/documentation/variables.rst new file mode 100644 index 000000000..e147bfd64 --- /dev/null +++ b/doc/source/user_guide/documentation/variables.rst @@ -0,0 +1,25 @@ +Variables Class +================= + +.. currentmodule:: icepyx + + +Constructor +----------- + +.. autosummary:: + :toctree: ../../_icepyx/ + + Variables + + +Methods +------- + +.. autosummary:: + :toctree: ../../_icepyx/ + + Variables.avail + Variables.parse_var_list + Variables.append + Variables.remove From 652a815fcb24b6414a20d377c567cdf26119d840 Mon Sep 17 00:00:00 2001 From: Rachel Wegener Date: Fri, 20 Oct 2023 18:33:31 +0000 Subject: [PATCH 35/55] remove variables from components section --- doc/source/user_guide/documentation/components.rst | 8 -------- doc/source/user_guide/documentation/icepyx.rst | 1 - 2 files changed, 9 deletions(-) diff --git a/doc/source/user_guide/documentation/components.rst b/doc/source/user_guide/documentation/components.rst index b4b658385..dea41a970 100644 --- a/doc/source/user_guide/documentation/components.rst +++ b/doc/source/user_guide/documentation/components.rst @@ -67,14 +67,6 @@ validate\_inputs :undoc-members: :show-inheritance: -variables ---------- - -.. automodule:: icepyx.core.variables - :members: - :undoc-members: - :show-inheritance: - visualize --------- diff --git a/doc/source/user_guide/documentation/icepyx.rst b/doc/source/user_guide/documentation/icepyx.rst index a8a9a6f8e..423561f52 100644 --- a/doc/source/user_guide/documentation/icepyx.rst +++ b/doc/source/user_guide/documentation/icepyx.rst @@ -24,4 +24,3 @@ Diagrams are updated automatically after a pull request (PR) is approved and bef read quest variables - components From 120694a11a62a93ffa335758016b46ec247daed4 Mon Sep 17 00:00:00 2001 From: Rachel Wegener Date: Fri, 20 Oct 2023 18:42:33 +0000 Subject: [PATCH 36/55] fix error dropping components --- doc/source/user_guide/documentation/icepyx.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/source/user_guide/documentation/icepyx.rst b/doc/source/user_guide/documentation/icepyx.rst index 423561f52..a8a9a6f8e 100644 --- a/doc/source/user_guide/documentation/icepyx.rst +++ b/doc/source/user_guide/documentation/icepyx.rst @@ -24,3 +24,4 @@ Diagrams are updated automatically after a pull request (PR) is approved and bef read quest variables + components From b4d59d6cd18b44247d22e2cc600a63785ca19824 Mon Sep 17 00:00:00 2001 From: Rachel Wegener Date: Fri, 20 Oct 2023 18:52:51 +0000 Subject: [PATCH 37/55] move latest_version to is2ref --- icepyx/core/is2ref.py | 14 ++++++++++++++ icepyx/core/query.py | 18 +----------------- icepyx/core/variables.py | 18 +----------------- 3 files changed, 16 insertions(+), 34 deletions(-) diff --git a/icepyx/core/is2ref.py b/icepyx/core/is2ref.py index 8abcd89d1..a8510455d 100644 --- a/icepyx/core/is2ref.py +++ b/icepyx/core/is2ref.py @@ -319,3 +319,17 @@ def gt2spot(gt, sc_orient): raise ValueError("Could not compute the spot number.") return np.uint8(spot) + +def latest_version(product): + """ + Determine the most recent version available for the given product. + + Examples + -------- + >>> latest_version('ATL03') + '006' + """ + _about_product = about_product(product) + return max( + [entry["version_id"] for entry in _about_product["feed"]["entry"]] + ) diff --git a/icepyx/core/query.py b/icepyx/core/query.py index 3c451ff48..cac689791 100644 --- a/icepyx/core/query.py +++ b/icepyx/core/query.py @@ -263,7 +263,7 @@ def __init__( super().__init__(spatial_extent, date_range, start_time, end_time, **kwargs) - self._version = val.prod_version(self.latest_version(), version) + self._version = val.prod_version(is2ref.latest_version(self._prod), version) # build list of available CMR parameters if reducing by cycle or RGT # or a list of explicitly named files (full or partial names) @@ -811,22 +811,6 @@ def product_all_info(self): self._about_product = is2ref.about_product(self._prod) pprint.pprint(self._about_product) - def latest_version(self): - """ - Determine the most recent version available for the given product. - - Examples - -------- - >>> reg_a = ipx.Query('ATL06',[-55, 68, -48, 71],['2019-02-20','2019-02-28']) - >>> reg_a.latest_version() - '006' - """ - if not hasattr(self, "_about_product"): - self._about_product = is2ref.about_product(self._prod) - return max( - [entry["version_id"] for entry in self._about_product["feed"]["entry"]] - ) - def show_custom_options(self, dictview=False): """ Display customization/subsetting options available for this product. diff --git a/icepyx/core/variables.py b/icepyx/core/variables.py index c7a313827..fae978c02 100644 --- a/icepyx/core/variables.py +++ b/icepyx/core/variables.py @@ -88,7 +88,7 @@ def __init__( self.product = is2ref._validate_product(product) # Check for valid version string # If version is not specified by the user assume the most recent version - self.version = val.prod_version(self._get_latest_version(), version) + self.version = val.prod_version(is2ref.latest_version(self.product), version) else: raise TypeError('Either a filepath or a product need to be given as input arguments.') @@ -657,19 +657,3 @@ def remove(self, all=False, var_list=None, beam_list=None, keyword_list=None): del self.wanted[vkey] except KeyError: pass - - # DevNote: This is a modified function from the Query class. - def _get_latest_version(self): - """ - Determine the most recent version available for the given product. - - Examples - -------- - >>> reg_a = ipx.Query('ATL06',[-55, 68, -48, 71],['2019-02-20','2019-02-28']) - >>> reg_a.latest_version() - '006' - """ - about_info = is2ref.about_product(self.product) - return max( - [entry["version_id"] for entry in about_info["feed"]["entry"]] - ) From 3f4bfa7274b4ef1c7d686206b22f752f1e9745f9 Mon Sep 17 00:00:00 2001 From: Rachel Wegener Date: Mon, 23 Oct 2023 20:14:09 +0000 Subject: [PATCH 38/55] current status --- icepyx/core/is2ref.py | 24 ++++++++++++++++ icepyx/core/read.py | 33 +++++++++++----------- icepyx/core/variables.py | 59 ++++++++++++++++++++-------------------- 3 files changed, 71 insertions(+), 45 deletions(-) diff --git a/icepyx/core/is2ref.py b/icepyx/core/is2ref.py index a8510455d..b6cc83f60 100644 --- a/icepyx/core/is2ref.py +++ b/icepyx/core/is2ref.py @@ -1,3 +1,4 @@ +import h5py import json import numpy as np import requests @@ -333,3 +334,26 @@ def latest_version(product): return max( [entry["version_id"] for entry in _about_product["feed"]["entry"]] ) + +def extract_product(filepath): + """ + Read the product type from the metadata of the file. Return the product as a string. + """ + with h5py.File(filepath, 'r') as f: + try: + product = f.attrs['short_name'].decode() + product = _validate_product(product) + except KeyError: + raise 'Unable to parse the product name from file metadata' + return product + +def extract_version(filepath): + """ + Read the version from the metadata of the file. Return the version as a string. + """ + with h5py.File(filepath, 'r') as f: + try: + version = f['METADATA']['DatasetIdentification'].attrs['VersionID'].decode() + except KeyError: + raise 'Unable to parse the version from file metadata' + return version diff --git a/icepyx/core/read.py b/icepyx/core/read.py index af70e65d5..253b9430f 100644 --- a/icepyx/core/read.py +++ b/icepyx/core/read.py @@ -379,7 +379,7 @@ def __init__( # Create a dictionary of the products as read from the metadata product_dict = {} for file_ in self._filelist: - product_dict[file_] = self._extract_product(file_) + product_dict[file_] = is2ref.extract_product(file_) # Raise warnings or errors for muliple products or products not matching the user-specified product all_products = list(set(product_dict.values())) @@ -472,21 +472,6 @@ def product(self): # ---------------------------------------------------------------------- # Methods - - @staticmethod - def _extract_product(filepath): - """ - Read the product type from the metadata of the file. Return the product as a string. - """ - with h5py.File(filepath, 'r') as f: - try: - product = f.attrs['short_name'].decode() - product = is2ref._validate_product(product) - # TODO test that this is the proper error - except KeyError: - raise 'Unable to parse the product name from file metadata' - return product - @staticmethod def _check_source_for_pattern(source, filename_pattern): """ @@ -735,6 +720,22 @@ def load(self): # so to get a combined dataset, we need to keep track of spots under the hood, open each group, and then combine them into one xarray where the spots are IDed somehow (or only the strong ones are returned) # this means we need to get/track from each dataset we open some of the metadata, which we include as mandatory variables when constructing the wanted list + # Append the minimum variables needed for icepyx to merge the datasets + # Adjust the nec_varlist for individual products + var_list=[ + "sc_orient", + "atlas_sdp_gps_epoch", + "cycle_number", + "rgt", + "data_start_utc", + "data_end_utc", + ] + + if self.product == "ATL11": + var_list.remove("sc_orient") + + self._read_vars.append(defaults=False, var_list=var_list) + print(self._read_vars) try: groups_list = list_of_dict_vals(self._read_vars.wanted) except AttributeError: diff --git a/icepyx/core/variables.py b/icepyx/core/variables.py index fae978c02..f909c3a96 100644 --- a/icepyx/core/variables.py +++ b/icepyx/core/variables.py @@ -83,6 +83,9 @@ def __init__( # Set the product and version from either the input args or the file if path: self.path = path + self.product = is2ref.extract_product(self.path) + # TODO what is the best way to fill in the version here? --> read it from the file + self.version = is2ref.extract_version(self.path) elif product: # Check for valid product string self.product = is2ref._validate_product(product) @@ -475,42 +478,40 @@ def append(self, defaults=False, var_list=None, beam_list=None, keyword_list=Non self._check_valid_lists(vgrp, allpaths, var_list, beam_list, keyword_list) # add the mandatory variables to the data object - # TODO QUESTION - why is this distinction made? How can we handle it wihtout vartype? - if self._vartype == "order": - nec_varlist = [ - "sc_orient", - "sc_orient_time", - "atlas_sdp_gps_epoch", - "data_start_utc", - "data_end_utc", - "granule_start_utc", - "granule_end_utc", - "start_delta_time", - "end_delta_time", - ] - elif self._vartype == "file": - nec_varlist = [ - "sc_orient", - "atlas_sdp_gps_epoch", - "cycle_number", - "rgt", - "data_start_utc", - "data_end_utc", - ] - - # Adjust the nec_varlist for individual products - if self.product == "ATL11": - nec_varlist.remove("sc_orient") + # TODO QUESTION - why is this distinction made? How can we handle it without vartype? + # if self._vartype == "order": + # nec_varlist = [ + # "sc_orient", + # "sc_orient_time", + # "atlas_sdp_gps_epoch", + # "data_start_utc", + # "data_end_utc", + # "granule_start_utc", + # "granule_end_utc", + # "start_delta_time", + # "end_delta_time", + # ] + # elif self._vartype == "file": + # nec_varlist = [ + # "sc_orient", + # "atlas_sdp_gps_epoch", + # "cycle_number", + # "rgt", + # "data_start_utc", + # "data_end_utc", + # ] try: - self._check_valid_lists(vgrp, allpaths, var_list=nec_varlist) + self._check_valid_lists(vgrp, allpaths) except ValueError: # Assume gridded product since user input lists were previously validated nec_varlist = [] + # TODO there is a lot of logic next that handles the required variables. Does it make + # sense to even have required variables anymore, if we are handling those via query/read? if not hasattr(self, "wanted") or self.wanted == None: - for varid in nec_varlist: - req_vars[varid] = vgrp[varid] + # for varid in nec_varlist: + # req_vars[varid] = vgrp[varid] self.wanted = req_vars # DEVGOAL: add a secondary var list to include uncertainty/error information for lower level data if specific data variables have been specified... From a29e7565489abe46baa059df7fc4451f5fea0bff Mon Sep 17 00:00:00 2001 From: Rachel Wegener Date: Mon, 23 Oct 2023 20:50:59 +0000 Subject: [PATCH 39/55] mvp updated read class --- icepyx/core/read.py | 6 +++--- icepyx/core/variables.py | 45 ++++++++-------------------------------- 2 files changed, 12 insertions(+), 39 deletions(-) diff --git a/icepyx/core/read.py b/icepyx/core/read.py index 253b9430f..b6090a8d4 100644 --- a/icepyx/core/read.py +++ b/icepyx/core/read.py @@ -734,10 +734,10 @@ def load(self): if self.product == "ATL11": var_list.remove("sc_orient") - self._read_vars.append(defaults=False, var_list=var_list) - print(self._read_vars) + self.vars.append(defaults=False, var_list=var_list) + try: - groups_list = list_of_dict_vals(self._read_vars.wanted) + groups_list = list_of_dict_vals(self.vars.wanted) except AttributeError: pass diff --git a/icepyx/core/variables.py b/icepyx/core/variables.py index f909c3a96..59b8c13d4 100644 --- a/icepyx/core/variables.py +++ b/icepyx/core/variables.py @@ -467,7 +467,7 @@ def append(self, defaults=False, var_list=None, beam_list=None, keyword_list=Non and keyword_list == None ), "You must enter parameters to add to a variable subset list. If you do not want to subset by variable, ensure your is2.subsetparams dictionary does not contain the key 'Coverage'." - req_vars = {} + final_vars = {} # if not hasattr(self, 'avail') or self.avail==None: self.get_avail() # vgrp, paths = self.parse_var_list(self.avail) @@ -477,42 +477,15 @@ def append(self, defaults=False, var_list=None, beam_list=None, keyword_list=Non self._check_valid_lists(vgrp, allpaths, var_list, beam_list, keyword_list) - # add the mandatory variables to the data object - # TODO QUESTION - why is this distinction made? How can we handle it without vartype? - # if self._vartype == "order": - # nec_varlist = [ - # "sc_orient", - # "sc_orient_time", - # "atlas_sdp_gps_epoch", - # "data_start_utc", - # "data_end_utc", - # "granule_start_utc", - # "granule_end_utc", - # "start_delta_time", - # "end_delta_time", - # ] - # elif self._vartype == "file": - # nec_varlist = [ - # "sc_orient", - # "atlas_sdp_gps_epoch", - # "cycle_number", - # "rgt", - # "data_start_utc", - # "data_end_utc", - # ] - try: self._check_valid_lists(vgrp, allpaths) except ValueError: # Assume gridded product since user input lists were previously validated nec_varlist = [] - # TODO there is a lot of logic next that handles the required variables. Does it make - # sense to even have required variables anymore, if we are handling those via query/read? + # Instantiate self.wanted to an empty dictionary if it doesn't exist if not hasattr(self, "wanted") or self.wanted == None: - # for varid in nec_varlist: - # req_vars[varid] = vgrp[varid] - self.wanted = req_vars + self.wanted = {} # DEVGOAL: add a secondary var list to include uncertainty/error information for lower level data if specific data variables have been specified... @@ -521,21 +494,21 @@ def append(self, defaults=False, var_list=None, beam_list=None, keyword_list=Non # Case only variables (but not keywords or beams) are specified if beam_list == None and keyword_list == None: - req_vars.update(self._iter_vars(sum_varlist, req_vars, vgrp)) + final_vars.update(self._iter_vars(sum_varlist, final_vars, vgrp)) # Case a beam and/or keyword list is specified (with or without variables) else: - req_vars.update( - self._iter_paths(sum_varlist, req_vars, vgrp, beam_list, keyword_list) + final_vars.update( + self._iter_paths(sum_varlist, final_vars, vgrp, beam_list, keyword_list) ) # update the data object variables - for vkey in req_vars.keys(): + for vkey in final_vars.keys(): # add all matching keys and paths for new variables if vkey not in self.wanted.keys(): - self.wanted[vkey] = req_vars[vkey] + self.wanted[vkey] = final_vars[vkey] else: - for vpath in req_vars[vkey]: + for vpath in final_vars[vkey]: if vpath not in self.wanted[vkey]: self.wanted[vkey].append(vpath) From 4f8e95a1e31f853bb0deaa79b555a33921da4918 Mon Sep 17 00:00:00 2001 From: Rachel Wegener Date: Thu, 26 Oct 2023 15:28:32 +0000 Subject: [PATCH 40/55] add error message if no vars.wanted --- icepyx/core/read.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/icepyx/core/read.py b/icepyx/core/read.py index 3d4356826..4f9439f2d 100644 --- a/icepyx/core/read.py +++ b/icepyx/core/read.py @@ -754,8 +754,14 @@ def load(self): # so to get a combined dataset, we need to keep track of spots under the hood, open each group, and then combine them into one xarray where the spots are IDed somehow (or only the strong ones are returned) # this means we need to get/track from each dataset we open some of the metadata, which we include as mandatory variables when constructing the wanted list + if not self.vars.wanted: + raise AttributeError( + 'No variables listed in self.vars.wanted. Please use the Variables class ' + 'via self.vars to search for desired variables to read and self.vars.append(...) ' + 'to add variables to the wanted variables list.' + ) + # Append the minimum variables needed for icepyx to merge the datasets - # Adjust the nec_varlist for individual products var_list=[ "sc_orient", "atlas_sdp_gps_epoch", @@ -765,6 +771,7 @@ def load(self): "data_end_utc", ] + # Adjust the nec_varlist for individual products if self.product == "ATL11": var_list.remove("sc_orient") From d0838c8c9528a23ceb3dee7900c97ee0887ee4f0 Mon Sep 17 00:00:00 2001 From: Rachel Wegener Date: Thu, 26 Oct 2023 16:26:51 +0000 Subject: [PATCH 41/55] update query class to append required vars --- icepyx/core/query.py | 21 +++++++++++++++++++++ icepyx/core/read.py | 2 ++ icepyx/core/variables.py | 5 ----- 3 files changed, 23 insertions(+), 5 deletions(-) diff --git a/icepyx/core/query.py b/icepyx/core/query.py index d05c014f4..a6825d37f 100644 --- a/icepyx/core/query.py +++ b/icepyx/core/query.py @@ -646,6 +646,27 @@ def subsetparams(self, **kwargs): if self._subsetparams == None and not kwargs: return {} else: + # If the user has supplied a subset list of variables, append the + # icepyx required variables to the Coverage dict + if 'Coverage' in kwargs.keys(): + var_list = [ + "sc_orient", + "sc_orient_time", + "atlas_sdp_gps_epoch", + "cycle_number", + "rgt", + "data_start_utc", + "data_end_utc", + "granule_start_utc", + "granule_end_utc", + "start_delta_time", + "end_delta_time", + ] + self.order_vars.append(defaults=False, var_list=var_list) + for var, path in self.order_vars.wanted.items(): + if var not in kwargs['Coverage'].keys(): + kwargs['Coverage'][var] = path + if self._subsetparams == None: self._subsetparams = apifmt.Parameters("subset") if self._spatial._geom_file is not None: diff --git a/icepyx/core/read.py b/icepyx/core/read.py index 4f9439f2d..4bb70c9d6 100644 --- a/icepyx/core/read.py +++ b/icepyx/core/read.py @@ -775,6 +775,8 @@ def load(self): if self.product == "ATL11": var_list.remove("sc_orient") + # Note: This fails if we are reading a file that doesn't have the var_list + # variables in it. self.vars.append(defaults=False, var_list=var_list) try: diff --git a/icepyx/core/variables.py b/icepyx/core/variables.py index 59b8c13d4..e86a13853 100644 --- a/icepyx/core/variables.py +++ b/icepyx/core/variables.py @@ -469,12 +469,7 @@ def append(self, defaults=False, var_list=None, beam_list=None, keyword_list=Non final_vars = {} - # if not hasattr(self, 'avail') or self.avail==None: self.get_avail() - # vgrp, paths = self.parse_var_list(self.avail) - # allpaths = [] - # [allpaths.extend(np.unique(np.array(paths[p]))) for p in range(len(paths))] vgrp, allpaths = self.avail(options=True, internal=True) - self._check_valid_lists(vgrp, allpaths, var_list, beam_list, keyword_list) try: From 8127dbf9ae9790d93b95ff8e55603cf710bb6cbc Mon Sep 17 00:00:00 2001 From: Rachel Wegener Date: Fri, 27 Oct 2023 14:30:08 +0000 Subject: [PATCH 42/55] remove local filepaths --- doc/source/example_notebooks/IS2_data_variables.ipynb | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/doc/source/example_notebooks/IS2_data_variables.ipynb b/doc/source/example_notebooks/IS2_data_variables.ipynb index 3835bf9af..78a250789 100644 --- a/doc/source/example_notebooks/IS2_data_variables.ipynb +++ b/doc/source/example_notebooks/IS2_data_variables.ipynb @@ -143,8 +143,7 @@ }, "outputs": [], "source": [ - "path_root = '../../../../data/ATL06/'\n", - "# path_root = '/full/path/to/your/data/'\n", + "path_root = '/full/path/to/your/data/'\n", "reader = ipx.Read(path_root)" ] }, @@ -203,7 +202,6 @@ "outputs": [], "source": [ "filepath = '/full/path/to/your/data.h5'\n", - "# v = ipx.Variables(path=path_root + 'processed_ATL03_20191130220138_09930502_006_01.h5')\n", "v = ipx.Variables(path=filepath)" ] }, @@ -1025,9 +1023,7 @@ }, "outputs": [], "source": [ - "# filepath = '/full/path/to/my/ATL06_file.h5'\n", - "filepath = path_root + 'processed_ATL06_20190225121032_09020203_006_02.h5'\n", - "\n", + "filepath = '/full/path/to/my/ATL06_file.h5'\n", "v = ipx.Variables(path=filepath)\n", "v.avail()\n", "# Browse paths and decide you need `gt1l/land_ice_segments/`" From b3341c12bad5846818256b67712a7c7677f7188a Mon Sep 17 00:00:00 2001 From: Rachel Wegener Date: Fri, 27 Oct 2023 14:34:35 +0000 Subject: [PATCH 43/55] clean extraneous comments --- icepyx/core/variables.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/icepyx/core/variables.py b/icepyx/core/variables.py index e86a13853..f5f0017df 100644 --- a/icepyx/core/variables.py +++ b/icepyx/core/variables.py @@ -31,7 +31,7 @@ class Variables(EarthdataAuthMixin): Parameters ---------- vartype : string - This argument is depreciated. The vartype will be inferred from data_source. + This argument is deprecated. The vartype will be inferred from data_source. One of ['order', 'file'] to indicate the source of the input variables. This field will be auto-populated when a variable object is created as an attribute of a query object. @@ -84,7 +84,6 @@ def __init__( if path: self.path = path self.product = is2ref.extract_product(self.path) - # TODO what is the best way to fill in the version here? --> read it from the file self.version = is2ref.extract_version(self.path) elif product: # Check for valid product string From 8ff1e70656d42f0a842ca85cfa119e819e0d236e Mon Sep 17 00:00:00 2001 From: Rachel Wegener <35503632+rwegener2@users.noreply.github.com> Date: Tue, 31 Oct 2023 09:29:07 -0400 Subject: [PATCH 44/55] Update icepyx/core/query.py Co-authored-by: Jessica Scheick --- icepyx/core/query.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/icepyx/core/query.py b/icepyx/core/query.py index a6825d37f..7be5ba1d0 100644 --- a/icepyx/core/query.py +++ b/icepyx/core/query.py @@ -711,8 +711,8 @@ def order_vars(self): self._order_vars = Variables( product=self.product, version = self._version, - auth = self.auth, avail=self._cust_options["variables"], + auth = self.auth, ) else: self._order_vars = Variables( From 32175656a3ac4b9186a169b4a07b630423b16814 Mon Sep 17 00:00:00 2001 From: Rachel Wegener Date: Tue, 31 Oct 2023 13:58:16 +0000 Subject: [PATCH 45/55] remove redundant lines --- icepyx/core/query.py | 1 - icepyx/core/variables.py | 6 ------ 2 files changed, 7 deletions(-) diff --git a/icepyx/core/query.py b/icepyx/core/query.py index 7be5ba1d0..10ac9c132 100644 --- a/icepyx/core/query.py +++ b/icepyx/core/query.py @@ -662,7 +662,6 @@ def subsetparams(self, **kwargs): "start_delta_time", "end_delta_time", ] - self.order_vars.append(defaults=False, var_list=var_list) for var, path in self.order_vars.wanted.items(): if var not in kwargs['Coverage'].keys(): kwargs['Coverage'][var] = path diff --git a/icepyx/core/variables.py b/icepyx/core/variables.py index f5f0017df..efaf1ab1e 100644 --- a/icepyx/core/variables.py +++ b/icepyx/core/variables.py @@ -471,12 +471,6 @@ def append(self, defaults=False, var_list=None, beam_list=None, keyword_list=Non vgrp, allpaths = self.avail(options=True, internal=True) self._check_valid_lists(vgrp, allpaths, var_list, beam_list, keyword_list) - try: - self._check_valid_lists(vgrp, allpaths) - except ValueError: - # Assume gridded product since user input lists were previously validated - nec_varlist = [] - # Instantiate self.wanted to an empty dictionary if it doesn't exist if not hasattr(self, "wanted") or self.wanted == None: self.wanted = {} From defc76f86ece68c77049e34f0420b739c56d98a8 Mon Sep 17 00:00:00 2001 From: Rachel Wegener Date: Tue, 31 Oct 2023 14:24:28 +0000 Subject: [PATCH 46/55] respond to review --- icepyx/core/read.py | 17 ----------------- icepyx/core/variables.py | 27 ++++++++++++++++++++++----- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/icepyx/core/read.py b/icepyx/core/read.py index 4bb70c9d6..698a3b208 100644 --- a/icepyx/core/read.py +++ b/icepyx/core/read.py @@ -289,9 +289,6 @@ class Read: glob_kwargs : dict, default {} Additional arguments to be passed into the [glob.glob()](https://docs.python.org/3/library/glob.html#glob.glob)function - glob_kwargs : dict, default {} - Additional arguments to be passed into the [glob.glob()](https://docs.python.org/3/library/glob.html#glob.glob)function - out_obj_type : object, default xarray.Dataset The desired format for the data to be read in. Currently, only xarray.Dataset objects (default) are available. @@ -475,20 +472,6 @@ def product(self): """ return self._product - @property - def filelist(self): - """ - Return the list of files represented by this Read object. - """ - return self._filelist - - @property - def product(self): - """ - Return the product associated with the Read object. - """ - return self._product - # ---------------------------------------------------------------------- # Methods @staticmethod diff --git a/icepyx/core/variables.py b/icepyx/core/variables.py index efaf1ab1e..94645ca94 100644 --- a/icepyx/core/variables.py +++ b/icepyx/core/variables.py @@ -82,15 +82,15 @@ def __init__( # Set the product and version from either the input args or the file if path: - self.path = path - self.product = is2ref.extract_product(self.path) - self.version = is2ref.extract_version(self.path) + self._path = path + self._product = is2ref.extract_product(self._path) + self._version = is2ref.extract_version(self._path) elif product: # Check for valid product string - self.product = is2ref._validate_product(product) + self._product = is2ref._validate_product(product) # Check for valid version string # If version is not specified by the user assume the most recent version - self.version = val.prod_version(is2ref.latest_version(self.product), version) + self._version = val.prod_version(is2ref.latest_version(self._product), version) else: raise TypeError('Either a filepath or a product need to be given as input arguments.') @@ -101,6 +101,23 @@ def __init__( self.wanted = wanted # DevGoal: put some more/robust checks here to assess validity of inputs + + @property + def path(self): + if self._path: + path = self._path + else: + path = None + return path + + @property + def product(self): + return self._product + + @property + def version(self): + return self._version + def avail(self, options=False, internal=False): """ From c6dffe8763006b8aff5b1802297d660d82468ce2 Mon Sep 17 00:00:00 2001 From: Rachel Wegener Date: Thu, 2 Nov 2023 18:14:31 +0000 Subject: [PATCH 47/55] remove duplicate extract_product method --- icepyx/core/read.py | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/icepyx/core/read.py b/icepyx/core/read.py index 698a3b208..1a653f40f 100644 --- a/icepyx/core/read.py +++ b/icepyx/core/read.py @@ -379,7 +379,7 @@ def __init__( # Create a dictionary of the products as read from the metadata product_dict = {} for file_ in self._filelist: - product_dict[file_] = self._extract_product(file_) + product_dict[file_] = is2ref.extract_product(file_) # Raise warnings or errors for multiple products or products not matching the user-specified product all_products = list(set(product_dict.values())) @@ -474,21 +474,6 @@ def product(self): # ---------------------------------------------------------------------- # Methods - @staticmethod - def _extract_product(filepath): - """ - Read the product type from the metadata of the file. Return the product as a string. - """ - with h5py.File(filepath, "r") as f: - try: - product = f.attrs["short_name"].decode() - product = is2ref._validate_product(product) - except KeyError: - raise AttributeError( - f"Unable to extract the product name from file metadata." - ) - return product - @staticmethod def _check_source_for_pattern(source, filename_pattern): """ From 24751e90a042f3d5c8f43e024bdfa6c338f80de3 Mon Sep 17 00:00:00 2001 From: Rachel Wegener Date: Thu, 2 Nov 2023 18:22:03 +0000 Subject: [PATCH 48/55] add check for ATL14 data in extract product and version --- icepyx/core/is2ref.py | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/icepyx/core/is2ref.py b/icepyx/core/is2ref.py index 336ef04c8..a90c8fafa 100644 --- a/icepyx/core/is2ref.py +++ b/icepyx/core/is2ref.py @@ -111,7 +111,11 @@ def _get_custom_options(session, product, version): # reformatting formats = [Format.attrib for Format in root.iter("Format")] format_vals = [formats[i]["value"] for i in range(len(formats))] - format_vals.remove("") + try: + format_vals.remove("") + except KeyError: + # ATL23 does not have an empty value + pass cust_options.update({"fileformats": format_vals}) # reprojection only applicable on ICESat-2 L3B products. @@ -346,7 +350,13 @@ def extract_product(filepath): """ with h5py.File(filepath, 'r') as f: try: - product = f.attrs['short_name'].decode() + product = f.attrs['short_name'] + if isinstance(product, bytes): + # For most products the short name is stored in a bytes string + product = product.decode() + elif isinstance(product, np.ndarray): + # ATL14 saves the short_name as an array ['ATL14'] + product = product[0] product = _validate_product(product) except KeyError: raise 'Unable to parse the product name from file metadata' @@ -358,7 +368,10 @@ def extract_version(filepath): """ with h5py.File(filepath, 'r') as f: try: - version = f['METADATA']['DatasetIdentification'].attrs['VersionID'].decode() + version = f['METADATA']['DatasetIdentification'].attrs['VersionID'] + if isinstance(version, np.ndarray): + # ATL14 stores the version as an array ['00x'] + version = version[0] except KeyError: raise 'Unable to parse the version from file metadata' return version From b552808a02389817ae735286b07f471894cf63ab Mon Sep 17 00:00:00 2001 From: Rachel Wegener Date: Thu, 2 Nov 2023 18:24:14 +0000 Subject: [PATCH 49/55] add reference to latest_version --- icepyx/core/query.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/icepyx/core/query.py b/icepyx/core/query.py index 10ac9c132..9c301203c 100644 --- a/icepyx/core/query.py +++ b/icepyx/core/query.py @@ -816,6 +816,14 @@ def product_summary_info(self): ] for key in summ_keys: print(key, ": ", self._about_product["feed"]["entry"][-1][key]) + + def latest_version(self): + """ + A reference function to is2ref.lates_version. + + Determine the most recent version available for the given product. + """ + return is2ref.latest_version(self.product) def product_all_info(self): """ From 37bcc96aa5943ab39cea3618e59b04bc5ff808e3 Mon Sep 17 00:00:00 2001 From: Rachel Wegener Date: Thu, 2 Nov 2023 18:54:20 +0000 Subject: [PATCH 50/55] skip appending required vars for ATL14, 15, and 23 --- icepyx/core/read.py | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/icepyx/core/read.py b/icepyx/core/read.py index 1a653f40f..842eab51f 100644 --- a/icepyx/core/read.py +++ b/icepyx/core/read.py @@ -730,22 +730,22 @@ def load(self): ) # Append the minimum variables needed for icepyx to merge the datasets - var_list=[ - "sc_orient", - "atlas_sdp_gps_epoch", - "cycle_number", - "rgt", - "data_start_utc", - "data_end_utc", - ] - - # Adjust the nec_varlist for individual products - if self.product == "ATL11": - var_list.remove("sc_orient") - - # Note: This fails if we are reading a file that doesn't have the var_list - # variables in it. - self.vars.append(defaults=False, var_list=var_list) + # Skip products which do not contain required variables + if self.product not in ['ATL14', 'ATL15', 'ATL23']: + var_list=[ + "sc_orient", + "atlas_sdp_gps_epoch", + "cycle_number", + "rgt", + "data_start_utc", + "data_end_utc", + ] + + # Adjust the nec_varlist for individual products + if self.product == "ATL11": + var_list.remove("sc_orient") + + self.vars.append(defaults=False, var_list=var_list) try: groups_list = list_of_dict_vals(self.vars.wanted) From afbec9802c639d80f9fd54f38a7acac0acda170b Mon Sep 17 00:00:00 2001 From: Rachel Wegener Date: Thu, 2 Nov 2023 19:25:11 +0000 Subject: [PATCH 51/55] clean logic for appending Coverage variables --- icepyx/core/query.py | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/icepyx/core/query.py b/icepyx/core/query.py index 9c301203c..e655fbce7 100644 --- a/icepyx/core/query.py +++ b/icepyx/core/query.py @@ -650,21 +650,22 @@ def subsetparams(self, **kwargs): # icepyx required variables to the Coverage dict if 'Coverage' in kwargs.keys(): var_list = [ - "sc_orient", - "sc_orient_time", - "atlas_sdp_gps_epoch", - "cycle_number", - "rgt", - "data_start_utc", - "data_end_utc", - "granule_start_utc", - "granule_end_utc", - "start_delta_time", - "end_delta_time", + "orbit_info/sc_orient", + "orbit_info/sc_orient_time", + "ancillary_data/atlas_sdp_gps_epoch", + "orbit_info/cycle_number", + "orbit_info/rgt", + "ancillary_data/data_start_utc", + "ancillary_data/data_end_utc", + "ancillary_data/granule_start_utc", + "ancillary_data/granule_end_utc", + "ancillary_data/start_delta_time", + "ancillary_data/end_delta_time", ] - for var, path in self.order_vars.wanted.items(): + # Add any variables from var_list to Coverage that are not already included + for var in var_list: if var not in kwargs['Coverage'].keys(): - kwargs['Coverage'][var] = path + kwargs['Coverage'][var.split('/')[-1]] = [var] if self._subsetparams == None: self._subsetparams = apifmt.Parameters("subset") From 9c4005a07fdbb5788b394762a824b6567a7f3dc0 Mon Sep 17 00:00:00 2001 From: Rachel Wegener <35503632+rwegener2@users.noreply.github.com> Date: Fri, 3 Nov 2023 14:15:00 -0400 Subject: [PATCH 52/55] spelling Co-authored-by: Jessica Scheick --- icepyx/core/query.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/icepyx/core/query.py b/icepyx/core/query.py index e655fbce7..7d7448c62 100644 --- a/icepyx/core/query.py +++ b/icepyx/core/query.py @@ -820,7 +820,7 @@ def product_summary_info(self): def latest_version(self): """ - A reference function to is2ref.lates_version. + A reference function to is2ref.latest_version. Determine the most recent version available for the given product. """ From 607ce65c0467f55a724d964ae29ca2260f7f9a08 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Mon, 6 Nov 2023 17:19:18 +0000 Subject: [PATCH 53/55] GitHub action UML generation auto-update --- .../documentation/classes_dev_uml.svg | 497 +++++++++--------- .../documentation/classes_user_uml.svg | 33 +- .../documentation/packages_user_uml.svg | 60 ++- 3 files changed, 299 insertions(+), 291 deletions(-) diff --git a/doc/source/user_guide/documentation/classes_dev_uml.svg b/doc/source/user_guide/documentation/classes_dev_uml.svg index 0cd08c9e9..765e0d531 100644 --- a/doc/source/user_guide/documentation/classes_dev_uml.svg +++ b/doc/source/user_guide/documentation/classes_dev_uml.svg @@ -4,328 +4,329 @@ - - + + classes_dev_uml - + icepyx.core.auth.AuthenticationError - -AuthenticationError - - - + +AuthenticationError + + + icepyx.core.exceptions.DeprecationError - -DeprecationError - - - + +DeprecationError + + + icepyx.core.auth.EarthdataAuthMixin - -EarthdataAuthMixin - -_auth : Auth, NoneType -_s3_initial_ts : NoneType, datetime -_s3login_credentials : NoneType, dict -_session : NoneType -auth -s3login_credentials -session - -__init__(auth) -__str__() -earthdata_login(uid, email, s3token): None + +EarthdataAuthMixin + +_auth : NoneType +_s3_initial_ts : NoneType, datetime +_s3login_credentials : NoneType +_session : NoneType +auth +s3login_credentials +session + +__init__(auth) +__str__() +earthdata_login(uid, email, s3token): None icepyx.core.query.GenQuery - -GenQuery - -_spatial -_temporal -dates -end_time -spatial -spatial_extent -start_time -temporal - -__init__(spatial_extent, date_range, start_time, end_time) -__str__() + +GenQuery + +_spatial +_temporal +dates +end_time +spatial +spatial_extent +start_time +temporal + +__init__(spatial_extent, date_range, start_time, end_time) +__str__() icepyx.core.granules.Granules - -Granules - -avail : list -orderIDs : list - -__init__ -() -download(verbose, path, session, restart) -get_avail(CMRparams, reqparams, cloud) -place_order(CMRparams, reqparams, subsetparams, verbose, subset, session, geom_filepath) + +Granules + +avail : list +orderIDs : list + +__init__ +() +download(verbose, path, session, restart) +get_avail(CMRparams, reqparams, cloud) +place_order(CMRparams, reqparams, subsetparams, verbose, subset, session, geom_filepath) icepyx.core.query.Query - -Query - -CMRparams -_CMRparams -_about_product -_cust_options : dict -_cycles : list -_file_vars -_granules -_order_vars -_prod : NoneType, str -_readable_granule_name : list -_reqparams -_source : str -_subsetparams : NoneType -_tracks : list -_version -cycles -dataset -file_vars -granules -order_vars -product -product_version -reqparams -tracks - -__init__(product, spatial_extent, date_range, start_time, end_time, version, cycles, tracks, files, auth) -__str__() -avail_granules(ids, cycles, tracks, cloud) -download_granules(path, verbose, subset, restart) -latest_version() -order_granules(verbose, subset, email) -product_all_info() -product_summary_info() -show_custom_options(dictview) -subsetparams() -visualize_elevation() -visualize_spatial_extent() + +Query + +CMRparams +_CMRparams +_about_product +_cust_options : dict +_cycles : list +_file_vars +_granules +_order_vars +_prod : NoneType, str +_readable_granule_name : list +_reqparams +_source : str +_subsetparams : NoneType +_tracks : list +_version +cycles +dataset +file_vars +granules +order_vars +product +product_version +reqparams +tracks + +__init__(product, spatial_extent, date_range, start_time, end_time, version, cycles, tracks, files, auth) +__str__() +avail_granules(ids, cycles, tracks, cloud) +download_granules(path, verbose, subset, restart) +latest_version() +order_granules(verbose, subset, email) +product_all_info() +product_summary_info() +show_custom_options(dictview) +subsetparams() +visualize_elevation() +visualize_spatial_extent() icepyx.core.granules.Granules->icepyx.core.query.Query - - -_granules + + +_granules icepyx.core.granules.Granules->icepyx.core.query.Query - - -_granules + + +_granules icepyx.core.icesat2data.Icesat2Data - -Icesat2Data - - -__init__() + +Icesat2Data + + +__init__() icepyx.core.exceptions.NsidcQueryError - -NsidcQueryError - -errmsg -msgtxt : str - -__init__(errmsg, msgtxt) -__str__() + +NsidcQueryError + +errmsg +msgtxt : str + +__init__(errmsg, msgtxt) +__str__() icepyx.core.exceptions.QueryError - -QueryError - - - + +QueryError + + + icepyx.core.exceptions.NsidcQueryError->icepyx.core.exceptions.QueryError - - + + icepyx.core.APIformatting.Parameters - -Parameters - -_fmted_keys : NoneType, dict -_poss_keys : dict -_reqtype : NoneType, str -fmted_keys -partype -poss_keys - -__init__(partype, values, reqtype) -_check_valid_keys() -_get_possible_keys() -build_params() -check_req_values() -check_values() + +Parameters + +_fmted_keys : NoneType, dict +_poss_keys : dict +_reqtype : NoneType, str +fmted_keys +partype +poss_keys + +__init__(partype, values, reqtype) +_check_valid_keys() +_get_possible_keys() +build_params() +check_req_values() +check_values() icepyx.core.APIformatting.Parameters->icepyx.core.query.Query - - -_CMRparams + + +_CMRparams icepyx.core.APIformatting.Parameters->icepyx.core.query.Query - - -_reqparams + + +_reqparams icepyx.core.APIformatting.Parameters->icepyx.core.query.Query - - -_subsetparams + + +_subsetparams icepyx.core.APIformatting.Parameters->icepyx.core.query.Query - - -_subsetparams + + +_subsetparams icepyx.core.query.Query->icepyx.core.auth.EarthdataAuthMixin - - + + icepyx.core.query.Query->icepyx.core.query.GenQuery - - + + icepyx.core.read.Read - -Read - -_filelist : NoneType, list -_out_obj : Dataset -_product : NoneType, str -_read_vars -filelist -product -vars - -__init__(data_source, product, filename_pattern, catalog, glob_kwargs, out_obj_type) -_add_vars_to_ds(is2ds, ds, grp_path, wanted_groups_tiered, wanted_dict) -_build_dataset_template(file) -_build_single_file_dataset(file, groups_list) -_check_source_for_pattern(source, filename_pattern) -_combine_nested_vars(is2ds, ds, grp_path, wanted_dict) -_extract_product(filepath) -_read_single_grp(file, grp_path) -load() + +Read + +_filelist : NoneType, list +_out_obj : Dataset +_product : NoneType, str +_read_vars +filelist +product +vars + +__init__(data_source, product, filename_pattern, catalog, glob_kwargs, out_obj_type) +_add_vars_to_ds(is2ds, ds, grp_path, wanted_groups_tiered, wanted_dict) +_build_dataset_template(file) +_build_single_file_dataset(file, groups_list) +_check_source_for_pattern(source, filename_pattern) +_combine_nested_vars(is2ds, ds, grp_path, wanted_dict) +_read_single_grp(file, grp_path) +load() icepyx.core.spatial.Spatial - -Spatial - -_ext_type : str -_gdf_spat : GeoDataFrame -_geom_file : NoneType -_spatial_ext -_xdateln -extent -extent_as_gdf -extent_file -extent_type - -__init__(spatial_extent) -__str__() -fmt_for_CMR() -fmt_for_EGI() + +Spatial + +_ext_type : str +_gdf_spat : GeoDataFrame +_geom_file : NoneType +_spatial_ext +_xdateln +extent +extent_as_gdf +extent_file +extent_type + +__init__(spatial_extent) +__str__() +fmt_for_CMR() +fmt_for_EGI() icepyx.core.spatial.Spatial->icepyx.core.query.GenQuery - - -_spatial + + +_spatial icepyx.core.spatial.Spatial->icepyx.core.query.GenQuery - - -_spatial + + +_spatial icepyx.core.temporal.Temporal - -Temporal - -_end : datetime -_start : datetime -end -start - -__init__(date_range, start_time, end_time) -__str__() + +Temporal + +_end : datetime +_start : datetime +end +start + +__init__(date_range, start_time, end_time) +__str__() icepyx.core.temporal.Temporal->icepyx.core.query.GenQuery - - -_temporal + + +_temporal icepyx.core.variables.Variables - -Variables - -_avail : NoneType, list -_vartype -_version : NoneType -path : NoneType -product : NoneType -wanted : NoneType, dict + +Variables + +_avail : NoneType, list +_path : NoneType +_product : NoneType, str +_version +path +product +version +wanted : NoneType, dict -__init__(vartype, avail, wanted, product, version, path, auth) +__init__(vartype, path, product, version, avail, wanted, auth) _check_valid_lists(vgrp, allpaths, var_list, beam_list, keyword_list) _get_combined_list(beam_list, keyword_list) _get_sum_varlist(var_list, all_vars, defaults) @@ -339,57 +340,57 @@ icepyx.core.variables.Variables->icepyx.core.auth.EarthdataAuthMixin - - + + icepyx.core.variables.Variables->icepyx.core.query.Query - - -_order_vars + + +_order_vars icepyx.core.variables.Variables->icepyx.core.query.Query - - -_order_vars + + +_order_vars icepyx.core.variables.Variables->icepyx.core.query.Query - - -_file_vars + + +_file_vars icepyx.core.variables.Variables->icepyx.core.read.Read - - -_read_vars + + +_read_vars icepyx.core.visualization.Visualize - -Visualize - -bbox : list -cycles : NoneType -date_range : NoneType -product : NoneType, str -tracks : NoneType - -__init__(query_obj, product, spatial_extent, date_range, cycles, tracks) -generate_OA_parameters(): list -grid_bbox(binsize): list -make_request(base_url, payload) -parallel_request_OA(): da.array -query_icesat2_filelist(): tuple -request_OA_data(paras): da.array -viz_elevation(): (hv.DynamicMap, hv.Layout) + +Visualize + +bbox : list +cycles : NoneType +date_range : NoneType +product : NoneType, str +tracks : NoneType + +__init__(query_obj, product, spatial_extent, date_range, cycles, tracks) +generate_OA_parameters(): list +grid_bbox(binsize): list +make_request(base_url, payload) +parallel_request_OA(): da.array +query_icesat2_filelist(): tuple +request_OA_data(paras): da.array +viz_elevation(): (hv.DynamicMap, hv.Layout) diff --git a/doc/source/user_guide/documentation/classes_user_uml.svg b/doc/source/user_guide/documentation/classes_user_uml.svg index a9c116469..59b8e8e6f 100644 --- a/doc/source/user_guide/documentation/classes_user_uml.svg +++ b/doc/source/user_guide/documentation/classes_user_uml.svg @@ -259,49 +259,50 @@ icepyx.core.variables.Variables - -Variables - -path : NoneType -product : NoneType -wanted : NoneType, dict - -append(defaults, var_list, beam_list, keyword_list) -avail(options, internal) -parse_var_list(varlist, tiered, tiered_vars) -remove(all, var_list, beam_list, keyword_list) + +Variables + +path +product +version +wanted : NoneType, dict + +append(defaults, var_list, beam_list, keyword_list) +avail(options, internal) +parse_var_list(varlist, tiered, tiered_vars) +remove(all, var_list, beam_list, keyword_list) icepyx.core.variables.Variables->icepyx.core.auth.EarthdataAuthMixin - + icepyx.core.variables.Variables->icepyx.core.query.Query - + _order_vars icepyx.core.variables.Variables->icepyx.core.query.Query - + _order_vars icepyx.core.variables.Variables->icepyx.core.query.Query - + _file_vars icepyx.core.variables.Variables->icepyx.core.read.Read - + _read_vars diff --git a/doc/source/user_guide/documentation/packages_user_uml.svg b/doc/source/user_guide/documentation/packages_user_uml.svg index 44a041c77..8d8cf0dc9 100644 --- a/doc/source/user_guide/documentation/packages_user_uml.svg +++ b/doc/source/user_guide/documentation/packages_user_uml.svg @@ -4,11 +4,11 @@ - + packages_user_uml - + icepyx.core @@ -24,14 +24,14 @@ icepyx.core.auth - -icepyx.core.auth + +icepyx.core.auth icepyx.core.exceptions - -icepyx.core.exceptions + +icepyx.core.exceptions @@ -42,14 +42,14 @@ icepyx.core.icesat2data - -icepyx.core.icesat2data + +icepyx.core.icesat2data icepyx.core.is2ref - -icepyx.core.is2ref + +icepyx.core.is2ref @@ -60,8 +60,8 @@ icepyx.core.query->icepyx.core.auth - - + + @@ -96,44 +96,50 @@ icepyx.core.read - -icepyx.core.read + +icepyx.core.read icepyx.core.read->icepyx.core.exceptions - - + + icepyx.core.read->icepyx.core.variables - - + + icepyx.core.spatial - -icepyx.core.spatial + +icepyx.core.spatial icepyx.core.temporal - -icepyx.core.temporal + +icepyx.core.temporal icepyx.core.validate_inputs - -icepyx.core.validate_inputs + +icepyx.core.validate_inputs icepyx.core.variables->icepyx.core.auth - - + + + + + +icepyx.core.variables->icepyx.core.exceptions + + From 170bd98a4f10b967a8bc0a218666f61e146d3f46 Mon Sep 17 00:00:00 2001 From: Rachel Wegener <35503632+rwegener2@users.noreply.github.com> Date: Mon, 6 Nov 2023 15:12:48 -0500 Subject: [PATCH 54/55] Add example Co-authored-by: Jessica Scheick --- icepyx/core/query.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/icepyx/core/query.py b/icepyx/core/query.py index 7d7448c62..ac28b1cee 100644 --- a/icepyx/core/query.py +++ b/icepyx/core/query.py @@ -823,6 +823,12 @@ def latest_version(self): A reference function to is2ref.latest_version. Determine the most recent version available for the given product. + + Examples + -------- + >>> reg_a = ipx.Query('ATL06',[-55, 68, -48, 71],['2019-02-20','2019-02-28']) + >>> reg_a.latest_version() + '006' """ return is2ref.latest_version(self.product) From ef26223e8577b7610b91cf9e602316762590b556 Mon Sep 17 00:00:00 2001 From: Jessica Scheick Date: Mon, 6 Nov 2023 15:56:57 -0500 Subject: [PATCH 55/55] move function to hopefully trigger a travis run --- icepyx/core/query.py | 53 +++++++++++++++++++++++--------------------- 1 file changed, 28 insertions(+), 25 deletions(-) diff --git a/icepyx/core/query.py b/icepyx/core/query.py index ac28b1cee..8700d5655 100644 --- a/icepyx/core/query.py +++ b/icepyx/core/query.py @@ -12,6 +12,7 @@ import icepyx.core.APIformatting as apifmt from icepyx.core.auth import EarthdataAuthMixin import icepyx.core.granules as granules + # QUESTION: why doesn't from granules import Granules work, since granules=icepyx.core.granules? from icepyx.core.granules import Granules import icepyx.core.is2ref as is2ref @@ -448,6 +449,7 @@ def __init__( # initialize authentication properties EarthdataAuthMixin.__init__(self) + # ---------------------------------------------------------------------- # Properties @@ -648,7 +650,7 @@ def subsetparams(self, **kwargs): else: # If the user has supplied a subset list of variables, append the # icepyx required variables to the Coverage dict - if 'Coverage' in kwargs.keys(): + if "Coverage" in kwargs.keys(): var_list = [ "orbit_info/sc_orient", "orbit_info/sc_orient_time", @@ -664,9 +666,9 @@ def subsetparams(self, **kwargs): ] # Add any variables from var_list to Coverage that are not already included for var in var_list: - if var not in kwargs['Coverage'].keys(): - kwargs['Coverage'][var.split('/')[-1]] = [var] - + if var not in kwargs["Coverage"].keys(): + kwargs["Coverage"][var.split("/")[-1]] = [var] + if self._subsetparams == None: self._subsetparams = apifmt.Parameters("subset") if self._spatial._geom_file is not None: @@ -710,9 +712,9 @@ def order_vars(self): if hasattr(self, "_cust_options"): self._order_vars = Variables( product=self.product, - version = self._version, + version=self._version, avail=self._cust_options["variables"], - auth = self.auth, + auth=self.auth, ) else: self._order_vars = Variables( @@ -742,17 +744,18 @@ def file_vars(self): Examples -------- >>> reg_a = ipx.Query('ATL06',[-55, 68, -48, 71],['2019-02-20','2019-02-28']) # doctest: +SKIP - + >>> reg_a.file_vars # doctest: +SKIP """ if not hasattr(self, "_file_vars"): if self._source == "file": - self._file_vars = Variables(auth=self.auth, - product=self.product, - version=self._version, - ) + self._file_vars = Variables( + auth=self.auth, + product=self.product, + version=self._version, + ) return self._file_vars @@ -817,20 +820,6 @@ def product_summary_info(self): ] for key in summ_keys: print(key, ": ", self._about_product["feed"]["entry"][-1][key]) - - def latest_version(self): - """ - A reference function to is2ref.latest_version. - - Determine the most recent version available for the given product. - - Examples - -------- - >>> reg_a = ipx.Query('ATL06',[-55, 68, -48, 71],['2019-02-20','2019-02-28']) - >>> reg_a.latest_version() - '006' - """ - return is2ref.latest_version(self.product) def product_all_info(self): """ @@ -847,6 +836,20 @@ def product_all_info(self): self._about_product = is2ref.about_product(self._prod) pprint.pprint(self._about_product) + def latest_version(self): + """ + A reference function to is2ref.latest_version. + + Determine the most recent version available for the given product. + + Examples + -------- + >>> reg_a = ipx.Query('ATL06',[-55, 68, -48, 71],['2019-02-20','2019-02-28']) + >>> reg_a.latest_version() + '006' + """ + return is2ref.latest_version(self.product) + def show_custom_options(self, dictview=False): """ Display customization/subsetting options available for this product.