Coordinate Reference System Management

xarray “… is particularly tailored to working with netCDF files, which were the source of xarray’s data model…” (http://xarray.pydata.org).

For netCDF files, the GIS community uses CF conventions (http://cfconventions.org/).

Additionally, GDAL also supports these attributes:

  • spatial_ref (Well Known Text)

  • GeoTransform (GeoTransform array)

References:

Operations on xarray objects can cause data loss. Due to this, rioxarray writes and expects the spatial reference information to exist in the coordinates.

Conventions

rioxarray supports multiple conventions for storing geospatial metadata. The convention system provides a flexible way to read and write CRS and transform information.

Supported Conventions

  • CF (Climate and Forecasts): The default convention, using grid_mapping coordinates with attributes like spatial_ref, crs_wkt, and GeoTransform. This is the standard for netCDF files in the geospatial community.

How Conventions Work

  • Reading: If a convention is set globally, that convention is tried first for better performance. If not found, other conventions are tried as fallback. This allows you to optimize reads when you know the data format.

  • Writing: Uses the global convention setting (default: CF) or a per-method convention parameter.

Setting the Convention

You can set the convention globally using set_options():

from rioxarray import set_options
from rioxarray.enum import Convention

# Set globally - reads will try CF first, writes will use CF
set_options(convention=Convention.CF)

# Or use as a context manager
with set_options(convention=Convention.CF):
    # CF convention is tried first when reading
    crs = data.rio.crs
    # CF convention is used for writing
    data.rio.write_crs("EPSG:4326", inplace=True)

Or specify the convention per-method (for writing only):

from rioxarray.enum import Convention

data.rio.write_crs("EPSG:4326", convention=Convention.CF, inplace=True)

API Documentation

Accessing the CRS object

If you have opened a dataset and the Coordinate Reference System (CRS) can be determined, you can access it via the rio.crs accessor.

  1. Look in encoding of your data array for the grid_mapping coordinate name. Inside the grid_mapping coordinate first look for spatial_ref then crs_wkt and lastly the CF grid mapping attributes. This is in line with the Climate and Forecast (CF) conventions for storing the CRS as well as GDAL netCDF conventions.

  2. Look in the crs attribute and load in the CRS from there. This is for backwards compatibility with xarray.open_rasterio, which is deprecated since version 0.20.0. We recommend using rioxarray.open_rasterio instead.

The value for the crs is anything accepted by rasterio.crs.CRS.from_user_input()

If the CRS is not found using the search methods above, it also searches the data_vars and uses the first valid CRS found.

If you use one of xarray’s open methods such as xarray.open_dataset to load netCDF files with the default engine, it is recommended to use decode_coords="all". This will load the grid mapping variable into coordinates for compatibility with rioxarray.

[ ]:
import rioxarray  # activate the rio accessor
import xarray
from affine import Affine
[ ]:
rds = xarray.open_dataset("../../test/test_data/input/PLANET_SCOPE_3D.nc", decode_coords="all")
[ ]:
rds.green.attrs
{'units': 'DN', 'nodata': 0.0}
[ ]:
rds.green.spatial_ref
<xarray.DataArray 'spatial_ref' ()>
array(0)
Coordinates:
    spatial_ref  int64 0
Attributes:
    spatial_ref:  PROJCS["WGS 84 / UTM zone 22S",GEOGCS["WGS 84",DATUM["WGS_1...
[ ]:
rds.green.rio.crs
CRS.from_epsg(32722)

Setting the CRS

Use the rio.write_crs method to set the CRS on your xarray.Dataset or xarray.DataArray. This modifies the xarray.Dataset or xarray.DataArray and sets the CRS using the configured convention (default: CF).

Note: It is recommended to use rio.write_crs() if you want the CRS to persist on the Dataset/DataArray and to write convention-compliant metadata. Calling only rio.set_crs() is lossy and will not modify the Dataset/DataArray metadata.

[6]:
xda = xarray.DataArray(1)
xda.rio.write_crs(4326, inplace=True)
xda.spatial_ref
[6]:
<xarray.DataArray 'spatial_ref' ()>
array(0)
Coordinates:
    spatial_ref  int64 0
Attributes:
    crs_wkt:                      GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["...
    semi_major_axis:              6378137.0
    semi_minor_axis:              6356752.314245179
    inverse_flattening:           298.257223563
    reference_ellipsoid_name:     WGS 84
    longitude_of_prime_meridian:  0.0
    prime_meridian_name:          Greenwich
    geographic_crs_name:          WGS 84
    grid_mapping_name:            latitude_longitude
    spatial_ref:                  GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["...
[7]:
xda.rio.crs
[7]:
CRS.from_epsg(4326)

Spatial dimensions

Only 1-dimensional X and Y dimensions are supported.

The expected X/Y dimension names searched for in the coords are:

  • x | y

  • longitude | latitude

  • Coordinates (coords) with the CF attributes in attrs:

    • axis: X | Y

    • standard_name: longitude | latitude or projection_x_coordinate | projection_y_coordinate

Option 1: Write the CF attributes for non-standard dimension names

If you don’t want to rename your dimensions/coordinates, you can write the CF attributes so the coordinates can be found.

[ ]:
rds.rio.write_crs(
    4326
    inplace=True,
).rio.set_spatial_dims(
    x_dim="lon",
    y_dim="lat"
    inplace=True,
).rio.write_coordinate_system(inplace=True)

Option 2: Rename your coordinates

xarray.Dataset.rename

[ ]:
rds = rds.rename(lon=longitude, lat=latitude)

Setting the transform of the dataset

The transform can be calculated from the coordinates of your data. This method is useful if your netCDF file does not have coordinates present. Use the rio.write_transform method to set the transform on your xarray.Dataset or xarray.DataArray.

[8]:
transform = Affine(3.0, 0.0, 466266.0, 0.0, -3.0, 8084700.0)
xda.rio.write_transform(transform, inplace=True)
xda.spatial_ref.GeoTransform
[8]:
'466266.0 3.0 0.0 8084700.0 0.0 -3.0'
[9]:
xda.rio.transform()
[9]:
Affine(3.0, 0.0, 466266.0,
       0.0, -3.0, 8084700.0)