Source code for FITS_tools.header_tools
from astropy import wcs
import numpy as np
from astropy import units as u
from astropy import coordinates
from astropy.wcs import WCSSUB_CELESTIAL
from astropy import log
from .load_header import get_cd
from .hcongrid import _ctype_to_csys
__all__ = ["enclosing_header", "header_to_platescale", "smoothing_kernel_size",
"wcs_to_platescale",]
[docs]def header_to_platescale(header, **kwargs):
"""
Attempt to determine the spatial platescale from a
`~astropy.io.fits.Header`
Parameters
----------
header : `~astropy.io.fits.Header`
The FITS header to extract the platescale from
kwargs : dict
Passed to `wcs_to_platescale`. See that function for more details
Returns
-------
platescale : float or `~astropy.units.Quantity`
The platescale in degrees with attached units if `use_units` is True
"""
w = wcs.WCS(header)
return wcs_to_platescale(w, **kwargs)
[docs]def wcs_to_platescale(mywcs, assert_square=True, use_units=False):
"""
Attempt to determine the spatial plate scale from a `~astropy.wcs.WCS`
Parameters
----------
mywcs : :class:`~astropy.wcs.WCS`
The WCS instance to examine
assert_square : bool
Fail if the pixels are non-square
use_units : bool
Return a `~astropy.units.Quantity` if True
Returns
-------
platescale : float or `~astropy.units.Quantity`
The platescale in degrees with attached units if `use_units` is True
"""
# Code adapted from APLpy
mywcs = mywcs.sub([WCSSUB_CELESTIAL])
cdelt = np.matrix(mywcs.wcs.get_cdelt())
pc = np.matrix(mywcs.wcs.get_pc())
scale = np.array(cdelt * pc)
if assert_square:
try:
np.testing.assert_almost_equal(abs(cdelt[0,0]), abs(cdelt[0,1]))
np.testing.assert_almost_equal(abs(pc[0,0]), abs(pc[1,1]))
np.testing.assert_almost_equal(abs(scale[0,0]), abs(scale[0,1]))
except AssertionError:
raise ValueError("Non-square pixels. Please resample data.")
if use_units:
return abs(scale[0,0]) * u.Unit(mywcs.wcs.cunit[0])
else:
return abs(scale[0,0])
[docs]def smoothing_kernel_size(hdr_from, hdr_to):
"""
Determine the smoothing kernel size needed to convolve header_from to
header_to without losing signal. Operates only in the spatial dimensions
"""
if not isinstance(hdr_from, wcs.WCS):
w_from = wcs.WCS(hdr_from).sub(wcs.WCSSUB_CELESTIAL)
if not isinstance(hdr_to, wcs.WCS):
w_to = wcs.WCS(hdr_to).sub(wcs.WCSSUB_CELESTIAL)
widths = []
for ii in (1,2):
cd_from = get_cd(w_from,ii)
cd_to = get_cd(w_to,ii)
if np.abs(cd_to) < np.abs(cd_from):
# upsampling: no convolution
widths[ii-1] = 1e-8
else:
# downsampling: smooth with fwhm = pixel size ratio
widths[ii] = np.abs(cd_to/cd_from) / np.sqrt(8*np.log(2))
return widths
[docs]def enclosing_header(header1, header2, wrapangle=180*u.deg):
"""
Find the smallest header that encloses both header1 and header2 in the
frame of header2
"""
log.warning("'Enclosing Header' does not work exactly - there is at least "
"a few-pixel offset in the result. Need a good test suite.")
w1 = wcs.WCS(header1).sub([WCSSUB_CELESTIAL])
w2 = wcs.WCS(header2).sub([WCSSUB_CELESTIAL])
pedges1 = np.array([(1, header1['NAXIS2']),
(header1['NAXIS1'], header1['NAXIS2']),
(header1['NAXIS1'], 1),
(1, 1),])
pedges2 = np.array([(1,header2['NAXIS2']),
(header2['NAXIS1'], header2['NAXIS2']),
(header2['NAXIS1'], 1),
(1, 1),])
edges1 = w1.wcs_pix2world(pedges1, 1)
edges2 = w2.wcs_pix2world(pedges2, 1)
cedges2 = coordinates.SkyCoord(edges2*u.deg,
frame=_ctype_to_csys(w1.wcs))
cedges1 = coordinates.SkyCoord(edges1*u.deg,
frame=_ctype_to_csys(w2.wcs)).transform_to(cedges2.frame)
#(a,b), = w2.wcs_pix2world([[1,1]],1)
#lbref = coordinates.SkyCoord(a*u.deg, b*u.deg,
# frame=_ctype_to_csys(w2.wcs))
wa = wrapangle
if hasattr(cedges2, 'l'):
llow = min(cedges1.l.wrap_at(wa).min(), cedges2.l.wrap_at(wa).min())
lhi = max(cedges1.l.wrap_at(wa).max(), cedges2.l.wrap_at(wa).max())
blow = min(cedges1.b.wrap_at(wa).min(), cedges2.b.wrap_at(wa).min())
bhi = max(cedges1.b.wrap_at(wa).max(), cedges2.b.wrap_at(wa).max())
#lref, bref = lbref.l, lbref.b
elif hasattr(cedges2, 'ra'):
llow = min(cedges1.ra.min(), cedges2.ra.min())
lhi = max(cedges1.ra.max(), cedges2.ra.max())
blow = min(cedges1.dec.min(), cedges2.dec.min())
bhi = max(cedges1.dec.max(), cedges2.dec.max())
#lref, bref = lbref.ra, lbref.dec
else:
raise ValueError("Invalid coordinates.")
(xlow,ylow), = w2.wcs_world2pix([[llow.deg,blow.deg]], 1)
(xhi ,yhi ), = w2.wcs_world2pix([[lhi.deg ,bhi.deg ]], 1)
(xref, yref), = w2.wcs_world2pix([[(max(llow,lhi)).deg,
(min(blow,bhi)).deg]], 0)
header = header2.copy()
header['NAXIS1'] = int(np.ceil(np.abs(xhi-xlow)))
header['NAXIS2'] = int(np.ceil(np.abs(yhi-ylow)))
header['CRPIX1'] = header2['CRPIX1'] - xref #min(xlow, xhi)
header['CRPIX2'] = header2['CRPIX2'] - yref #min(ylow, yhi)
return header