diff --git a/docs/api-hidden.rst b/docs/api-hidden.rst index 0dc483b..1ee23b2 100644 --- a/docs/api-hidden.rst +++ b/docs/api-hidden.rst @@ -5,7 +5,7 @@ .. autosummary:: :toctree: generated - DGGSInfo.resolution + DGGSInfo.level DGGSInfo.from_dict DGGSInfo.to_dict @@ -13,7 +13,7 @@ DGGSInfo.cell_ids2geographic DGGSInfo.geographic2cell_ids - HealpixInfo.resolution + HealpixInfo.level HealpixInfo.indexing_scheme HealpixInfo.valid_parameters HealpixInfo.nside @@ -25,7 +25,7 @@ HealpixInfo.cell_ids2geographic HealpixInfo.geographic2cell_ids - H3Info.resolution + H3Info.level H3Info.valid_parameters H3Info.from_dict diff --git a/xdggs/grid.py b/xdggs/grid.py index 6582c83..eab1843 100644 --- a/xdggs/grid.py +++ b/xdggs/grid.py @@ -1,11 +1,19 @@ +import operator from dataclasses import dataclass from typing import Any, TypeVar +from xdggs.itertools import groupby, identity + try: from typing import Self except ImportError: # pragma: no cover from typing_extensions import Self +try: + ExceptionGroup +except NameError: # pragma: no cover + from exceptiongroup import ExceptionGroup + T = TypeVar("T") @@ -15,18 +23,22 @@ class DGGSInfo: Parameters ---------- - resolution : int - The resolution of the grid. + level : int + Grid hierarchical level. A higher value corresponds to a finer grid resolution + with smaller cell areas. The number of cells covering the whole sphere usually + grows exponentially with increasing level values, ranging from 5-100 cells at + level 0 to millions or billions of cells at level 10+ (the exact numbers depends + on the specific grid). """ - resolution: int + level: int @classmethod def from_dict(cls: type[T], mapping: dict[str, Any]) -> T: return cls(**mapping) def to_dict(self: Self) -> dict[str, Any]: - return {"resolution": self.resolution} + return {"level": self.level} def cell_ids2geographic(self, cell_ids): raise NotImplementedError() @@ -36,3 +48,34 @@ def geographic2cell_ids(self, lon, lat): def cell_boundaries(self, cell_ids, backend="shapely"): raise NotImplementedError() + + +def translate_parameters(mapping, translations): + def translate(name, value): + new_name, translator = translations.get(name, (name, identity)) + + return new_name, name, translator(value) + + translated = (translate(name, value) for name, value in mapping.items()) + grouped = { + name: [(old_name, value) for _, old_name, value in group] + for name, group in groupby(translated, key=operator.itemgetter(0)) + } + duplicated_parameters = { + name: group for name, group in grouped.items() if len(group) != 1 + } + if duplicated_parameters: + raise ExceptionGroup( + "received multiple values for parameters", + [ + ValueError( + f"Parameter {name} received multiple values: {sorted(n for n, _ in group)}" + ) + for name, group in duplicated_parameters.items() + ], + ) + + params = { + name: group[0][1] for name, group in grouped.items() if name != "grid_name" + } + return params diff --git a/xdggs/h3.py b/xdggs/h3.py index a38592b..f9e5178 100644 --- a/xdggs/h3.py +++ b/xdggs/h3.py @@ -17,8 +17,9 @@ ) from xarray.indexes import PandasIndex -from xdggs.grid import DGGSInfo +from xdggs.grid import DGGSInfo, translate_parameters from xdggs.index import DGGSIndex +from xdggs.itertools import identity from xdggs.utils import _extract_cell_id_variable, register_dggs @@ -65,18 +66,22 @@ class H3Info(DGGSInfo): Parameters ---------- - resolution : int - The resolution of the grid + level : int + Grid hierarchical level. A higher value corresponds to a finer grid resolution + with smaller cell areas. The number of cells covering the whole sphere usually + grows exponentially with increasing level values, ranging from 5-100 cells at + level 0 to millions or billions of cells at level 10+ (the exact numbers depends + on the specific grid). """ - resolution: int - """int : The resolution of the grid""" + level: int + """int : The hierarchical level of the grid""" - valid_parameters: ClassVar[dict[str, Any]] = {"resolution": range(16)} + valid_parameters: ClassVar[dict[str, Any]] = {"level": range(16)} def __post_init__(self): - if self.resolution not in self.valid_parameters["resolution"]: - raise ValueError("resolution must be an integer between 0 and 15") + if self.level not in self.valid_parameters["level"]: + raise ValueError("level must be an integer between 0 and 15") @classmethod def from_dict(cls: type[Self], mapping: dict[str, Any]) -> Self: @@ -92,8 +97,11 @@ def from_dict(cls: type[Self], mapping: dict[str, Any]) -> Self: grid_info : H3Info The constructed grid info object. """ + translations = { + "resolution": ("level", identity), + } - params = {k: v for k, v in mapping.items() if k != "grid_name"} + params = translate_parameters(mapping, translations) return cls(**params) def to_dict(self: Self) -> dict[str, Any]: @@ -105,7 +113,7 @@ def to_dict(self: Self) -> dict[str, Any]: mapping : dict of str to any The normalized grid parameters. """ - return {"grid_name": "h3", "resolution": self.resolution} + return {"grid_name": "h3", "level": self.level} def cell_ids2geographic( self, cell_ids: np.ndarray @@ -148,7 +156,7 @@ def geographic2cell_ids(self, lon, lat): cell_ids : array-like Array-like containing the cell ids. """ - return coordinates_to_cells(lat, lon, self.resolution, radians=False) + return coordinates_to_cells(lat, lon, self.level, radians=False) def cell_boundaries(self, cell_ids, backend="shapely"): """ @@ -216,4 +224,4 @@ def _replace(self, new_pd_index: PandasIndex): return type(self)(new_pd_index, self._dim, self._grid) def _repr_inline_(self, max_width: int): - return f"H3Index(resolution={self._grid.resolution})" + return f"H3Index(level={self._grid.level})" diff --git a/xdggs/healpix.py b/xdggs/healpix.py index 6db12db..ab1db0f 100644 --- a/xdggs/healpix.py +++ b/xdggs/healpix.py @@ -1,5 +1,4 @@ import json -import operator from collections.abc import Mapping from dataclasses import dataclass from typing import Any, ClassVar, Literal, TypeVar @@ -14,18 +13,13 @@ import xarray as xr from xarray.indexes import PandasIndex -from xdggs.grid import DGGSInfo +from xdggs.grid import DGGSInfo, translate_parameters from xdggs.index import DGGSIndex -from xdggs.itertools import groupby, identity +from xdggs.itertools import identity from xdggs.utils import _extract_cell_id_variable, register_dggs T = TypeVar("T") -try: - ExceptionGroup -except NameError: # pragma: no cover - from exceptiongroup import ExceptionGroup - def polygons_shapely(vertices): import shapely @@ -100,8 +94,12 @@ class HealpixInfo(DGGSInfo): Parameters ---------- - resolution : int - The resolution of the grid + level : int + Grid hierarchical level. A higher value corresponds to a finer grid resolution + with smaller cell areas. The number of cells covering the whole sphere usually + grows exponentially with increasing level values, ranging from 5-100 cells at + level 0 to millions or billions of cells at level 10+ (the exact numbers depends + on the specific grid). indexing_scheme : {"nested", "ring", "unique"}, default: "nested" The indexing scheme of the healpix grid. @@ -110,20 +108,20 @@ class HealpixInfo(DGGSInfo): (:doc:`healpy `) does not support it. """ - resolution: int - """int : The resolution of the grid""" + level: int + """int : The hierarchical level of the grid""" indexing_scheme: Literal["nested", "ring", "unique"] = "nested" """int : The indexing scheme of the grid""" valid_parameters: ClassVar[dict[str, Any]] = { - "resolution": range(0, 29 + 1), + "level": range(0, 29 + 1), "indexing_scheme": ["nested", "ring", "unique"], } def __post_init__(self): - if self.resolution not in self.valid_parameters["resolution"]: - raise ValueError("resolution must be an integer in the range of [0, 29]") + if self.level not in self.valid_parameters["level"]: + raise ValueError("level must be an integer in the range of [0, 29]") if self.indexing_scheme not in self.valid_parameters["indexing_scheme"]: raise ValueError( @@ -135,7 +133,7 @@ def __post_init__(self): @property def nside(self: Self) -> int: """resolution as the healpy-compatible nside parameter""" - return 2**self.resolution + return 2**self.level @property def nest(self: Self) -> bool: @@ -164,49 +162,21 @@ def from_dict(cls: type[T], mapping: dict[str, Any]) -> T: def translate_nside(nside): log = np.log2(nside) - potential_resolution = int(log) - if potential_resolution != log: + potential_level = int(log) + if potential_level != log: raise ValueError("`nside` has to be an integer power of 2") - return potential_resolution + return potential_level translations = { - "nside": ("resolution", translate_nside), - "order": ("resolution", identity), - "level": ("resolution", identity), - "depth": ("resolution", identity), + "nside": ("level", translate_nside), + "order": ("level", identity), + "resolution": ("level", identity), + "depth": ("level", identity), "nest": ("indexing_scheme", lambda nest: "nested" if nest else "ring"), } - def translate(name, value): - new_name, translator = translations.get(name, (name, identity)) - - return new_name, name, translator(value) - - translated = (translate(name, value) for name, value in mapping.items()) - - grouped = { - name: [(old_name, value) for _, old_name, value in group] - for name, group in groupby(translated, key=operator.itemgetter(0)) - } - duplicated_parameters = { - name: group for name, group in grouped.items() if len(group) != 1 - } - if duplicated_parameters: - raise ExceptionGroup( - "received multiple values for parameters", - [ - ValueError( - f"Parameter {name} received multiple values: {sorted(n for n, _ in group)}" - ) - for name, group in duplicated_parameters.items() - ], - ) - - params = { - name: group[0][1] for name, group in grouped.items() if name != "grid_name" - } - + params = translate_parameters(mapping, translations) return cls(**params) def to_dict(self: Self) -> dict[str, Any]: @@ -220,7 +190,7 @@ def to_dict(self: Self) -> dict[str, Any]: """ return { "grid_name": "healpix", - "resolution": self.resolution, + "level": self.level, "indexing_scheme": self.indexing_scheme, } @@ -343,4 +313,4 @@ def grid_info(self) -> HealpixInfo: return self._grid def _repr_inline_(self, max_width: int): - return f"HealpixIndex(nside={self._grid.resolution}, indexing_scheme={self._grid.indexing_scheme})" + return f"HealpixIndex(nside={self._grid.level}, indexing_scheme={self._grid.indexing_scheme})" diff --git a/xdggs/tests/test_accessor.py b/xdggs/tests/test_accessor.py index cd6755a..306e6d4 100644 --- a/xdggs/tests/test_accessor.py +++ b/xdggs/tests/test_accessor.py @@ -16,7 +16,7 @@ [3], { "grid_name": "healpix", - "resolution": 1, + "level": 1, "indexing_scheme": "ring", }, ) @@ -36,7 +36,7 @@ "cell_ids": ( "cells", [0x832830FFFFFFFFF], - {"grid_name": "h3", "resolution": 3}, + {"grid_name": "h3", "level": 3}, ) } ), @@ -69,7 +69,7 @@ def test_cell_centers(obj, expected): [3], { "grid_name": "healpix", - "resolution": 1, + "level": 1, "indexing_scheme": "ring", }, ) @@ -86,7 +86,7 @@ def test_cell_centers(obj, expected): [3], { "grid_name": "healpix", - "resolution": 1, + "level": 1, "indexing_scheme": "ring", }, ), @@ -100,7 +100,7 @@ def test_cell_centers(obj, expected): "cell_ids": ( "cells", [0x832830FFFFFFFFF], - {"grid_name": "h3", "resolution": 3}, + {"grid_name": "h3", "level": 3}, ) } ), @@ -111,7 +111,7 @@ def test_cell_centers(obj, expected): "cell_ids": ( "cells", [0x832830FFFFFFFFF], - {"grid_name": "h3", "resolution": 3}, + {"grid_name": "h3", "level": 3}, ), } ), diff --git a/xdggs/tests/test_h3.py b/xdggs/tests/test_h3.py index 3ab102c..b6e758e 100644 --- a/xdggs/tests/test_h3.py +++ b/xdggs/tests/test_h3.py @@ -28,22 +28,14 @@ ), ] dims = ["cells", "zones"] -resolutions = [1, 5, 15] +levels = [1, 5, 15] variable_names = ["cell_ids", "zonal_ids", "zone_ids"] variables = [ - xr.Variable( - dims[0], cell_ids[0], {"grid_name": "h3", "resolution": resolutions[0]} - ), - xr.Variable( - dims[1], cell_ids[0], {"grid_name": "h3", "resolution": resolutions[0]} - ), - xr.Variable( - dims[0], cell_ids[1], {"grid_name": "h3", "resolution": resolutions[1]} - ), - xr.Variable( - dims[1], cell_ids[2], {"grid_name": "h3", "resolution": resolutions[2]} - ), + xr.Variable(dims[0], cell_ids[0], {"grid_name": "h3", "level": levels[0]}), + xr.Variable(dims[1], cell_ids[0], {"grid_name": "h3", "level": levels[0]}), + xr.Variable(dims[0], cell_ids[1], {"grid_name": "h3", "level": levels[1]}), + xr.Variable(dims[1], cell_ids[2], {"grid_name": "h3", "level": levels[2]}), ] variable_combinations = [ (old, new) for old, new in itertools.product(variables, repeat=2) @@ -53,29 +45,29 @@ class TestH3Info: @pytest.mark.parametrize( - ["resolution", "error"], + ["level", "error"], ( (0, None), (1, None), - (-1, ValueError("resolution must be an integer between")), + (-1, ValueError("level must be an integer between")), ), ) - def test_init(self, resolution, error): + def test_init(self, level, error): if error is not None: with pytest.raises(type(error), match=str(error)): - h3.H3Info(resolution=resolution) + h3.H3Info(level=level) return - actual = h3.H3Info(resolution=resolution) + actual = h3.H3Info(level=level) - assert actual.resolution == resolution + assert actual.level == level @pytest.mark.parametrize( ["mapping", "expected"], ( - ({"resolution": 0}, 0), + ({"level": 0}, 0), ({"resolution": 1}, 1), - ({"resolution": -1}, ValueError("resolution must be an integer between")), + ({"level": -1}, ValueError("level must be an integer between")), ), ) def test_from_dict(self, mapping, expected): @@ -85,10 +77,10 @@ def test_from_dict(self, mapping, expected): return actual = h3.H3Info.from_dict(mapping) - assert actual.resolution == expected + assert actual.level == expected def test_roundtrip(self): - mapping = {"grid_name": "h3", "resolution": 0} + mapping = {"grid_name": "h3", "level": 0} grid = h3.H3Info.from_dict(mapping) actual = grid.to_dict() @@ -99,7 +91,7 @@ def test_roundtrip(self): ["cell_ids", "cell_centers"], list(zip(cell_ids, cell_centers)) ) def test_cell_ids2geographic(self, cell_ids, cell_centers): - grid = h3.H3Info(resolution=3) + grid = h3.H3Info(level=3) actual = grid.cell_ids2geographic(cell_ids) expected = cell_centers.T @@ -111,7 +103,7 @@ def test_cell_ids2geographic(self, cell_ids, cell_centers): ["cell_centers", "cell_ids"], list(zip(cell_centers, cell_ids)) ) def test_geographic2cell_ids(self, cell_centers, cell_ids): - grid = h3.H3Info(resolution=3) + grid = h3.H3Info(level=3) actual = grid.geographic2cell_ids( lon=cell_centers[:, 0], lat=cell_centers[:, 1] @@ -121,7 +113,7 @@ def test_geographic2cell_ids(self, cell_centers, cell_ids): np.testing.assert_equal(actual, expected) @pytest.mark.parametrize( - ["resolution", "cell_ids", "expected_coords"], + ["level", "cell_ids", "expected_coords"], ( ( 1, @@ -204,10 +196,10 @@ def test_geographic2cell_ids(self, cell_centers, cell_ids): ), ) @pytest.mark.parametrize("backend", ["shapely", "geoarrow"]) - def test_cell_boundaries(self, resolution, cell_ids, backend, expected_coords): + def test_cell_boundaries(self, level, cell_ids, backend, expected_coords): expected = shapely.polygons(expected_coords) - grid = h3.H3Info(resolution=resolution) + grid = h3.H3Info(level=level) backends = {"shapely": lambda arr: arr, "geoarrow": geoarrow_to_shapely} converter = backends[backend] @@ -217,11 +209,11 @@ def test_cell_boundaries(self, resolution, cell_ids, backend, expected_coords): shapely.testing.assert_geometries_equal(converter(actual), expected) -@pytest.mark.parametrize("resolution", resolutions) +@pytest.mark.parametrize("level", levels) @pytest.mark.parametrize("dim", dims) @pytest.mark.parametrize("cell_ids", cell_ids) -def test_init(cell_ids, dim, resolution): - grid = h3.H3Info(resolution) +def test_init(cell_ids, dim, level): + grid = h3.H3Info(level) index = h3.H3Index(cell_ids, dim, grid) assert index._grid == grid @@ -232,9 +224,9 @@ def test_init(cell_ids, dim, resolution): assert np.all(index._pd_index.index.values == cell_ids) -@pytest.mark.parametrize("resolution", resolutions) -def test_grid(resolution): - grid = h3.H3Info(resolution) +@pytest.mark.parametrize("level", levels) +def test_grid(level): + grid = h3.H3Info(level) index = h3.H3Index([0], "cell_ids", grid) @@ -245,12 +237,12 @@ def test_grid(resolution): @pytest.mark.parametrize("variable_name", variable_names) @pytest.mark.parametrize("options", [{}]) def test_from_variables(variable_name, variable, options): - expected_resolution = variable.attrs["resolution"] + expected_level = variable.attrs["level"] variables = {variable_name: variable} index = h3.H3Index.from_variables(variables, options=options) - assert index._grid.resolution == expected_resolution + assert index._grid.level == expected_level assert (index._dim,) == variable.dims # TODO: how do we check the index, if at all? @@ -260,7 +252,7 @@ def test_from_variables(variable_name, variable, options): @pytest.mark.parametrize(["old_variable", "new_variable"], variable_combinations) def test_replace(old_variable, new_variable): - grid = h3.H3Info(resolution=old_variable.attrs["resolution"]) + grid = h3.H3Info(level=old_variable.attrs["level"]) index = h3.H3Index( cell_ids=old_variable.data, dim=old_variable.dims[0], @@ -278,13 +270,13 @@ def test_replace(old_variable, new_variable): @pytest.mark.parametrize("max_width", [20, 50, 80, 120]) -@pytest.mark.parametrize("resolution", resolutions) -def test_repr_inline(resolution, max_width): - grid = h3.H3Info(resolution=resolution) +@pytest.mark.parametrize("level", levels) +def test_repr_inline(level, max_width): + grid = h3.H3Info(level=level) index = h3.H3Index(cell_ids=[0], dim="cells", grid_info=grid) actual = index._repr_inline_(max_width) - assert f"resolution={resolution}" in actual + assert f"level={level}" in actual # ignore max_width for now # assert len(actual) <= max_width diff --git a/xdggs/tests/test_healpix.py b/xdggs/tests/test_healpix.py index 5de2f7f..0e8d82c 100644 --- a/xdggs/tests/test_healpix.py +++ b/xdggs/tests/test_healpix.py @@ -22,8 +22,8 @@ # namespace class class strategies: - invalid_resolutions = st.integers(max_value=-1) | st.integers(min_value=30) - resolutions = st.integers(min_value=0, max_value=29) + invalid_levels = st.integers(max_value=-1) | st.integers(min_value=30) + levels = st.integers(min_value=0, max_value=29) # TODO: add back `"unique"` once that is supported indexing_schemes = st.sampled_from(["nested", "ring"]) invalid_indexing_schemes = st.text().filter(lambda x: x not in ["nested", "ring"]) @@ -33,17 +33,17 @@ class strategies: @classmethod def grid_mappings(cls): strategies = { - "resolution": cls.resolutions, - "nside": cls.resolutions.map(lambda n: 2**n), - "depth": cls.resolutions, - "level": cls.resolutions, - "order": cls.resolutions, + "resolution": cls.levels, + "nside": cls.levels.map(lambda n: 2**n), + "depth": cls.levels, + "level": cls.levels, + "order": cls.levels, "indexing_scheme": cls.indexing_schemes, "nest": st.booleans(), } names = { - "resolution": st.sampled_from( + "level": st.sampled_from( ["resolution", "nside", "depth", "level", "order"] ), "indexing_scheme": st.sampled_from(["indexing_scheme", "nest"]), @@ -70,31 +70,31 @@ def cell_ids(max_value=None, dtypes=None): options = st.just({}) def grids( - resolutions=resolutions, + levels=levels, indexing_schemes=indexing_schemes, ): return st.builds( healpix.HealpixInfo, - resolution=resolutions, + level=levels, indexing_scheme=indexing_schemes, ) @classmethod def grid_and_cell_ids( cls, - resolutions=resolutions, + levels=levels, indexing_schemes=indexing_schemes, dtypes=None, ): - cell_resolutions = st.shared(resolutions, key="common-resolutions") - grid_resolutions = st.shared(resolutions, key="common-resolutions") - cell_ids_ = cell_resolutions.flatmap( - lambda resolution: cls.cell_ids( - max_value=12 * 2 ** (resolution * 2) - 1, dtypes=dtypes + cell_levels = st.shared(levels, key="common-levels") + grid_levels = st.shared(levels, key="common-levels") + cell_ids_ = cell_levels.flatmap( + lambda level: cls.cell_ids( + max_value=12 * 2 ** (level * 2) - 1, dtypes=dtypes ) ) grids_ = cls.grids( - resolutions=grid_resolutions, + levels=grid_levels, indexing_schemes=indexing_schemes, ) @@ -109,7 +109,7 @@ def grid_and_cell_ids( np.array([3]), { "grid_name": "healpix", - "resolution": 0, + "level": 0, "indexing_scheme": "nested", }, ), @@ -118,7 +118,7 @@ def grid_and_cell_ids( np.array([3]), { "grid_name": "healpix", - "resolution": 0, + "level": 0, "indexing_scheme": "ring", }, ), @@ -127,7 +127,7 @@ def grid_and_cell_ids( np.array([5, 11, 21]), { "grid_name": "healpix", - "resolution": 1, + "level": 1, "indexing_scheme": "nested", }, ), @@ -136,7 +136,7 @@ def grid_and_cell_ids( np.array([54, 70, 82, 91]), { "grid_name": "healpix", - "resolution": 3, + "level": 3, "indexing_scheme": "nested", }, ), @@ -145,39 +145,37 @@ def grid_and_cell_ids( class TestHealpixInfo: - @given(strategies.invalid_resolutions) - def test_init_invalid_resolutions(self, resolution): + @given(strategies.invalid_levels) + def test_init_invalid_levels(self, level): with pytest.raises( - ValueError, match="resolution must be an integer in the range of" + ValueError, match="level must be an integer in the range of" ): - healpix.HealpixInfo(resolution=resolution) + healpix.HealpixInfo(level=level) @given(strategies.invalid_indexing_schemes) def test_init_invalid_indexing_scheme(self, indexing_scheme): with pytest.raises(ValueError, match="indexing scheme must be one of"): healpix.HealpixInfo( - resolution=0, + level=0, indexing_scheme=indexing_scheme, ) - @given(strategies.resolutions, strategies.indexing_schemes) - def test_init(self, resolution, indexing_scheme): - grid = healpix.HealpixInfo( - resolution=resolution, indexing_scheme=indexing_scheme - ) + @given(strategies.levels, strategies.indexing_schemes) + def test_init(self, level, indexing_scheme): + grid = healpix.HealpixInfo(level=level, indexing_scheme=indexing_scheme) - assert grid.resolution == resolution + assert grid.level == level assert grid.indexing_scheme == indexing_scheme - @given(strategies.resolutions) - def test_nside(self, resolution): - grid = healpix.HealpixInfo(resolution=resolution) + @given(strategies.levels) + def test_nside(self, level): + grid = healpix.HealpixInfo(level=level) - assert grid.nside == 2**resolution + assert grid.nside == 2**level @given(strategies.indexing_schemes) def test_nest(self, indexing_scheme): - grid = healpix.HealpixInfo(resolution=1, indexing_scheme=indexing_scheme) + grid = healpix.HealpixInfo(level=1, indexing_scheme=indexing_scheme) if indexing_scheme not in {"nested", "ring"}: with pytest.raises( ValueError, match="cannot convert indexing scheme .* to `nest`" @@ -193,23 +191,21 @@ def test_nest(self, indexing_scheme): def test_from_dict(self, mapping) -> None: healpix.HealpixInfo.from_dict(mapping) - @given(strategies.resolutions, strategies.indexing_schemes) - def test_to_dict(self, resolution, indexing_scheme) -> None: - grid = healpix.HealpixInfo( - resolution=resolution, indexing_scheme=indexing_scheme - ) + @given(strategies.levels, strategies.indexing_schemes) + def test_to_dict(self, level, indexing_scheme) -> None: + grid = healpix.HealpixInfo(level=level, indexing_scheme=indexing_scheme) actual = grid.to_dict() - assert set(actual) == {"grid_name", "resolution", "indexing_scheme"} + assert set(actual) == {"grid_name", "level", "indexing_scheme"} assert actual["grid_name"] == "healpix" - assert actual["resolution"] == resolution + assert actual["level"] == level assert actual["indexing_scheme"] == indexing_scheme - @given(strategies.resolutions, strategies.indexing_schemes) - def test_roundtrip(self, resolution, indexing_scheme): + @given(strategies.levels, strategies.indexing_schemes) + def test_roundtrip(self, level, indexing_scheme): mapping = { "grid_name": "healpix", - "resolution": resolution, + "level": level, "indexing_scheme": indexing_scheme, } @@ -222,7 +218,7 @@ def test_roundtrip(self, resolution, indexing_scheme): ["params", "cell_ids", "expected_coords"], ( ( - {"resolution": 0, "indexing_scheme": "nested"}, + {"level": 0, "indexing_scheme": "nested"}, np.array([2]), np.array( [ @@ -234,7 +230,7 @@ def test_roundtrip(self, resolution, indexing_scheme): ), ), ( - {"resolution": 2, "indexing_scheme": "ring"}, + {"level": 2, "indexing_scheme": "ring"}, np.array([12, 54]), np.array( [ @@ -254,7 +250,7 @@ def test_roundtrip(self, resolution, indexing_scheme): ), ), ( - {"resolution": 3, "indexing_scheme": "nested"}, + {"level": 3, "indexing_scheme": "nested"}, np.array([293, 17]), np.array( [ @@ -274,7 +270,7 @@ def test_roundtrip(self, resolution, indexing_scheme): ), ), ( - {"resolution": 2, "indexing_scheme": "nested"}, + {"level": 2, "indexing_scheme": "nested"}, np.array([79]), np.array( [ @@ -316,7 +312,7 @@ def test_cell_center_roundtrip(self, cell_ids, grid) -> None: np.testing.assert_equal(roundtripped, cell_ids) @pytest.mark.parametrize( - ["cell_ids", "resolution", "indexing_scheme", "expected"], + ["cell_ids", "level", "indexing_scheme", "expected"], ( pytest.param( np.array([3]), @@ -336,11 +332,9 @@ def test_cell_center_roundtrip(self, cell_ids, grid) -> None: ), ) def test_cell_ids2geographic( - self, cell_ids, resolution, indexing_scheme, expected + self, cell_ids, level, indexing_scheme, expected ) -> None: - grid = healpix.HealpixInfo( - resolution=resolution, indexing_scheme=indexing_scheme - ) + grid = healpix.HealpixInfo(level=level, indexing_scheme=indexing_scheme) actual_lon, actual_lat = grid.cell_ids2geographic(cell_ids) @@ -348,7 +342,7 @@ def test_cell_ids2geographic( np.testing.assert_allclose(actual_lat, expected[1]) @pytest.mark.parametrize( - ["cell_centers", "resolution", "indexing_scheme", "expected"], + ["cell_centers", "level", "indexing_scheme", "expected"], ( pytest.param( np.array([[315.0, 66.44353569089877]]), @@ -367,11 +361,9 @@ def test_cell_ids2geographic( ), ) def test_geographic2cell_ids( - self, cell_centers, resolution, indexing_scheme, expected + self, cell_centers, level, indexing_scheme, expected ) -> None: - grid = healpix.HealpixInfo( - resolution=resolution, indexing_scheme=indexing_scheme - ) + grid = healpix.HealpixInfo(level=level, indexing_scheme=indexing_scheme) actual = grid.geographic2cell_ids( lon=cell_centers[:, 0], lat=cell_centers[:, 1] @@ -385,34 +377,34 @@ def test_geographic2cell_ids( ( pytest.param( {"resolution": 10, "indexing_scheme": "nested"}, - {"resolution": 10, "indexing_scheme": "nested"}, + {"level": 10, "indexing_scheme": "nested"}, id="no_translation", ), pytest.param( { - "resolution": 10, + "level": 10, "indexing_scheme": "nested", "grid_name": "healpix", }, - {"resolution": 10, "indexing_scheme": "nested"}, + {"level": 10, "indexing_scheme": "nested"}, id="no_translation-grid_name", ), pytest.param( {"nside": 1024, "indexing_scheme": "nested"}, - {"resolution": 10, "indexing_scheme": "nested"}, + {"level": 10, "indexing_scheme": "nested"}, id="nside-alone", ), pytest.param( { "nside": 1024, - "resolution": 10, + "level": 10, "indexing_scheme": "nested", }, ExceptionGroup( "received multiple values for parameters", [ ValueError( - "Parameter resolution received multiple values: ['nside', 'resolution']" + "Parameter level received multiple values: ['level', 'nside']" ) ], ), @@ -420,7 +412,7 @@ def test_geographic2cell_ids( ), pytest.param( { - "resolution": 10, + "level": 10, "indexing_scheme": "nested", "nest": True, }, @@ -437,7 +429,7 @@ def test_geographic2cell_ids( pytest.param( { "nside": 1024, - "resolution": 10, + "level": 10, "indexing_scheme": "nested", "nest": True, }, @@ -448,7 +440,7 @@ def test_geographic2cell_ids( "Parameter indexing_scheme received multiple values: ['indexing_scheme', 'nest']" ), ValueError( - "Parameter resolution received multiple values: ['nside', 'resolution']" + "Parameter level received multiple values: ['level', 'nside']" ), ], ), @@ -488,14 +480,14 @@ def test_grid(self, grid): @pytest.mark.parametrize("variable", variables) @pytest.mark.parametrize("variable_name", variable_names) def test_from_variables(variable_name, variable, options) -> None: - expected_resolution = variable.attrs["resolution"] + expected_level = variable.attrs["level"] expected_scheme = variable.attrs["indexing_scheme"] variables = {variable_name: variable} index = healpix.HealpixIndex.from_variables(variables, options=options) - assert index._grid.resolution == expected_resolution + assert index._grid.level == expected_level assert index._grid.indexing_scheme == expected_scheme assert (index._dim,) == variable.dims @@ -524,13 +516,13 @@ def test_replace(old_variable, new_variable) -> None: @pytest.mark.parametrize("max_width", [20, 50, 80, 120]) -@pytest.mark.parametrize("resolution", [0, 1, 3]) -def test_repr_inline(resolution, max_width) -> None: - grid_info = healpix.HealpixInfo(resolution=resolution, indexing_scheme="nested") +@pytest.mark.parametrize("level", [0, 1, 3]) +def test_repr_inline(level, max_width) -> None: + grid_info = healpix.HealpixInfo(level=level, indexing_scheme="nested") index = healpix.HealpixIndex(cell_ids=[0], dim="cells", grid_info=grid_info) actual = index._repr_inline_(max_width) - assert f"nside={resolution}" in actual + assert f"nside={level}" in actual # ignore max_width for now # assert len(actual) <= max_width diff --git a/xdggs/tests/test_index.py b/xdggs/tests/test_index.py index fb0907f..2fa45a7 100644 --- a/xdggs/tests/test_index.py +++ b/xdggs/tests/test_index.py @@ -7,7 +7,7 @@ @pytest.fixture def dggs_example(): return xr.Dataset( - coords={"cell_ids": ("cells", [0, 1], {"grid_name": "test", "resolution": 2})} + coords={"cell_ids": ("cells", [0, 1], {"grid_name": "test", "level": 2})} )