Repository: SciTools/cartopy

Issue:
Bug for Reprojecting an Image
![Garbled image](http://i.stack.imgur.com/lCDXN.png)

This bug was originally described on stackoverflow [here](http://stackoverflow.com/questions/19127697/difficulty-reprojecting-an-image-with-cartopy).

Images are getting garbled when reprojecting.

Replace

```
ax = plt.axes(projection=ccrs.PlateCarree())
```

with

```
ax = plt.axes(projection=ccrs.LambertConformal())
```

in the code below to reproduce the bug.

```
import os
import matplotlib.pyplot as plt

from cartopy import config
import cartopy.crs as ccrs
import cartopy.feature as cfeature

fig = plt.figure(figsize=(8, 10))

img_extent = (-120.67660000000001, -106.32104523100001, 13.2301484511245, 30.766899999999502)
# Modis image file available in Cartopy code base
img = plt.imread('/tmp/Miriam.A2012270.2050.2km.jpg')

ax = plt.axes(projection=ccrs.PlateCarree())
plt.title('Hurricane Miriam from the Aqua/MODIS satellite\n'
          '2012 09/26/2012 20:50 UTC')

ax.set_extent([-125, -105, 10, 35], ccrs.Geodetic())

ax.imshow(img, origin='upper', extent=img_extent, transform=ccrs.PlateCarree())
ax.coastlines(resolution='50m', color='black', linewidth=1)
ax.gridlines()

plt.show()
```



Discussion:

Resolving patch:
diff --git a/lib/cartopy/img_transform.py b/lib/cartopy/img_transform.py
--- a/lib/cartopy/img_transform.py
+++ b/lib/cartopy/img_transform.py
@@ -321,4 +321,25 @@ def regrid(array, source_x_coords, source_y_coords, source_cs, target_proj,
             new_array.mask[non_self_inverse_points] = True
         else:
             new_array = np.ma.array(new_array, mask=non_self_inverse_points)
+    # Transform the target points to the source projection and mask any points
+    # that fall outside the original source domain.
+    target_in_source_xyz = source_cs.transform_points(
+        target_proj, target_x_points, target_y_points)
+    target_in_source_x = target_in_source_xyz[..., 0]
+    target_in_source_y = target_in_source_xyz[..., 1]
+    outside_source_domain = ((target_in_source_x < source_x_coords.min()) |
+                             (target_in_source_x > source_x_coords.max()) |
+                             (target_in_source_y < source_y_coords.min()) |
+                             (target_in_source_y > source_y_coords.max()))
+    if np.ma.isMaskedArray(new_array):
+        if np.any(outside_source_domain):
+            new_array.mask[outside_source_domain] = True
+    else:
+        new_array = np.ma.array(new_array, mask=True)
+        new_array.mask[...] = False
+        if new_array.ndim == 3:
+            for i in range(new_array.shape[2]):
+                new_array.mask[:, :, i] = outside_source_domain
+        else:
+            new_array.mask[...] = outside_source_domain
     return new_array
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
@@ -742,6 +742,12 @@ def imshow(self, img, *args, **kwargs):
         else:
             extent = kwargs.pop('extent', None)
             img = np.asanyarray(img)
+            if kwargs['origin'] == 'upper':
+                # It is implicitly assumed by the regridding operation that the
+                # origin of the image is 'lower', so simply adjust for that
+                # here.
+                img = img[::-1]
+                kwargs['origin'] = 'lower'
 
             if not isinstance(transform, ccrs.Projection):
                 raise ValueError('Expected a projection subclass. Cannot '


Test patch:
diff --git a/lib/cartopy/tests/mpl/baseline_images/mpl/test_images/imshow_regional_projected.png b/lib/cartopy/tests/mpl/baseline_images/mpl/test_images/imshow_regional_projected.png
new file mode 100644
Binary files /dev/null and b/lib/cartopy/tests/mpl/baseline_images/mpl/test_images/imshow_regional_projected.png differ
diff --git a/lib/cartopy/tests/mpl/test_images.py b/lib/cartopy/tests/mpl/test_images.py
--- a/lib/cartopy/tests/mpl/test_images.py
+++ b/lib/cartopy/tests/mpl/test_images.py
@@ -35,6 +35,8 @@
 NATURAL_EARTH_IMG = os.path.join(config["repo_data_dir"],
                                  'raster', 'natural_earth',
                                  '50-natural-earth-1-downsampled.png')
+REGIONAL_IMG = os.path.join(config['repo_data_dir'], 'raster', 'sample',
+                            'Miriam.A2012270.2050.2km.jpg')
 
 
 @ImageTesting(['web_tiles'], tolerance=12)
@@ -133,6 +135,18 @@ def test_imshow():
               extent=[-180, 180, -90, 90])
 
 
+@ImageTesting(['imshow_regional_projected'])
+def test_imshow_projected():
+    source_proj = ccrs.PlateCarree()
+    img_extent = (-120.67660000000001, -106.32104523100001,
+                  13.2301484511245, 30.766899999999502)
+    img = plt.imread(REGIONAL_IMG)
+    ax = plt.axes(projection=ccrs.LambertConformal())
+    ax.set_extent(img_extent, crs=source_proj)
+    ax.coastlines(resolution='50m')
+    ax.imshow(img, extent=img_extent, origin='upper', transform=source_proj)
+
+
 @ImageTesting(['imshow_natural_earth_ortho'])
 def test_stock_img():
     ax = plt.axes(projection=ccrs.Orthographic())