SciTools/cartopy

AttributeError returned by caropy.crs.CRS with proj4_params

Closed this issue · 4 comments

Example:

Python 3.5.1 (default, Dec  7 2015, 21:59:10) 
[GCC 4.2.1 Compatible Apple LLVM 7.0.0 (clang-700.1.76)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import cartopy
>>> crs = cartopy.crs.Mercator()
>>> crs
<cartopy.crs.Mercator object at 0x1039d5a40>
>>> proj4 = crs.proj4_params
>>> proj4
{'ellps': 'WGS84', 'k': 1, 'proj': 'merc', 'lon_0': 0.0, 'units': 'm'}
>>> cartopy.crs.CRS(proj4)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "cartopy/_crs.pyx", line 142, in cartopy._crs.CRS.__init__ (lib/cartopy/_crs.c:2771)
AttributeError: 'cartopy._crs.CRS' object has no attribute 'globe'
>>> 

What am I missing? Or is this a bug?

That's a really interesting question - thank you!

The CRS class wasn't really designed for direct use (the focus is more on Projection), but if you're just doing low-level point/vector transformations then I suppose there's no reason why you couldn't use it... except for the problem you've found.

CRS is a class defined in Cython code, so in reality it's a C extension type. This means it doesn't have an attribute dictionary - only explicitly declared attributes exist. Looking in _crs.pxd we can see the declared attributes are only: proj4, proj4_init, and proj4_params. Hence the error that you're seeing - there really is no globe attribute for the code to access.

So how come Mercator and the other CRS subclasses actually work? A Python subclass of CRS does have an attribute dictionary, so it's possible to add any attributes. In the general case:

>>> class Foo(object): pass
...
>>> foo = Foo()
>>> foo.bar = 'This works!'

So... where does that leave us? A simple workaround for now is just to create a trivial Python subclass:

>>> class Foo(cartopy.crs.CRS): pass
...
>>> Foo({'proj': 'lonlat'}).proj4_params
{'ellps': 'WGS84', 'proj': 'lonlat'}

But it seems reasonable that we should also make CRS usable on its own! (i.e. possibly just as simple as adding globe to the list of declared attributes, possibly we'll also need to define a property) @pelson - any idea why CRS shouldn't be usable?

... cartopy.crs.CRS(proj4) ...

NB. If you're trying to define a new map then you don't want to be doing this. A map needs to be a subclass of Projection with a defined boundary, etc.

Same issue as #813.

You guys should fix this issue. I suggest removing the raise error:
@property def crs(self): """Return the coordinate reference system (CRS) as a CFProjection object.""" if 'crs' in self._data_array.coords: return self._data_array.coords['crs'].item() raise AttributeError('crs attribute is not available.')

This will allow coders to abstract the class.