Skip to content
Draft
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions pixi.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 11 additions & 0 deletions xdggs/accessor.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,17 @@ def sel_latlon(
}
return self._obj.sel(cell_indexers)

def query(self, geom):
index, indexer = self._index.query(geom)

coords = xr.Coordinates.from_xindex(index)

return (
self._obj.drop_indexes(self._name)
.isel({self._index._dim: indexer})
.assign_coords(coords)
)

def assign_latlon_coords(self) -> xr.Dataset | xr.DataArray:
"""Return a new Dataset or DataArray with new "latitude" and "longitude"
coordinates representing the grid cell centers."""
Expand Down
3 changes: 3 additions & 0 deletions xdggs/grid.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ def geographic2cell_ids(self, lon, lat):
def cell_boundaries(self, cell_ids, backend="shapely"):
raise NotImplementedError()

def rasterize_geometry(self, geom, *, mode=None):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The name rasterize_geometry is misleading here. This function basically converts an arbitrary geometry to cell ids so I would suggest a name like geometry2cells_ids that is consistent with the other methods of DGGSInfo.

Adding type hints would help too. Is this method intended to support multiple geometries in the future?

The name mode is a bit too generic. Shall we use containment_mode like h3ronpy?

Copy link
Member Author

@keewis keewis Sep 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unless you assume "raster" to equal 2D image (I understand "rasterize" as "to discretize onto a grid") I don't see how the name would be misleading. However, you have a point with the consistency argument.

Adding type hints would help too.

We currently don't have type hints on any of the methods, so we'd have to figure out common type hints, first. Maybe in a new PR?

Is this method intended to support multiple geometries in the future?

Potentially, although I'd say querying by a single geometry covers at least 90% of the use-cases for query (and I'm currently not planning to vectorize it).

The name mode is a bit too generic. Shall we use containment_mode like h3ronpy?

Indeed, but containment_mode is also quite the mouthful. I'd argue that in the context of rasterization / conversion to cell ids the word mode is unambiguous, but if that doesn't convince you we can just remove the word mode and use containment="center" or containment="overlap" or something similar, like contained=....

raise NotImplementedError()


def translate_parameters(mapping, translations):
def translate(name, value):
Expand Down
20 changes: 20 additions & 0 deletions xdggs/h3.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,19 @@

try:
from h3ronpy.vector import (
ContainmentMode,
cells_to_coordinates,
cells_to_wkb_polygons,
coordinates_to_cells,
geometry_to_cells,
)
except ImportError:
from h3ronpy.arrow.vector import (
ContainmentMode,
cells_to_coordinates,
cells_to_wkb_polygons,
coordinates_to_cells,
geometry_to_cells,
)
from xarray.indexes import PandasIndex

Expand Down Expand Up @@ -201,6 +205,22 @@ def cell_boundaries(self, cell_ids, backend="shapely"):
raise ValueError(f"invalid backend: {backend!r}")
return backend_func(wkb)

def rasterize_geometry(self, geom, *, mode="contains_centroid"):
modes = {
"contains_centroid": ContainmentMode.ContainsCentroid,
"contains_boundary": ContainmentMode.ContainsBoundary,
"covers": ContainmentMode.Covers,
"intersects_boundary": ContainmentMode.IntersectsBoundary,
}
containment_mode = modes.get(mode)
if containment_mode is None:
raise ValueError(
f"invalid mode: {mode}."
f" Must be one of [{', '.join(repr(m) for m in modes)}]"
)

return geometry_to_cells(geom, resolution=self.level)


@register_dggs("h3")
class H3Index(DGGSIndex):
Expand Down
23 changes: 23 additions & 0 deletions xdggs/healpix.py
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,29 @@ def cell_boundaries(self, cell_ids: Any, backend="shapely") -> np.ndarray:

return backend_func(vertices)

def rasterize_geometry(self, geom, *, mode=None):
from astropy.coordinates import Latitude, Longitude

if self.indexing_scheme != "nested":
raise ValueError(
"rasterizing geometries is only supported for the 'nested' scheme"
)

if geom.geom_type != "Polygon":
raise NotImplementedError(
f"geometries of type {geom.geom_type!r} are not supported, yet"
)

coords = np.asarray(geom.exterior.coords)
lon_ = Longitude(coords[:, 0], unit="deg")
lat_ = Latitude(coords[:, 1], unit="deg")

ipix, _, _ = cdshealpix.nested.polygon_search(
lon_, lat_, depth=self.level, flat=True
)

return ipix


@register_dggs("healpix")
class HealpixIndex(DGGSIndex):
Expand Down
11 changes: 11 additions & 0 deletions xdggs/index.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from typing import Any, Union

import numpy as np
import pandas as pd
import xarray as xr
from xarray.indexes import Index, PandasIndex

Expand Down Expand Up @@ -89,6 +90,16 @@ def sel(self, labels, method=None, tolerance=None):
raise ValueError("finding nearest grid cell has no meaning")
return self._pd_index.sel(labels, method=method, tolerance=tolerance)

def query(self, geom):
rasterized = self._grid.rasterize_geometry(geom)

geometry_index = pd.Index(rasterized)
new_index, _, indexer = geometry_index.join(
self._pd_index.index, how="inner", return_indexers=True
)

return self._replace(new_index), indexer

def _replace(self, new_pd_index: PandasIndex):
raise NotImplementedError()

Expand Down
Loading