We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
ImageAnimatorWCS unit_x_axis and unit_y_axis do not follow image_axes
The units are always applied to the last two dimensions of the WCS, which is just wrong.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `sunpy/visualization/animator/image.py`
Content:
```
1 import numpy as np
2 import matplotlib as mpl
3 import astropy.wcs
4 
5 from . base import ArrayAnimator
6 
7 __all__ = ['ImageAnimator', 'ImageAnimatorWCS']
8 
9 
10 class ImageAnimator(ArrayAnimator):
11     """
12     Create a matplotlib backend independent data explorer for 2D images.
13 
14     The following keyboard shortcuts are defined in the viewer:
15 
16     - 'left': previous step on active slider
17     - 'right': next step on active slider
18     - 'top': change the active slider up one
19     - 'bottom': change the active slider down one
20     - 'p': play/pause active slider
21 
22     This viewer can have user defined buttons added by specifying the labels
23     and functions called when those buttons are clicked as keyword arguments.
24 
25     Parameters
26     ----------
27     data: ndarray
28         The data to be visualized >= 2D
29 
30     image_axes: list
31         The two axes that make the image
32 
33     fig: mpl.figure
34         Figure to use
35 
36     axis_ranges: list of physical coordinates for array or None
37         If None array indices will be used for all axes.
38         If a list it should contain one element for each axis of the numpy array.
39         For the image axes a [min, max] pair should be specified which will be
40         passed to :func:`matplotlib.pyplot.imshow` as extent.
41         For the slider axes a [min, max] pair can be specified or an array the
42         same length as the axis which will provide all values for that slider.
43         If None is specified for an axis then the array indices will be used
44         for that axis.
45 
46     interval: int
47         Animation interval in ms
48 
49     colorbar: bool
50         Plot colorbar
51 
52     button_labels: list
53         List of strings to label buttons
54 
55     button_func: list
56         List of functions to map to the buttons
57 
58     Extra keywords are passed to imshow.
59 
60     """
61 
62     def __init__(self, data, image_axes=[-2, -1], axis_ranges=None, **kwargs):
63         # Check that number of axes is 2.
64         if len(image_axes) != 2:
65             raise ValueError("There can only be two spatial axes")
66         # Define number of slider axes.
67         self.naxis = data.ndim
68         self.num_sliders = self.naxis-2
69         # Define marker to determine if plot axes values are supplied via array of
70         # pixel values or min max pair. This will determine the type of image produced
71         # and hence how to plot and update it.
72         self._non_regular_plot_axis = False
73         # Run init for parent class
74         super(ImageAnimator, self).__init__(data, image_axes=image_axes,
75                                             axis_ranges=axis_ranges, **kwargs)
76 
77     def plot_start_image(self, ax):
78         """Sets up plot of initial image."""
79         # Create extent arg
80         extent = []
81         # reverse because numpy is in y-x and extent is x-y
82         for i in self.image_axes[::-1]:
83             if self._non_regular_plot_axis is False and len(self.axis_ranges[i]) > 2:
84                 self._non_regular_plot_axis = True
85             extent.append(self.axis_ranges[i][0])
86             extent.append(self.axis_ranges[i][-1])
87 
88         imshow_args = {'interpolation': 'nearest',
89                        'origin': 'lower',
90                        'extent': extent}
91         imshow_args.update(self.imshow_kwargs)
92 
93         # If value along an axis is set with an array, generate a NonUniformImage
94         if self._non_regular_plot_axis:
95             # If user has inverted the axes, transpose the data so the dimensions match.
96             if self.image_axes[0] < self.image_axes[1]:
97                 data = self.data[self.frame_index].transpose()
98             else:
99                 data = self.data[self.frame_index]
100             # Initialize a NonUniformImage with the relevant data and axis values and
101             # add the image to the axes.
102             im = mpl.image.NonUniformImage(ax, **imshow_args)
103             im.set_data(self.axis_ranges[self.image_axes[0]],
104                         self.axis_ranges[self.image_axes[1]], data)
105             ax.add_image(im)
106             # Define the xlim and ylim bearing in mind that the pixel values along
107             # the axes are plotted in the middle of the pixel.  Therefore make sure
108             # there's half a pixel buffer either side of the ends of the axis ranges.
109             x_ranges = self.axis_ranges[self.image_axes[0]]
110             xlim = (x_ranges[0] - (x_ranges[1] - x_ranges[0]) / 2.,
111                     x_ranges[-1] + (x_ranges[-1] - x_ranges[-2]) / 2.)
112             y_ranges = self.axis_ranges[self.image_axes[1]]
113             ylim = (y_ranges[0] - (y_ranges[1] - y_ranges[0]) / 2.,
114                     y_ranges[-1] + (y_ranges[-1] - y_ranges[-2]) / 2.)
115             ax.set_xlim(xlim)
116             ax.set_ylim(ylim)
117         else:
118             # Else produce a more basic plot with regular axes.
119             im = ax.imshow(self.data[self.frame_index], **imshow_args)
120         if self.if_colorbar:
121             self._add_colorbar(im)
122 
123         return im
124 
125     def update_plot(self, val, im, slider):
126         """Updates plot based on slider/array dimension being iterated."""
127         val = int(val)
128         ax_ind = self.slider_axes[slider.slider_ind]
129         ind = int(np.argmin(np.abs(self.axis_ranges[ax_ind] - val)))
130         self.frame_slice[ax_ind] = ind
131         if val != slider.cval:
132             if self._non_regular_plot_axis:
133                 if self.image_axes[0] < self.image_axes[1]:
134                     data = self.data[self.frame_index].transpose()
135                 else:
136                     data = self.data[self.frame_index]
137                 im.set_data(self.axis_ranges[self.image_axes[0]],
138                             self.axis_ranges[self.image_axes[1]], data)
139             else:
140                 im.set_array(self.data[self.frame_index])
141             slider.cval = val
142 
143 
144 class ImageAnimatorWCS(ImageAnimator):
145     """
146     Animates N-dimensional data with the associated astropy WCS object.
147 
148     The following keyboard shortcuts are defined in the viewer:
149 
150     - 'left': previous step on active slider
151     - 'right': next step on active slider
152     - 'top': change the active slider up one
153     - 'bottom': change the active slider down one
154     - 'p': play/pause active slider
155 
156     This viewer can have user defined buttons added by specifying the labels
157     and functions called when those buttons are clicked as keyword arguments.
158 
159     Parameters
160     ----------
161     data: `numpy.ndarray`
162         The data to be visualized >= 2D
163 
164     wcs: `astropy.wcs.WCS`
165         The wcs data.
166 
167     image_axes: `list`
168         The two axes that make the image
169 
170     fig: `matplotlib.figure.Figure`
171         Figure to use
172 
173     axis_ranges: list of physical coordinates for array or None
174         If None array indices will be used for all axes.
175         If a list it should contain one element for each axis of the numpy array.
176         For the image axes a [min, max] pair should be specified which will be
177         passed to :func:`matplotlib.pyplot.imshow` as extent.
178         For the slider axes a [min, max] pair can be specified or an array the
179         same length as the axis which will provide all values for that slider.
180         If None is specified for an axis then the array indices will be used
181         for that axis.
182 
183     interval: `int`
184         Animation interval in ms
185 
186     colorbar: `bool`
187         Plot colorbar
188 
189     button_labels: `list`
190         List of strings to label buttons
191 
192     button_func: `list`
193         List of functions to map to the buttons
194 
195     unit_x_axis: `astropy.units.Unit`
196         The unit of x axis.
197 
198     unit_y_axis: `astropy.units.Unit`
199         The unit of y axis.
200 
201     Extra keywords are passed to imshow.
202 
203     """
204     def __init__(self, data, wcs=None, image_axes=[-1, -2], unit_x_axis=None, unit_y_axis=None,
205                  axis_ranges=None, **kwargs):
206         if not isinstance(wcs, astropy.wcs.WCS):
207             raise ValueError("wcs data should be provided.")
208         if wcs.wcs.naxis is not data.ndim:
209             raise ValueError("Dimensions of data and wcs not matching")
210         self.wcs = wcs
211         list_slices_wcsaxes = [0 for i in range(self.wcs.naxis)]
212         list_slices_wcsaxes[image_axes[0]] = 'x'
213         list_slices_wcsaxes[image_axes[1]] = 'y'
214         self.slices_wcsaxes = list_slices_wcsaxes[::-1]
215         self.unit_x_axis = unit_x_axis
216         self.unit_y_axis = unit_y_axis
217         super(ImageAnimatorWCS, self).__init__(data, image_axes=image_axes,
218                                                axis_ranges=axis_ranges, **kwargs)
219 
220     def _get_main_axes(self):
221         axes = self.fig.add_axes([0.1, 0.1, 0.8, 0.8], projection=self.wcs,
222                                  slices=self.slices_wcsaxes)
223         self._set_unit_in_axis(axes)
224         return axes
225 
226     def _set_unit_in_axis(self, axes):
227         if self.unit_x_axis is not None:
228             axes.coords[2].set_format_unit(self.unit_x_axis)
229             axes.coords[2].set_ticks(exclude_overlapping=True)
230         if self.unit_y_axis is not None:
231             axes.coords[1].set_format_unit(self.unit_y_axis)
232             axes.coords[1].set_ticks(exclude_overlapping=True)
233 
234     def plot_start_image(self, ax):
235         """Sets up plot of initial image."""
236         imshow_args = {'interpolation': 'nearest',
237                        'origin': 'lower',
238                        }
239         imshow_args.update(self.imshow_kwargs)
240         im = ax.imshow(self.data[self.frame_index], **imshow_args)
241         if self.if_colorbar:
242             self._add_colorbar(im)
243         return im
244 
245     def update_plot(self, val, im, slider):
246         """Updates plot based on slider/array dimension being iterated."""
247         val = int(val)
248         ax_ind = self.slider_axes[slider.slider_ind]
249         ind = int(np.argmin(np.abs(self.axis_ranges[ax_ind] - val)))
250         self.frame_slice[ax_ind] = ind
251         list_slices_wcsaxes = list(self.slices_wcsaxes)
252         list_slices_wcsaxes[self.wcs.naxis-ax_ind-1] = val
253         self.slices_wcsaxes = list_slices_wcsaxes
254         if val != slider.cval:
255             self.axes.reset_wcs(wcs=self.wcs, slices=self.slices_wcsaxes)
256             self._set_unit_in_axis(self.axes)
257             im.set_array(self.data[self.frame_index])
258             slider.cval = val
259
```
--- 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/sunpy/visualization/animator/image.py b/sunpy/visualization/animator/image.py
--- a/sunpy/visualization/animator/image.py
+++ b/sunpy/visualization/animator/image.py
@@ -224,12 +224,14 @@
         return axes
 
     def _set_unit_in_axis(self, axes):
+        x_index = self.slices_wcsaxes.index("x")
+        y_index = self.slices_wcsaxes.index("y")
         if self.unit_x_axis is not None:
-            axes.coords[2].set_format_unit(self.unit_x_axis)
-            axes.coords[2].set_ticks(exclude_overlapping=True)
+            axes.coords[x_index].set_format_unit(self.unit_x_axis)
+            axes.coords[x_index].set_ticks(exclude_overlapping=True)
         if self.unit_y_axis is not None:
-            axes.coords[1].set_format_unit(self.unit_y_axis)
-            axes.coords[1].set_ticks(exclude_overlapping=True)
+            axes.coords[y_index].set_format_unit(self.unit_y_axis)
+            axes.coords[y_index].set_ticks(exclude_overlapping=True)
 
     def plot_start_image(self, ax):
         """Sets up plot of initial image."""

{"golden_diff": "diff --git a/sunpy/visualization/animator/image.py b/sunpy/visualization/animator/image.py\n--- a/sunpy/visualization/animator/image.py\n+++ b/sunpy/visualization/animator/image.py\n@@ -224,12 +224,14 @@\n         return axes\n \n     def _set_unit_in_axis(self, axes):\n+        x_index = self.slices_wcsaxes.index(\"x\")\n+        y_index = self.slices_wcsaxes.index(\"y\")\n         if self.unit_x_axis is not None:\n-            axes.coords[2].set_format_unit(self.unit_x_axis)\n-            axes.coords[2].set_ticks(exclude_overlapping=True)\n+            axes.coords[x_index].set_format_unit(self.unit_x_axis)\n+            axes.coords[x_index].set_ticks(exclude_overlapping=True)\n         if self.unit_y_axis is not None:\n-            axes.coords[1].set_format_unit(self.unit_y_axis)\n-            axes.coords[1].set_ticks(exclude_overlapping=True)\n+            axes.coords[y_index].set_format_unit(self.unit_y_axis)\n+            axes.coords[y_index].set_ticks(exclude_overlapping=True)\n \n     def plot_start_image(self, ax):\n         \"\"\"Sets up plot of initial image.\"\"\"\n", "issue": "ImageAnimatorWCS unit_x_axis and unit_y_axis do not follow image_axes\nThe units are always applied to the last two dimensions of the WCS, which is just wrong.\n", "before_files": [{"content": "import numpy as np\nimport matplotlib as mpl\nimport astropy.wcs\n\nfrom . base import ArrayAnimator\n\n__all__ = ['ImageAnimator', 'ImageAnimatorWCS']\n\n\nclass ImageAnimator(ArrayAnimator):\n    \"\"\"\n    Create a matplotlib backend independent data explorer for 2D images.\n\n    The following keyboard shortcuts are defined in the viewer:\n\n    - 'left': previous step on active slider\n    - 'right': next step on active slider\n    - 'top': change the active slider up one\n    - 'bottom': change the active slider down one\n    - 'p': play/pause active slider\n\n    This viewer can have user defined buttons added by specifying the labels\n    and functions called when those buttons are clicked as keyword arguments.\n\n    Parameters\n    ----------\n    data: ndarray\n        The data to be visualized >= 2D\n\n    image_axes: list\n        The two axes that make the image\n\n    fig: mpl.figure\n        Figure to use\n\n    axis_ranges: list of physical coordinates for array or None\n        If None array indices will be used for all axes.\n        If a list it should contain one element for each axis of the numpy array.\n        For the image axes a [min, max] pair should be specified which will be\n        passed to :func:`matplotlib.pyplot.imshow` as extent.\n        For the slider axes a [min, max] pair can be specified or an array the\n        same length as the axis which will provide all values for that slider.\n        If None is specified for an axis then the array indices will be used\n        for that axis.\n\n    interval: int\n        Animation interval in ms\n\n    colorbar: bool\n        Plot colorbar\n\n    button_labels: list\n        List of strings to label buttons\n\n    button_func: list\n        List of functions to map to the buttons\n\n    Extra keywords are passed to imshow.\n\n    \"\"\"\n\n    def __init__(self, data, image_axes=[-2, -1], axis_ranges=None, **kwargs):\n        # Check that number of axes is 2.\n        if len(image_axes) != 2:\n            raise ValueError(\"There can only be two spatial axes\")\n        # Define number of slider axes.\n        self.naxis = data.ndim\n        self.num_sliders = self.naxis-2\n        # Define marker to determine if plot axes values are supplied via array of\n        # pixel values or min max pair. This will determine the type of image produced\n        # and hence how to plot and update it.\n        self._non_regular_plot_axis = False\n        # Run init for parent class\n        super(ImageAnimator, self).__init__(data, image_axes=image_axes,\n                                            axis_ranges=axis_ranges, **kwargs)\n\n    def plot_start_image(self, ax):\n        \"\"\"Sets up plot of initial image.\"\"\"\n        # Create extent arg\n        extent = []\n        # reverse because numpy is in y-x and extent is x-y\n        for i in self.image_axes[::-1]:\n            if self._non_regular_plot_axis is False and len(self.axis_ranges[i]) > 2:\n                self._non_regular_plot_axis = True\n            extent.append(self.axis_ranges[i][0])\n            extent.append(self.axis_ranges[i][-1])\n\n        imshow_args = {'interpolation': 'nearest',\n                       'origin': 'lower',\n                       'extent': extent}\n        imshow_args.update(self.imshow_kwargs)\n\n        # If value along an axis is set with an array, generate a NonUniformImage\n        if self._non_regular_plot_axis:\n            # If user has inverted the axes, transpose the data so the dimensions match.\n            if self.image_axes[0] < self.image_axes[1]:\n                data = self.data[self.frame_index].transpose()\n            else:\n                data = self.data[self.frame_index]\n            # Initialize a NonUniformImage with the relevant data and axis values and\n            # add the image to the axes.\n            im = mpl.image.NonUniformImage(ax, **imshow_args)\n            im.set_data(self.axis_ranges[self.image_axes[0]],\n                        self.axis_ranges[self.image_axes[1]], data)\n            ax.add_image(im)\n            # Define the xlim and ylim bearing in mind that the pixel values along\n            # the axes are plotted in the middle of the pixel.  Therefore make sure\n            # there's half a pixel buffer either side of the ends of the axis ranges.\n            x_ranges = self.axis_ranges[self.image_axes[0]]\n            xlim = (x_ranges[0] - (x_ranges[1] - x_ranges[0]) / 2.,\n                    x_ranges[-1] + (x_ranges[-1] - x_ranges[-2]) / 2.)\n            y_ranges = self.axis_ranges[self.image_axes[1]]\n            ylim = (y_ranges[0] - (y_ranges[1] - y_ranges[0]) / 2.,\n                    y_ranges[-1] + (y_ranges[-1] - y_ranges[-2]) / 2.)\n            ax.set_xlim(xlim)\n            ax.set_ylim(ylim)\n        else:\n            # Else produce a more basic plot with regular axes.\n            im = ax.imshow(self.data[self.frame_index], **imshow_args)\n        if self.if_colorbar:\n            self._add_colorbar(im)\n\n        return im\n\n    def update_plot(self, val, im, slider):\n        \"\"\"Updates plot based on slider/array dimension being iterated.\"\"\"\n        val = int(val)\n        ax_ind = self.slider_axes[slider.slider_ind]\n        ind = int(np.argmin(np.abs(self.axis_ranges[ax_ind] - val)))\n        self.frame_slice[ax_ind] = ind\n        if val != slider.cval:\n            if self._non_regular_plot_axis:\n                if self.image_axes[0] < self.image_axes[1]:\n                    data = self.data[self.frame_index].transpose()\n                else:\n                    data = self.data[self.frame_index]\n                im.set_data(self.axis_ranges[self.image_axes[0]],\n                            self.axis_ranges[self.image_axes[1]], data)\n            else:\n                im.set_array(self.data[self.frame_index])\n            slider.cval = val\n\n\nclass ImageAnimatorWCS(ImageAnimator):\n    \"\"\"\n    Animates N-dimensional data with the associated astropy WCS object.\n\n    The following keyboard shortcuts are defined in the viewer:\n\n    - 'left': previous step on active slider\n    - 'right': next step on active slider\n    - 'top': change the active slider up one\n    - 'bottom': change the active slider down one\n    - 'p': play/pause active slider\n\n    This viewer can have user defined buttons added by specifying the labels\n    and functions called when those buttons are clicked as keyword arguments.\n\n    Parameters\n    ----------\n    data: `numpy.ndarray`\n        The data to be visualized >= 2D\n\n    wcs: `astropy.wcs.WCS`\n        The wcs data.\n\n    image_axes: `list`\n        The two axes that make the image\n\n    fig: `matplotlib.figure.Figure`\n        Figure to use\n\n    axis_ranges: list of physical coordinates for array or None\n        If None array indices will be used for all axes.\n        If a list it should contain one element for each axis of the numpy array.\n        For the image axes a [min, max] pair should be specified which will be\n        passed to :func:`matplotlib.pyplot.imshow` as extent.\n        For the slider axes a [min, max] pair can be specified or an array the\n        same length as the axis which will provide all values for that slider.\n        If None is specified for an axis then the array indices will be used\n        for that axis.\n\n    interval: `int`\n        Animation interval in ms\n\n    colorbar: `bool`\n        Plot colorbar\n\n    button_labels: `list`\n        List of strings to label buttons\n\n    button_func: `list`\n        List of functions to map to the buttons\n\n    unit_x_axis: `astropy.units.Unit`\n        The unit of x axis.\n\n    unit_y_axis: `astropy.units.Unit`\n        The unit of y axis.\n\n    Extra keywords are passed to imshow.\n\n    \"\"\"\n    def __init__(self, data, wcs=None, image_axes=[-1, -2], unit_x_axis=None, unit_y_axis=None,\n                 axis_ranges=None, **kwargs):\n        if not isinstance(wcs, astropy.wcs.WCS):\n            raise ValueError(\"wcs data should be provided.\")\n        if wcs.wcs.naxis is not data.ndim:\n            raise ValueError(\"Dimensions of data and wcs not matching\")\n        self.wcs = wcs\n        list_slices_wcsaxes = [0 for i in range(self.wcs.naxis)]\n        list_slices_wcsaxes[image_axes[0]] = 'x'\n        list_slices_wcsaxes[image_axes[1]] = 'y'\n        self.slices_wcsaxes = list_slices_wcsaxes[::-1]\n        self.unit_x_axis = unit_x_axis\n        self.unit_y_axis = unit_y_axis\n        super(ImageAnimatorWCS, self).__init__(data, image_axes=image_axes,\n                                               axis_ranges=axis_ranges, **kwargs)\n\n    def _get_main_axes(self):\n        axes = self.fig.add_axes([0.1, 0.1, 0.8, 0.8], projection=self.wcs,\n                                 slices=self.slices_wcsaxes)\n        self._set_unit_in_axis(axes)\n        return axes\n\n    def _set_unit_in_axis(self, axes):\n        if self.unit_x_axis is not None:\n            axes.coords[2].set_format_unit(self.unit_x_axis)\n            axes.coords[2].set_ticks(exclude_overlapping=True)\n        if self.unit_y_axis is not None:\n            axes.coords[1].set_format_unit(self.unit_y_axis)\n            axes.coords[1].set_ticks(exclude_overlapping=True)\n\n    def plot_start_image(self, ax):\n        \"\"\"Sets up plot of initial image.\"\"\"\n        imshow_args = {'interpolation': 'nearest',\n                       'origin': 'lower',\n                       }\n        imshow_args.update(self.imshow_kwargs)\n        im = ax.imshow(self.data[self.frame_index], **imshow_args)\n        if self.if_colorbar:\n            self._add_colorbar(im)\n        return im\n\n    def update_plot(self, val, im, slider):\n        \"\"\"Updates plot based on slider/array dimension being iterated.\"\"\"\n        val = int(val)\n        ax_ind = self.slider_axes[slider.slider_ind]\n        ind = int(np.argmin(np.abs(self.axis_ranges[ax_ind] - val)))\n        self.frame_slice[ax_ind] = ind\n        list_slices_wcsaxes = list(self.slices_wcsaxes)\n        list_slices_wcsaxes[self.wcs.naxis-ax_ind-1] = val\n        self.slices_wcsaxes = list_slices_wcsaxes\n        if val != slider.cval:\n            self.axes.reset_wcs(wcs=self.wcs, slices=self.slices_wcsaxes)\n            self._set_unit_in_axis(self.axes)\n            im.set_array(self.data[self.frame_index])\n            slider.cval = val\n", "path": "sunpy/visualization/animator/image.py"}], "after_files": [{"content": "import numpy as np\nimport matplotlib as mpl\nimport astropy.wcs\n\nfrom . base import ArrayAnimator\n\n__all__ = ['ImageAnimator', 'ImageAnimatorWCS']\n\n\nclass ImageAnimator(ArrayAnimator):\n    \"\"\"\n    Create a matplotlib backend independent data explorer for 2D images.\n\n    The following keyboard shortcuts are defined in the viewer:\n\n    - 'left': previous step on active slider\n    - 'right': next step on active slider\n    - 'top': change the active slider up one\n    - 'bottom': change the active slider down one\n    - 'p': play/pause active slider\n\n    This viewer can have user defined buttons added by specifying the labels\n    and functions called when those buttons are clicked as keyword arguments.\n\n    Parameters\n    ----------\n    data: ndarray\n        The data to be visualized >= 2D\n\n    image_axes: list\n        The two axes that make the image\n\n    fig: mpl.figure\n        Figure to use\n\n    axis_ranges: list of physical coordinates for array or None\n        If None array indices will be used for all axes.\n        If a list it should contain one element for each axis of the numpy array.\n        For the image axes a [min, max] pair should be specified which will be\n        passed to :func:`matplotlib.pyplot.imshow` as extent.\n        For the slider axes a [min, max] pair can be specified or an array the\n        same length as the axis which will provide all values for that slider.\n        If None is specified for an axis then the array indices will be used\n        for that axis.\n\n    interval: int\n        Animation interval in ms\n\n    colorbar: bool\n        Plot colorbar\n\n    button_labels: list\n        List of strings to label buttons\n\n    button_func: list\n        List of functions to map to the buttons\n\n    Extra keywords are passed to imshow.\n\n    \"\"\"\n\n    def __init__(self, data, image_axes=[-2, -1], axis_ranges=None, **kwargs):\n        # Check that number of axes is 2.\n        if len(image_axes) != 2:\n            raise ValueError(\"There can only be two spatial axes\")\n        # Define number of slider axes.\n        self.naxis = data.ndim\n        self.num_sliders = self.naxis-2\n        # Define marker to determine if plot axes values are supplied via array of\n        # pixel values or min max pair. This will determine the type of image produced\n        # and hence how to plot and update it.\n        self._non_regular_plot_axis = False\n        # Run init for parent class\n        super(ImageAnimator, self).__init__(data, image_axes=image_axes,\n                                            axis_ranges=axis_ranges, **kwargs)\n\n    def plot_start_image(self, ax):\n        \"\"\"Sets up plot of initial image.\"\"\"\n        # Create extent arg\n        extent = []\n        # reverse because numpy is in y-x and extent is x-y\n        for i in self.image_axes[::-1]:\n            if self._non_regular_plot_axis is False and len(self.axis_ranges[i]) > 2:\n                self._non_regular_plot_axis = True\n            extent.append(self.axis_ranges[i][0])\n            extent.append(self.axis_ranges[i][-1])\n\n        imshow_args = {'interpolation': 'nearest',\n                       'origin': 'lower',\n                       'extent': extent}\n        imshow_args.update(self.imshow_kwargs)\n\n        # If value along an axis is set with an array, generate a NonUniformImage\n        if self._non_regular_plot_axis:\n            # If user has inverted the axes, transpose the data so the dimensions match.\n            if self.image_axes[0] < self.image_axes[1]:\n                data = self.data[self.frame_index].transpose()\n            else:\n                data = self.data[self.frame_index]\n            # Initialize a NonUniformImage with the relevant data and axis values and\n            # add the image to the axes.\n            im = mpl.image.NonUniformImage(ax, **imshow_args)\n            im.set_data(self.axis_ranges[self.image_axes[0]],\n                        self.axis_ranges[self.image_axes[1]], data)\n            ax.add_image(im)\n            # Define the xlim and ylim bearing in mind that the pixel values along\n            # the axes are plotted in the middle of the pixel.  Therefore make sure\n            # there's half a pixel buffer either side of the ends of the axis ranges.\n            x_ranges = self.axis_ranges[self.image_axes[0]]\n            xlim = (x_ranges[0] - (x_ranges[1] - x_ranges[0]) / 2.,\n                    x_ranges[-1] + (x_ranges[-1] - x_ranges[-2]) / 2.)\n            y_ranges = self.axis_ranges[self.image_axes[1]]\n            ylim = (y_ranges[0] - (y_ranges[1] - y_ranges[0]) / 2.,\n                    y_ranges[-1] + (y_ranges[-1] - y_ranges[-2]) / 2.)\n            ax.set_xlim(xlim)\n            ax.set_ylim(ylim)\n        else:\n            # Else produce a more basic plot with regular axes.\n            im = ax.imshow(self.data[self.frame_index], **imshow_args)\n        if self.if_colorbar:\n            self._add_colorbar(im)\n\n        return im\n\n    def update_plot(self, val, im, slider):\n        \"\"\"Updates plot based on slider/array dimension being iterated.\"\"\"\n        val = int(val)\n        ax_ind = self.slider_axes[slider.slider_ind]\n        ind = int(np.argmin(np.abs(self.axis_ranges[ax_ind] - val)))\n        self.frame_slice[ax_ind] = ind\n        if val != slider.cval:\n            if self._non_regular_plot_axis:\n                if self.image_axes[0] < self.image_axes[1]:\n                    data = self.data[self.frame_index].transpose()\n                else:\n                    data = self.data[self.frame_index]\n                im.set_data(self.axis_ranges[self.image_axes[0]],\n                            self.axis_ranges[self.image_axes[1]], data)\n            else:\n                im.set_array(self.data[self.frame_index])\n            slider.cval = val\n\n\nclass ImageAnimatorWCS(ImageAnimator):\n    \"\"\"\n    Animates N-dimensional data with the associated astropy WCS object.\n\n    The following keyboard shortcuts are defined in the viewer:\n\n    - 'left': previous step on active slider\n    - 'right': next step on active slider\n    - 'top': change the active slider up one\n    - 'bottom': change the active slider down one\n    - 'p': play/pause active slider\n\n    This viewer can have user defined buttons added by specifying the labels\n    and functions called when those buttons are clicked as keyword arguments.\n\n    Parameters\n    ----------\n    data: `numpy.ndarray`\n        The data to be visualized >= 2D\n\n    wcs: `astropy.wcs.WCS`\n        The wcs data.\n\n    image_axes: `list`\n        The two axes that make the image\n\n    fig: `matplotlib.figure.Figure`\n        Figure to use\n\n    axis_ranges: list of physical coordinates for array or None\n        If None array indices will be used for all axes.\n        If a list it should contain one element for each axis of the numpy array.\n        For the image axes a [min, max] pair should be specified which will be\n        passed to :func:`matplotlib.pyplot.imshow` as extent.\n        For the slider axes a [min, max] pair can be specified or an array the\n        same length as the axis which will provide all values for that slider.\n        If None is specified for an axis then the array indices will be used\n        for that axis.\n\n    interval: `int`\n        Animation interval in ms\n\n    colorbar: `bool`\n        Plot colorbar\n\n    button_labels: `list`\n        List of strings to label buttons\n\n    button_func: `list`\n        List of functions to map to the buttons\n\n    unit_x_axis: `astropy.units.Unit`\n        The unit of x axis.\n\n    unit_y_axis: `astropy.units.Unit`\n        The unit of y axis.\n\n    Extra keywords are passed to imshow.\n\n    \"\"\"\n    def __init__(self, data, wcs=None, image_axes=[-1, -2], unit_x_axis=None, unit_y_axis=None,\n                 axis_ranges=None, **kwargs):\n        if not isinstance(wcs, astropy.wcs.WCS):\n            raise ValueError(\"wcs data should be provided.\")\n        if wcs.wcs.naxis is not data.ndim:\n            raise ValueError(\"Dimensions of data and wcs not matching\")\n        self.wcs = wcs\n        list_slices_wcsaxes = [0 for i in range(self.wcs.naxis)]\n        list_slices_wcsaxes[image_axes[0]] = 'x'\n        list_slices_wcsaxes[image_axes[1]] = 'y'\n        self.slices_wcsaxes = list_slices_wcsaxes[::-1]\n        self.unit_x_axis = unit_x_axis\n        self.unit_y_axis = unit_y_axis\n        super(ImageAnimatorWCS, self).__init__(data, image_axes=image_axes,\n                                               axis_ranges=axis_ranges, **kwargs)\n\n    def _get_main_axes(self):\n        axes = self.fig.add_axes([0.1, 0.1, 0.8, 0.8], projection=self.wcs,\n                                 slices=self.slices_wcsaxes)\n        self._set_unit_in_axis(axes)\n        return axes\n\n    def _set_unit_in_axis(self, axes):\n        x_index = self.slices_wcsaxes.index(\"x\")\n        y_index = self.slices_wcsaxes.index(\"y\")\n        if self.unit_x_axis is not None:\n            axes.coords[x_index].set_format_unit(self.unit_x_axis)\n            axes.coords[x_index].set_ticks(exclude_overlapping=True)\n        if self.unit_y_axis is not None:\n            axes.coords[y_index].set_format_unit(self.unit_y_axis)\n            axes.coords[y_index].set_ticks(exclude_overlapping=True)\n\n    def plot_start_image(self, ax):\n        \"\"\"Sets up plot of initial image.\"\"\"\n        imshow_args = {'interpolation': 'nearest',\n                       'origin': 'lower',\n                       }\n        imshow_args.update(self.imshow_kwargs)\n        im = ax.imshow(self.data[self.frame_index], **imshow_args)\n        if self.if_colorbar:\n            self._add_colorbar(im)\n        return im\n\n    def update_plot(self, val, im, slider):\n        \"\"\"Updates plot based on slider/array dimension being iterated.\"\"\"\n        val = int(val)\n        ax_ind = self.slider_axes[slider.slider_ind]\n        ind = int(np.argmin(np.abs(self.axis_ranges[ax_ind] - val)))\n        self.frame_slice[ax_ind] = ind\n        list_slices_wcsaxes = list(self.slices_wcsaxes)\n        list_slices_wcsaxes[self.wcs.naxis-ax_ind-1] = val\n        self.slices_wcsaxes = list_slices_wcsaxes\n        if val != slider.cval:\n            self.axes.reset_wcs(wcs=self.wcs, slices=self.slices_wcsaxes)\n            self._set_unit_in_axis(self.axes)\n            im.set_array(self.data[self.frame_index])\n            slider.cval = val\n", "path": "sunpy/visualization/animator/image.py"}]}