pyproj review
pyproj 3.7.2 is the Python binding for PROJ. `CRS` parses coordinate reference systems, `Transformer` selects and runs coordinate operations, and `Geod` calculates ellipsoidal distances, azimuths, areas, and perimeters. It works on coordinates and arrays rather than geometry objects. This release raises the floors to Python 3.11 and PROJ 9.4, adds Windows ARM64 wheels, and enables free-threading compatibility. Our wheel contained compiled extensions, imported in 0.33 seconds, and shipped typing metadata.
pyproj 3.7.2 installed in 0.6 seconds, occupied 32 MB, imported in 0.33 seconds, and returned 0 audit findings in our sandbox. Install it for authoritative CRS transformations and ellipsoidal calculations, but verify axis order and grid availability before trusting production coordinates.
We installed it
| Install | ✓ · 0.6s | 2 packages on disk · 32 MB |
| Import | ✓ | import pyproj in 0.33s · compiled extensions · py.typed · requires Python >=3.11 |
| Known vulns | 0 | (pip-audit) |
Answers from our run
Does pyproj install cleanly?
Yes. In a fresh container with an empty cache, pip install pyproj finished in 0.6s, leaving 2 packages and 32 MB on disk. pip-audit reported no known vulnerabilities.
What does pyproj need to run?
Python >=3.11, and a platform wheel with compiled extensions. In our run import pyproj succeeded in 0.33s, and the package ships py.typed for type checkers.
pyproj or geopandas: which should you use?
geopandas: Use it when whole geometry columns need CRS-aware reprojection and tabular operations. pyproj 3.7.2 installed in 0.6 seconds, occupied 32 MB, imported in 0.33 seconds, and returned 0 audit findings in our sandbox.
When should you not use pyproj?
You need buffers, intersections, clipping, or geometry repair. pyproj transforms coordinates; Shapely or GeoPandas owns geometry operations.
Use it if
- Coordinates must move between EPSG, WKT, PROJ, or authority-defined reference systems with an inspectable operation.
- NumPy coordinate arrays need one vectorized transform rather than a Python call for every point.
- Distance, direction, or polygon area must be calculated on a named ellipsoid rather than a sphere.
- A pipeline must inspect axis order, units, area of use, deprecation status, or missing transformation grids before processing data.
- You need buffers, intersections, clipping, or geometry repair. pyproj transforms coordinates; Shapely or GeoPandas owns geometry operations.
- Every point is already WGS84 and a documented spherical approximation is acceptable. A small haversine function avoids compiled extensions and CRS data.
- The runtime is Python 3.10 or older. Version 3.7.2 requires Python 3.11 or newer, and source builds also require PROJ 9.4 or newer.
- Accurate datum shifts must work offline without provisioning grid files. PROJ can mark the preferred operation unavailable when its required grid is absent.
- The code assumes every geographic CRS accepts longitude first. Authority axis order can put latitude first; omit `always_xy=True` and valid-looking coordinates may be wrong.
Setup reality
We installed pyproj 3.7.2 in a fresh Python 3.12 Bookworm sandbox. pip completed in 0.6 seconds and left 2 packages using 32 MB. The package declared 1 direct dependency and Python 3.11 or newer. import pyproj worked in 0.33 seconds. The wheel included compiled .so files and py.typed; pip-audit found 0 known vulnerabilities. Package metadata did not identify a license.
Binary wheels carry the native PROJ runtime, but coordinate operations may also need grid files. Check TransformerGroup.unavailable_operations before claiming a datum shift uses the preferred path. A connected service can enable PROJ network access; an offline deployment should prefetch the required grids with pyproj sync. Network fetching changes latency and availability, so it should be an explicit deployment choice rather than a surprise on the first transformation.
Axis order is the easiest way to return a plausible wrong result. EPSG authority definitions can specify latitude before longitude. Pass always_xy=True when application data follows the common longitude, latitude order. Inspect CRS.axis_info, area_of_use, and the chosen transformer's accuracy before bulk work. Build one Transformer and reuse it instead of repeating operation discovery for every coordinate.
Source installation is a different risk profile from the 0.6-second wheel install. pyproj 3.7.2 needs PROJ 9.4 or later, a matching database, headers, and a compiler toolchain. Mixed pip and conda libraries can point the extension at the wrong PROJ data directory. pyproj.show_versions() and pyproj.datadir.get_data_dir() expose the runtime versions and active data path when a deployment disagrees with a laptop.
Patterns
Project longitude and latitude transform-coordinates
from pyproj import Transformer
web_mercator = Transformer.from_crs(
'EPSG:4326', 'EPSG:3857', always_xy=True
)
x, y = web_mercator.transform(72.8777, 19.0760)`always_xy=True` makes 2 inputs mean longitude then latitude even when the authority axis order differs.
Reuse operation selection reuse-transformer
utm = Transformer.from_crs(4326, 32643, always_xy=True)
for lon, lat in points:
x, y = utm.transform(lon, lat)
save(x, y)Create 1 `Transformer` outside the loop so PROJ does not repeat database and operation selection for every point.
Transform NumPy arrays transform-arrays
import numpy as np
lons = np.array([72.87, 77.20, 88.36])
lats = np.array([19.07, 28.61, 22.57])
xs, ys = web_mercator.transform(lons, lats)
valid = np.isfinite(xs) & np.isfinite(ys)One array call crosses the compiled boundary once; check `isfinite` because an untransformable coordinate can return infinity.
Inspect axes and valid area inspect-crs
from pyproj import CRS
crs = CRS.from_epsg(32643)
print(crs.name)
print(crs.axis_info)
print(crs.area_of_use)
print(crs.to_authority())Read `axis_info` before deciding input order, and reject data outside the 1 declared area of use when accuracy matters.
Store a CRS as WKT serialize-crs
crs = CRS.from_user_input('EPSG:4326')
wkt = crs.to_wkt(version='WKT2_2019')
restored = CRS.from_wkt(wkt)WKT2 retains more CRS metadata than a short PROJ string and is safer for 1 persisted definition.
Measure an ellipsoidal route calculate-distance
from pyproj import Geod
geod = Geod(ellps='WGS84')
forward, back, metres = geod.inv(
72.8777, 19.0760,
77.2090, 28.6139,
)`Geod.inv()` expects 2 longitude-latitude pairs and returns forward azimuth, back azimuth, and distance in metres.
Measure geodesic area calculate-polygon-area
lons = [72.80, 72.95, 72.95, 72.80]
lats = [18.90, 18.90, 19.10, 19.10]
area_m2, perimeter_m = geod.polygon_area_perimeter(lons, lats)
area_m2 = abs(area_m2)The sign records ring direction; `abs()` discards that 1 orientation signal when only area is needed.
Allow remote transformation grids enable-grid-network
from pyproj import network
network.set_network_enabled(True)
assert network.is_network_enabled()Enabling this lets PROJ fetch missing grids at runtime, adding network latency and an external availability dependency.
Prepare grids for an offline region prefetch-grids
# inspect files first
pyproj sync --bbox 68,6,98,38 --list-files
# download the selected set
pyproj sync --bbox 68,6,98,38Use 1 bounded region instead of mirroring unrelated grids, and bake the resulting data directory into the deployment.
Find unavailable operations inspect-transform-options
from pyproj.transformer import TransformerGroup
group = TransformerGroup('EPSG:4267', 'EPSG:4326', always_xy=True)
for operation in group.transformers:
print(operation.description, operation.accuracy)
print(group.unavailable_operations)A nonempty unavailable list means at least 1 candidate cannot run, commonly because its grid is absent.
Reproject curved bounds transform-bounds
left, bottom, right, top = web_mercator.transform_bounds(
68.0, 6.0, 98.0, 38.0,
densify_pts=21,
)Sampling 21 points along each edge catches curvature that a four-corner transform can miss.
Show linked PROJ details diagnose-runtime
import pyproj
print(pyproj.__version__)
print(pyproj.proj_version_str)
print(pyproj.datadir.get_data_dir())
pyproj.show_versions()Compare all 3 version and path outputs when compiled code and the CRS database appear to come from different installations.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| geopandas | PyPI | Use it when whole geometry columns need CRS-aware reprojection and tabular operations. |
| shapely | PyPI | Use it for geometry construction, predicates, intersections, buffers, and repair; pair it with pyproj for CRS changes. |
| rasterio | PyPI | Use it when raster pixels, affine transforms, bounds, and reprojection must be handled together. |
| utm | PyPI | Use it for a narrow latitude-longitude to UTM conversion where general CRS lookup and datum operations are unnecessary. |
More data guides
numpy · fsspec · pandas · sqlalchemy · pyarrow · lxml · the whole shelf →
How this guide is made: grounded in the library's documentation, release notes, changelog, and issue history, on a fixed rubric — not a hands-on install of every release. The 50 most-downloaded entries are additionally install-verified in clean containers. Corrections: contact the desk.

