mortie.arrow
Arrow interop: the morton_index pyarrow ExtensionType and the
library-agnostic Arrow C Data Interface surface. MortonIndexType and
MortonIndexExtArray are built lazily behind a module __getattr__ (pyarrow is
optional), so they are documented narratively in
Arrow interchange rather than here.
The morton_index Arrow skin: a pyarrow ExtensionType over the words.
A pyarrow :class:pyarrow.ExtensionType over uint64 storage carrying the
morton_index tag (issue #35, phase 4; issue #58 flipped the storage to
uint64).
This is the Arrow-interop sibling of the pandas ExtensionArray in
:mod:mortie.morton_index. The packed 64-bit decimal-Morton words live in Rust
(src_rust/src/decimal_morton.rs); this module only wraps them so the same
words can travel through an Arrow array and survive a parquet round-trip with
their morton_index identity attached as extension metadata. Storage is the
raw uint64 words verbatim (over the kernel's bit layout), so the raw word
order is the Z-order, the same convention as the pandas skin.
pyarrow is an optional dependency exactly like pandas: importing mortie
succeeds with neither installed. The extension type is built lazily on first
use and a clear ImportError is raised if it is touched without pyarrow.
morton_index_type()
Return the (registered) morton_index pyarrow extension type.
Returns:
| Type | Description |
|---|---|
ExtensionType
|
The singleton type instance, registered with pyarrow on first call. |
Raises:
| Type | Description |
|---|---|
ImportError
|
If pyarrow is not installed. |
Source code in mortie/arrow.py
186 187 188 189 190 191 192 193 194 195 196 197 198 199 | |
polygons_to_morton_mocs(polygons, order=18, tolerance=None, max_cells=None, normalize=True)
Batch MOC coverage over an Arrow polygon column (issue #153).
The Arrow skin of :func:mortie.polygons_to_morton_mocs (plural MOCs:
one MOC per input polygon, many→many — not the many→one ring union of the
multipart scalar form): the ragged polygon batch goes in as an Arrow list
array, its child arrays feed the numpy core directly, and the ragged
result comes back as a ListArray whose values carry the registered
morton_index extension type — parquet-ready, e.g. for a catalog's
footprint_cells column.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
polygons
|
Array or ChunkedArray or tuple
|
Either a |
required |
order
|
int
|
Finest HEALPix order (1-29), shared by every polygon. Default 18. |
18
|
tolerance
|
(float, int)
|
The shared per-polygon stop criteria, exactly as on
:func: |
None
|
max_cells
|
(float, int)
|
The shared per-polygon stop criteria, exactly as on
:func: |
None
|
normalize
|
bool
|
Ring-orientation handling, as on :func: |
True
|
Returns:
| Type | Description |
|---|---|
ListArray
|
One entry per input polygon; entry |
Raises:
| Type | Description |
|---|---|
ImportError
|
If pyarrow is not installed. |
ValueError
|
Fail-fast with the lowest-index offending polygon named, as on
:func: |
Examples:
>>> import pyarrow as pa
>>> from mortie import arrow as marrow
>>> polys = pa.array(
... [[{"lat": 40.0, "lon": -120.0}, {"lat": 50.0, "lon": -120.0},
... {"lat": 45.0, "lon": -110.0}]])
>>> mocs = marrow.polygons_to_morton_mocs(polys, order=6)
>>> mocs.type
ListType(list<item: extension<mortie.morton_index<MortonIndexType>>>)
Source code in mortie/arrow.py
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 | |
from_wkbs(column, order=18, tolerance=None, max_cells=None, normalize=True)
Batch MOC coverage over an Arrow WKB column (issue #163).
The Arrow skin of :func:mortie.from_wkbs: a geoparquet / STAC geometry
column goes in as it comes off the file — binary or large_binary,
chunked or not, sliced or not — and the same ragged
(values, out_offsets) pair comes back, with every scalar parameter
forwarded unchanged. Result i is byte-identical to the core called on
blob i.
What this buys is correctness, not speed. The core already accepts
byte buffers, so a caller can hand it memoryview slices off the
column's value buffer today — but doing that by hand has to get the array
offset, the chunk boundaries and the offset width all right, and gets
different data with no error if it misses any of them (issue #163) —
and, for a null, an empty blob reported as a truncated geometry rather
than as a missing one. All four traps are handled once here, in
:func:_wkb_blobs_from_arrow.
Memory is the core's posture unchanged, because this is the core: the
blobs are zero-copy views into the column's own buffer, so no bytes
object is built (the ~305 MB materialization to_pylist() costs on a
555,867-blob column, englacial/zagg#408), and the byte-capped chunk loop
still bounds the peak at the result plus one chunk. What it does not
remove is the per-chunk copy — releasing the GIL needs owned bytes — nor
the one memoryview object per row.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
column
|
Array or ChunkedArray
|
A |
required |
order
|
int
|
Finest HEALPix order (1-29), shared by every blob. Default 18. |
18
|
tolerance
|
float
|
Shared per-blob stop radius in degrees, mutually exclusive with
|
None
|
max_cells
|
int
|
Shared per-blob cell budget, exactly as on :func: |
None
|
normalize
|
bool
|
Ring-orientation handling, as on :func: |
True
|
Returns:
| Name | Type | Description |
|---|---|---|
values |
ndarray
|
Every blob's morton MOC words concatenated ( |
out_offsets |
ndarray
|
|
Raises:
| Type | Description |
|---|---|
ImportError
|
If pyarrow is not installed. |
ValueError
|
Fail-fast naming the offending blob in the logical column's frame (so
an offender in the third chunk reports its column index, not its
within-chunk one): a null entry, plus every failure class
:func: |
TypeError
|
For a column that is not an Arrow |
Notes
Two ordered gates, as on :func:mortie.from_wkbs itself: nulls are
screened by a vectorised pre-pass over the whole column, and only then
are the blobs parsed and covered. Each gate reports its own lowest-index
offender, so a null preempts a malformed blob at a lower index — a
null at row 20 is raised ahead of a truncated blob at row 3. Within each
class the lowest index wins. The pre-pass is an earlier gate, not a
competing one: it is what turns a null from the core's misleading
truncated WKB into the absence of a geometry, and it is per column
rather than per blob because is_null() is one vectorised call.
See Also
mortie.from_wkbs : the core batch, and the contract in full.
Examples:
>>> import pyarrow as pa
>>> import shapely
>>> from mortie import arrow as marrow
>>> col = pa.array([shapely.to_wkb(geom)])
>>> values, off = marrow.from_wkbs(col, order=8)
Source code in mortie/arrow.py
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 628 629 630 631 632 633 634 635 636 637 638 639 | |
from_morton_index(array)
Wrap a :class:~mortie.morton_index.MortonIndexArray as an Arrow array.
Builds a pyarrow ExtensionArray of the morton_index type over the
same uint64 words. Missing elements -- a MortonIndexArray for which
:meth:isna is True, i.e. the all-zero empty sentinel word -- emit Arrow
nulls, so a null survives the round-trip back through
:func:to_morton_index. (The missing mask is read off the uint64
words, so a sentinel word in a raw array is treated as a null too; an
already-built Arrow array goes back through :func:to_morton_index, not
here.)
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
array
|
MortonIndexArray or array_like
|
The words to wrap; may also be a raw |
required |
Returns:
| Type | Description |
|---|---|
ExtensionArray
|
A |
Raises:
| Type | Description |
|---|---|
ImportError
|
If pyarrow is not installed. |
Source code in mortie/arrow.py
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 | |
to_morton_index(array)
Convert an Arrow morton_index array back to a MortonIndexArray.
Arrow nulls come back as the all-zero empty sentinel word, so the pandas
:meth:isna reports them as missing.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
array
|
ExtensionArray or Array
|
The extension array, or its plain |
required |
Returns:
| Type | Description |
|---|---|
MortonIndexArray
|
The pandas-side :class: |
Raises:
| Type | Description |
|---|---|
ImportError
|
If pyarrow is not installed. |
Source code in mortie/arrow.py
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 | |
export_c_array(words)
Export packed uint64 words as an Arrow C Data Interface capsule pair.
Consumable by any Arrow lib without pandas or pyarrow.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
words
|
array_like
|
Any |
required |
Returns:
| Type | Description |
|---|---|
tuple of PyCapsule
|
The |
Source code in mortie/arrow.py
654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 | |
export_c_schema()
Return the morton_index Arrow schema capsule.
The __arrow_c_schema__ half of the C Data Interface surface.
Returns:
| Type | Description |
|---|---|
PyCapsule
|
An |
Source code in mortie/arrow.py
681 682 683 684 685 686 687 688 689 690 691 692 693 694 | |
import_c_array(source)
Import an Arrow C Data Interface array/stream as packed uint64 words.
Arrow nulls come back as the all-zero empty sentinel, so the null<->sentinel convention round-trips byte-for-byte. No pyarrow dependency on any path.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
source
|
object or tuple
|
One of:
|
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
The packed words as a |
Source code in mortie/arrow.py
697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 | |