Skip to content

from_cf constructor inherited from pyproj is broken. #2099

@aulemahal

Description

@aulemahal

Description

For 2 years now, pyproj has been able to parse CF "grid mapping" attributes (http://cfconventions.org/Data/cf-conventions/cf-conventions-1.10/cf-conventions.html#coordinate-system) (pyproj4/pyproj#660).

This is done through the pyproj.CRS.from_cf method. Sadly, it is a staticmethod, and not a classmethod. Thus, the result is a pyproj.CRS object, even we call the method on cartopy.crs.Projection. And this is does not work as expected with matplotlib.

The code seems to be done this way because even though the from_cf method lives in CRS, it actually returns children of CRS, but the exact type depends on the inputs. Would that mean it is not compatible with cartopy?

It would be interesting if the function could be overrided to actually return a cartopy object. Also, this direct use of cartopy.crs.Projection doesn't seem much documented. Is it uncommon? In that case, would a cartopy.crs.from_cf function be interesting?

I may have time to help on this. My long term goal would be to make cf-xarray aware of these grid_mappings CF variables and able to inject the correct cartopy CRS argument to matplotlib's transform argument.

Code to reproduce

import matplotlib.pyplot as plt
import cartopy.crs as ccrs

cf_attrs = {'grid_mapping_name': 'rotated_latitude_longitude',
 'grid_north_pole_latitude': 42.5,
 'grid_north_pole_longitude': 83.0,
 'north_pole_grid_longitude': 0.0}
rp = ccrs.Projection.from_cf(cf_attrs)
fig, ax = plt.subplots(subplot_kw={'projection': rp})

Traceback

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In [15], line 6
      1 cf_attrs = {'grid_mapping_name': 'rotated_latitude_longitude',
      2  'grid_north_pole_latitude': 42.5,
      3  'grid_north_pole_longitude': 83.0,
      4  'north_pole_grid_longitude': 0.0}
      5 rp = ccrs.Projection.from_cf(cf_attrs)
----> 6 fig, ax = plt.subplots(subplot_kw={'projection': rp})

File /exec/pbourg/.conda/cf_xarray_test/lib/python3.10/site-packages/matplotlib/pyplot.py:1443, in subplots(nrows, ncols, sharex, sharey, squeeze, width_ratios, height_ratios, subplot_kw, gridspec_kw, **fig_kw)
   1299 """
   1300 Create a figure and a set of subplots.
   1301 
   (...)
   1440 
   1441 """
   1442 fig = figure(**fig_kw)
-> 1443 axs = fig.subplots(nrows=nrows, ncols=ncols, sharex=sharex, sharey=sharey,
   1444                    squeeze=squeeze, subplot_kw=subplot_kw,
   1445                    gridspec_kw=gridspec_kw, height_ratios=height_ratios,
   1446                    width_ratios=width_ratios)
   1447 return fig, axs

File /exec/pbourg/.conda/cf_xarray_test/lib/python3.10/site-packages/matplotlib/figure.py:894, in FigureBase.subplots(self, nrows, ncols, sharex, sharey, squeeze, width_ratios, height_ratios, subplot_kw, gridspec_kw)
    891     gridspec_kw['width_ratios'] = width_ratios
    893 gs = self.add_gridspec(nrows, ncols, figure=self, **gridspec_kw)
--> 894 axs = gs.subplots(sharex=sharex, sharey=sharey, squeeze=squeeze,
    895                   subplot_kw=subplot_kw)
    896 return axs

File /exec/pbourg/.conda/cf_xarray_test/lib/python3.10/site-packages/matplotlib/gridspec.py:308, in GridSpecBase.subplots(self, sharex, sharey, squeeze, subplot_kw)
    306         subplot_kw["sharex"] = shared_with[sharex]
    307         subplot_kw["sharey"] = shared_with[sharey]
--> 308         axarr[row, col] = figure.add_subplot(
    309             self[row, col], **subplot_kw)
    311 # turn off redundant tick labeling
    312 if sharex in ["col", "all"]:

File /exec/pbourg/.conda/cf_xarray_test/lib/python3.10/site-packages/matplotlib/figure.py:743, in FigureBase.add_subplot(self, *args, **kwargs)
    740 if (len(args) == 1 and isinstance(args[0], Integral)
    741         and 100 <= args[0] <= 999):
    742     args = tuple(map(int, str(args[0])))
--> 743 projection_class, pkw = self._process_projection_requirements(
    744     *args, **kwargs)
    745 ax = subplot_class_factory(projection_class)(self, *args, **pkw)
    746 key = (projection_class, pkw)

File /exec/pbourg/.conda/cf_xarray_test/lib/python3.10/site-packages/matplotlib/figure.py:1682, in FigureBase._process_projection_requirements(self, axes_class, polar, projection, *args, **kwargs)
   1680         kwargs.update(**extra_kwargs)
   1681     else:
-> 1682         raise TypeError(
   1683             f"projection must be a string, None or implement a "
   1684             f"_as_mpl_axes method, not {projection!r}")
   1685 if projection_class.__name__ == 'Axes3D':
   1686     kwargs.setdefault('auto_add_to_figure', False)

TypeError: projection must be a string, None or implement a _as_mpl_axes method, not <Derived Geographic 2D CRS: {"$schema": "https://proj.org/schemas/v0.2/projjso ...>
Name: undefined
Axis Info [ellipsoidal]:
- lon[east]: Longitude (degree)
- lat[north]: Latitude (degree)
Area of Use:
- undefined
Coordinate Operation:
- name: Pole rotation (netCDF CF convention)
- method: Pole rotation (netCDF CF convention)
Datum: World Geodetic System 1984
- Ellipsoid: WGS 84
- Prime Meridian: Greenwich
Full environment definition

Operating system

Linux 64

Cartopy version

0.21.0

pyproj version

3.4.0

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions