Repository: sunpy/sunpy

Issue:
The repr of GenericMap raises an error if the WCS is an unknown type.
Make a WCS with some nonsense in CTYPE1 CTYPE2 and then call `repr(smap)`


Discussion:
@Cadair I would like to take this issue. May I?
You don't need to ask for permission to tackle an issue. 
Okay thanks, From now on I would just inform about me taking the issue.
```python
In [1]: from sunpy.data.sample import AIA_171_IMAGE; import sunpy.map; import matplotlib.pyplot as plt

In [2]: m = sunpy.map.Map(AIA_171_IMAGE)

In [3]: m.meta['CTYPE1'] = "STUART1"

In [4]: m.meta['CTYPE2'] = "STUART2"

In [5]: m
```
```
Out[5]: ---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
/opt/miniconda/envs/sunpy-dev/lib/python3.6/site-packages/IPython/core/formatters.py in __call__(self, obj)
    700                 type_pprinters=self.type_printers,
    701                 deferred_pprinters=self.deferred_printers)
--> 702             printer.pretty(obj)
    703             printer.flush()
    704             return stream.getvalue()

/opt/miniconda/envs/sunpy-dev/lib/python3.6/site-packages/IPython/lib/pretty.py in pretty(self, obj)
    393                             if callable(meth):
    394                                 return meth(obj, self, cycle)
--> 395             return _default_pprint(obj, self, cycle)
    396         finally:
    397             self.end_group()

/opt/miniconda/envs/sunpy-dev/lib/python3.6/site-packages/IPython/lib/pretty.py in _default_pprint(obj, p, cycle)
    508     if _safe_getattr(klass, '__repr__', None) is not object.__repr__:
    509         # A user-provided repr. Find newlines and replace them with p.break_()
--> 510         _repr_pprint(obj, p, cycle)
    511         return
    512     p.begin_group(1, '<')

/opt/miniconda/envs/sunpy-dev/lib/python3.6/site-packages/IPython/lib/pretty.py in _repr_pprint(obj, p, cycle)
    699     """A pprint that just redirects to the normal repr function."""
    700     # Find newlines and replace them with p.break_()
--> 701     output = repr(obj)
    702     for idx,output_line in enumerate(output.splitlines()):
    703         if idx:

~/Git/sunpy/sunpy/map/mapbase.py in __repr__(self)
    247            dim=u.Quantity(self.dimensions),
    248            scale=u.Quantity(self.scale),
--> 249            coord=self.coordinate_frame,
    250            refpix=u.Quantity(self.reference_pixel),
    251            refcoord=u.Quantity((self.reference_coordinate.data.lon,

~/Git/sunpy/sunpy/map/mapbase.py in coordinate_frame(self)
    305         information for this Map.
    306         """
--> 307         return astropy.wcs.utils.wcs_to_celestial_frame(self.wcs)
    308 
    309     def _as_mpl_axes(self):

~/Git/astropy/astropy/wcs/utils.py in wcs_to_celestial_frame(wcs)
    203             if frame is not None:
    204                 return frame
--> 205     raise ValueError("Could not determine celestial frame corresponding to "
    206                      "the specified WCS object")
    207 

ValueError: Could not determine celestial frame corresponding to the specified WCS object
```

From this error, it looks like it's the `coordinate_frame` property that needs the fix. I think it should probably return `None` if the lookup fails.
I tried returning None if the lookup failed in ```coordinate_frame()``` function of ```mapbase```.
```
try:
    return astropy.wcs.utils.wcs_to_celestial_frame(self.wcs)
except ValueError:
    return None
```
However, this leads to new errors such as ```AttributeError: 'NoneType' object has no attribute 'name'``` when calling ```repr(mapobj)```

Resolving patch:
diff --git a/sunpy/map/mapbase.py b/sunpy/map/mapbase.py
--- a/sunpy/map/mapbase.py
+++ b/sunpy/map/mapbase.py
@@ -229,7 +229,7 @@ def __repr__(self):
                    Observation Date:\t {date}
                    Exposure Time:\t\t {dt:f}
                    Dimension:\t\t {dim}
-                   Coordinate System:\t {coord.name}
+                   Coordinate System:\t {coord}
                    Scale:\t\t\t {scale}
                    Reference Pixel:\t {refpix}
                    Reference Coord:\t {refcoord}
@@ -239,7 +239,7 @@ def __repr__(self):
                                dt=self.exposure_time,
                                dim=u.Quantity(self.dimensions),
                                scale=u.Quantity(self.scale),
-                               coord=self.coordinate_frame,
+                               coord=self._coordinate_frame_name,
                                refpix=u.Quantity(self.reference_pixel),
                                refcoord=u.Quantity((self.reference_coordinate.data.lon,
                                                     self.reference_coordinate.data.lat)),
@@ -266,8 +266,6 @@ def wcs(self):
         """
         The `~astropy.wcs.WCS` property of the map.
         """
-
-
         # Construct the WCS based on the FITS header, but don't "do_set" which
         # analyses the FITS header for correctness.
         with warnings.catch_warnings():
@@ -322,9 +320,20 @@ def wcs(self):
     def coordinate_frame(self):
         """
         An `astropy.coordinates.BaseFrame` instance created from the coordinate
-        information for this Map.
+        information for this Map, or None if the frame cannot be determined.
         """
-        return astropy.wcs.utils.wcs_to_celestial_frame(self.wcs)
+        try:
+            return astropy.wcs.utils.wcs_to_celestial_frame(self.wcs)
+        except ValueError as e:
+            warnings.warn(f'Could not determine coordinate frame from map metadata',
+                          SunpyUserWarning)
+            return None
+
+    @property
+    def _coordinate_frame_name(self):
+        if self.coordinate_frame is None:
+            return 'Unknown'
+        return self.coordinate_frame.name
 
     def _as_mpl_axes(self):
         """


Test patch:
diff --git a/sunpy/map/tests/test_mapbase.py b/sunpy/map/tests/test_mapbase.py
--- a/sunpy/map/tests/test_mapbase.py
+++ b/sunpy/map/tests/test_mapbase.py
@@ -719,3 +719,9 @@ def test_missing_metadata_warnings():
 
 def test_fits_header(aia171_test_map):
     assert isinstance(aia171_test_map.fits_header, fits.Header)
+
+
+def test_bad_coordframe_repr(generic_map):
+    generic_map.meta['CTYPE1'] = "STUART1"
+    generic_map.meta['CTYPE2'] = "STUART2"
+    assert 'Unknown' in generic_map.__repr__()