Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

from_cf constructor inherited from pyproj is broken. #2099

Open
aulemahal opened this issue Nov 7, 2022 · 1 comment
Open

from_cf constructor inherited from pyproj is broken. #2099

aulemahal opened this issue Nov 7, 2022 · 1 comment

Comments

@aulemahal
Copy link

aulemahal commented Nov 7, 2022

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

@dopplershift
Copy link
Contributor

Well, we still have the issue that we can't go from PROJ -> Cartopy CRS directly (see e.g. #1911 ), mainly due to needing to set the area of use. It would be great if we could eliminate our manual handling of that and rely more on PROJ, which would (I think) help somewhat with this issue. Might also want to talk to PyPROJ about making from_cf() a classmethod.

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

No branches or pull requests

2 participants