CRS Compatibility Guide for Geospatial Python

This is meant to be a guide to help you along the way of you use pyproj.crs.CRS with other Python Geospatial libraries.

Note

WKT2 is the best format for storing your CRS according to the PROJ FAQ.

osgeo/gdal

https://github.com/osgeo/gdal

Converting from osgeo.osr.SpatialReference to pyproj.crs.CRS

from osgeo.osr import SpatialReference
from pyproj.crs import CRS

osr_crs = SpatialReference()
osr_crs.ImportFromEPSG(4326)
if osgeo.version_info.major < 3:
    proj_crs = CRS.from_wkt(osr_crs.ExportToWkt())
else:
    proj_crs = CRS.from_wkt(osr_crs.ExportToWkt(["FORMAT=WKT2_2018"]))

Converting from pyproj.crs.CRS to osgeo.osr.SpatialReference

Warning

WKT2 is only supported in GDAL 3+

import osgeo
from osgeo.osr import SpatialReference
from pyproj.crs import CRS
from pyproj.enums import WktVersion

proj_crs = CRS.from_epsg(4326)

osr_crs = SpatialReference()
if osgeo.version_info.major < 3:
    osr_crs.ImportFromWkt(proj_crs.to_wkt(WktVersion.WKT1_GDAL))
else:
    osr_crs.ImportFromWkt(proj_crs.to_wkt())

rasterio

https://github.com/mapbox/rasterio

Converting from rasterio.crs.CRS to pyproj.crs.CRS

If you have rasterio >= 1.0.14, then you can pass in the rasterio.crs.CRS directly:

import rasterio
import rasterio.crs
from pyproj.crs import CRS

with rasterio.Env(OSR_WKT_FORMAT="WKT2_2018"):
    rio_crs = rasterio.crs.CRS.from_epsg(4326)
    proj_crs = CRS.from_user_input(rio_crs)

Otherwise, you should use the wkt property:

import rasterio.crs
from pyproj.crs import CRS

with rasterio.Env(OSR_WKT_FORMAT="WKT2_2018"):
    rio_crs = rasterio.crs.CRS.from_epsg(4326)
    proj_crs = CRS.from_wkt(rio_crs.wkt)

Converting from pyproj.crs.CRS to rasterio.crs.CRS

Warning

WKT2 is only supported in GDAL 3+

If you have rasterio >= 1.0.26 and GDAL 3+, then you can pass in the pyproj.crs.CRS directly:

import rasterio.crs
from pyproj.crs import CRS

proj_crs = CRS.from_epsg(4326)
rio_crs = rasterio.crs.CRS.from_user_input(proj_crs)

If you want to be compatible across GDAL/rasterio versions, you can do:

from packaging import version

import rasterio
import rasterio.crs
from pyproj.crs import CRS
from pyproj.enums import WktVersion

proj_crs = CRS.from_epsg(4326)
if version.parse(rasterio.__gdal_version__) < version.parse("3.0.0")
    rio_crs = rasterio.crs.CRS.from_wkt(proj_crs.to_wkt(WktVersion.WKT1_GDAL))
else:
    rio_crs = rasterio.crs.CRS.from_wkt(proj_crs.to_wkt())

fiona

https://github.com/Toblerity/Fiona

Converting from fiona CRS to pyproj.crs.CRS

Fiona currently stores the CRS as a PROJ string dictionary in the crs attribute. As such, it is best to use the crs_wkt attribute.

It is also useful to know that plans exist to add CRS class. Related GitHub issue here.

Example:

import fiona
from pyproj.crs import CRS

with fiona.Env(OSR_WKT_FORMAT="WKT2_2018"), fiona.open(...) as fds:
    proj_crs = CRS.from_wkt(fds.crs_wkt)

Converting from pyproj.crs.CRS for fiona

Warning

WKT2 is only supported in GDAL 3+

If you want to be compatible across GDAL versions, you can do:

from packaging import version

import fiona
from pyproj.crs import CRS

proj_crs = CRS.from_epsg(4326)

if version.parse(fiona.__gdal_version__) < version.parse("3.0.0"):
    fio_crs = proj_crs.to_wkt(WktVersion.WKT1_GDAL)
else:
    # GDAL 3+ can use WKT2
    fio_crs = dc_crs.to_wkt()

# with fiona.open(..., "w", crs_wkt=fio_crs) as fds:
#     ...

geopandas

https://github.com/geopandas/geopandas

Also see the geopandas guide for upgrading to use pyproj CRS class

Preparing pyproj.crs.CRS for geopandas

import fiona
import geopandas
from pyproj.crs import CRS
from pyproj.enums import WktVersion

proj_crs = CRS.from_epsg(4326)

if version.parse(geopandas.__version__) >= version.parse("0.7.0"):
    # geopandas uses pyproj.crs.CRS
    geo_crs = proj_crs
elif version.parse(geopandas.__version__) >= version.parse("0.6.0"):
    # this version of geopandas uses always_xy=True so WKT version is safe
    if version.parse(fiona.__gdal_version__) < version.parse("3.0.0"):
        geo_crs = proj_crs.to_wkt(WktVersion.WKT1_GDAL)
    else:
        # GDAL 3+ can use WKT2
        geo_crs = dc_crs.to_wkt()
else:
    geo_crs = dc_crs.to_proj4()

geopandas to pyproj.crs.CRS

pyproj.crs.CRS.from_user_input() can handle anything across the geopandas versions. The only gotcha would be if it is None.

import geopandas
from pyproj.crs import CRS

gdf = geopandas.read_file(...)
proj_crs = CRS.from_user_input(gdf.crs)

cartopy

https://github.com/SciTools/cartopy

Note

These examples require cartopy 0.20+

Preparing pyproj.crs.CRS for cartopy.crs.CRS

Warning

This only works for CRS created with WKT2, PROJ JSON, or a spatial reference ID (i.e. EPSG) with the area of use defined. Otherwise, the x_limits and y_limits will not work.

import cartopy.crs as ccrs
from pyproj.crs import CRS

# geographic
proj_crs = CRS.from_epsg(4326)
cart_crs = ccrs.CRS(proj_crs)

# projected
proj_crs = CRS.from_epsg(6933)
cart_crs = ccrs.Projection(proj_crs)

Preparing cartopy.crs.CRS for pyproj.crs.CRS

Note

cartopy.crs.CRS inherits from pyproj.crs.CRS, so it should behave like a pyproj.crs.CRS.

from cartopy.crs import PlateCarree
from pyproj.crs import CRS

cart_crs = PlateCarree()
proj_crs = CRS.from_user_input(cart_crs)

pycrs

https://github.com/karimbahgat/PyCRS

Warning

Currently does not support WKT2

Preparing pyproj.crs.CRS for pycrs

import pycrs
from pyproj.crs import CRS

proj_crs = CRS.from_epsg(4326)
py_crs = pycrs.parse.from_ogc_wkt(proj_crs.to_wkt("WKT1_GDAL"))

Preparing pycrs for pyproj.crs.CRS

import pycrs
from pyproj.crs import CRS

py_crs = pycrs.parse.from_epsg_code(4326)
proj_crs = CRS.from_wkt(py_crs.to_ogc_wkt())