Skip to content

Commit

Permalink
rename the primary grid parameter (#65)
Browse files Browse the repository at this point in the history
* rename `resolution` to `level` in the base grid info class

* rename for healpix

* rename for h3

* rename the remaining instances

* improve the description of the grid level

Co-authored-by: Benoit Bovy <[email protected]>

* deduplicate the parameter translation machinery

* add a fallback for `ExceptionGroup` on python 3.10

* rename in the test I missed before

* rename in the final test

* create documentation for `DGGSInfo.level`

---------

Co-authored-by: Benoit Bovy <[email protected]>
  • Loading branch information
keewis and benbovy authored Oct 30, 2024
1 parent 65c4189 commit d88c077
Show file tree
Hide file tree
Showing 8 changed files with 202 additions and 197 deletions.
6 changes: 3 additions & 3 deletions docs/api-hidden.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@
.. autosummary::
:toctree: generated

DGGSInfo.resolution
DGGSInfo.level

DGGSInfo.from_dict
DGGSInfo.to_dict
DGGSInfo.cell_boundaries
DGGSInfo.cell_ids2geographic
DGGSInfo.geographic2cell_ids

HealpixInfo.resolution
HealpixInfo.level
HealpixInfo.indexing_scheme
HealpixInfo.valid_parameters
HealpixInfo.nside
Expand All @@ -25,7 +25,7 @@
HealpixInfo.cell_ids2geographic
HealpixInfo.geographic2cell_ids

H3Info.resolution
H3Info.level
H3Info.valid_parameters

H3Info.from_dict
Expand Down
51 changes: 47 additions & 4 deletions xdggs/grid.py
Original file line number Diff line number Diff line change
@@ -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")


Expand All @@ -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()
Expand All @@ -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
32 changes: 20 additions & 12 deletions xdggs/h3.py
Original file line number Diff line number Diff line change
Expand Up @@ -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


Expand Down Expand Up @@ -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:
Expand All @@ -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]:
Expand All @@ -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
Expand Down Expand Up @@ -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"):
"""
Expand Down Expand Up @@ -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})"
78 changes: 24 additions & 54 deletions xdggs/healpix.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import json
import operator
from collections.abc import Mapping
from dataclasses import dataclass
from typing import Any, ClassVar, Literal, TypeVar
Expand All @@ -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
Expand Down Expand Up @@ -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.
Expand All @@ -110,20 +108,20 @@ class HealpixInfo(DGGSInfo):
(:doc:`healpy <healpy:index>`) 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(
Expand All @@ -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:
Expand Down Expand Up @@ -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]:
Expand All @@ -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,
}

Expand Down Expand Up @@ -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})"
12 changes: 6 additions & 6 deletions xdggs/tests/test_accessor.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
[3],
{
"grid_name": "healpix",
"resolution": 1,
"level": 1,
"indexing_scheme": "ring",
},
)
Expand All @@ -36,7 +36,7 @@
"cell_ids": (
"cells",
[0x832830FFFFFFFFF],
{"grid_name": "h3", "resolution": 3},
{"grid_name": "h3", "level": 3},
)
}
),
Expand Down Expand Up @@ -69,7 +69,7 @@ def test_cell_centers(obj, expected):
[3],
{
"grid_name": "healpix",
"resolution": 1,
"level": 1,
"indexing_scheme": "ring",
},
)
Expand All @@ -86,7 +86,7 @@ def test_cell_centers(obj, expected):
[3],
{
"grid_name": "healpix",
"resolution": 1,
"level": 1,
"indexing_scheme": "ring",
},
),
Expand All @@ -100,7 +100,7 @@ def test_cell_centers(obj, expected):
"cell_ids": (
"cells",
[0x832830FFFFFFFFF],
{"grid_name": "h3", "resolution": 3},
{"grid_name": "h3", "level": 3},
)
}
),
Expand All @@ -111,7 +111,7 @@ def test_cell_centers(obj, expected):
"cell_ids": (
"cells",
[0x832830FFFFFFFFF],
{"grid_name": "h3", "resolution": 3},
{"grid_name": "h3", "level": 3},
),
}
),
Expand Down
Loading

0 comments on commit d88c077

Please sign in to comment.