Skip to content

mortie.geometry

Lazy WKB/WKT geometry codec. The geometry backend (shapely>=2 preferred, spherely accepted) is imported on first use, so numpy stays the only runtime dependency.

from_wkb needs no backend at all (issue #157): mortie parses WKB itself, in Rust, and covers the rings directly. A backend is still required for WKT ingest (there is no Rust WKT parser) and for the whole emit direction, which hands back a backend geometry object by definition. from_wkb is polymorphic (issue

187): one blob, a sequence of blobs, or a packed binary column via its

offsets= keyword — the batch kernel behind it lives in mortie.batch (issue #170) as a private function.

The spherical outline machinery behind to_geometry(dissolve=True) lives in mortie.dissolve (issue #159), mirroring src_rust/src/dissolve.rs, and the backend gate plus the codec quartet live in mortie.codec. Neither has any public member, so neither has a page of its own; the functions below are still where the whole ingest/emit path is documented.

WKB/WKT geometry ingest and emit for mortie (issue #71).

The runtime stays numpy-only: :mod:mortie.codec imports a geometry backend (shapely>=2 preferred, spherely accepted) lazily, and this module uses it only as a codec — bytes/text ↔ ring coordinate arrays. All spherical correctness (antimeridian / pole handling) stays mortie's own job; the backend is never asked for spatial predicates. Importing :mod:mortie succeeds with neither backend installed; the geometry functions raise a clear :class:ImportError when first touched without one (the same lazy-gate pattern :mod:mortie.arrow uses for pyarrow).

WKB ingest needs no backend at all (issue #157): :func:from_wkb parses the bytes with mortie's own Rust reader and feeds the rings straight to the coverage kernels. What still needs a backend is WKT ingest (there is no Rust WKT parser) and the whole emit direction — :func:to_geometry and friends hand back a geometry object, which is a backend object by definition.

Coordinate convention: WKB/WKT store (x, y) = (lon, lat) degrees (EPSG:4326). mortie's coverage entry points take (lats, lons), so this module flips the axes at the boundary and works in degrees throughout.

from_wkb(data, order=18, moc=None, normalize=True, tolerance=None, max_cells=None, *, latitude='authalic', offsets=None)

Cover WKB (or EWKB) geometry with morton indices -- no backend needed.

Blobs are parsed by mortie's own Rust WKB reader (issue #157) and their rings go straight to the coverage kernels, so this works with neither shapely nor spherely installed — mortie's runtime really is numpy-only on this path. The cover is identical to what the backend-decoded path produced: same rings, same descent. (:func:from_wkt still decodes via a backend — #157 scoped the Rust parser to WKB.)

Batch vectorized (issue #187), three forms selected by the input — never by a flag:

  • offsets= given — a packed column: data is one uint8 buffer of every blob's bytes and offsets are the arrow binary-column list offsets. Zero-copy: the kernel reads views of the caller's buffer.
  • list / tuple / object-ndarray — a sequence batch: each entry is coerced exactly as the scalar form accepts (the pandas case, via series.to_numpy()).
  • anything else — one blob (bytes, hex str, bytearray, memoryview, or a uint8 array of the blob's bytes).

Those three rules are the whole dispatch — it does not sniff any wider — so a container the retired from_wkbs merely iterated over, but that this rule does not name, is a TypeError: materialize a pandas Series, a generator or a bytes-dtype (S) array into one of the batch spellings first (series.to_numpy(), list(gen), arr.astype(object)), or pack it into the offsets= column form.

Both batch forms return the ragged (values, out_offsets) MOC pair, with result i byte-identical to the scalar form on blob i with moc=True; a whole column crosses the Python/Rust boundary once and is parsed and covered in parallel, in byte-capped chunks (the memory posture documented on the kernel, :func:mortie.batch._from_wkbs).

moc is a tri-state: the default None means the flat cover for one blob (the historical from_wkb default) and the ragged MOC pair for a batch (the historical from_wkbs behaviour). An explicit moc=True works in every form; an explicit moc=False on a batch raises — there is no ragged flat-cover kernel to answer with (densify the MOC pair via moc_to_order(values, order, offsets=out_offsets) instead).

Parameters:

Name Type Description Default
data bytes, str, buffer, or sequence

One WKB/EWKB geometry, a sequence of them, or a packed column (with offsets). Per blob: both byte orders, the ISO and EWKB dimension spellings (Z/M are dropped — mortie is 2-D lon/lat), and an EWKB SRID prefix (stripped; mortie's contract is always EPSG:4326) are accepted. A hex string of a blob is accepted too, as the backend-decoded path accepted one; so is any byte buffer (bytearray / memoryview / a uint8 array) — a deliberate widening for arrow-backed callers. Anything else (an iterable of ints included) is a TypeError naming its type. For a batch, only the three spellings above are read as one: a Series / generator / S-dtype array reaches the scalar path and is refused by name, so materialize it first (see the dispatch note above).

required
order int

Finest HEALPix order (1-29), shared by every blob. Default 18.

18
moc bool or None

Tri-state, see above. For one blob, moc=True returns the compact mixed-order MOC instead of the flat cover.

None
normalize optional

As :func:from_geometry; in a batch each is a single shared setting. tolerance / max_cells are mutually exclusive and, with a linear scalar geometry, refused.

True
tolerance optional

As :func:from_geometry; in a batch each is a single shared setting. tolerance / max_cells are mutually exclusive and, with a linear scalar geometry, refused.

True
max_cells optional

As :func:from_geometry; in a batch each is a single shared setting. tolerance / max_cells are mutually exclusive and, with a linear scalar geometry, refused.

True
latitude optional

As :func:from_geometry; in a batch each is a single shared setting. tolerance / max_cells are mutually exclusive and, with a linear scalar geometry, refused.

True
offsets array_like or None

int64 arrow list offsets selecting the packed-column form: blob i spans data[offsets[i]:offsets[i + 1]], and the offsets must exactly cover data. Keyword-only.

None

Returns:

Type Description
numpy.ndarray, list of numpy.ndarray, or tuple of numpy.ndarray

One blob: as :func:from_geometry (a flat uint64 cover, a MOC with moc=True, or per-line arrays for linear geometry). A batch: the ragged (values, out_offsets) pair — blob i's MOC is values[out_offsets[i]:out_offsets[i + 1]].

Raises:

Type Description
ValueError

As :func:from_geometry — including moc / tolerance / max_cells passed for linear geometry — plus, from the reader, a truncated or malformed blob (an unclosed polygon ring included), an unsupported or empty geometry, or an invalid hex string. In the batch forms the message names the lowest-index offending blob, linear geometry is refused by index (one MOC per blob has no per-line spelling), moc=False is refused as above, and bad offsets raise here too.

TypeError

For an input (or a batch entry, named by index) that is neither a string nor a buffer of bytes; with offsets, for data that is not one packed buffer. Also for a moc that is neither a bool nor None — the guard that catches a from_wkbs(blobs, order, tol) call migrated positionally, which would otherwise bind tolerance to moc and drop it silently.

See Also

from_geometry : the shared parameter semantics and the full contract. mortie.arrow.from_wkb : the pyarrow skin — hand it a binary / large_binary column as it comes off a file.

Source code in mortie/geometry.py
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
def from_wkb(data, order=18, moc=None, normalize=True,
             tolerance=None, max_cells=None, *, latitude="authalic",
             offsets=None):
    """Cover WKB (or EWKB) geometry with morton indices -- **no backend needed**.

    Blobs are parsed by mortie's own Rust WKB reader (issue #157) and their
    rings go straight to the coverage kernels, so this works with neither
    shapely nor spherely installed — mortie's runtime really is numpy-only on
    this path.  The cover is identical to what the backend-decoded path
    produced: same rings, same descent.  (:func:`from_wkt` still decodes via a
    backend — #157 scoped the Rust parser to WKB.)

    **Batch vectorized** (issue #187), three forms selected by the input —
    never by a flag:

    * ``offsets=`` given — a **packed column**: ``data`` is one ``uint8``
      buffer of every blob's bytes and ``offsets`` are the arrow binary-column
      list offsets.  Zero-copy: the kernel reads views of the caller's buffer.
    * ``list`` / ``tuple`` / object-``ndarray`` — a **sequence batch**: each
      entry is coerced exactly as the scalar form accepts (the pandas case,
      via ``series.to_numpy()``).
    * anything else — **one blob** (``bytes``, hex ``str``, ``bytearray``,
      ``memoryview``, or a ``uint8`` array of the blob's bytes).

    Those three rules are the whole dispatch — it does not sniff any wider —
    so a container the retired ``from_wkbs`` merely iterated over, but that
    this rule does not name, is a ``TypeError``: materialize a pandas
    ``Series``, a generator or a bytes-dtype (``S``) array into one of the
    batch spellings first (``series.to_numpy()``, ``list(gen)``,
    ``arr.astype(object)``), or pack it into the ``offsets=`` column form.

    Both batch forms return the ragged ``(values, out_offsets)`` MOC pair,
    with result ``i`` byte-identical to the scalar form on blob ``i`` with
    ``moc=True``; a whole column crosses the Python/Rust boundary once and is
    parsed and covered in parallel, in byte-capped chunks (the memory posture
    documented on the kernel, :func:`mortie.batch._from_wkbs`).

    ``moc`` is a **tri-state**: the default ``None`` means the flat cover for
    one blob (the historical ``from_wkb`` default) and the ragged MOC pair for
    a batch (the historical ``from_wkbs`` behaviour).  An explicit ``moc=True``
    works in every form; an explicit ``moc=False`` on a batch raises — there
    is no ragged flat-cover kernel to answer with (densify the MOC pair via
    ``moc_to_order(values, order, offsets=out_offsets)`` instead).

    Parameters
    ----------
    data : bytes, str, buffer, or sequence
        One WKB/EWKB geometry, a sequence of them, or a packed column (with
        ``offsets``).  Per blob: both byte orders, the ISO and EWKB dimension
        spellings (Z/M are dropped — mortie is 2-D lon/lat), and an EWKB SRID
        prefix (stripped; mortie's contract is always EPSG:4326) are accepted.
        A **hex string** of a blob is accepted too, as the backend-decoded
        path accepted one; so is any **byte buffer** (``bytearray`` /
        ``memoryview`` / a ``uint8`` array) — a deliberate widening for
        arrow-backed callers.  Anything else (an iterable of ints included)
        is a ``TypeError`` naming its type.  For a batch, only the three
        spellings above are read as one: a ``Series`` / generator /
        ``S``-dtype array reaches the scalar path and is refused by name, so
        materialize it first (see the dispatch note above).
    order : int, optional
        Finest HEALPix order (1-29), shared by every blob.  Default 18.
    moc : bool or None, optional
        Tri-state, see above.  For one blob, ``moc=True`` returns the compact
        mixed-order MOC instead of the flat cover.
    normalize, tolerance, max_cells, latitude : optional
        As :func:`from_geometry`; in a batch each is a single shared setting.
        ``tolerance`` / ``max_cells`` are mutually exclusive and, with a
        *linear* scalar geometry, refused.
    offsets : array_like or None, optional
        ``int64`` arrow list offsets selecting the packed-column form: blob
        ``i`` spans ``data[offsets[i]:offsets[i + 1]]``, and the offsets must
        exactly cover ``data``.  Keyword-only.

    Returns
    -------
    numpy.ndarray, list of numpy.ndarray, or tuple of numpy.ndarray
        One blob: as :func:`from_geometry` (a flat ``uint64`` cover, a MOC
        with ``moc=True``, or per-line arrays for linear geometry).  A batch:
        the ragged ``(values, out_offsets)`` pair — blob ``i``'s MOC is
        ``values[out_offsets[i]:out_offsets[i + 1]]``.

    Raises
    ------
    ValueError
        As :func:`from_geometry` — including ``moc`` / ``tolerance`` /
        ``max_cells`` passed for linear geometry — plus, from the reader, a
        truncated or malformed blob (an unclosed polygon ring included), an
        unsupported or empty geometry, or an invalid hex string.  In the batch
        forms the message names the **lowest-index** offending blob, linear
        geometry is refused by index (one MOC per blob has no per-line
        spelling), ``moc=False`` is refused as above, and bad ``offsets``
        raise here too.
    TypeError
        For an input (or a batch entry, named by index) that is neither a
        string nor a buffer of bytes; with ``offsets``, for ``data`` that is
        not one packed buffer.  Also for a ``moc`` that is neither a ``bool``
        nor ``None`` — the guard that catches a ``from_wkbs(blobs, order,
        tol)`` call migrated positionally, which would otherwise bind
        ``tolerance`` to ``moc`` and drop it silently.

    See Also
    --------
    from_geometry : the shared parameter semantics and the full contract.
    mortie.arrow.from_wkb : the pyarrow skin — hand it a ``binary`` /
        ``large_binary`` column as it comes off a file.
    """
    if moc is not None and not isinstance(moc, (bool, np.bool_)):
        raise TypeError(
            "from_wkb's `moc` is a bool (tri-state with None), got "
            f"{moc!r} ({type(moc).__name__}) -- migrating a positional "
            "from_wkbs(blobs, order, tol) call?  from_wkb's third positional "
            "is moc; pass tolerance= as a keyword."
        )
    if offsets is not None:
        return _from_wkb_batch(
            _wkb_column_views(data, offsets), moc, order, normalize,
            tolerance, max_cells, latitude,
        )
    if isinstance(data, (list, tuple)) or (
        isinstance(data, np.ndarray) and data.dtype == object
    ):
        return _from_wkb_batch(data, moc, order, normalize, tolerance,
                               max_cells, latitude)
    kind, parts = _rings_from_wkb(data)
    return _cover_parts(kind, parts, order, moc, normalize, tolerance,
                        max_cells, latitude)

from_wkt(text, order=18, moc=False, normalize=True, tolerance=None, max_cells=None, *, latitude='authalic')

Cover a geometry given as WKT (or EWKT) text.

Thin wrapper: decode with the geometry backend, then :func:from_geometry. Unlike :func:from_wkb, this does need a backend installed — mortie has no Rust WKT parser (issue #157 scoped the reader to WKB).

Not batch vectorized: one WKT string per call.

Parameters:

Name Type Description Default
text str

WKT or EWKT text.

required
order optional

Forwarded to :func:from_geometry unchanged. See there for the full contract.

18
moc optional

Forwarded to :func:from_geometry unchanged. See there for the full contract.

18
normalize optional

Forwarded to :func:from_geometry unchanged. See there for the full contract.

18
tolerance optional

Forwarded to :func:from_geometry unchanged. See there for the full contract.

18
max_cells optional

Forwarded to :func:from_geometry unchanged. See there for the full contract.

18
latitude optional

Forwarded to :func:from_geometry unchanged. See there for the full contract.

18

Returns:

Type Description
numpy.ndarray or list of numpy.ndarray

As :func:from_geometry.

Raises:

Type Description
ValueError

As :func:from_geometry — including moc / tolerance / max_cells passed for linear geometry.

See Also

from_geometry : The shared parameter semantics and the full contract.

Source code in mortie/geometry.py
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
def from_wkt(text, order=18, moc=False, normalize=True,
             tolerance=None, max_cells=None, *, latitude="authalic"):
    """Cover a geometry given as WKT (or EWKT) text.

    Thin wrapper: decode with the geometry backend, then
    :func:`from_geometry`.  Unlike :func:`from_wkb`, this **does** need a
    backend installed — mortie has no Rust WKT parser (issue #157 scoped the
    reader to WKB).

    **Not batch vectorized**: one WKT string per call.

    Parameters
    ----------
    text : str
        WKT or EWKT text.
    order, moc, normalize, tolerance, max_cells, latitude : optional
        Forwarded to :func:`from_geometry` unchanged.  See there for the
        full contract.

    Returns
    -------
    numpy.ndarray or list of numpy.ndarray
        As :func:`from_geometry`.

    Raises
    ------
    ValueError
        As :func:`from_geometry` — including ``moc`` / ``tolerance`` /
        ``max_cells`` passed for linear geometry.

    See Also
    --------
    from_geometry : The shared parameter semantics and the full contract.
    """
    return from_geometry(
        _geometry_from_wkt(text), order=order, moc=moc, normalize=normalize,
        tolerance=tolerance, max_cells=max_cells, latitude=latitude,
    )

from_geometry(geom, order=18, moc=False, normalize=True, tolerance=None, max_cells=None, *, latitude='authalic')

Cover a backend geometry with morton indices (issue #71).

The geometry is decomposed via :func:decompose and routed to mortie's existing coverage entry points — so WKB/WKT ingest produces exactly the same cover as calling those functions on the same (lats, lons) arrays.

  • Polygon / MultiPolygon → :func:mortie.morton_coverage (flat) or, with moc=True, the compact mixed-order MOC coverer (:func:mortie.coverage._morton_coverage_moc). Holes and disjoint parts are handled by the one even-odd descent.
  • LineString / MultiLineString → :func:mortie.linestring_coverage.

Not batch vectorized: one geometry per call.

Parameters:

Name Type Description Default
geom backend geometry

A shapely/spherely geometry object (e.g. from shapely.from_wkb).

required
order int

HEALPix order (1–29). Default 18.

18
moc bool

Polygonal only: return a compact MOC instead of a flat cover.

False
normalize bool

Polygonal: auto-correct ring orientation at ingest, on both the flat and the moc=True path (see :func:mortie.morton_coverage). Default True: any simple ring whose interior decisively reads as the larger region is reversed so the smaller side is covered (S2's convention; issue #144 decision (A)), hemisphere-plus rings included. Pass False to take the winding as authored — the only way a WKB/WKT ring can express a bigger-than-complement interior (wind every ring, holes included, with its intended region on the left). normalize=False with linear geometry raises ValueError (a line has no ring orientation).

True
tolerance optional

Polygonal moc=True only: the MOC coverer's adaptive stop criteria (mutually exclusive).

None
max_cells optional

Polygonal moc=True only: the MOC coverer's adaptive stop criteria (mutually exclusive).

None
latitude str

Latitude convention of the geometry's coordinates (issue #186): "authalic" (default; geodetic latitudes are converted so cells are equal-area on the WGS84 ellipsoid) or "geodetic-spherical" (legacy: geodetic latitude fed to the spherical kernel as-is).

'authalic'

Returns:

Type Description
numpy.ndarray or list of numpy.ndarray

Polygonal → 1-D uint64 morton array. LineString → 1-D array; MultiLineString → list of arrays, one per line (the :func:mortie.linestring_coverage contract).

Raises:

Type Description
ValueError

If moc / tolerance / max_cells are passed for linear geometry (they apply only to polygonal geometry), or from :func:decompose for an unsupported or empty geometry.

Source code in mortie/geometry.py
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
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
def from_geometry(geom, order=18, moc=False, normalize=True,
                  tolerance=None, max_cells=None, *, latitude="authalic"):
    """Cover a backend geometry with morton indices (issue #71).

    The geometry is decomposed via :func:`decompose` and routed to mortie's
    existing coverage entry points — so WKB/WKT ingest produces exactly the same
    cover as calling those functions on the same ``(lats, lons)`` arrays.

    * **Polygon / MultiPolygon** → :func:`mortie.morton_coverage` (flat) or,
      with ``moc=True``, the compact mixed-order MOC coverer
      (:func:`mortie.coverage._morton_coverage_moc`).
      Holes and disjoint parts are handled by the one even-odd descent.
    * **LineString / MultiLineString** → :func:`mortie.linestring_coverage`.

    **Not batch vectorized**: one geometry per call.

    Parameters
    ----------
    geom : backend geometry
        A shapely/spherely geometry object (e.g. from ``shapely.from_wkb``).
    order : int, optional
        HEALPix order (1–29).  Default 18.
    moc : bool, optional
        Polygonal only: return a compact MOC instead of a flat cover.
    normalize : bool, optional
        Polygonal: auto-correct ring orientation at ingest, on both the
        flat and the ``moc=True`` path (see :func:`mortie.morton_coverage`).
        Default ``True``: any simple ring whose interior decisively reads as
        the larger region is reversed so the smaller side is covered (S2's
        convention; issue #144 decision (A)), hemisphere-plus rings included.
        Pass ``False`` to take the winding **as authored** — the only way a
        WKB/WKT ring can express a bigger-than-complement interior (wind
        every ring, holes included, with its intended region on the left).
        ``normalize=False`` with linear geometry raises ``ValueError`` (a
        line has no ring orientation).
    tolerance, max_cells : optional
        Polygonal ``moc=True`` only: the MOC coverer's adaptive stop
        criteria (mutually exclusive).
    latitude : str, optional
        Latitude convention of the geometry's coordinates (issue #186):
        ``"authalic"`` (default; geodetic latitudes are converted so cells
        are equal-area on the WGS84 ellipsoid) or ``"geodetic-spherical"``
        (legacy: geodetic latitude fed to the spherical kernel as-is).

    Returns
    -------
    numpy.ndarray or list of numpy.ndarray
        Polygonal → 1-D ``uint64`` morton array.  LineString → 1-D array;
        MultiLineString → list of arrays, one per line (the
        :func:`mortie.linestring_coverage` contract).

    Raises
    ------
    ValueError
        If ``moc`` / ``tolerance`` / ``max_cells`` are passed for linear
        geometry (they apply only to polygonal geometry), or from
        :func:`decompose` for an unsupported or empty geometry.
    """
    kind, parts = decompose(geom)
    return _cover_parts(kind, parts, order, moc, normalize, tolerance,
                        max_cells, latitude)

to_wkb(morton, dissolve=True, step=1, srid=None, *, latitude='authalic')

Emit a morton cover as WKB (or EWKB) bytes.

Not batch vectorized: one cover per call, emitted as one blob.

Parameters:

Name Type Description Default
morton array_like of uint64

A morton cover (flat or mixed-order MOC).

required
dissolve optional

Forwarded to :func:to_geometry unchanged; see there for the full contract (pole caps, antimeridian splitting, edge densification).

True
step optional

Forwarded to :func:to_geometry unchanged; see there for the full contract (pole caps, antimeridian splitting, edge densification).

True
srid int

With srid set (e.g. 4326), emit EWKB carrying that SRID; otherwise plain WKB.

None
latitude str

Latitude convention of the emitted vertices, forwarded to :func:to_geometry (issue #186).

'authalic'

Returns:

Type Description
bytes

The encoded WKB (or EWKB) bytes.

Raises:

Type Description
NotImplementedError

As :func:to_geometry — a non-shapely backend, or a dissolved hole that nests into no exterior.

ValueError

As :func:to_geometry — a bad latitude, or a morton that is not integer-typed or holds a negative value (issue #194).

See Also

to_geometry : The dissolve / step contract in full.

Source code in mortie/geometry.py
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
def to_wkb(morton, dissolve=True, step=1, srid=None, *, latitude="authalic"):
    """Emit a morton cover as WKB (or EWKB) bytes.

    **Not batch vectorized**: one cover per call, emitted as one blob.

    Parameters
    ----------
    morton : array_like of uint64
        A morton cover (flat or mixed-order MOC).
    dissolve, step : optional
        Forwarded to :func:`to_geometry` unchanged; see there for the full
        contract (pole caps, antimeridian splitting, edge densification).
    srid : int, optional
        With ``srid`` set (e.g. ``4326``), emit EWKB carrying that SRID;
        otherwise plain WKB.
    latitude : str, optional
        Latitude convention of the emitted vertices, forwarded to
        :func:`to_geometry` (issue #186).

    Returns
    -------
    bytes
        The encoded WKB (or EWKB) bytes.

    Raises
    ------
    NotImplementedError
        As :func:`to_geometry` — a non-shapely backend, or a dissolved hole
        that nests into no exterior.
    ValueError
        As :func:`to_geometry` — a bad *latitude*, or a *morton* that is not
        integer-typed or holds a negative value (issue #194).

    See Also
    --------
    to_geometry : The ``dissolve`` / ``step`` contract in full.
    """
    geom = to_geometry(morton, dissolve=dissolve, step=step,
                       latitude=latitude)
    return _geometry_to_wkb(geom, srid=srid)

to_wkt(morton, dissolve=True, step=1, srid=None, *, latitude='authalic')

Emit a morton cover as WKT (or EWKT) text.

Not batch vectorized: one cover per call, emitted as one string.

Parameters:

Name Type Description Default
morton array_like of uint64

A morton cover (flat or mixed-order MOC).

required
dissolve optional

Forwarded to :func:to_geometry unchanged; see there for the full contract (pole caps, antimeridian splitting, edge densification).

True
step optional

Forwarded to :func:to_geometry unchanged; see there for the full contract (pole caps, antimeridian splitting, edge densification).

True
srid int

With srid set, emit EWKT (SRID=<n>;<WKT>); otherwise plain WKT.

None
latitude str

Latitude convention of the emitted vertices, forwarded to :func:to_geometry (issue #186).

'authalic'

Returns:

Type Description
str

The encoded WKT (or EWKT) text.

Raises:

Type Description
NotImplementedError

As :func:to_geometry — a non-shapely backend, or a dissolved hole that nests into no exterior.

ValueError

As :func:to_geometry — a bad latitude, or a morton that is not integer-typed or holds a negative value (issue #194).

See Also

to_geometry : The dissolve / step contract in full.

Source code in mortie/geometry.py
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
def to_wkt(morton, dissolve=True, step=1, srid=None, *, latitude="authalic"):
    """Emit a morton cover as WKT (or EWKT) text.

    **Not batch vectorized**: one cover per call, emitted as one string.

    Parameters
    ----------
    morton : array_like of uint64
        A morton cover (flat or mixed-order MOC).
    dissolve, step : optional
        Forwarded to :func:`to_geometry` unchanged; see there for the full
        contract (pole caps, antimeridian splitting, edge densification).
    srid : int, optional
        With ``srid`` set, emit EWKT (``SRID=<n>;<WKT>``); otherwise plain WKT.
    latitude : str, optional
        Latitude convention of the emitted vertices, forwarded to
        :func:`to_geometry` (issue #186).

    Returns
    -------
    str
        The encoded WKT (or EWKT) text.

    Raises
    ------
    NotImplementedError
        As :func:`to_geometry` — a non-shapely backend, or a dissolved hole
        that nests into no exterior.
    ValueError
        As :func:`to_geometry` — a bad *latitude*, or a *morton* that is not
        integer-typed or holds a negative value (issue #194).

    See Also
    --------
    to_geometry : The ``dissolve`` / ``step`` contract in full.
    """
    geom = to_geometry(morton, dissolve=dissolve, step=step,
                       latitude=latitude)
    return _geometry_to_wkt(geom, srid=srid)

to_geometry(morton, dissolve=True, step=1, *, latitude='authalic')

Convert a morton cover to a backend geometry (issue #71).

Not batch vectorized: one cover per call, emitted as one geometry.

Parameters:

Name Type Description Default
morton array_like of uint64

A morton cover (flat or mixed-order MOC; each word self-encodes order).

required
dissolve bool

True (default) emits the single dissolved outline of the whole cover (exterior rings, holes, and disjoint components), built natively by edge-cancellation — no backend spatial predicate. False emits a per-cell MultiPolygon — one quad per cell.

True
step int

Boundary points per cell edge (default 1 = 4 corners / straight chords). step>1 densifies each edge to follow the curved HEALPix boundary.

1
latitude str

Latitude convention of the emitted vertices (issue #186): "authalic" (default) converts cell-boundary latitudes back to WGS84 geodetic; "geodetic-spherical" emits legacy spherical latitudes. Pass the convention the words were encoded under.

'authalic'

Returns:

Type Description
backend geometry

A shapely (or spherely) MultiPolygon in EPSG:4326 lon/lat degrees.

Raises:

Type Description
NotImplementedError

If the active backend is not shapely, or if a dissolved hole nests into no exterior (pass dissolve=False).

ValueError

If latitude is not one of the two conventions — checked before the empty-cover early return, so the contract does not depend on input; or if morton is not integer-typed or holds a negative value (issue #194), refused for both dissolve arms alike.

Notes

Emit requires the shapely backend (it constructs geometry objects). The dissolved emit (dissolve=True) handles pole-enclosing covers (e.g. polar caps), exteriors crossing the antimeridian any even number of times, and antimeridian-crossing holes: crossing rings are cut at ±180° and reconnected by the GeoJSON convention — a single split MultiPolygon with explicit ±90° pole vertices stitched down the antimeridian. A cover spanning near or over a hemisphere (2π sr), or one with a boundary ring enclosing more than a hemisphere (e.g. an equatorial band), raises ValueError — its exterior/hole winding is ambiguous (issue #108); split such a cover or use dissolve=False.

Source code in mortie/geometry.py
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
def to_geometry(morton, dissolve=True, step=1, *, latitude="authalic"):
    """Convert a morton cover to a backend geometry (issue #71).

    **Not batch vectorized**: one cover per call, emitted as one geometry.

    Parameters
    ----------
    morton : array_like of uint64
        A morton cover (flat or mixed-order MOC; each word self-encodes order).
    dissolve : bool, optional
        ``True`` (default) emits the single dissolved outline of the whole cover
        (exterior rings, holes, and disjoint components), built natively by
        edge-cancellation — no backend spatial predicate.  ``False`` emits a
        per-cell ``MultiPolygon`` — one quad per cell.
    step : int, optional
        Boundary points per cell edge (default 1 = 4 corners / straight chords).
        ``step>1`` densifies each edge to follow the curved HEALPix boundary.
    latitude : str, optional
        Latitude convention of the **emitted** vertices (issue #186):
        ``"authalic"`` (default) converts cell-boundary latitudes back to
        WGS84 geodetic; ``"geodetic-spherical"`` emits legacy spherical
        latitudes.  Pass the convention the words were encoded under.

    Returns
    -------
    backend geometry
        A shapely (or spherely) ``MultiPolygon`` in EPSG:4326 lon/lat degrees.

    Raises
    ------
    NotImplementedError
        If the active backend is not shapely, or if a dissolved hole nests
        into no exterior (pass ``dissolve=False``).
    ValueError
        If *latitude* is not one of the two conventions — checked before the
        empty-cover early return, so the contract does not depend on input;
        or if *morton* is not integer-typed or holds a negative value
        (issue #194), refused for both ``dissolve`` arms alike.

    Notes
    -----
    Emit requires the shapely backend (it constructs geometry objects).  The
    dissolved emit (``dissolve=True``) handles pole-enclosing covers (e.g. polar
    caps), exteriors crossing the antimeridian any even number of times, and
    antimeridian-crossing holes: crossing rings are cut at ±180° and reconnected
    by the GeoJSON convention — a single split ``MultiPolygon`` with explicit
    ±90° pole vertices stitched down the antimeridian.  A cover spanning near
    or over a hemisphere (2π sr), or one with a boundary ring enclosing more
    than a hemisphere (e.g. an equatorial band), raises ``ValueError`` — its
    exterior/hole winding is ambiguous (issue #108); split such a cover or use
    ``dissolve=False``.
    """
    from .convert import _check_latitude

    mod = _require_shapely("geometry emit")
    # Up front: an empty cover short-circuits below either branch, and would
    # otherwise return silently on an invalid convention (issue #186).
    _check_latitude(latitude)
    # One validation seam for both arms -- and so for to_wkb/to_wkt, which
    # route here.  The dissolved arm's own coercion (dissolve.py) would
    # otherwise truncate floats and wrap negatives on the *default* spelling.
    morton = _as_u64(morton, "morton")
    if dissolve:
        return mod.MultiPolygon(_dissolved_polygons(mod, morton, step, latitude))
    return mod.MultiPolygon(_per_cell_polygons(mod, morton, step, latitude))