I'm using astropy, matplotlib, skyfield and astroquery to create sky charts with Python. With skyfield I do the stereographic projection of the sky. With astropy I use WCS, something like this:
fig = plt.figure(figsize=[600/96, 500/96])
plt.subplots_adjust(left=0.1, right=0.75, top=0.9, bottom=0.1)
wcs = WCS(naxis=2)
wcs.wcs.crpix = [1, 1]
wcs.wcs.cdelt = np.array([-360 / np.pi, 360 / np.pi])
wcs.wcs.crval = [COORD.ra.deg, COORD.dec.deg]
wcs.wcs.ctype = ["RA---STG", "DEC--STG"]
ax = fig.add_subplot(111, projection=wcs)
angle = np.pi - FOV / 360.0 * np.pi
limit = np.sin(angle) / (1.0 - np.cos(angle))
ax.set_xlim(-limit, limit)
ax.set_ylim(-limit, limit)
ax.set_aspect('equal')
ax.coords.grid(True, color='white', linestyle='dotted')
ax.coords[0].set_axislabel(' ')
ax.coords[1].set_axislabel(' ')
I wish to add the plus sign (“+”) for the positive values in the Declination axis (Y). I tried a lot of solutions. For example, the method set_major_formatter
, but it seems very limited, because it only allows a small usecase. With this line of code I can configure the decimal numbers, for example, but nothing else:
ax.coords[1].set_major_formatter('d')
It’s crazy because it’s a very simple action in terms of programming, however I’m not able to achieve this through the built-in functions that astropy and matplotlib have.
Of course unsigned values are positive per se, however I'm preparing sky charts for all public usage, so adding the "+" sign would be helpful for the people.
I also tried to iterate the values. Then I would create a simple bucle with a conditional for the positive numbers. However, this iteration seems impossible.
I also tried matplotlib.ticker
, and this is not working at all because [I think] it's about WCS from astropy.
You can't do it in an official way, but here are two hacky workarounds. Both, however, depend on implementation details and may not work on future versions of astropy
. Also you'll have to switch off the removal of repeating tick label parts ("tick label simplification") which can't be done in a documented way (so we just replace this function with a noop).
Variant 1: subclass AngleFormatterLocator
As you've noticed there's no working way to set your own formatter, so we'll have to directly assign it to the internal _formatter_locator
variable:
from astropy.wcs import WCS
from astropy.visualization.wcsaxes.formatter_locator import AngleFormatterLocator
from matplotlib.ticker import Formatter
class AngleFormatterLocatorPlus(AngleFormatterLocator):
def formatter(self, values, spacing, format="auto"):
ticklabels = super().formatter(values, spacing, format)
minus = Formatter.fix_minus('-')
return [tl if tl.startswith(minus) else '+' + tl for tl in ticklabels]
wcs = WCS(naxis=2)
wcs.wcs.crpix = [1, 1]
wcs.wcs.cdelt = np.array([-360 / np.pi, 360 / np.pi])
wcs.wcs.ctype = ["RA---STG", "DEC--STG"]
fig, ax = plt.subplots(subplot_kw=dict(projection=wcs))
ax.coords[1]._formatter_locator = AngleFormatterLocatorPlus()
ax.coords[1].ticklabels.simplify_labels = lambda: None
ax.set_xlim(-.3, .3)
ax.set_ylim(-.1, .3)
Variant 2: globally supply the alwayssign
argument to Angle.to_string
which affects both axes (which might or might not be desired):
import functools
import matplotlib.pyplot as plt
import numpy as np
from astropy.wcs import WCS
from astropy.visualization.wcsaxes.formatter_locator import Angle
Angle.to_string = functools.partialmethod(Angle.to_string, alwayssign=True)
wcs = WCS(naxis=2)
wcs.wcs.crpix = [1, 1]
wcs.wcs.cdelt = np.array([-360 / np.pi, 360 / np.pi])
wcs.wcs.ctype = ["RA---STG", "DEC--STG"]
fig, ax = plt.subplots(subplot_kw=dict(projection=wcs))
ax.coords[0].ticklabels.simplify_labels = lambda: None
ax.coords[1].ticklabels.simplify_labels = lambda: None
ax.set_xlim(-.3, .3)
ax.set_ylim(-.1, .3)