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:datais oneuint8buffer of every blob's bytes andoffsetsare 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, viaseries.to_numpy()).- anything else — one blob (
bytes, hexstr,bytearray,memoryview, or auint8array 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
|
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, |
None
|
normalize
|
optional
|
As :func: |
True
|
tolerance
|
optional
|
As :func: |
True
|
max_cells
|
optional
|
As :func: |
True
|
latitude
|
optional
|
As :func: |
True
|
offsets
|
array_like or None
|
|
None
|
Returns:
| Type | Description |
|---|---|
numpy.ndarray, list of numpy.ndarray, or tuple of numpy.ndarray
|
One blob: as :func: |
Raises:
| Type | Description |
|---|---|
ValueError
|
As :func: |
TypeError
|
For an input (or a batch entry, named by index) that is neither a
string nor a buffer of bytes; with |
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 | |
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: |
18
|
moc
|
optional
|
Forwarded to :func: |
18
|
normalize
|
optional
|
Forwarded to :func: |
18
|
tolerance
|
optional
|
Forwarded to :func: |
18
|
max_cells
|
optional
|
Forwarded to :func: |
18
|
latitude
|
optional
|
Forwarded to :func: |
18
|
Returns:
| Type | Description |
|---|---|
numpy.ndarray or list of numpy.ndarray
|
As :func: |
Raises:
| Type | Description |
|---|---|
ValueError
|
As :func: |
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 | |
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, withmoc=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 |
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 |
True
|
tolerance
|
optional
|
Polygonal |
None
|
max_cells
|
optional
|
Polygonal |
None
|
latitude
|
str
|
Latitude convention of the geometry's coordinates (issue #186):
|
'authalic'
|
Returns:
| Type | Description |
|---|---|
numpy.ndarray or list of numpy.ndarray
|
Polygonal → 1-D |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
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 | |
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: |
True
|
step
|
optional
|
Forwarded to :func: |
True
|
srid
|
int
|
With |
None
|
latitude
|
str
|
Latitude convention of the emitted vertices, forwarded to
:func: |
'authalic'
|
Returns:
| Type | Description |
|---|---|
bytes
|
The encoded WKB (or EWKB) bytes. |
Raises:
| Type | Description |
|---|---|
NotImplementedError
|
As :func: |
ValueError
|
As :func: |
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 | |
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: |
True
|
step
|
optional
|
Forwarded to :func: |
True
|
srid
|
int
|
With |
None
|
latitude
|
str
|
Latitude convention of the emitted vertices, forwarded to
:func: |
'authalic'
|
Returns:
| Type | Description |
|---|---|
str
|
The encoded WKT (or EWKT) text. |
Raises:
| Type | Description |
|---|---|
NotImplementedError
|
As :func: |
ValueError
|
As :func: |
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 | |
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
|
step
|
int
|
Boundary points per cell edge (default 1 = 4 corners / straight chords).
|
1
|
latitude
|
str
|
Latitude convention of the emitted vertices (issue #186):
|
'authalic'
|
Returns:
| Type | Description |
|---|---|
backend geometry
|
A shapely (or spherely) |
Raises:
| Type | Description |
|---|---|
NotImplementedError
|
If the active backend is not shapely, or if a dissolved hole nests
into no exterior (pass |
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 |
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 | |