Repository: SciTools/cartopy

Issue:
Discontinuity in Interupted Goode Homolosine
The following code produces a plot containing a vertical discontinuity which appears to run through 0 longitude. 

import iris
import cartopy
import matplotlib.pyplot as plt
import iris.quickplot as qplt
import cartopy.crs as ccrs

cube = iris.load_cube(iris.sample_data_path('A1B.2098.pp'))

plt.axes(projection=ccrs.InterruptedGoodeHomolosine())

qplt.pcolormesh(cube)

plt.gca().coastlines()

plt.show()

![discontinuity](https://f.cloud.github.com/assets/4940023/1035783/f83ac962-0f2f-11e3-9082-a10f6fcd6177.png)

The plot also appears not to be plotting the correct data to the right of the discontinuity, as the uniform horizontal stripes do not look correct. 



Discussion:
A bit of preliminary diagnosis, expert assistance is required! Using a dummy data set to reproduce without iris and adding a matplotlib plot:

``` python
import numpy as np
import matplotlib.pyplot as plt
import cartopy.crs as ccrs

x = np.arange(0, 360, 2.5)
y = np.arange(-89, 90, 2)
X, Y = np.meshgrid(*[np.deg2rad(c) for c in (x, y)])
Z = np.cos(Y) + 0.375 * np.sin(2. * X)
Z[:, 1:3] = 2.
Z[:, -4:-2] = -2.

plt.pcolormesh(x, y, Z)

plt.figure()
ax = plt.axes(projection=ccrs.InterruptedGoodeHomolosine())
ax.coastlines()
ax.pcolormesh(x, y, Z, transform=ccrs.PlateCarree())
```

![data_0-360](https://f.cloud.github.com/assets/390602/1726112/2c796d52-628a-11e3-8a72-78ae58686bc3.png)
![goode_0-360](https://f.cloud.github.com/assets/390602/1726113/2ebc0a34-628a-11e3-9dae-4bb536089290.png)

So as @mikewalker92 said there is a discontinuity, but if I had my data defined on the longitude interval -180 to 180:

``` python
import numpy as np
import matplotlib.pyplot as plt
import cartopy.crs as ccrs

x = np.arange(-180, 180, 2.5)
y = np.arange(-89, 90, 2)
X, Y = np.meshgrid(*[np.deg2rad(c) for c in (x, y)])
Z = np.cos(Y) + 0.375 * np.sin(2. * X)
Z[:, 1:3] = 2.
Z[:, -4:-2] = -2.

plt.pcolormesh(x, y, Z)

plt.figure()
ax = plt.axes(projection=ccrs.InterruptedGoodeHomolosine())
ax.coastlines()
ax.pcolormesh(x, y, Z, transform=ccrs.PlateCarree())
```

![data_-180-180](https://f.cloud.github.com/assets/390602/1726131/70c5386a-628a-11e3-927f-ac5dd711468f.png)
![goode_-180-180](https://f.cloud.github.com/assets/390602/1726133/72b696be-628a-11e3-910e-6e73b8dee2ff.png)

Things look better (aside from data going outside the map boundary, which is likely a separate issue). Problem is I don't know how to fix it! But hopefully this might be helpful at least.

Very helpful. There is code in pcolormesh which looks for wrapping polygons (https://github.com/SciTools/cartopy/blob/master/lib/cartopy/mpl/geoaxes.py#L1109)[here]. Looks like it either isn't including Interrupted, or there is a case that it isn't handling properly.

Interestingly pcolor seems to work as expected. I guess they are rather different implementations under the hood though.

OK so I added `ccrs.InterruptedGoodeHomolosine` to `warp_proj_types` and the discontinuity is gone, which is good. However I can't figure out how to get it to stop drawing outside the map boundary. Any ideas on that one @pelson?

I managed to get a correct looking plot but I fear it is just a hack rather than a fix! As I said before I added `ccrs.InterruptedGoodeHomolosine` to `warp_proj_types` in the `_pcolormesh_patched()` method, and then just set the clip path of the resulting QuadMesh to the projection boundary with `collection.set_clip_path(self.outline_patch)` at the end of `_pcolormesh_patched()`.

Is that sensible? It feels like a hack but I couldn't figure out what was really wrong. If it sounds reasonable I'll submit a PR.

![goode_good](https://f.cloud.github.com/assets/390602/1732235/13e20bf6-631a-11e3-8681-13aac3176fd9.png)

(blue and red bands are there on purpose to make sure the data values are in the correct location, correspond to the first plot in my first comment)


Resolving patch:
diff --git a/lib/cartopy/mpl/geoaxes.py b/lib/cartopy/mpl/geoaxes.py
--- a/lib/cartopy/mpl/geoaxes.py
+++ b/lib/cartopy/mpl/geoaxes.py
@@ -1108,7 +1108,8 @@ def _pcolormesh_patched(self, *args, **kwargs):
         t = kwargs.get('transform', None)
         if isinstance(t, ccrs.CRS):
             wrap_proj_types = (ccrs._RectangularProjection,
-                               ccrs._WarpedRectangularProjection)
+                               ccrs._WarpedRectangularProjection,
+                               ccrs.InterruptedGoodeHomolosine)
             if isinstance(t, wrap_proj_types) and \
                     isinstance(self.projection, wrap_proj_types):
 
@@ -1178,8 +1179,14 @@ def _pcolormesh_patched(self, *args, **kwargs):
 
                     pts = pts.reshape((Ny, Nx, 2))
                     if np.any(~pcolor_data.mask):
+                        # plot with slightly lower zorder to avoid odd issue
+                        # where the main plot is obscured
+                        zorder = collection.zorder - .1
+                        kwargs.pop('zorder', None)
                         pcolor_col = self.pcolor(pts[..., 0], pts[..., 1],
-                                                 pcolor_data, **kwargs)
+                                                 pcolor_data, zorder=zorder,
+                                                 **kwargs)
+
                         pcolor_col.set_cmap(cmap)
                         pcolor_col.set_norm(norm)
                         pcolor_col.set_clim(vmin, vmax)
@@ -1191,6 +1198,10 @@ def _pcolormesh_patched(self, *args, **kwargs):
                         # this method
                         collection._wrapped_collection_fix = pcolor_col
 
+            # Clip the QuadMesh to the projection boundary, which is required
+            # to keep the shading inside the projection bounds.
+            collection.set_clip_path(self.outline_patch)
+
         return collection
 
     def pcolor(self, *args, **kwargs):


Test patch:
diff --git a/lib/cartopy/tests/mpl/baseline_images/mpl/test_mpl_integration/pcolormesh_global_wrap2.png b/lib/cartopy/tests/mpl/baseline_images/mpl/test_mpl_integration/pcolormesh_global_wrap2.png
Binary files a/lib/cartopy/tests/mpl/baseline_images/mpl/test_mpl_integration/pcolormesh_global_wrap2.png and b/lib/cartopy/tests/mpl/baseline_images/mpl/test_mpl_integration/pcolormesh_global_wrap2.png differ
diff --git a/lib/cartopy/tests/mpl/baseline_images/mpl/test_mpl_integration/pcolormesh_goode_wrap.png b/lib/cartopy/tests/mpl/baseline_images/mpl/test_mpl_integration/pcolormesh_goode_wrap.png
new file mode 100644
Binary files /dev/null and b/lib/cartopy/tests/mpl/baseline_images/mpl/test_mpl_integration/pcolormesh_goode_wrap.png differ
diff --git a/lib/cartopy/tests/mpl/test_mpl_integration.py b/lib/cartopy/tests/mpl/test_mpl_integration.py
--- a/lib/cartopy/tests/mpl/test_mpl_integration.py
+++ b/lib/cartopy/tests/mpl/test_mpl_integration.py
@@ -327,6 +327,20 @@ def test_pcolormesh_limited_area_wrap():
     ax.coastlines()
 
 
+@ImageTesting(['pcolormesh_goode_wrap'])
+def test_pcolormesh_goode_wrap():
+    # global data on an Interrupted Goode Homolosine projection
+    # shouldn't spill outside projection boundary
+    x = np.linspace(0, 360, 73)
+    y = np.linspace(-87.5, 87.5, 36)
+    X, Y = np.meshgrid(*[np.deg2rad(c) for c in (x, y)])
+    Z = np.cos(Y) + 0.375 * np.sin(2. * X)
+    Z = Z[:-1, :-1]
+    ax = plt.axes(projection=ccrs.InterruptedGoodeHomolosine())
+    ax.coastlines()
+    ax.pcolormesh(x, y, Z, transform=ccrs.PlateCarree())
+
+
 @ImageTesting(['quiver_plate_carree'])
 def test_quiver_plate_carree():
     x = np.arange(-60, 42.5, 2.5)