Skip to content

Conversation

@aulemahal
Copy link

@aulemahal aulemahal commented Jul 14, 2025

This fixes #2099.

Rationale

As detailed in #2099, the from_cf class method inherited from pyproj does not work with cartopy as it creates a pyproj class, and not the cartopy class it was called from.

The following was the closest I could get:

import cartopy.crs
import pyproj

p = cartopy.crs.Projection(pyproj.CRS.from_cf(cf_attrs))

However, as p.bounds is None here, plotting with matplotlib fails if you pass this as the projection of the axes.

Thus, I thought it would be useful to be able to create the exact cartopy projection needed from a set of CF convention attributes.

Implications

The only "breaking" change here is that calling from_cf on a projection that does not map to a CF grid mapping will fail with a NotImplementedError instead of returning a pyproj object. But that behaviour was already broken I would say.

This PR exposes cartopy.crs.from_cf that takes in all attributes of the grid mapping variable. It uses a lookup dict to map from the CF grid_mapping_name to cartopy's Projection and calls that projection's from_cf constructor.

The PR does not try to validate CF attributes, except that it will raise a KeyError if an attribute that the conventions does not flag as optional is missing. The mappings of grid mapping names and attributes is based on the description from the conventions themselves and on this (old) note from the CF conventions wiki.

Checklist

  • Example of use:
import cartopy.crs as ccrs
import xarray as xr

ds = xr.open_dataset('rotated_pole_example.nc')

proj = ccrs.from_cf(**ds.rotated_pole.attrs)
  • Tests added (minimal)

  • I did sign the CLA.

@CLAassistant
Copy link

CLAassistant commented Jul 14, 2025

CLA assistant check
All committers have signed the CLA.

aulemahal added a commit to Ouranosinc/xscen that referenced this pull request Sep 2, 2025
<!-- Please ensure the PR fulfills the following requirements! -->
<!-- If this is your first PR, make sure to add your details to the
AUTHORS.rst! -->
### Pull Request Checklist:
- [ ] This PR addresses an already opened issue (for bug fixes /
features)
    - This PR fixes #xyz
- [x] (If applicable) Documentation has been added / updated (for bug
fixes / features).
- [x] (If applicable) Tests have been added.
- [x] This PR does not seem to break the templates.
- [x] CHANGELOG.rst has been updated (with summary of main changes).
- [x] Link to issue (:issue:`number`) and pull request (:pull:`number`)
has been added.

### What kind of change does this PR introduce?
Two new functions in `xs.spatial`
* `rotate_vectors` to rotate vectors from their native grid axes to
actual S-N W-E axes. Mostly useful for wind that comes out of some
models. Can also do the opposite rotation.
* `get_crs` to get a cartopy crs from a grid mapping variable. This is
only temporary, waiting for my PR in cartopy: SciTools/cartopy#2548.

### Does this PR introduce a breaking change?
No.

### Other information:
@aslibese fyi
@aulemahal aulemahal marked this pull request as ready for review October 24, 2025 18:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

from_cf constructor inherited from pyproj is broken.

3 participants