Rasterio supports three primary methods for transforming of coordinates from image pixel (row, col) to and from geographic/projected (x, y) coordinates. The interface for performing these coordinate transformations is available in rasterio.transforms through one of AffineTransformer, GCPTransformer, or RPCTransformer. The methods xy and rowcol are responsible for converting between (row, col) -> (x, y) and (x, y) -> (row, col), respectively.

Using Affine transformation matrix

rasterio.transform.AffineTransformer takes care of coordinate transformations given an Affine transformation matrix. For example

>>> transform = Affine(300.0379266750948, 0.0, 101985.0, 0.0,
                       -300.041782729805, 2826915.0)
>>> transformer = rasterio.transform.AffineTransformer(transform)
>>> transformer.xy(0, 0)
(102135.01896333754, 2826764.979108635)
>>> transformer.rowcol(102135.01896333754, 2826764.979108635)
(0, 0)

This is approximately equivalent to

>>> transform = Affine(300.0379266750948, 0.0, 101985.0, 0.0,
                       -300.041782729805, 2826915.0)
>>> transform * (0.5, 0.5)
(102135.01896333754, 2826764.979108635)
>>> ~transform * (102135.01896333754, 2826764.979108635)
(0.5, 0.5)

The dataset methods xy and index use rasterio.transform under the hood

>>> with'RGB.byte.tif') as src:
        print(src.xy(0, 0))
(102135.01896333754, 2826764.979108635)

Using Ground Control Points

>>> gcps = [GroundControlPoint(row=11521.5, col=0.5, x=-123.6185142817931, y=48.99561141948625, z=89.13533782958984, id='217', info=''),
            GroundControlPoint(row=11521.5, col=7448.5, x=-122.8802747777599, y=48.91210259315549, z=89.13533782958984, id='234', info=''),
            GroundControlPoint(row=0.5, col=0.5, x=-123.4809665720148, y=49.52809729106944, z=89.13533782958984, id='1', info=''),
            GroundControlPoint(row=0.5, col=7448.5, x=-122.7345733674704, y=49.44455878004666, z=89.13533782958984, id='18', info='')]
>>> transformer = rasterio.transform.GCPTransformer(gcps)
>>> transformer.xy(0, 0)
(-123.478928146887, 49.52808986989645)

Using Rational Polynomial Coefficients

For accuracy a height value is typically required when using RPCTransformer. By default, a value of 0 is assumed.

>>> with'RGB.byte.rpc.vrt') as src:
        transformer = rasterio.trasform.RPCTransformer(src.rpcs)
        transformer.xy(0, 0)
(-123.47959047080701, 49.52794990575094)

A first order correction would be to use a mean elevation value for the image

>>> with'RGB.byte.rpc.vrt') as src:
        transformer = rasterio.trasform.RPCTransformer(src.rpcs)
        transformer.xy(0, 0, zs=src.rpcs.height_off)
(-123.48096552376548, 49.528097381526386)

Better yet is to sample height values from a digital elevation model (DEM). RPCTransformer allows for options to be passed to GDALCreateRPCTransformer

>>> with'RGB.byte.rpc.vrt') as src:
        transformer = rasterio.trasform.RPCTransformer(src.rpcs, rpc_dem='vancouver-dem.tif')
        transformer.xy(0, 0)
(-123.47954729595642, 49.5279448909449)

See for more details.

Transformer Resources

The AffineTransformer is a pure Python class, however GCPTransformer and RPCTransformer make use of C/C++ GDAL objects. Explicit control of the transformer object can be achieved by use within a context manager or by calling close() method e.g.

>>> with rasterio.transform.RPCTransformer(rpcs) as transform:
        transform.xy(0, 0)
>>> transform.xy(0, 0)
ValueError: Unexpected NULL transformer


If RPC_DEM is specified in rpc_options, GDAL will maintain an open file handle to the DEM until the transformer is closed.