We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Increase tolerance of test_pcolormesh_mercator_wrap.
This is the only test that is affected by matplotlib/matplotlib#15970. The new image is correct, but the difference is small enough to just change the tolerance.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `lib/cartopy/io/srtm.py`
Content:
```
1 # (C) British Crown Copyright 2011 - 2014, Met Office
2 #
3 # This file is part of cartopy.
4 #
5 # cartopy is free software: you can redistribute it and/or modify it under
6 # the terms of the GNU Lesser General Public License as published by the
7 # Free Software Foundation, either version 3 of the License, or
8 # (at your option) any later version.
9 #
10 # cartopy is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 # GNU Lesser General Public License for more details.
14 #
15 # You should have received a copy of the GNU Lesser General Public License
16 # along with cartopy.  If not, see <http://www.gnu.org/licenses/>.
17 
18 
19 """
20 The Shuttle Radar Topography Mission (SRTM) is an international research
21 effort that obtained digital elevation models on a near-global scale from
22 56S to 60N, to generate the most complete high-resolution digital topographic
23 database of Earth prior to the release of the ASTER GDEM in 2009.
24 
25    - Wikipedia (August 2012)
26 
27 """
28 
29 from __future__ import (absolute_import, division, print_function)
30 
31 import json
32 import os
33 
34 import numpy as np
35 import six
36 
37 from cartopy import config
38 import cartopy.crs as ccrs
39 from cartopy.io import fh_getter, Downloader
40 
41 
42 def srtm(lon, lat):
43     """
44     Return (elevation, crs, extent) for the given longitude latitude.
45     Elevation is in meters.
46     """
47     fname = SRTM3_retrieve(lon, lat)
48     if fname is None:
49         raise ValueError('No srtm tile found for those coordinates.')
50     return read_SRTM3(fname)
51 
52 
53 def add_shading(elevation, azimuth, altitude):
54     """Adds shading to SRTM elevation data, using azimuth and altitude
55     of the sun.
56 
57     :type elevation: numpy.ndarray
58     :param elevation: SRTM elevation data (in meters)
59     :type azimuth: float
60     :param azimuth: azimuth of the Sun (in degrees)
61     :type altitude: float
62     :param altitude: altitude of the Sun (in degrees)
63 
64     :rtype: numpy.ndarray
65     :return: shaded SRTM relief map.
66     """
67     azimuth = np.deg2rad(azimuth)
68     altitude = np.deg2rad(altitude)
69     x, y = np.gradient(elevation)
70     slope = np.pi/2. - np.arctan(np.sqrt(x*x + y*y))
71     # -x here because of pixel orders in the SRTM tile
72     aspect = np.arctan2(-x, y)
73     shaded = np.sin(altitude) * np.sin(slope)\
74         + np.cos(altitude) * np.cos(slope)\
75         * np.cos((azimuth - np.pi/2.) - aspect)
76     return shaded
77 
78 
79 def fill_gaps(elevation, max_distance=10):
80     """Fills gaps in SRTM elevation data for which the distance from
81     missing pixel to nearest existing one is smaller than `max_distance`.
82 
83     This function requires osgeo/gdal to work.
84 
85     :type elevation: numpy.ndarray
86     :param elevation: SRTM elevation data (in meters)
87     :type max_distance: int
88     :param max_distance: maximal distance (in pixels) between a missing point
89     and the nearest valid one.
90 
91     :rtype: numpy.ndarray
92     :return: SRTM elevation data with filled gaps..
93     """
94     # Lazily import osgeo - it is only an optional dependency for cartopy.
95     from osgeo import gdal
96     from osgeo import gdal_array
97 
98     src_ds = gdal_array.OpenArray(elevation)
99     srcband = src_ds.GetRasterBand(1)
100     dstband = srcband
101     maskband = srcband
102     smoothing_iterations = 0
103     options = []
104     gdal.FillNodata(dstband, maskband,
105                     max_distance, smoothing_iterations, options,
106                     callback=None)
107     elevation = dstband.ReadAsArray()
108     return elevation
109 
110 
111 def srtm_composite(lon_min, lat_min, nx, ny):
112 
113     # XXX nx and ny have got confused in the code (numpy array ordering?).
114     # However, the interface works well.
115 
116     bottom_left_ll = (lon_min, lat_min)
117     shape = np.array([1201, 1201])
118     img = np.empty(shape * (nx, ny))
119 
120     for i in range(nx):
121         for j in range(ny):
122             x_img_slice = slice(i * shape[0], (i + 1) * shape[0])
123             y_img_slice = slice(j * shape[1], (j + 1) * shape[1])
124 
125             try:
126                 tile_img, _, _ = srtm(bottom_left_ll[0] + j,
127                                       bottom_left_ll[1] + i)
128             except ValueError:
129                 img[x_img_slice, y_img_slice] = 0
130             else:
131                 img[x_img_slice, y_img_slice] = tile_img
132 
133     extent = (bottom_left_ll[0], bottom_left_ll[0] + ny,
134               bottom_left_ll[1], bottom_left_ll[1] + nx)
135 
136     return img, ccrs.PlateCarree(), extent
137 
138 
139 def read_SRTM3(fh):
140     fh, fname = fh_getter(fh, needs_filename=True)
141     if fname.endswith('.zip'):
142         from zipfile import ZipFile
143         zfh = ZipFile(fh, 'rb')
144         fh = zfh.open(os.path.basename(fname[:-4]), 'r')
145 
146     elev = np.fromfile(fh, dtype=np.dtype('>i2'))
147     elev.shape = (1201, 1201)
148 
149     fname = os.path.basename(fname)
150     y_dir, y, x_dir, x = fname[0], int(fname[1:3]), fname[3], int(fname[4:7])
151 
152     if y_dir == 'S':
153         y *= -1
154 
155     if x_dir == 'W':
156         x *= -1
157 
158     # xxx extent may need to be wider by half a pixel
159     return elev[::-1, ...], ccrs.PlateCarree(), [x, x + 1, y, y + 1]
160 
161 
162 def SRTM3_retrieve(lon, lat):
163     """
164     Return the path of a .hgt file for the given SRTM location.
165 
166     If no such .hgt file exists (because it is over the ocean)
167     None will be returned.
168 
169     """
170     x = '%s%03d' % ('E' if lon > 0 else 'W', abs(int(lon)))
171     y = '%s%02d' % ('N' if lat > 0 else 'S', abs(int(lat)))
172 
173     srtm_downloader = Downloader.from_config(('SRTM', 'SRTM3'))
174     params = {'config': config, 'x': x, 'y': y}
175     if srtm_downloader.url(params) is None:
176         return None
177     else:
178         return srtm_downloader.path({'config': config, 'x': x, 'y': y})
179 
180 
181 class SRTM3Downloader(Downloader):
182     """
183     Provides a SRTM3 download mechanism.
184 
185     """
186     FORMAT_KEYS = ('config', 'x', 'y')
187 
188     _JSON_SRTM3_LOOKUP = os.path.join(os.path.dirname(__file__),
189                                       'srtm.json')
190     _SRTM3_LOOKUP_URL = json.load(open(_JSON_SRTM3_LOOKUP, 'r'))
191     """
192     The SRTM3 url lookup dictionary maps keys such as 'N43E043' to the url
193     of the file to download.
194 
195     """
196 
197     def __init__(self,
198                  target_path_template,
199                  pre_downloaded_path_template='',
200                  ):
201         # adds some SRTM3 defaults to the __init__ of a Downloader
202         # namely, the URl is determined on the fly using the
203         # ``SRTM3Downloader._SRTM3_LOOKUP_URL`` dictionary
204         Downloader.__init__(self, None,
205                             target_path_template,
206                             pre_downloaded_path_template)
207 
208     def url(self, format_dict):
209         # override the url method, looking up the url from the
210         # ``SRTM3Downloader._SRTM3_LOOKUP_URL`` dictionary
211         key = u'{y}{x}'.format(**format_dict)
212         url = SRTM3Downloader._SRTM3_LOOKUP_URL.get(key, None)
213         return url
214 
215     def acquire_resource(self, target_path, format_dict):
216         from zipfile import ZipFile
217 
218         target_dir = os.path.dirname(target_path)
219         if not os.path.isdir(target_dir):
220             os.makedirs(target_dir)
221 
222         url = self.url(format_dict)
223 
224         srtm_online = self._urlopen(url)
225         zfh = ZipFile(six.BytesIO(srtm_online.read()), 'r')
226 
227         zip_member_path = u'{y}{x}.hgt'.format(**format_dict)
228         member = zfh.getinfo(zip_member_path)
229         with open(target_path, 'wb') as fh:
230             fh.write(zfh.open(member).read())
231 
232         srtm_online.close()
233         zfh.close()
234 
235         return target_path
236 
237     @staticmethod
238     def _create_srtm3_dict():
239         """
240         Returns a dictionary mapping srtm filename to the URL of the file.
241 
242         This is slow as it must query the SRTM server to identify the
243         continent from which the tile comes. Hence a json file with this
244         content exists in ``SRTM3Downloader._JSON_SRTM3_LOOKUP``.
245 
246         The json file was created with::
247 
248             import cartopy.io.srtm as srtm
249             import json
250             fh = open(srtm.SRTM3Downloader._JSON_SRTM3_LOOKUP, 'w')
251             json.dump(srtm.SRTM3Downloader._create_srtm3_dict(), fh)
252 
253         """
254         # lazy imports. In most situations, these are not
255         # dependencies of cartopy.
256         if six.PY3:
257             from urllib.request import urlopen
258         else:
259             from urllib2 import urlopen
260         from BeautifulSoup import BeautifulSoup
261 
262         files = {}
263 
264         for continent in ['Australia', 'Africa', 'Eurasia', 'Islands',
265                           'North_America', 'South_America']:
266 
267             url = "http://dds.cr.usgs.gov/srtm/version2_1/SRTM3/%s" % continent
268             f = urlopen(url)
269             html = f.read()
270             soup = BeautifulSoup(html)
271 
272             for link in soup('li'):
273                 name = str(link.text)
274                 if name != ' Parent Directory':
275                     # remove the '.hgt.zip'
276                     files[name[:-8]] = url + '/' + name
277             f.close()
278         return files
279 
280     @classmethod
281     def default_downloader(cls):
282         """
283         Returns a typical downloader for this class. In general, this static
284         method is used to create the default configuration in cartopy.config
285 
286         """
287         default_spec = ('SRTM', 'SRTM3', '{y}{x}.hgt')
288         target_path_template = os.path.join('{config[data_dir]}',
289                                             *default_spec)
290         pre_path_template = os.path.join('{config[pre_existing_data_dir]}',
291                                          *default_spec)
292         return cls(target_path_template=target_path_template,
293                    pre_downloaded_path_template=pre_path_template)
294 
295 
296 # add a generic SRTM downloader to the config 'downloaders' section.
297 config['downloaders'].setdefault(('SRTM', 'SRTM3'),
298                                  SRTM3Downloader.default_downloader())
299
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/lib/cartopy/io/srtm.py b/lib/cartopy/io/srtm.py
--- a/lib/cartopy/io/srtm.py
+++ b/lib/cartopy/io/srtm.py
@@ -167,8 +167,8 @@
     None will be returned.
 
     """
-    x = '%s%03d' % ('E' if lon > 0 else 'W', abs(int(lon)))
-    y = '%s%02d' % ('N' if lat > 0 else 'S', abs(int(lat)))
+    x = '%s%03d' % ('E' if lon >= 0 else 'W', abs(int(lon)))
+    y = '%s%02d' % ('N' if lat >= 0 else 'S', abs(int(lat)))
 
     srtm_downloader = Downloader.from_config(('SRTM', 'SRTM3'))
     params = {'config': config, 'x': x, 'y': y}

{"golden_diff": "diff --git a/lib/cartopy/io/srtm.py b/lib/cartopy/io/srtm.py\n--- a/lib/cartopy/io/srtm.py\n+++ b/lib/cartopy/io/srtm.py\n@@ -167,8 +167,8 @@\n     None will be returned.\n \n     \"\"\"\n-    x = '%s%03d' % ('E' if lon > 0 else 'W', abs(int(lon)))\n-    y = '%s%02d' % ('N' if lat > 0 else 'S', abs(int(lat)))\n+    x = '%s%03d' % ('E' if lon >= 0 else 'W', abs(int(lon)))\n+    y = '%s%02d' % ('N' if lat >= 0 else 'S', abs(int(lat)))\n \n     srtm_downloader = Downloader.from_config(('SRTM', 'SRTM3'))\n     params = {'config': config, 'x': x, 'y': y}\n", "issue": "Increase tolerance of test_pcolormesh_mercator_wrap.\nThis is the only test that is affected by matplotlib/matplotlib#15970. The new image is correct, but the difference is small enough to just change the tolerance.\n", "before_files": [{"content": "# (C) British Crown Copyright 2011 - 2014, Met Office\n#\n# This file is part of cartopy.\n#\n# cartopy is free software: you can redistribute it and/or modify it under\n# the terms of the GNU Lesser General Public License as published by the\n# Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# cartopy is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU Lesser General Public License for more details.\n#\n# You should have received a copy of the GNU Lesser General Public License\n# along with cartopy.  If not, see <http://www.gnu.org/licenses/>.\n\n\n\"\"\"\nThe Shuttle Radar Topography Mission (SRTM) is an international research\neffort that obtained digital elevation models on a near-global scale from\n56S to 60N, to generate the most complete high-resolution digital topographic\ndatabase of Earth prior to the release of the ASTER GDEM in 2009.\n\n   - Wikipedia (August 2012)\n\n\"\"\"\n\nfrom __future__ import (absolute_import, division, print_function)\n\nimport json\nimport os\n\nimport numpy as np\nimport six\n\nfrom cartopy import config\nimport cartopy.crs as ccrs\nfrom cartopy.io import fh_getter, Downloader\n\n\ndef srtm(lon, lat):\n    \"\"\"\n    Return (elevation, crs, extent) for the given longitude latitude.\n    Elevation is in meters.\n    \"\"\"\n    fname = SRTM3_retrieve(lon, lat)\n    if fname is None:\n        raise ValueError('No srtm tile found for those coordinates.')\n    return read_SRTM3(fname)\n\n\ndef add_shading(elevation, azimuth, altitude):\n    \"\"\"Adds shading to SRTM elevation data, using azimuth and altitude\n    of the sun.\n\n    :type elevation: numpy.ndarray\n    :param elevation: SRTM elevation data (in meters)\n    :type azimuth: float\n    :param azimuth: azimuth of the Sun (in degrees)\n    :type altitude: float\n    :param altitude: altitude of the Sun (in degrees)\n\n    :rtype: numpy.ndarray\n    :return: shaded SRTM relief map.\n    \"\"\"\n    azimuth = np.deg2rad(azimuth)\n    altitude = np.deg2rad(altitude)\n    x, y = np.gradient(elevation)\n    slope = np.pi/2. - np.arctan(np.sqrt(x*x + y*y))\n    # -x here because of pixel orders in the SRTM tile\n    aspect = np.arctan2(-x, y)\n    shaded = np.sin(altitude) * np.sin(slope)\\\n        + np.cos(altitude) * np.cos(slope)\\\n        * np.cos((azimuth - np.pi/2.) - aspect)\n    return shaded\n\n\ndef fill_gaps(elevation, max_distance=10):\n    \"\"\"Fills gaps in SRTM elevation data for which the distance from\n    missing pixel to nearest existing one is smaller than `max_distance`.\n\n    This function requires osgeo/gdal to work.\n\n    :type elevation: numpy.ndarray\n    :param elevation: SRTM elevation data (in meters)\n    :type max_distance: int\n    :param max_distance: maximal distance (in pixels) between a missing point\n    and the nearest valid one.\n\n    :rtype: numpy.ndarray\n    :return: SRTM elevation data with filled gaps..\n    \"\"\"\n    # Lazily import osgeo - it is only an optional dependency for cartopy.\n    from osgeo import gdal\n    from osgeo import gdal_array\n\n    src_ds = gdal_array.OpenArray(elevation)\n    srcband = src_ds.GetRasterBand(1)\n    dstband = srcband\n    maskband = srcband\n    smoothing_iterations = 0\n    options = []\n    gdal.FillNodata(dstband, maskband,\n                    max_distance, smoothing_iterations, options,\n                    callback=None)\n    elevation = dstband.ReadAsArray()\n    return elevation\n\n\ndef srtm_composite(lon_min, lat_min, nx, ny):\n\n    # XXX nx and ny have got confused in the code (numpy array ordering?).\n    # However, the interface works well.\n\n    bottom_left_ll = (lon_min, lat_min)\n    shape = np.array([1201, 1201])\n    img = np.empty(shape * (nx, ny))\n\n    for i in range(nx):\n        for j in range(ny):\n            x_img_slice = slice(i * shape[0], (i + 1) * shape[0])\n            y_img_slice = slice(j * shape[1], (j + 1) * shape[1])\n\n            try:\n                tile_img, _, _ = srtm(bottom_left_ll[0] + j,\n                                      bottom_left_ll[1] + i)\n            except ValueError:\n                img[x_img_slice, y_img_slice] = 0\n            else:\n                img[x_img_slice, y_img_slice] = tile_img\n\n    extent = (bottom_left_ll[0], bottom_left_ll[0] + ny,\n              bottom_left_ll[1], bottom_left_ll[1] + nx)\n\n    return img, ccrs.PlateCarree(), extent\n\n\ndef read_SRTM3(fh):\n    fh, fname = fh_getter(fh, needs_filename=True)\n    if fname.endswith('.zip'):\n        from zipfile import ZipFile\n        zfh = ZipFile(fh, 'rb')\n        fh = zfh.open(os.path.basename(fname[:-4]), 'r')\n\n    elev = np.fromfile(fh, dtype=np.dtype('>i2'))\n    elev.shape = (1201, 1201)\n\n    fname = os.path.basename(fname)\n    y_dir, y, x_dir, x = fname[0], int(fname[1:3]), fname[3], int(fname[4:7])\n\n    if y_dir == 'S':\n        y *= -1\n\n    if x_dir == 'W':\n        x *= -1\n\n    # xxx extent may need to be wider by half a pixel\n    return elev[::-1, ...], ccrs.PlateCarree(), [x, x + 1, y, y + 1]\n\n\ndef SRTM3_retrieve(lon, lat):\n    \"\"\"\n    Return the path of a .hgt file for the given SRTM location.\n\n    If no such .hgt file exists (because it is over the ocean)\n    None will be returned.\n\n    \"\"\"\n    x = '%s%03d' % ('E' if lon > 0 else 'W', abs(int(lon)))\n    y = '%s%02d' % ('N' if lat > 0 else 'S', abs(int(lat)))\n\n    srtm_downloader = Downloader.from_config(('SRTM', 'SRTM3'))\n    params = {'config': config, 'x': x, 'y': y}\n    if srtm_downloader.url(params) is None:\n        return None\n    else:\n        return srtm_downloader.path({'config': config, 'x': x, 'y': y})\n\n\nclass SRTM3Downloader(Downloader):\n    \"\"\"\n    Provides a SRTM3 download mechanism.\n\n    \"\"\"\n    FORMAT_KEYS = ('config', 'x', 'y')\n\n    _JSON_SRTM3_LOOKUP = os.path.join(os.path.dirname(__file__),\n                                      'srtm.json')\n    _SRTM3_LOOKUP_URL = json.load(open(_JSON_SRTM3_LOOKUP, 'r'))\n    \"\"\"\n    The SRTM3 url lookup dictionary maps keys such as 'N43E043' to the url\n    of the file to download.\n\n    \"\"\"\n\n    def __init__(self,\n                 target_path_template,\n                 pre_downloaded_path_template='',\n                 ):\n        # adds some SRTM3 defaults to the __init__ of a Downloader\n        # namely, the URl is determined on the fly using the\n        # ``SRTM3Downloader._SRTM3_LOOKUP_URL`` dictionary\n        Downloader.__init__(self, None,\n                            target_path_template,\n                            pre_downloaded_path_template)\n\n    def url(self, format_dict):\n        # override the url method, looking up the url from the\n        # ``SRTM3Downloader._SRTM3_LOOKUP_URL`` dictionary\n        key = u'{y}{x}'.format(**format_dict)\n        url = SRTM3Downloader._SRTM3_LOOKUP_URL.get(key, None)\n        return url\n\n    def acquire_resource(self, target_path, format_dict):\n        from zipfile import ZipFile\n\n        target_dir = os.path.dirname(target_path)\n        if not os.path.isdir(target_dir):\n            os.makedirs(target_dir)\n\n        url = self.url(format_dict)\n\n        srtm_online = self._urlopen(url)\n        zfh = ZipFile(six.BytesIO(srtm_online.read()), 'r')\n\n        zip_member_path = u'{y}{x}.hgt'.format(**format_dict)\n        member = zfh.getinfo(zip_member_path)\n        with open(target_path, 'wb') as fh:\n            fh.write(zfh.open(member).read())\n\n        srtm_online.close()\n        zfh.close()\n\n        return target_path\n\n    @staticmethod\n    def _create_srtm3_dict():\n        \"\"\"\n        Returns a dictionary mapping srtm filename to the URL of the file.\n\n        This is slow as it must query the SRTM server to identify the\n        continent from which the tile comes. Hence a json file with this\n        content exists in ``SRTM3Downloader._JSON_SRTM3_LOOKUP``.\n\n        The json file was created with::\n\n            import cartopy.io.srtm as srtm\n            import json\n            fh = open(srtm.SRTM3Downloader._JSON_SRTM3_LOOKUP, 'w')\n            json.dump(srtm.SRTM3Downloader._create_srtm3_dict(), fh)\n\n        \"\"\"\n        # lazy imports. In most situations, these are not\n        # dependencies of cartopy.\n        if six.PY3:\n            from urllib.request import urlopen\n        else:\n            from urllib2 import urlopen\n        from BeautifulSoup import BeautifulSoup\n\n        files = {}\n\n        for continent in ['Australia', 'Africa', 'Eurasia', 'Islands',\n                          'North_America', 'South_America']:\n\n            url = \"http://dds.cr.usgs.gov/srtm/version2_1/SRTM3/%s\" % continent\n            f = urlopen(url)\n            html = f.read()\n            soup = BeautifulSoup(html)\n\n            for link in soup('li'):\n                name = str(link.text)\n                if name != ' Parent Directory':\n                    # remove the '.hgt.zip'\n                    files[name[:-8]] = url + '/' + name\n            f.close()\n        return files\n\n    @classmethod\n    def default_downloader(cls):\n        \"\"\"\n        Returns a typical downloader for this class. In general, this static\n        method is used to create the default configuration in cartopy.config\n\n        \"\"\"\n        default_spec = ('SRTM', 'SRTM3', '{y}{x}.hgt')\n        target_path_template = os.path.join('{config[data_dir]}',\n                                            *default_spec)\n        pre_path_template = os.path.join('{config[pre_existing_data_dir]}',\n                                         *default_spec)\n        return cls(target_path_template=target_path_template,\n                   pre_downloaded_path_template=pre_path_template)\n\n\n# add a generic SRTM downloader to the config 'downloaders' section.\nconfig['downloaders'].setdefault(('SRTM', 'SRTM3'),\n                                 SRTM3Downloader.default_downloader())\n", "path": "lib/cartopy/io/srtm.py"}], "after_files": [{"content": "# (C) British Crown Copyright 2011 - 2014, Met Office\n#\n# This file is part of cartopy.\n#\n# cartopy is free software: you can redistribute it and/or modify it under\n# the terms of the GNU Lesser General Public License as published by the\n# Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# cartopy is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU Lesser General Public License for more details.\n#\n# You should have received a copy of the GNU Lesser General Public License\n# along with cartopy.  If not, see <http://www.gnu.org/licenses/>.\n\n\n\"\"\"\nThe Shuttle Radar Topography Mission (SRTM) is an international research\neffort that obtained digital elevation models on a near-global scale from\n56S to 60N, to generate the most complete high-resolution digital topographic\ndatabase of Earth prior to the release of the ASTER GDEM in 2009.\n\n   - Wikipedia (August 2012)\n\n\"\"\"\n\nfrom __future__ import (absolute_import, division, print_function)\n\nimport json\nimport os\n\nimport numpy as np\nimport six\n\nfrom cartopy import config\nimport cartopy.crs as ccrs\nfrom cartopy.io import fh_getter, Downloader\n\n\ndef srtm(lon, lat):\n    \"\"\"\n    Return (elevation, crs, extent) for the given longitude latitude.\n    Elevation is in meters.\n    \"\"\"\n    fname = SRTM3_retrieve(lon, lat)\n    if fname is None:\n        raise ValueError('No srtm tile found for those coordinates.')\n    return read_SRTM3(fname)\n\n\ndef add_shading(elevation, azimuth, altitude):\n    \"\"\"Adds shading to SRTM elevation data, using azimuth and altitude\n    of the sun.\n\n    :type elevation: numpy.ndarray\n    :param elevation: SRTM elevation data (in meters)\n    :type azimuth: float\n    :param azimuth: azimuth of the Sun (in degrees)\n    :type altitude: float\n    :param altitude: altitude of the Sun (in degrees)\n\n    :rtype: numpy.ndarray\n    :return: shaded SRTM relief map.\n    \"\"\"\n    azimuth = np.deg2rad(azimuth)\n    altitude = np.deg2rad(altitude)\n    x, y = np.gradient(elevation)\n    slope = np.pi/2. - np.arctan(np.sqrt(x*x + y*y))\n    # -x here because of pixel orders in the SRTM tile\n    aspect = np.arctan2(-x, y)\n    shaded = np.sin(altitude) * np.sin(slope)\\\n        + np.cos(altitude) * np.cos(slope)\\\n        * np.cos((azimuth - np.pi/2.) - aspect)\n    return shaded\n\n\ndef fill_gaps(elevation, max_distance=10):\n    \"\"\"Fills gaps in SRTM elevation data for which the distance from\n    missing pixel to nearest existing one is smaller than `max_distance`.\n\n    This function requires osgeo/gdal to work.\n\n    :type elevation: numpy.ndarray\n    :param elevation: SRTM elevation data (in meters)\n    :type max_distance: int\n    :param max_distance: maximal distance (in pixels) between a missing point\n    and the nearest valid one.\n\n    :rtype: numpy.ndarray\n    :return: SRTM elevation data with filled gaps..\n    \"\"\"\n    # Lazily import osgeo - it is only an optional dependency for cartopy.\n    from osgeo import gdal\n    from osgeo import gdal_array\n\n    src_ds = gdal_array.OpenArray(elevation)\n    srcband = src_ds.GetRasterBand(1)\n    dstband = srcband\n    maskband = srcband\n    smoothing_iterations = 0\n    options = []\n    gdal.FillNodata(dstband, maskband,\n                    max_distance, smoothing_iterations, options,\n                    callback=None)\n    elevation = dstband.ReadAsArray()\n    return elevation\n\n\ndef srtm_composite(lon_min, lat_min, nx, ny):\n\n    # XXX nx and ny have got confused in the code (numpy array ordering?).\n    # However, the interface works well.\n\n    bottom_left_ll = (lon_min, lat_min)\n    shape = np.array([1201, 1201])\n    img = np.empty(shape * (nx, ny))\n\n    for i in range(nx):\n        for j in range(ny):\n            x_img_slice = slice(i * shape[0], (i + 1) * shape[0])\n            y_img_slice = slice(j * shape[1], (j + 1) * shape[1])\n\n            try:\n                tile_img, _, _ = srtm(bottom_left_ll[0] + j,\n                                      bottom_left_ll[1] + i)\n            except ValueError:\n                img[x_img_slice, y_img_slice] = 0\n            else:\n                img[x_img_slice, y_img_slice] = tile_img\n\n    extent = (bottom_left_ll[0], bottom_left_ll[0] + ny,\n              bottom_left_ll[1], bottom_left_ll[1] + nx)\n\n    return img, ccrs.PlateCarree(), extent\n\n\ndef read_SRTM3(fh):\n    fh, fname = fh_getter(fh, needs_filename=True)\n    if fname.endswith('.zip'):\n        from zipfile import ZipFile\n        zfh = ZipFile(fh, 'rb')\n        fh = zfh.open(os.path.basename(fname[:-4]), 'r')\n\n    elev = np.fromfile(fh, dtype=np.dtype('>i2'))\n    elev.shape = (1201, 1201)\n\n    fname = os.path.basename(fname)\n    y_dir, y, x_dir, x = fname[0], int(fname[1:3]), fname[3], int(fname[4:7])\n\n    if y_dir == 'S':\n        y *= -1\n\n    if x_dir == 'W':\n        x *= -1\n\n    # xxx extent may need to be wider by half a pixel\n    return elev[::-1, ...], ccrs.PlateCarree(), [x, x + 1, y, y + 1]\n\n\ndef SRTM3_retrieve(lon, lat):\n    \"\"\"\n    Return the path of a .hgt file for the given SRTM location.\n\n    If no such .hgt file exists (because it is over the ocean)\n    None will be returned.\n\n    \"\"\"\n    x = '%s%03d' % ('E' if lon >= 0 else 'W', abs(int(lon)))\n    y = '%s%02d' % ('N' if lat >= 0 else 'S', abs(int(lat)))\n\n    srtm_downloader = Downloader.from_config(('SRTM', 'SRTM3'))\n    params = {'config': config, 'x': x, 'y': y}\n    if srtm_downloader.url(params) is None:\n        return None\n    else:\n        return srtm_downloader.path({'config': config, 'x': x, 'y': y})\n\n\nclass SRTM3Downloader(Downloader):\n    \"\"\"\n    Provides a SRTM3 download mechanism.\n\n    \"\"\"\n    FORMAT_KEYS = ('config', 'x', 'y')\n\n    _JSON_SRTM3_LOOKUP = os.path.join(os.path.dirname(__file__),\n                                      'srtm.json')\n    _SRTM3_LOOKUP_URL = json.load(open(_JSON_SRTM3_LOOKUP, 'r'))\n    \"\"\"\n    The SRTM3 url lookup dictionary maps keys such as 'N43E043' to the url\n    of the file to download.\n\n    \"\"\"\n\n    def __init__(self,\n                 target_path_template,\n                 pre_downloaded_path_template='',\n                 ):\n        # adds some SRTM3 defaults to the __init__ of a Downloader\n        # namely, the URl is determined on the fly using the\n        # ``SRTM3Downloader._SRTM3_LOOKUP_URL`` dictionary\n        Downloader.__init__(self, None,\n                            target_path_template,\n                            pre_downloaded_path_template)\n\n    def url(self, format_dict):\n        # override the url method, looking up the url from the\n        # ``SRTM3Downloader._SRTM3_LOOKUP_URL`` dictionary\n        key = u'{y}{x}'.format(**format_dict)\n        url = SRTM3Downloader._SRTM3_LOOKUP_URL.get(key, None)\n        return url\n\n    def acquire_resource(self, target_path, format_dict):\n        from zipfile import ZipFile\n\n        target_dir = os.path.dirname(target_path)\n        if not os.path.isdir(target_dir):\n            os.makedirs(target_dir)\n\n        url = self.url(format_dict)\n\n        srtm_online = self._urlopen(url)\n        zfh = ZipFile(six.BytesIO(srtm_online.read()), 'r')\n\n        zip_member_path = u'{y}{x}.hgt'.format(**format_dict)\n        member = zfh.getinfo(zip_member_path)\n        with open(target_path, 'wb') as fh:\n            fh.write(zfh.open(member).read())\n\n        srtm_online.close()\n        zfh.close()\n\n        return target_path\n\n    @staticmethod\n    def _create_srtm3_dict():\n        \"\"\"\n        Returns a dictionary mapping srtm filename to the URL of the file.\n\n        This is slow as it must query the SRTM server to identify the\n        continent from which the tile comes. Hence a json file with this\n        content exists in ``SRTM3Downloader._JSON_SRTM3_LOOKUP``.\n\n        The json file was created with::\n\n            import cartopy.io.srtm as srtm\n            import json\n            fh = open(srtm.SRTM3Downloader._JSON_SRTM3_LOOKUP, 'w')\n            json.dump(srtm.SRTM3Downloader._create_srtm3_dict(), fh)\n\n        \"\"\"\n        # lazy imports. In most situations, these are not\n        # dependencies of cartopy.\n        if six.PY3:\n            from urllib.request import urlopen\n        else:\n            from urllib2 import urlopen\n        from BeautifulSoup import BeautifulSoup\n\n        files = {}\n\n        for continent in ['Australia', 'Africa', 'Eurasia', 'Islands',\n                          'North_America', 'South_America']:\n\n            url = \"http://dds.cr.usgs.gov/srtm/version2_1/SRTM3/%s\" % continent\n            f = urlopen(url)\n            html = f.read()\n            soup = BeautifulSoup(html)\n\n            for link in soup('li'):\n                name = str(link.text)\n                if name != ' Parent Directory':\n                    # remove the '.hgt.zip'\n                    files[name[:-8]] = url + '/' + name\n            f.close()\n        return files\n\n    @classmethod\n    def default_downloader(cls):\n        \"\"\"\n        Returns a typical downloader for this class. In general, this static\n        method is used to create the default configuration in cartopy.config\n\n        \"\"\"\n        default_spec = ('SRTM', 'SRTM3', '{y}{x}.hgt')\n        target_path_template = os.path.join('{config[data_dir]}',\n                                            *default_spec)\n        pre_path_template = os.path.join('{config[pre_existing_data_dir]}',\n                                         *default_spec)\n        return cls(target_path_template=target_path_template,\n                   pre_downloaded_path_template=pre_path_template)\n\n\n# add a generic SRTM downloader to the config 'downloaders' section.\nconfig['downloaders'].setdefault(('SRTM', 'SRTM3'),\n                                 SRTM3Downloader.default_downloader())\n", "path": "lib/cartopy/io/srtm.py"}]}