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¶
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. The internal kernel cache uses an RLock to serialize NVRTC compilation on first use; subsequent calls are lock-free. Call
compile()at startup to front-load compilation if you want deterministic latency.- 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 projected CRS. This matches 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.
- property accuracy: str¶
Rough accuracy classification for this transform.
Returns¶
- str
“sub-millimeter” — same datum, projection math only. “sub-decimeter” — cross-datum with 15-param time-dependent Helmert evaluated at a known epoch. “sub-meter” — cross-datum with 7-param Helmert. “degraded — no datum shift applied” — different datums; results may differ from pyproj by meters to hundreds of meters.
- compile(*, precision: str = 'auto') None¶
Pre-compile fused NVRTC kernels for this transformer.
Call this to front-load kernel compilation latency before the first transform. No-op if CuPy is not available.
- transform(x: numpy.typing.ArrayLike, y: numpy.typing.ArrayLike, z: None = None, direction: Literal['FORWARD', 'INVERSE'] = 'FORWARD') tuple[Any, Any]¶
- transform(x: numpy.typing.ArrayLike, y: numpy.typing.ArrayLike, z: numpy.typing.ArrayLike, direction: Literal['FORWARD', 'INVERSE'] = 'FORWARD') 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. 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”.
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', 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', stream: Any = None) tuple[Any, Any, Any]
Zero-overhead transform for device-resident arrays.
Designed for integration with vibeSpatial’s OwnedGeometryArray. Skips scalar detection, dtype conversion, and array module inference.
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 otherwise.
- 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. Only used when z is provided and a Helmert datum shift is active.
- precisionstr
“fp64” = full double precision. “fp32” = fp32 compute with fp64 I/O (ADR-0002 mixed precision). “auto” = fp64 (projection math is trig-dominated / SFU-bound).
- streamcupy.cuda.Stream, optional
CUDA stream for async kernel execution. Enables overlapping projection compute with data transfers in pipeline workloads.
Returns¶
- tuple of arrays
Transformed (out_x, out_y) or (out_x, out_y, z_out). Same objects if pre-allocated.
- transform_chunked(x: numpy.typing.ArrayLike, y: numpy.typing.ArrayLike, z: None = None, *, direction: Literal['FORWARD', 'INVERSE'] = 'FORWARD', chunk_size: int = 1000000) 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) 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 2 CUDA streams to overlap H<->D transfers with GPU compute. Each stream owns a dedicated set of device buffers so chunk N can execute on stream A while chunk N+1 transfers on stream B.
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.
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') 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.
Returns¶
- tuple of four floats
(left, bottom, right, top)of the transformed bounding box.
Notes¶
Antimeridian-crossing bounding boxes (where
left > rightin longitude) are not supported. Split into two boxes at ±180° first.