diff --git a/CHANGELOG.md b/CHANGELOG.md index 246a384e..1cc13378 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Deprecated ### Removed ### Fixed +- Updated xarray enhancement get_indexers_from_nd function for SMAP_RSS_L2_SSS_V6. +- Fix minor bug in checking for empty indexers and same data bounds. ### Security diff --git a/docs/conf.py b/docs/conf.py index fc210fbd..9bb875bb 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -47,8 +47,7 @@ 'sphinx.ext.coverage', 'sphinx.ext.viewcode', 'sphinx.ext.napoleon', - 'sphinx_rtd_theme', - 'm2r2' + 'sphinx_rtd_theme' ] # Add any paths that contain templates here, relative to this directory. @@ -77,3 +76,4 @@ # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] + diff --git a/podaac/subsetter/xarray_enhancements.py b/podaac/subsetter/xarray_enhancements.py index 522eebc3..84b94982 100644 --- a/podaac/subsetter/xarray_enhancements.py +++ b/podaac/subsetter/xarray_enhancements.py @@ -69,57 +69,50 @@ def get_indexers_from_nd(cond: xr.Dataset, cut: bool) -> dict: Indexer dictionary for the provided condition. """ # check if the lat/lon coordinate numpy array has 2 or more dimensions - transpose = False - if cond.values.squeeze().ndim == 2: - x_axis = 1 - y_axis = 0 + transpose = dim_grid = False + ndim = cond.values.squeeze().ndim + + # Determine axes and flags + if ndim == 2: + x_axis, y_axis = 1, 0 else: - if any('xtrack' in dim for dim in list(cond.dims)) and\ - any('atrack' in dim for dim in list(cond.dims)): - x_axis = list(cond.dims).index('xtrack') - y_axis = list(cond.dims).index('atrack') + if 'xtrack' in cond.dims and 'atrack' in cond.dims: + x_axis, y_axis = cond.dims.index('xtrack'), cond.dims.index('atrack') transpose = True + elif 'xdim_grid' in cond.dims and 'ydim_grid' in cond.dims: + x_axis, y_axis = cond.dims.index('xdim_grid'), cond.dims.index('ydim_grid') + dim_grid = x_axis == 1 and y_axis == 0 else: - x_axis = 2 - y_axis = 1 + x_axis, y_axis = 2, 1 - rows = np.any(cond.values.squeeze(), axis=x_axis) - if cut: - cols = np.any(cond.values.squeeze(), axis=y_axis) - else: - cols = np.ones(len(cond.values[0])) + # Compute rows and columns + squeezed_values = cond.values.squeeze() + rows = np.any(squeezed_values, axis=x_axis) + cols = np.any(squeezed_values, axis=y_axis) if cut else np.ones(len(squeezed_values[0])) - # If the subsetted area is equal to the original area - if np.all(rows) & np.all(cols): + # Log information about subsetted area + if np.all(rows) and np.all(cols): logging.info("Subsetted area equal to the original granule.") - - # If the subsetted area is empty - if not np.any(rows) | np.any(cols): + if not np.any(rows) or not np.any(cols): logging.info("No data within the given bounding box.") - cond_shape_list = list(cond.shape) - cond_list = list(cond.dims) - output = [idx for idx, element in enumerate(cond_shape_list) if element == 1] - for i in output: - cond_list.pop(i) - - if rows.ndim == 1: - indexers = { - cond_list[0]: np.where(rows)[0], - cond_list[1]: np.where(cols)[0] - } - else: - # if the lat/lon had 3 dimensions the conditional array was identical in the z direction - taking the first + # Determine dimensions and clean them up + cond_dims = list(cond.dims) + cond_shape = list(cond.shape) + cond_dims = [dim for dim, size in zip(cond_dims, cond_shape) if size > 1] + + # Adjust for 3D data + if rows.ndim > 1: if transpose: - rows = rows.transpose()[0] - cols = cols.transpose()[0] - else: - rows = rows[0] - cols = cols[0] - indexers = { - cond_list[y_axis]: np.where(rows)[0], - cond_list[x_axis]: np.where(cols)[0] - } + rows, cols = rows.transpose()[0], cols.transpose()[0] + elif not dim_grid: + rows, cols = rows[0], cols[0] + + # Generate indexers + indexers = { + cond_dims[y_axis]: np.where(rows)[0], + cond_dims[x_axis]: np.where(cols)[0] + } return indexers diff --git a/poetry.lock b/poetry.lock index 67210aa4..3f587d5b 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.5 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.4 and should not be changed by hand. [[package]] name = "alabaster" @@ -589,7 +589,6 @@ files = [ {file = "cryptography-44.0.0-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:761817a3377ef15ac23cd7834715081791d4ec77f9297ee694ca1ee9c2c7e5eb"}, {file = "cryptography-44.0.0-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3c672a53c0fb4725a29c303be906d3c1fa99c32f58abe008a82705f9ee96f40b"}, {file = "cryptography-44.0.0-cp37-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:4ac4c9f37eba52cb6fbeaf5b59c152ea976726b865bd4cf87883a7e7006cc543"}, - {file = "cryptography-44.0.0-cp37-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:60eb32934076fa07e4316b7b2742fa52cbb190b42c2df2863dbc4230a0a9b385"}, {file = "cryptography-44.0.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:ed3534eb1090483c96178fcb0f8893719d96d5274dfde98aa6add34614e97c8e"}, {file = "cryptography-44.0.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:f3f6fdfa89ee2d9d496e2c087cebef9d4fcbb0ad63c40e821b39f74bf48d9c5e"}, {file = "cryptography-44.0.0-cp37-abi3-win32.whl", hash = "sha256:eb33480f1bad5b78233b0ad3e1b0be21e8ef1da745d8d2aecbb20671658b9053"}, @@ -600,7 +599,6 @@ files = [ {file = "cryptography-44.0.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:c5eb858beed7835e5ad1faba59e865109f3e52b3783b9ac21e7e47dc5554e289"}, {file = "cryptography-44.0.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:f53c2c87e0fb4b0c00fa9571082a057e37690a8f12233306161c8f4b819960b7"}, {file = "cryptography-44.0.0-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:9e6fc8a08e116fb7c7dd1f040074c9d7b51d74a8ea40d4df2fc7aa08b76b9e6c"}, - {file = "cryptography-44.0.0-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:9abcc2e083cbe8dde89124a47e5e53ec38751f0d7dfd36801008f316a127d7ba"}, {file = "cryptography-44.0.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:d2436114e46b36d00f8b72ff57e598978b37399d2786fd39793c36c6d5cb1c64"}, {file = "cryptography-44.0.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a01956ddfa0a6790d594f5b34fc1bfa6098aca434696a03cfdbe469b8ed79285"}, {file = "cryptography-44.0.0-cp39-abi3-win32.whl", hash = "sha256:eca27345e1214d1b9f9490d200f9db5a874479be914199194e746c893788d417"}, @@ -897,13 +895,13 @@ numpy = ">=1.19.3" [[package]] name = "harmony-service-lib" -version = "2.3.0" +version = "2.2.0" description = "A library for Python-based Harmony services to parse incoming message, fetch data, stage data, and call back to Harmony." optional = true python-versions = ">=3.9" files = [ - {file = "harmony_service_lib-2.3.0-py3-none-any.whl", hash = "sha256:2aec682d4c5a7947a22861f396c15760cb98cef6b41ac152aa32a87a98955318"}, - {file = "harmony_service_lib-2.3.0.tar.gz", hash = "sha256:b83593c8f5d79c881da4d5255af72ff55f6ad9d21b44ed18e5df499c9d4245e8"}, + {file = "harmony_service_lib-2.2.0-py3-none-any.whl", hash = "sha256:26961961d2f5749f2ee460949cd556ee09893b664a4cacb7e34bac5a9262224c"}, + {file = "harmony_service_lib-2.2.0.tar.gz", hash = "sha256:23ea2d615d4ce7b8f29bad8d4f96f7f8de208dedc9ac53ed3fea8cae6de174d1"}, ] [package.dependencies] @@ -916,7 +914,7 @@ requests = ">=2.24,<3.0" urllib3 = ">=1.26.9,<1.27.0" [package.extras] -dev = ["Faker (>=8.1.3,<8.2.0)", "autopep8 (>=1.5,<2.0)", "debugpy (>=1.2,<2.0)", "flake8 (>=6.1.0)", "ipython (>=8.10.0,<8.11.0)", "jedi (>=0.17.2,<0.18.0)", "packaging (>=24.1,<25.0)", "parameterized (>=0.7,<1.0)", "pycodestyle (>=2.9.1)", "pytest (>=7.2.0,<7.3.0)", "pytest-cov (>=2.11,<3.0)", "pytest-mock (>=3.5,<4.0)", "python-language-server (>=0.35,<1.0)", "responses (>=0.22.0,<0.23.0)", "safety (==3.2.3)", "safety-schemas (==0.0.5)", "setuptools (==70.0.0)"] +dev = ["Faker (>=8.1.3,<8.2.0)", "autopep8 (>=1.5,<2.0)", "debugpy (>=1.2,<2.0)", "flake8 (>=6.1.0)", "ipython (>=8.10.0,<8.11.0)", "jedi (>=0.17.2,<0.18.0)", "packaging (>=24.1,<25.0)", "parameterized (>=0.7,<1.0)", "pycodestyle (>=2.9.1)", "pytest (>=7.2.0,<7.3.0)", "pytest-cov (>=2.11,<3.0)", "pytest-mock (>=3.5,<4.0)", "python-language-server (>=0.35,<1.0)", "responses (>=0.22.0,<0.23.0)", "safety (>=3.2.7,<3.3.0)", "setuptools (==70.0.0)"] [[package]] name = "idna" @@ -1400,66 +1398,47 @@ tests = ["Cython", "packaging", "pytest"] [[package]] name = "numpy" -version = "2.2.0" +version = "1.26.4" description = "Fundamental package for array computing in Python" optional = false -python-versions = ">=3.10" +python-versions = ">=3.9" files = [ - {file = "numpy-2.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1e25507d85da11ff5066269d0bd25d06e0a0f2e908415534f3e603d2a78e4ffa"}, - {file = "numpy-2.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a62eb442011776e4036af5c8b1a00b706c5bc02dc15eb5344b0c750428c94219"}, - {file = "numpy-2.2.0-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:b606b1aaf802e6468c2608c65ff7ece53eae1a6874b3765f69b8ceb20c5fa78e"}, - {file = "numpy-2.2.0-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:36b2b43146f646642b425dd2027730f99bac962618ec2052932157e213a040e9"}, - {file = "numpy-2.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7fe8f3583e0607ad4e43a954e35c1748b553bfe9fdac8635c02058023277d1b3"}, - {file = "numpy-2.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:122fd2fcfafdefc889c64ad99c228d5a1f9692c3a83f56c292618a59aa60ae83"}, - {file = "numpy-2.2.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3f2f5cddeaa4424a0a118924b988746db6ffa8565e5829b1841a8a3bd73eb59a"}, - {file = "numpy-2.2.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7fe4bb0695fe986a9e4deec3b6857003b4cfe5c5e4aac0b95f6a658c14635e31"}, - {file = "numpy-2.2.0-cp310-cp310-win32.whl", hash = "sha256:b30042fe92dbd79f1ba7f6898fada10bdaad1847c44f2dff9a16147e00a93661"}, - {file = "numpy-2.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:54dc1d6d66f8d37843ed281773c7174f03bf7ad826523f73435deb88ba60d2d4"}, - {file = "numpy-2.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9874bc2ff574c40ab7a5cbb7464bf9b045d617e36754a7bc93f933d52bd9ffc6"}, - {file = "numpy-2.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0da8495970f6b101ddd0c38ace92edea30e7e12b9a926b57f5fabb1ecc25bb90"}, - {file = "numpy-2.2.0-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:0557eebc699c1c34cccdd8c3778c9294e8196df27d713706895edc6f57d29608"}, - {file = "numpy-2.2.0-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:3579eaeb5e07f3ded59298ce22b65f877a86ba8e9fe701f5576c99bb17c283da"}, - {file = "numpy-2.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40deb10198bbaa531509aad0cd2f9fadb26c8b94070831e2208e7df543562b74"}, - {file = "numpy-2.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c2aed8fcf8abc3020d6a9ccb31dbc9e7d7819c56a348cc88fd44be269b37427e"}, - {file = "numpy-2.2.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a222d764352c773aa5ebde02dd84dba3279c81c6db2e482d62a3fa54e5ece69b"}, - {file = "numpy-2.2.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4e58666988605e251d42c2818c7d3d8991555381be26399303053b58a5bbf30d"}, - {file = "numpy-2.2.0-cp311-cp311-win32.whl", hash = "sha256:4723a50e1523e1de4fccd1b9a6dcea750c2102461e9a02b2ac55ffeae09a4410"}, - {file = "numpy-2.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:16757cf28621e43e252c560d25b15f18a2f11da94fea344bf26c599b9cf54b73"}, - {file = "numpy-2.2.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:cff210198bb4cae3f3c100444c5eaa573a823f05c253e7188e1362a5555235b3"}, - {file = "numpy-2.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:58b92a5828bd4d9aa0952492b7de803135038de47343b2aa3cc23f3b71a3dc4e"}, - {file = "numpy-2.2.0-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:ebe5e59545401fbb1b24da76f006ab19734ae71e703cdb4a8b347e84a0cece67"}, - {file = "numpy-2.2.0-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:e2b8cd48a9942ed3f85b95ca4105c45758438c7ed28fff1e4ce3e57c3b589d8e"}, - {file = "numpy-2.2.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:57fcc997ffc0bef234b8875a54d4058afa92b0b0c4223fc1f62f24b3b5e86038"}, - {file = "numpy-2.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85ad7d11b309bd132d74397fcf2920933c9d1dc865487128f5c03d580f2c3d03"}, - {file = "numpy-2.2.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:cb24cca1968b21355cc6f3da1a20cd1cebd8a023e3c5b09b432444617949085a"}, - {file = "numpy-2.2.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0798b138c291d792f8ea40fe3768610f3c7dd2574389e37c3f26573757c8f7ef"}, - {file = "numpy-2.2.0-cp312-cp312-win32.whl", hash = "sha256:afe8fb968743d40435c3827632fd36c5fbde633b0423da7692e426529b1759b1"}, - {file = "numpy-2.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:3a4199f519e57d517ebd48cb76b36c82da0360781c6a0353e64c0cac30ecaad3"}, - {file = "numpy-2.2.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f8c8b141ef9699ae777c6278b52c706b653bf15d135d302754f6b2e90eb30367"}, - {file = "numpy-2.2.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0f0986e917aca18f7a567b812ef7ca9391288e2acb7a4308aa9d265bd724bdae"}, - {file = "numpy-2.2.0-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:1c92113619f7b272838b8d6702a7f8ebe5edea0df48166c47929611d0b4dea69"}, - {file = "numpy-2.2.0-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:5a145e956b374e72ad1dff82779177d4a3c62bc8248f41b80cb5122e68f22d13"}, - {file = "numpy-2.2.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18142b497d70a34b01642b9feabb70156311b326fdddd875a9981f34a369b671"}, - {file = "numpy-2.2.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a7d41d1612c1a82b64697e894b75db6758d4f21c3ec069d841e60ebe54b5b571"}, - {file = "numpy-2.2.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a98f6f20465e7618c83252c02041517bd2f7ea29be5378f09667a8f654a5918d"}, - {file = "numpy-2.2.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e09d40edfdb4e260cb1567d8ae770ccf3b8b7e9f0d9b5c2a9992696b30ce2742"}, - {file = "numpy-2.2.0-cp313-cp313-win32.whl", hash = "sha256:3905a5fffcc23e597ee4d9fb3fcd209bd658c352657548db7316e810ca80458e"}, - {file = "numpy-2.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:a184288538e6ad699cbe6b24859206e38ce5fba28f3bcfa51c90d0502c1582b2"}, - {file = "numpy-2.2.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:7832f9e8eb00be32f15fdfb9a981d6955ea9adc8574c521d48710171b6c55e95"}, - {file = "numpy-2.2.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:f0dd071b95bbca244f4cb7f70b77d2ff3aaaba7fa16dc41f58d14854a6204e6c"}, - {file = "numpy-2.2.0-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:b0b227dcff8cdc3efbce66d4e50891f04d0a387cce282fe1e66199146a6a8fca"}, - {file = "numpy-2.2.0-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:6ab153263a7c5ccaf6dfe7e53447b74f77789f28ecb278c3b5d49db7ece10d6d"}, - {file = "numpy-2.2.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e500aba968a48e9019e42c0c199b7ec0696a97fa69037bea163b55398e390529"}, - {file = "numpy-2.2.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:440cfb3db4c5029775803794f8638fbdbf71ec702caf32735f53b008e1eaece3"}, - {file = "numpy-2.2.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:a55dc7a7f0b6198b07ec0cd445fbb98b05234e8b00c5ac4874a63372ba98d4ab"}, - {file = "numpy-2.2.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4bddbaa30d78c86329b26bd6aaaea06b1e47444da99eddac7bf1e2fab717bd72"}, - {file = "numpy-2.2.0-cp313-cp313t-win32.whl", hash = "sha256:30bf971c12e4365153afb31fc73f441d4da157153f3400b82db32d04de1e4066"}, - {file = "numpy-2.2.0-cp313-cp313t-win_amd64.whl", hash = "sha256:d35717333b39d1b6bb8433fa758a55f1081543de527171543a2b710551d40881"}, - {file = "numpy-2.2.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:e12c6c1ce84628c52d6367863773f7c8c8241be554e8b79686e91a43f1733773"}, - {file = "numpy-2.2.0-pp310-pypy310_pp73-macosx_14_0_x86_64.whl", hash = "sha256:b6207dc8fb3c8cb5668e885cef9ec7f70189bec4e276f0ff70d5aa078d32c88e"}, - {file = "numpy-2.2.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a50aeff71d0f97b6450d33940c7181b08be1441c6c193e678211bff11aa725e7"}, - {file = "numpy-2.2.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:df12a1f99b99f569a7c2ae59aa2d31724e8d835fc7f33e14f4792e3071d11221"}, - {file = "numpy-2.2.0.tar.gz", hash = "sha256:140dd80ff8981a583a60980be1a655068f8adebf7a45a06a6858c873fcdcd4a0"}, + {file = "numpy-1.26.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9ff0f4f29c51e2803569d7a51c2304de5554655a60c5d776e35b4a41413830d0"}, + {file = "numpy-1.26.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2e4ee3380d6de9c9ec04745830fd9e2eccb3e6cf790d39d7b98ffd19b0dd754a"}, + {file = "numpy-1.26.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d209d8969599b27ad20994c8e41936ee0964e6da07478d6c35016bc386b66ad4"}, + {file = "numpy-1.26.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ffa75af20b44f8dba823498024771d5ac50620e6915abac414251bd971b4529f"}, + {file = "numpy-1.26.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:62b8e4b1e28009ef2846b4c7852046736bab361f7aeadeb6a5b89ebec3c7055a"}, + {file = "numpy-1.26.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a4abb4f9001ad2858e7ac189089c42178fcce737e4169dc61321660f1a96c7d2"}, + {file = "numpy-1.26.4-cp310-cp310-win32.whl", hash = "sha256:bfe25acf8b437eb2a8b2d49d443800a5f18508cd811fea3181723922a8a82b07"}, + {file = "numpy-1.26.4-cp310-cp310-win_amd64.whl", hash = "sha256:b97fe8060236edf3662adfc2c633f56a08ae30560c56310562cb4f95500022d5"}, + {file = "numpy-1.26.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4c66707fabe114439db9068ee468c26bbdf909cac0fb58686a42a24de1760c71"}, + {file = "numpy-1.26.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:edd8b5fe47dab091176d21bb6de568acdd906d1887a4584a15a9a96a1dca06ef"}, + {file = "numpy-1.26.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ab55401287bfec946ced39700c053796e7cc0e3acbef09993a9ad2adba6ca6e"}, + {file = "numpy-1.26.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:666dbfb6ec68962c033a450943ded891bed2d54e6755e35e5835d63f4f6931d5"}, + {file = "numpy-1.26.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:96ff0b2ad353d8f990b63294c8986f1ec3cb19d749234014f4e7eb0112ceba5a"}, + {file = "numpy-1.26.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:60dedbb91afcbfdc9bc0b1f3f402804070deed7392c23eb7a7f07fa857868e8a"}, + {file = "numpy-1.26.4-cp311-cp311-win32.whl", hash = "sha256:1af303d6b2210eb850fcf03064d364652b7120803a0b872f5211f5234b399f20"}, + {file = "numpy-1.26.4-cp311-cp311-win_amd64.whl", hash = "sha256:cd25bcecc4974d09257ffcd1f098ee778f7834c3ad767fe5db785be9a4aa9cb2"}, + {file = "numpy-1.26.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b3ce300f3644fb06443ee2222c2201dd3a89ea6040541412b8fa189341847218"}, + {file = "numpy-1.26.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:03a8c78d01d9781b28a6989f6fa1bb2c4f2d51201cf99d3dd875df6fbd96b23b"}, + {file = "numpy-1.26.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9fad7dcb1aac3c7f0584a5a8133e3a43eeb2fe127f47e3632d43d677c66c102b"}, + {file = "numpy-1.26.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:675d61ffbfa78604709862923189bad94014bef562cc35cf61d3a07bba02a7ed"}, + {file = "numpy-1.26.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ab47dbe5cc8210f55aa58e4805fe224dac469cde56b9f731a4c098b91917159a"}, + {file = "numpy-1.26.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:1dda2e7b4ec9dd512f84935c5f126c8bd8b9f2fc001e9f54af255e8c5f16b0e0"}, + {file = "numpy-1.26.4-cp312-cp312-win32.whl", hash = "sha256:50193e430acfc1346175fcbdaa28ffec49947a06918b7b92130744e81e640110"}, + {file = "numpy-1.26.4-cp312-cp312-win_amd64.whl", hash = "sha256:08beddf13648eb95f8d867350f6a018a4be2e5ad54c8d8caed89ebca558b2818"}, + {file = "numpy-1.26.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7349ab0fa0c429c82442a27a9673fc802ffdb7c7775fad780226cb234965e53c"}, + {file = "numpy-1.26.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:52b8b60467cd7dd1e9ed082188b4e6bb35aa5cdd01777621a1658910745b90be"}, + {file = "numpy-1.26.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d5241e0a80d808d70546c697135da2c613f30e28251ff8307eb72ba696945764"}, + {file = "numpy-1.26.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f870204a840a60da0b12273ef34f7051e98c3b5961b61b0c2c1be6dfd64fbcd3"}, + {file = "numpy-1.26.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:679b0076f67ecc0138fd2ede3a8fd196dddc2ad3254069bcb9faf9a79b1cebcd"}, + {file = "numpy-1.26.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:47711010ad8555514b434df65f7d7b076bb8261df1ca9bb78f53d3b2db02e95c"}, + {file = "numpy-1.26.4-cp39-cp39-win32.whl", hash = "sha256:a354325ee03388678242a4d7ebcd08b5c727033fcff3b2f536aea978e15ee9e6"}, + {file = "numpy-1.26.4-cp39-cp39-win_amd64.whl", hash = "sha256:3373d5d70a5fe74a2c1bb6d2cfd9609ecf686d47a2d7b1d37a8f3b6bf6003aea"}, + {file = "numpy-1.26.4-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:afedb719a9dcfc7eaf2287b839d8198e06dcd4cb5d276a3df279231138e83d30"}, + {file = "numpy-1.26.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95a7476c59002f2f6c590b9b7b998306fba6a5aa646b1e22ddfeaf8f78c3a29c"}, + {file = "numpy-1.26.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7e50d0a0cc3189f9cb0aeb3a6a6af18c16f59f004b866cd2be1c14b36134a4a0"}, + {file = "numpy-1.26.4.tar.gz", hash = "sha256:2a02aba9ed12e4ac4eb3ea9421c420301a0c6460d9830d74a9df87efa4912010"}, ] [[package]] @@ -2868,4 +2847,4 @@ harmony = ["harmony-service-lib", "pystac"] [metadata] lock-version = "2.0" python-versions = "^3.10" -content-hash = "2b358275e429ac3b70136bdaa643a29728458698a53d89efea803955abf58f38" +content-hash = "0fd8e023498c232831d947d79ea859651b206a4f316fb272f2d9c87d5021de37" diff --git a/pyproject.toml b/pyproject.toml index 06bc2a43..d8fbb1de 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -32,13 +32,13 @@ netCDF4 = "^1.5" xarray = {version = "*", extras = ["parallel"]} geopandas ="^1.0.1" Shapely = "^2.0.2" -harmony-service-lib = { version = "^2.0.0", optional = true } +harmony-service-lib = { version = "<=2.2.0", optional = true } pystac = { version = "^1.10.1", optional = true } julian = "^0.14" importlib-metadata = "^8.2.0" h5py = "^3.6.0" cf-xarray = "*" -numpy = "^2.2.0" +numpy = "^1.26.4" [tool.poetry.dev-dependencies] pytest = "^8.0.2" diff --git a/tests/test_get_indexers.py b/tests/test_get_indexers.py new file mode 100644 index 00000000..d56900ea --- /dev/null +++ b/tests/test_get_indexers.py @@ -0,0 +1,212 @@ +import pytest +import numpy as np +import xarray as xr +import operator +from podaac.subsetter.xarray_enhancements import get_indexers_from_nd + +def create_lon_lat_condition_dataset( + lon_bounds, lat_bounds, + lon_var_name='longitude', lat_var_name='latitude', + lon_dim_name='lon', lat_dim_name='lat', + third_dim_name='level', third_dim_values=None, include_third_dim=False +): + """ + Create a test dataset using xarray with customizable longitude, latitude, and optional third dimension. + + Parameters: + - lon_bounds: Tuple of longitude bounds (start, end) + - lat_bounds: Tuple of latitude bounds (start, end) + - lon_var_name: Name of the longitude variable + - lat_var_name: Name of the latitude variable + - lon_dim_name: Name of the longitude dimension + - lat_dim_name: Name of the latitude dimension + - third_dim_name: Name of the third dimension + - third_dim_values: Values for the third dimension (e.g., levels, time). Defaults to range(0, 10). + - include_third_dim: Whether to include the third dimension (True for 3D, False for 2D) + """ + # Default values for the third dimension if not provided + if third_dim_values is None: + third_dim_values = np.linspace(0, 1000, 11) # Example: 11 levels from 0 to 1000 + + # Create coordinates + lats = np.linspace(-90, 90, 181) + lons = np.linspace(-180, 180, 361) + + if include_third_dim: + coords = {lat_dim_name: lats, lon_dim_name: lons, third_dim_name: third_dim_values} + else: + coords = {lat_dim_name: lats, lon_dim_name: lons} + + # Create the dataset with coordinates + ds = xr.Dataset(coords=coords) + + # Add longitude and latitude as variables + if include_third_dim: + ds[lon_var_name] = xr.DataArray( + data=np.tile(lons[None, None, :], (len(third_dim_values), len(lats), 1)), + dims=[third_dim_name, lat_dim_name, lon_dim_name] + ) + ds[lat_var_name] = xr.DataArray( + data=np.tile(lats[None, :, None], (len(third_dim_values), 1, len(lons))), + dims=[third_dim_name, lat_dim_name, lon_dim_name] + ) + ds[third_dim_name] = xr.DataArray( + data=np.tile(third_dim_values[:, None, None], (1, len(lats), len(lons))), + dims=[third_dim_name, lat_dim_name, lon_dim_name] + ) + else: + ds[lon_var_name] = xr.DataArray( + data=np.tile(lons[None, :], (len(lats), 1)), + dims=[lat_dim_name, lon_dim_name] + ) + ds[lat_var_name] = xr.DataArray( + data=np.tile(lats[:, None], (1, len(lons))), + dims=[lat_dim_name, lon_dim_name] + ) + + # Determine operator based on longitude bounds + oper = operator.and_ + if lon_bounds[0] > lon_bounds[1]: + oper = operator.or_ + + # Create condition based on bounds + if oper == operator.and_: + condition = ( + (ds[lon_var_name] >= lon_bounds[0]) & + (ds[lon_var_name] <= lon_bounds[1]) & + (ds[lat_var_name] >= lat_bounds[0]) & + (ds[lat_var_name] <= lat_bounds[1]) + ) + else: + condition = ( + ((ds[lon_var_name] >= lon_bounds[0]) | + (ds[lon_var_name] <= lon_bounds[1])) & + (ds[lat_var_name] >= lat_bounds[0]) & + (ds[lat_var_name] <= lat_bounds[1]) + ) + + return condition + +def test_standard_longitude_bounds(): + """ + Test standard longitude bounds (no antimeridian crossing) + """ + lon_bounds = (30, 60) + lat_bounds = (-30, 30) + + cond = create_lon_lat_condition_dataset(lon_bounds, lat_bounds) + indexers = get_indexers_from_nd(cond, cut=True) + + assert 'lat' in indexers + assert 'lon' in indexers + assert len(indexers['lat']) > 0 + assert len(indexers['lon']) > 0 + +def test_antimeridian_crossing(): + """ + Test longitude bounds crossing the antimeridian + """ + lon_bounds = (170, -170) + lat_bounds = (-30, 30) + + cond = create_lon_lat_condition_dataset(lon_bounds, lat_bounds) + indexers = get_indexers_from_nd(cond, cut=True) + + assert 'lat' in indexers + assert 'lon' in indexers + assert len(indexers['lat']) > 0 + assert len(indexers['lon']) > 0 + +def test_full_latitude_range(): + """ + Test full latitude range + """ + lon_bounds = (0, 60) + lat_bounds = (-90, 90) + + cond = create_lon_lat_condition_dataset(lon_bounds, lat_bounds) + indexers = get_indexers_from_nd(cond, cut=True) + + assert 'lat' in indexers + assert len(indexers['lat']) == 181 # Full latitude range + +def test_no_data_region(): + """ + Test region with no data + """ + lon_bounds = (190, 200) # Outside our longitude range + lat_bounds = (91, 92) # Outside our latitude range + + cond = create_lon_lat_condition_dataset(lon_bounds, lat_bounds) + indexers = get_indexers_from_nd(cond, cut=True) + + assert 'lat' in indexers + assert 'lon' in indexers + assert len(indexers['lat']) == 0 + assert len(indexers['lon']) == 0 + +def test_custom_variable_names(): + """ + Test with custom longitude and latitude variable names + """ + lon_bounds = (0, 60) + lat_bounds = (-30, 30) + + cond = create_lon_lat_condition_dataset( + lon_bounds, lat_bounds, + lon_var_name='lon_custom', + lat_var_name='lat_custom' + ) + indexers = get_indexers_from_nd(cond, cut=True) + + assert 'lat' in indexers + assert 'lon' in indexers + assert len(indexers['lat']) > 0 + assert len(indexers['lon']) > 0 + +def test_random_dim(): + """ + Test with custom longitude and latitude variable names + """ + lon_bounds = (0, 60) + lat_bounds = (-30, 30) + + cond = create_lon_lat_condition_dataset( + lon_bounds, lat_bounds, + lon_var_name='lon_custom', + lat_var_name='lat_custom', + lon_dim_name='x', + lat_dim_name='y', + third_dim_name='ztrack', + include_third_dim=True + ) + indexers = get_indexers_from_nd(cond, cut=True) + + print(indexers) + assert 'x' in indexers + assert 'y' in indexers + assert len(indexers['x']) > 0 + assert len(indexers['y']) > 0 + +def test_xdim_grid(): + """ + Test with custom longitude and latitude variable names + """ + lon_bounds = (0, 60) + lat_bounds = (-30, 30) + + cond = create_lon_lat_condition_dataset( + lon_bounds, lat_bounds, + lon_var_name='lon_custom', + lat_var_name='lat_custom', + lon_dim_name='xdim_grid', + lat_dim_name='ydim_grid', + third_dim_name='ztrack', + include_third_dim=True + ) + indexers = get_indexers_from_nd(cond, cut=True) + + assert 'xdim_grid' in indexers + assert 'ydim_grid' in indexers + assert len(indexers['xdim_grid']) > 0 + assert len(indexers['ydim_grid']) > 0 \ No newline at end of file diff --git a/tests/test_subset_harmony.py b/tests/test_subset_harmony.py index 6d8e3ee4..b62c51ca 100644 --- a/tests/test_subset_harmony.py +++ b/tests/test_subset_harmony.py @@ -84,7 +84,8 @@ def test_service_invoke(mock_environ, temp_dir): test_args = [ podaac.subsetter.subset_harmony.__file__, "--harmony-action", "invoke", - "--harmony-input", json.dumps(input_json) + "--harmony-input", json.dumps(input_json), + "--harmony-metadata-dir", temp_dir ] process_item_spy = spy_on(L2SubsetterService.process_item) @@ -134,7 +135,8 @@ def test_service_invoke(mock_environ, temp_dir): test_args = [ podaac.subsetter.subset_harmony.__file__, "--harmony-action", "invoke", - "--harmony-input", json.dumps(input_json) + "--harmony-input", json.dumps(input_json), + "--harmony-metadata-dir", temp_dir ] process_item_spy = spy_on(L2SubsetterService.process_item) @@ -192,7 +194,8 @@ def test_service_invoke_coord_vars(mock_environ, temp_dir): test_args = [ podaac.subsetter.subset_harmony.__file__, "--harmony-action", "invoke", - "--harmony-input", json.dumps(input_json) + "--harmony-input", json.dumps(input_json), + "--harmony-metadata-dir", temp_dir ] process_item_spy = spy_on(L2SubsetterService.process_item)