mrkeyoor.com_
Wed 23 Sept 00:34 UTC
PyPIDataupdated 19 Sept 2026

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.

Verdict

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

Lab card: what happened when we installed pyprojScreenshot of pyproj documentation
Install✓ · 0.6s2 packages on disk · 32 MB
Importimport pyproj in 0.33s · compiled extensions · py.typed · requires Python >=3.11
Known vulns0(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.

API stability4/5`CRS`, `Transformer`, and `Geod` remain the main public objects, and the recommended `Transformer.from_crs()` path has survived the 3.x line. Older module-level transform functions remain deprecated, so copied pre-2.0 examples are a maintenance smell. Version 3.7.2 changes deployment compatibility by requiring Python 3.11 and PROJ 9.4, even though the day-to-day transformation calls stayed recognizable.
Docs4/5The stable site documents the API, installation matrix, CRS compatibility, axis-order warning, transformation grids, network controls, data directories, and migration from older pyproj calls. `TransformerGroup` examples make unavailable operations inspectable. Readers still need the separate PROJ documentation to understand individual pipelines and grid packages, so diagnosing accuracy can cross two documentation systems.
Maintenance5/5Release 3.7.2 shipped on August 14, 2025, and GitHub records a repository push on August 21, 2026. GitHub shows 58 open issues and pull requests, 1,220 stars, and an unarchived project. Recent release work includes Windows ARM64 wheels, free-threading support, current build tooling, and higher PROJ and Python floors, all concrete maintenance for a compiled cross-platform binding.
Ecosystem5/5The package records 6,098,525 weekly downloads and sits underneath common Python geospatial workflows involving GeoPandas, raster data, plotting, and geometry libraries. It accepts authority codes, WKT, PROJ strings, and native CRS objects, which makes it the shared conversion layer between tools. The cost is environmental: accurate results can depend on external PROJ grid data as well as Python package versions.

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.
Skip it if

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,38

Use 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

PackageRegistryPick it when
geopandasPyPIUse it when whole geometry columns need CRS-aware reprojection and tabular operations.
shapelyPyPIUse it for geometry construction, predicates, intersections, buffers, and repair; pair it with pyproj for CRS changes.
rasterioPyPIUse it when raster pixels, affine transforms, bounds, and reprojection must be handled together.
utmPyPIUse 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.