Repository: SciTools/cartopy

Issue:
ax.add_image produces incorrect results near the prime meridian
### Description
Tile images added to an axis using `ax.add_image` do not display correctly when the edge of the tiled image is on the prime meridian (0˚E). This is not directly caused by the actual extents of the axis, but rather by the extent of the tiled image produced by `cartopy.io.img_tiles._merge_tiles`. 

I've tried to track down the source of this bug, and it appears that it is caused by an incorrect calculation of `extent` when `imshow` is called. Ultimately it looks like there's a bug somewhere in `cartopy.img_transform.wrap_array` which occurs specifically for a longitude of 0˚, but I've not been able to narrow it down any further.



#### Code to reproduce
Produces incorrect plot:
```python
import matplotlib.pyplot as plt
import cartopy.crs as ccrs
import cartopy.io.img_tiles as cimgt

proj = ccrs.Mercator()
ax = plt.axes(projection=proj)
ax.set_extent([-11, -1, 50, 55])
tiles = cimgt.GoogleTiles()
ax.add_image(tiles, 5)
ax.coastlines(resolution='50m')
plt.show()
```
![image](https://user-images.githubusercontent.com/26504228/75910229-6f394500-5e45-11ea-8e21-5389665114a0.png)



Produces correct plot as the meridian is not at the edge of the tiled image:
```python
import matplotlib.pyplot as plt
import cartopy.crs as ccrs
import cartopy.io.img_tiles as cimgt

proj = ccrs.Mercator()
ax = plt.axes(projection=proj)
ax.set_extent([-11, 11, 50, 55])
tiles = cimgt.GoogleTiles()
ax.add_image(tiles, 5)
ax.coastlines(resolution='50m')
plt.show()
```
![image](https://user-images.githubusercontent.com/26504228/75910272-7fe9bb00-5e45-11ea-91ed-4261ff801a7d.png)


Produces correct plot as the tiled image is forced to cover both sides of the meridian by calling `plt.draw()` with a larger extent:
```python
import matplotlib.pyplot as plt
import cartopy.crs as ccrs
import cartopy.io.img_tiles as cimgt

proj = ccrs.Mercator()
ax = plt.axes(projection=proj)
tiles = cimgt.GoogleTiles()
ax.add_image(tiles, 5)
ax.coastlines(resolution='50m')
ax.set_extent([-11, 11, 50, 55])
plt.draw()
ax.set_extent([-11, -1, 50, 55])
plt.show()
```
![image](https://user-images.githubusercontent.com/26504228/75910826-73b22d80-5e46-11ea-9663-2a984782cfc6.png)

My very temporary workaround is to create a custom class which replaces longitude tile extents of 0˚ with a very small number to ensure that `cartopy.img_transform.wrap_array` is never provided with a longitude value of 0˚:
```python
import matplotlib.pyplot as plt
import cartopy.crs as ccrs
import cartopy.io.img_tiles as cimgt
import six
from PIL import Image

class CustomTiles(cimgt.GoogleTiles):
    def get_image(self, tile):
        if six.PY3:
            from urllib.request import urlopen
        else:
            from urllib2 import urlopen
        url = self._image_url(tile)

        fh = urlopen(url)
        im_data = six.BytesIO(fh.read())
        fh.close()
        img = Image.open(im_data)

        img = img.convert(self.desired_tile_form)

        extent = list(self.tileextent(tile))
        if extent[0] == 0:
            extent[0] = 1e-100
        if extent[1] == 0:
            extent[1] = 1e-100

        return img, extent, 'lower'


proj = ccrs.Mercator()
ax = plt.axes(projection=proj)
ax.set_extent([-11, -1, 50, 55])
tiles = CustomTiles()
ax.add_image(tiles, 5)
ax.coastlines(resolution='50m')
plt.show()
```
![image](https://user-images.githubusercontent.com/26504228/75911751-f2f43100-5e47-11ea-8e6c-de3fe20d95d8.png)




<details>
 <summary>Full environment definition</summary>

<!-- fill in the following information as appropriate -->

### Operating system
Mac OS 10.14.6

### Cartopy version
0.17.0

### conda list

```
# packages in environment at /anaconda3:
#
# Name                    Version                   Build  Channel
acronym                   1.6.0                    pypi_0    pypi
alabaster                 0.7.12                   py37_0  
anaconda-client           1.7.2                    py37_0  
anaconda-navigator        1.9.7                    py37_1  
applaunchservices         0.2.1                      py_0  
appnope                   0.1.0                    py37_0  
appscript                 1.1.0            py37h1de35cc_0  
argh                      0.26.2                   py37_0  
asdf                      2.5.1                      py_0    conda-forge
asn1crypto                1.3.0                    py37_0  
astroid                   2.3.3                    py37_0  
astroplan                 0.6                        py_0    conda-forge
astropy                   4.0              py37h1de35cc_0  
astroquery                0.4.1.dev0               pypi_0    pypi
atomicwrites              1.3.0                    py37_1  
attrs                     19.3.0                     py_0  
autopep8                  1.5                        py_0    conda-forge
babel                     2.8.0                      py_0  
backcall                  0.1.0                    py37_0  
beautifulsoup4            4.5.3                    pypi_0    pypi
biblib                    0.1.3                    pypi_0    pypi
bibtex-pygments-lexer     0.0.1                    pypi_0    pypi
bibtexparser              1.1.0                    pypi_0    pypi
bibulous                  1.3.2                    pypi_0    pypi
blas                      1.0                    openblas  
bleach                    3.0.2                    pypi_0    pypi
bzip2                     1.0.8                h1de35cc_0  
ca-certificates           2019.11.27                    0  
cairo                     1.14.12              hc4e6be7_4  
cartopy                   0.17.0                   pypi_0    pypi
certifi                   2019.11.28               py37_0  
cffi                      1.13.2           py37hb5b8e2f_0  
chardet                   3.0.4                 py37_1003  
cloudpickle               1.2.2                      py_0  
clyent                    1.2.2                    py37_1  
conda                     4.8.2                    py37_0    conda-forge
conda-build               3.18.11                  py37_0  
conda-package-handling    1.6.0            py37h1de35cc_0  
corner                    2.0.1                   py_1002    conda-forge
country-converter         0.6.6                    pypi_0    pypi
coverage                  4.5.3                    pypi_0    pypi
cryptography              2.5              py37hdbc3d79_1    conda-forge
cssselect                 1.0.3                    pypi_0    pypi
curl                      7.64.0               heae2a1f_0    conda-forge
cycler                    0.10.0                   py37_0  
cytoolz                   0.10.1           py37h1de35cc_0  
dask-core                 2.9.2                      py_0  
dbus                      1.13.12              h90a0687_0  
decorator                 4.4.1                      py_0  
defusedxml                0.6.0                      py_0  
diff-match-patch          20181111                   py_0  
docutils                  0.16                     py37_0  
dropbox                   9.4.0                    py37_0  
emcee                     2.2.1              pyh24bf2e0_4    conda-forge
entrypoints               0.3                      py37_0  
expat                     2.2.6                h0a44026_0  
feedfinder2               0.0.4                    pypi_0    pypi
feedparser                5.2.1                    py37_1  
filelock                  3.0.12                     py_0  
flake8                    3.7.9                    py37_0  
fontconfig                2.13.0               h5d5b041_1  
freetype                  2.9.1                hb4e5f40_0  
freexl                    1.0.5                h1de35cc_0  
future                    0.17.1                   pypi_0    pypi
gdal                      2.3.2            py37h3f5b778_0  
geocoder                  1.38.1                   pypi_0    pypi
geographiclib             1.49                     pypi_0    pypi
geopy                     1.18.1                   pypi_0    pypi
geos                      3.6.2                h5470d99_2  
geotext                   0.4.0                    pypi_0    pypi
gettext                   0.19.8.1             h15daf44_3  
giflib                    5.1.4                h1de35cc_1  
glib                      2.63.1               hd977a24_0  
glob2                     0.7                        py_0  
gwcs                      0.12.0                     py_0    conda-forge
hdf4                      4.2.13               h39711bb_2  
hdf5                      1.10.2               hfa1e0ec_1  
html-table-extractor      1.4.0                    pypi_0    pypi
html5lib                  1.0.1                    py37_0  
hypothesis                4.54.2                   py37_0  
icu                       58.2                 h4b95b61_1  
idna                      2.8                      py37_0  
imageio                   2.5.0                    pypi_0    pypi
imagesize                 1.2.0                      py_0  
importlib_metadata        1.4.0                    py37_0  
intervaltree              3.0.2                      py_0  
ipykernel                 5.1.4            py37h39e3cac_0  
ipython                   7.11.1           py37h39e3cac_0  
ipython_genutils          0.2.0                    py37_0  
isbnlib                   3.9.4                    pypi_0    pypi
isort                     4.3.21                   py37_0  
jedi                      0.14.1                   py37_0  
jieba3k                   0.35.1                   pypi_0    pypi
jinja2                    2.10.3                     py_0  
joblib                    0.14.1                     py_0  
jpeg                      9b                   he5867d9_2  
json-c                    0.13.1               h3efe00b_0  
jsonschema                3.2.0                    py37_0  
jupyter_client            5.3.4                    py37_0  
jupyter_core              4.6.1                    py37_0  
kealib                    1.4.7                h40e48e4_6  
keyring                   20.0.0                   py37_0  
kiwisolver                1.1.0            py37h0a44026_0  
krb5                      1.16.3            h24a3359_1000    conda-forge
latexcodec                1.0.6                    pypi_0    pypi
lazy-object-proxy         1.4.3            py37h1de35cc_0  
libarchive                3.3.3                h786848e_5  
libblas                   3.8.0               11_openblas    conda-forge
libboost                  1.67.0               hebc422b_4  
libcblas                  3.8.0               11_openblas    conda-forge
libcurl                   7.64.0               h76de61e_0    conda-forge
libcxx                    9.0.1                         1    conda-forge
libdap4                   3.19.1               h3d3e54a_0  
libedit                   3.1.20181209         hb402a30_0  
libffi                    3.2.1                h475c297_4  
libgdal                   2.3.2                h7b1ea53_0  
libgfortran               3.0.1                h93005f0_2  
libiconv                  1.15                 hdd342a3_7  
libkml                    1.3.0                hbe12b63_4  
liblief                   0.9.0                h2a1bed3_2  
libnetcdf                 4.6.1                h4e6abe9_2  
libopenblas               0.3.6                hdc02c5d_2  
libpng                    1.6.37               ha441bb4_0  
libpq                     10.6              hbe1e24e_1000    conda-forge
libsodium                 1.0.17               h01d97ff_0    conda-forge
libspatialindex           1.9.3                h0a44026_0  
libspatialite             4.3.0a              ha12ebda_19  
libssh2                   1.8.0                h322a93b_4  
libtiff                   4.1.0                hcb84e12_0  
libxml2                   2.9.9                hf6e021a_1  
libxslt                   1.1.33               h33a18ac_0  
line_profiler             3.0.2            py37ha1b3eb9_0    conda-forge
llvm-openmp               9.0.1                h28b9765_2    conda-forge
llvmlite                  0.31.0                   pypi_0    pypi
lxml                      4.4.2            py37hef8c89e_0  
lz4-c                     1.8.1.2              h1de35cc_0  
lzo                       2.10                 h362108e_2  
markupsafe                1.1.1            py37h1de35cc_0  
matplotlib                3.1.2                    py37_1    conda-forge
matplotlib-base           3.1.2            py37h11da6c2_1    conda-forge
mccabe                    0.6.1                    py37_1  
miepython                 1.3.0                    pypi_0    pypi
mistune                   0.8.4            py37h1de35cc_0  
mkl                       2019.5                      281    conda-forge
mkl-service               2.3.0            py37hfbe908c_0  
more-itertools            8.0.2                      py_0  
nbconvert                 5.6.1                    py37_0  
nbformat                  5.0.4                      py_0  
ncurses                   6.1                  h0a44026_1  
networkx                  2.3                      pypi_0    pypi
newspaper3k               0.2.8                    pypi_0    pypi
nltk                      3.4.5                    py37_0  
numba                     0.48.0                   pypi_0    pypi
numexpr                   2.7.1            py37h4f17bb1_0    conda-forge
numpy                     1.18.1           py37h0c13f30_0  
numpy-base                1.18.1           py37ha711998_1  
numpydoc                  0.9.2                      py_0  
olefile                   0.46                     py37_0  
openjpeg                  2.3.0                hb95cd4c_1  
openssl                   1.0.2u               h1de35cc_0  
packaging                 20.1                       py_0  
palettable                3.3.0                      py_0  
pandoc                    2.2.3.2                       0  
pandocfilters             1.4.2                    py37_1  
parso                     0.6.0                      py_0  
pathtools                 0.1.2                      py_1  
pcre                      8.43                 h0a44026_0  
pexpect                   4.8.0                    py37_0  
photutils                 0.7.2            py37h0b31af3_0    conda-forge
pickleshare               0.7.5                    py37_0  
pillow                    7.0.0            py37h4655f20_0  
pip                       18.1                     pypi_0    pypi
pixman                    0.38.0               h1de35cc_0  
pkginfo                   1.5.0.1                  py37_0  
pluggy                    0.13.1                   py37_0  
poppler                   0.65.0               ha097c24_1  
poppler-data              0.4.9                         0  
pprintpp                  0.4.0                    pypi_0    pypi
proj4                     5.2.0             h6de7cb9_1006    <unknown>
prompt_toolkit            3.0.2                      py_0  
psutil                    5.6.7            py37h1de35cc_0  
ptyprocess                0.6.0                    py37_0  
py                        1.8.1                      py_0  
py-lief                   0.9.0            py37h1413db1_2  
pybtex                    0.22.2                   pypi_0    pypi
pycodestyle               2.5.0                    py37_0  
pycosat                   0.6.3            py37h1de35cc_0  
pycountry                 18.12.8                  pypi_0    pypi
pycountry-convert         0.7.2                    pypi_0    pypi
pycparser                 2.19                     py37_0  
pydocstyle                4.0.1                      py_0  
pyflakes                  2.1.1                    py37_0  
pygments                  2.5.2                      py_0  
pylint                    2.4.4                    py37_0  
pyopenssl                 19.0.0                   py37_0  
pyparsing                 2.4.6                      py_0  
pyproj                    2.4.2.post1              pypi_0    pypi
pyqt                      5.9.2            py37h655552a_2  
pyrsistent                0.15.7           py37h1de35cc_0  
pyshp                     2.1.0                    pypi_0    pypi
pysocks                   1.7.1                    py37_0  
pytest                    5.3.4                    py37_0  
pytest-arraydiff          0.3              py37h39e3cac_0  
pytest-astropy            0.7.0                      py_0  
pytest-astropy-header     0.1.1                      py_0  
pytest-cov                2.6.1                    pypi_0    pypi
pytest-doctestplus        0.5.0                      py_0  
pytest-mock               1.10.2                   pypi_0    pypi
pytest-openfiles          0.4.0                      py_0  
pytest-remotedata         0.3.2                    py37_0  
python                    3.7.0                hc167b69_0  
python-dateutil           2.8.1                      py_0  
python-jsonrpc-server     0.3.2                      py_0  
python-language-server    0.31.2                   py37_0  
python-libarchive-c       2.8                     py37_13  
python-magic              0.4.15                   pypi_0    pypi
python.app                2                        py37_9  
pytz                      2019.3                     py_0  
pywavelets                1.1.1            py37h1de35cc_0  
pyyaml                    5.2              py37h1de35cc_0  
pyzmq                     18.1.1           py37h4bf09a9_0    conda-forge
qdarkstyle                2.7                        py_0  
qt                        5.9.7                h468cd18_1  
qtawesome                 0.6.0                      py_0  
qtconsole                 4.6.0                      py_1  
qtpy                      1.9.0                      py_0  
ratelim                   0.1.6                    pypi_0    pypi
readline                  7.0                  h1de35cc_5  
repoze-lru                0.7                      pypi_0    pypi
requests                  2.22.0                   py37_1  
requests-file             1.4.3                    pypi_0    pypi
ripgrep                   11.0.2               he32d670_0  
rope                      0.16.0                     py_0  
rtree                     0.8.3                    py37_0  
ruamel_yaml               0.15.87          py37h1de35cc_0  
scikit-image              0.15.0           py37h0a44026_0  
scikit-learn              0.22.1           py37hebd9d1a_0  
scipy                     1.3.1            py37h1a1e112_0  
semantic_version          2.8.2                      py_0    conda-forge
setuptools                45.1.0                   py37_0  
shapely                   1.6.4            py37h20de77a_0  
simplejson                3.17.0                   pypi_0    pypi
sip                       4.19.8           py37h0a44026_0  
six                       1.14.0                   py37_0  
snowballstemmer           2.0.0                      py_0  
sortedcontainers          2.1.0                    py37_0  
soupsieve                 1.9.5                    py37_0  
sphinx                    2.3.1                      py_0  
sphinxcontrib-applehelp   1.0.1                      py_0  
sphinxcontrib-devhelp     1.0.1                      py_0  
sphinxcontrib-htmlhelp    1.0.2                      py_0  
sphinxcontrib-jsmath      1.0.1                      py_0  
sphinxcontrib-qthelp      1.0.2                      py_0  
sphinxcontrib-serializinghtml 1.1.3                      py_0  
spyder                    4.0.1                    py37_0  
spyder-kernels            1.8.1                    py37_0  
spyder-line-profiler      0.2.0                      py_0    conda-forge
sqlite                    3.30.1               ha441bb4_0  
testpath                  0.4.4                      py_0  
tinysegmenter             0.3                      pypi_0    pypi
tk                        8.6.10               hbbe82c9_0    conda-forge
tldextract                2.2.0                    pypi_0    pypi
toolz                     0.10.0                     py_0  
tornado                   6.0.3            py37h1de35cc_0  
tqdm                      4.42.0                     py_0  
traitlets                 4.3.3                    py37_0  
ujson                     1.35             py37h1de35cc_0  
unidecode                 1.0.23                   pypi_0    pypi
urllib3                   1.25.8                   py37_0  
watchdog                  0.9.0                    py37_1  
wcwidth                   0.1.7                    py37_0  
webencodings              0.5.1                    py37_1  
wheel                     0.32.1                   pypi_0    pypi
wrapt                     1.11.2           py37h1de35cc_0  
wurlitzer                 2.0.0                    py37_0  
xerces-c                  3.2.2                h44e365a_0  
xlwings                   0.17.0                   py37_0  
xz                        5.2.4                h1de35cc_4  
yaml                      0.1.7                hc338f04_2  
yapf                      0.28.0                     py_0  
zeromq                    4.3.2                h6de7cb9_2    conda-forge
zipp                      0.6.0                      py_0  
zlib                      1.2.11               h1de35cc_3  
zstd                      1.3.7                h5bba6e5_0  
```

### pip list

```
```

</details>



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
@@ -1,4 +1,4 @@
-# (C) British Crown Copyright 2011 - 2018, Met Office
+# (C) British Crown Copyright 2011 - 2020, Met Office
 #
 # This file is part of cartopy.
 #
@@ -34,8 +34,8 @@
 
 
 def mesh_projection(projection, nx, ny,
-                    x_extents=[None, None],
-                    y_extents=[None, None]):
+                    x_extents=(None, None),
+                    y_extents=(None, None)):
     """
     Return sample points in the given projection which span the entire
     projection range evenly.
@@ -69,11 +69,17 @@ def mesh_projection(projection, nx, ny,
 
     """
 
+    def extent(specified, default, index):
+        if specified[index] is not None:
+            return specified[index]
+        else:
+            return default[index]
+
     # Establish the x-direction and y-direction extents.
-    x_lower = x_extents[0] or projection.x_limits[0]
-    x_upper = x_extents[1] or projection.x_limits[1]
-    y_lower = y_extents[0] or projection.y_limits[0]
-    y_upper = y_extents[1] or projection.y_limits[1]
+    x_lower = extent(x_extents, projection.x_limits, 0)
+    x_upper = extent(x_extents, projection.x_limits, 1)
+    y_lower = extent(y_extents, projection.y_limits, 0)
+    y_upper = extent(y_extents, projection.y_limits, 1)
 
     # Calculate evenly spaced sample points spanning the
     # extent - excluding endpoint.


Test patch:
diff --git a/lib/cartopy/tests/test_img_transform.py b/lib/cartopy/tests/test_img_transform.py
--- a/lib/cartopy/tests/test_img_transform.py
+++ b/lib/cartopy/tests/test_img_transform.py
@@ -1,4 +1,4 @@
-# (C) British Crown Copyright 2014 - 2017, Met Office
+# (C) British Crown Copyright 2014 - 2020, Met Office
 #
 # This file is part of cartopy.
 #
@@ -19,11 +19,43 @@
 
 import numpy as np
 from numpy.testing import assert_array_equal
+import pytest
 
 import cartopy.img_transform as img_trans
 import cartopy.crs as ccrs
 
 
+@pytest.mark.parametrize('xmin, xmax', [
+    (-90, 0), (-90, 90), (-90, None),
+    (0, 90), (0, None),
+    (None, 0), (None, 90), (None, None)])
+@pytest.mark.parametrize('ymin, ymax', [
+    (-45, 0), (-45, 45), (-45, None),
+    (0, 45), (0, None),
+    (None, 0), (None, 45), (None, None)])
+def test_mesh_projection_extent(xmin, xmax, ymin, ymax):
+    proj = ccrs.PlateCarree()
+    nx = 4
+    ny = 2
+
+    target_x, target_y, extent = img_trans.mesh_projection(
+        proj, nx, ny,
+        x_extents=(xmin, xmax),
+        y_extents=(ymin, ymax))
+
+    if xmin is None:
+        xmin = proj.x_limits[0]
+    if xmax is None:
+        xmax = proj.x_limits[1]
+    if ymin is None:
+        ymin = proj.y_limits[0]
+    if ymax is None:
+        ymax = proj.y_limits[1]
+    assert_array_equal(extent, [xmin, xmax, ymin, ymax])
+    assert_array_equal(np.diff(target_x, axis=1), (xmax - xmin) / nx)
+    assert_array_equal(np.diff(target_y, axis=0), (ymax - ymin) / ny)
+
+
 def test_griding_data_std_range():
     # Data which exists inside the standard projection bounds i.e.
     # [-180, 180].