Skip to content

moczarr.dggs

xdggs integration: grid_name: "morton" in the public grid registry.

The layer-2 extra (pip install 'moczarr[xdggs]'): a :class:MortonInfo / :class:MortonIndex pair registered via register_dggs("morton"), so the ds.dggs accessor (sel_latlon, cell_centers, cell_boundaries, zoom_to) works on the morton coordinate of an open_hive result. Importing this module performs the registration; the core package never imports it (xdggs and its heavy deps stay optional).

Cell ids here are mortie's packed uint64 morton words — NOT bare HEALPix nested ids (those ride along as the unindexed cell_ids coordinate). A word encodes its own order, so transforms that read cells (mort2geo, mort2polygon, clip2order) never consult level; only the binning direction (geographic2cell_ids) does.

Two layers of order discipline follow from that (issue #8): :class:MortonInfo is a transform helper usable on any words — it stays level-tolerant on the read direction but takes ONE order per call (:func:_single_order); :class:MortonIndex binds level to an actual coordinate, so it validates once at construction that the domain is order-uniform and at level (:func:_check_domain), which is what makes "level is a frozen parameter" true of an index.

MortonIndex

Bases: DGGSIndex

DGGS index over packed uint64 morton words — pandas- or MOC-backed.

index_kind mirrors upstream HealpixIndex: "pandas" (default) materializes a PandasIndex; "moc" wraps the core :class:moczarr.moc_index.MortonMocIndex (interval-set domain, fabricated coordinate) so the ds.dggs accessor works on a lazy index. An xr.Index instance passes through as the inner index directly (the open_hive(index_kind="moc", decode=True) wrap — no rebuild).

The "pandas" default here is the decode-a-materialized-coordinate convention (upstream parity); :func:moczarr.open_hive itself defaults to index_kind="moc" and passes the kind through explicitly.

The domain contract — one order, and that order is grid_info.level — is checked HERE, once, whenever the words are handed in raw (:func:_check_domain; issue #8, PR #22 Question 2). That covers every read surface at once rather than guarding them one at a time, and it makes "level is a frozen parameter" literally true of an index. A passed xr.Index is trusted for its words (an already-built index, or the _replace a sel/isel makes from a validated one — revalidating there would put an O(n) scan on every selection); a :class:~moczarr.moc_index.MortonMocIndex still gets its O(1) cell_order cross-checked.

Source code in src/moczarr/dggs.py
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
@register_dggs(GRID_NAME)
class MortonIndex(DGGSIndex):
    """DGGS index over packed ``uint64`` morton words — pandas- or MOC-backed.

    ``index_kind`` mirrors upstream ``HealpixIndex``: ``"pandas"`` (default)
    materializes a ``PandasIndex``; ``"moc"`` wraps the core
    :class:`moczarr.moc_index.MortonMocIndex` (interval-set domain, fabricated
    coordinate) so the ``ds.dggs`` accessor works on a lazy index. An
    ``xr.Index`` instance passes through as the inner index directly (the
    ``open_hive(index_kind="moc", decode=True)`` wrap — no rebuild).

    The ``"pandas"`` default here is the decode-a-materialized-coordinate
    convention (upstream parity); :func:`moczarr.open_hive` itself defaults
    to ``index_kind="moc"`` and passes the kind through explicitly.

    The domain contract — one order, and that order is ``grid_info.level`` —
    is checked HERE, once, whenever the words are handed in raw
    (:func:`_check_domain`; issue #8, PR #22 Question 2). That covers every
    read surface at once rather than guarding them one at a time, and it makes
    "``level`` is a frozen parameter" literally true of an index. A passed
    ``xr.Index`` is trusted for its words (an already-built index, or the
    ``_replace`` a ``sel``/``isel`` makes from a validated one — revalidating
    there would put an ``O(n)`` scan on every selection); a
    :class:`~moczarr.moc_index.MortonMocIndex` still gets its ``O(1)``
    ``cell_order`` cross-checked.
    """

    _grid: DGGSInfo

    def __init__(
        self, cell_ids, dim: str, name: str, grid_info: DGGSInfo, index_kind: str = "pandas"
    ):
        if index_kind not in ("pandas", "moc"):
            raise ValueError(f"index_kind={index_kind!r}: expected 'pandas' or 'moc'")
        self._dim = dim
        self._name = name
        if isinstance(cell_ids, xr.Index):
            from moczarr.moc_index import MortonMocIndex

            if isinstance(cell_ids, MortonMocIndex) and cell_ids.level != grid_info.level:
                raise ValueError(
                    f"morton domain is at order {cell_ids.level}, not the declared level "
                    f"{grid_info.level}: an index binds level to its coordinate. Pass "
                    f"level={cell_ids.level}, or omit level to read it from the store."
                )
            self._index = cell_ids
        elif index_kind == "pandas":
            from xarray.indexes import PandasIndex

            _check_domain(_words(cell_ids), grid_info.level)
            self._index = PandasIndex(cell_ids, dim)
            self._index.index.name = name
        else:
            from moczarr.moc_index import MortonMocIndex
            from moczarr.ranges import MortonRanges

            words = _words(cell_ids)
            _check_domain(words, grid_info.level)
            ranges = MortonRanges.from_cell_words(words, grid_info.level)
            self._index = MortonMocIndex(ranges, dim=dim, name=name)
        self._kind = index_kind
        self._grid = grid_info

    def values(self):
        if self._kind == "moc":
            return self._index.ranges.fabricate()
        return self._index.index.values

    # xdggs 0.7's DGGSIndex base reads ``name``/``size`` off a PandasIndex
    # (``self._index.index``), and its ``sel`` calls ``self.name``. Answered
    # here for both kinds, as upstream HealpixIndex does for its moc kind.
    @property
    def name(self) -> str:
        return self._name

    @property
    def size(self) -> int:
        if self._kind == "moc":
            return self._index.size
        return self._index.index.size

    @classmethod
    def from_variables(cls, variables, *, options) -> "MortonIndex":
        name, var, dim = _extract_cell_id_variable(variables)
        options = dict(options or {})
        index_kind = options.pop("index_kind", "pandas")
        grid_info = MortonInfo.from_dict(dict(var.attrs) | options)
        return cls(var.data, dim, name, grid_info, index_kind=index_kind)

    @property
    def grid_info(self) -> MortonInfo:
        return self._grid

    def _replace(self, new_index) -> "MortonIndex":
        return type(self)(new_index, self._dim, self._name, self._grid, index_kind=self._kind)

    def __repr__(self):
        return f"<MortonIndex(level={self._grid.level}, kind={self._kind})>"

    def _repr_inline_(self, max_width: int):
        return f"MortonIndex(level={self._grid.level}, kind={self._kind})"

MortonInfo dataclass

Bases: DGGSInfo

Grid info for packed-morton HEALPix cells (mortie encoding).

level is the HEALPix order of the cells — cell_order in the morton-hive manifest (:func:decode translates one to the other). The packing supports orders 0..29 (2 bits per level in a uint64 word).

Source code in src/moczarr/dggs.py
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
@dataclass(frozen=True)
class MortonInfo(DGGSInfo):
    """Grid info for packed-morton HEALPix cells (mortie encoding).

    ``level`` is the HEALPix order of the cells — ``cell_order`` in the
    morton-hive manifest (:func:`decode` translates one to the other). The
    packing supports orders 0..29 (2 bits per level in a ``uint64`` word).
    """

    level: int

    valid_parameters: ClassVar[dict[str, Any]] = {"level": range(30)}

    def __post_init__(self):
        if self.level not in self.valid_parameters["level"]:
            raise ValueError("level must be an integer between 0 and 29")

    @classmethod
    def from_dict(cls, mapping: dict[str, Any]) -> "MortonInfo":
        """Construct from grid attrs; accepts ``level`` or manifest ``cell_order``."""
        translations = {
            "level": ("level", int),
            "cell_order": ("level", int),
        }
        params = translate_parameters(mapping, translations)
        return cls(**params)

    def to_dict(self) -> dict[str, Any]:
        """The normalized grid parameters (the xdggs coordinate-attrs form)."""
        return {"grid_name": GRID_NAME, "level": self.level}

    def cell_ids2geographic(self, cell_ids) -> tuple[np.ndarray, np.ndarray]:
        """``(lon, lat)`` cell centers in degrees, lon normalized to [-180, 180).

        ``mort2geo`` reads each word's own order — ``level`` is not consulted —
        and returns lon in [0, 360); the xdggs surface is [-180, 180).

        Mixed-order ``cell_ids`` reject here as this *surface's* one-order-
        per-call contract, not as a kernel limitation and not as a claim about
        ``level`` (issue #8): mortie 0.9.1+ converts mixed orders natively
        (mortie#116), and this method deliberately ignores ``level``, so
        citing a "frozen level" would describe an invariant it does not
        enforce. The domain-wide contract (one order, *and* that order is
        ``level``) is enforced once in :class:`MortonIndex` instead — see
        :func:`_check_domain`. Decode each level separately.
        """
        from mortie import mort2geo

        words = _words(cell_ids)
        _single_order(words, "cell_ids2geographic")
        lat, lon = mort2geo(words)
        lon = (np.atleast_1d(np.asarray(lon, dtype=np.float64)) + 180.0) % 360.0 - 180.0
        return lon, np.atleast_1d(np.asarray(lat, dtype=np.float64))

    def geographic2cell_ids(self, lon, lat) -> np.ndarray:
        """Bin points (degrees) to the morton words containing them at ``level``."""
        from mortie import geo2mort

        return geo2mort(
            np.atleast_1d(np.asarray(lat, dtype=np.float64)),
            np.atleast_1d(np.asarray(lon, dtype=np.float64)),
            order=self.level,
        )

    def cell_boundaries(self, cell_ids, backend="shapely") -> np.ndarray:
        """Cell boundary polygons — ``backend="shapely"`` (default) or ``"geoarrow"``.

        Mirrors ``xdggs.healpix``'s ``cell_boundaries`` contract by dispatching
        to its backend builders: ``"shapely"`` returns an array of
        :py:class:`shapely.Polygon` (what the accessor consumes);
        ``"geoarrow"`` returns a geoarrow polygon array tagged with spherical
        edges (the lonboard-fast path). ``mort2polygon`` returns closed
        ``[lat, lon]`` rings (4 unique vertices plus the repeated first) — and
        the bare ring (not a one-element list) for a single cell, re-wrapped
        here. The four vertices are recentered around the prime meridian
        before ring construction so dateline-crossing and polar cells stay
        well-formed (same ``center_around_prime_meridian`` xdggs applies).

        Mixed-order input rejects, same one-order-per-call contract as
        :meth:`cell_ids2geographic` (issue #8 fold): ``mort2polygon`` would
        happily emit rings at several orders, but a boundary set that silently
        mixes cell sizes is the bypass that let a mixed-order coordinate reach
        the accessor at all.
        """
        from mortie import mort2polygon

        backends = {"shapely": polygons_shapely, "geoarrow": polygons_geoarrow}
        backend_func = backends.get(backend)
        if backend_func is None:
            raise ValueError(f"invalid backend: {backend!r} (one of {sorted(backends)})")
        words = _words(cell_ids)
        _single_order(words, "cell_boundaries")
        rings = mort2polygon(words)
        if np.asarray(cell_ids).size == 1:
            rings = [rings]
        if len(rings) == 0:
            if backend == "geoarrow":
                # arro3's list_array rejects 0-length dimensions; upstream
                # xdggs shares the limitation, so fail pointedly.
                raise ValueError("backend='geoarrow' does not support zero cells (arro3 limit)")
            import shapely

            return shapely.polygons(shapely.linearrings(np.empty((0, 4, 2))))
        # Drop the closing vertex -> (n, 4, 2) rings of [lat, lon].
        verts = np.stack([np.asarray(ring, dtype=np.float64)[:-1] for ring in rings])
        lat = verts[..., 0]
        lon = center_around_prime_meridian(verts[..., 1], lat)
        return backend_func(np.stack((lon, lat), axis=-1))

    def zoom_to(self, cell_ids, level: int) -> np.ndarray:
        """Cells at another order — the xdggs zoom semantics.

        Coarser: one parent per input cell (``clip2order``; length and
        duplicates preserved). Finer: a ``(n, 4**(level - order))`` children
        array (extra trailing dimension, like the healpix backend). Same
        order: the cells unchanged.

        ``order`` is the order the WORDS carry, not ``self.level`` (issue #8
        fold). The kernels are per-word — ``clip2order`` truncates each word
        from its own order and ``generate_morton_children`` fans out from its
        own order — so keying the branch on ``self.level`` disagreed with the
        kernel for words that are simply off-level: a coarsening request could
        land in the finer branch (mortie then rejecting
        ``target_order < parent_order``) and the trailing dimension could
        count from the wrong base. Mixed-order input rejects up front, same
        one-order-per-call contract as :meth:`cell_ids2geographic`: there is no
        single trailing dimension to give it. An index-backed call always has
        ``order == self.level`` (the domain contract, :func:`_check_domain`);
        an empty array carries no order, so ``self.level`` is the only
        reference left and the finer path counts from it.
        """
        if level not in self.valid_parameters["level"]:
            raise ValueError("level must be an integer between 0 and 29")
        words = _words(cell_ids)
        order = _single_order(words, "zoom_to")
        if order is None:  # empty: no words to read an order from
            order = self.level
        if level == order:
            return words
        from mortie import clip2order, generate_morton_children

        if level < order:
            return np.asarray(clip2order(level, words), dtype=np.uint64)
        # Finer: ONE batched ``generate_morton_children`` call for every
        # parent. The O(n) Python loop this replaces predated mortie 1.0,
        # which folded the batch kernel (``children_of``) into
        # ``generate_morton_children`` (espg/mortie#187) — the array arm
        # returns the dense ``(n, 4**(level - order))`` block, row ``i`` the
        # children of ``words[i]``, which is exactly what the loop stacked.
        # Its preconditions are already met here: ``_single_order`` rejects
        # mixed orders, and ``level == order`` / ``level < order`` returned
        # above, so every word is strictly coarser than ``level``.
        # Empty input mirrors the coarser ``(0,)`` with a clean ``(0, 4**diff)``
        # rather than the kernel's ``(0, 1)``.
        if words.size == 0:
            return np.empty((0, 4 ** (level - order)), dtype=np.uint64)
        return np.asarray(generate_morton_children(words, level), dtype=np.uint64)

cell_boundaries(cell_ids, backend='shapely')

Cell boundary polygons — backend="shapely" (default) or "geoarrow".

Mirrors xdggs.healpix's cell_boundaries contract by dispatching to its backend builders: "shapely" returns an array of :py:class:shapely.Polygon (what the accessor consumes); "geoarrow" returns a geoarrow polygon array tagged with spherical edges (the lonboard-fast path). mort2polygon returns closed [lat, lon] rings (4 unique vertices plus the repeated first) — and the bare ring (not a one-element list) for a single cell, re-wrapped here. The four vertices are recentered around the prime meridian before ring construction so dateline-crossing and polar cells stay well-formed (same center_around_prime_meridian xdggs applies).

Mixed-order input rejects, same one-order-per-call contract as :meth:cell_ids2geographic (issue #8 fold): mort2polygon would happily emit rings at several orders, but a boundary set that silently mixes cell sizes is the bypass that let a mixed-order coordinate reach the accessor at all.

Source code in src/moczarr/dggs.py
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
def cell_boundaries(self, cell_ids, backend="shapely") -> np.ndarray:
    """Cell boundary polygons — ``backend="shapely"`` (default) or ``"geoarrow"``.

    Mirrors ``xdggs.healpix``'s ``cell_boundaries`` contract by dispatching
    to its backend builders: ``"shapely"`` returns an array of
    :py:class:`shapely.Polygon` (what the accessor consumes);
    ``"geoarrow"`` returns a geoarrow polygon array tagged with spherical
    edges (the lonboard-fast path). ``mort2polygon`` returns closed
    ``[lat, lon]`` rings (4 unique vertices plus the repeated first) — and
    the bare ring (not a one-element list) for a single cell, re-wrapped
    here. The four vertices are recentered around the prime meridian
    before ring construction so dateline-crossing and polar cells stay
    well-formed (same ``center_around_prime_meridian`` xdggs applies).

    Mixed-order input rejects, same one-order-per-call contract as
    :meth:`cell_ids2geographic` (issue #8 fold): ``mort2polygon`` would
    happily emit rings at several orders, but a boundary set that silently
    mixes cell sizes is the bypass that let a mixed-order coordinate reach
    the accessor at all.
    """
    from mortie import mort2polygon

    backends = {"shapely": polygons_shapely, "geoarrow": polygons_geoarrow}
    backend_func = backends.get(backend)
    if backend_func is None:
        raise ValueError(f"invalid backend: {backend!r} (one of {sorted(backends)})")
    words = _words(cell_ids)
    _single_order(words, "cell_boundaries")
    rings = mort2polygon(words)
    if np.asarray(cell_ids).size == 1:
        rings = [rings]
    if len(rings) == 0:
        if backend == "geoarrow":
            # arro3's list_array rejects 0-length dimensions; upstream
            # xdggs shares the limitation, so fail pointedly.
            raise ValueError("backend='geoarrow' does not support zero cells (arro3 limit)")
        import shapely

        return shapely.polygons(shapely.linearrings(np.empty((0, 4, 2))))
    # Drop the closing vertex -> (n, 4, 2) rings of [lat, lon].
    verts = np.stack([np.asarray(ring, dtype=np.float64)[:-1] for ring in rings])
    lat = verts[..., 0]
    lon = center_around_prime_meridian(verts[..., 1], lat)
    return backend_func(np.stack((lon, lat), axis=-1))

cell_ids2geographic(cell_ids)

(lon, lat) cell centers in degrees, lon normalized to [-180, 180).

mort2geo reads each word's own order — level is not consulted — and returns lon in [0, 360); the xdggs surface is [-180, 180).

Mixed-order cell_ids reject here as this surface's one-order- per-call contract, not as a kernel limitation and not as a claim about level (issue #8): mortie 0.9.1+ converts mixed orders natively (mortie#116), and this method deliberately ignores level, so citing a "frozen level" would describe an invariant it does not enforce. The domain-wide contract (one order, and that order is level) is enforced once in :class:MortonIndex instead — see :func:_check_domain. Decode each level separately.

Source code in src/moczarr/dggs.py
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
def cell_ids2geographic(self, cell_ids) -> tuple[np.ndarray, np.ndarray]:
    """``(lon, lat)`` cell centers in degrees, lon normalized to [-180, 180).

    ``mort2geo`` reads each word's own order — ``level`` is not consulted —
    and returns lon in [0, 360); the xdggs surface is [-180, 180).

    Mixed-order ``cell_ids`` reject here as this *surface's* one-order-
    per-call contract, not as a kernel limitation and not as a claim about
    ``level`` (issue #8): mortie 0.9.1+ converts mixed orders natively
    (mortie#116), and this method deliberately ignores ``level``, so
    citing a "frozen level" would describe an invariant it does not
    enforce. The domain-wide contract (one order, *and* that order is
    ``level``) is enforced once in :class:`MortonIndex` instead — see
    :func:`_check_domain`. Decode each level separately.
    """
    from mortie import mort2geo

    words = _words(cell_ids)
    _single_order(words, "cell_ids2geographic")
    lat, lon = mort2geo(words)
    lon = (np.atleast_1d(np.asarray(lon, dtype=np.float64)) + 180.0) % 360.0 - 180.0
    return lon, np.atleast_1d(np.asarray(lat, dtype=np.float64))

from_dict(mapping) classmethod

Construct from grid attrs; accepts level or manifest cell_order.

Source code in src/moczarr/dggs.py
186
187
188
189
190
191
192
193
194
@classmethod
def from_dict(cls, mapping: dict[str, Any]) -> "MortonInfo":
    """Construct from grid attrs; accepts ``level`` or manifest ``cell_order``."""
    translations = {
        "level": ("level", int),
        "cell_order": ("level", int),
    }
    params = translate_parameters(mapping, translations)
    return cls(**params)

geographic2cell_ids(lon, lat)

Bin points (degrees) to the morton words containing them at level.

Source code in src/moczarr/dggs.py
223
224
225
226
227
228
229
230
231
def geographic2cell_ids(self, lon, lat) -> np.ndarray:
    """Bin points (degrees) to the morton words containing them at ``level``."""
    from mortie import geo2mort

    return geo2mort(
        np.atleast_1d(np.asarray(lat, dtype=np.float64)),
        np.atleast_1d(np.asarray(lon, dtype=np.float64)),
        order=self.level,
    )

to_dict()

The normalized grid parameters (the xdggs coordinate-attrs form).

Source code in src/moczarr/dggs.py
196
197
198
def to_dict(self) -> dict[str, Any]:
    """The normalized grid parameters (the xdggs coordinate-attrs form)."""
    return {"grid_name": GRID_NAME, "level": self.level}

zoom_to(cell_ids, level)

Cells at another order — the xdggs zoom semantics.

Coarser: one parent per input cell (clip2order; length and duplicates preserved). Finer: a (n, 4**(level - order)) children array (extra trailing dimension, like the healpix backend). Same order: the cells unchanged.

order is the order the WORDS carry, not self.level (issue #8 fold). The kernels are per-word — clip2order truncates each word from its own order and generate_morton_children fans out from its own order — so keying the branch on self.level disagreed with the kernel for words that are simply off-level: a coarsening request could land in the finer branch (mortie then rejecting target_order < parent_order) and the trailing dimension could count from the wrong base. Mixed-order input rejects up front, same one-order-per-call contract as :meth:cell_ids2geographic: there is no single trailing dimension to give it. An index-backed call always has order == self.level (the domain contract, :func:_check_domain); an empty array carries no order, so self.level is the only reference left and the finer path counts from it.

Source code in src/moczarr/dggs.py
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
def zoom_to(self, cell_ids, level: int) -> np.ndarray:
    """Cells at another order — the xdggs zoom semantics.

    Coarser: one parent per input cell (``clip2order``; length and
    duplicates preserved). Finer: a ``(n, 4**(level - order))`` children
    array (extra trailing dimension, like the healpix backend). Same
    order: the cells unchanged.

    ``order`` is the order the WORDS carry, not ``self.level`` (issue #8
    fold). The kernels are per-word — ``clip2order`` truncates each word
    from its own order and ``generate_morton_children`` fans out from its
    own order — so keying the branch on ``self.level`` disagreed with the
    kernel for words that are simply off-level: a coarsening request could
    land in the finer branch (mortie then rejecting
    ``target_order < parent_order``) and the trailing dimension could
    count from the wrong base. Mixed-order input rejects up front, same
    one-order-per-call contract as :meth:`cell_ids2geographic`: there is no
    single trailing dimension to give it. An index-backed call always has
    ``order == self.level`` (the domain contract, :func:`_check_domain`);
    an empty array carries no order, so ``self.level`` is the only
    reference left and the finer path counts from it.
    """
    if level not in self.valid_parameters["level"]:
        raise ValueError("level must be an integer between 0 and 29")
    words = _words(cell_ids)
    order = _single_order(words, "zoom_to")
    if order is None:  # empty: no words to read an order from
        order = self.level
    if level == order:
        return words
    from mortie import clip2order, generate_morton_children

    if level < order:
        return np.asarray(clip2order(level, words), dtype=np.uint64)
    # Finer: ONE batched ``generate_morton_children`` call for every
    # parent. The O(n) Python loop this replaces predated mortie 1.0,
    # which folded the batch kernel (``children_of``) into
    # ``generate_morton_children`` (espg/mortie#187) — the array arm
    # returns the dense ``(n, 4**(level - order))`` block, row ``i`` the
    # children of ``words[i]``, which is exactly what the loop stacked.
    # Its preconditions are already met here: ``_single_order`` rejects
    # mixed orders, and ``level == order`` / ``level < order`` returned
    # above, so every word is strictly coarser than ``level``.
    # Empty input mirrors the coarser ``(0,)`` with a clean ``(0, 4**diff)``
    # rather than the kernel's ``(0, 1)``.
    if words.size == 0:
        return np.empty((0, 4 ** (level - order)), dtype=np.uint64)
    return np.asarray(generate_morton_children(words, level), dtype=np.uint64)

decode(ds, name='morton', index_kind='pandas', **options)

Assign a :class:MortonIndex to the name coordinate.

The xdggs-convention decode pattern: build the index off the coord's grid attrs (merged with **options, e.g. level=...), assign it via xr.Coordinates.from_xindex. Missing attrs are filled in — grid_name is always "morton", and level falls back to the §5 convention block's refinement_level (a dataset carrying the writer-flip dggs/zarr_conventions attrs — mortie spec §5, recognized by name: "morton" and/or the permanent convention UUID), then to ds.attrs["morton_hive"]["cell_order"] (the open_hive manifest summary), then to the order packed in the first word itself. The retired name: "healpix" + indexing_scheme: "morton" attrs shape hard-rejects with a diagnostic (§5: the scheme-blind misread hazard). The filled attrs land on the returned coord, so xdggs.decode round-trips. xdggs's own zarr-convention path (xdggs.decode(ds, convention="zarr", name="morton")) reaches the same registered grid directly off the §5 block.

index_kind="moc" backs the index with the lazy :class:~moczarr.moc_index.MortonMocIndex: a coordinate already indexed by one (the open_hive(index_kind="moc") result) is wrapped as-is; otherwise the materialized words run-detect into intervals.

Source code in src/moczarr/dggs.py
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
def decode(
    ds: xr.Dataset, name: str = "morton", index_kind: str = "pandas", **options
) -> xr.Dataset:
    """Assign a :class:`MortonIndex` to the ``name`` coordinate.

    The xdggs-convention decode pattern: build the index off the coord's grid
    attrs (merged with ``**options``, e.g. ``level=...``), assign it via
    ``xr.Coordinates.from_xindex``. Missing attrs are filled in —
    ``grid_name`` is always ``"morton"``, and ``level`` falls back to the §5
    convention block's ``refinement_level`` (a dataset carrying the
    writer-flip ``dggs``/``zarr_conventions`` attrs — mortie spec §5,
    recognized by ``name: "morton"`` and/or the permanent convention UUID),
    then to ``ds.attrs["morton_hive"]["cell_order"]`` (the ``open_hive``
    manifest summary), then to the order packed in the first word itself.
    The retired ``name: "healpix"`` + ``indexing_scheme: "morton"`` attrs
    shape hard-rejects with a diagnostic (§5: the scheme-blind misread
    hazard). The filled attrs land on the returned coord, so
    ``xdggs.decode`` round-trips. xdggs's own zarr-convention path
    (``xdggs.decode(ds, convention="zarr", name="morton")``) reaches the
    same registered grid directly off the §5 block.

    ``index_kind="moc"`` backs the index with the lazy
    :class:`~moczarr.moc_index.MortonMocIndex`: a coordinate already indexed
    by one (the ``open_hive(index_kind="moc")`` result) is wrapped as-is;
    otherwise the materialized words run-detect into intervals.
    """
    try:
        var = ds[name].variable
    except KeyError:
        raise ValueError(f"no {name!r} coordinate to index at") from None
    attrs = dict(var.attrs)
    attrs.setdefault("grid_name", GRID_NAME)
    if attrs["grid_name"] != GRID_NAME:
        raise ValueError(f"{name!r} carries grid_name {attrs['grid_name']!r}, not {GRID_NAME!r}")
    # The §5 convention block (writer-flip attrs shape): recognized whenever
    # present — and the retired healpix+morton shape hard-rejects here even
    # when the level comes from elsewhere.
    block = _morton_convention_block(ds)
    if "level" not in attrs and "level" not in options and "cell_order" not in attrs:
        hive = ds.attrs.get("morton_hive") or {}
        if block is not None and "refinement_level" in block:
            attrs["level"] = int(block["refinement_level"])
        elif "cell_order" in hive:
            attrs["level"] = int(hive["cell_order"])
        else:
            from mortie import infer_order_from_morton

            attrs["level"] = int(infer_order_from_morton(_words(var.data[:1])))
    var = var.copy(deep=False)
    var.attrs = attrs
    existing = ds.xindexes.get(name) if index_kind == "moc" else None
    if existing is not None:
        from moczarr.moc_index import MortonMocIndex

        if isinstance(existing, MortonMocIndex):
            grid_info = MortonInfo.from_dict(attrs | dict(options))
            index: MortonIndex = MortonIndex(
                existing, ds[name].dims[0], name, grid_info, index_kind="moc"
            )
        else:
            existing = None
    if existing is None:
        index = MortonIndex.from_variables(
            {name: var}, options=dict(options) | {"index_kind": index_kind}
        )
    decoded = ds.assign_coords(xr.Coordinates.from_xindex(index))
    decoded[name].attrs.update(index.grid_info.to_dict())
    return decoded