We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Bug in xsection plot map code
* ACT version: Current Version
* Python version: All
* Operating System: All

### Description

xsection plot map is generating images with duplicate axes, see image below. I believe this is probably the cause to our baseline image failure.

![test_xsection_plot_map](https://github.com/ARM-DOE/ACT/assets/19153455/c644a2bf-f011-4f2f-a865-de8ca380db75)



--- 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/xsectiondisplay.py`
Content:
```
1 """
2 Stores the class for XSectionDisplay.
3 
4 """
5 
6 # Import third party libraries
7 import matplotlib.pyplot as plt
8 import numpy as np
9 
10 try:
11     import cartopy.crs as ccrs
12 
13     CARTOPY_AVAILABLE = True
14 except ImportError:
15     CARTOPY_AVAILABLE = False
16 
17 # Import Local Libs
18 from ..utils import data_utils
19 from .plot import Display
20 
21 
22 class XSectionDisplay(Display):
23     """
24     Plots cross sections of multidimensional datasets. The data
25     must be able to be sliced into a 2 dimensional slice using the
26     xarray :func:`xarray.Dataset.sel` and :func:`xarray.Dataset.isel` commands.
27 
28     This is inherited from the :func:`act.plotting.Display`
29     class and has therefore has the same attributes as that class.
30     See :func:`act.plotting.Display`
31     for more information. There are no additional attributes or parameters
32     to this class.
33 
34     In order to create geographic plots, ACT needs the Cartopy package to be
35     installed on your system. More information about
36     Cartopy go here:https://scitools.org.uk/cartopy/docs/latest/.
37 
38     Examples
39     --------
40     For example, if you only want to do a cross section through the first
41     time period of a 3D dataset called :code:`ir_temperature`, you would
42     do the following in xarray:
43 
44     .. code-block:: python
45 
46         time_slice = my_ds["ir_temperature"].isel(time=0)
47 
48     The methods of this class support passing in keyword arguments into
49     xarray :func:`xarray.Dataset.sel` and :func:`xarray.Dataset.isel` commands
50     so that new datasets do not need to be created when slicing by specific time
51     periods or spatial slices. For example, to plot the first time period
52     from :code:`my_ds`, simply do:
53 
54     .. code-block:: python
55 
56         xsection = XSectionDisplay(my_ds, figsize=(15, 8))
57         xsection.plot_xsection_map(
58             None,
59             "ir_temperature",
60             vmin=220,
61             vmax=300,
62             cmap="Greys",
63             x="longitude",
64             y="latitude",
65             isel_kwargs={"time": 0},
66         )
67 
68     Here, the array is sliced by the first time period as specified
69     in :code:`isel_kwargs`. The other keyword arguments are standard keyword
70     arguments taken by :func:`matplotlib.pyplot.pcolormesh`.
71 
72     """
73 
74     def __init__(self, ds, subplot_shape=(1,), ds_name=None, **kwargs):
75         super().__init__(ds, subplot_shape, ds_name, **kwargs)
76 
77     def set_subplot_to_map(self, subplot_index):
78         total_num_plots = self.axes.shape
79 
80         if len(total_num_plots) == 2:
81             second_number = total_num_plots[0]
82             j = subplot_index[1]
83         else:
84             second_number = 1
85             j = 0
86 
87         third_number = second_number * subplot_index[0] + j + 1
88 
89         self.axes[subplot_index] = plt.subplot(
90             total_num_plots[0],
91             second_number,
92             third_number,
93             projection=ccrs.PlateCarree(),
94         )
95 
96     def set_xrng(self, xrng, subplot_index=(0,)):
97         """
98         Sets the x range of the plot.
99 
100         Parameters
101         ----------
102         xrng : 2 number array
103             The x limits of the plot.
104         subplot_index : 1 or 2D tuple, list, or array
105             The index of the subplot to set the x range of.
106 
107         """
108         if self.axes is None:
109             raise RuntimeError('set_xrng requires the plot to be displayed.')
110 
111         if not hasattr(self, 'xrng') and len(self.axes.shape) == 2:
112             self.xrng = np.zeros((self.axes.shape[0], self.axes.shape[1], 2), dtype=xrng[0].dtype)
113         elif not hasattr(self, 'xrng') and len(self.axes.shape) == 1:
114             self.xrng = np.zeros((self.axes.shape[0], 2), dtype=xrng[0].dtype)
115 
116         self.axes[subplot_index].set_xlim(xrng)
117         self.xrng[subplot_index, :] = np.array(xrng)
118 
119     def set_yrng(self, yrng, subplot_index=(0,)):
120         """
121         Sets the y range of the plot.
122 
123         Parameters
124         ----------
125         yrng : 2 number array
126             The y limits of the plot.
127         subplot_index : 1 or 2D tuple, list, or array
128             The index of the subplot to set the x range of.
129 
130         """
131         if self.axes is None:
132             raise RuntimeError('set_yrng requires the plot to be displayed.')
133 
134         if not hasattr(self, 'yrng') and len(self.axes.shape) == 2:
135             self.yrng = np.zeros((self.axes.shape[0], self.axes.shape[1], 2), dtype=yrng[0].dtype)
136         elif not hasattr(self, 'yrng') and len(self.axes.shape) == 1:
137             self.yrng = np.zeros((self.axes.shape[0], 2), dtype=yrng[0].dtype)
138 
139         if yrng[0] == yrng[1]:
140             yrng[1] = yrng[1] + 1
141 
142         self.axes[subplot_index].set_ylim(yrng)
143 
144         self.yrng[subplot_index, :] = yrng
145 
146     def plot_xsection(
147         self,
148         dsname,
149         varname,
150         x=None,
151         y=None,
152         subplot_index=(0,),
153         sel_kwargs=None,
154         isel_kwargs=None,
155         **kwargs,
156     ):
157         """
158         This function plots a cross section whose x and y coordinates are
159         specified by the variable names either provided by the user or
160         automatically detected by xarray.
161 
162         Parameters
163         ----------
164         dsname : str or None
165             The name of the datastream to plot from. Set to None to have
166             ACT attempt to automatically detect this.
167         varname : str
168             The name of the variable to plot.
169         x : str or None
170             The name of the x coordinate variable.
171         y : str or None
172             The name of the y coordinate variable.
173         subplot_index : tuple
174             The index of the subplot to create the plot in.
175         sel_kwargs : dict
176             The keyword arguments to pass into :py:func:`xarray.DataArray.sel`
177             This is useful when your data is in 3 or more dimensions and you
178             want to only view a cross section on a specific x-y plane. For more
179             information on how to use xarray's .sel and .isel functionality
180             to slice datasets, see the documentation on :func:`xarray.DataArray.sel`.
181         isel_kwargs : dict
182             The keyword arguments to pass into :py:func:`xarray.DataArray.sel`
183         **kwargs : keyword arguments
184             Additional keyword arguments will be passed into
185             :func:`xarray.DataArray.plot`.
186 
187         Returns
188         -------
189         ax : matplotlib axis handle
190             The matplotlib axis handle corresponding to the plot.
191 
192         """
193         if dsname is None and len(self._ds.keys()) > 1:
194             raise ValueError(
195                 'You must choose a datastream when there are 2 '
196                 'or more datasets in the TimeSeriesDisplay '
197                 'object.'
198             )
199         elif dsname is None:
200             dsname = list(self._ds.keys())[0]
201         temp_ds = self._ds[dsname].copy()
202 
203         if sel_kwargs is not None:
204             temp_ds = temp_ds.sel(**sel_kwargs, method='nearest')
205 
206         if isel_kwargs is not None:
207             temp_ds = temp_ds.isel(**isel_kwargs)
208 
209         if (x is not None and y is None) or (y is None and x is not None):
210             raise RuntimeError(
211                 'Both x and y must be specified if we are'
212                 + 'not trying to automatically detect them!'
213             )
214 
215         if x is not None:
216             coord_list = {}
217             x_coord_dim = temp_ds[x].dims[0]
218             coord_list[x] = x_coord_dim
219             y_coord_dim = temp_ds[y].dims[0]
220             coord_list[y] = y_coord_dim
221             new_ds = data_utils.assign_coordinates(temp_ds, coord_list)
222             my_dataarray = new_ds[varname]
223         else:
224             my_dataarray = temp_ds[varname]
225 
226         coord_keys = [key for key in my_dataarray.coords.keys()]
227         # X-array will sometimes shorten latitude and longitude variables
228         if x == 'longitude' and x not in coord_keys:
229             xc = 'lon'
230         else:
231             xc = x
232         if y == 'latitude' and y not in coord_keys:
233             yc = 'lat'
234         else:
235             yc = y
236 
237         if x is None:
238             ax = my_dataarray.plot(ax=self.axes[subplot_index], **kwargs)
239         else:
240             ax = my_dataarray.plot(ax=self.axes[subplot_index], x=xc, y=yc, **kwargs)
241 
242         the_coords = [the_keys for the_keys in my_dataarray.coords.keys()]
243         if x is None:
244             x = the_coords[0]
245         else:
246             x = coord_list[x]
247 
248         if y is None:
249             y = the_coords[1]
250         else:
251             y = coord_list[y]
252 
253         xrng = self.axes[subplot_index].get_xlim()
254         self.set_xrng(xrng, subplot_index)
255         yrng = self.axes[subplot_index].get_ylim()
256         self.set_yrng(yrng, subplot_index)
257         del temp_ds
258         return ax
259 
260     def plot_xsection_map(
261         self, dsname, varname, subplot_index=(0,), coastlines=True, background=False, **kwargs
262     ):
263         """
264         Plots a cross section of 2D data on a geographical map.
265 
266         Parameters
267         ----------
268         dsname : str or None
269             The name of the datastream to plot from. Set to None
270             to have ACT attempt to automatically detect this.
271         varname : str
272             The name of the variable to plot.
273         subplot_index : tuple
274             The index of the subplot to plot inside.
275         coastlines : bool
276             Set to True to plot the coastlines.
277         background : bool
278             Set to True to plot a stock image background.
279         **kwargs : keyword arguments
280             Additional keyword arguments will be passed into
281             :func:`act.plotting.XSectionDisplay.plot_xsection`
282 
283         Returns
284         -------
285         ax : matplotlib axis handle
286             The matplotlib axis handle corresponding to the plot.
287 
288         """
289         if not CARTOPY_AVAILABLE:
290             raise ImportError(
291                 'Cartopy needs to be installed in order to plot ' + 'cross sections on maps!'
292             )
293 
294         self.set_subplot_to_map(subplot_index)
295         self.plot_xsection(dsname, varname, subplot_index=subplot_index, **kwargs)
296         xlims = self.xrng[subplot_index].flatten()
297         ylims = self.yrng[subplot_index].flatten()
298         self.axes[subplot_index].set_xticks(np.linspace(round(xlims[0], 0), round(xlims[1], 0), 10))
299         self.axes[subplot_index].set_yticks(np.linspace(round(ylims[0], 0), round(ylims[1], 0), 10))
300 
301         if coastlines:
302             self.axes[subplot_index].coastlines(resolution='10m')
303 
304         if background:
305             self.axes[subplot_index].stock_img()
306 
307         return self.axes[subplot_index]
308
```
--- 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/xsectiondisplay.py b/act/plotting/xsectiondisplay.py
--- a/act/plotting/xsectiondisplay.py
+++ b/act/plotting/xsectiondisplay.py
@@ -75,6 +75,7 @@
         super().__init__(ds, subplot_shape, ds_name, **kwargs)
 
     def set_subplot_to_map(self, subplot_index):
+        self.fig.delaxes(self.axes[subplot_index])
         total_num_plots = self.axes.shape
 
         if len(total_num_plots) == 2:
@@ -235,9 +236,9 @@
             yc = y
 
         if x is None:
-            ax = my_dataarray.plot(ax=self.axes[subplot_index], **kwargs)
+            my_dataarray.plot(ax=self.axes[subplot_index], **kwargs)
         else:
-            ax = my_dataarray.plot(ax=self.axes[subplot_index], x=xc, y=yc, **kwargs)
+            my_dataarray.plot(ax=self.axes[subplot_index], x=xc, y=yc, **kwargs)
 
         the_coords = [the_keys for the_keys in my_dataarray.coords.keys()]
         if x is None:
@@ -255,7 +256,7 @@
         yrng = self.axes[subplot_index].get_ylim()
         self.set_yrng(yrng, subplot_index)
         del temp_ds
-        return ax
+        return self.axes[subplot_index]
 
     def plot_xsection_map(
         self, dsname, varname, subplot_index=(0,), coastlines=True, background=False, **kwargs
@@ -290,7 +291,6 @@
             raise ImportError(
                 'Cartopy needs to be installed in order to plot ' + 'cross sections on maps!'
             )
-
         self.set_subplot_to_map(subplot_index)
         self.plot_xsection(dsname, varname, subplot_index=subplot_index, **kwargs)
         xlims = self.xrng[subplot_index].flatten()

{"golden_diff": "diff --git a/act/plotting/xsectiondisplay.py b/act/plotting/xsectiondisplay.py\n--- a/act/plotting/xsectiondisplay.py\n+++ b/act/plotting/xsectiondisplay.py\n@@ -75,6 +75,7 @@\n         super().__init__(ds, subplot_shape, ds_name, **kwargs)\n \n     def set_subplot_to_map(self, subplot_index):\n+        self.fig.delaxes(self.axes[subplot_index])\n         total_num_plots = self.axes.shape\n \n         if len(total_num_plots) == 2:\n@@ -235,9 +236,9 @@\n             yc = y\n \n         if x is None:\n-            ax = my_dataarray.plot(ax=self.axes[subplot_index], **kwargs)\n+            my_dataarray.plot(ax=self.axes[subplot_index], **kwargs)\n         else:\n-            ax = my_dataarray.plot(ax=self.axes[subplot_index], x=xc, y=yc, **kwargs)\n+            my_dataarray.plot(ax=self.axes[subplot_index], x=xc, y=yc, **kwargs)\n \n         the_coords = [the_keys for the_keys in my_dataarray.coords.keys()]\n         if x is None:\n@@ -255,7 +256,7 @@\n         yrng = self.axes[subplot_index].get_ylim()\n         self.set_yrng(yrng, subplot_index)\n         del temp_ds\n-        return ax\n+        return self.axes[subplot_index]\n \n     def plot_xsection_map(\n         self, dsname, varname, subplot_index=(0,), coastlines=True, background=False, **kwargs\n@@ -290,7 +291,6 @@\n             raise ImportError(\n                 'Cartopy needs to be installed in order to plot ' + 'cross sections on maps!'\n             )\n-\n         self.set_subplot_to_map(subplot_index)\n         self.plot_xsection(dsname, varname, subplot_index=subplot_index, **kwargs)\n         xlims = self.xrng[subplot_index].flatten()\n", "issue": "Bug in xsection plot map code\n* ACT version: Current Version\r\n* Python version: All\r\n* Operating System: All\r\n\r\n### Description\r\n\r\nxsection plot map is generating images with duplicate axes, see image below. I believe this is probably the cause to our baseline image failure.\r\n\r\n![test_xsection_plot_map](https://github.com/ARM-DOE/ACT/assets/19153455/c644a2bf-f011-4f2f-a865-de8ca380db75)\r\n\r\n\n", "before_files": [{"content": "\"\"\"\nStores the class for XSectionDisplay.\n\n\"\"\"\n\n# Import third party libraries\nimport matplotlib.pyplot as plt\nimport numpy as np\n\ntry:\n    import cartopy.crs as ccrs\n\n    CARTOPY_AVAILABLE = True\nexcept ImportError:\n    CARTOPY_AVAILABLE = False\n\n# Import Local Libs\nfrom ..utils import data_utils\nfrom .plot import Display\n\n\nclass XSectionDisplay(Display):\n    \"\"\"\n    Plots cross sections of multidimensional datasets. The data\n    must be able to be sliced into a 2 dimensional slice using the\n    xarray :func:`xarray.Dataset.sel` and :func:`xarray.Dataset.isel` commands.\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    Examples\n    --------\n    For example, if you only want to do a cross section through the first\n    time period of a 3D dataset called :code:`ir_temperature`, you would\n    do the following in xarray:\n\n    .. code-block:: python\n\n        time_slice = my_ds[\"ir_temperature\"].isel(time=0)\n\n    The methods of this class support passing in keyword arguments into\n    xarray :func:`xarray.Dataset.sel` and :func:`xarray.Dataset.isel` commands\n    so that new datasets do not need to be created when slicing by specific time\n    periods or spatial slices. For example, to plot the first time period\n    from :code:`my_ds`, simply do:\n\n    .. code-block:: python\n\n        xsection = XSectionDisplay(my_ds, figsize=(15, 8))\n        xsection.plot_xsection_map(\n            None,\n            \"ir_temperature\",\n            vmin=220,\n            vmax=300,\n            cmap=\"Greys\",\n            x=\"longitude\",\n            y=\"latitude\",\n            isel_kwargs={\"time\": 0},\n        )\n\n    Here, the array is sliced by the first time period as specified\n    in :code:`isel_kwargs`. The other keyword arguments are standard keyword\n    arguments taken by :func:`matplotlib.pyplot.pcolormesh`.\n\n    \"\"\"\n\n    def __init__(self, ds, subplot_shape=(1,), ds_name=None, **kwargs):\n        super().__init__(ds, subplot_shape, ds_name, **kwargs)\n\n    def set_subplot_to_map(self, subplot_index):\n        total_num_plots = self.axes.shape\n\n        if len(total_num_plots) == 2:\n            second_number = total_num_plots[0]\n            j = subplot_index[1]\n        else:\n            second_number = 1\n            j = 0\n\n        third_number = second_number * subplot_index[0] + j + 1\n\n        self.axes[subplot_index] = plt.subplot(\n            total_num_plots[0],\n            second_number,\n            third_number,\n            projection=ccrs.PlateCarree(),\n        )\n\n    def set_xrng(self, xrng, subplot_index=(0,)):\n        \"\"\"\n        Sets the x range of the plot.\n\n        Parameters\n        ----------\n        xrng : 2 number array\n            The x limits of the plot.\n        subplot_index : 1 or 2D tuple, list, or array\n            The index of the subplot to set the x range of.\n\n        \"\"\"\n        if self.axes is None:\n            raise RuntimeError('set_xrng requires the plot to be displayed.')\n\n        if not hasattr(self, 'xrng') and len(self.axes.shape) == 2:\n            self.xrng = np.zeros((self.axes.shape[0], self.axes.shape[1], 2), dtype=xrng[0].dtype)\n        elif not hasattr(self, 'xrng') and len(self.axes.shape) == 1:\n            self.xrng = np.zeros((self.axes.shape[0], 2), dtype=xrng[0].dtype)\n\n        self.axes[subplot_index].set_xlim(xrng)\n        self.xrng[subplot_index, :] = np.array(xrng)\n\n    def set_yrng(self, yrng, subplot_index=(0,)):\n        \"\"\"\n        Sets the y range of the plot.\n\n        Parameters\n        ----------\n        yrng : 2 number array\n            The y limits of the plot.\n        subplot_index : 1 or 2D tuple, list, or array\n            The index of the subplot to set the x range of.\n\n        \"\"\"\n        if self.axes is None:\n            raise RuntimeError('set_yrng requires the plot to be displayed.')\n\n        if not hasattr(self, 'yrng') and len(self.axes.shape) == 2:\n            self.yrng = np.zeros((self.axes.shape[0], self.axes.shape[1], 2), dtype=yrng[0].dtype)\n        elif not hasattr(self, 'yrng') and len(self.axes.shape) == 1:\n            self.yrng = np.zeros((self.axes.shape[0], 2), dtype=yrng[0].dtype)\n\n        if yrng[0] == yrng[1]:\n            yrng[1] = yrng[1] + 1\n\n        self.axes[subplot_index].set_ylim(yrng)\n\n        self.yrng[subplot_index, :] = yrng\n\n    def plot_xsection(\n        self,\n        dsname,\n        varname,\n        x=None,\n        y=None,\n        subplot_index=(0,),\n        sel_kwargs=None,\n        isel_kwargs=None,\n        **kwargs,\n    ):\n        \"\"\"\n        This function plots a cross section whose x and y coordinates are\n        specified by the variable names either provided by the user or\n        automatically detected by xarray.\n\n        Parameters\n        ----------\n        dsname : str or None\n            The name of the datastream to plot from. Set to None to have\n            ACT attempt to automatically detect this.\n        varname : str\n            The name of the variable to plot.\n        x : str or None\n            The name of the x coordinate variable.\n        y : str or None\n            The name of the y coordinate variable.\n        subplot_index : tuple\n            The index of the subplot to create the plot in.\n        sel_kwargs : dict\n            The keyword arguments to pass into :py:func:`xarray.DataArray.sel`\n            This is useful when your data is in 3 or more dimensions and you\n            want to only view a cross section on a specific x-y plane. For more\n            information on how to use xarray's .sel and .isel functionality\n            to slice datasets, see the documentation on :func:`xarray.DataArray.sel`.\n        isel_kwargs : dict\n            The keyword arguments to pass into :py:func:`xarray.DataArray.sel`\n        **kwargs : keyword arguments\n            Additional keyword arguments will be passed into\n            :func:`xarray.DataArray.plot`.\n\n        Returns\n        -------\n        ax : matplotlib axis handle\n            The matplotlib axis handle corresponding to the plot.\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 TimeSeriesDisplay '\n                'object.'\n            )\n        elif dsname is None:\n            dsname = list(self._ds.keys())[0]\n        temp_ds = self._ds[dsname].copy()\n\n        if sel_kwargs is not None:\n            temp_ds = temp_ds.sel(**sel_kwargs, method='nearest')\n\n        if isel_kwargs is not None:\n            temp_ds = temp_ds.isel(**isel_kwargs)\n\n        if (x is not None and y is None) or (y is None and x is not None):\n            raise RuntimeError(\n                'Both x and y must be specified if we are'\n                + 'not trying to automatically detect them!'\n            )\n\n        if x is not None:\n            coord_list = {}\n            x_coord_dim = temp_ds[x].dims[0]\n            coord_list[x] = x_coord_dim\n            y_coord_dim = temp_ds[y].dims[0]\n            coord_list[y] = y_coord_dim\n            new_ds = data_utils.assign_coordinates(temp_ds, coord_list)\n            my_dataarray = new_ds[varname]\n        else:\n            my_dataarray = temp_ds[varname]\n\n        coord_keys = [key for key in my_dataarray.coords.keys()]\n        # X-array will sometimes shorten latitude and longitude variables\n        if x == 'longitude' and x not in coord_keys:\n            xc = 'lon'\n        else:\n            xc = x\n        if y == 'latitude' and y not in coord_keys:\n            yc = 'lat'\n        else:\n            yc = y\n\n        if x is None:\n            ax = my_dataarray.plot(ax=self.axes[subplot_index], **kwargs)\n        else:\n            ax = my_dataarray.plot(ax=self.axes[subplot_index], x=xc, y=yc, **kwargs)\n\n        the_coords = [the_keys for the_keys in my_dataarray.coords.keys()]\n        if x is None:\n            x = the_coords[0]\n        else:\n            x = coord_list[x]\n\n        if y is None:\n            y = the_coords[1]\n        else:\n            y = coord_list[y]\n\n        xrng = self.axes[subplot_index].get_xlim()\n        self.set_xrng(xrng, subplot_index)\n        yrng = self.axes[subplot_index].get_ylim()\n        self.set_yrng(yrng, subplot_index)\n        del temp_ds\n        return ax\n\n    def plot_xsection_map(\n        self, dsname, varname, subplot_index=(0,), coastlines=True, background=False, **kwargs\n    ):\n        \"\"\"\n        Plots a cross section of 2D data on a geographical map.\n\n        Parameters\n        ----------\n        dsname : str or None\n            The name of the datastream to plot from. Set to None\n            to have ACT attempt to automatically detect this.\n        varname : str\n            The name of the variable to plot.\n        subplot_index : tuple\n            The index of the subplot to plot inside.\n        coastlines : bool\n            Set to True to plot the coastlines.\n        background : bool\n            Set to True to plot a stock image background.\n        **kwargs : keyword arguments\n            Additional keyword arguments will be passed into\n            :func:`act.plotting.XSectionDisplay.plot_xsection`\n\n        Returns\n        -------\n        ax : matplotlib axis handle\n            The matplotlib axis handle corresponding to the plot.\n\n        \"\"\"\n        if not CARTOPY_AVAILABLE:\n            raise ImportError(\n                'Cartopy needs to be installed in order to plot ' + 'cross sections on maps!'\n            )\n\n        self.set_subplot_to_map(subplot_index)\n        self.plot_xsection(dsname, varname, subplot_index=subplot_index, **kwargs)\n        xlims = self.xrng[subplot_index].flatten()\n        ylims = self.yrng[subplot_index].flatten()\n        self.axes[subplot_index].set_xticks(np.linspace(round(xlims[0], 0), round(xlims[1], 0), 10))\n        self.axes[subplot_index].set_yticks(np.linspace(round(ylims[0], 0), round(ylims[1], 0), 10))\n\n        if coastlines:\n            self.axes[subplot_index].coastlines(resolution='10m')\n\n        if background:\n            self.axes[subplot_index].stock_img()\n\n        return self.axes[subplot_index]\n", "path": "act/plotting/xsectiondisplay.py"}], "after_files": [{"content": "\"\"\"\nStores the class for XSectionDisplay.\n\n\"\"\"\n\n# Import third party libraries\nimport matplotlib.pyplot as plt\nimport numpy as np\n\ntry:\n    import cartopy.crs as ccrs\n\n    CARTOPY_AVAILABLE = True\nexcept ImportError:\n    CARTOPY_AVAILABLE = False\n\n# Import Local Libs\nfrom ..utils import data_utils\nfrom .plot import Display\n\n\nclass XSectionDisplay(Display):\n    \"\"\"\n    Plots cross sections of multidimensional datasets. The data\n    must be able to be sliced into a 2 dimensional slice using the\n    xarray :func:`xarray.Dataset.sel` and :func:`xarray.Dataset.isel` commands.\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    Examples\n    --------\n    For example, if you only want to do a cross section through the first\n    time period of a 3D dataset called :code:`ir_temperature`, you would\n    do the following in xarray:\n\n    .. code-block:: python\n\n        time_slice = my_ds[\"ir_temperature\"].isel(time=0)\n\n    The methods of this class support passing in keyword arguments into\n    xarray :func:`xarray.Dataset.sel` and :func:`xarray.Dataset.isel` commands\n    so that new datasets do not need to be created when slicing by specific time\n    periods or spatial slices. For example, to plot the first time period\n    from :code:`my_ds`, simply do:\n\n    .. code-block:: python\n\n        xsection = XSectionDisplay(my_ds, figsize=(15, 8))\n        xsection.plot_xsection_map(\n            None,\n            \"ir_temperature\",\n            vmin=220,\n            vmax=300,\n            cmap=\"Greys\",\n            x=\"longitude\",\n            y=\"latitude\",\n            isel_kwargs={\"time\": 0},\n        )\n\n    Here, the array is sliced by the first time period as specified\n    in :code:`isel_kwargs`. The other keyword arguments are standard keyword\n    arguments taken by :func:`matplotlib.pyplot.pcolormesh`.\n\n    \"\"\"\n\n    def __init__(self, ds, subplot_shape=(1,), ds_name=None, **kwargs):\n        super().__init__(ds, subplot_shape, ds_name, **kwargs)\n\n    def set_subplot_to_map(self, subplot_index):\n        self.fig.delaxes(self.axes[subplot_index])\n        total_num_plots = self.axes.shape\n\n        if len(total_num_plots) == 2:\n            second_number = total_num_plots[0]\n            j = subplot_index[1]\n        else:\n            second_number = 1\n            j = 0\n\n        third_number = second_number * subplot_index[0] + j + 1\n\n        self.axes[subplot_index] = plt.subplot(\n            total_num_plots[0],\n            second_number,\n            third_number,\n            projection=ccrs.PlateCarree(),\n        )\n\n    def set_xrng(self, xrng, subplot_index=(0,)):\n        \"\"\"\n        Sets the x range of the plot.\n\n        Parameters\n        ----------\n        xrng : 2 number array\n            The x limits of the plot.\n        subplot_index : 1 or 2D tuple, list, or array\n            The index of the subplot to set the x range of.\n\n        \"\"\"\n        if self.axes is None:\n            raise RuntimeError('set_xrng requires the plot to be displayed.')\n\n        if not hasattr(self, 'xrng') and len(self.axes.shape) == 2:\n            self.xrng = np.zeros((self.axes.shape[0], self.axes.shape[1], 2), dtype=xrng[0].dtype)\n        elif not hasattr(self, 'xrng') and len(self.axes.shape) == 1:\n            self.xrng = np.zeros((self.axes.shape[0], 2), dtype=xrng[0].dtype)\n\n        self.axes[subplot_index].set_xlim(xrng)\n        self.xrng[subplot_index, :] = np.array(xrng)\n\n    def set_yrng(self, yrng, subplot_index=(0,)):\n        \"\"\"\n        Sets the y range of the plot.\n\n        Parameters\n        ----------\n        yrng : 2 number array\n            The y limits of the plot.\n        subplot_index : 1 or 2D tuple, list, or array\n            The index of the subplot to set the x range of.\n\n        \"\"\"\n        if self.axes is None:\n            raise RuntimeError('set_yrng requires the plot to be displayed.')\n\n        if not hasattr(self, 'yrng') and len(self.axes.shape) == 2:\n            self.yrng = np.zeros((self.axes.shape[0], self.axes.shape[1], 2), dtype=yrng[0].dtype)\n        elif not hasattr(self, 'yrng') and len(self.axes.shape) == 1:\n            self.yrng = np.zeros((self.axes.shape[0], 2), dtype=yrng[0].dtype)\n\n        if yrng[0] == yrng[1]:\n            yrng[1] = yrng[1] + 1\n\n        self.axes[subplot_index].set_ylim(yrng)\n\n        self.yrng[subplot_index, :] = yrng\n\n    def plot_xsection(\n        self,\n        dsname,\n        varname,\n        x=None,\n        y=None,\n        subplot_index=(0,),\n        sel_kwargs=None,\n        isel_kwargs=None,\n        **kwargs,\n    ):\n        \"\"\"\n        This function plots a cross section whose x and y coordinates are\n        specified by the variable names either provided by the user or\n        automatically detected by xarray.\n\n        Parameters\n        ----------\n        dsname : str or None\n            The name of the datastream to plot from. Set to None to have\n            ACT attempt to automatically detect this.\n        varname : str\n            The name of the variable to plot.\n        x : str or None\n            The name of the x coordinate variable.\n        y : str or None\n            The name of the y coordinate variable.\n        subplot_index : tuple\n            The index of the subplot to create the plot in.\n        sel_kwargs : dict\n            The keyword arguments to pass into :py:func:`xarray.DataArray.sel`\n            This is useful when your data is in 3 or more dimensions and you\n            want to only view a cross section on a specific x-y plane. For more\n            information on how to use xarray's .sel and .isel functionality\n            to slice datasets, see the documentation on :func:`xarray.DataArray.sel`.\n        isel_kwargs : dict\n            The keyword arguments to pass into :py:func:`xarray.DataArray.sel`\n        **kwargs : keyword arguments\n            Additional keyword arguments will be passed into\n            :func:`xarray.DataArray.plot`.\n\n        Returns\n        -------\n        ax : matplotlib axis handle\n            The matplotlib axis handle corresponding to the plot.\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 TimeSeriesDisplay '\n                'object.'\n            )\n        elif dsname is None:\n            dsname = list(self._ds.keys())[0]\n        temp_ds = self._ds[dsname].copy()\n\n        if sel_kwargs is not None:\n            temp_ds = temp_ds.sel(**sel_kwargs, method='nearest')\n\n        if isel_kwargs is not None:\n            temp_ds = temp_ds.isel(**isel_kwargs)\n\n        if (x is not None and y is None) or (y is None and x is not None):\n            raise RuntimeError(\n                'Both x and y must be specified if we are'\n                + 'not trying to automatically detect them!'\n            )\n\n        if x is not None:\n            coord_list = {}\n            x_coord_dim = temp_ds[x].dims[0]\n            coord_list[x] = x_coord_dim\n            y_coord_dim = temp_ds[y].dims[0]\n            coord_list[y] = y_coord_dim\n            new_ds = data_utils.assign_coordinates(temp_ds, coord_list)\n            my_dataarray = new_ds[varname]\n        else:\n            my_dataarray = temp_ds[varname]\n\n        coord_keys = [key for key in my_dataarray.coords.keys()]\n        # X-array will sometimes shorten latitude and longitude variables\n        if x == 'longitude' and x not in coord_keys:\n            xc = 'lon'\n        else:\n            xc = x\n        if y == 'latitude' and y not in coord_keys:\n            yc = 'lat'\n        else:\n            yc = y\n\n        if x is None:\n            my_dataarray.plot(ax=self.axes[subplot_index], **kwargs)\n        else:\n            my_dataarray.plot(ax=self.axes[subplot_index], x=xc, y=yc, **kwargs)\n\n        the_coords = [the_keys for the_keys in my_dataarray.coords.keys()]\n        if x is None:\n            x = the_coords[0]\n        else:\n            x = coord_list[x]\n\n        if y is None:\n            y = the_coords[1]\n        else:\n            y = coord_list[y]\n\n        xrng = self.axes[subplot_index].get_xlim()\n        self.set_xrng(xrng, subplot_index)\n        yrng = self.axes[subplot_index].get_ylim()\n        self.set_yrng(yrng, subplot_index)\n        del temp_ds\n        return self.axes[subplot_index]\n\n    def plot_xsection_map(\n        self, dsname, varname, subplot_index=(0,), coastlines=True, background=False, **kwargs\n    ):\n        \"\"\"\n        Plots a cross section of 2D data on a geographical map.\n\n        Parameters\n        ----------\n        dsname : str or None\n            The name of the datastream to plot from. Set to None\n            to have ACT attempt to automatically detect this.\n        varname : str\n            The name of the variable to plot.\n        subplot_index : tuple\n            The index of the subplot to plot inside.\n        coastlines : bool\n            Set to True to plot the coastlines.\n        background : bool\n            Set to True to plot a stock image background.\n        **kwargs : keyword arguments\n            Additional keyword arguments will be passed into\n            :func:`act.plotting.XSectionDisplay.plot_xsection`\n\n        Returns\n        -------\n        ax : matplotlib axis handle\n            The matplotlib axis handle corresponding to the plot.\n\n        \"\"\"\n        if not CARTOPY_AVAILABLE:\n            raise ImportError(\n                'Cartopy needs to be installed in order to plot ' + 'cross sections on maps!'\n            )\n        self.set_subplot_to_map(subplot_index)\n        self.plot_xsection(dsname, varname, subplot_index=subplot_index, **kwargs)\n        xlims = self.xrng[subplot_index].flatten()\n        ylims = self.yrng[subplot_index].flatten()\n        self.axes[subplot_index].set_xticks(np.linspace(round(xlims[0], 0), round(xlims[1], 0), 10))\n        self.axes[subplot_index].set_yticks(np.linspace(round(ylims[0], 0), round(ylims[1], 0), 10))\n\n        if coastlines:\n            self.axes[subplot_index].coastlines(resolution='10m')\n\n        if background:\n            self.axes[subplot_index].stock_img()\n\n        return self.axes[subplot_index]\n", "path": "act/plotting/xsectiondisplay.py"}]}