Python interface to PROJ library for cartographic projections and coordinate transformations
—
Coordinate transformations enable conversion between different coordinate reference systems with high precision. PyProj provides robust transformation capabilities through the Transformer class and global transformation functions, supporting transformation pipelines, accuracy estimation, and batch processing.
Create Transformer objects for coordinate transformations between different CRS with various configuration options.
class Transformer:
def __init__(
self,
transformer_maker: TransformerMaker | None = None,
) -> None:
"""
Create a Transformer object (internal use only).
Note:
Transformer objects should be created using class methods:
- Transformer.from_crs() - most common
- Transformer.from_proj() - from PROJ strings
- Transformer.from_pipeline() - from PROJ pipelines
Args:
transformer_maker: Internal transformer creation strategy
Raises:
ProjError: If transformer_maker is invalid or None
"""
@classmethod
def from_crs(
cls,
crs_from: Any,
crs_to: Any,
always_xy: bool = False,
area_of_interest: AreaOfInterest | None = None,
authority: str | None = None,
accuracy: float | None = None,
allow_ballpark: bool | None = None,
force_over: bool = False,
only_best: bool | None = None,
) -> "Transformer":
"""
Create Transformer from coordinate reference systems.
Args:
crs_from: Source CRS (any format accepted by CRS.from_user_input)
crs_to: Target CRS (any format accepted by CRS.from_user_input)
always_xy: Force x,y axis order regardless of CRS definitions
area_of_interest: Constrain transformation search to area
authority: Authority to prefer for transformation selection
accuracy: Minimum required accuracy in meters
allow_ballpark: Allow approximate transformations if exact unavailable
force_over: Force Over transformation method selection
only_best: Use only the highest accuracy transformation
Returns:
Configured Transformer object
Raises:
CRSError: If CRS inputs are invalid
ProjError: If no suitable transformation found
"""
@classmethod
def from_proj(
cls,
proj_from: str,
proj_to: str,
always_xy: bool = False,
area_of_interest: AreaOfInterest | None = None,
**kwargs
) -> "Transformer":
"""
Create Transformer from PROJ strings.
Args:
proj_from: Source PROJ string
proj_to: Target PROJ string
always_xy: Force x,y axis order
area_of_interest: Area constraint for transformation
**kwargs: Additional parameters
Returns:
Transformer object from PROJ definitions
Raises:
ProjError: If PROJ strings are invalid
"""
@classmethod
def from_pipeline(cls, proj_pipeline: str) -> "Transformer":
"""
Create Transformer from PROJ pipeline string.
Args:
proj_pipeline: PROJ pipeline definition string
Returns:
Transformer object from pipeline
Raises:
ProjError: If pipeline string is invalid or unsupported
"""Transform coordinates between coordinate reference systems with support for various data formats and coordinate dimensions.
class Transformer:
def transform(
self,
xx,
yy,
zz=None,
tt=None,
radians: bool = False,
errcheck: bool = False,
direction: TransformDirection = TransformDirection.FORWARD,
**kwargs
) -> tuple:
"""
Transform coordinates from source to target CRS.
Args:
xx: X coordinates (longitude, easting, etc.)
yy: Y coordinates (latitude, northing, etc.)
zz: Z coordinates (height, elevation, etc.), optional
tt: T coordinates (time), optional
radians: Input coordinates are in radians (for geographic CRS)
errcheck: Check for transformation errors
direction: Transformation direction (FORWARD, INVERSE, IDENT)
**kwargs: Additional transformation parameters
Returns:
Tuple of transformed coordinates (x, y) or (x, y, z) or (x, y, z, t)
Raises:
ProjError: If transformation fails
"""
def itransform(
self,
points,
switch: bool = False,
time_3rd: bool = False,
radians: bool = False,
errcheck: bool = False,
direction: TransformDirection = TransformDirection.FORWARD,
**kwargs
) -> Iterator:
"""
Transform iterator of coordinate points.
Args:
points: Iterable of coordinate tuples [(x1,y1), (x2,y2), ...]
switch: Switch x,y order in input points
time_3rd: Treat third coordinate as time instead of height
radians: Input coordinates are in radians
errcheck: Check for transformation errors
direction: Transformation direction
**kwargs: Additional parameters
Yields:
Transformed coordinate tuples
Raises:
ProjError: If transformation fails for any point
"""
def transform_bounds(
self,
left: float,
bottom: float,
right: float,
top: float,
densify_pts: int = 21,
radians: bool = False,
errcheck: bool = False,
direction: TransformDirection = TransformDirection.FORWARD,
**kwargs
) -> tuple[float, float, float, float]:
"""
Transform bounding box from source to target CRS.
Args:
left: Left boundary (min x)
bottom: Bottom boundary (min y)
right: Right boundary (max x)
top: Top boundary (max y)
densify_pts: Number of points to add along each boundary edge
radians: Boundaries are in radians
errcheck: Check for transformation errors
direction: Transformation direction
**kwargs: Additional parameters
Returns:
Tuple of transformed boundaries (left, bottom, right, top)
Raises:
ProjError: If transformation fails
"""Access information about transformation operations, accuracy, and available alternatives.
class Transformer:
@property
def name(self) -> str | None:
"""Get the name of the transformation."""
@property
def description(self) -> str | None:
"""Get the description of the transformation."""
@property
def definition(self) -> str:
"""Get the definition string of the transformation."""
@property
def has_inverse(self) -> bool:
"""Check if transformation has an inverse operation."""
@property
def accuracy(self) -> float | None:
"""Get transformation accuracy in meters."""
@property
def area_of_use(self) -> AreaOfUse | None:
"""Get the area of use for the transformation."""
@property
def remarks(self) -> str | None:
"""Get remarks about the transformation."""
@property
def scope(self) -> str | None:
"""Get the scope of the transformation."""
@property
def operations(self) -> tuple[CoordinateOperation, ...] | None:
"""Get available transformation operations."""
@property
def is_network_enabled(self) -> bool:
"""Check if network access is enabled for grid downloads."""
@property
def source_crs(self) -> CRS:
"""Get the source coordinate reference system."""
@property
def target_crs(self) -> CRS:
"""Get the target coordinate reference system."""
def get_last_used_operation(self) -> "Transformer":
"""
Get the last used transformation operation.
Returns:
Transformer representing the last used operation
Raises:
ProjError: If no operation has been used yet
"""Export transformer definitions to various formats for interoperability and persistence.
class Transformer:
def to_proj4(self, version: ProjVersion = ProjVersion.PROJ_5) -> str:
"""
Export transformation as PROJ.4 string.
Args:
version: PROJ version for compatibility
Returns:
PROJ.4 string representation
Raises:
ProjError: If transformation cannot be represented as PROJ.4
"""
def to_wkt(self, version: WktVersion = WktVersion.WKT2_2019, pretty: bool = False) -> str:
"""
Export transformation as WKT string.
Args:
version: WKT version to use
pretty: Whether to format with indentation
Returns:
WKT string representation
Raises:
ProjError: If transformation cannot be represented as WKT
"""
def to_json(self, pretty: bool = False, indentation: int = 2) -> str:
"""
Export transformation as JSON string.
Args:
pretty: Whether to format with indentation
indentation: Spaces for indentation
Returns:
JSON string representation
"""
def to_json_dict(self) -> dict:
"""
Export transformation as JSON dictionary.
Returns:
Dictionary containing JSON representation
"""
def is_exact_same(self, other: Any) -> bool:
"""
Check if two transformers are exactly the same.
Args:
other: Another Transformer object
Returns:
True if transformers are identical, False otherwise
"""Work with multiple transformation options and select optimal transformations for specific requirements.
class TransformerGroup:
"""Group of transformers with different accuracy and area constraints."""
def __init__(
self,
crs_from: Any,
crs_to: Any,
always_xy: bool = False,
area_of_interest: AreaOfInterest | None = None,
**kwargs
) -> None:
"""
Create group of available transformers.
Args:
crs_from: Source CRS
crs_to: Target CRS
always_xy: Force x,y axis order
area_of_interest: Area constraint
**kwargs: Additional parameters
"""
@property
def transformers(self) -> list[Transformer]:
"""Get list of available transformers ordered by accuracy."""
@property
def unavailable_operations(self) -> list[CoordinateOperation]:
"""Get list of operations that are unavailable (missing grids, etc.)."""
@property
def best_available(self) -> Transformer:
"""Get the most accurate available transformer."""
def download_grids(self, verbose: bool = False) -> bool:
"""
Download transformation grids for unavailable operations.
Args:
verbose: Print download progress
Returns:
True if all grids downloaded successfully, False otherwise
"""Standalone functions for coordinate transformation without explicitly creating Transformer objects.
def transform(
p1: Proj | Transformer | CRS | str,
p2: Proj | Transformer | CRS | str,
x,
y,
z=None,
tt=None,
radians: bool = False,
errcheck: bool = False,
always_xy: bool = False,
**kwargs
) -> tuple:
"""
Transform coordinates between projections.
Args:
p1: Source projection/CRS
p2: Target projection/CRS
x: X coordinates
y: Y coordinates
z: Z coordinates, optional
tt: Time coordinates, optional
radians: Coordinates in radians
errcheck: Check for errors
always_xy: Force x,y axis order
**kwargs: Additional parameters
Returns:
Tuple of transformed coordinates
Raises:
ProjError: If transformation fails
"""
def itransform(
p1: Proj | Transformer | CRS | str,
p2: Proj | Transformer | CRS | str,
points,
switch: bool = False,
time_3rd: bool = False,
radians: bool = False,
errcheck: bool = False,
always_xy: bool = False,
**kwargs
) -> Iterator:
"""
Transform iterator of points between projections.
Args:
p1: Source projection/CRS
p2: Target projection/CRS
points: Iterable of coordinate points
switch: Switch x,y order
time_3rd: Third coordinate is time
radians: Coordinates in radians
errcheck: Check for errors
always_xy: Force x,y axis order
**kwargs: Additional parameters
Yields:
Transformed coordinate tuples
Raises:
ProjError: If transformation fails
"""from pyproj import Transformer, CRS
# Create transformer between WGS84 and UTM Zone 33N
wgs84 = CRS.from_epsg(4326)
utm_33n = CRS.from_epsg(32633)
transformer = Transformer.from_crs(wgs84, utm_33n, always_xy=True)
# Transform single point
lon, lat = 10.0, 60.0
x, y = transformer.transform(lon, lat)
print(f"UTM: {x:.2f}, {y:.2f}")
# Transform multiple points
points = [(10.0, 60.0), (11.0, 61.0), (12.0, 62.0)]
transformed = list(transformer.itransform(points))
print(f"Transformed points: {transformed}")from pyproj import Transformer, AreaOfInterest
# Define area of interest for better transformation accuracy
aoi = AreaOfInterest(
west_lon_degree=-180,
south_lat_degree=-90,
east_lon_degree=180,
north_lat_degree=90
)
transformer = Transformer.from_crs(
'EPSG:4326',
'EPSG:3857', # Web Mercator
area_of_interest=aoi,
accuracy=1.0 # Require 1 meter accuracy
)
# Transform coordinates
lon, lat = -74.0, 40.7 # New York
x, y = transformer.transform(lon, lat)
print(f"Web Mercator: {x:.2f}, {y:.2f}")from pyproj import Transformer
transformer = Transformer.from_crs('EPSG:4326', 'EPSG:32633')
# Transform bounding box
west, south, east, north = 9.0, 59.0, 11.0, 61.0
bbox_utm = transformer.transform_bounds(west, south, east, north)
print(f"UTM bounding box: {bbox_utm}")
# Transform with densification for curved boundaries
bbox_dense = transformer.transform_bounds(
west, south, east, north,
densify_pts=50 # Add more points along edges
)from pyproj import Transformer, TransformDirection
transformer = Transformer.from_crs('EPSG:4326', 'EPSG:32633')
# Forward transformation
lon, lat = 10.0, 60.0
x, y = transformer.transform(lon, lat)
# Inverse transformation
lon_back, lat_back = transformer.transform(
x, y,
direction=TransformDirection.INVERSE
)
print(f"Original: {lon:.6f}, {lat:.6f}")
print(f"Recovered: {lon_back:.6f}, {lat_back:.6f}")from pyproj import TransformerGroup
# Get all available transformations
group = TransformerGroup('EPSG:4326', 'EPSG:32633')
print(f"Available transformers: {len(group.transformers)}")
print(f"Best accuracy: {group.best_available.accuracy} meters")
# Check for unavailable operations
if group.unavailable_operations:
print(f"Unavailable: {len(group.unavailable_operations)} operations")
# Download missing grids if needed
success = group.download_grids(verbose=True)
print(f"Grid download success: {success}")
# Use the best available transformer
transformer = group.best_available
x, y = transformer.transform(10.0, 60.0)from pyproj import Transformer
# Create transformer from PROJ pipeline
pipeline = "+proj=pipeline +step +proj=unitconvert +xy_in=deg +xy_out=rad +step +proj=cart +ellps=WGS84"
transformer = Transformer.from_pipeline(pipeline)
# Transform geographic to geocentric
lon, lat, height = 10.0, 60.0, 100.0
x, y, z = transformer.transform(lon, lat, height)
print(f"Geocentric: {x:.2f}, {y:.2f}, {z:.2f}")from pyproj import transform, itransform
# Direct transformation without creating Transformer
x, y = transform('EPSG:4326', 'EPSG:32633', 10.0, 60.0, always_xy=True)
# Transform multiple points
points = [(10.0, 60.0), (11.0, 61.0)]
transformed = list(itransform('EPSG:4326', 'EPSG:32633', points, always_xy=True))# Supporting classes
class TransformerMaker:
"""Abstract base class for transformer creation strategies."""
class TransformerFromCRS(TransformerMaker):
"""Creates transformers from CRS objects."""
class TransformerFromPipeline(TransformerMaker):
"""Creates transformers from PROJ pipelines."""
# Wrapper classes
class TransformerUnsafe:
"""Transformer wrapper that skips error checking for performance."""
# Enumerations used in transformations
class TransformDirection(Enum):
FORWARD = "FORWARD"
INVERSE = "INVERSE"
IDENT = "IDENT"Install with Tessl CLI
npx tessl i tessl/pypi-pyproj