PyProj Coordinate Systems — Core Concepts
PyProj is the Python interface to the PROJ library, the industry-standard engine for cartographic projections and coordinate transformations. Whenever you need to convert coordinates from one reference system to another — latitude/longitude to meters, one national grid to another — PyProj handles the math.
Coordinate Reference Systems (CRS)
A CRS defines how numbers map to locations on Earth. PyProj represents them with the CRS class:
from pyproj import CRS
wgs84 = CRS.from_epsg(4326) # WGS-84 (lat/lon, used by GPS)
utm18n = CRS.from_epsg(32618) # UTM zone 18N (meters, covers NYC area)
web_mercator = CRS.from_epsg(3857) # Web Mercator (used by Google Maps tiles)
print(wgs84.is_geographic) # True — units are degrees
print(utm18n.is_projected) # True — units are meters
print(utm18n.axis_info) # [Axis(name='Easting', ...), Axis(name='Northing', ...)]
EPSG codes are the most common way to identify a CRS. The EPSG registry catalogs over 6,000 systems.
Transforming coordinates
The Transformer class converts coordinates between any two CRS:
from pyproj import Transformer
transformer = Transformer.from_crs("EPSG:4326", "EPSG:32618", always_xy=True)
# Convert a single point (longitude, latitude) → (easting, northing)
x, y = transformer.transform(-73.985, 40.748)
print(f"Easting: {x:.2f}, Northing: {y:.2f}")
The always_xy=True flag ensures input/output order is always (x, y) — longitude first, latitude second. Without it, PROJ follows each CRS’s native axis order, which is (lat, lon) for EPSG:4326.
Batch transformations
PyProj handles arrays efficiently through NumPy integration:
import numpy as np
lons = np.array([-73.985, -118.243, -87.623])
lats = np.array([40.748, 34.052, 41.881])
xs, ys = transformer.transform(lons, lats)
The transformation runs in C, so batch operations are orders of magnitude faster than looping in Python.
Geodetic calculations
Beyond projections, PyProj computes distances and azimuths on the ellipsoid:
from pyproj import Geod
geod = Geod(ellps="WGS84")
# Distance between New York and London
az_fwd, az_back, distance = geod.inv(-73.985, 40.748, -0.118, 51.509)
print(f"Distance: {distance / 1000:.1f} km") # ~5,570 km
# Walk 1 km northeast from a point
lon2, lat2, _ = geod.fwd(-73.985, 40.748, az=45, dist=1000)
These calculations account for Earth’s ellipsoidal shape, providing centimeter-level accuracy for surveying and navigation.
How it works
PROJ uses a pipeline model internally. A transformation from CRS A to CRS B is decomposed into steps:
- CRS A → geographic coordinates on A’s datum
- Datum shift (Helmert transform, grid shift, or other method)
- Geographic coordinates on B’s datum → CRS B
PyProj selects the most accurate transformation path available, preferring grid-based corrections when datum grids are installed.
Common misconception
Many people assume WGS-84 (EPSG:4326) is always appropriate for area or distance calculations. Because its units are degrees — not meters — calculating areas in WGS-84 gives meaningless numbers. Always project to a meter-based CRS (like UTM) before computing distances or areas.
The one thing to remember: PyProj converts between any two coordinate systems on Earth, handling the ellipsoidal math so your datasets align correctly regardless of their original projection.
See Also
- Python Adaptive Learning Systems How Python builds learning apps that adjust to each student like a personal tutor who knows exactly what you need next.
- Python Airflow Learn Airflow as a timetable manager that makes sure data tasks run in the right order every day.
- Python Altair Learn Altair through the idea of drawing charts by describing rules, not by hand-placing every visual element.
- Python Automated Grading How Python grades homework and exams automatically, from simple answer keys to understanding written essays.
- Python Batch Vs Stream Processing Batch processing is like doing laundry once a week; stream processing is like a self-cleaning shirt that cleans itself constantly.