vibeproj.transformer

High-level Transformer API — drop-in replacement for pyproj.Transformer.

Usage:

from vibeproj import Transformer

t = Transformer.from_crs(“EPSG:4326”, “EPSG:32631”) x, y = t.transform(lon, lat) # always_xy=True (default) lon, lat = t.transform(x, y, direction=”INVERSE”)

Classes

Transformer

GPU-accelerated coordinate transformer.

Module Contents

class vibeproj.transformer.Transformer(crs_from: vibeproj.crs.CRSInput, crs_to: vibeproj.crs.CRSInput, *, always_xy: bool = True, datum_shift: Literal['accurate', 'fast'] = 'accurate', epoch: float | None = None)

GPU-accelerated coordinate transformer.

  • transform(x, y) where x=lon, y=lat for geographic CRS (always_xy=True default)

  • direction=”FORWARD” or “INVERSE”

  • Accepts scalars, lists, numpy arrays, or cupy arrays

When CuPy is available and inputs are on GPU, transforms run on GPU. Otherwise falls back to NumPy on CPU.

Thread Safety

Transformer instances are safe to share across threads. NVRTC compilation is serialized on first use. Device-resident calls lease stream-specific scratch as needed. transform_chunked() calls sharing one Transformer and CUDA device serialize around that device’s persistent staging workspace while retaining two-stream overlap within each call.

static from_crs(crs_from: vibeproj.crs.CRSInput, crs_to: vibeproj.crs.CRSInput, *, always_xy: bool = True, datum_shift: Literal['accurate', 'fast'] = 'accurate', epoch: float | None = None) Transformer

Create a Transformer from source and target CRS.

Parameters

crs_from, crs_to :

EPSG integer (4326), string (“EPSG:4326”), or tuple ((“EPSG”, 4326)).

always_xybool, default True

If True, input/output axis order is always (x, y) — i.e. (longitude, latitude) for geographic CRS and (easting, northing) for most projected CRS. Projected X/Y declarations are preserved: EPSG:5513 remains X=Southing, Y=Westing, matching pyproj. This otherwise follows shapely and geopandas conventions. If False, uses the CRS native axis order (pyproj default).

datum_shiftstr, default “accurate”

“accurate” — use 15-parameter time-dependent Helmert when available, evaluating rate terms at the given epoch. Falls back to 7-parameter when no rates are present or no epoch can be resolved. “fast” — always use the base 7-parameter Helmert (ignores rate terms).

epochfloat, optional

Decimal year at which to evaluate the time-dependent Helmert (e.g. 2024.0). Only used when datum_shift=”accurate”. If omitted, the source CRS coordinate epoch is used when available.

property is_fused: bool

True if fused GPU kernels are available for this transform.

explain_strategy(*, transcendentals: vibeproj.transcendentals.TranscendentalPolicy = 'auto', precision: str = 'auto', direction: Literal['FORWARD', 'INVERSE'] = 'FORWARD', device: vibeproj.transcendentals.DeviceCapability | None = None, workload_size: int | None = None) vibeproj.transcendentals.StrategyExplanation

Explain transcendental decisions for one transform direction.

The result is immutable and contains a decision for every projection stage plus Helmert when active. Passing device makes hardware policy tests deterministic; otherwise the current device is detected lazily. precision accepts "auto", "fp64", "fp32", or "ds"; transcendentals accepts "auto", "native", or "accelerated". Pass workload_size to preview size-aware "auto" selection. None represents compilation/planning without a concrete array size and selects an otherwise-qualified accelerated implementation. This method does not materialize coordinate arrays.

property accuracy: str

Rough accuracy classification for this transform.

Returns

str

“sub-millimeter” — same datum, projection math only. “sub-5cm” — cross-datum with SVD-compressed grid correction. “sub-decimeter” — cross-datum with 15-param time-dependent Helmert evaluated at a known epoch. “sub-meter” — cross-datum with 7-param Helmert. “datum no-op (… m PROJ accuracy)” — PROJ selected an explicit no-op datum operation with meter-level expected accuracy. “degraded — no datum shift applied” — different datums; results may differ from pyproj by meters to hundreds of meters.

compile(*, precision: str = 'auto', transcendentals: vibeproj.transcendentals.TranscendentalPolicy = 'auto') None

Pre-compile fused NVRTC kernels for this transformer.

precision accepts "auto", "fp64", "fp32", or "ds". transcendentals accepts "auto", "native", or "accelerated" independently. Compilation has no concrete workload size, so "auto" selects an otherwise-qualified implementation without applying runtime crossover thresholds. This front-loads kernel compilation for every distinct projection/direction/implementation triple reachable in either transform direction; first-use stream scratch and chunk staging workspaces are still allocated lazily. No-op if CuPy is unavailable.

transform(x: numpy.typing.ArrayLike, y: numpy.typing.ArrayLike, z: None = None, direction: Literal['FORWARD', 'INVERSE'] = 'FORWARD', *, precision: str = 'auto', transcendentals: vibeproj.transcendentals.TranscendentalPolicy = 'auto') tuple[Any, Any]
transform(x: numpy.typing.ArrayLike, y: numpy.typing.ArrayLike, z: numpy.typing.ArrayLike, direction: Literal['FORWARD', 'INVERSE'] = 'FORWARD', *, precision: str = 'auto', transcendentals: vibeproj.transcendentals.TranscendentalPolicy = 'auto') tuple[Any, Any, Any]

Transform coordinates.

Parameters

x, yscalar, list, numpy array, or cupy array

Input coordinates. With always_xy=True (default): x=longitude, y=latitude for geographic CRS and PROJ visualization order for projected CRS. EPSG:5513 remains X=Southing, Y=Westing. With always_xy=False: native CRS axis order.

zscalar, list, numpy array, or cupy array, optional

Ellipsoidal height in meters. When a Helmert datum shift is active, z is transformed through the ECEF intermediate (correctness fix). When no datum shift is needed, z is passed through unchanged.

directionstr

“FORWARD” or “INVERSE”.

precision{“auto”, “fp64”, “fp32”, “ds”}

Numeric compute precision for fused GPU kernels.

transcendentals{“auto”, “native”, “accelerated”}

Transcendental implementation policy, independent of precision. "auto" uses the concrete input size and device capability when applying qualified acceleration crossover thresholds.

Returns

tuple of arrays (or scalars if scalar input)

Transformed (x, y) or (x, y, z) if z was provided.

transform_buffers(x: numpy.typing.ArrayLike, y: numpy.typing.ArrayLike, z: None = None, *, direction: Literal['FORWARD', 'INVERSE'] = 'FORWARD', out_x: numpy.typing.ArrayLike | None = None, out_y: numpy.typing.ArrayLike | None = None, out_z: numpy.typing.ArrayLike | None = None, precision: str = 'auto', transcendentals: vibeproj.transcendentals.TranscendentalPolicy = 'auto', stream: Any = None) tuple[Any, Any]
transform_buffers(x: numpy.typing.ArrayLike, y: numpy.typing.ArrayLike, z: numpy.typing.ArrayLike, *, direction: Literal['FORWARD', 'INVERSE'] = 'FORWARD', out_x: numpy.typing.ArrayLike | None = None, out_y: numpy.typing.ArrayLike | None = None, out_z: numpy.typing.ArrayLike | None = None, precision: str = 'auto', transcendentals: vibeproj.transcendentals.TranscendentalPolicy = 'auto', stream: Any = None) tuple[Any, Any, Any]

Transform device-resident arrays with optional pre-allocated outputs.

Designed for integration with vibeSpatial’s OwnedGeometryArray. It skips scalar detection and dtype conversion. With pre-allocated outputs, repeated same-size GPU calls on a warmed, cached stream avoid output and correction-scratch allocation. First use, cache growth, and kernel compilation can still allocate.

Parameters

x, ycupy.ndarray or numpy.ndarray

Coordinate arrays (fp64 storage per ADR-0002).

zcupy.ndarray or numpy.ndarray, optional

Ellipsoidal height array. Transformed through Helmert when a datum shift is active; passed through unchanged by projection/SVD-only paths.

directionstr

“FORWARD” or “INVERSE”.

out_x, out_ycupy.ndarray or numpy.ndarray, optional

Pre-allocated fp64 output arrays. Avoids allocation.

out_zcupy.ndarray or numpy.ndarray, optional

Pre-allocated fp64 output height array. Whenever z is provided, this buffer is honored and returned for transformed or passthrough height paths.

precision{“auto”, “fp64”, “fp32”, “ds”}

Numeric compute precision for fused GPU kernels.

transcendentals{“auto”, “native”, “accelerated”}

Hardware-aware transcendental implementation policy. This is independent of numeric compute precision. "auto" uses the array size when applying qualified acceleration thresholds.

streamcupy.cuda.Stream, optional

CUDA stream for asynchronous execution. None uses the current stream on the input array’s device; the legacy null stream is supported. Explicit streams from another device are rejected. The caller owns synchronization.

Returns

tuple of arrays

Transformed (out_x, out_y) or (out_x, out_y, z_out). Supplied output buffers are returned identically.

transform_chunked(x: numpy.typing.ArrayLike, y: numpy.typing.ArrayLike, z: None = None, *, direction: Literal['FORWARD', 'INVERSE'] = 'FORWARD', chunk_size: int = 1000000, precision: str = 'auto', transcendentals: vibeproj.transcendentals.TranscendentalPolicy = 'auto') tuple[numpy.ndarray, numpy.ndarray]
transform_chunked(x: numpy.typing.ArrayLike, y: numpy.typing.ArrayLike, z: numpy.typing.ArrayLike, *, direction: Literal['FORWARD', 'INVERSE'] = 'FORWARD', chunk_size: int = 1000000, precision: str = 'auto', transcendentals: vibeproj.transcendentals.TranscendentalPolicy = 'auto') tuple[numpy.ndarray, numpy.ndarray, numpy.ndarray]

Transform large host-resident arrays in GPU-sized chunks.

Uses a double-buffered pipeline with pinned host memory and two CUDA streams to overlap transfers with GPU compute. Each Transformer keeps a grow-only workspace per CUDA device containing persistent streams, pinned buffers, and device buffers. Calls sharing that Transformer and device serialize around the complete workspace lifecycle; the two streams still overlap work within a call. Workspace and correction scratch allocation occurs on first use or growth and is reused by subsequent same-size calls.

Falls back to CPU transform() when CuPy is not available.

Parameters

x, yarray-like

Input coordinate arrays (host memory).

zarray-like, optional

Ellipsoidal height. Transformed through Helmert when a datum shift is active; passed through unchanged otherwise.

directionstr

“FORWARD” or “INVERSE”.

chunk_sizeint, default 1_000_000

Coordinates per GPU chunk. Larger values use more GPU memory but reduce per-chunk overhead. Size-aware "auto" dispatch uses the planned buffer size once and reuses that decision for every chunk, including a smaller final chunk.

precision{“auto”, “fp64”, “fp32”, “ds”}

Numeric compute precision for fused GPU kernels.

transcendentals{“auto”, “native”, “accelerated”}

Hardware-aware transcendental implementation policy, independent of precision.

Returns

tuple of numpy.ndarray

Transformed (x, y) or (x, y, z) on the host.

transform_bounds(left: float, bottom: float, right: float, top: float, *, densify_pts: int = 21, direction: Literal['FORWARD', 'INVERSE'] = 'FORWARD', precision: str = 'auto', transcendentals: vibeproj.transcendentals.TranscendentalPolicy = 'auto') tuple[float, float, float, float]

Transform a bounding box, densifying edges to handle projection curvature.

Densifies the four edges of the input bounding box, transforms all points, and returns the min/max envelope of the transformed result. This correctly handles non-linear projection distortion that would be missed by transforming only the four corners.

Parameters

left, bottom, right, topfloat

Bounding box coordinates. With always_xy=True (default): left/right are x (longitude), bottom/top are y (latitude).

densify_ptsint, default 21

Number of additional intermediate points per edge (not counting corner endpoints). Matches pyproj/GDAL convention: 0 means corners only, 21 (the default) adds 21 points between each pair of adjacent corners. Clamped to a minimum of 0.

direction{“FORWARD”, “INVERSE”}

Transform direction.

precision{“auto”, “fp64”, “fp32”, “ds”}

Numeric compute precision forwarded to the densified transform.

transcendentals{“auto”, “native”, “accelerated”}

Hardware-aware transcendental implementation policy, independent of precision. "auto" uses the densified point count.

Returns

tuple of four floats

(left, bottom, right, top) of the transformed bounding box.

Notes

When the transformed result crosses the antimeridian (±180°), the returned left will be greater than right (e.g. left=153, right=-162), matching the pyproj convention.