Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

rename the primary grid parameter #65

Merged
merged 13 commits into from
Oct 30, 2024
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