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:
GDAL: https://gdal.org/drivers/raster/netcdf.html#georeference
pyproj: https://pyproj4.github.io/pyproj/stable/build_crs_cf.html
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_mappingcoordinates with attributes likespatial_ref,crs_wkt, andGeoTransform. 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
conventionsetting (default: CF) or a per-methodconventionparameter.
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.
Look in
encodingof your data array for thegrid_mappingcoordinate name. Inside thegrid_mappingcoordinate first look forspatial_refthencrs_wktand 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.Look in the
crsattribute and load in the CRS from there. This is for backwards compatibility withxarray.open_rasterio, which is deprecated since version 0.20.0. We recommend usingrioxarray.open_rasterioinstead.
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...- 0
array(0)
- spatial_ref()int640
- spatial_ref :
- PROJCS["WGS 84 / UTM zone 22S",GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4326"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",-51],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",10000000],UNIT["metre",1,AUTHORITY["EPSG","9001"]],AXIS["Easting",EAST],AXIS["Northing",NORTH],AUTHORITY["EPSG","32722"]]
array(0)
- spatial_ref :
- PROJCS["WGS 84 / UTM zone 22S",GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4326"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",-51],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",10000000],UNIT["metre",1,AUTHORITY["EPSG","9001"]],AXIS["Easting",EAST],AXIS["Northing",NORTH],AUTHORITY["EPSG","32722"]]
[ ]:
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["...- 0
array(0)
- spatial_ref()int640
- crs_wkt :
- GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AXIS["Latitude",NORTH],AXIS["Longitude",EAST],AUTHORITY["EPSG","4326"]]
- 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["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AXIS["Latitude",NORTH],AXIS["Longitude",EAST],AUTHORITY["EPSG","4326"]]
array(0)
- crs_wkt :
- GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AXIS["Latitude",NORTH],AXIS["Longitude",EAST],AUTHORITY["EPSG","4326"]]
- 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["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AXIS["Latitude",NORTH],AXIS["Longitude",EAST],AUTHORITY["EPSG","4326"]]
[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 inattrs: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
[ ]:
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)