Repository: spacetelescope/jwql

Issue:
Two sets of ticklabels on colorbars
Some preview images contain a colorbar where there are two sets of tick labels. This happens in cases where the max-min values of the labels are less than a factor of 10 - according to matplotlib developers. In this case, we need to turn off the tick labels that are automatically generated by matplotlib. 

I tried filing an issue, but the answer didn't seem to work: https://github.com/matplotlib/matplotlib/issues/22425

After playing with this some more today, I think I have a hacky version that works. Note that running this solution gives:
1) a warning that "AutoMinorLocator does not work with logarithmic scale" (but the plot is still made and saved correctly in my toy example)
2) You can use any number you want in the call to AutoMinorLocator and the result is the same.

Here is the updated code that seems to work:

```
import numpy as np
import matplotlib.colors as colors
mport matplotlib.pyplot as plt
from matplotlib.ticker import AutoMinorLocator

# Create image
image = np.random.random((256, 2048))

# Set the figure size
fig, ax = plt.subplots(figsize=(8, 6))

shiftmin = 1
shiftmax = 2

cax = ax.imshow(image, norm=colors.LogNorm(vmin=shiftmin,vmax=shiftmax), cmap='viridis')
plt.gca().invert_yaxis()

tickvals = np.logspace(np.log10(shiftmin), np.log10(shiftmax), 5)

#test adding a point that's out of range - not helpful
#tickvals = np.append(tickvals, 25)

format_string = "%.3f"
tlabelstr = [format_string % number for number in tickvals]

cbar = fig.colorbar(cax, ticks=tickvals, format=format_string, location='right', fraction=0.2, extend='both', label='something')

# This seems to correctly removes the points we want to remove, even though it gives a warning that
# it doesn't work on log scales.
cbar.ax.yaxis.set_minor_locator(AutoMinorLocator(n=0))

# This is where I'd like to run the fix given in the matplotlib issue above, but this gives an error because the number
# of entries in set_ticks (0) is not the same as the number of ticks (5).
#cbar.set_ticks([], which='minor')

cbar.set_ticklabels(tlabelstr)

# This is the fix suggested in the matplotlib issue above. But all this does is remove the points we
# want to keep while keeping those we want to remove. Using "major" rather than "minor" gives the
# same result
#cbar.set_ticks([], which='minor')

ax.set_xlabel('Pixels')
ax.set_ylabel('Pixels')
plt.savefig('tmp.png')
```

So it seems like all we need to do here is ignore the cbar.set_ticks([], which='minor') lines, and add the line containing the call to AutoMinorLocator().


Discussion:
@BradleySappington I can create a new branch and make this fix, unless you already have (or will have) an active branch with preview image/thumbnail updates from the last round of testing.
Go for it, I dont have any active development in progress.

if you know a warning will occur, you can consider supressing it for that location (just make sure you provide adequate comments)
```
import warnings

with warnings.catch_warnings():
    warnings.simplefilter("ignore")
    fxn_that_has_expected_warnings()
```

Resolving patch:
diff --git a/jwql/utils/preview_image.py b/jwql/utils/preview_image.py
--- a/jwql/utils/preview_image.py
+++ b/jwql/utils/preview_image.py
@@ -37,6 +37,7 @@
 import logging
 import os
 import socket
+import warnings
 
 from astropy.io import fits
 import numpy as np
@@ -48,6 +49,7 @@
 matplotlib.use('Agg')
 import matplotlib.pyplot as plt
 import matplotlib.colors as colors
+from matplotlib.ticker import AutoMinorLocator
 
 # Only import jwst if not running from readthedocs
 if 'build' and 'project' not in socket.gethostname():
@@ -205,6 +207,10 @@ def get_data(self, filename, ext):
                         data = hdulist[ext].data[:, [0, -1], :, :].astype(np.float)
                     else:
                         data = hdulist[ext].data.astype(np.float)
+                    try:
+                        self.units = f"{hdulist[ext].header['BUNIT']}  "
+                    except KeyError:
+                        self.units = ''
                 else:
                     raise ValueError('WARNING: no {} extension in {}!'.format(ext, filename))
 
@@ -330,8 +336,15 @@ def make_figure(self, image, integration_number, min_value, max_value,
                 format_string = "%.{}f".format(dig)
                 tlabelstr = [format_string % number for number in tlabelflt]
                 cbar = fig.colorbar(cax, ticks=tickvals)
+
+                # This seems to correctly remove the ticks and labels we want to remove. It gives a warning that
+                # it doesn't work on log scales, which we don't care about. So let's ignore that warning.
+                warnings.filterwarnings("ignore", message="AutoMinorLocator does not work with logarithmic scale")
+                cbar.ax.yaxis.set_minor_locator(AutoMinorLocator(n=0))
+
                 cbar.ax.set_yticklabels(tlabelstr)
                 cbar.ax.tick_params(labelsize=maxsize * 5. / 4)
+                cbar.ax.set_ylabel(self.units, labelpad=10, rotation=270)
                 ax.set_xlabel('Pixels', fontsize=maxsize * 5. / 4)
                 ax.set_ylabel('Pixels', fontsize=maxsize * 5. / 4)
                 ax.tick_params(labelsize=maxsize)
@@ -355,7 +368,7 @@ def make_figure(self, image, integration_number, min_value, max_value,
             filename = os.path.split(self.file)[-1]
             ax.set_title(filename + ' Int: {}'.format(np.int(integration_number)))
 
-    def make_image(self, max_img_size=8):
+    def make_image(self, max_img_size=8.0):
         """The main function of the ``PreviewImage`` class."""
 
         shape = self.data.shape


Test patch: