We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Sunset Stamen maps in GeoDisplay and potentially replace
Stamen is transitioning their maps to stadia at the end of October 2023.  ACT will need to deprecate that feature in GeoDisplay and potentially look for replacements.

https://github.com/SciTools/cartopy/pull/2266 

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `act/plotting/geodisplay.py`
Content:
```
1 """
2 Stores the class for GeographicPlotDisplay.
3 
4 """
5 
6 import warnings
7 
8 import matplotlib
9 import matplotlib.pyplot as plt
10 import numpy as np
11 import pandas as pd
12 
13 from .plot import Display
14 
15 try:
16     import cartopy.crs as ccrs
17     import cartopy.feature as cfeature
18     from cartopy.io import img_tiles
19 
20     CARTOPY_AVAILABLE = True
21 except ImportError:
22     CARTOPY_AVAILABLE = False
23 
24 
25 class GeographicPlotDisplay(Display):
26     """
27     A class for making geographic tracer plot of aircraft, ship or other moving
28     platform plot.
29 
30     This is inherited from the :func:`act.plotting.Display`
31     class and has therefore has the same attributes as that class.
32     See :func:`act.plotting.Display`
33     for more information. There are no additional attributes or parameters
34     to this class.
35 
36     In order to create geographic plots, ACT needs the Cartopy package to be
37     installed on your system. More information about
38     Cartopy go here:https://scitools.org.uk/cartopy/docs/latest/ .
39 
40     """
41 
42     def __init__(self, ds, ds_name=None, **kwargs):
43         if not CARTOPY_AVAILABLE:
44             raise ImportError(
45                 'Cartopy needs to be installed on your ' 'system to make geographic display plots.'
46             )
47         super().__init__(ds, ds_name, secondary_y_allowed=False, **kwargs)
48         if self.fig is None:
49             self.fig = plt.figure(**kwargs)
50 
51     def geoplot(
52         self,
53         data_field=None,
54         lat_field='lat',
55         lon_field='lon',
56         dsname=None,
57         cbar_label=None,
58         title=None,
59         projection=None,
60         plot_buffer=0.08,
61         img_tile=None,
62         img_tile_args={},
63         tile=8,
64         stamen='terrain-background',
65         cartopy_feature=None,
66         cmap='rainbow',
67         text=None,
68         gridlines=True,
69         **kwargs,
70     ):
71         """
72         Creates a latitude and longitude plot of a time series data set with
73         data values indicated by color and described with a colorbar.
74         Latitude values must be in degree north (-90 to 90) and
75         longitude must be in degree east (-180 to 180).
76 
77         Parameters
78         ----------
79         data_field : str
80             Name of data field in the dataset to plot.
81         lat_field : str
82             Name of latitude field in the dataset to use.
83         lon_field : str
84             Name of longitude field in the dataset to use.
85         dsname : str or None
86             The name of the datastream to plot. Set to None to make ACT
87             attempt to automatically determine this.
88         cbar_label : str
89             Label to use with colorbar. If set to None will attempt
90             to create label from long_name and units.
91         title : str
92             Plot title.
93         projection : cartopy.crs object
94             Project to use on plot. See
95             https://scitools.org.uk/cartopy/docs/latest/reference/projections.html?highlight=projections
96         plot_buffer : float
97             Buffer to add around data on plot in lat and lon dimension.
98         img_tile : str
99             Image to use for the plot background. Set to None to not use
100             background image. For all image background types, see:
101             https://scitools.org.uk/cartopy/docs/v0.16/cartopy/io/img_tiles.html
102             Default is None.
103         img_tile_args : dict
104             Keyword arguments for the chosen img_tile. These arguments can be
105             found for the corresponding img_tile here:
106             https://scitools.org.uk/cartopy/docs/v0.16/cartopy/io/img_tiles.html
107             Default is an empty dictionary.
108         tile : int
109             Tile zoom to use with background image. Higher number indicates
110             more resolution. A value of 8 is typical for a normal sonde plot.
111         cartopy_feature : list of str or str
112             Cartopy feature to add to plot.
113         cmap : str
114             Color map to use for colorbar.
115         text : dictionary
116             Dictionary of {text:[lon,lat]} to add to plot. Can have more
117             than one set of text to add.
118         gridlines : boolean
119             Use latitude and longitude gridlines.
120         **kwargs : keyword arguments
121             Any other keyword arguments that will be passed
122             into :func:`matplotlib.pyplot.scatter` when the figure
123             is made. See the matplotlib documentation for further details
124             on what keyword arguments are available.
125 
126         """
127         if dsname is None and len(self._ds.keys()) > 1:
128             raise ValueError(
129                 'You must choose a datastream when there are 2 '
130                 'or more datasets in the GeographicPlotDisplay '
131                 'object.'
132             )
133         elif dsname is None:
134             dsname = list(self._ds.keys())[0]
135 
136         if data_field is None:
137             raise ValueError('You must enter the name of the data ' 'to be plotted.')
138 
139         if projection is None:
140             if CARTOPY_AVAILABLE:
141                 projection = ccrs.PlateCarree()
142 
143         # Extract data from the dataset
144         try:
145             lat = self._ds[dsname][lat_field].values
146         except KeyError:
147             raise ValueError(
148                 (
149                     'You will need to provide the name of the '
150                     "field if not '{}' to use for latitude "
151                     'data.'
152                 ).format(lat_field)
153             )
154         try:
155             lon = self._ds[dsname][lon_field].values
156         except KeyError:
157             raise ValueError(
158                 (
159                     'You will need to provide the name of the '
160                     "field if not '{}' to use for longitude "
161                     'data.'
162                 ).format(lon_field)
163             )
164 
165         # Set up metadata information for display on plot
166         if cbar_label is None:
167             try:
168                 cbar_label = (
169                     self._ds[dsname][data_field].attrs['long_name']
170                     + ' ('
171                     + self._ds[dsname][data_field].attrs['units']
172                     + ')'
173                 )
174             except KeyError:
175                 cbar_label = data_field
176 
177         lat_limits = [np.nanmin(lat), np.nanmax(lat)]
178         lon_limits = [np.nanmin(lon), np.nanmax(lon)]
179         box_size = np.max([np.abs(np.diff(lat_limits)), np.abs(np.diff(lon_limits))])
180         bx_buf = box_size * plot_buffer
181 
182         lat_center = np.sum(lat_limits) / 2.0
183         lon_center = np.sum(lon_limits) / 2.0
184 
185         lat_limits = [
186             lat_center - box_size / 2.0 - bx_buf,
187             lat_center + box_size / 2.0 + bx_buf,
188         ]
189         lon_limits = [
190             lon_center - box_size / 2.0 - bx_buf,
191             lon_center + box_size / 2.0 + bx_buf,
192         ]
193 
194         data = self._ds[dsname][data_field].values
195 
196         # Create base plot projection
197         ax = plt.axes(projection=projection)
198         plt.subplots_adjust(left=0.01, right=0.99, bottom=0.05, top=0.93)
199         ax.set_extent([lon_limits[0], lon_limits[1], lat_limits[0], lat_limits[1]], crs=projection)
200 
201         if title is None:
202             try:
203                 dim = list(self._ds[dsname][data_field].dims)
204                 ts = pd.to_datetime(str(self._ds[dsname][dim[0]].values[0]))
205                 date = ts.strftime('%Y-%m-%d')
206                 time_str = ts.strftime('%H:%M:%S')
207                 plt.title(' '.join([dsname, 'at', date, time_str]))
208             except NameError:
209                 plt.title(dsname)
210         else:
211             plt.title(title)
212 
213         if stamen and img_tile is None:
214             tiler = img_tiles.Stamen(stamen)
215             ax.add_image(tiler, tile)
216             warnings.warn(
217                 "Stamen is deprecated in Cartopy and in future versions of ACT, "
218                 "please use img_tile to specify the image background. ")
219         else:
220             if img_tile is not None:
221                 tiler = getattr(img_tiles, img_tile)(**img_tile_args)
222                 ax.add_image(tiler, tile)
223 
224         colorbar_map = None
225         if cmap is not None:
226             colorbar_map = matplotlib.colormaps.get_cmap(cmap)
227         sc = ax.scatter(lon, lat, c=data, cmap=colorbar_map, **kwargs)
228         cbar = plt.colorbar(sc)
229         cbar.ax.set_ylabel(cbar_label)
230         if cartopy_feature is not None:
231             if isinstance(cartopy_feature, str):
232                 cartopy_feature = [cartopy_feature]
233             cartopy_feature = [ii.upper() for ii in cartopy_feature]
234             if 'STATES' in cartopy_feature:
235                 ax.add_feature(cfeature.STATES.with_scale('10m'))
236             if 'LAND' in cartopy_feature:
237                 ax.add_feature(cfeature.LAND)
238             if 'OCEAN' in cartopy_feature:
239                 ax.add_feature(cfeature.OCEAN)
240             if 'COASTLINE' in cartopy_feature:
241                 ax.add_feature(cfeature.COASTLINE)
242             if 'BORDERS' in cartopy_feature:
243                 ax.add_feature(cfeature.BORDERS, linestyle=':')
244             if 'LAKES' in cartopy_feature:
245                 ax.add_feature(cfeature.LAKES, alpha=0.5)
246             if 'RIVERS' in cartopy_feature:
247                 ax.add_feature(cfeature.RIVERS)
248         if text is not None:
249             for label, location in text.items():
250                 ax.plot(location[0], location[1], marker='*', color='black')
251                 ax.text(location[0], location[1], label, color='black')
252 
253         if gridlines:
254             if projection == ccrs.PlateCarree() or projection == ccrs.Mercator:
255                 gl = ax.gridlines(
256                     crs=projection,
257                     draw_labels=True,
258                     linewidth=1,
259                     color='gray',
260                     alpha=0.5,
261                     linestyle='--',
262                 )
263                 gl.top_labels = False
264                 gl.left_labels = True
265                 gl.bottom_labels = True
266                 gl.right_labels = False
267                 gl.xlabel_style = {'size': 6, 'color': 'gray'}
268                 gl.ylabel_style = {'size': 6, 'color': 'gray'}
269             else:
270                 # Labels are only currently supported for PlateCarree and Mercator
271                 gl = ax.gridlines(
272                     draw_labels=False,
273                     linewidth=1,
274                     color='gray',
275                     alpha=0.5,
276                     linestyle='--',
277                 )
278 
279         return ax
280
```
--- 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/act/plotting/geodisplay.py b/act/plotting/geodisplay.py
--- a/act/plotting/geodisplay.py
+++ b/act/plotting/geodisplay.py
@@ -61,7 +61,6 @@
         img_tile=None,
         img_tile_args={},
         tile=8,
-        stamen='terrain-background',
         cartopy_feature=None,
         cmap='rainbow',
         text=None,
@@ -210,16 +209,9 @@
         else:
             plt.title(title)
 
-        if stamen and img_tile is None:
-            tiler = img_tiles.Stamen(stamen)
+        if img_tile is not None:
+            tiler = getattr(img_tiles, img_tile)(**img_tile_args)
             ax.add_image(tiler, tile)
-            warnings.warn(
-                "Stamen is deprecated in Cartopy and in future versions of ACT, "
-                "please use img_tile to specify the image background. ")
-        else:
-            if img_tile is not None:
-                tiler = getattr(img_tiles, img_tile)(**img_tile_args)
-                ax.add_image(tiler, tile)
 
         colorbar_map = None
         if cmap is not None:

{"golden_diff": "diff --git a/act/plotting/geodisplay.py b/act/plotting/geodisplay.py\n--- a/act/plotting/geodisplay.py\n+++ b/act/plotting/geodisplay.py\n@@ -61,7 +61,6 @@\n         img_tile=None,\n         img_tile_args={},\n         tile=8,\n-        stamen='terrain-background',\n         cartopy_feature=None,\n         cmap='rainbow',\n         text=None,\n@@ -210,16 +209,9 @@\n         else:\n             plt.title(title)\n \n-        if stamen and img_tile is None:\n-            tiler = img_tiles.Stamen(stamen)\n+        if img_tile is not None:\n+            tiler = getattr(img_tiles, img_tile)(**img_tile_args)\n             ax.add_image(tiler, tile)\n-            warnings.warn(\n-                \"Stamen is deprecated in Cartopy and in future versions of ACT, \"\n-                \"please use img_tile to specify the image background. \")\n-        else:\n-            if img_tile is not None:\n-                tiler = getattr(img_tiles, img_tile)(**img_tile_args)\n-                ax.add_image(tiler, tile)\n \n         colorbar_map = None\n         if cmap is not None:\n", "issue": "Sunset Stamen maps in GeoDisplay and potentially replace\nStamen is transitioning their maps to stadia at the end of October 2023.  ACT will need to deprecate that feature in GeoDisplay and potentially look for replacements.\r\n\r\nhttps://github.com/SciTools/cartopy/pull/2266 \n", "before_files": [{"content": "\"\"\"\nStores the class for GeographicPlotDisplay.\n\n\"\"\"\n\nimport warnings\n\nimport matplotlib\nimport matplotlib.pyplot as plt\nimport numpy as np\nimport pandas as pd\n\nfrom .plot import Display\n\ntry:\n    import cartopy.crs as ccrs\n    import cartopy.feature as cfeature\n    from cartopy.io import img_tiles\n\n    CARTOPY_AVAILABLE = True\nexcept ImportError:\n    CARTOPY_AVAILABLE = False\n\n\nclass GeographicPlotDisplay(Display):\n    \"\"\"\n    A class for making geographic tracer plot of aircraft, ship or other moving\n    platform plot.\n\n    This is inherited from the :func:`act.plotting.Display`\n    class and has therefore has the same attributes as that class.\n    See :func:`act.plotting.Display`\n    for more information. There are no additional attributes or parameters\n    to this class.\n\n    In order to create geographic plots, ACT needs the Cartopy package to be\n    installed on your system. More information about\n    Cartopy go here:https://scitools.org.uk/cartopy/docs/latest/ .\n\n    \"\"\"\n\n    def __init__(self, ds, ds_name=None, **kwargs):\n        if not CARTOPY_AVAILABLE:\n            raise ImportError(\n                'Cartopy needs to be installed on your ' 'system to make geographic display plots.'\n            )\n        super().__init__(ds, ds_name, secondary_y_allowed=False, **kwargs)\n        if self.fig is None:\n            self.fig = plt.figure(**kwargs)\n\n    def geoplot(\n        self,\n        data_field=None,\n        lat_field='lat',\n        lon_field='lon',\n        dsname=None,\n        cbar_label=None,\n        title=None,\n        projection=None,\n        plot_buffer=0.08,\n        img_tile=None,\n        img_tile_args={},\n        tile=8,\n        stamen='terrain-background',\n        cartopy_feature=None,\n        cmap='rainbow',\n        text=None,\n        gridlines=True,\n        **kwargs,\n    ):\n        \"\"\"\n        Creates a latitude and longitude plot of a time series data set with\n        data values indicated by color and described with a colorbar.\n        Latitude values must be in degree north (-90 to 90) and\n        longitude must be in degree east (-180 to 180).\n\n        Parameters\n        ----------\n        data_field : str\n            Name of data field in the dataset to plot.\n        lat_field : str\n            Name of latitude field in the dataset to use.\n        lon_field : str\n            Name of longitude field in the dataset to use.\n        dsname : str or None\n            The name of the datastream to plot. Set to None to make ACT\n            attempt to automatically determine this.\n        cbar_label : str\n            Label to use with colorbar. If set to None will attempt\n            to create label from long_name and units.\n        title : str\n            Plot title.\n        projection : cartopy.crs object\n            Project to use on plot. See\n            https://scitools.org.uk/cartopy/docs/latest/reference/projections.html?highlight=projections\n        plot_buffer : float\n            Buffer to add around data on plot in lat and lon dimension.\n        img_tile : str\n            Image to use for the plot background. Set to None to not use\n            background image. For all image background types, see:\n            https://scitools.org.uk/cartopy/docs/v0.16/cartopy/io/img_tiles.html\n            Default is None.\n        img_tile_args : dict\n            Keyword arguments for the chosen img_tile. These arguments can be\n            found for the corresponding img_tile here:\n            https://scitools.org.uk/cartopy/docs/v0.16/cartopy/io/img_tiles.html\n            Default is an empty dictionary.\n        tile : int\n            Tile zoom to use with background image. Higher number indicates\n            more resolution. A value of 8 is typical for a normal sonde plot.\n        cartopy_feature : list of str or str\n            Cartopy feature to add to plot.\n        cmap : str\n            Color map to use for colorbar.\n        text : dictionary\n            Dictionary of {text:[lon,lat]} to add to plot. Can have more\n            than one set of text to add.\n        gridlines : boolean\n            Use latitude and longitude gridlines.\n        **kwargs : keyword arguments\n            Any other keyword arguments that will be passed\n            into :func:`matplotlib.pyplot.scatter` when the figure\n            is made. See the matplotlib documentation for further details\n            on what keyword arguments are available.\n\n        \"\"\"\n        if dsname is None and len(self._ds.keys()) > 1:\n            raise ValueError(\n                'You must choose a datastream when there are 2 '\n                'or more datasets in the GeographicPlotDisplay '\n                'object.'\n            )\n        elif dsname is None:\n            dsname = list(self._ds.keys())[0]\n\n        if data_field is None:\n            raise ValueError('You must enter the name of the data ' 'to be plotted.')\n\n        if projection is None:\n            if CARTOPY_AVAILABLE:\n                projection = ccrs.PlateCarree()\n\n        # Extract data from the dataset\n        try:\n            lat = self._ds[dsname][lat_field].values\n        except KeyError:\n            raise ValueError(\n                (\n                    'You will need to provide the name of the '\n                    \"field if not '{}' to use for latitude \"\n                    'data.'\n                ).format(lat_field)\n            )\n        try:\n            lon = self._ds[dsname][lon_field].values\n        except KeyError:\n            raise ValueError(\n                (\n                    'You will need to provide the name of the '\n                    \"field if not '{}' to use for longitude \"\n                    'data.'\n                ).format(lon_field)\n            )\n\n        # Set up metadata information for display on plot\n        if cbar_label is None:\n            try:\n                cbar_label = (\n                    self._ds[dsname][data_field].attrs['long_name']\n                    + ' ('\n                    + self._ds[dsname][data_field].attrs['units']\n                    + ')'\n                )\n            except KeyError:\n                cbar_label = data_field\n\n        lat_limits = [np.nanmin(lat), np.nanmax(lat)]\n        lon_limits = [np.nanmin(lon), np.nanmax(lon)]\n        box_size = np.max([np.abs(np.diff(lat_limits)), np.abs(np.diff(lon_limits))])\n        bx_buf = box_size * plot_buffer\n\n        lat_center = np.sum(lat_limits) / 2.0\n        lon_center = np.sum(lon_limits) / 2.0\n\n        lat_limits = [\n            lat_center - box_size / 2.0 - bx_buf,\n            lat_center + box_size / 2.0 + bx_buf,\n        ]\n        lon_limits = [\n            lon_center - box_size / 2.0 - bx_buf,\n            lon_center + box_size / 2.0 + bx_buf,\n        ]\n\n        data = self._ds[dsname][data_field].values\n\n        # Create base plot projection\n        ax = plt.axes(projection=projection)\n        plt.subplots_adjust(left=0.01, right=0.99, bottom=0.05, top=0.93)\n        ax.set_extent([lon_limits[0], lon_limits[1], lat_limits[0], lat_limits[1]], crs=projection)\n\n        if title is None:\n            try:\n                dim = list(self._ds[dsname][data_field].dims)\n                ts = pd.to_datetime(str(self._ds[dsname][dim[0]].values[0]))\n                date = ts.strftime('%Y-%m-%d')\n                time_str = ts.strftime('%H:%M:%S')\n                plt.title(' '.join([dsname, 'at', date, time_str]))\n            except NameError:\n                plt.title(dsname)\n        else:\n            plt.title(title)\n\n        if stamen and img_tile is None:\n            tiler = img_tiles.Stamen(stamen)\n            ax.add_image(tiler, tile)\n            warnings.warn(\n                \"Stamen is deprecated in Cartopy and in future versions of ACT, \"\n                \"please use img_tile to specify the image background. \")\n        else:\n            if img_tile is not None:\n                tiler = getattr(img_tiles, img_tile)(**img_tile_args)\n                ax.add_image(tiler, tile)\n\n        colorbar_map = None\n        if cmap is not None:\n            colorbar_map = matplotlib.colormaps.get_cmap(cmap)\n        sc = ax.scatter(lon, lat, c=data, cmap=colorbar_map, **kwargs)\n        cbar = plt.colorbar(sc)\n        cbar.ax.set_ylabel(cbar_label)\n        if cartopy_feature is not None:\n            if isinstance(cartopy_feature, str):\n                cartopy_feature = [cartopy_feature]\n            cartopy_feature = [ii.upper() for ii in cartopy_feature]\n            if 'STATES' in cartopy_feature:\n                ax.add_feature(cfeature.STATES.with_scale('10m'))\n            if 'LAND' in cartopy_feature:\n                ax.add_feature(cfeature.LAND)\n            if 'OCEAN' in cartopy_feature:\n                ax.add_feature(cfeature.OCEAN)\n            if 'COASTLINE' in cartopy_feature:\n                ax.add_feature(cfeature.COASTLINE)\n            if 'BORDERS' in cartopy_feature:\n                ax.add_feature(cfeature.BORDERS, linestyle=':')\n            if 'LAKES' in cartopy_feature:\n                ax.add_feature(cfeature.LAKES, alpha=0.5)\n            if 'RIVERS' in cartopy_feature:\n                ax.add_feature(cfeature.RIVERS)\n        if text is not None:\n            for label, location in text.items():\n                ax.plot(location[0], location[1], marker='*', color='black')\n                ax.text(location[0], location[1], label, color='black')\n\n        if gridlines:\n            if projection == ccrs.PlateCarree() or projection == ccrs.Mercator:\n                gl = ax.gridlines(\n                    crs=projection,\n                    draw_labels=True,\n                    linewidth=1,\n                    color='gray',\n                    alpha=0.5,\n                    linestyle='--',\n                )\n                gl.top_labels = False\n                gl.left_labels = True\n                gl.bottom_labels = True\n                gl.right_labels = False\n                gl.xlabel_style = {'size': 6, 'color': 'gray'}\n                gl.ylabel_style = {'size': 6, 'color': 'gray'}\n            else:\n                # Labels are only currently supported for PlateCarree and Mercator\n                gl = ax.gridlines(\n                    draw_labels=False,\n                    linewidth=1,\n                    color='gray',\n                    alpha=0.5,\n                    linestyle='--',\n                )\n\n        return ax\n", "path": "act/plotting/geodisplay.py"}], "after_files": [{"content": "\"\"\"\nStores the class for GeographicPlotDisplay.\n\n\"\"\"\n\nimport warnings\n\nimport matplotlib\nimport matplotlib.pyplot as plt\nimport numpy as np\nimport pandas as pd\n\nfrom .plot import Display\n\ntry:\n    import cartopy.crs as ccrs\n    import cartopy.feature as cfeature\n    from cartopy.io import img_tiles\n\n    CARTOPY_AVAILABLE = True\nexcept ImportError:\n    CARTOPY_AVAILABLE = False\n\n\nclass GeographicPlotDisplay(Display):\n    \"\"\"\n    A class for making geographic tracer plot of aircraft, ship or other moving\n    platform plot.\n\n    This is inherited from the :func:`act.plotting.Display`\n    class and has therefore has the same attributes as that class.\n    See :func:`act.plotting.Display`\n    for more information. There are no additional attributes or parameters\n    to this class.\n\n    In order to create geographic plots, ACT needs the Cartopy package to be\n    installed on your system. More information about\n    Cartopy go here:https://scitools.org.uk/cartopy/docs/latest/ .\n\n    \"\"\"\n\n    def __init__(self, ds, ds_name=None, **kwargs):\n        if not CARTOPY_AVAILABLE:\n            raise ImportError(\n                'Cartopy needs to be installed on your ' 'system to make geographic display plots.'\n            )\n        super().__init__(ds, ds_name, **kwargs)\n        if self.fig is None:\n            self.fig = plt.figure(**kwargs)\n\n    def geoplot(\n        self,\n        data_field=None,\n        lat_field='lat',\n        lon_field='lon',\n        dsname=None,\n        cbar_label=None,\n        title=None,\n        projection=None,\n        plot_buffer=0.08,\n        img_tile=None,\n        img_tile_args={},\n        tile=8,\n        cartopy_feature=None,\n        cmap='rainbow',\n        text=None,\n        gridlines=True,\n        **kwargs,\n    ):\n        \"\"\"\n        Creates a latitude and longitude plot of a time series data set with\n        data values indicated by color and described with a colorbar.\n        Latitude values must be in degree north (-90 to 90) and\n        longitude must be in degree east (-180 to 180).\n\n        Parameters\n        ----------\n        data_field : str\n            Name of data field in the dataset to plot.\n        lat_field : str\n            Name of latitude field in the dataset to use.\n        lon_field : str\n            Name of longitude field in the dataset to use.\n        dsname : str or None\n            The name of the datastream to plot. Set to None to make ACT\n            attempt to automatically determine this.\n        cbar_label : str\n            Label to use with colorbar. If set to None will attempt\n            to create label from long_name and units.\n        title : str\n            Plot title.\n        projection : cartopy.crs object\n            Project to use on plot. See\n            https://scitools.org.uk/cartopy/docs/latest/reference/projections.html?highlight=projections\n        plot_buffer : float\n            Buffer to add around data on plot in lat and lon dimension.\n        img_tile : str\n            Image to use for the plot background. Set to None to not use\n            background image. For all image background types, see:\n            https://scitools.org.uk/cartopy/docs/v0.16/cartopy/io/img_tiles.html\n            Default is None.\n        img_tile_args : dict\n            Keyword arguments for the chosen img_tile. These arguments can be\n            found for the corresponding img_tile here:\n            https://scitools.org.uk/cartopy/docs/v0.16/cartopy/io/img_tiles.html\n            Default is an empty dictionary.\n        tile : int\n            Tile zoom to use with background image. Higher number indicates\n            more resolution. A value of 8 is typical for a normal sonde plot.\n        cartopy_feature : list of str or str\n            Cartopy feature to add to plot.\n        cmap : str\n            Color map to use for colorbar.\n        text : dictionary\n            Dictionary of {text:[lon,lat]} to add to plot. Can have more\n            than one set of text to add.\n        gridlines : boolean\n            Use latitude and longitude gridlines.\n        **kwargs : keyword arguments\n            Any other keyword arguments that will be passed\n            into :func:`matplotlib.pyplot.scatter` when the figure\n            is made. See the matplotlib documentation for further details\n            on what keyword arguments are available.\n\n        \"\"\"\n        if dsname is None and len(self._ds.keys()) > 1:\n            raise ValueError(\n                'You must choose a datastream when there are 2 '\n                'or more datasets in the GeographicPlotDisplay '\n                'object.'\n            )\n        elif dsname is None:\n            dsname = list(self._ds.keys())[0]\n\n        if data_field is None:\n            raise ValueError('You must enter the name of the data ' 'to be plotted.')\n\n        if projection is None:\n            if CARTOPY_AVAILABLE:\n                projection = ccrs.PlateCarree()\n\n        # Extract data from the dataset\n        try:\n            lat = self._ds[dsname][lat_field].values\n        except KeyError:\n            raise ValueError(\n                (\n                    'You will need to provide the name of the '\n                    \"field if not '{}' to use for latitude \"\n                    'data.'\n                ).format(lat_field)\n            )\n        try:\n            lon = self._ds[dsname][lon_field].values\n        except KeyError:\n            raise ValueError(\n                (\n                    'You will need to provide the name of the '\n                    \"field if not '{}' to use for longitude \"\n                    'data.'\n                ).format(lon_field)\n            )\n\n        # Set up metadata information for display on plot\n        if cbar_label is None:\n            try:\n                cbar_label = (\n                    self._ds[dsname][data_field].attrs['long_name']\n                    + ' ('\n                    + self._ds[dsname][data_field].attrs['units']\n                    + ')'\n                )\n            except KeyError:\n                cbar_label = data_field\n\n        lat_limits = [np.nanmin(lat), np.nanmax(lat)]\n        lon_limits = [np.nanmin(lon), np.nanmax(lon)]\n        box_size = np.max([np.abs(np.diff(lat_limits)), np.abs(np.diff(lon_limits))])\n        bx_buf = box_size * plot_buffer\n\n        lat_center = np.sum(lat_limits) / 2.0\n        lon_center = np.sum(lon_limits) / 2.0\n\n        lat_limits = [\n            lat_center - box_size / 2.0 - bx_buf,\n            lat_center + box_size / 2.0 + bx_buf,\n        ]\n        lon_limits = [\n            lon_center - box_size / 2.0 - bx_buf,\n            lon_center + box_size / 2.0 + bx_buf,\n        ]\n\n        data = self._ds[dsname][data_field].values\n\n        # Create base plot projection\n        ax = plt.axes(projection=projection)\n        plt.subplots_adjust(left=0.01, right=0.99, bottom=0.05, top=0.93)\n        ax.set_extent([lon_limits[0], lon_limits[1], lat_limits[0], lat_limits[1]], crs=projection)\n\n        if title is None:\n            try:\n                dim = list(self._ds[dsname][data_field].dims)\n                ts = pd.to_datetime(str(self._ds[dsname][dim[0]].values[0]))\n                date = ts.strftime('%Y-%m-%d')\n                time_str = ts.strftime('%H:%M:%S')\n                plt.title(' '.join([dsname, 'at', date, time_str]))\n            except NameError:\n                plt.title(dsname)\n        else:\n            plt.title(title)\n\n        if img_tile is not None:\n            tiler = getattr(img_tiles, img_tile)(**img_tile_args)\n            ax.add_image(tiler, tile)\n\n        colorbar_map = None\n        if cmap is not None:\n            colorbar_map = matplotlib.colormaps.get_cmap(cmap)\n        sc = ax.scatter(lon, lat, c=data, cmap=colorbar_map, **kwargs)\n        cbar = plt.colorbar(sc)\n        cbar.ax.set_ylabel(cbar_label)\n        if cartopy_feature is not None:\n            if isinstance(cartopy_feature, str):\n                cartopy_feature = [cartopy_feature]\n            cartopy_feature = [ii.upper() for ii in cartopy_feature]\n            if 'STATES' in cartopy_feature:\n                ax.add_feature(cfeature.STATES.with_scale('10m'))\n            if 'LAND' in cartopy_feature:\n                ax.add_feature(cfeature.LAND)\n            if 'OCEAN' in cartopy_feature:\n                ax.add_feature(cfeature.OCEAN)\n            if 'COASTLINE' in cartopy_feature:\n                ax.add_feature(cfeature.COASTLINE)\n            if 'BORDERS' in cartopy_feature:\n                ax.add_feature(cfeature.BORDERS, linestyle=':')\n            if 'LAKES' in cartopy_feature:\n                ax.add_feature(cfeature.LAKES, alpha=0.5)\n            if 'RIVERS' in cartopy_feature:\n                ax.add_feature(cfeature.RIVERS)\n        if text is not None:\n            for label, location in text.items():\n                ax.plot(location[0], location[1], marker='*', color='black')\n                ax.text(location[0], location[1], label, color='black')\n\n        if gridlines:\n            if projection == ccrs.PlateCarree() or projection == ccrs.Mercator:\n                gl = ax.gridlines(\n                    crs=projection,\n                    draw_labels=True,\n                    linewidth=1,\n                    color='gray',\n                    alpha=0.5,\n                    linestyle='--',\n                )\n                gl.top_labels = False\n                gl.left_labels = True\n                gl.bottom_labels = True\n                gl.right_labels = False\n                gl.xlabel_style = {'size': 6, 'color': 'gray'}\n                gl.ylabel_style = {'size': 6, 'color': 'gray'}\n            else:\n                # Labels are only currently supported for PlateCarree and Mercator\n                gl = ax.gridlines(\n                    draw_labels=False,\n                    linewidth=1,\n                    color='gray',\n                    alpha=0.5,\n                    linestyle='--',\n                )\n\n        return ax\n", "path": "act/plotting/geodisplay.py"}]}