summaryrefslogtreecommitdiff
path: root/lib/stwcs
diff options
context:
space:
mode:
Diffstat (limited to 'lib/stwcs')
-rw-r--r--lib/stwcs/__init__.py33
-rw-r--r--lib/stwcs/distortion/__init__.py0
-rw-r--r--lib/stwcs/distortion/coeff_converter.py142
-rw-r--r--lib/stwcs/distortion/models.py362
-rw-r--r--lib/stwcs/distortion/mutil.py703
-rw-r--r--lib/stwcs/distortion/utils.py270
-rw-r--r--lib/stwcs/gui/__init__.py19
-rw-r--r--lib/stwcs/gui/apply_headerlet.help38
-rw-r--r--lib/stwcs/gui/apply_headerlet.py54
-rw-r--r--lib/stwcs/gui/archive_headerlet.py69
-rw-r--r--lib/stwcs/gui/attach_headerlet.py36
-rw-r--r--lib/stwcs/gui/delete_headerlet.py52
-rw-r--r--lib/stwcs/gui/extract_headerlet.py58
-rw-r--r--lib/stwcs/gui/headerlet_summary.py47
-rw-r--r--lib/stwcs/gui/pars/apply_headerlet.cfg10
-rw-r--r--lib/stwcs/gui/pars/apply_headerlet.cfgspc12
-rw-r--r--lib/stwcs/gui/pars/archive_headerlet.cfg14
-rw-r--r--lib/stwcs/gui/pars/archive_headerlet.cfgspc14
-rw-r--r--lib/stwcs/gui/pars/attach_headerlet.cfg4
-rw-r--r--lib/stwcs/gui/pars/attach_headerlet.cfgspc4
-rw-r--r--lib/stwcs/gui/pars/delete_headerlet.cfg6
-rw-r--r--lib/stwcs/gui/pars/delete_headerlet.cfgspc6
-rw-r--r--lib/stwcs/gui/pars/extract_headerlet.cfg7
-rw-r--r--lib/stwcs/gui/pars/extract_headerlet.cfgspc7
-rw-r--r--lib/stwcs/gui/pars/headerlet_summary.cfg8
-rw-r--r--lib/stwcs/gui/pars/headerlet_summary.cfgspc8
-rw-r--r--lib/stwcs/gui/pars/restore_headerlet.cfg10
-rw-r--r--lib/stwcs/gui/pars/restore_headerlet.cfgspc12
-rw-r--r--lib/stwcs/gui/pars/updatewcs.cfg8
-rw-r--r--lib/stwcs/gui/pars/updatewcs.cfgspc8
-rw-r--r--lib/stwcs/gui/pars/write_headerlet.cfg18
-rw-r--r--lib/stwcs/gui/pars/write_headerlet.cfgspc18
-rw-r--r--lib/stwcs/gui/restore_headerlet.help43
-rw-r--r--lib/stwcs/gui/restore_headerlet.py48
-rw-r--r--lib/stwcs/gui/updatewcs.py90
-rw-r--r--lib/stwcs/gui/write_headerlet.py80
-rw-r--r--lib/stwcs/updatewcs/__init__.py376
-rw-r--r--lib/stwcs/updatewcs/apply_corrections.py248
-rw-r--r--lib/stwcs/updatewcs/corrections.py326
-rw-r--r--lib/stwcs/updatewcs/det2im.py299
-rw-r--r--lib/stwcs/updatewcs/makewcs.py273
-rw-r--r--lib/stwcs/updatewcs/npol.py343
-rw-r--r--lib/stwcs/updatewcs/utils.py264
-rw-r--r--lib/stwcs/updatewcs/wfpc2_dgeo.py123
-rw-r--r--lib/stwcs/wcsutil/__init__.py34
-rw-r--r--lib/stwcs/wcsutil/altwcs.py758
-rw-r--r--lib/stwcs/wcsutil/convertwcs.py118
-rw-r--r--lib/stwcs/wcsutil/getinput.py62
-rw-r--r--lib/stwcs/wcsutil/headerlet.py2754
-rw-r--r--lib/stwcs/wcsutil/hstwcs.py988
-rw-r--r--lib/stwcs/wcsutil/instruments.py320
-rw-r--r--lib/stwcs/wcsutil/mappings.py29
-rw-r--r--lib/stwcs/wcsutil/mosaic.py183
-rw-r--r--lib/stwcs/wcsutil/wcscorr.py668
-rw-r--r--lib/stwcs/wcsutil/wcsdiff.py150
55 files changed, 0 insertions, 10634 deletions
diff --git a/lib/stwcs/__init__.py b/lib/stwcs/__init__.py
deleted file mode 100644
index 9f12bce..0000000
--- a/lib/stwcs/__init__.py
+++ /dev/null
@@ -1,33 +0,0 @@
-""" STWCS
-
-This package provides support for WCS based distortion models and coordinate
-transformation. It relies on PyWCS (based on WCSLIB). It consists of two
-subpackages: updatewcs and wcsutil.
-
-updatewcs performs corrections to the
-basic WCS and includes other distortion infomation in the science files as
-header keywords or file extensions.
-
-Wcsutil provides an HSTWCS object which extends pywcs.WCS object and provides
-HST instrument specific information as well as methods for coordinate
-transformation. wcsutil also provides functions for manipulating alternate WCS
-descriptions in the headers.
-
-"""
-from __future__ import absolute_import, print_function # confidence high
-import os
-
-from . import distortion
-from stsci.tools import fileutil
-from stsci.tools import teal
-
-__docformat__ = 'restructuredtext'
-
-from .version import *
-
-try:
- from . import gui
- teal.print_tasknames(gui.__name__, os.path.dirname(gui.__file__))
- print('\n')
-except:
- print('No TEAL-based tasks available for this package!')
diff --git a/lib/stwcs/distortion/__init__.py b/lib/stwcs/distortion/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/lib/stwcs/distortion/__init__.py
+++ /dev/null
diff --git a/lib/stwcs/distortion/coeff_converter.py b/lib/stwcs/distortion/coeff_converter.py
deleted file mode 100644
index 415b512..0000000
--- a/lib/stwcs/distortion/coeff_converter.py
+++ /dev/null
@@ -1,142 +0,0 @@
-from __future__ import division, print_function # confidence high
-
-import numpy as np
-from astropy.io import fits
-from astropy import wcs as pywcs
-
-def sip2idc(wcs):
- """
- Converts SIP style coefficients to IDCTAB coefficients.
-
- Parameters
- ----------
- wcs : `astropy.io.fits.Header` or `astropy.wcs.WCS` object
- """
- if isinstance(wcs, fits.Header):
- ocx10 = wcs.get('OCX10', None)
- ocx11 = wcs.get('OCX11', None)
- ocy10 = wcs.get('OCY10', None)
- ocy11 = wcs.get('OCY11', None)
- order = wcs.get('A_ORDER', None)
- sipa, sipb = _read_sip_kw(wcs)
- if None in [ocx10, ocx11, ocy10, ocy11, sipa, sipb]:
- print('Cannot convert SIP to IDC coefficients.\n')
- return None, None
- elif isinstance(wcs, pywcs.WCS):
- try:
- ocx10 = wcs.ocx10
- ocx11 = wcs.ocx11
- ocy10 = wcs.ocy10
- ocy11 = wcs.ocy11
- except AttributeError:
- print('First order IDCTAB coefficients are not available.\n')
- print('Cannot convert SIP to IDC coefficients.\n')
- return None, None
- try:
- sipa = wcs.sip.a
- sipb = wcs.sip.b
- except AttributeError:
- print('SIP coefficients are not available.')
- print('Cannot convert SIP to IDC coefficients.\n')
- return None, None
- try:
- order = wcs.sip.a_order
- except AttributeError:
- print('SIP model order unknown, exiting ...\n')
- return None, None
-
- else:
- print('Input to sip2idc must be a PyFITS header or a wcsutil.HSTWCS object\n')
- return
-
-
- if None in [ocx10, ocx11, ocy10, ocy11]:
- print('First order IDC coefficients not found, exiting ...\n')
- return None, None
- idc_coeff = np.array([[ocx11, ocx10], [ocy11, ocy10]])
- cx = np.zeros((order+1,order+1), dtype=np.double)
- cy = np.zeros((order+1,order+1), dtype=np.double)
- for n in range(order+1):
- for m in range(order+1):
- if n >= m and n>=2:
- sipval = np.array([[sipa[m,n-m]],[sipb[m,n-m]]])
- idcval = np.dot(idc_coeff, sipval)
- cx[n,m] = idcval[0]
- cy[n,m] = idcval[1]
-
- cx[1,0] = ocx10
- cx[1,1] = ocx11
- cy[1,0] = ocy10
- cy[1,1] = ocy11
-
- return cx, cy
-
-def _read_sip_kw(header):
- """
- Reads SIP header keywords and returns an array of coefficients.
-
- If no SIP header keywords are found, None is returned.
- """
- if "A_ORDER" in header:
- if "B_ORDER" not in header:
- raise ValueError(
- "A_ORDER provided without corresponding B_ORDER "
- "keyword for SIP distortion")
-
- m = int(header["A_ORDER"])
- a = np.zeros((m+1, m+1), np.double)
- for i in range(m+1):
- for j in range(m-i+1):
- a[i, j] = header.get("A_%d_%d" % (i, j), 0.0)
-
- m = int(header["B_ORDER"])
- b = np.zeros((m+1, m+1), np.double)
- for i in range(m+1):
- for j in range(m-i+1):
- b[i, j] = header.get("B_%d_%d" % (i, j), 0.0)
- elif "B_ORDER" in header:
- raise ValueError(
- "B_ORDER provided without corresponding A_ORDER "
- "keyword for SIP distortion")
- else:
- a = None
- b = None
-
- return a , b
-
-
-"""
-def idc2sip(wcsobj, idctab = None):
- if isinstance(wcs,pywcs.WCS):
- try:
- cx10 = wcsobj.ocx10
- cx11 = wcsobj.cx11
- cy10 = wcsobj.cy10
- cy11 = wcsobj.cy11
- except AttributeError:
- print
- try:
- order = wcs.sip.a_order
- except AttributeError:
- print 'SIP model order unknown, exiting ...\n'
- return
- else:
- print 'Input to sip2idc must be a PyFITS header or a wcsutil.HSTWCS object\n'
- return
-
- if None in [ocx10, ocx11, ocy10, ocy11]:
- print 'First order IDC coefficients not found, exiting ...\n'
- return
- idc_coeff = np.array([[wcsobj.cx11, wcsobj.cx10], [wcsobj.cy11, wcsobj.cy10]])
- cx = numpy.zeros((order+1,order+1), dtype=numpy.double)
- cy = numpy.zeros((order+1,order+1), dtype=numpy.double)
- for n in range(order+1):
- for m in range(order+1):
- if n >= m and n>=2:
- sipval = numpy.array([[wcsobj.sip.a[n,m]],[wcsobj.sip.b[n,m]]])
- idcval = numpy.dot(idc_coeff, sipval)
- cx[m,n-m] = idcval[0]
- cy[m,n-m] = idcval[1]
-
- return cx, cy
-"""
diff --git a/lib/stwcs/distortion/models.py b/lib/stwcs/distortion/models.py
deleted file mode 100644
index 231a9f1..0000000
--- a/lib/stwcs/distortion/models.py
+++ /dev/null
@@ -1,362 +0,0 @@
-from __future__ import absolute_import, division, print_function # confidence high
-
-
-import numpy as np
-
-# Import PyDrizzle utility modules
-from . import mutil
-from .mutil import combin
-
-yes = True
-no = False
-
-#################
-#
-#
-# Geometry/Distortion Classes
-#
-#
-#################
-
-class GeometryModel:
- """
- Base class for Distortion model.
- There will be a separate class for each type of
- model/filetype used with drizzle, i.e., IDCModel and
- DrizzleModel.
-
- Each class will know how to apply the distortion to a
- single point and how to convert coefficients to an input table
- suitable for the drizzle task.
-
- Coefficients will be stored in CX,CY arrays.
- """
- #
- #
- #
- #
- #
- #
- #
- NORDER = 3
-
- def __init__(self):
- " This will open the given file and determine its type and norder."
-
- # Method to read in coefficients from given table and
- # populate the n arrays 'cx' and 'cy'.
- # This will be different for each type of input file,
- # IDCTAB vs. drizzle table.
-
- # Set these up here for all sub-classes to use...
- # But, calculate norder and cx,cy arrays in detector specific classes.
- self.cx = None
- self.cy = None
- self.refpix = None
- self.norder = self.NORDER
- # Keep track of computed zero-point for distortion coeffs
- self.x0 = None
- self.y0 = None
-
- # default values for these attributes
- self.direction = 'forward'
-
- self.pscale = 1.0
-
- def shift(self, xs, ys):
- """
- Shift reference position of coefficients to new center
- where (xs,ys) = old-reference-position - subarray/image center.
- This will support creating coeffs files for drizzle which will
- be applied relative to the center of the image, rather than relative
- to the reference position of the chip.
- """
-
- _cxs = np.zeros(shape=self.cx.shape,dtype=self.cx.dtype)
- _cys = np.zeros(shape=self.cy.shape,dtype=self.cy.dtype)
- _k = self.norder + 1
- # loop over each input coefficient
- for m in range(_k):
- for n in range(_k):
- if m >= n:
- # For this coefficient, shift by xs/ys.
- _ilist = list(range(m, _k))
- # sum from m to k
- for i in _ilist:
- _jlist = list(range(n, i - (m-n)+1))
- # sum from n to i-(m-n)
- for j in _jlist:
- _cxs[m,n] += self.cx[i,j]*combin(j,n)*combin((i-j),(m-n))*pow(xs,(j-n))*pow(ys,((i-j)-(m-n)))
- _cys[m,n] += self.cy[i,j]*combin(j,n)*combin((i-j),(m-n))*pow(xs,(j-n))*pow(ys,((i-j)-(m-n)))
- self.cx = _cxs.copy()
- self.cy = _cys.copy()
-
- def convert(self, tmpname, xref=None,yref=None,delta=yes):
- """
- Open up an ASCII file, output coefficients in drizzle
- format after converting them as necessary.
- First, normalize these coefficients to what drizzle expects
- Normalize the coefficients by the MODEL/output plate scale.
-
- 16-May-2002:
- Revised to work with higher order polynomials by John Blakeslee.
- 27-June-2002:
- Added ability to shift coefficients to new center for support
- of subarrays.
- """
- cx = self.cx/self.pscale
- cy = self.cy/self.pscale
- x0 = self.refpix['XDELTA'] + cx[0,0]
- y0 = self.refpix['YDELTA'] + cy[0,0]
- #xr = self.refpix['XREF']
- #yr = self.refpix['YREF']
- xr = self.refpix['CHIP_XREF']
- yr = self.refpix['CHIP_YREF']
-
-
-
- '''
- if xref != None:
- # Shift coefficients for use with drizzle
- _xs = xref - self.refpix['XREF'] + 1.0
- _ys = yref - self.refpix['YREF'] + 1.0
-
-
- if _xs != 0 or _ys != 0:
- cxs,cys= self.shift(cx, cy, _xs, _ys)
- cx = cxs
- cy = cys
-
- # We only want to apply this shift to coeffs
- # for subarray images.
- if delta == no:
- cxs[0,0] = cxs[0,0] - _xs
- cys[0,0] = cys[0,0] - _ys
-
- # Now, apply only the difference introduced by the distortion..
- # i.e., (undistorted - original) shift.
- x0 += cxs[0,0]
- y0 += cys[0,0]
- '''
- self.x0 = x0 #+ 1.0
- self.y0 = y0 #+ 1.0
-
- # Now, write out the coefficients into an ASCII
- # file in 'drizzle' format.
- lines = []
-
-
- lines.append('# Polynomial distortion coefficients\n')
- lines.append('# Extracted from "%s" \n'%self.name)
- lines.append('refpix %f %f \n'%(xr,yr))
- if self.norder==3:
- lines.append('cubic\n')
- elif self.norder==4:
- lines.append('quartic\n')
- elif self.norder==5:
- lines.append('quintic\n')
- else:
- raise ValueError("Drizzle cannot handle poly distortions of order %d" % self.norder)
-
- str = '%16.8f %16.8g %16.8g %16.8g %16.8g \n'% (x0,cx[1,1],cx[1,0],cx[2,2],cx[2,1])
- lines.append(str)
- str = '%16.8g %16.8g %16.8g %16.8g %16.8g \n'% (cx[2,0],cx[3,3],cx[3,2],cx[3,1],cx[3,0])
- lines.append(str)
- if self.norder>3:
- str = '%16.8g %16.8g %16.8g %16.8g %16.8g \n'% (cx[4,4],cx[4,3],cx[4,2],cx[4,1],cx[4,0])
- lines.append(str)
- if self.norder>4:
- str = '%16.8g %16.8g %16.8g %16.8g %16.8g %16.8g \n'% (cx[5,5],cx[5,4],cx[5,3],cx[5,2],cx[5,1],cx[5,0])
- lines.append(str)
- lines.append("\n")
-
- str = '%16.8f %16.8g %16.8g %16.8g %16.8g \n'% (y0,cy[1,1],cy[1,0],cy[2,2],cy[2,1])
- lines.append(str)
- str = '%16.8g %16.8g %16.8g %16.8g %16.8g \n'% (cy[2,0],cy[3,3],cy[3,2],cy[3,1],cy[3,0])
- lines.append(str)
- if self.norder>3:
- str = '%16.8g %16.8g %16.8g %16.8g %16.8g \n'% (cy[4,4],cy[4,3],cy[4,2],cy[4,1],cy[4,0])
- lines.append(str)
- if self.norder>4:
- str = '%16.8g %16.8g %16.8g %16.8g %16.8g %16.8g \n'% (cy[5,5],cy[5,4],cy[5,3],cy[5,2],cy[5,1],cy[5,0])
- lines.append(str)
-
- output = open(tmpname,'w')
- output.writelines(lines)
- output.close()
-
-
- def apply(self, pixpos,scale=1.0,order=None):
- """
- Apply coefficients to a pixel position or a list of positions.
- This should be the same for all coefficients tables.
- Return the geometrically-adjusted position
- in arcseconds from the reference position as a tuple (x,y).
-
- Compute delta from reference position
- """
-
- """
- scale actually is a ratio of pscale/self.model.pscale
- what is pscale?
- """
- if self.cx == None:
- return pixpos[:,0],pixpos[:,1]
-
- if order is None:
- order = self.norder
-
- # Apply in the same way that 'drizzle' would...
- _cx = self.cx / (self.pscale * scale)
- _cy = self.cy / (self.pscale * scale)
- _convert = no
- _p = pixpos
-
- # Do NOT include any zero-point terms in CX,CY here
- # as they should not be scaled by plate-scale like rest
- # of coeffs... This makes the computations consistent
- # with 'drizzle'. WJH 17-Feb-2004
- _cx[0,0] = 0.
- _cy[0,0] = 0.
-
- if isinstance(_p, list) or isinstance(_p, tuple):
- _p = np.array(_p,dtype=np.float64)
- _convert = yes
-
- dxy = _p - (self.refpix['XREF'],self.refpix['YREF'])
- # Apply coefficients from distortion model here...
- c = _p * 0.
- for i in range(order+1):
- for j in range(i+1):
- c[:,0] = c[:,0] + _cx[i][j] * pow(dxy[:,0],j) * pow(dxy[:,1],(i-j))
- c[:,1] = c[:,1] + _cy[i][j] * pow(dxy[:,0],j) * pow(dxy[:,1],(i-j))
- xc = c[:,0]
- yc = c[:,1]
-
- # Convert results back to same form as original input
- if _convert:
- xc = xc.tolist()
- yc = yc.tolist()
- # If a single tuple was input, return just a single tuple
- if len(xc) == 1:
- xc = xc[0]
- yc = yc[0]
-
- return xc,yc
-
- def setPScaleCoeffs(self,pscale):
- self.cx[1,1] = pscale
- self.cy[1,0] = pscale
-
- self.refpix['PSCALE'] = pscale
- self.pscale = pscale
-
-
-class IDCModel(GeometryModel):
- """
- This class will open the IDCTAB, select proper row based on
- chip/direction and populate cx,cy arrays.
- We also need to read in SCALE, XCOM,YCOM, XREF,YREF as well.
- """
- def __init__(self, idcfile, date=None, chip=1, direction='forward',
- filter1='CLEAR1',filter2='CLEAR2',offtab=None, binned=1):
- GeometryModel.__init__(self)
- #
- # Norder must be derived from the coeffs file itself,
- # then the arrays can be setup. Thus, it needs to be
- # done in the sub-class, not in the base class.
- # Read in table.
- # Populate cx,cy,scale, and other variables here.
- #
- self.name = idcfile
- self.cx,self.cy,self.refpix,self.norder = mutil.readIDCtab(idcfile,
- chip=chip,direction=direction,filter1=filter1,filter2=filter2,
- date=date, offtab=offtab)
-
- if 'empty_model' in self.refpix and self.refpix['empty_model']:
- pass
- else:
- self.refpix['PSCALE'] = self.refpix['PSCALE'] * binned
- self.cx = self.cx * binned
- self.cy = self.cy * binned
- self.refpix['XREF'] = self.refpix['XREF'] / binned
- self.refpix['YREF'] = self.refpix['YREF'] / binned
- self.refpix['XSIZE'] = self.refpix['XSIZE'] / binned
- self.refpix['YSIZE'] = self.refpix['YSIZE'] / binned
-
- self.pscale = self.refpix['PSCALE']
-
-
-class WCSModel(GeometryModel):
- """
- This class sets up a distortion model based on coefficients
- found in the image header.
- """
- def __init__(self,header,rootname):
- GeometryModel.__init__(self)
-
-
- if 'rootname' in header:
- self.name = header['rootname']
- else:
- self.name = rootname
- # Initialize all necessary distortion arrays with
- # default model...
- #self.cx,self.cy,self.refpix,self.order = mutil.defaultModel()
-
- # Read in values from header, and update distortion arrays.
- self.cx,self.cy,self.refpix,self.norder = mutil.readWCSCoeffs(header)
-
- self.pscale = self.refpix['PSCALE']
-
-
-
-class DrizzleModel(GeometryModel):
- """
- This class will read in an ASCII Cubic
- drizzle coeffs file and populate the cx,cy arrays.
- """
-
- def __init__(self, idcfile, scale = None):
- GeometryModel.__init__(self)
- #
- # We now need to read in the file, populate cx,cy, and
- # other variables as necessary.
- #
- self.name = idcfile
- self.cx,self.cy,self.refpix,self.norder = mutil.readCubicTable(idcfile)
-
- # scale is the ratio wcs.pscale/model.pscale.
- # model.pscale for WFPC2 is passed from REFDATA.
- # This is needed for WFPC2 binned data.
-
- if scale != None:
- self.pscale = scale
- else:
- self.pscale = self.refpix['PSCALE']
-
- """
- The above definition looks wrong.
- In one case it's a ratio in the other it's pscale.
-
- """
-
-class TraugerModel(GeometryModel):
- """
- This class will read in the ASCII Trauger coeffs
- file, convert them to SIAF coefficients, then populate
- the cx,cy arrays.
- """
- NORDER = 3
-
- def __init__(self, idcfile,lam):
- GeometryModel.__init__(self)
- self.name = idcfile
- self.cx,self.cy,self.refpix,self.norder = mutil.readTraugerTable(idcfile,lam)
- self.pscale = self.refpix['PSCALE']
- #
- # Read in file here.
- # Populate cx,cy, and other variables.
- #
-
-
diff --git a/lib/stwcs/distortion/mutil.py b/lib/stwcs/distortion/mutil.py
deleted file mode 100644
index ed6a1ea..0000000
--- a/lib/stwcs/distortion/mutil.py
+++ /dev/null
@@ -1,703 +0,0 @@
-from __future__ import division, print_function # confidence high
-
-from stsci.tools import fileutil
-import numpy as np
-import calendar
-
-# Set up IRAF-compatible Boolean values
-yes = True
-no = False
-
-# This function read the IDC table and generates the two matrices with
-# the geometric correction coefficients.
-#
-# INPUT: FITS object of open IDC table
-# OUTPUT: coefficient matrices for Fx and Fy
-#
-#### If 'tabname' == None: This should return a default, undistorted
-#### solution.
-#
-
-def readIDCtab (tabname, chip=1, date=None, direction='forward',
- filter1=None,filter2=None, offtab=None):
-
- """
- Read IDCTAB, and optional OFFTAB if sepcified, and generate
- the two matrices with the geometric correction coefficients.
-
- If tabname == None, then return a default, undistorted solution.
- If offtab is specified, dateobs also needs to be given.
-
- """
-
- # Return a default geometry model if no IDCTAB filename
- # is given. This model will not distort the data in any way.
- if tabname == None:
- print('Warning: No IDCTAB specified! No distortion correction will be applied.')
- return defaultModel()
-
- # Implement default values for filters here to avoid the default
- # being overwritten by values of None passed by user.
- if filter1 == None or filter1.find('CLEAR') == 0 or filter1.strip() == '':
- filter1 = 'CLEAR'
- if filter2 == None or filter2.find('CLEAR') == 0 or filter2.strip() == '':
- filter2 = 'CLEAR'
-
- # Insure that tabname is full filename with fully expanded
- # IRAF variables; i.e. 'jref$mc41442gj_idc.fits' should get
- # expanded to '/data/cdbs7/jref/mc41442gj_idc.fits' before
- # being used here.
- # Open up IDC table now...
- try:
- ftab = fileutil.openImage(tabname)
- except:
- err_str = "------------------------------------------------------------------------ \n"
- err_str += "WARNING: the IDCTAB geometric distortion file specified in the image \n"
- err_str += "header was not found on disk. Please verify that your environment \n"
- err_str += "variable ('jref'/'uref'/'oref'/'nref') has been correctly defined. If \n"
- err_str += "you do not have the IDCTAB file, you may obtain the latest version \n"
- err_str += "of it from the relevant instrument page on the STScI HST website: \n"
- err_str += "http://www.stsci.edu/hst/ For WFPC2, STIS and NICMOS data, the \n"
- err_str += "present run will continue using the old coefficients provided in \n"
- err_str += "the Dither Package (ca. 1995-1998). \n"
- err_str += "------------------------------------------------------------------------ \n"
- raise IOError(err_str)
-
- #First thing we need, is to read in the coefficients from the IDC
- # table and populate the Fx and Fy matrices.
-
- if 'DETECTOR' in ftab['PRIMARY'].header:
- detector = ftab['PRIMARY'].header['DETECTOR']
- else:
- if 'CAMERA' in ftab['PRIMARY'].header:
- detector = str(ftab['PRIMARY'].header['CAMERA'])
- else:
- detector = 1
- # First, read in TDD coeffs if present
- phdr = ftab['PRIMARY'].header
- instrument = phdr['INSTRUME']
- if instrument == 'ACS' and detector == 'WFC':
- skew_coeffs = read_tdd_coeffs(phdr, chip=chip)
- else:
- skew_coeffs = None
-
- # Set default filters for SBC
- if detector == 'SBC':
- if filter1 == 'CLEAR':
- filter1 = 'F115LP'
- filter2 = 'N/A'
- if filter2 == 'CLEAR':
- filter2 = 'N/A'
-
- # Read FITS header to determine order of fit, i.e. k
- norder = ftab['PRIMARY'].header['NORDER']
- if norder < 3:
- order = 3
- else:
- order = norder
-
- fx = np.zeros(shape=(order+1,order+1),dtype=np.float64)
- fy = np.zeros(shape=(order+1,order+1),dtype=np.float64)
-
- #Determine row from which to get the coefficients.
- # How many rows do we have in the table...
- fshape = ftab[1].data.shape
- colnames = ftab[1].data.names
- row = -1
-
- # Loop over all the rows looking for the one which corresponds
- # to the value of CCDCHIP we are working on...
- for i in range(fshape[0]):
-
- try:
- # Match FILTER combo to appropriate row,
- #if there is a filter column in the IDCTAB...
- if 'FILTER1' in colnames and 'FILTER2' in colnames:
-
- filt1 = ftab[1].data.field('FILTER1')[i]
- if filt1.find('CLEAR') > -1: filt1 = filt1[:5]
-
- filt2 = ftab[1].data.field('FILTER2')[i]
- if filt2.find('CLEAR') > -1: filt2 = filt2[:5]
- else:
- if 'OPT_ELEM' in colnames:
- filt1 = ftab[1].data.field('OPT_ELEM')
- if filt1.find('CLEAR') > -1: filt1 = filt1[:5]
- else:
- filt1 = filter1
-
- if 'FILTER' in colnames:
- _filt = ftab[1].data.field('FILTER')[i]
- if _filt.find('CLEAR') > -1: _filt = _filt[:5]
- if 'OPT_ELEM' in colnames:
- filt2 = _filt
- else:
- filt1 = _filt
- filt2 = 'CLEAR'
- else:
- filt2 = filter2
- except:
- # Otherwise assume all rows apply and compare to input filters...
- filt1 = filter1
- filt2 = filter2
-
- if 'DETCHIP' in colnames:
- detchip = ftab[1].data.field('DETCHIP')[i]
- if not str(detchip).isdigit():
- detchip = 1
- else:
- detchip = 1
-
- if 'DIRECTION' in colnames:
- direct = ftab[1].data.field('DIRECTION')[i].lower().strip()
- else:
- direct = 'forward'
-
- if filt1 == filter1.strip() and filt2 == filter2.strip():
- if direct == direction.strip():
- if int(detchip) == int(chip) or int(detchip) == -999:
- row = i
- break
-
- joinstr = ','
- if 'CLEAR' in filter1:
- f1str = ''
- joinstr = ''
- else:
- f1str = filter1.strip()
- if 'CLEAR' in filter2:
- f2str = ''
- joinstr = ''
- else:
- f2str = filter2.strip()
- filtstr = (joinstr.join([f1str,f2str])).strip()
- if row < 0:
- err_str = '\nProblem finding row in IDCTAB! Could not find row matching:\n'
- err_str += ' CHIP: '+str(detchip)+'\n'
- err_str += ' FILTERS: '+filtstr+'\n'
- ftab.close()
- del ftab
- raise LookupError(err_str)
- else:
- print('- IDCTAB: Distortion model from row',str(row+1),'for chip',detchip,':',filtstr)
-
- # Read in V2REF and V3REF: this can either come from current table,
- # or from an OFFTAB if time-dependent (i.e., for WFPC2)
- theta = None
- if 'V2REF' in colnames:
- v2ref = ftab[1].data.field('V2REF')[row]
- v3ref = ftab[1].data.field('V3REF')[row]
- else:
- # Read V2REF/V3REF from offset table (OFFTAB)
- if offtab:
- v2ref,v3ref,theta = readOfftab(offtab, date, chip=detchip)
- else:
- v2ref = 0.0
- v3ref = 0.0
-
- if theta == None:
- if 'THETA' in colnames:
- theta = ftab[1].data.field('THETA')[row]
- else:
- theta = 0.0
-
- refpix = {}
- refpix['XREF'] = ftab[1].data.field('XREF')[row]
- refpix['YREF'] = ftab[1].data.field('YREF')[row]
- refpix['XSIZE'] = ftab[1].data.field('XSIZE')[row]
- refpix['YSIZE'] = ftab[1].data.field('YSIZE')[row]
- refpix['PSCALE'] = round(ftab[1].data.field('SCALE')[row],8)
- refpix['V2REF'] = v2ref
- refpix['V3REF'] = v3ref
- refpix['THETA'] = theta
- refpix['XDELTA'] = 0.0
- refpix['YDELTA'] = 0.0
- refpix['DEFAULT_SCALE'] = yes
- refpix['centered'] = no
- refpix['skew_coeffs'] = skew_coeffs
- # Now that we know which row to look at, read coefficients into the
- # numeric arrays we have set up...
- # Setup which column name convention the IDCTAB follows
- # either: A,B or CX,CY
- if 'CX10' in ftab[1].data.names:
- cxstr = 'CX'
- cystr = 'CY'
- else:
- cxstr = 'A'
- cystr = 'B'
-
- for i in range(norder+1):
- if i > 0:
- for j in range(i+1):
- xcname = cxstr+str(i)+str(j)
- ycname = cystr+str(i)+str(j)
- fx[i,j] = ftab[1].data.field(xcname)[row]
- fy[i,j] = ftab[1].data.field(ycname)[row]
-
- ftab.close()
- del ftab
-
- # If CX11 is 1.0 and not equal to the PSCALE, then the
- # coeffs need to be scaled
-
- if fx[1,1] == 1.0 and abs(fx[1,1]) != refpix['PSCALE']:
- fx *= refpix['PSCALE']
- fy *= refpix['PSCALE']
-
- # Return arrays and polynomial order read in from table.
- # NOTE: XREF and YREF are stored in Fx,Fy arrays respectively.
- return fx,fy,refpix,order
-#
-#
-# Time-dependent skew correction coefficients (only ACS/WFC)
-#
-#
-def read_tdd_coeffs(phdr, chip=1):
- ''' Read in the TDD related keywords from the PRIMARY header of the IDCTAB
- '''
- # Insure we have an integer form of chip
- ic = int(chip)
-
- skew_coeffs = {}
- skew_coeffs['TDDORDER'] = 0
- skew_coeffs['TDD_DATE'] = ""
- skew_coeffs['TDD_A'] = None
- skew_coeffs['TDD_B'] = None
- skew_coeffs['TDD_CY_BETA'] = None
- skew_coeffs['TDD_CY_ALPHA'] = None
- skew_coeffs['TDD_CX_BETA'] = None
- skew_coeffs['TDD_CX_ALPHA'] = None
-
- # Skew-based TDD coefficients
- skew_terms = ['TDD_CTB','TDD_CTA','TDD_CYA','TDD_CYB','TDD_CXA','TDD_CXB']
- for s in skew_terms:
- skew_coeffs[s] = None
-
- if "TDD_CTB1" in phdr:
- # We have the 2015-calibrated TDD correction to apply
- # This correction is based on correcting the skew in the linear terms
- # not just set polynomial terms
- print("Using 2015-calibrated VAFACTOR-corrected TDD correction...")
- skew_coeffs['TDD_DATE'] = phdr['TDD_DATE']
- for s in skew_terms:
- skew_coeffs[s] = phdr.get('{0}{1}'.format(s,ic),None)
-
- elif "TDD_CYB1" in phdr:
- # We have 2014-calibrated TDD correction to apply, not J.A.-derived values
- print("Using 2014-calibrated TDD correction...")
- skew_coeffs['TDD_DATE'] = phdr['TDD_DATE']
- # Read coefficients for TDD Y coefficient
- cyb_kw = 'TDD_CYB{0}'.format(int(chip))
- skew_coeffs['TDD_CY_BETA'] = phdr.get(cyb_kw,None)
- cya_kw = 'TDD_CYA{0}'.format(int(chip))
- tdd_cya = phdr.get(cya_kw,None)
- if tdd_cya == 0 or tdd_cya == 'N/A': tdd_cya = None
- skew_coeffs['TDD_CY_ALPHA'] = tdd_cya
-
- # Read coefficients for TDD X coefficient
- cxb_kw = 'TDD_CXB{0}'.format(int(chip))
- skew_coeffs['TDD_CX_BETA'] = phdr.get(cxb_kw,None)
- cxa_kw = 'TDD_CXA{0}'.format(int(chip))
- tdd_cxa = phdr.get(cxa_kw,None)
- if tdd_cxa == 0 or tdd_cxa == 'N/A': tdd_cxa = None
- skew_coeffs['TDD_CX_ALPHA'] = tdd_cxa
-
- else:
- if "TDDORDER" in phdr:
- n = int(phdr["TDDORDER"])
- else:
- print('TDDORDER kw not present, using default TDD correction')
- return None
-
- a = np.zeros((n+1,), np.float64)
- b = np.zeros((n+1,), np.float64)
- for i in range(n+1):
- a[i] = phdr.get(("TDD_A%d" % i), 0.0)
- b[i] = phdr.get(("TDD_B%d" % i), 0.0)
- if (a==0).all() and (b==0).all():
- print('Warning: TDD_A and TDD_B coeffiecients have values of 0, \n \
- but TDDORDER is %d.' % TDDORDER)
-
- skew_coeffs['TDDORDER'] = n
- skew_coeffs['TDD_DATE'] = phdr['TDD_DATE']
- skew_coeffs['TDD_A'] = a
- skew_coeffs['TDD_B'] = b
-
- return skew_coeffs
-
-def readOfftab(offtab, date, chip=None):
-
-
-#Read V2REF,V3REF from a specified offset table (OFFTAB).
-# Return a default geometry model if no IDCTAB filenam e
-# is given. This model will not distort the data in any way.
-
- if offtab == None:
- return 0.,0.
-
- # Provide a default value for chip
- if chip:
- detchip = chip
- else:
- detchip = 1
-
- # Open up IDC table now...
- try:
- ftab = fileutil.openImage(offtab)
- except:
- raise IOError("Offset table '%s' not valid as specified!" % offtab)
-
- #Determine row from which to get the coefficients.
- # How many rows do we have in the table...
- fshape = ftab[1].data.shape
- colnames = ftab[1].data.names
- row = -1
-
- row_start = None
- row_end = None
-
- v2end = None
- v3end = None
- date_end = None
- theta_end = None
-
- num_date = convertDate(date)
- # Loop over all the rows looking for the one which corresponds
- # to the value of CCDCHIP we are working on...
- for ri in range(fshape[0]):
- i = fshape[0] - ri - 1
- if 'DETCHIP' in colnames:
- detchip = ftab[1].data.field('DETCHIP')[i]
- else:
- detchip = 1
-
- obsdate = convertDate(ftab[1].data.field('OBSDATE')[i])
-
- # If the row is appropriate for the chip...
- # Interpolate between dates
- if int(detchip) == int(chip) or int(detchip) == -999:
- if num_date <= obsdate:
- date_end = obsdate
- v2end = ftab[1].data.field('V2REF')[i]
- v3end = ftab[1].data.field('V3REF')[i]
- theta_end = ftab[1].data.field('THETA')[i]
- row_end = i
- continue
-
- if row_end == None and (num_date > obsdate):
- date_end = obsdate
- v2end = ftab[1].data.field('V2REF')[i]
- v3end = ftab[1].data.field('V3REF')[i]
- theta_end = ftab[1].data.field('THETA')[i]
- row_end = i
- continue
-
- if num_date > obsdate:
- date_start = obsdate
- v2start = ftab[1].data.field('V2REF')[i]
- v3start = ftab[1].data.field('V3REF')[i]
- theta_start = ftab[1].data.field('THETA')[i]
- row_start = i
- break
-
- ftab.close()
- del ftab
-
- if row_start == None and row_end == None:
- print('Row corresponding to DETCHIP of ',detchip,' was not found!')
- raise LookupError
- elif row_start == None:
- print('- OFFTAB: Offset defined by row',str(row_end+1))
- else:
- print('- OFFTAB: Offset interpolated from rows',str(row_start+1),'and',str(row_end+1))
-
- # Now, do the interpolation for v2ref, v3ref, and theta
- if row_start == None or row_end == row_start:
- # We are processing an observation taken after the last calibration
- date_start = date_end
- v2start = v2end
- v3start = v3end
- _fraction = 0.
- theta_start = theta_end
- else:
- _fraction = float((num_date - date_start)) / float((date_end - date_start))
-
- v2ref = _fraction * (v2end - v2start) + v2start
- v3ref = _fraction * (v3end - v3start) + v3start
- theta = _fraction * (theta_end - theta_start) + theta_start
-
- return v2ref,v3ref,theta
-
-def readWCSCoeffs(header):
-
- #Read distortion coeffs from WCS header keywords and
- #populate distortion coeffs arrays.
-
- # Read in order for polynomials
- _xorder = header['a_order']
- _yorder = header['b_order']
- order = max(max(_xorder,_yorder),3)
-
- fx = np.zeros(shape=(order+1,order+1),dtype=np.float64)
- fy = np.zeros(shape=(order+1,order+1),dtype=np.float64)
-
- # Read in CD matrix
- _cd11 = header['cd1_1']
- _cd12 = header['cd1_2']
- _cd21 = header['cd2_1']
- _cd22 = header['cd2_2']
- _cdmat = np.array([[_cd11,_cd12],[_cd21,_cd22]])
- _theta = np.arctan2(-_cd12,_cd22)
- _rotmat = np.array([[np.cos(_theta),np.sin(_theta)],
- [-np.sin(_theta),np.cos(_theta)]])
- _rCD = np.dot(_rotmat,_cdmat)
- _skew = np.arcsin(-_rCD[1][0] / _rCD[0][0])
- _scale = _rCD[0][0] * np.cos(_skew) * 3600.
- _scale2 = _rCD[1][1] * 3600.
-
- # Set up refpix
- refpix = {}
- refpix['XREF'] = header['crpix1']
- refpix['YREF'] = header['crpix2']
- refpix['XSIZE'] = header['naxis1']
- refpix['YSIZE'] = header['naxis2']
- refpix['PSCALE'] = _scale
- refpix['V2REF'] = 0.
- refpix['V3REF'] = 0.
- refpix['THETA'] = np.rad2deg(_theta)
- refpix['XDELTA'] = 0.0
- refpix['YDELTA'] = 0.0
- refpix['DEFAULT_SCALE'] = yes
- refpix['centered'] = yes
-
-
- # Set up template for coeffs keyword names
- cxstr = 'A_'
- cystr = 'B_'
- # Read coeffs into their own matrix
- for i in range(_xorder+1):
- for j in range(i+1):
- xcname = cxstr+str(j)+'_'+str(i-j)
- if xcname in header:
- fx[i,j] = header[xcname]
-
- # Extract Y coeffs separately as a different order may
- # have been used to fit it.
- for i in range(_yorder+1):
- for j in range(i+1):
- ycname = cystr+str(j)+'_'+str(i-j)
- if ycname in header:
- fy[i,j] = header[ycname]
-
- # Now set the linear terms
- fx[0][0] = 1.0
- fy[0][0] = 1.0
-
- return fx,fy,refpix,order
-
-
-def readTraugerTable(idcfile,wavelength):
-
- # Return a default geometry model if no coefficients filename
- # is given. This model will not distort the data in any way.
- if idcfile == None:
- return fileutil.defaultModel()
-
- # Trauger coefficients only result in a cubic file...
- order = 3
- numco = 10
- a_coeffs = [0] * numco
- b_coeffs = [0] * numco
- indx = _MgF2(wavelength)
-
- ifile = open(idcfile,'r')
- # Search for the first line of the coefficients
- _line = fileutil.rAsciiLine(ifile)
- while _line[:7].lower() != 'trauger':
- _line = fileutil.rAsciiLine(ifile)
- # Read in each row of coefficients,split them into their values,
- # and convert them into cubic coefficients based on
- # index of refraction value for the given wavelength
- # Build X coefficients from first 10 rows of Trauger coefficients
- j = 0
- while j < 20:
- _line = fileutil.rAsciiLine(ifile)
- if _line == '': continue
- _lc = _line.split()
- if j < 10:
- a_coeffs[j] = float(_lc[0])+float(_lc[1])*(indx-1.5)+float(_lc[2])*(indx-1.5)**2
- else:
- b_coeffs[j-10] = float(_lc[0])+float(_lc[1])*(indx-1.5)+float(_lc[2])*(indx-1.5)**2
- j = j + 1
-
- ifile.close()
- del ifile
-
- # Now, convert the coefficients into a Numeric array
- # with the right coefficients in the right place.
- # Populate output values now...
- fx = np.zeros(shape=(order+1,order+1),dtype=np.float64)
- fy = np.zeros(shape=(order+1,order+1),dtype=np.float64)
- # Assign the coefficients to their array positions
- fx[0,0] = 0.
- fx[1] = np.array([a_coeffs[2],a_coeffs[1],0.,0.],dtype=np.float64)
- fx[2] = np.array([a_coeffs[5],a_coeffs[4],a_coeffs[3],0.],dtype=np.float64)
- fx[3] = np.array([a_coeffs[9],a_coeffs[8],a_coeffs[7],a_coeffs[6]],dtype=np.float64)
- fy[0,0] = 0.
- fy[1] = np.array([b_coeffs[2],b_coeffs[1],0.,0.],dtype=np.float64)
- fy[2] = np.array([b_coeffs[5],b_coeffs[4],b_coeffs[3],0.],dtype=np.float64)
- fy[3] = np.array([b_coeffs[9],b_coeffs[8],b_coeffs[7],b_coeffs[6]],dtype=np.float64)
-
- # Used in Pattern.computeOffsets()
- refpix = {}
- refpix['XREF'] = None
- refpix['YREF'] = None
- refpix['V2REF'] = None
- refpix['V3REF'] = None
- refpix['XDELTA'] = 0.
- refpix['YDELTA'] = 0.
- refpix['PSCALE'] = None
- refpix['DEFAULT_SCALE'] = no
- refpix['centered'] = yes
-
- return fx,fy,refpix,order
-
-
-def readCubicTable(idcfile):
- # Assumption: this will only be used for cubic file...
- order = 3
- # Also, this function does NOT perform any scaling on
- # the coefficients, it simply passes along what is found
- # in the file as is...
-
- # Return a default geometry model if no coefficients filename
- # is given. This model will not distort the data in any way.
- if idcfile == None:
- return fileutil.defaultModel()
-
- ifile = open(idcfile,'r')
- # Search for the first line of the coefficients
- _line = fileutil.rAsciiLine(ifile)
-
- _found = no
- while _found == no:
- if _line[:7] in ['cubic','quartic','quintic'] or _line[:4] == 'poly':
- found = yes
- break
- _line = fileutil.rAsciiLine(ifile)
-
- # Read in each row of coefficients, without line breaks or newlines
- # split them into their values, and create a list for A coefficients
- # and another list for the B coefficients
- _line = fileutil.rAsciiLine(ifile)
- a_coeffs = _line.split()
-
- x0 = float(a_coeffs[0])
- _line = fileutil.rAsciiLine(ifile)
- a_coeffs[len(a_coeffs):] = _line.split()
- # Scale coefficients for use within PyDrizzle
- for i in range(len(a_coeffs)):
- a_coeffs[i] = float(a_coeffs[i])
-
- _line = fileutil.rAsciiLine(ifile)
- b_coeffs = _line.split()
- y0 = float(b_coeffs[0])
- _line = fileutil.rAsciiLine(ifile)
- b_coeffs[len(b_coeffs):] = _line.split()
- # Scale coefficients for use within PyDrizzle
- for i in range(len(b_coeffs)):
- b_coeffs[i] = float(b_coeffs[i])
-
- ifile.close()
- del ifile
- # Now, convert the coefficients into a Numeric array
- # with the right coefficients in the right place.
- # Populate output values now...
- fx = np.zeros(shape=(order+1,order+1),dtype=np.float64)
- fy = np.zeros(shape=(order+1,order+1),dtype=np.float64)
- # Assign the coefficients to their array positions
- fx[0,0] = 0.
- fx[1] = np.array([a_coeffs[2],a_coeffs[1],0.,0.],dtype=np.float64)
- fx[2] = np.array([a_coeffs[5],a_coeffs[4],a_coeffs[3],0.],dtype=np.float64)
- fx[3] = np.array([a_coeffs[9],a_coeffs[8],a_coeffs[7],a_coeffs[6]],dtype=np.float64)
- fy[0,0] = 0.
- fy[1] = np.array([b_coeffs[2],b_coeffs[1],0.,0.],dtype=np.float64)
- fy[2] = np.array([b_coeffs[5],b_coeffs[4],b_coeffs[3],0.],dtype=np.float64)
- fy[3] = np.array([b_coeffs[9],b_coeffs[8],b_coeffs[7],b_coeffs[6]],dtype=np.float64)
-
- # Used in Pattern.computeOffsets()
- refpix = {}
- refpix['XREF'] = None
- refpix['YREF'] = None
- refpix['V2REF'] = x0
- refpix['V3REF'] = y0
- refpix['XDELTA'] = 0.
- refpix['YDELTA'] = 0.
- refpix['PSCALE'] = None
- refpix['DEFAULT_SCALE'] = no
- refpix['centered'] = yes
-
- return fx,fy,refpix,order
-
-def factorial(n):
- """ Compute a factorial for integer n. """
- m = 1
- for i in range(int(n)):
- m = m * (i+1)
- return m
-
-def combin(j,n):
- """ Return the combinatorial factor for j in n."""
- return (factorial(j) / (factorial(n) * factorial( (j-n) ) ) )
-
-
-def defaultModel():
- """ This function returns a default, non-distorting model
- that can be used with the data.
- """
- order = 3
-
- fx = np.zeros(shape=(order+1,order+1),dtype=np.float64)
- fy = np.zeros(shape=(order+1,order+1),dtype=np.float64)
-
- fx[1,1] = 1.
- fy[1,0] = 1.
-
- # Used in Pattern.computeOffsets()
- refpix = {}
- refpix['empty_model'] = yes
- refpix['XREF'] = None
- refpix['YREF'] = None
- refpix['V2REF'] = 0.
- refpix['XSIZE'] = 0.
- refpix['YSIZE'] = 0.
- refpix['V3REF'] = 0.
- refpix['XDELTA'] = 0.
- refpix['YDELTA'] = 0.
- refpix['PSCALE'] = None
- refpix['DEFAULT_SCALE'] = no
- refpix['THETA'] = 0.
- refpix['centered'] = yes
- return fx,fy,refpix,order
-
-# Function to compute the index of refraction for MgF2 at
-# the specified wavelength for use with Trauger coefficients
-def _MgF2(lam):
- _sig = pow((1.0e7/lam),2)
- return np.sqrt(1.0 + 2.590355e10/(5.312993e10-_sig) +
- 4.4543708e9/(11.17083e9-_sig) + 4.0838897e5/(1.766361e5-_sig))
-
-
-def convertDate(date):
- """ Converts the DATE-OBS date string into an integer of the
- number of seconds since 1970.0 using calendar.timegm().
-
- INPUT: DATE-OBS in format of 'YYYY-MM-DD'.
- OUTPUT: Date (integer) in seconds.
- """
-
- _dates = date.split('-')
- _val = 0
- _date_tuple = (int(_dates[0]), int(_dates[1]), int(_dates[2]), 0, 0, 0, 0, 0, 0)
-
- return calendar.timegm(_date_tuple)
diff --git a/lib/stwcs/distortion/utils.py b/lib/stwcs/distortion/utils.py
deleted file mode 100644
index 4228f62..0000000
--- a/lib/stwcs/distortion/utils.py
+++ /dev/null
@@ -1,270 +0,0 @@
-from __future__ import division, print_function # confidence high
-
-import os
-
-import numpy as np
-from numpy import linalg
-from astropy import wcs as pywcs
-
-from stwcs import wcsutil
-from stwcs import updatewcs
-from numpy import sqrt, arctan2
-from stsci.tools import fileutil
-
-def output_wcs(list_of_wcsobj, ref_wcs=None, owcs=None, undistort=True):
- """
- Create an output WCS.
-
- Parameters
- ----------
- list_of_wcsobj: Python list
- a list of HSTWCS objects
- ref_wcs: an HSTWCS object
- to be used as a reference WCS, in case outwcs is None.
- if ref_wcs is None (default), the first member of the list
- is used as a reference
- outwcs: an HSTWCS object
- the tangent plane defined by this object is used as a reference
- undistort: boolean (default-True)
- a flag whether to create an undistorted output WCS
- """
- fra_dec = np.vstack([w.calc_footprint() for w in list_of_wcsobj])
- wcsname = list_of_wcsobj[0].wcs.name
-
- # This new algorithm may not be strictly necessary, but it may be more
- # robust in handling regions near the poles or at 0h RA.
- crval1,crval2 = computeFootprintCenter(fra_dec)
-
- crval = np.array([crval1,crval2], dtype=np.float64) # this value is now zero-based
- if owcs is None:
- if ref_wcs is None:
- ref_wcs = list_of_wcsobj[0].deepcopy()
- if undistort:
- #outwcs = undistortWCS(ref_wcs)
- outwcs = make_orthogonal_cd(ref_wcs)
- else:
- outwcs = ref_wcs.deepcopy()
- outwcs.wcs.crval = crval
- outwcs.wcs.set()
- outwcs.pscale = sqrt(outwcs.wcs.cd[0,0]**2 + outwcs.wcs.cd[1,0]**2)*3600.
- outwcs.orientat = arctan2(outwcs.wcs.cd[0,1],outwcs.wcs.cd[1,1]) * 180./np.pi
- else:
- outwcs = owcs.deepcopy()
- outwcs.pscale = sqrt(outwcs.wcs.cd[0,0]**2 + outwcs.wcs.cd[1,0]**2)*3600.
- outwcs.orientat = arctan2(outwcs.wcs.cd[0,1],outwcs.wcs.cd[1,1]) * 180./np.pi
-
- tanpix = outwcs.wcs.s2p(fra_dec, 0)['pixcrd']
-
- outwcs._naxis1 = int(np.ceil(tanpix[:,0].max() - tanpix[:,0].min()))
- outwcs._naxis2 = int(np.ceil(tanpix[:,1].max() - tanpix[:,1].min()))
- crpix = np.array([outwcs._naxis1/2., outwcs._naxis2/2.], dtype=np.float64)
- outwcs.wcs.crpix = crpix
- outwcs.wcs.set()
- tanpix = outwcs.wcs.s2p(fra_dec, 0)['pixcrd']
-
- # shift crpix to take into account (floating-point value of) position of
- # corner pixel relative to output frame size: no rounding necessary...
- newcrpix = np.array([crpix[0]+tanpix[:,0].min(), crpix[1]+
- tanpix[:,1].min()])
-
- newcrval = outwcs.wcs.p2s([newcrpix], 1)['world'][0]
- outwcs.wcs.crval = newcrval
- outwcs.wcs.set()
- outwcs.wcs.name = wcsname # keep track of label for this solution
- return outwcs
-
-def computeFootprintCenter(edges):
- """ Geographic midpoint in spherical coords for points defined by footprints.
- Algorithm derived from: http://www.geomidpoint.com/calculation.html
-
- This algorithm should be more robust against discontinuities at the poles.
- """
- alpha = np.deg2rad(edges[:,0])
- dec = np.deg2rad(edges[:,1])
-
- xmean = np.mean(np.cos(dec)*np.cos(alpha))
- ymean = np.mean(np.cos(dec)*np.sin(alpha))
- zmean = np.mean(np.sin(dec))
-
- crval1 = np.rad2deg(np.arctan2(ymean,xmean))%360.0
- crval2 = np.rad2deg(np.arctan2(zmean,np.sqrt(xmean*xmean+ymean*ymean)))
-
- return crval1,crval2
-
-def make_orthogonal_cd(wcs):
- """ Create a perfect (square, orthogonal, undistorted) CD matrix from the
- input WCS.
- """
- # get determinant of the CD matrix:
- cd = wcs.celestial.pixel_scale_matrix
-
-
- if hasattr(wcs, 'idcv2ref') and wcs.idcv2ref is not None:
- # Convert the PA_V3 orientation to the orientation at the aperture
- # This is for the reference chip only - we use this for the
- # reference tangent plane definition
- # It has the same orientation as the reference chip
- pv = updatewcs.makewcs.troll(wcs.pav3,wcs.wcs.crval[1],wcs.idcv2ref,wcs.idcv3ref)
- # Add the chip rotation angle
- if wcs.idctheta:
- pv += wcs.idctheta
- cs = np.cos(np.deg2rad(pv))
- sn = np.sin(np.deg2rad(pv))
- pvmat = np.dot(np.array([[cs,sn],[-sn,cs]]),wcs.parity)
- rot = np.arctan2(pvmat[0,1],pvmat[1,1])
- scale = wcs.idcscale/3600.
-
- det = linalg.det(wcs.parity)
-
- else:
-
- det = linalg.det(cd)
-
- # find pixel scale:
- if hasattr(wcs, 'idcscale'):
- scale = (wcs.idcscale) / 3600. # HST pixel scale provided
- else:
- scale = np.sqrt(np.abs(det)) # find as sqrt(pixel area)
-
- # find Y-axis orientation:
- if hasattr(wcs, 'orientat') and not ignoreHST:
- rot = np.deg2rad(wcs.orientat) # use HST ORIENTAT
- else:
- rot = np.arctan2(wcs.wcs.cd[0,1], wcs.wcs.cd[1,1]) # angle of the Y-axis
-
- par = -1 if det < 0.0 else 1
-
- # create a perfectly square, orthogonal WCS
- sn = np.sin(rot)
- cs = np.cos(rot)
- orthogonal_cd = scale * np.array([[par*cs, sn], [-par*sn, cs]])
-
- lin_wcsobj = pywcs.WCS()
- lin_wcsobj.wcs.cd = orthogonal_cd
- lin_wcsobj.wcs.set()
- lin_wcsobj.orientat = arctan2(lin_wcsobj.wcs.cd[0,1],lin_wcsobj.wcs.cd[1,1]) * 180./np.pi
- lin_wcsobj.pscale = sqrt(lin_wcsobj.wcs.cd[0,0]**2 + lin_wcsobj.wcs.cd[1,0]**2)*3600.
- lin_wcsobj.wcs.crval = np.array([0.,0.])
- lin_wcsobj.wcs.crpix = np.array([0.,0.])
- lin_wcsobj.wcs.ctype = ['RA---TAN', 'DEC--TAN']
- lin_wcsobj.wcs.set()
-
- return lin_wcsobj
-
-def undistortWCS(wcsobj):
- """
- Creates an undistorted linear WCS by applying the IDCTAB distortion model
- to a 3-point square. The new ORIENTAT angle is calculated as well as the
- plate scale in the undistorted frame.
- """
- assert isinstance(wcsobj, pywcs.WCS)
- from . import coeff_converter
-
- cx, cy = coeff_converter.sip2idc(wcsobj)
- # cx, cy can be None because either there is no model available
- # or updatewcs was not run.
- if cx is None or cy is None:
- if foundIDCTAB(wcsobj.idctab):
- m = """IDCTAB is present but distortion model is missing.
- Run updatewcs() to update the headers or
- pass 'undistort=False' keyword to output_wcs().\n
- """
- raise RuntimeError(m)
- else:
- print('Distortion model is not available, using input reference image for output WCS.\n')
- return wcsobj.copy()
- crpix1 = wcsobj.wcs.crpix[0]
- crpix2 = wcsobj.wcs.crpix[1]
- xy = np.array([(crpix1,crpix2),(crpix1+1.,crpix2),(crpix1,crpix2+1.)],dtype=np.double)
- offsets = np.array([wcsobj.ltv1, wcsobj.ltv2])
- px = xy + offsets
- #order = wcsobj.sip.a_order
- pscale = wcsobj.idcscale
- #pixref = np.array([wcsobj.sip.SIPREF1, wcsobj.sip.SIPREF2])
-
- tan_pix = apply_idc(px, cx, cy, wcsobj.wcs.crpix, pscale, order=1)
- xc = tan_pix[:,0]
- yc = tan_pix[:,1]
- am = xc[1] - xc[0]
- bm = xc[2] - xc[0]
- cm = yc[1] - yc[0]
- dm = yc[2] - yc[0]
- cd_mat = np.array([[am,bm],[cm,dm]],dtype=np.double)
-
- # Check the determinant for singularity
- _det = (am * dm) - (bm * cm)
- if ( _det == 0.0):
- print('Singular matrix in updateWCS, aborting ...')
- return
-
- lin_wcsobj = pywcs.WCS()
- cd_inv = np.linalg.inv(cd_mat)
- cd = np.dot(wcsobj.wcs.cd, cd_inv).astype(np.float64)
- lin_wcsobj.wcs.cd = cd
- lin_wcsobj.wcs.set()
- lin_wcsobj.orientat = arctan2(lin_wcsobj.wcs.cd[0,1],lin_wcsobj.wcs.cd[1,1]) * 180./np.pi
- lin_wcsobj.pscale = sqrt(lin_wcsobj.wcs.cd[0,0]**2 + lin_wcsobj.wcs.cd[1,0]**2)*3600.
- lin_wcsobj.wcs.crval = np.array([0.,0.])
- lin_wcsobj.wcs.crpix = np.array([0.,0.])
- lin_wcsobj.wcs.ctype = ['RA---TAN', 'DEC--TAN']
- lin_wcsobj.wcs.set()
- return lin_wcsobj
-
-def apply_idc(pixpos, cx, cy, pixref, pscale= None, order=None):
- """
- Apply the IDCTAB polynomial distortion model to pixel positions.
- pixpos must be already corrected for ltv1/2.
-
- Parameters
- ----------
- pixpos: a 2D numpy array of (x,y) pixel positions to be distortion corrected
- cx, cy: IDC model distortion coefficients
- pixref: reference opixel position
-
- """
- if cx is None:
- return pixpos
-
- if order is None:
- print('Unknown order of distortion model \n')
- return pixpos
- if pscale is None:
- print('Unknown model plate scale\n')
- return pixpos
-
- # Apply in the same way that 'drizzle' would...
- _cx = cx/pscale
- _cy = cy/ pscale
- _p = pixpos
-
- # Do NOT include any zero-point terms in CX,CY here
- # as they should not be scaled by plate-scale like rest
- # of coeffs... This makes the computations consistent
- # with 'drizzle'. WJH 17-Feb-2004
- _cx[0,0] = 0.
- _cy[0,0] = 0.
-
- dxy = _p - pixref
- # Apply coefficients from distortion model here...
-
- c = _p * 0.
- for i in range(order+1):
- for j in range(i+1):
- c[:,0] = c[:,0] + _cx[i][j] * pow(dxy[:,0],j) * pow(dxy[:,1],(i-j))
- c[:,1] = c[:,1] + _cy[i][j] * pow(dxy[:,0],j) * pow(dxy[:,1],(i-j))
-
- return c
-
-def foundIDCTAB(idctab):
- idctab_found = True
- try:
- idctab = fileutil.osfn(idctab)
- if idctab == 'N/A' or idctab == "":
- idctab_found = False
- if os.path.exists(idctab):
- idctab_found = True
- else:
- idctab_found = False
- except KeyError:
- idctab_found = False
- return idctab_found
diff --git a/lib/stwcs/gui/__init__.py b/lib/stwcs/gui/__init__.py
deleted file mode 100644
index cd21bf6..0000000
--- a/lib/stwcs/gui/__init__.py
+++ /dev/null
@@ -1,19 +0,0 @@
-""" STWCS.GUI
-
-This package defines the TEAL interfaces for public, file-based operations
-provided by the STWCS package.
-
-"""
-from __future__ import absolute_import # confidence high
-__docformat__ = 'restructuredtext'
-
-# import modules which define the TEAL interfaces
-from . import write_headerlet
-from . import extract_headerlet
-from . import attach_headerlet
-from . import delete_headerlet
-from . import headerlet_summary
-from . import archive_headerlet
-from . import restore_headerlet
-from . import apply_headerlet
-from . import updatewcs
diff --git a/lib/stwcs/gui/apply_headerlet.help b/lib/stwcs/gui/apply_headerlet.help
deleted file mode 100644
index f701b52..0000000
--- a/lib/stwcs/gui/apply_headerlet.help
+++ /dev/null
@@ -1,38 +0,0 @@
-This task applies a headerlet to a science observation to update either the
-PRIMARY WCS or to add it as an alternate WCS.
-
-filename = ""
-hdrlet = ""
-attach = True
-primary = True
-archive = True
-force = False
-wcskey = ""
-wcsname = ""
-verbose = False
-
-Parameters
-----------
-filename: string, @-file or wild-card name
- File name(s) of science observation whose WCS solution will be updated
-hdrlet: string, @-file or wild-card name
- Headerlet file(s), must match input filenames 1-to-1
-attach: boolean
- True (default): append headerlet to FITS file as a new extension.
-primary: boolean
- Specify whether or not to replace PRIMARY WCS with WCS from headerlet.
-archive: boolean
- True (default): before updating, create a headerlet with the
- WCS old solution.
-force: boolean
- If True, this will cause the headerlet to replace the current PRIMARY
- WCS even if it has a different distortion model. [Default: False]
-wcskey: string
- Key value (A-Z, except O) for this alternate WCS
- If None, the next available key will be used
-wcsname: string
- Name to be assigned to this alternate WCS
- WCSNAME is a required keyword in a Headerlet but this allows the
- user to change it as desired.
-logging: boolean
- enable file logging
diff --git a/lib/stwcs/gui/apply_headerlet.py b/lib/stwcs/gui/apply_headerlet.py
deleted file mode 100644
index d517e9f..0000000
--- a/lib/stwcs/gui/apply_headerlet.py
+++ /dev/null
@@ -1,54 +0,0 @@
-import os
-from stsci.tools import teal, parseinput
-
-import stwcs
-from stwcs.wcsutil import headerlet
-
-__taskname__ = __name__.split('.')[-1] # needed for help string
-__package__ = headerlet.__name__
-__version__ = stwcs.__version__
-#
-#### Interfaces used by TEAL
-#
-def getHelpAsString(docstring=False):
- """
- return useful help from a file in the script directory called __taskname__.help
- """
- install_dir = os.path.dirname(__file__)
- htmlfile = os.path.join(install_dir,'htmlhelp',__taskname__+'.html')
- helpfile = os.path.join(install_dir,__taskname__+'.help')
- if docstring or (not docstring and not os.path.exists(htmlfile)):
- helpString = __taskname__+' Version '+__version__+'\n\n'
- if os.path.exists(helpfile):
- helpString += teal.getHelpFileAsString(__taskname__,__file__)
- else:
- helpString = 'file://'+htmlfile
-
- return helpString
-
-def run(configObj=None):
-
- # start by interpreting filename and hdrlet inputs
- filename = parseinput.parseinput(configObj['filename'])[0]
- hdrlet = parseinput.parseinput(configObj['hdrlet'])[0]
-
- if configObj['primary']:
- # Call function with properly interpreted input parameters
- # Syntax: apply_headerlet_as_primary(filename, hdrlet, attach=True,
- # archive=True, force=False, verbose=False)
- headerlet.apply_headerlet_as_primary(filename,
- hdrlet,attach=configObj['attach'],
- archive=configObj['archive'],force=configObj['force'],
- logging=configObj['logging'])
- else:
- wcsname = configObj['wcsname']
- if wcsname in ['',' ','INDEF']: wcsname = None
- wcskey = configObj['wcskey']
- if wcskey == '': wcskey = None
- # Call function with properly interpreted input parameters
- # apply_headerlet_as_alternate(filename, hdrlet, attach=True,
- # wcskey=None, wcsname=None, verbose=False)
- headerlet.apply_headerlet_as_alternate(filename,
- hdrlet, attach=configObj['attach'],
- wcsname=wcsname, wcskey=wcskey,
- logging=configObj['logging'])
diff --git a/lib/stwcs/gui/archive_headerlet.py b/lib/stwcs/gui/archive_headerlet.py
deleted file mode 100644
index 7ad3d4d..0000000
--- a/lib/stwcs/gui/archive_headerlet.py
+++ /dev/null
@@ -1,69 +0,0 @@
-from __future__ import print_function
-import os
-
-from astropy.io import fits
-from stsci.tools import teal
-
-import stwcs
-from stwcs.wcsutil import headerlet
-
-__taskname__ = __name__.split('.')[-1] # needed for help string
-__package__ = headerlet.__name__
-__version__ = stwcs.__version__
-#
-#### Interfaces used by TEAL
-#
-def getHelpAsString(docstring=False):
- """
- return useful help from a file in the script directory called __taskname__.help
- """
- install_dir = os.path.dirname(__file__)
- htmlfile = os.path.join(install_dir,'htmlhelp',__taskname__+'.html')
- helpfile = os.path.join(install_dir,__taskname__+'.help')
- if docstring or (not docstring and not os.path.exists(htmlfile)):
- helpString = __taskname__+' Version '+__version__+'\n\n'
- if os.path.exists(helpfile):
- helpString += teal.getHelpFileAsString(__taskname__,__file__)
- else:
- helpString += headerlet.archive_as_headerlet.__doc__
-
- else:
- helpString = 'file://'+htmlfile
-
- return helpString
-
-def run(configObj=None):
-
- if configObj['hdrname'] in ['',' ','INDEF']:
- print('='*60)
- print('ERROR:')
- print(' No valid "hdrname" parameter value provided!')
- print(' Please restart this task and provide a value for this parameter.')
- print('='*60)
- return
-
- str_kw = ['wcsname','destim','sipname','npolfile','d2imfile',
- 'descrip','history','author']
-
- # create dictionary of remaining parameters, deleting extraneous ones
- # such as those above
- cdict = configObj.dict()
- # remove any rules defined for the TEAL interface
- if "_RULES_" in cdict: del cdict['_RULES_']
- del cdict['_task_name_']
- del cdict['filename']
- del cdict['hdrname']
-
- # Convert blank string input as None
- for kw in str_kw:
- if cdict[kw] == '': cdict[kw] = None
- if cdict['wcskey'].lower() == 'primary': cdict['wcskey'] = ' '
-
- # Call function with properly interpreted input parameters
- # Syntax: archive_as_headerlet(filename, sciext='SCI', wcsname=None, wcskey=None,
- # hdrname=None, destim=None,
- # sipname=None, npolfile=None, d2imfile=None,
- # author=None, descrip=None, history=None,
- # hdrlet=None, clobber=False)
- headerlet.archive_as_headerlet(configObj['filename'], configObj['hdrname'],
- **cdict)
diff --git a/lib/stwcs/gui/attach_headerlet.py b/lib/stwcs/gui/attach_headerlet.py
deleted file mode 100644
index 873c549..0000000
--- a/lib/stwcs/gui/attach_headerlet.py
+++ /dev/null
@@ -1,36 +0,0 @@
-import os
-
-from stsci.tools import teal
-
-import stwcs
-from stwcs.wcsutil import headerlet
-
-__taskname__ = __name__.split('.')[-1] # needed for help string
-__package__ = headerlet.__name__
-__version__ = stwcs.__version__
-#
-#### Interfaces used by TEAL
-#
-def getHelpAsString(docstring=False):
- """
- return useful help from a file in the script directory called __taskname__.help
- """
- install_dir = os.path.dirname(__file__)
- htmlfile = os.path.join(install_dir,'htmlhelp',__taskname__+'.html')
- helpfile = os.path.join(install_dir,__taskname__+'.help')
- if docstring or (not docstring and not os.path.exists(htmlfile)):
- helpString = __taskname__+' Version '+__version__+'\n\n'
- if os.path.exists(helpfile):
- helpString += teal.getHelpFileAsString(__taskname__,__file__)
- else:
- helpString += eval('.'.join([__package__,__taskname__,'__doc__']))
- else:
- helpString = 'file://'+htmlfile
-
- return helpString
-
-def run(configObj=None):
-
- headerlet.attach_headerlet(configObj['filename'],configObj['hdrlet'],
- configObj['logging'])
-
diff --git a/lib/stwcs/gui/delete_headerlet.py b/lib/stwcs/gui/delete_headerlet.py
deleted file mode 100644
index b3df5a7..0000000
--- a/lib/stwcs/gui/delete_headerlet.py
+++ /dev/null
@@ -1,52 +0,0 @@
-from __future__ import print_function
-import os
-
-from stsci.tools import teal
-from stsci.tools import parseinput
-
-import stwcs
-from stwcs.wcsutil import headerlet
-
-__taskname__ = __name__.split('.')[-1] # needed for help string
-__package__ = headerlet.__name__
-__version__ = stwcs.__version__
-#
-#### Interfaces used by TEAL
-#
-def getHelpAsString(docstring=False):
- """
- return useful help from a file in the script directory called __taskname__.help
- """
- install_dir = os.path.dirname(__file__)
- htmlfile = os.path.join(install_dir,'htmlhelp',__taskname__+'.html')
- helpfile = os.path.join(install_dir,__taskname__+'.help')
- if docstring or (not docstring and not os.path.exists(htmlfile)):
- helpString = __taskname__+' Version '+__version__+'\n\n'
- if os.path.exists(helpfile):
- helpString += teal.getHelpFileAsString(__taskname__,__file__)
- else:
- helpString += eval('.'.join([__package__,__taskname__,'__doc__']))
- else:
- helpString = 'file://'+htmlfile
-
- return helpString
-
-def run(configObj=None):
-
- if configObj['hdrname'] == '' and configObj['hdrext'] is None and \
- configObj['distname'] == '':
- print('='*60)
- print('ERROR:')
- print(' No valid "hdrname", "hdrext" or "distname" parameter value provided!')
- print(' Please restart this task and provide a value for one of these parameters.')
- print('='*60)
- return
- filename = parseinput.parseinput(configObj['filename'])[0]
- # Call function with properly interpreted input parameters
- # Syntax: delete_headerlet(filename, hdrname=None, hdrext=None, distname=None)
- headerlet.delete_headerlet(filename,
- hdrname = configObj['hdrname'],
- hdrext = configObj['hdrext'],
- distname = configObj['distname'],
- logging = configObj['logging'])
-
diff --git a/lib/stwcs/gui/extract_headerlet.py b/lib/stwcs/gui/extract_headerlet.py
deleted file mode 100644
index 02ecd7a..0000000
--- a/lib/stwcs/gui/extract_headerlet.py
+++ /dev/null
@@ -1,58 +0,0 @@
-from __future__ import print_function
-import os
-
-from stsci.tools import teal
-
-import stwcs
-from stwcs.wcsutil import headerlet
-
-__taskname__ = __name__.split('.')[-1] # needed for help string
-__package__ = headerlet.__name__
-__version__ = stwcs.__version__
-#
-#### Interfaces used by TEAL
-#
-def getHelpAsString(docstring=False):
- """
- return useful help from a file in the script directory called __taskname__.help
- """
- install_dir = os.path.dirname(__file__)
- htmlfile = os.path.join(install_dir,'htmlhelp',__taskname__+'.html')
- helpfile = os.path.join(install_dir,__taskname__+'.help')
- if docstring or (not docstring and not os.path.exists(htmlfile)):
- helpString = __taskname__+' Version '+__version__+'\n\n'
- if os.path.exists(helpfile):
- helpString += teal.getHelpFileAsString(__taskname__,__file__)
- else:
- helpString += eval('.'.join([__package__,__taskname__,'__doc__']))
-
- else:
- helpString = 'file://'+htmlfile
-
- return helpString
-
-def run(configObj=None):
-
- if configObj['output'] in ['',' ',None]:
- print('='*60)
- print('ERROR:')
- print(' No valid "output" parameter value provided!')
- print(' Please restart this task and provide a value for this parameter.')
- print('='*60)
- return
-
- # create dictionary of remaining parameters, deleting extraneous ones
- # such as those above
- cdict = configObj.dict()
- # remove any rules defined for the TEAL interface
- if "_RULES_" in cdict: del cdict['_RULES_']
- del cdict['_task_name_']
- del cdict['filename']
- del cdict['output']
-
- # Call function with properly interpreted input parameters
- # Syntax: extract_headerlet(filename, output, extnum=None, hdrname=None,
- # clobber=False, verbose=100)
- headerlet.extract_headerlet(configObj['filename'], configObj['output'],
- **cdict)
-
diff --git a/lib/stwcs/gui/headerlet_summary.py b/lib/stwcs/gui/headerlet_summary.py
deleted file mode 100644
index 82a3e0c..0000000
--- a/lib/stwcs/gui/headerlet_summary.py
+++ /dev/null
@@ -1,47 +0,0 @@
-import os
-from stsci.tools import teal
-
-import stwcs
-from stwcs.wcsutil import headerlet
-
-__taskname__ = __name__.split('.')[-1] # needed for help string
-__package__ = headerlet.__name__
-__version__ = stwcs.__version__
-#
-#### Interfaces used by TEAL
-#
-def getHelpAsString(docstring=False):
- """
- return useful help from a file in the script directory called __taskname__.help
- """
- install_dir = os.path.dirname(__file__)
- htmlfile = os.path.join(install_dir,'htmlhelp',__taskname__+'.html')
- helpfile = os.path.join(install_dir,__taskname__+'.help')
- if docstring or (not docstring and not os.path.exists(htmlfile)):
- helpString = __taskname__+' Version '+__version__+'\n\n'
- if os.path.exists(helpfile):
- helpString += teal.getHelpFileAsString(__taskname__,__file__)
- else:
- helpString += eval('.'.join([__package__,__taskname__,'__doc__']))
- else:
- helpString = 'file://'+htmlfile
-
- return helpString
-
-def run(configObj=None):
-
- # create dictionary of remaining parameters, deleting extraneous ones
- # such as those above
- cdict = configObj.dict()
- # remove any rules defined for the TEAL interface
- if "_RULES_" in cdict: del cdict['_RULES_']
- del cdict['_task_name_']
- del cdict['filename']
- if headerlet.is_par_blank(cdict['columns']):
- cdict['columns'] = None
- # Call function with properly interpreted input parameters
-
- # Syntax: headerlet_summary(filename,columns=None,pad=2,maxwidth=None,
- # output=None,clobber=True,quiet=False)
- headerlet.headerlet_summary(configObj['filename'],**cdict)
-
diff --git a/lib/stwcs/gui/pars/apply_headerlet.cfg b/lib/stwcs/gui/pars/apply_headerlet.cfg
deleted file mode 100644
index 06d6daf..0000000
--- a/lib/stwcs/gui/pars/apply_headerlet.cfg
+++ /dev/null
@@ -1,10 +0,0 @@
-_task_name_ = apply_headerlet
-filename = ""
-hdrlet = ""
-attach = True
-primary = True
-archive = True
-force = False
-wcskey = ""
-wcsname = ""
-logging = False
diff --git a/lib/stwcs/gui/pars/apply_headerlet.cfgspc b/lib/stwcs/gui/pars/apply_headerlet.cfgspc
deleted file mode 100644
index 648e562..0000000
--- a/lib/stwcs/gui/pars/apply_headerlet.cfgspc
+++ /dev/null
@@ -1,12 +0,0 @@
-_task_name_ = string_kw(default="apply_headerlet")
-filename = string_kw(default="", comment="Input file name")
-hdrlet = string_kw(default="", comment="Headerlet FITS filename")
-attach = boolean_kw(default=True, comment= "Append headerlet to FITS file as new extension?")
-primary = boolean_kw(default=True, triggers="_rule1_", comment="Replace PRIMARY WCS with headerlet WCS?")
-archive = boolean_kw(default=True, active_if="_rule1_", comment="Save PRIMARY WCS as new headerlet extension?")
-force = boolean_kw(default=False, active_if="_rule1_", comment="If distortions do not match, force update anyway?")
-wcskey = option_kw("A","B","C","D","E","F","G","H","I","J","K","L","M","N","P","Q","R","S","T","U","V","W","X","Y","Z","", default="", inactive_if="_rule1_", comment="Apply headerlet as alternate WCS with this letter")
-wcsname = string_kw(default="", inactive_if="_rule1_", comment="Apply headerlet as alternate WCS with this name")
-logging = boolean_kw(default=False, comment= "Enable logging to a file")
-[ _RULES_ ]
-_rule1_ = string_kw(default=True, code='tyfn={"yes":True, "no":False}; OUT = tyfn[VAL]')
diff --git a/lib/stwcs/gui/pars/archive_headerlet.cfg b/lib/stwcs/gui/pars/archive_headerlet.cfg
deleted file mode 100644
index 97cd7ef..0000000
--- a/lib/stwcs/gui/pars/archive_headerlet.cfg
+++ /dev/null
@@ -1,14 +0,0 @@
-_task_name_ = archive_headerlet
-filename = ""
-hdrname = ""
-sciext = "SCI"
-wcsname = ""
-wcskey = "PRIMARY"
-destim = ""
-sipname = ""
-npolfile = ""
-d2imfile = ""
-author = ""
-descrip = ""
-history = ""
-logging = False \ No newline at end of file
diff --git a/lib/stwcs/gui/pars/archive_headerlet.cfgspc b/lib/stwcs/gui/pars/archive_headerlet.cfgspc
deleted file mode 100644
index 03e67f0..0000000
--- a/lib/stwcs/gui/pars/archive_headerlet.cfgspc
+++ /dev/null
@@ -1,14 +0,0 @@
-_task_name_ = string_kw(default="archive_headerlet")
-filename = string_kw(default="", comment="Input file name")
-hdrname = string_kw(default="", comment="Unique name(HDRNAME) for headerlet")
-sciext = string_kw(default="SCI", comment="EXTNAME of extension with WCS")
-wcsname = string_kw(default="", comment="Name of WCS to be archived")
-wcskey = option_kw("A","B","C","D","E","F","G","H","I","J","K","L","M","N","P","Q","R","S","T","U","V","W","X","Y","Z","PRIMARY", default="PRIMARY", comment="Archive the WCS with this letter")
-destim = string_kw(default="", comment="Rootname of image to which this headerlet applies ")
-sipname = string_kw(default="", comment="Name for source of polynomial distortion keywords")
-npolfile = string_kw(default="", comment="Name for source of non-polynomial residuals")
-d2imfile = string_kw(default="", comment="Name for source of detector correction table")
-author = string_kw(default="", comment="Author name for creator of headerlet")
-descrip = string_kw(default="", comment="Short description of headerlet solution")
-history = string_kw(default="", comment="Name of ASCII file containing history for headerlet")
-logging = boolean_kw(default=False, comment= "Enable logging to a file") \ No newline at end of file
diff --git a/lib/stwcs/gui/pars/attach_headerlet.cfg b/lib/stwcs/gui/pars/attach_headerlet.cfg
deleted file mode 100644
index d131a70..0000000
--- a/lib/stwcs/gui/pars/attach_headerlet.cfg
+++ /dev/null
@@ -1,4 +0,0 @@
-_task_name_ = attach_headerlet
-filename = ""
-hdrlet = ""
-logging = False
diff --git a/lib/stwcs/gui/pars/attach_headerlet.cfgspc b/lib/stwcs/gui/pars/attach_headerlet.cfgspc
deleted file mode 100644
index bc91bca..0000000
--- a/lib/stwcs/gui/pars/attach_headerlet.cfgspc
+++ /dev/null
@@ -1,4 +0,0 @@
-_task_name_ = string_kw(default="attach_headerlet")
-filename = string_kw(default="", comment="FITS image file name")
-hdrlet = string_kw(default="", comment="Headerlet FITS filename")
-logging = boolean_kw(default=False, comment= "Enable logging to a file") \ No newline at end of file
diff --git a/lib/stwcs/gui/pars/delete_headerlet.cfg b/lib/stwcs/gui/pars/delete_headerlet.cfg
deleted file mode 100644
index d156937..0000000
--- a/lib/stwcs/gui/pars/delete_headerlet.cfg
+++ /dev/null
@@ -1,6 +0,0 @@
-_task_name_ = delete_headerlet
-filename = ""
-hdrname = ""
-hdrext = None
-distname = ""
-logging = False \ No newline at end of file
diff --git a/lib/stwcs/gui/pars/delete_headerlet.cfgspc b/lib/stwcs/gui/pars/delete_headerlet.cfgspc
deleted file mode 100644
index 43790b1..0000000
--- a/lib/stwcs/gui/pars/delete_headerlet.cfgspc
+++ /dev/null
@@ -1,6 +0,0 @@
-_task_name_ = string_kw(default="delete_headerlet")
-filename = string_kw(default="", comment="FITS image file name(s), list or wild-card")
-hdrname = string_kw(default="", comment="Delete headerlet with this HDRNAME")
-hdrext = integer_or_none_kw(default=None, comment="Delete headerlet from this extension")
-distname = string_kw(default="", comment="Delete *ALL* with this DISTNAME")
-logging = boolean_kw(default=False, comment= "Enable logging to a file")
diff --git a/lib/stwcs/gui/pars/extract_headerlet.cfg b/lib/stwcs/gui/pars/extract_headerlet.cfg
deleted file mode 100644
index 3dd0a7a..0000000
--- a/lib/stwcs/gui/pars/extract_headerlet.cfg
+++ /dev/null
@@ -1,7 +0,0 @@
-_task_name_ = extract_headerlet
-filename = ""
-output = ""
-extnum = None
-hdrname = ""
-clobber = True
-logging = False
diff --git a/lib/stwcs/gui/pars/extract_headerlet.cfgspc b/lib/stwcs/gui/pars/extract_headerlet.cfgspc
deleted file mode 100644
index b50d4bf..0000000
--- a/lib/stwcs/gui/pars/extract_headerlet.cfgspc
+++ /dev/null
@@ -1,7 +0,0 @@
-_task_name_ = string_kw(default="extract_headerlet")
-filename = string_kw(default="", comment="Input file name")
-output = string_kw(default="", comment="Output headerlet FITS filename")
-extnum = integer_or_none_kw(default=None, comment="FITS extension number of headerlet")
-hdrname = string_kw(default="", comment="Unique name(HDRNAME) for headerlet")
-clobber = boolean_kw(default=True, comment= "Overwrite existing headerlet FITS file?")
-logging = boolean_kw(default=False, comment= "Enable logging to a file")
diff --git a/lib/stwcs/gui/pars/headerlet_summary.cfg b/lib/stwcs/gui/pars/headerlet_summary.cfg
deleted file mode 100644
index 7203552..0000000
--- a/lib/stwcs/gui/pars/headerlet_summary.cfg
+++ /dev/null
@@ -1,8 +0,0 @@
-_task_name_ = headerlet_summary
-filename = ""
-columns = None
-pad = 2
-maxwidth = None
-output = ""
-clobber = True
-quiet = False
diff --git a/lib/stwcs/gui/pars/headerlet_summary.cfgspc b/lib/stwcs/gui/pars/headerlet_summary.cfgspc
deleted file mode 100644
index ce65930..0000000
--- a/lib/stwcs/gui/pars/headerlet_summary.cfgspc
+++ /dev/null
@@ -1,8 +0,0 @@
-_task_name_ = string_kw(default="headerlet_summary")
-filename = string_kw(default="", comment="FITS image file name")
-columns = string_kw(default="", comment="Headerlet keyword(s) to be reported")
-pad = integer_kw(default=2, comment="Number of spaces between output columns")
-maxwidth = integer_or_none_kw(default=None, comment="Max width for each column")
-output = string_kw(default="", comment="Name of output file for summary")
-clobber = boolean_kw(default=True, comment="Overwrite previously written summary?")
-quiet = boolean_kw(default=False, comment="Suppress output of summary to STDOUT?")
diff --git a/lib/stwcs/gui/pars/restore_headerlet.cfg b/lib/stwcs/gui/pars/restore_headerlet.cfg
deleted file mode 100644
index b0ec427..0000000
--- a/lib/stwcs/gui/pars/restore_headerlet.cfg
+++ /dev/null
@@ -1,10 +0,0 @@
-_task_name_ = restore_headerlet
-filename = ""
-archive = True
-force = False
-distname = ""
-primary = None
-sciext = "SCI"
-hdrname = ""
-hdrext = None
-logging = False \ No newline at end of file
diff --git a/lib/stwcs/gui/pars/restore_headerlet.cfgspc b/lib/stwcs/gui/pars/restore_headerlet.cfgspc
deleted file mode 100644
index df47e3f..0000000
--- a/lib/stwcs/gui/pars/restore_headerlet.cfgspc
+++ /dev/null
@@ -1,12 +0,0 @@
-_task_name_ = string_kw(default="restore_headerlet")
-filename = string_kw(default="", comment="Input file name")
-archive = boolean_kw(default=True, comment= "Create headerlets from WCSs being replaced?")
-force = boolean_kw(default=False, comment="If distortions do not match, force update anyway?")
-distname = string_kw(default="", triggers="_rule1_", comment="Restore ALL headerlet extensions with this DISTNAME")
-primary = integer_or_none_kw(default=None, inactive_if="_rule1_", comment="Headerlet extension to restore as new primary WCS")
-sciext = string_kw(default="SCI", inactive_if="_rule1_", comment="EXTNAME of extension with WCS")
-hdrname = string_kw(default="", active_if="_rule1_", comment="HDRNAME of headerlet extension to be restored")
-hdrext = integer_or_none_kw(default=None, active_if="_rule1_", comment="Extension number for headerlet to be restored")
-logging = boolean_kw(default=False, comment= "Enable logging to a file")
-[ _RULES_ ]
-_rule1_ = string_kw(default=True, code='from stwcs import wcsutil;from stwcs.wcsutil import headerlet;OUT = headerlet.is_par_blank(VAL)')
diff --git a/lib/stwcs/gui/pars/updatewcs.cfg b/lib/stwcs/gui/pars/updatewcs.cfg
deleted file mode 100644
index 35360f2..0000000
--- a/lib/stwcs/gui/pars/updatewcs.cfg
+++ /dev/null
@@ -1,8 +0,0 @@
-_task_name_ = updatewcs
-input = "*flt.fits"
-extname = "SCI"
-vacorr = True
-tddcorr = True
-npolcorr = True
-d2imcorr = True
-checkfiles = True
diff --git a/lib/stwcs/gui/pars/updatewcs.cfgspc b/lib/stwcs/gui/pars/updatewcs.cfgspc
deleted file mode 100644
index a3a3fb5..0000000
--- a/lib/stwcs/gui/pars/updatewcs.cfgspc
+++ /dev/null
@@ -1,8 +0,0 @@
-_task_name_ = string_kw(default="updatewcs")
-input = string_kw(default="", comment="Input files (name, suffix, or @list)")
-extname = string_kw(default="SCI", comment="EXTNAME of extensions to be archived")
-vacorr = boolean_kw(default=True, comment= "Apply velocity aberration correction?")
-tddcorr = boolean_kw(default=True, comment= "Apply time dependent distortion correction?")
-npolcorr = boolean_kw(default=True, comment= "Apply lookup table distortion?")
-d2imcorr = boolean_kw(default=True, comment= "Apply detector to image correction?")
-checkfiles = boolean_kw(default=True, comment= "Check format of input files?")
diff --git a/lib/stwcs/gui/pars/write_headerlet.cfg b/lib/stwcs/gui/pars/write_headerlet.cfg
deleted file mode 100644
index 9eb4592..0000000
--- a/lib/stwcs/gui/pars/write_headerlet.cfg
+++ /dev/null
@@ -1,18 +0,0 @@
-_task_name_ = write_headerlet
-filename = ""
-output = ""
-clobber = True
-hdrname = ""
-wcskey = "PRIMARY"
-wcsname = ""
-author = ""
-descrip = ""
-catalog = ""
-history = ""
-sciext = "SCI"
-destim = ""
-sipname = ""
-npolfile = ""
-d2imfile = ""
-attach = True
-logging = False \ No newline at end of file
diff --git a/lib/stwcs/gui/pars/write_headerlet.cfgspc b/lib/stwcs/gui/pars/write_headerlet.cfgspc
deleted file mode 100644
index ba28b05..0000000
--- a/lib/stwcs/gui/pars/write_headerlet.cfgspc
+++ /dev/null
@@ -1,18 +0,0 @@
-_task_name_ = string_kw(default="write_headerlet")
-filename = string_kw(default="", comment="Input file name")
-output = string_kw(default="", comment="Filename for headerlet FITS file")
-clobber = boolean_kw(default=True, comment= "Overwrite existing headerlet FITS file?")
-hdrname = string_kw(default="", comment="Unique name(HDRNAME) for headerlet[REQUIRED]")
-wcskey = option_kw("A","B","C","D","E","F","G","H","I","J","K","L","M","N","P","Q","R","S","T","U","V","W","X","Y","Z","PRIMARY", default="PRIMARY", comment="Create headerlet from WCS with this letter")
-wcsname = string_kw(default="", comment="Create headerlet from WCS with this name")
-author = string_kw(default="", comment="Author name for creator of headerlet")
-descrip = string_kw(default="", comment="Short description of headerlet solution")
-catalog = string_kw(default="", comment="Reference frame for headerlet solution")
-history = string_kw(default="", comment="Name of ASCII file containing history for headerlet")
-sciext = string_kw(default="SCI", comment="EXTNAME of extension with WCS")
-destim = string_kw(default="", comment="Rootname of image to which this headerlet applies ")
-sipname = string_kw(default="", comment="Name for source of polynomial distortion keywords")
-npolfile = string_kw(default="", comment="Name for source of non-polynomial residuals")
-d2imfile = string_kw(default="", comment="Name for source of detector correction table")
-attach = boolean_kw(default=True, comment="Create headerlet FITS extension?")
-logging = boolean_kw(default=False, comment= "Enable logging to a file") \ No newline at end of file
diff --git a/lib/stwcs/gui/restore_headerlet.help b/lib/stwcs/gui/restore_headerlet.help
deleted file mode 100644
index fe07a15..0000000
--- a/lib/stwcs/gui/restore_headerlet.help
+++ /dev/null
@@ -1,43 +0,0 @@
-Restore headerlet extension(s) as either a primary WCS or as alternate WCSs
-
-This task can restore a WCS solution stored in a headerlet extension or
-restore all WCS solutions from all headerlet extensions with the same
-distortion model.
-
-Parameters
-----------
-filename: string or HDUList
- Either a filename or PyFITS HDUList object for the input science file
- An input filename (str) will be expanded as necessary to interpret
- any environmental variables included in the filename.
-
-archive: boolean (default True)
- flag indicating if HeaderletHDUs should be created from the
- primary and alternate WCSs in fname before restoring all matching
- headerlet extensions
-
-force: boolean (default:False)
- When the distortion models of the headerlet and the primary do
- not match, and archive is False, this flag forces an update of
- the primary.
-
-distname: string
- distortion model as represented by a DISTNAME keyword
-
-primary: int or string or None
- HeaderletHDU to be restored as primary
- if int - a fits extension
- if string - HDRNAME
- if None - use first HeaderletHDU
-
-sciext: string (default: SCI)
- EXTNAME value of FITS extensions with WCS keywords
-
-hdrname: string
- HDRNAME keyword of HeaderletHDU
-
-hdrext: int or tuple
- Headerlet extension number of tuple ('HDRLET',2)
-
-logging: boolean
- enable file logging \ No newline at end of file
diff --git a/lib/stwcs/gui/restore_headerlet.py b/lib/stwcs/gui/restore_headerlet.py
deleted file mode 100644
index 7570d76..0000000
--- a/lib/stwcs/gui/restore_headerlet.py
+++ /dev/null
@@ -1,48 +0,0 @@
-import os
-
-from stsci.tools import teal
-
-import stwcs
-from stwcs.wcsutil import headerlet
-
-__taskname__ = __name__.split('.')[-1] # needed for help string
-__package__ = headerlet.__name__
-__version__ = stwcs.__version__
-#
-#### Interfaces used by TEAL
-#
-def getHelpAsString(docstring=False):
- """
- return useful help from a file in the script directory called __taskname__.help
- """
- install_dir = os.path.dirname(__file__)
- htmlfile = os.path.join(install_dir,'htmlhelp',__taskname__+'.html')
- helpfile = os.path.join(install_dir,__taskname__+'.help')
- if docstring or (not docstring and not os.path.exists(htmlfile)):
- helpString = __taskname__+' Version '+__version__+'\n\n'
- if os.path.exists(helpfile):
- helpString += teal.getHelpFileAsString(__taskname__,__file__)
- else:
- helpString = 'file://'+htmlfile
-
- return helpString
-
-def run(configObj=None):
-
- if configObj['distname'] not in ['',' ','INDEF']:
- # Call function with properly interpreted input parameters
- # Syntax: restore_all_with_distname(filename, distname, primary,
- # archive=True, sciext='SCI', verbose=False)
- headerlet.restore_all_with_distname(configObj['filename'],
- configObj['distname'],configObj['primary'],
- archive=configObj['archive'],sciext=configObj['sciext'],
- logging=configObj['logging'])
- else:
- # Call function with properly interpreted input parameters
- # restore_from_headerlet(filename, hdrname=None, hdrext=None,
- # archive=True, force=False)
- headerlet.restore_from_headerlet(configObj['filename'],
- hdrname=configObj['hdrname'],hdrext=configObj['hdrext'],
- archive=configObj['archive'], force=configObj['force'],
- logging=configObj['logging'])
-
diff --git a/lib/stwcs/gui/updatewcs.py b/lib/stwcs/gui/updatewcs.py
deleted file mode 100644
index 3dacb67..0000000
--- a/lib/stwcs/gui/updatewcs.py
+++ /dev/null
@@ -1,90 +0,0 @@
-from __future__ import print_function
-import os
-
-from astropy.io import fits
-from stsci.tools import parseinput
-from stsci.tools import fileutil
-from stsci.tools import teal
-import stwcs
-from stwcs import updatewcs
-from stwcs.wcsutil import convertwcs
-
-allowed_corr_dict = {'vacorr':'VACorr','tddcorr':'TDDCorr','npolcorr':'NPOLCorr','d2imcorr':'DET2IMCorr'}
-
-__taskname__ = __name__.split('.')[-1] # needed for help string
-__package__ = updatewcs.__name__
-__version__ = stwcs.__version__
-
-#
-#### Interfaces used by TEAL
-#
-def getHelpAsString(docstring=False):
- """
- return useful help from a file in the script directory called __taskname__.help
- """
- install_dir = os.path.dirname(__file__)
- htmlfile = os.path.join(install_dir,'htmlhelp',__taskname__+'.html')
- helpfile = os.path.join(install_dir,__taskname__+'.help')
- if docstring or (not docstring and not os.path.exists(htmlfile)):
- helpString = __taskname__+' Version '+__version__+'\n\n'
- if os.path.exists(helpfile):
- helpString += teal.getHelpFileAsString(__taskname__,__file__)
- else:
- helpString += eval('.'.join([__package__,__taskname__,'__doc__']))
-
- else:
- helpString = 'file://'+htmlfile
-
- return helpString
-
-def run(configObj=None):
-
- # Interpret primary parameters from configObj instance
- extname = configObj['extname']
- input = configObj['input']
-
- # create dictionary of remaining parameters, deleting extraneous ones
- # such as those above
- cdict = configObj.dict()
- # remove any rules defined for the TEAL interface
- if "_RULES_" in cdict: del cdict['_RULES_']
- del cdict['_task_name_']
- del cdict['input']
- del cdict['extname']
-
- # parse input
- input,altfiles = parseinput.parseinput(configObj['input'])
-
- # Insure that all input files have a correctly archived
- # set of OPUS WCS keywords
- # Legacy files from OTFR, like all WFPC2 data from OTFR, will only
- # have the OPUS WCS keywords archived using a prefix of 'O'
- # These keywords need to be converted to the Paper I alternate WCS
- # standard using a wcskey (suffix) of 'O'
- # If an alternate WCS with wcskey='O' already exists, this will copy
- # the values from the old prefix-'O' WCS keywords to insure the correct
- # OPUS keyword values get archived for use with updatewcs.
- #
- for file in input:
- # Check to insure that there is a valid reference file to be used
- idctab = fits.getval(file, 'idctab')
- if not os.path.exists(fileutil.osfn(idctab)):
- print('No valid distortion reference file ',idctab,' found in ',file,'!')
- raise ValueError
-
- # Re-define 'cdict' to only have switches for steps supported by that instrument
- # the set of supported steps are defined by the dictionary
- # updatewcs.apply_corrections.allowed_corrections
- #
- for file in input:
- # get instrument name from input file
- instr = fits.getval(file,'INSTRUME')
- # make copy of input parameters dict for this file
- fdict = cdict.copy()
- # Remove any parameter that is not part of this instrument's allowed corrections
- for step in allowed_corr_dict:
- if allowed_corr_dict[step] not in updatewcs.apply_corrections.allowed_corrections[instr]:
- fdict[step]
- # Call 'updatewcs' on correctly archived file
- updatewcs.updatewcs(file,**fdict)
-
diff --git a/lib/stwcs/gui/write_headerlet.py b/lib/stwcs/gui/write_headerlet.py
deleted file mode 100644
index e18bed8..0000000
--- a/lib/stwcs/gui/write_headerlet.py
+++ /dev/null
@@ -1,80 +0,0 @@
-from __future__ import print_function
-import os
-
-from stsci.tools import teal
-from stsci.tools import parseinput
-
-import stwcs
-from stwcs.wcsutil import headerlet
-
-__taskname__ = __name__.split('.')[-1] # needed for help string
-__package__ = headerlet.__name__
-__version__ = stwcs.__version__
-#
-#### Interfaces used by TEAL
-#
-def getHelpAsString(docstring=False):
- """
- return useful help from a file in the script directory called __taskname__.help
- """
- install_dir = os.path.dirname(__file__)
- htmlfile = os.path.join(install_dir,'htmlhelp',__taskname__+'.html')
- helpfile = os.path.join(install_dir,__taskname__+'.help')
- if docstring or (not docstring and not os.path.exists(htmlfile)):
- helpString = __taskname__+' Version '+__version__+'\n\n'
- if os.path.exists(helpfile):
- helpString += teal.getHelpFileAsString(__taskname__,__file__)
- else:
- helpString += headerlet.write_headerlet.__doc__
-
- else:
- helpString = 'file://'+htmlfile
-
- return helpString
-
-def run(configObj=None):
- flist,oname = parseinput.parseinput(configObj['filename'])
- if len(flist) == 0:
- print('='*60)
- print('ERROR:')
- print(' No valid "filename" parameter value provided!')
- print(' Please check the working directory and restart this task.')
- print('='*60)
- return
-
- if configObj['hdrname'] in ['',' ','INDEF']:
- print('='*60)
- print('ERROR:')
- print(' No valid "hdrname" parameter value provided!')
- print(' Please restart this task and provide a value for this parameter.')
- print('='*60)
- return
-
- if configObj['output'] in ['',' ','INDEF']:
- configObj['output'] = None
-
- str_kw = ['wcsname','destim','sipname','npolfile','d2imfile',
- 'descrip','history','author','output','catalog']
-
- # create dictionary of remaining parameters, deleting extraneous ones
- # such as those above
- cdict = configObj.dict()
- # remove any rules defined for the TEAL interface
- if "_RULES_" in cdict: del cdict['_RULES_']
- del cdict['_task_name_']
- del cdict['filename']
- del cdict['hdrname']
-
- # Convert blank string input as None
- for kw in str_kw:
- if cdict[kw] == '': cdict[kw] = None
- if cdict['wcskey'].lower() == 'primary': cdict['wcskey'] = ' '
-
- # Call function with properly interpreted input parameters
- # Syntax: write_headerlet(filename, hdrname, output, sciext='SCI',
- # wcsname=None, wcskey=None, destim=None,
- # sipname=None, npolfile=None, d2imfile=None,
- # author=None, descrip=None, history=None,
- # attach=True, clobber=False)
- headerlet.write_headerlet(flist, configObj['hdrname'],
- **cdict)
diff --git a/lib/stwcs/updatewcs/__init__.py b/lib/stwcs/updatewcs/__init__.py
deleted file mode 100644
index eb5e850..0000000
--- a/lib/stwcs/updatewcs/__init__.py
+++ /dev/null
@@ -1,376 +0,0 @@
-from __future__ import absolute_import, division, print_function # confidence high
-
-from astropy.io import fits
-from stwcs import wcsutil
-from stwcs.wcsutil import HSTWCS
-import stwcs
-
-from astropy import wcs as pywcs
-import astropy
-
-from . import utils, corrections, makewcs
-from . import npol, det2im
-from stsci.tools import parseinput, fileutil
-from . import apply_corrections
-
-import time
-import logging
-logger = logging.getLogger('stwcs.updatewcs')
-
-import atexit
-atexit.register(logging.shutdown)
-
-#Note: The order of corrections is important
-
-def updatewcs(input, vacorr=True, tddcorr=True, npolcorr=True, d2imcorr=True,
- checkfiles=True, verbose=False):
- """
-
- Updates HST science files with the best available calibration information.
- This allows users to retrieve from the archive self contained science files
- which do not require additional reference files.
-
- Basic WCS keywords are updated in the process and new keywords (following WCS
- Paper IV and the SIP convention) as well as new extensions are added to the science files.
-
-
- Example
- -------
- >>>from stwcs import updatewcs
- >>>updatewcs.updatewcs(filename)
-
- Dependencies
- ------------
- `stsci.tools`
- `astropy.io.fits`
- `astropy.wcs`
-
- Parameters
- ----------
- input: a python list of file names or a string (wild card characters allowed)
- input files may be in fits, geis or waiver fits format
- vacorr: boolean
- If True, vecocity aberration correction will be applied
- tddcorr: boolean
- If True, time dependent distortion correction will be applied
- npolcorr: boolean
- If True, a Lookup table distortion will be applied
- d2imcorr: boolean
- If True, detector to image correction will be applied
- checkfiles: boolean
- If True, the format of the input files will be checked,
- geis and waiver fits files will be converted to MEF format.
- Default value is True for standalone mode.
- """
- if verbose == False:
- logger.setLevel(100)
- else:
- formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
- log_filename = 'stwcs.log'
- fh = logging.FileHandler(log_filename, mode='w')
- fh.setLevel(logging.DEBUG)
- fh.setFormatter(formatter)
- logger.addHandler(fh)
- logger.setLevel(verbose)
- args = "vacorr=%s, tddcorr=%s, npolcorr=%s, d2imcorr=%s, checkfiles=%s, \
- " % (str(vacorr), str(tddcorr), str(npolcorr),
- str(d2imcorr), str(checkfiles))
- logger.info('\n\tStarting UPDATEWCS: %s', time.asctime())
-
- files = parseinput.parseinput(input)[0]
- logger.info("\n\tInput files: %s, " % [i for i in files])
- logger.info("\n\tInput arguments: %s" %args)
- if checkfiles:
- files = checkFiles(files)
- if not files:
- print('No valid input, quitting ...\n')
- return
-
- for f in files:
- acorr = apply_corrections.setCorrections(f, vacorr=vacorr, \
- tddcorr=tddcorr,npolcorr=npolcorr, d2imcorr=d2imcorr)
- if 'MakeWCS' in acorr and newIDCTAB(f):
- logger.warning("\n\tNew IDCTAB file detected. All current WCSs will be deleted")
- cleanWCS(f)
-
- makecorr(f, acorr)
-
- return files
-
-def makecorr(fname, allowed_corr):
- """
- Purpose
- =======
- Applies corrections to the WCS of a single file
-
- :Parameters:
- `fname`: string
- file name
- `acorr`: list
- list of corrections to be applied
- """
- logger.info("Allowed corrections: {0}".format(allowed_corr))
- f = fits.open(fname, mode='update')
- #Determine the reference chip and create the reference HSTWCS object
- nrefchip, nrefext = getNrefchip(f)
- wcsutil.restoreWCS(f, nrefext, wcskey='O')
- rwcs = HSTWCS(fobj=f, ext=nrefext)
- rwcs.readModel(update=True,header=f[nrefext].header)
-
- if 'DET2IMCorr' in allowed_corr:
- kw2update = det2im.DET2IMCorr.updateWCS(f)
- for kw in kw2update:
- f[1].header[kw] = kw2update[kw]
-
- for i in range(len(f))[1:]:
- extn = f[i]
-
- if 'extname' in extn.header:
- extname = extn.header['extname'].lower()
- if extname == 'sci':
- wcsutil.restoreWCS(f, ext=i, wcskey='O')
- sciextver = extn.header['extver']
- ref_wcs = rwcs.deepcopy()
- hdr = extn.header
- ext_wcs = HSTWCS(fobj=f, ext=i)
- ### check if it exists first!!!
- # 'O ' can be safely archived again because it has been restored first.
- wcsutil.archiveWCS(f, ext=i, wcskey="O", wcsname="OPUS", reusekey=True)
- ext_wcs.readModel(update=True,header=hdr)
- for c in allowed_corr:
- if c != 'NPOLCorr' and c != 'DET2IMCorr':
- corr_klass = corrections.__getattribute__(c)
- kw2update = corr_klass.updateWCS(ext_wcs, ref_wcs)
- for kw in kw2update:
- hdr[kw] = kw2update[kw]
- # give the primary WCS a WCSNAME value
- idcname = f[0].header.get('IDCTAB', " ")
- if idcname.strip() and 'idc.fits' in idcname:
- wname = ''.join(['IDC_',
- utils.extract_rootname(idcname,suffix='_idc')])
- else: wname = " "
- hdr['WCSNAME'] = wname
-
- elif extname in ['err', 'dq', 'sdq', 'samp', 'time']:
- cextver = extn.header['extver']
- if cextver == sciextver:
- hdr = f[('SCI',sciextver)].header
- w = pywcs.WCS(hdr, f)
- copyWCS(w, extn.header)
-
- else:
- continue
-
- if 'NPOLCorr' in allowed_corr:
- kw2update = npol.NPOLCorr.updateWCS(f)
- for kw in kw2update:
- f[1].header[kw] = kw2update[kw]
- # Finally record the version of the software which updated the WCS
- if 'HISTORY' in f[0].header:
- f[0].header.set('UPWCSVER', value=stwcs.__version__,
- comment="Version of STWCS used to updated the WCS",
- before='HISTORY')
- f[0].header.set('PYWCSVER', value=astropy.__version__,
- comment="Version of PYWCS used to updated the WCS",
- before='HISTORY')
- elif 'ASN_MTYP' in f[0].header:
- f[0].header.set('UPWCSVER', value=stwcs.__version__,
- comment="Version of STWCS used to updated the WCS",
- after='ASN_MTYP')
- f[0].header.set('PYWCSVER', value=astropy.__version__,
- comment="Version of PYWCS used to updated the WCS",
- after='ASN_MTYP')
- else:
- # Find index of last non-blank card, and insert this new keyword after that card
- for i in range(len(f[0].header) - 1, 0, -1):
- if f[0].header[i].strip() != '':
- break
- f[0].header.set('UPWCSVER', stwcs.__version__,
- "Version of STWCS used to updated the WCS",
- after=i)
- f[0].header.set('PYWCSVER', astropy.__version__,
- "Version of PYWCS used to updated the WCS",
- after=i)
- # add additional keywords to be used by headerlets
- distdict = utils.construct_distname(f,rwcs)
- f[0].header['DISTNAME'] = distdict['DISTNAME']
- f[0].header['SIPNAME'] = distdict['SIPNAME']
- # Make sure NEXTEND keyword remains accurate
- f[0].header['NEXTEND'] = len(f)-1
- f.close()
-
-def copyWCS(w, ehdr):
- """
- This is a convenience function to copy a WCS object
- to a header as a primary WCS. It is used only to copy the
- WCS of the 'SCI' extension to the headers of 'ERR', 'DQ', 'SDQ',
- 'TIME' or 'SAMP' extensions.
- """
- hwcs = w.to_header()
-
- if w.wcs.has_cd():
- wcsutil.pc2cd(hwcs)
- for k in hwcs.keys():
- key = k[:7]
- ehdr[key] = hwcs[k]
-
-def getNrefchip(fobj):
- """
-
- Finds which FITS extension holds the reference chip.
-
- The reference chip has EXTNAME='SCI', can be in any extension and
- is instrument specific. This functions provides mappings between
- sci extensions, chips and fits extensions.
- In the case of a subarray when the reference chip is missing, the
- first 'SCI' extension is the reference chip.
-
- Parameters
- ----------
- fobj: `astropy.io.fits.HDUList` object
- """
- nrefext = 1
- nrefchip = 1
- instrument = fobj[0].header['INSTRUME']
-
- if instrument == 'WFPC2':
- chipkw = 'DETECTOR'
- extvers = [("SCI",img.header['EXTVER']) for img in
- fobj[1:] if img.header['EXTNAME'].lower()=='sci']
- detectors = [img.header[chipkw] for img in fobj[1:] if
- img.header['EXTNAME'].lower()=='sci']
- fitsext = [i for i in range(len(fobj))[1:] if
- fobj[i].header['EXTNAME'].lower()=='sci']
- det2ext=dict(list(zip(detectors, extvers)))
- ext2det=dict(list(zip(extvers, detectors)))
- ext2fitsext=dict(list(zip(extvers, fitsext)))
-
- if 3 not in detectors:
- nrefchip = ext2det.pop(extvers[0])
- nrefext = ext2fitsext.pop(extvers[0])
- else:
- nrefchip = 3
- extname = det2ext.pop(nrefchip)
- nrefext = ext2fitsext.pop(extname)
-
- elif (instrument == 'ACS' and fobj[0].header['DETECTOR'] == 'WFC') or \
- (instrument == 'WFC3' and fobj[0].header['DETECTOR'] == 'UVIS'):
- chipkw = 'CCDCHIP'
- extvers = [("SCI",img.header['EXTVER']) for img in
- fobj[1:] if img.header['EXTNAME'].lower()=='sci']
- detectors = [img.header[chipkw] for img in fobj[1:] if
- img.header['EXTNAME'].lower()=='sci']
- fitsext = [i for i in range(len(fobj))[1:] if
- fobj[i].header['EXTNAME'].lower()=='sci']
- det2ext=dict(list(zip(detectors, extvers)))
- ext2det=dict(list(zip(extvers, detectors)))
- ext2fitsext=dict(list(zip(extvers, fitsext)))
-
- if 2 not in detectors:
- nrefchip = ext2det.pop(extvers[0])
- nrefext = ext2fitsext.pop(extvers[0])
- else:
- nrefchip = 2
- extname = det2ext.pop(nrefchip)
- nrefext = ext2fitsext.pop(extname)
- else:
- for i in range(len(fobj)):
- extname = fobj[i].header.get('EXTNAME', None)
- if extname != None and extname.lower == 'sci':
- nrefext = i
- break
-
- return nrefchip, nrefext
-
-def checkFiles(input):
- """
- Checks that input files are in the correct format.
- Converts geis and waiver fits files to multiextension fits.
- """
- from stsci.tools.check_files import geis2mef, waiver2mef, checkFiles
- logger.info("\n\tChecking files %s" % input)
- removed_files = []
- newfiles = []
- if not isinstance(input, list):
- input=[input]
-
- for file in input:
- try:
- imgfits,imgtype = fileutil.isFits(file)
- except IOError:
- logger.warning( "\n\tFile %s could not be found, removing it from the input list.\n" %file)
- removed_files.append(file)
- continue
- # Check for existence of waiver FITS input, and quit if found.
- # Or should we print a warning and continue but not use that file
- if imgfits:
- if imgtype == 'waiver':
- newfilename = waiver2mef(file, convert_dq=True)
- if newfilename == None:
- logger.warning("\n\tRemoving file %s from input list - could not convert waiver to mef" %file)
- removed_files.append(file)
- else:
- newfiles.append(newfilename)
- else:
- newfiles.append(file)
-
- # If a GEIS image is provided as input, create a new MEF file with
- # a name generated using 'buildFITSName()'
- # Convert the corresponding data quality file if present
- if not imgfits:
- newfilename = geis2mef(file, convert_dq=True)
- if newfilename == None:
- logger.warning("\n\tRemoving file %s from input list - could not convert geis to mef" %file)
- removed_files.append(file)
- else:
- newfiles.append(newfilename)
- if removed_files:
- logger.warning('\n\tThe following files will be removed from the list of files to be processed %s' % removed_files)
-
- newfiles = checkFiles(newfiles)[0]
- logger.info("\n\tThese files passed the input check and will be processed: %s" % newfiles)
- return newfiles
-
-def newIDCTAB(fname):
- #When this is called we know there's a kw IDCTAB in the header
- hdul = fits.open(fname)
- idctab = fileutil.osfn(hdul[0].header['IDCTAB'])
- try:
- #check for the presence of IDCTAB in the first extension
- oldidctab = fileutil.osfn(hdul[1].header['IDCTAB'])
- except KeyError:
- return False
- if idctab == oldidctab:
- return False
- else:
- return True
-
-def cleanWCS(fname):
- # A new IDCTAB means all previously computed WCS's are invalid
- # We are deleting all of them except the original OPUS WCS.
- f = fits.open(fname, mode='update')
- keys = wcsutil.wcskeys(f[1].header)
- # Remove the primary WCS from the list
- try:
- keys.remove(' ')
- except ValueError:
- pass
- fext = list(range(1, len(f)))
- for key in keys:
- try:
- wcsutil.deleteWCS(fname, ext=fext, wcskey=key)
- except KeyError:
- # Some extensions don't have the alternate (or any) WCS keywords
- continue
-
-def getCorrections(instrument):
- """
- Print corrections available for an instrument
-
- :Parameters:
- `instrument`: string, one of 'WFPC2', 'NICMOS', 'STIS', 'ACS', 'WFC3'
- """
- acorr = apply_corrections.allowed_corrections[instrument]
-
- print("The following corrections will be performed for instrument %s\n" % instrument)
- for c in acorr: print(c,': ' , apply_corrections.cnames[c])
diff --git a/lib/stwcs/updatewcs/apply_corrections.py b/lib/stwcs/updatewcs/apply_corrections.py
deleted file mode 100644
index 86b623a..0000000
--- a/lib/stwcs/updatewcs/apply_corrections.py
+++ /dev/null
@@ -1,248 +0,0 @@
-from __future__ import division, print_function # confidence high
-
-import os
-from astropy.io import fits
-import time
-from stsci.tools import fileutil
-import os.path
-from stwcs.wcsutil import altwcs
-from . import utils
-from . import wfpc2_dgeo
-
-import logging
-logger = logging.getLogger("stwcs.updatewcs.apply_corrections")
-
-#Note: The order of corrections is important
-
-
-# A dictionary which lists the allowed corrections for each instrument.
-# These are the default corrections applied also in the pipeline.
-
-allowed_corrections={'WFPC2': ['DET2IMCorr', 'MakeWCS','CompSIP', 'VACorr'],
- 'ACS': ['DET2IMCorr', 'TDDCorr', 'MakeWCS', 'CompSIP','VACorr', 'NPOLCorr'],
- 'STIS': ['MakeWCS', 'CompSIP','VACorr'],
- 'NICMOS': ['MakeWCS', 'CompSIP','VACorr'],
- 'WFC3': ['DET2IMCorr', 'MakeWCS', 'CompSIP','VACorr', 'NPOLCorr'],
- }
-
-cnames = {'DET2IMCorr': 'Detector to Image Correction',
- 'TDDCorr': 'Time Dependent Distortion Correction',
- 'MakeWCS': 'Recalculate basic WCS keywords based on the distortion model',
- 'CompSIP': 'Given IDCTAB distortion model calculate the SIP coefficients',
- 'VACorr': 'Velocity Aberration Correction',
- 'NPOLCorr': 'Lookup Table Distortion'
- }
-
-def setCorrections(fname, vacorr=True, tddcorr=True, npolcorr=True, d2imcorr=True):
- """
- Creates a list of corrections to be applied to a file
- based on user input paramters and allowed corrections
- for the instrument.
- """
- instrument = fits.getval(fname, 'INSTRUME')
- # make a copy of this list !
- acorr = allowed_corrections[instrument][:]
-
- # For WFPC2 images, the old-style DGEOFILE needs to be
- # converted on-the-fly into a proper D2IMFILE here...
- if instrument == 'WFPC2':
- # check for DGEOFILE, and convert it to D2IMFILE if found
- d2imfile = wfpc2_dgeo.update_wfpc2_d2geofile(fname)
- # Check if idctab is present on disk
- # If kw IDCTAB is present in the header but the file is
- # not found on disk, do not run TDDCorr, MakeCWS and CompSIP
- if not foundIDCTAB(fname):
- if 'TDDCorr' in acorr: acorr.remove('TDDCorr')
- if 'MakeWCS' in acorr: acorr.remove('MakeWCS')
- if 'CompSIP' in acorr: acorr.remove('CompSIP')
-
- if 'VACorr' in acorr and vacorr==False: acorr.remove('VACorr')
- if 'TDDCorr' in acorr:
- tddcorr = applyTDDCorr(fname, tddcorr)
- if tddcorr == False: acorr.remove('TDDCorr')
-
- if 'NPOLCorr' in acorr:
- npolcorr = applyNpolCorr(fname, npolcorr)
- if npolcorr == False: acorr.remove('NPOLCorr')
- if 'DET2IMCorr' in acorr:
- d2imcorr = applyD2ImCorr(fname, d2imcorr)
- if d2imcorr == False: acorr.remove('DET2IMCorr')
- logger.info("\n\tCorrections to be applied to %s: %s " % (fname, str(acorr)))
- return acorr
-
-
-def foundIDCTAB(fname):
- """
- This functions looks for an "IDCTAB" keyword in the primary header.
-
- Returns
- -------
- status : bool
- If False : MakeWCS, CompSIP and TDDCorr should not be applied.
- If True : there's no restriction on corrections, they all should be applied.
-
- Raises
- ------
- IOError : If IDCTAB file not found on disk.
- """
-
- try:
- idctab = fits.getval(fname, 'IDCTAB').strip()
- if idctab == 'N/A' or idctab == "":
- return False
- except KeyError:
- return False
- idctab = fileutil.osfn(idctab)
- if os.path.exists(idctab):
- return True
- else:
- raise IOError("IDCTAB file {0} not found".format(idctab))
-
-
-def applyTDDCorr(fname, utddcorr):
- """
- The default value of tddcorr for all ACS images is True.
- This correction will be performed if all conditions below are True:
- - the user did not turn it off on the command line
- - the detector is WFC
- - the idc table specified in the primary header is available.
- """
-
- phdr = fits.getheader(fname)
- instrument = phdr['INSTRUME']
- try:
- detector = phdr['DETECTOR']
- except KeyError:
- detector = None
- try:
- tddswitch = phdr['TDDCORR']
- except KeyError:
- tddswitch = 'PERFORM'
-
- if instrument == 'ACS' and detector == 'WFC' and utddcorr == True and tddswitch == 'PERFORM':
- tddcorr = True
- try:
- idctab = phdr['IDCTAB']
- except KeyError:
- tddcorr = False
- #print "***IDCTAB keyword not found - not applying TDD correction***\n"
- if os.path.exists(fileutil.osfn(idctab)):
- tddcorr = True
- else:
- tddcorr = False
- #print "***IDCTAB file not found - not applying TDD correction***\n"
- else:
- tddcorr = False
- return tddcorr
-
-def applyNpolCorr(fname, unpolcorr):
- """
- Determines whether non-polynomial distortion lookup tables should be added
- as extensions to the science file based on the 'NPOLFILE' keyword in the
- primary header and NPOLEXT kw in the first extension.
- This is a default correction and will always run in the pipeline.
- The file used to generate the extensions is
- recorded in the NPOLEXT keyword in the first science extension.
- If 'NPOLFILE' in the primary header is different from 'NPOLEXT' in the
- extension header and the file exists on disk and is a 'new type' npolfile,
- then the lookup tables will be updated as 'WCSDVARR' extensions.
- """
- applyNPOLCorr = True
- try:
- # get NPOLFILE kw from primary header
- fnpol0 = fits.getval(fname, 'NPOLFILE')
- if fnpol0 == 'N/A':
- utils.remove_distortion(fname, "NPOLFILE")
- return False
- fnpol0 = fileutil.osfn(fnpol0)
- if not fileutil.findFile(fnpol0):
- msg = """\n\tKw "NPOLFILE" exists in primary header but file %s not found
- Non-polynomial distortion correction will not be applied\n
- """ % fnpol0
- logger.critical(msg)
- raise IOError("NPOLFILE {0} not found".format(fnpol0))
- try:
- # get NPOLEXT kw from first extension header
- fnpol1 = fits.getval(fname, 'NPOLEXT', ext=1)
- fnpol1 = fileutil.osfn(fnpol1)
- if fnpol1 and fileutil.findFile(fnpol1):
- if fnpol0 != fnpol1:
- applyNPOLCorr = True
- else:
- msg = """\n\tNPOLEXT with the same value as NPOLFILE found in first extension.
- NPOL correction will not be applied."""
- logger.info(msg)
- applyNPOLCorr = False
- else:
- # npl file defined in first extension may not be found
- # but if a valid kw exists in the primary header, non-polynomial
- #distortion correction should be applied.
- applyNPOLCorr = True
- except KeyError:
- # the case of "NPOLFILE" kw present in primary header but "NPOLEXT" missing
- # in first extension header
- applyNPOLCorr = True
- except KeyError:
- logger.info('\n\t"NPOLFILE" keyword not found in primary header')
- applyNPOLCorr = False
- return applyNPOLCorr
-
- if isOldStyleDGEO(fname, fnpol0):
- applyNPOLCorr = False
- return (applyNPOLCorr and unpolcorr)
-
-def isOldStyleDGEO(fname, dgname):
- # checks if the file defined in a NPOLFILE kw is a full size
- # (old style) image
-
- sci_hdr = fits.getheader(fname, ext=1)
- dgeo_hdr = fits.getheader(dgname, ext=1)
- sci_naxis1 = sci_hdr['NAXIS1']
- sci_naxis2 = sci_hdr['NAXIS2']
- dg_naxis1 = dgeo_hdr['NAXIS1']
- dg_naxis2 = dgeo_hdr['NAXIS2']
- if sci_naxis1 <= dg_naxis1 or sci_naxis2 <= dg_naxis2:
- msg = """\n\tOnly full size (old style) DGEO file was found.\n
- Non-polynomial distortion correction will not be applied."""
- logger.critical(msg)
- return True
- else:
- return False
-
-def applyD2ImCorr(fname, d2imcorr):
- applyD2IMCorr = True
- try:
- # get D2IMFILE kw from primary header
- fd2im0 = fits.getval(fname, 'D2IMFILE')
- if fd2im0 == 'N/A':
- utils.remove_distortion(fname, "D2IMFILE")
- return False
- fd2im0 = fileutil.osfn(fd2im0)
- if not fileutil.findFile(fd2im0):
- msg = """\n\tKw D2IMFILE exists in primary header but file %s not found\n
- Detector to image correction will not be applied\n""" % fd2im0
- logger.critical(msg)
- print(msg)
- raise IOError("D2IMFILE {0} not found".format(fd2im0))
- try:
- # get D2IMEXT kw from first extension header
- fd2imext = fits.getval(fname, 'D2IMEXT', ext=1)
- fd2imext = fileutil.osfn(fd2imext)
- if fd2imext and fileutil.findFile(fd2imext):
- if fd2im0 != fd2imext:
- applyD2IMCorr = True
- else:
- applyD2IMCorr = False
- else:
- # D2IM file defined in first extension may not be found
- # but if a valid kw exists in the primary header,
- # detector to image correction should be applied.
- applyD2IMCorr = True
- except KeyError:
- # the case of D2IMFILE kw present in primary header but D2IMEXT missing
- # in first extension header
- applyD2IMCorr = True
- except KeyError:
- print('D2IMFILE keyword not found in primary header')
- applyD2IMCorr = False
- return applyD2IMCorr
diff --git a/lib/stwcs/updatewcs/corrections.py b/lib/stwcs/updatewcs/corrections.py
deleted file mode 100644
index d3641eb..0000000
--- a/lib/stwcs/updatewcs/corrections.py
+++ /dev/null
@@ -1,326 +0,0 @@
-from __future__ import division, print_function # confidence high
-
-import copy
-import datetime
-import logging, time
-import numpy as np
-from numpy import linalg
-from stsci.tools import fileutil
-
-from . import npol
-from . import makewcs
-from .utils import diff_angles
-
-logger=logging.getLogger('stwcs.updatewcs.corrections')
-
-MakeWCS = makewcs.MakeWCS
-NPOLCorr = npol.NPOLCorr
-
-class TDDCorr(object):
- """
- Apply time dependent distortion correction to distortion coefficients and basic
- WCS keywords. This correction **must** be done before any other WCS correction.
-
- Parameters
- ----------
- ext_wcs: HSTWCS object
- An HSTWCS object to be modified
- ref_wcs: HSTWCS object
- A reference HSTWCS object
-
- Notes
- -----
- Compute the ACS/WFC time dependent distortion terms as described
- in [1]_ and apply the correction to the WCS of the observation.
-
- The model coefficients are stored in the primary header of the IDCTAB.
- :math:`D_{ref}` is the reference date. The computed corrections are saved
- in the science extension header as TDDALPHA and TDDBETA keywords.
-
- .. math:: TDDALPHA = A_{0} + {A_{1}*(obsdate - D_{ref})}
-
- .. math:: TDDBETA = B_{0} + B_{1}*(obsdate - D_{ref})
-
-
- The time dependent distortion affects the IDCTAB coefficients, and the
- relative location of the two chips. Because the linear order IDCTAB
- coefficients ar eused in the computatuion of the NPOL extensions,
- the TDD correction affects all components of the distortion model.
-
- Application of TDD to the IDCTAB polynomial coefficients:
- The TDD model is computed in Jay's frame, while the IDCTAB coefficients
- are in the HST V2/V3 frame. The coefficients are transformed to Jay's frame,
- TDD is applied and they are transformed back to the V2/V3 frame. This
- correction is performed in this class.
-
- Application of TDD to the relative location of the two chips is
- done in `makewcs`.
-
- References
- ----------
- .. [1] Jay Anderson, "Variation of the Distortion Solution of the WFC", ACS ISR 2007-08.
-
- """
- def updateWCS(cls, ext_wcs, ref_wcs):
- """
- - Calculates alpha and beta for ACS/WFC data.
- - Writes 2 new kw to the extension header: TDDALPHA and TDDBETA
- """
- logger.info("\n\tStarting TDDCorr: %s" % time.asctime())
- ext_wcs.idcmodel.ocx = copy.deepcopy(ext_wcs.idcmodel.cx)
- ext_wcs.idcmodel.ocy = copy.deepcopy(ext_wcs.idcmodel.cy)
-
- newkw = {'TDDALPHA': None, 'TDDBETA':None,
- 'OCX10':ext_wcs.idcmodel.ocx[1,0],
- 'OCX11':ext_wcs.idcmodel.ocx[1,1],
- 'OCY10':ext_wcs.idcmodel.ocy[1,0],
- 'OCY11':ext_wcs.idcmodel.ocy[1,1],
- 'TDD_CTA':None, 'TDD_CTB':None,
- 'TDD_CYA':None, 'TDD_CYB':None,
- 'TDD_CXA':None, 'TDD_CXB':None}
-
- if ext_wcs.idcmodel.refpix['skew_coeffs']is not None and \
- ext_wcs.idcmodel.refpix['skew_coeffs']['TDD_CTB'] is not None:
- cls.apply_tdd2idc2015(ref_wcs)
- cls.apply_tdd2idc2015(ext_wcs)
-
- newkw.update({
- 'TDD_CTA':ext_wcs.idcmodel.refpix['skew_coeffs']['TDD_CTA'],
- 'TDD_CYA':ext_wcs.idcmodel.refpix['skew_coeffs']['TDD_CYA'],
- 'TDD_CXA':ext_wcs.idcmodel.refpix['skew_coeffs']['TDD_CXA'],
- 'TDD_CTB':ext_wcs.idcmodel.refpix['skew_coeffs']['TDD_CTB'],
- 'TDD_CYB':ext_wcs.idcmodel.refpix['skew_coeffs']['TDD_CYB'],
- 'TDD_CXB':ext_wcs.idcmodel.refpix['skew_coeffs']['TDD_CXB']})
-
- elif ext_wcs.idcmodel.refpix['skew_coeffs']is not None and \
- ext_wcs.idcmodel.refpix['skew_coeffs']['TDD_CY_BETA'] is not None:
- logger.info("\n\t Applying 2014-calibrated TDD: %s" % time.asctime())
- # We have 2014-calibrated TDD, not J.A.-style TDD
- cls.apply_tdd2idc2(ref_wcs)
- cls.apply_tdd2idc2(ext_wcs)
- newkw.update({'TDD_CYA':ext_wcs.idcmodel.refpix['skew_coeffs']['TDD_CY_ALPHA'],
- 'TDD_CYB':ext_wcs.idcmodel.refpix['skew_coeffs']['TDD_CY_BETA'],
- 'TDD_CXA':ext_wcs.idcmodel.refpix['skew_coeffs']['TDD_CX_ALPHA'],
- 'TDD_CXB':ext_wcs.idcmodel.refpix['skew_coeffs']['TDD_CX_BETA']})
-
- else:
- alpha, beta = cls.compute_alpha_beta(ext_wcs)
- cls.apply_tdd2idc(ref_wcs, alpha, beta)
- cls.apply_tdd2idc(ext_wcs, alpha, beta)
- ext_wcs.idcmodel.refpix['TDDALPHA'] = alpha
- ext_wcs.idcmodel.refpix['TDDBETA'] = beta
- ref_wcs.idcmodel.refpix['TDDALPHA'] = alpha
- ref_wcs.idcmodel.refpix['TDDBETA'] = beta
-
- newkw.update( {'TDDALPHA': alpha, 'TDDBETA':beta} )
-
- return newkw
- updateWCS = classmethod(updateWCS)
-
- def apply_tdd2idc2015(cls, hwcs):
- """ Applies 2015-calibrated TDD correction to a couple of IDCTAB
- coefficients for ACS/WFC observations.
- """
- if not isinstance(hwcs.date_obs,float):
- year,month,day = hwcs.date_obs.split('-')
- rdate = datetime.datetime(int(year),int(month),int(day))
- rday = float(rdate.strftime("%j"))/365.25 + rdate.year
- else:
- rday = hwcs.date_obs
-
- skew_coeffs = hwcs.idcmodel.refpix['skew_coeffs']
- delta_date = rday - skew_coeffs['TDD_DATE']
-
- if skew_coeffs['TDD_CXB'] is not None:
- hwcs.idcmodel.cx[1,1] += skew_coeffs['TDD_CXB']*delta_date
- if skew_coeffs['TDD_CTB'] is not None:
- hwcs.idcmodel.cy[1,1] += skew_coeffs['TDD_CTB']*delta_date
- if skew_coeffs['TDD_CYB'] is not None:
- hwcs.idcmodel.cy[1,0] += skew_coeffs['TDD_CYB']*delta_date
- #print("CX[1,1]_TDD={}, CY[1,1]_TDD={}, CY[1,0]_TDD={}".format(hwcs.idcmodel.cx[1,1],hwcs.idcmodel.cy[1,1],hwcs.idcmodel.cy[1,0]))
-
- apply_tdd2idc2015 = classmethod(apply_tdd2idc2015)
-
- def apply_tdd2idc2(cls, hwcs):
- """ Applies 2014-calibrated TDD correction to single IDCTAB coefficient
- of an ACS/WFC observation.
- """
- if not isinstance(hwcs.date_obs,float):
- year,month,day = hwcs.date_obs.split('-')
- rdate = datetime.datetime(int(year),int(month),int(day))
- rday = float(rdate.strftime("%j"))/365.25 + rdate.year
- else:
- rday = hwcs.date_obs
-
- skew_coeffs = hwcs.idcmodel.refpix['skew_coeffs']
- cy_beta = skew_coeffs['TDD_CY_BETA']
- cy_alpha = skew_coeffs['TDD_CY_ALPHA']
- delta_date = rday - skew_coeffs['TDD_DATE']
- print("DELTA_DATE: {0} based on rday: {1}, TDD_DATE: {2}".format(delta_date,rday,skew_coeffs['TDD_DATE']))
-
- if cy_alpha is None:
- hwcs.idcmodel.cy[1,1] += cy_beta*delta_date
- else:
- new_beta = cy_alpha + cy_beta*delta_date
- hwcs.idcmodel.cy[1,1] = new_beta
- print("CY11: {0} based on alpha: {1}, beta: {2}".format(hwcs.idcmodel.cy[1,1],cy_alpha,cy_beta))
-
- cx_beta = skew_coeffs['TDD_CX_BETA']
- cx_alpha = skew_coeffs['TDD_CX_ALPHA']
- if cx_alpha is not None:
- new_beta = cx_alpha + cx_beta*delta_date
- hwcs.idcmodel.cx[1,1] = new_beta
- print("CX11: {0} based on alpha: {1}, beta: {2}".format(new_beta,cx_alpha,cx_beta))
-
- apply_tdd2idc2 = classmethod(apply_tdd2idc2)
-
- def apply_tdd2idc(cls, hwcs, alpha, beta):
- """
- Applies TDD to the idctab coefficients of a ACS/WFC observation.
- This should be always the first correction.
- """
- theta_v2v3 = 2.234529
- mrotp = fileutil.buildRotMatrix(theta_v2v3)
- mrotn = fileutil.buildRotMatrix(-theta_v2v3)
- tdd_mat = np.array([[1+(beta/2048.), alpha/2048.],[alpha/2048.,1-(beta/2048.)]],np.float64)
- abmat1 = np.dot(tdd_mat, mrotn)
- abmat2 = np.dot(mrotp,abmat1)
- xshape, yshape = hwcs.idcmodel.cx.shape, hwcs.idcmodel.cy.shape
- icxy = np.dot(abmat2,[hwcs.idcmodel.cx.ravel(), hwcs.idcmodel.cy.ravel()])
- hwcs.idcmodel.cx = icxy[0]
- hwcs.idcmodel.cy = icxy[1]
- hwcs.idcmodel.cx.shape = xshape
- hwcs.idcmodel.cy.shape = yshape
-
- apply_tdd2idc = classmethod(apply_tdd2idc)
-
- def compute_alpha_beta(cls, ext_wcs):
- """
- Compute the ACS time dependent distortion skew terms
- as described in ACS ISR 07-08 by J. Anderson.
-
- Jay's code only computes the alpha/beta values based on a decimal year
- with only 3 digits, so this line reproduces that when needed for comparison
- with his results.
- rday = float(('%0.3f')%rday)
-
- The zero-point terms account for the skew accumulated between
- 2002.0 and 2004.5, when the latest IDCTAB was delivered.
- alpha = 0.095 + 0.090*(rday-dday)/2.5
- beta = -0.029 - 0.030*(rday-dday)/2.5
- """
- if not isinstance(ext_wcs.date_obs,float):
- year,month,day = ext_wcs.date_obs.split('-')
- rdate = datetime.datetime(int(year),int(month),int(day))
- rday = float(rdate.strftime("%j"))/365.25 + rdate.year
- else:
- rday = ext_wcs.date_obs
-
- skew_coeffs = ext_wcs.idcmodel.refpix['skew_coeffs']
- if skew_coeffs is None:
- # Only print out warning for post-SM4 data where this may matter
- if rday > 2009.0:
- err_str = "------------------------------------------------------------------------ \n"
- err_str += "WARNING: the IDCTAB geometric distortion file specified in the image \n"
- err_str += " header did not have the time-dependent distortion coefficients. \n"
- err_str += " The pre-SM4 time-dependent skew solution will be used by default.\n"
- err_str += " Please update IDCTAB with new reference file from HST archive. \n"
- err_str += "------------------------------------------------------------------------ \n"
- print(err_str)
- # Using default pre-SM4 coefficients
- skew_coeffs = {'TDD_A':[0.095,0.090/2.5],
- 'TDD_B':[-0.029,-0.030/2.5],
- 'TDD_DATE':2004.5,'TDDORDER':1}
-
- alpha = 0
- beta = 0
- # Compute skew terms, allowing for non-linear coefficients as well
- for c in range(skew_coeffs['TDDORDER']+1):
- alpha += skew_coeffs['TDD_A'][c]* np.power((rday-skew_coeffs['TDD_DATE']),c)
- beta += skew_coeffs['TDD_B'][c]*np.power((rday-skew_coeffs['TDD_DATE']),c)
-
- return alpha,beta
- compute_alpha_beta = classmethod(compute_alpha_beta)
-
-
-class VACorr(object):
- """
- Apply velocity aberation correction to WCS keywords.
-
- Notes
- -----
- Velocity Aberration is stored in the extension header keyword 'VAFACTOR'.
- The correction is applied to the CD matrix and CRVALs.
-
- """
- def updateWCS(cls, ext_wcs, ref_wcs):
- logger.info("\n\tStarting VACorr: %s" % time.asctime())
- if ext_wcs.vafactor != 1:
- ext_wcs.wcs.cd = ext_wcs.wcs.cd * ext_wcs.vafactor
- crval0 = ref_wcs.wcs.crval[0] + ext_wcs.vafactor*diff_angles(ext_wcs.wcs.crval[0],
- ref_wcs.wcs.crval[0])
- crval1 = ref_wcs.wcs.crval[1] + ext_wcs.vafactor*diff_angles(ext_wcs.wcs.crval[1],
- ref_wcs.wcs.crval[1])
- crval = np.array([crval0,crval1])
- ext_wcs.wcs.crval = crval
- ext_wcs.wcs.set()
- else:
- pass
- kw2update={'CD1_1': ext_wcs.wcs.cd[0,0], 'CD1_2':ext_wcs.wcs.cd[0,1],
- 'CD2_1':ext_wcs.wcs.cd[1,0], 'CD2_2':ext_wcs.wcs.cd[1,1],
- 'CRVAL1':ext_wcs.wcs.crval[0], 'CRVAL2':ext_wcs.wcs.crval[1]}
- return kw2update
-
- updateWCS = classmethod(updateWCS)
-
-
-class CompSIP(object):
- """
- Compute Simple Imaging Polynomial (SIP) coefficients as defined in [2]_
- from IDC table coefficients.
-
- This class transforms the TDD corrected IDCTAB coefficients into SIP format.
- It also applies a binning factor to the coefficients if the observation was
- binned.
-
- References
- ----------
- .. [2] David Shupe, et al, "The SIP Convention of representing Distortion
- in FITS Image headers", Astronomical Data Analysis Software And Systems, ASP
- Conference Series, Vol. 347, 2005
-
- """
- def updateWCS(cls, ext_wcs, ref_wcs):
- logger.info("\n\tStarting CompSIP: %s" %time.asctime())
- kw2update = {}
- if not ext_wcs.idcmodel:
- logger.info("IDC model not found, SIP coefficient will not be computed")
- return kw2update
- order = ext_wcs.idcmodel.norder
- kw2update['A_ORDER'] = order
- kw2update['B_ORDER'] = order
- pscale = ext_wcs.idcmodel.refpix['PSCALE']
-
- cx = ext_wcs.idcmodel.cx
- cy = ext_wcs.idcmodel.cy
-
- matr = np.array([[cx[1,1],cx[1,0]], [cy[1,1],cy[1,0]]], dtype=np.float64)
- imatr = linalg.inv(matr)
- akeys1 = np.zeros((order+1,order+1), dtype=np.float64)
- bkeys1 = np.zeros((order+1,order+1), dtype=np.float64)
- for n in range(order+1):
- for m in range(order+1):
- if n >= m and n>=2:
- idcval = np.array([[cx[n,m]],[cy[n,m]]])
- sipval = np.dot(imatr, idcval)
- akeys1[m,n-m] = sipval[0]
- bkeys1[m,n-m] = sipval[1]
- Akey="A_%d_%d" % (m,n-m)
- Bkey="B_%d_%d" % (m,n-m)
- kw2update[Akey] = sipval[0,0] * ext_wcs.binned
- kw2update[Bkey] = sipval[1,0] * ext_wcs.binned
- kw2update['CTYPE1'] = 'RA---TAN-SIP'
- kw2update['CTYPE2'] = 'DEC--TAN-SIP'
- return kw2update
-
- updateWCS = classmethod(updateWCS)
diff --git a/lib/stwcs/updatewcs/det2im.py b/lib/stwcs/updatewcs/det2im.py
deleted file mode 100644
index bddb37b..0000000
--- a/lib/stwcs/updatewcs/det2im.py
+++ /dev/null
@@ -1,299 +0,0 @@
-from __future__ import absolute_import, division # confidence high
-
-import numpy as np
-from astropy.io import fits
-from stsci.tools import fileutil
-
-from . import utils
-
-import logging, time
-logger = logging.getLogger('stwcs.updatewcs.d2im')
-
-class DET2IMCorr(object):
- """
- Defines a Lookup table prior distortion correction as per WCS paper IV.
- It uses a reference file defined by the D2IMFILE (suffix 'd2im') keyword
- in the primary header.
-
- Notes
- -----
- - Using extensions in the reference file create a WCSDVARR extensions
- and add them to the science file.
- - Add record-valued keywords to the science extension header to describe
- the lookup tables.
- - Add a keyword 'D2IMEXT' to the science extension header to store
- the name of the reference file used to create the WCSDVARR extensions.
-
- If WCSDVARR extensions exist and `D2IMFILE` is different from `D2IMEXT`,
- a subsequent update will overwrite the existing extensions.
- If WCSDVARR extensions were not found in the science file, they will be added.
-
- """
-
- def updateWCS(cls, fobj):
- """
- Parameters
- ----------
- fobj: `astropy.io.fits.HDUList` object
- Science file, for which a distortion correction in a NPOLFILE is available
-
- """
- logger.info("\n\tStarting DET2IM: %s" %time.asctime())
- try:
- assert isinstance(fobj, fits.HDUList)
- except AssertionError:
- logger.exception('\n\tInput must be a fits.HDUList object')
- raise
-
- cls.applyDet2ImCorr(fobj)
- d2imfile = fobj[0].header['D2IMFILE']
-
- new_kw = {'D2IMEXT': d2imfile}
- return new_kw
-
- updateWCS = classmethod(updateWCS)
-
- def applyDet2ImCorr(cls, fobj):
- """
- For each science extension in a fits file object:
- - create a WCSDVARR extension
- - update science header
- - add/update D2IMEXT keyword
- """
- d2imfile = fileutil.osfn(fobj[0].header['D2IMFILE'])
- # Map D2IMARR EXTVER numbers to FITS extension numbers
- wcsdvarr_ind = cls.getWCSIndex(fobj)
- d2im_num_ext = 1
- for ext in fobj:
- try:
- extname = ext.header['EXTNAME'].lower()
- except KeyError:
- continue
- if extname == 'sci':
- extversion = ext.header['EXTVER']
- ccdchip = cls.get_ccdchip(fobj, extname='SCI', extver=extversion)
- header = ext.header
- # get the data arrays from the reference file
- dx, dy = cls.getData(d2imfile, ccdchip)
- # Determine EXTVER for the D2IMARR extension from the D2I file (EXTNAME, EXTVER) kw.
- # This is used to populate DPj.EXTVER kw
- for ename in zip(['DX', 'DY'], [dx, dy]):
- if ename[1] is not None:
- error_val = ename[1].max()
- cls.addSciExtKw(header, wdvarr_ver=d2im_num_ext, d2im_extname=ename[0], error_val=error_val)
- hdu = cls.createD2ImHDU(header, d2imfile=d2imfile,
- wdvarr_ver=d2im_num_ext,
- d2im_extname=ename[0],
- data=ename[1],ccdchip=ccdchip)
- if wcsdvarr_ind and d2im_num_ext in wcsdvarr_ind:
- fobj[wcsdvarr_ind[d2im_num_ext]] = hdu
- else:
- fobj.append(hdu)
- d2im_num_ext = d2im_num_ext + 1
- applyDet2ImCorr = classmethod(applyDet2ImCorr)
-
- def getWCSIndex(cls, fobj):
-
- """
- If fobj has WCSDVARR extensions:
- returns a mapping of their EXTVER kw to file object extension numbers
- if fobj does not have WCSDVARR extensions:
- an empty dictionary is returned
- """
- wcsd = {}
- for e in range(len(fobj)):
- try:
- ename = fobj[e].header['EXTNAME']
- except KeyError:
- continue
- if ename == 'D2IMARR':
- wcsd[fobj[e].header['EXTVER']] = e
- logger.debug("A map of D2IMARR extensions %s" % wcsd)
- return wcsd
-
- getWCSIndex = classmethod(getWCSIndex)
-
- def addSciExtKw(cls, hdr, wdvarr_ver=None, d2im_extname=None, error_val=0.0):
- """
- Adds kw to sci extension to define WCSDVARR lookup table extensions
-
- """
- if d2im_extname =='DX':
- j=1
- else:
- j=2
-
- d2imerror = 'D2IMERR%s' %j
- d2imdis = 'D2IMDIS%s' %j
- d2imext = 'D2IM%s.' %j + 'EXTVER'
- d2imnaxes = 'D2IM%s.' %j +'NAXES'
- d2imaxis1 = 'D2IM%s.' %j+'AXIS.1'
- d2imaxis2 = 'D2IM%s.' %j+'AXIS.2'
- keys = [d2imerror, d2imdis, d2imext, d2imnaxes, d2imaxis1, d2imaxis2]
- values = {d2imerror: error_val,
- d2imdis: 'Lookup',
- d2imext: wdvarr_ver,
- d2imnaxes: 2,
- d2imaxis1: 1,
- d2imaxis2: 2}
-
- comments = {d2imerror: 'Maximum error of NPOL correction for axis %s' % j,
- d2imdis: 'Detector to image correction type',
- d2imext: 'Version number of WCSDVARR extension containing d2im lookup table',
- d2imnaxes: 'Number of independent variables in d2im function',
- d2imaxis1: 'Axis number of the jth independent variable in a d2im function',
- d2imaxis2: 'Axis number of the jth independent variable in a d2im function'
- }
- # Look for HISTORY keywords. If present, insert new keywords before them
- before_key = 'HISTORY'
- if before_key not in hdr:
- before_key = None
-
- for key in keys:
- hdr.set(key, value=values[key], comment=comments[key], before=before_key)
-
- addSciExtKw = classmethod(addSciExtKw)
-
- def getData(cls,d2imfile, ccdchip):
- """
- Get the data arrays from the reference D2I files
- Make sure 'CCDCHIP' in the npolfile matches "CCDCHIP' in the science file.
- """
- xdata, ydata = (None, None)
- d2im = fits.open(d2imfile)
- for ext in d2im:
- d2imextname = ext.header.get('EXTNAME',"")
- d2imccdchip = ext.header.get('CCDCHIP',1)
- if d2imextname == 'DX' and d2imccdchip == ccdchip:
- xdata = ext.data.copy()
- continue
- elif d2imextname == 'DY' and d2imccdchip == ccdchip:
- ydata = ext.data.copy()
- continue
- else:
- continue
- d2im.close()
- return xdata, ydata
- getData = classmethod(getData)
-
- def createD2ImHDU(cls, sciheader, d2imfile=None, wdvarr_ver=1,
- d2im_extname=None,data = None, ccdchip=1):
- """
- Creates an HDU to be added to the file object.
- """
- hdr = cls.createD2ImHdr(sciheader, d2imfile=d2imfile,
- wdvarr_ver=wdvarr_ver, d2im_extname=d2im_extname,
- ccdchip=ccdchip)
- hdu = fits.ImageHDU(header=hdr, data=data)
- return hdu
-
- createD2ImHDU = classmethod(createD2ImHDU)
-
- def createD2ImHdr(cls, sciheader, d2imfile, wdvarr_ver, d2im_extname, ccdchip):
- """
- Creates a header for the D2IMARR extension based on the D2I reference file
- and sci extension header. The goal is to always work in image coordinates
- (also for subarrays and binned images). The WCS for the D2IMARR extension
- is such that a full size d2im table is created and then shifted or scaled
- if the science image is a subarray or binned image.
- """
- d2im = fits.open(d2imfile)
- d2im_phdr = d2im[0].header
- for ext in d2im:
- try:
- d2imextname = ext.header['EXTNAME']
- d2imextver = ext.header['EXTVER']
- except KeyError:
- continue
- d2imccdchip = cls.get_ccdchip(d2im, extname=d2imextname, extver=d2imextver)
- if d2imextname == d2im_extname and d2imccdchip == ccdchip:
- d2im_header = ext.header
- break
- else:
- continue
- d2im.close()
-
- naxis = d2im[1].header['NAXIS']
- ccdchip = d2imextname
-
- kw = { 'NAXIS': 'Size of the axis',
- 'CDELT': 'Coordinate increment along axis',
- 'CRPIX': 'Coordinate system reference pixel',
- 'CRVAL': 'Coordinate system value at reference pixel',
- }
-
- kw_comm1 = {}
- kw_val1 = {}
- for key in kw.keys():
- for i in range(1, naxis+1):
- si = str(i)
- kw_comm1[key+si] = kw[key]
-
- for i in range(1, naxis+1):
- si = str(i)
- kw_val1['NAXIS'+si] = d2im_header.get('NAXIS'+si)
- kw_val1['CDELT'+si] = d2im_header.get('CDELT'+si, 1.0) * sciheader.get('LTM'+si+'_'+si, 1)
- kw_val1['CRPIX'+si] = d2im_header.get('CRPIX'+si, 0.0)
- kw_val1['CRVAL'+si] = (d2im_header.get('CRVAL'+si, 0.0) - \
- sciheader.get('LTV'+str(i), 0))
- kw_comm0 = {'XTENSION': 'Image extension',
- 'BITPIX': 'IEEE floating point',
- 'NAXIS': 'Number of axes',
- 'EXTNAME': 'WCS distortion array',
- 'EXTVER': 'Distortion array version number',
- 'PCOUNT': 'Special data area of size 0',
- 'GCOUNT': 'One data group',
- }
-
- kw_val0 = { 'XTENSION': 'IMAGE',
- 'BITPIX': -32,
- 'NAXIS': naxis,
- 'EXTNAME': 'D2IMARR',
- 'EXTVER': wdvarr_ver,
- 'PCOUNT': 0,
- 'GCOUNT': 1,
- 'CCDCHIP': ccdchip,
- }
- cdl = []
- for key in kw_comm0.keys():
- cdl.append((key, kw_val0[key], kw_comm0[key]))
- for key in kw_comm1.keys():
- cdl.append((key, kw_val1[key], kw_comm1[key]))
- # Now add keywords from NPOLFILE header to document source of calibration
- # include all keywords after and including 'FILENAME' from header
- start_indx = -1
- end_indx = 0
- for i, c in enumerate(d2im_phdr):
- if c == 'FILENAME':
- start_indx = i
- if c == '': # remove blanks from end of header
- end_indx = i+1
- break
- if start_indx >= 0:
- for card in d2im_phdr.cards[start_indx:end_indx]:
- cdl.append(card)
-
- hdr = fits.Header(cards=cdl)
-
- return hdr
-
- createD2ImHdr = classmethod(createD2ImHdr)
-
- def get_ccdchip(cls, fobj, extname, extver):
- """
- Given a science file or npol file determine CCDCHIP
- """
- ccdchip = 1
- if fobj[0].header['INSTRUME'] == 'ACS' and fobj[0].header['DETECTOR'] == 'WFC':
- ccdchip = fobj[extname, extver].header['CCDCHIP']
- elif fobj[0].header['INSTRUME'] == 'WFC3' and fobj[0].header['DETECTOR'] == 'UVIS':
- ccdchip = fobj[extname, extver].header['CCDCHIP']
- elif fobj[0].header['INSTRUME'] == 'WFPC2':
- ccdchip = fobj[extname, extver].header['DETECTOR']
- elif fobj[0].header['INSTRUME'] == 'STIS':
- ccdchip = fobj[extname, extver].header['DETECTOR']
- elif fobj[0].header['INSTRUME'] == 'NICMOS':
- ccdchip = fobj[extname, extver].header['CAMERA']
- return ccdchip
-
- get_ccdchip = classmethod(get_ccdchip)
diff --git a/lib/stwcs/updatewcs/makewcs.py b/lib/stwcs/updatewcs/makewcs.py
deleted file mode 100644
index 06c6f9c..0000000
--- a/lib/stwcs/updatewcs/makewcs.py
+++ /dev/null
@@ -1,273 +0,0 @@
-from __future__ import absolute_import, division # confidence high
-
-import datetime
-
-import numpy as np
-import logging, time
-from math import sin, sqrt, pow, cos, asin, atan2,pi
-
-from stsci.tools import fileutil
-from . import utils
-
-logger = logging.getLogger(__name__)
-
-class MakeWCS(object):
- """
- Recompute basic WCS keywords based on PA_V3 and distortion model.
-
- Notes
- -----
- - Compute the reference chip WCS:
-
- -- CRVAL: transform the model XREF/YREF to the sky
- -- PA_V3 is calculated at the target position and adjusted
- for each chip orientation
- -- CD: PA_V3 and the model scale are used to cnstruct a CD matrix
-
- - Compute the second chip WCS:
-
- -- CRVAL: - the distance between the zero points of the two
- chip models on the sky
- -- CD matrix: first order coefficients are added to the components
- of this distance and transfered on the sky. The difference
- between CRVAL and these vectors is the new CD matrix for each chip.
- -- CRPIX: chip's model zero point in pixel space (XREF/YREF)
-
- - Time dependent distortion correction is applied to both chips' V2/V3 values.
-
- """
- tdd_xyref = {1: [2048, 3072], 2:[2048, 1024]}
- def updateWCS(cls, ext_wcs, ref_wcs):
- """
- recomputes the basic WCS kw
- """
- logger.info("\n\tStarting MakeWCS: %s" % time.asctime())
- if not ext_wcs.idcmodel:
- logger.info("IDC model not found, turning off Makewcs")
- return {}
- ltvoff, offshift = cls.getOffsets(ext_wcs)
-
- v23_corr = cls.zero_point_corr(ext_wcs)
- rv23_corr = cls.zero_point_corr(ref_wcs)
-
- cls.uprefwcs(ext_wcs, ref_wcs, rv23_corr, ltvoff, offshift)
- cls.upextwcs(ext_wcs, ref_wcs, v23_corr, rv23_corr, ltvoff, offshift)
-
- kw2update = {'CD1_1': ext_wcs.wcs.cd[0,0],
- 'CD1_2': ext_wcs.wcs.cd[0,1],
- 'CD2_1': ext_wcs.wcs.cd[1,0],
- 'CD2_2': ext_wcs.wcs.cd[1,1],
- 'CRVAL1': ext_wcs.wcs.crval[0],
- 'CRVAL2': ext_wcs.wcs.crval[1],
- 'CRPIX1': ext_wcs.wcs.crpix[0],
- 'CRPIX2': ext_wcs.wcs.crpix[1],
- 'IDCTAB': ext_wcs.idctab,
- 'OCX10' : ext_wcs.idcmodel.cx[1,0],
- 'OCX11' : ext_wcs.idcmodel.cx[1,1],
- 'OCY10' : ext_wcs.idcmodel.cy[1,0],
- 'OCY11' : ext_wcs.idcmodel.cy[1,1]
- }
- return kw2update
-
- updateWCS = classmethod(updateWCS)
-
- def upextwcs(cls, ext_wcs, ref_wcs, v23_corr, rv23_corr, loff, offsh):
- """
- updates an extension wcs
- """
- ltvoffx, ltvoffy = loff[0], loff[1]
- offshiftx, offshifty = offsh[0], offsh[1]
- ltv1 = ext_wcs.ltv1
- ltv2 = ext_wcs.ltv2
- if ltv1 != 0. or ltv2 != 0.:
- offsetx = ext_wcs.wcs.crpix[0] - ltv1 - ext_wcs.idcmodel.refpix['XREF']
- offsety = ext_wcs.wcs.crpix[1] - ltv2 - ext_wcs.idcmodel.refpix['YREF']
- ext_wcs.idcmodel.shift(offsetx,offsety)
-
- fx, fy = ext_wcs.idcmodel.cx, ext_wcs.idcmodel.cy
-
- ext_wcs.setPscale()
- tddscale = (ref_wcs.pscale/fx[1,1])
- v2 = ext_wcs.idcmodel.refpix['V2REF'] + v23_corr[0,0] * tddscale
- v3 = ext_wcs.idcmodel.refpix['V3REF'] - v23_corr[1,0] * tddscale
- v2ref = ref_wcs.idcmodel.refpix['V2REF'] + rv23_corr[0,0] * tddscale
- v3ref = ref_wcs.idcmodel.refpix['V3REF'] - rv23_corr[1,0] * tddscale
-
- R_scale = ref_wcs.idcmodel.refpix['PSCALE']/3600.0
- off = sqrt((v2-v2ref)**2 + (v3-v3ref)**2)/(R_scale*3600.0)
-
- if v3 == v3ref:
- theta=0.0
- else:
- theta = atan2(ext_wcs.parity[0][0]*(v2-v2ref), ext_wcs.parity[1][1]*(v3-v3ref))
-
- if ref_wcs.idcmodel.refpix['THETA']: theta += ref_wcs.idcmodel.refpix['THETA']*pi/180.0
-
- dX=(off*sin(theta)) + offshiftx
- dY=(off*cos(theta)) + offshifty
-
- px = np.array([[dX,dY]])
- newcrval = ref_wcs.wcs.p2s(px, 1)['world'][0]
- newcrpix = np.array([ext_wcs.idcmodel.refpix['XREF'] + ltvoffx,
- ext_wcs.idcmodel.refpix['YREF'] + ltvoffy])
- ext_wcs.wcs.crval = newcrval
- ext_wcs.wcs.crpix = newcrpix
- ext_wcs.wcs.set()
-
- # Create a small vector, in reference image pixel scale
- delmat = np.array([[fx[1,1], fy[1,1]], \
- [fx[1,0], fy[1,0]]]) / R_scale/3600.
-
- # Account for subarray offset
- # Angle of chip relative to chip
- if ext_wcs.idcmodel.refpix['THETA']:
- dtheta = ext_wcs.idcmodel.refpix['THETA'] - ref_wcs.idcmodel.refpix['THETA']
- else:
- dtheta = 0.0
-
- rrmat = fileutil.buildRotMatrix(dtheta)
- # Rotate the vectors
- dxy = np.dot(delmat, rrmat)
- wc = ref_wcs.wcs.p2s((px + dxy), 1)['world']
-
- # Calculate the new CDs and convert to degrees
- cd11 = utils.diff_angles(wc[0,0],newcrval[0])*cos(newcrval[1]*pi/180.0)
- cd12 = utils.diff_angles(wc[1,0],newcrval[0])*cos(newcrval[1]*pi/180.0)
- cd21 = utils.diff_angles(wc[0,1],newcrval[1])
- cd22 = utils.diff_angles(wc[1,1],newcrval[1])
- cd = np.array([[cd11, cd12], [cd21, cd22]])
- ext_wcs.wcs.cd = cd
- ext_wcs.wcs.set()
-
- upextwcs = classmethod(upextwcs)
-
- def uprefwcs(cls, ext_wcs, ref_wcs, rv23_corr_tdd, loff, offsh):
- """
- Updates the reference chip
- """
- ltvoffx, ltvoffy = loff[0], loff[1]
- ltv1 = ref_wcs.ltv1
- ltv2 = ref_wcs.ltv2
- if ref_wcs.ltv1 != 0. or ref_wcs.ltv2 != 0.:
- offsetx = ref_wcs.wcs.crpix[0] - ltv1 - ref_wcs.idcmodel.refpix['XREF']
- offsety = ref_wcs.wcs.crpix[1] - ltv2 - ref_wcs.idcmodel.refpix['YREF']
- ref_wcs.idcmodel.shift(offsetx,offsety)
-
- rfx, rfy = ref_wcs.idcmodel.cx, ref_wcs.idcmodel.cy
-
- offshift = offsh
- dec = ref_wcs.wcs.crval[1]
- tddscale = (ref_wcs.pscale/ref_wcs.idcmodel.cx[1,1])
- rv23 = [ref_wcs.idcmodel.refpix['V2REF'] + (rv23_corr_tdd[0,0] *tddscale),
- ref_wcs.idcmodel.refpix['V3REF'] - (rv23_corr_tdd[1,0] * tddscale)]
- # Get an approximate reference position on the sky
- rref = np.array([[ref_wcs.idcmodel.refpix['XREF']+ltvoffx ,
- ref_wcs.idcmodel.refpix['YREF']+ltvoffy]])
-
- crval = ref_wcs.wcs.p2s(rref, 1)['world'][0]
- # Convert the PA_V3 orientation to the orientation at the aperture
- # This is for the reference chip only - we use this for the
- # reference tangent plane definition
- # It has the same orientation as the reference chip
- pv = troll(ext_wcs.pav3,dec,rv23[0], rv23[1])
- # Add the chip rotation angle
- if ref_wcs.idcmodel.refpix['THETA']:
- pv += ref_wcs.idcmodel.refpix['THETA']
-
-
- # Set values for the rest of the reference WCS
- ref_wcs.wcs.crval = crval
- ref_wcs.wcs.crpix = np.array([0.0,0.0])+offsh
- parity = ref_wcs.parity
- R_scale = ref_wcs.idcmodel.refpix['PSCALE']/3600.0
- cd11 = parity[0][0] * cos(pv*pi/180.0)*R_scale
- cd12 = parity[0][0] * -sin(pv*pi/180.0)*R_scale
- cd21 = parity[1][1] * sin(pv*pi/180.0)*R_scale
- cd22 = parity[1][1] * cos(pv*pi/180.0)*R_scale
-
- rcd = np.array([[cd11, cd12], [cd21, cd22]])
- ref_wcs.wcs.cd = rcd
- ref_wcs.wcs.set()
-
- uprefwcs = classmethod(uprefwcs)
-
- def zero_point_corr(cls,hwcs):
- if hwcs.idcmodel.refpix['skew_coeffs'] is not None and 'TDD_CY_BETA' in hwcs.idcmodel.refpix['skew_coeffs']:
- v23_corr = np.array([[0.],[0.]])
- return v23_corr
- else:
- try:
- alpha = hwcs.idcmodel.refpix['TDDALPHA']
- beta = hwcs.idcmodel.refpix['TDDBETA']
- except KeyError:
- alpha = 0.0
- beta = 0.0
- v23_corr = np.array([[0.],[0.]])
- logger.debug("\n\tTDD Zero point correction for chip %s defaulted to: %s" % (hwcs.chip, v23_corr))
- return v23_corr
-
- tdd = np.array([[beta, alpha], [alpha, -beta]])
- mrotp = fileutil.buildRotMatrix(2.234529)/2048.
- xy0 = np.array([[cls.tdd_xyref[hwcs.chip][0]-2048.], [cls.tdd_xyref[hwcs.chip][1]-2048.]])
- v23_corr = np.dot(mrotp,np.dot(tdd,xy0)) * 0.05
- logger.debug("\n\tTDD Zero point correction for chip %s: %s" % (hwcs.chip, v23_corr))
- return v23_corr
-
- zero_point_corr = classmethod(zero_point_corr)
-
- def getOffsets(cls, ext_wcs):
- ltv1 = ext_wcs.ltv1
- ltv2 = ext_wcs.ltv2
-
- offsetx = ext_wcs.wcs.crpix[0] - ltv1 - ext_wcs.idcmodel.refpix['XREF']
- offsety = ext_wcs.wcs.crpix[1] - ltv2 - ext_wcs.idcmodel.refpix['YREF']
-
- shiftx = ext_wcs.idcmodel.refpix['XREF'] + ltv1
- shifty = ext_wcs.idcmodel.refpix['YREF'] + ltv2
- if ltv1 != 0. or ltv2 != 0.:
- ltvoffx = ltv1 + offsetx
- ltvoffy = ltv2 + offsety
- offshiftx = offsetx + shiftx
- offshifty = offsety + shifty
- else:
- ltvoffx = 0.
- ltvoffy = 0.
- offshiftx = 0.
- offshifty = 0.
-
- ltvoff = np.array([ltvoffx, ltvoffy])
- offshift = np.array([offshiftx, offshifty])
- return ltvoff, offshift
-
- getOffsets = classmethod(getOffsets)
-
-
-def troll(roll, dec, v2, v3):
- """ Computes the roll angle at the target position based on:
- the roll angle at the V1 axis(roll),
- the dec of the target(dec), and
- the V2/V3 position of the aperture (v2,v3) in arcseconds.
-
- Based on algorithm provided by Colin Cox and used in
- Generic Conversion at STScI.
- """
- # Convert all angles to radians
- _roll = np.deg2rad(roll)
- _dec = np.deg2rad(dec)
- _v2 = np.deg2rad(v2 / 3600.)
- _v3 = np.deg2rad(v3 / 3600.)
-
- # compute components
- sin_rho = sqrt((pow(sin(_v2),2)+pow(sin(_v3),2)) - (pow(sin(_v2),2)*pow(sin(_v3),2)))
- rho = asin(sin_rho)
- beta = asin(sin(_v3)/sin_rho)
- if _v2 < 0: beta = pi - beta
- gamma = asin(sin(_v2)/sin_rho)
- if _v3 < 0: gamma = pi - gamma
- A = pi/2. + _roll - beta
- B = atan2( sin(A)*cos(_dec), (sin(_dec)*sin_rho - cos(_dec)*cos(rho)*cos(A)))
-
- # compute final value
- troll = np.rad2deg(pi - (gamma+B))
-
- return troll
diff --git a/lib/stwcs/updatewcs/npol.py b/lib/stwcs/updatewcs/npol.py
deleted file mode 100644
index 33579f0..0000000
--- a/lib/stwcs/updatewcs/npol.py
+++ /dev/null
@@ -1,343 +0,0 @@
-from __future__ import absolute_import, division # confidence high
-
-import logging, time
-
-import numpy as np
-from astropy.io import fits
-
-from stsci.tools import fileutil
-from . import utils
-
-logger = logging.getLogger('stwcs.updatewcs.npol')
-
-class NPOLCorr(object):
- """
- Defines a Lookup table prior distortion correction as per WCS paper IV.
- It uses a reference file defined by the NPOLFILE (suffix 'NPL') keyword
- in the primary header.
-
- Notes
- -----
- - Using extensions in the reference file create a WCSDVARR extensions
- and add them to the science file.
- - Add record-valued keywords to the science extension header to describe
- the lookup tables.
- - Add a keyword 'NPOLEXT' to the science extension header to store
- the name of the reference file used to create the WCSDVARR extensions.
-
- If WCSDVARR extensions exist and `NPOLFILE` is different from `NPOLEXT`,
- a subsequent update will overwrite the existing extensions.
- If WCSDVARR extensions were not found in the science file, they will be added.
-
- It is assumed that the NPL reference files were created to work with IDC tables
- but will be applied with SIP coefficients. A transformation is applied to correct
- for the fact that the lookup tables will be applied before the first order coefficients
- which are in the CD matrix when the SIP convention is used.
- """
-
- def updateWCS(cls, fobj):
- """
- Parameters
- ----------
- fobj : `astropy.io.fits.HDUList` object
- Science file, for which a distortion correction in a NPOLFILE is available
-
- """
- logger.info("\n\tStarting NPOL: %s" %time.asctime())
- try:
- assert isinstance(fobj, fits.HDUList)
- except AssertionError:
- logger.exception('\n\tInput must be a fits.HDUList object')
- raise
-
- cls.applyNPOLCorr(fobj)
- nplfile = fobj[0].header['NPOLFILE']
-
- new_kw = {'NPOLEXT': nplfile}
- return new_kw
-
- updateWCS = classmethod(updateWCS)
-
- def applyNPOLCorr(cls, fobj):
- """
- For each science extension in a fits file object:
- - create a WCSDVARR extension
- - update science header
- - add/update NPOLEXT keyword
- """
- nplfile = fileutil.osfn(fobj[0].header['NPOLFILE'])
- # Map WCSDVARR EXTVER numbers to extension numbers
- wcsdvarr_ind = cls.getWCSIndex(fobj)
- for ext in fobj:
- try:
- extname = ext.header['EXTNAME'].lower()
- except KeyError:
- continue
- if extname == 'sci':
- extversion = ext.header['EXTVER']
- ccdchip = cls.get_ccdchip(fobj, extname='SCI', extver=extversion)
- header = ext.header
- # get the data arrays from the reference file and transform
- # them for use with SIP
- dx,dy = cls.getData(nplfile, ccdchip)
- idccoeffs = cls.getIDCCoeffs(header)
-
- if idccoeffs is not None:
- dx, dy = cls.transformData(dx,dy, idccoeffs)
-
- # Determine EXTVER for the WCSDVARR extension from the
- # NPL file (EXTNAME, EXTVER) kw.
- # This is used to populate DPj.EXTVER kw
- wcsdvarr_x_version = 2 * extversion -1
- wcsdvarr_y_version = 2 * extversion
- for ename in zip(['DX', 'DY'], [wcsdvarr_x_version,wcsdvarr_y_version],[dx, dy]):
- error_val = ename[2].max()
- cls.addSciExtKw(header, wdvarr_ver=ename[1], npol_extname=ename[0], error_val=error_val)
- hdu = cls.createNpolHDU(header, npolfile=nplfile, \
- wdvarr_ver=ename[1], npl_extname=ename[0], data=ename[2],ccdchip=ccdchip)
- if wcsdvarr_ind:
- fobj[wcsdvarr_ind[ename[1]]] = hdu
- else:
- fobj.append(hdu)
-
-
- applyNPOLCorr = classmethod(applyNPOLCorr)
-
- def getWCSIndex(cls, fobj):
-
- """
- If fobj has WCSDVARR extensions:
- returns a mapping of their EXTVER kw to file object extension numbers
- if fobj does not have WCSDVARR extensions:
- an empty dictionary is returned
- """
- wcsd = {}
- for e in range(len(fobj)):
- try:
- ename = fobj[e].header['EXTNAME']
- except KeyError:
- continue
- if ename == 'WCSDVARR':
- wcsd[fobj[e].header['EXTVER']] = e
- logger.debug("A map of WSCDVARR externsions %s" % wcsd)
- return wcsd
-
- getWCSIndex = classmethod(getWCSIndex)
-
- def addSciExtKw(cls, hdr, wdvarr_ver=None, npol_extname=None, error_val=0.0):
- """
- Adds kw to sci extension to define WCSDVARR lookup table extensions
-
- """
- if npol_extname =='DX':
- j=1
- else:
- j=2
-
- cperror = 'CPERR%s' %j
- cpdis = 'CPDIS%s' %j
- dpext = 'DP%s.' %j + 'EXTVER'
- dpnaxes = 'DP%s.' %j +'NAXES'
- dpaxis1 = 'DP%s.' %j+'AXIS.1'
- dpaxis2 = 'DP%s.' %j+'AXIS.2'
- keys = [cperror, cpdis, dpext, dpnaxes, dpaxis1, dpaxis2]
- values = {cperror: error_val,
- cpdis: 'Lookup',
- dpext: wdvarr_ver,
- dpnaxes: 2,
- dpaxis1: 1,
- dpaxis2: 2}
-
- comments = {cperror: 'Maximum error of NPOL correction for axis %s' % j,
- cpdis: 'Prior distortion function type',
- dpext: 'Version number of WCSDVARR extension containing lookup distortion table',
- dpnaxes: 'Number of independent variables in distortion function',
- dpaxis1: 'Axis number of the jth independent variable in a distortion function',
- dpaxis2: 'Axis number of the jth independent variable in a distortion function'
- }
- # Look for HISTORY keywords. If present, insert new keywords before them
- before_key = 'HISTORY'
- if before_key not in hdr:
- before_key = None
-
- for key in keys:
- hdr.set(key, value=values[key], comment=comments[key], before=before_key)
-
- addSciExtKw = classmethod(addSciExtKw)
-
- def getData(cls,nplfile, ccdchip):
- """
- Get the data arrays from the reference NPOL files
- Make sure 'CCDCHIP' in the npolfile matches "CCDCHIP' in the science file.
- """
- npl = fits.open(nplfile)
- for ext in npl:
- nplextname = ext.header.get('EXTNAME',"")
- nplccdchip = ext.header.get('CCDCHIP',1)
- if nplextname == 'DX' and nplccdchip == ccdchip:
- xdata = ext.data.copy()
- continue
- elif nplextname == 'DY' and nplccdchip == ccdchip:
- ydata = ext.data.copy()
- continue
- else:
- continue
- npl.close()
- return xdata, ydata
- getData = classmethod(getData)
-
- def transformData(cls, dx, dy, coeffs):
- """
- Transform the NPOL data arrays for use with SIP
- """
- ndx, ndy = np.dot(coeffs, [dx.ravel(), dy.ravel()]).astype(np.float32)
- ndx.shape = dx.shape
- ndy.shape=dy.shape
- return ndx, ndy
-
- transformData = classmethod(transformData)
-
- def getIDCCoeffs(cls, header):
- """
- Return a matrix of the scaled first order IDC coefficients.
- """
- try:
- ocx10 = header['OCX10']
- ocx11 = header['OCX11']
- ocy10 = header['OCY10']
- ocy11 = header['OCY11']
- coeffs = np.array([[ocx11, ocx10], [ocy11,ocy10]], dtype=np.float32)
- except KeyError:
- logger.exception('\n\tFirst order IDCTAB coefficients are not available. \n\
- Cannot convert SIP to IDC coefficients.')
- return None
- try:
- idcscale = header['IDCSCALE']
- except KeyError:
- logger.exception("IDCSCALE not found in header - setting it to 1.")
- idcscale = 1
-
- return np.linalg.inv(coeffs/idcscale)
-
- getIDCCoeffs = classmethod(getIDCCoeffs)
-
- def createNpolHDU(cls, sciheader, npolfile=None, wdvarr_ver=1, npl_extname=None,data = None, ccdchip=1):
- """
- Creates an HDU to be added to the file object.
- """
- hdr = cls.createNpolHdr(sciheader, npolfile=npolfile, wdvarr_ver=wdvarr_ver, npl_extname=npl_extname, ccdchip=ccdchip)
- hdu = fits.ImageHDU(header=hdr, data=data)
- return hdu
-
- createNpolHDU = classmethod(createNpolHDU)
-
- def createNpolHdr(cls, sciheader, npolfile, wdvarr_ver, npl_extname, ccdchip):
- """
- Creates a header for the WCSDVARR extension based on the NPOL reference file
- and sci extension header. The goal is to always work in image coordinates
- (also for subarrays and binned images. The WCS for the WCSDVARR extension
- i ssuch that a full size npol table is created and then shifted or scaled
- if the science image is a subarray or binned image.
- """
- npl = fits.open(npolfile)
- npol_phdr = npl[0].header
- for ext in npl:
- try:
- nplextname = ext.header['EXTNAME']
- nplextver = ext.header['EXTVER']
- except KeyError:
- continue
- nplccdchip = cls.get_ccdchip(npl, extname=nplextname, extver=nplextver)
- if nplextname == npl_extname and nplccdchip == ccdchip:
- npol_header = ext.header
- break
- else:
- continue
- npl.close()
-
- naxis = npl[1].header['NAXIS']
- ccdchip = nplextname #npol_header['CCDCHIP']
-
- kw = { 'NAXIS': 'Size of the axis',
- 'CDELT': 'Coordinate increment along axis',
- 'CRPIX': 'Coordinate system reference pixel',
- 'CRVAL': 'Coordinate system value at reference pixel',
- }
-
- kw_comm1 = {}
- kw_val1 = {}
- for key in kw.keys():
- for i in range(1, naxis+1):
- si = str(i)
- kw_comm1[key+si] = kw[key]
-
- for i in range(1, naxis+1):
- si = str(i)
- kw_val1['NAXIS'+si] = npol_header.get('NAXIS'+si)
- kw_val1['CDELT'+si] = npol_header.get('CDELT'+si, 1.0) * \
- sciheader.get('LTM'+si+'_'+si, 1)
- kw_val1['CRPIX'+si] = npol_header.get('CRPIX'+si, 0.0)
- kw_val1['CRVAL'+si] = (npol_header.get('CRVAL'+si, 0.0) - \
- sciheader.get('LTV'+str(i), 0))
-
- kw_comm0 = {'XTENSION': 'Image extension',
- 'BITPIX': 'IEEE floating point',
- 'NAXIS': 'Number of axes',
- 'EXTNAME': 'WCS distortion array',
- 'EXTVER': 'Distortion array version number',
- 'PCOUNT': 'Special data area of size 0',
- 'GCOUNT': 'One data group',
- }
-
- kw_val0 = { 'XTENSION': 'IMAGE',
- 'BITPIX': -32,
- 'NAXIS': naxis,
- 'EXTNAME': 'WCSDVARR',
- 'EXTVER': wdvarr_ver,
- 'PCOUNT': 0,
- 'GCOUNT': 1,
- 'CCDCHIP': ccdchip,
- }
- cdl = []
- for key in kw_comm0.keys():
- cdl.append((key, kw_val0[key], kw_comm0[key]))
- for key in kw_comm1.keys():
- cdl.append((key, kw_val1[key], kw_comm1[key]))
- # Now add keywords from NPOLFILE header to document source of calibration
- # include all keywords after and including 'FILENAME' from header
- start_indx = -1
- end_indx = 0
- for i, c in enumerate(npol_phdr):
- if c == 'FILENAME':
- start_indx = i
- if c == '': # remove blanks from end of header
- end_indx = i+1
- break
- if start_indx >= 0:
- for card in npol_phdr.cards[start_indx:end_indx]:
- cdl.append(card)
-
- hdr = fits.Header(cards=cdl)
-
- return hdr
-
- createNpolHdr = classmethod(createNpolHdr)
-
- def get_ccdchip(cls, fobj, extname, extver):
- """
- Given a science file or npol file determine CCDCHIP
- """
- ccdchip = 1
- if fobj[0].header['INSTRUME'] == 'ACS' and fobj[0].header['DETECTOR'] == 'WFC':
- ccdchip = fobj[extname, extver].header['CCDCHIP']
- elif fobj[0].header['INSTRUME'] == 'WFC3' and fobj[0].header['DETECTOR'] == 'UVIS':
- ccdchip = fobj[extname, extver].header['CCDCHIP']
- elif fobj[0].header['INSTRUME'] == 'WFPC2':
- ccdchip = fobj[extname, extver].header['DETECTOR']
- elif fobj[0].header['INSTRUME'] == 'STIS':
- ccdchip = fobj[extname, extver].header['DETECTOR']
- elif fobj[0].header['INSTRUME'] == 'NICMOS':
- ccdchip = fobj[extname, extver].header['CAMERA']
- return ccdchip
-
- get_ccdchip = classmethod(get_ccdchip)
diff --git a/lib/stwcs/updatewcs/utils.py b/lib/stwcs/updatewcs/utils.py
deleted file mode 100644
index 1214199..0000000
--- a/lib/stwcs/updatewcs/utils.py
+++ /dev/null
@@ -1,264 +0,0 @@
-from __future__ import division # confidence high
-import os
-from astropy.io import fits
-from stsci.tools import fileutil
-
-import logging
-logger = logging.getLogger("stwcs.updatewcs.utils")
-
-
-def diff_angles(a,b):
- """
- Perform angle subtraction a-b taking into account
- small-angle differences across 360degree line.
- """
-
- diff = a - b
-
- if diff > 180.0:
- diff -= 360.0
-
- if diff < -180.0:
- diff += 360.0
-
- return diff
-
-def getBinning(fobj, extver=1):
- # Return the binning factor
- binned = 1
- if fobj[0].header['INSTRUME'] == 'WFPC2':
- mode = fobj[0].header.get('MODE', "")
- if mode == 'AREA': binned = 2
- else:
- binned = fobj['SCI', extver].header.get('BINAXIS',1)
- return binned
-
-def updateNEXTENDKw(fobj):
- """
- Updates PRIMARY header with correct value for NEXTEND, if present.
-
- Parameters
- -----------
- fobj : `astropy.io.fits.HDUList`
- The FITS object for file opened in `update` mode
-
- """
- if 'nextend' in fobj[0].header:
- fobj[0].header['nextend'] = len(fobj) -1
-
-def extract_rootname(kwvalue,suffix=""):
- """ Returns the rootname from a full reference filename
-
- If a non-valid value (any of ['','N/A','NONE','INDEF',None]) is input,
- simply return a string value of 'NONE'
-
- This function will also replace any 'suffix' specified with a blank.
- """
- # check to see whether a valid kwvalue has been provided as input
- if kwvalue.strip() in ['','N/A','NONE','INDEF',None]:
- return 'NONE' # no valid value, so return 'NONE'
-
- # for a valid kwvalue, parse out the rootname
- # strip off any environment variable from input filename, if any are given
- if '$' in kwvalue:
- fullval = kwvalue[kwvalue.find('$')+1:]
- else:
- fullval = kwvalue
- # Extract filename without path from kwvalue
- fname = os.path.basename(fullval).strip()
-
- # Now, rip out just the rootname from the full filename
- rootname = fileutil.buildNewRootname(fname)
-
- # Now, remove any known suffix from rootname
- rootname = rootname.replace(suffix,'')
- return rootname.strip()
-
-def construct_distname(fobj,wcsobj):
- """
- This function constructs the value for the keyword 'DISTNAME'.
- It relies on the reference files specified by the keywords 'IDCTAB',
- 'NPOLFILE', and 'D2IMFILE'.
-
- The final constructed value will be of the form:
- <idctab rootname>-<npolfile rootname>-<d2imfile rootname>
- and have a value of 'NONE' if no reference files are specified.
- """
- idcname = extract_rootname(fobj[0].header.get('IDCTAB', "NONE"),
- suffix='_idc')
- if (idcname is None or idcname=='NONE') and wcsobj.sip is not None:
- idcname = 'UNKNOWN'
-
- npolname, npolfile = build_npolname(fobj)
- if npolname is None and wcsobj.cpdis1 is not None:
- npolname = 'UNKNOWN'
-
- d2imname, d2imfile = build_d2imname(fobj)
- if d2imname is None and wcsobj.det2im is not None:
- d2imname = 'UNKNOWN'
-
- sipname, idctab = build_sipname(fobj)
-
- distname = build_distname(sipname,npolname,d2imname)
- return {'DISTNAME':distname,'SIPNAME':sipname}
-
-def build_distname(sipname,npolname,d2imname):
- """
- Core function to build DISTNAME keyword value without the HSTWCS input.
- """
-
- distname = sipname.strip()
- if npolname != 'NONE' or d2imname != 'NONE':
- if d2imname != 'NONE':
- distname+= '-'+npolname.strip() + '-'+d2imname.strip()
- else:
- distname+='-'+npolname.strip()
-
- return distname
-
-def build_default_wcsname(idctab):
-
- idcname = extract_rootname(idctab,suffix='_idc')
- wcsname = 'IDC_' + idcname
- return wcsname
-
-def build_sipname(fobj, fname=None, sipname=None):
- """
- Build a SIPNAME from IDCTAB
-
- Parameters
- ----------
- fobj : `astropy.io.fits.HDUList`
- file object
- fname : string
- science file name (to be used if ROOTNAME is not present
- sipname : string
- user supplied SIPNAME keyword
-
- Returns
- -------
- sipname, idctab
- """
- try:
- idctab = fobj[0].header['IDCTAB']
- idcname = extract_rootname(idctab,suffix='_idc')
- except KeyError:
- idctab = 'N/A'
- idcname= 'N/A'
- if not fname:
- try:
- fname = fobj.filename()
- except:
- fname = " "
- if not sipname:
- try:
- sipname = fobj[0].header["SIPNAME"]
- if idcname == 'N/A' or idcname not in sipname:
- raise KeyError
- except KeyError:
- if idcname != 'N/A':
- try:
- rootname = fobj[0].header['rootname']
- except KeyError:
- rootname = fname
- sipname = rootname +'_'+ idcname
- else:
- if 'A_ORDER' in fobj[1].header or 'B_ORDER' in fobj[1].header:
- sipname = 'UNKNOWN'
- else:
- idcname = 'NOMODEL'
-
- return sipname, idctab
-
-def build_npolname(fobj, npolfile=None):
- """
- Build a NPOLNAME from NPOLFILE
-
- Parameters
- ----------
- fobj : `astropy.io.fits.HDUList`
- file object
- npolfile : string
- user supplied NPOLFILE keyword
-
- Returns
- -------
- npolname, npolfile
- """
- if not npolfile:
- try:
- npolfile = fobj[0].header["NPOLFILE"]
- except KeyError:
- npolfile = ' '
- if fileutil.countExtn(fobj, 'WCSDVARR'):
- npolname = 'UNKNOWN'
- else:
- npolname = 'NOMODEL'
- npolname = extract_rootname(npolfile, suffix='_npl')
- if npolname == 'NONE':
- npolname = 'NOMODEL'
- else:
- npolname = extract_rootname(npolfile, suffix='_npl')
- if npolname == 'NONE':
- npolname = 'NOMODEL'
- return npolname, npolfile
-
-def build_d2imname(fobj, d2imfile=None):
- """
- Build a D2IMNAME from D2IMFILE
-
- Parameters
- ----------
- fobj : `astropy.io.fits.HDUList`
- file object
- d2imfile : string
- user supplied NPOLFILE keyword
-
- Returns
- -------
- d2imname, d2imfile
- """
- if not d2imfile:
- try:
- d2imfile = fobj[0].header["D2IMFILE"]
- except KeyError:
- d2imfile = 'N/A'
- if fileutil.countExtn(fobj, 'D2IMARR'):
- d2imname = 'UNKNOWN'
- else:
- d2imname = 'NOMODEL'
- d2imname = extract_rootname(d2imfile,suffix='_d2i')
- if d2imname == 'NONE':
- d2imname = 'NOMODEL'
- else:
- d2imname = extract_rootname(d2imfile,suffix='_d2i')
- if d2imname == 'NONE':
- d2imname = 'NOMODEL'
- return d2imname, d2imfile
-
-
-def remove_distortion(fname, dist_keyword):
- logger.info("Removing distortion {0} from file {0}".format(dist_keyword, fname))
- from ..wcsutil import altwcs
- if dist_keyword == "NPOLFILE":
- extname = "WCSDVARR"
- keywords = ["CPERR*", "DP1.*", "DP2.*", "CPDIS*", "NPOLEXT"]
- elif dist_keyword == "D2IMFILE":
- extname = "D2IMARR"
- keywords = ["D2IMERR*", "D2IM1.*", "D2IM2.*", "D2IMDIS*", "D2IMEXT"]
- else:
- raise AttributeError("Unrecognized distortion keyword "
- "{0} when attempting to remove distortion".format(dist_keyword))
- ext_mapping = altwcs.mapFitsExt2HDUListInd(fname, "SCI").values()
- f = fits.open(fname, mode="update")
- for hdu in ext_mapping:
- for kw in keywords:
- try:
- del f[hdu].header[kw]
- except KeyError:
- pass
- ext_mapping = altwcs.mapFitsExt2HDUListInd(fname, extname).values()
- ext_mapping.sort()
- for hdu in ext_mapping[::-1]:
- del f[hdu]
- f.close()
diff --git a/lib/stwcs/updatewcs/wfpc2_dgeo.py b/lib/stwcs/updatewcs/wfpc2_dgeo.py
deleted file mode 100644
index e57bb5c..0000000
--- a/lib/stwcs/updatewcs/wfpc2_dgeo.py
+++ /dev/null
@@ -1,123 +0,0 @@
-""" wfpc2_dgeo - Functions to convert WFPC2 DGEOFILE into D2IMFILE
-
-"""
-import os
-import datetime
-
-import astropy
-from astropy.io import fits
-import numpy as np
-
-from stsci.tools import fileutil
-
-import logging
-logger = logging.getLogger("stwcs.updatewcs.apply_corrections")
-
-def update_wfpc2_d2geofile(filename, fhdu=None):
- """
- Creates a D2IMFILE from the DGEOFILE for a WFPC2 image (input), and
- modifies the header to reflect the new usage.
-
- Parameters
- ----------
- filename: string
- Name of WFPC2 file to be processed. This file will be updated
- to delete any reference to a DGEOFILE and add a D2IMFILE to replace
- that correction when running updatewcs.
- fhdu: object
- FITS object for WFPC2 image. If user has already opened the WFPC2
- file, they can simply pass that FITS object in for direct processing.
-
- Returns
- -------
- d2imfile: string
- Name of D2IMFILE created from DGEOFILE. The D2IMFILE keyword in the
- image header will be updated/added to point to this newly created file.
-
- """
-
- close_fhdu = False
- if fhdu is None:
- fhdu = fileutil.openImage(filename, mode='update')
- close_fhdu = True
-
- dgeofile = fhdu['PRIMARY'].header.get('DGEOFILE', None)
- already_converted = dgeofile not in [None, "N/A", "", " "]
- if already_converted or 'ODGEOFIL' in fhdu['PRIMARY'].header:
- if not already_converted:
- dgeofile = fhdu['PRIMARY'].header.get('ODGEOFIL', None)
- logger.info('Converting DGEOFILE %s into D2IMFILE...' % dgeofile)
- rootname = filename[:filename.find('.fits')]
- d2imfile = convert_dgeo_to_d2im(dgeofile,rootname)
- fhdu['PRIMARY'].header['ODGEOFIL'] = dgeofile
- fhdu['PRIMARY'].header['DGEOFILE'] = 'N/A'
- fhdu['PRIMARY'].header['D2IMFILE'] = d2imfile
- else:
- d2imfile = None
- fhdu['PRIMARY'].header['DGEOFILE'] = 'N/A'
- if 'D2IMFILE' not in fhdu['PRIMARY'].header:
- fhdu['PRIMARY'].header['D2IMFILE'] = 'N/A'
-
- # Only close the file handle if opened in this function
- if close_fhdu:
- fhdu.close()
-
- # return the d2imfile name so that calling routine can keep
- # track of the new file created and delete it later if necessary
- # (multidrizzle clean=True mode of operation)
- return d2imfile
-
-def convert_dgeo_to_d2im(dgeofile,output,clobber=True):
- """ Routine that converts the WFPC2 DGEOFILE into a D2IMFILE.
- """
- dgeo = fileutil.openImage(dgeofile)
- outname = output+'_d2im.fits'
-
- removeFileSafely(outname)
- data = np.array([dgeo['dy',1].data[:,0]])
- scihdu = fits.ImageHDU(data=data)
- dgeo.close()
- # add required keywords for D2IM header
- scihdu.header['EXTNAME'] = ('DY', 'Extension name')
- scihdu.header['EXTVER'] = (1, 'Extension version')
- fits_str = 'PYFITS Version '+str(astropy.__version__)
- scihdu.header['ORIGIN'] = (fits_str, 'FITS file originator')
- scihdu.header['INHERIT'] = (False, 'Inherits global header')
-
- dnow = datetime.datetime.now()
- scihdu.header['DATE'] = (str(dnow).replace(' ','T'),
- 'Date FITS file was generated')
-
- scihdu.header['CRPIX1'] = (0, 'Distortion array reference pixel')
- scihdu.header['CDELT1'] = (1, 'Grid step size in first coordinate')
- scihdu.header['CRVAL1'] = (0, 'Image array pixel coordinate')
- scihdu.header['CRPIX2'] = (0, 'Distortion array reference pixel')
- scihdu.header['CDELT2'] = (1, 'Grid step size in second coordinate')
- scihdu.header['CRVAL2'] = (0, 'Image array pixel coordinate')
-
- phdu = fits.PrimaryHDU()
- phdu.header['INSTRUME'] = 'WFPC2'
- d2imhdu = fits.HDUList()
- d2imhdu.append(phdu)
- scihdu.header['DETECTOR'] = (1, 'CCD number of the detector: PC 1, WFC 2-4 ')
- d2imhdu.append(scihdu.copy())
- scihdu.header['EXTVER'] = (2, 'Extension version')
- scihdu.header['DETECTOR'] = (2, 'CCD number of the detector: PC 1, WFC 2-4 ')
- d2imhdu.append(scihdu.copy())
- scihdu.header['EXTVER'] = (3, 'Extension version')
- scihdu.header['DETECTOR'] = (3, 'CCD number of the detector: PC 1, WFC 2-4 ')
- d2imhdu.append(scihdu.copy())
- scihdu.header['EXTVER'] = (4, 'Extension version')
- scihdu.header['DETECTOR'] = (4, 'CCD number of the detector: PC 1, WFC 2-4 ')
- d2imhdu.append(scihdu.copy())
- d2imhdu.writeto(outname)
- d2imhdu.close()
-
- return outname
-
-
-def removeFileSafely(filename,clobber=True):
- """ Delete the file specified, but only if it exists and clobber is True.
- """
- if filename is not None and filename.strip() != '':
- if os.path.exists(filename) and clobber: os.remove(filename)
diff --git a/lib/stwcs/wcsutil/__init__.py b/lib/stwcs/wcsutil/__init__.py
deleted file mode 100644
index 65280be..0000000
--- a/lib/stwcs/wcsutil/__init__.py
+++ /dev/null
@@ -1,34 +0,0 @@
-from __future__ import absolute_import, print_function # confidence high
-
-from .altwcs import *
-from .hstwcs import HSTWCS
-
-
-def help():
- doc = """ \
- 1. Using a `astropy.io.fits.HDUList` object and an extension number \n
- Example:\n
- from astropy.io improt fits
- fobj = fits.open('some_file.fits')\n
- w = wcsutil.HSTWCS(fobj, 3)\n\n
-
- 2. Create an HSTWCS object using a qualified file name. \n
- Example:\n
- w = wcsutil.HSTWCS('j9irw4b1q_flt.fits[sci,1]')\n\n
-
- 3. Create an HSTWCS object using a file name and an extension number. \n
- Example:\n
- w = wcsutil.HSTWCS('j9irw4b1q_flt.fits', ext=2)\n\n
-
- 4. Create an HSTWCS object from WCS with key 'O'.\n
- Example:\n
-
- w = wcsutil.HSTWCS('j9irw4b1q_flt.fits', ext=2, wcskey='O')\n\n
-
- 5. Create a template HSTWCS object for a DEFAULT object.\n
- Example:\n
- w = wcsutil.HSTWCS(instrument='DEFAULT')\n\n
- """
-
- print('How to create an HSTWCS object:\n\n')
- print(doc)
diff --git a/lib/stwcs/wcsutil/altwcs.py b/lib/stwcs/wcsutil/altwcs.py
deleted file mode 100644
index aae1a9f..0000000
--- a/lib/stwcs/wcsutil/altwcs.py
+++ /dev/null
@@ -1,758 +0,0 @@
-from __future__ import division, print_function # confidence high
-import os
-import string
-
-import numpy as np
-from astropy import wcs as pywcs
-from astropy.io import fits
-from stsci.tools import fileutil as fu
-
-altwcskw = ['WCSAXES', 'CRVAL', 'CRPIX', 'PC', 'CDELT', 'CD', 'CTYPE', 'CUNIT',
- 'PV', 'PS']
-altwcskw_extra = ['LATPOLE','LONPOLE','RESTWAV','RESTFRQ']
-
-# file operations
-def archiveWCS(fname, ext, wcskey=" ", wcsname=" ", reusekey=False):
- """
- Copy the primary WCS to the header as an alternate WCS
- with wcskey and name WCSNAME. It loops over all extensions in 'ext'
-
- Parameters
- ----------
- fname : string or `astropy.io.fits.HDUList`
- file name or a file object
- ext : int, tuple, str, or list of integers or tuples (e.g.('sci',1))
- fits extensions to work with
- If a string is provided, it should specify the EXTNAME of extensions
- with WCSs to be archived
- wcskey : string "A"-"Z" or " "
- if " ": get next available key if wcsname is also " " or try
- to get a key from WCSNAME value
- wcsname : string
- Name of alternate WCS description
- reusekey : boolean
- if True - overwrites a WCS with the same key
-
- Examples
- --------
- Copy the primary WCS of an in memory headrlet object to an
- alternate WCS with key 'T'
-
- >>> hlet=headerlet.createHeaderlet('junk.fits', 'hdr1.fits')
- >>> altwcs.wcskeys(hlet[1].header)
- ['A']
- >>> altwcs.archiveWCS(hlet, ext=[('SIPWCS',1),('SIPWCS',2)], wcskey='T')
- >>> altwcs.wcskeys(hlet[1].header)
- ['A', 'T']
-
-
- See Also
- --------
- wcsutil.restoreWCS: Copy an alternate WCS to the primary WCS
-
- """
-
- if isinstance(fname, str):
- f = fits.open(fname, mode='update')
- else:
- f = fname
-
- if not _parpasscheck(f, ext, wcskey, wcsname):
- closefobj(fname, f)
- raise ValueError("Input parameters problem")
-
- # Interpret input 'ext' value to get list of extensions to process
- ext = _buildExtlist(f, ext)
-
- if not wcskey and not wcsname:
- raise KeyError("Either wcskey or wcsname should be specified")
-
- if wcsname.strip() == "":
- try:
- wcsname = readAltWCS(f, ext[0], wcskey=" ")['WCSNAME']
- except KeyError:
- pass
- wcsext = ext[0]
- if wcskey != " " and wcskey in wcskeys(f[wcsext].header) and not reusekey:
- closefobj(fname, f)
- raise KeyError("Wcskey %s is aready used. \
- Run archiveWCS() with reusekey=True to overwrite this alternate WCS. \
- Alternatively choose another wcskey with altwcs.available_wcskeys()." %wcskey)
- elif wcskey == " ":
- # wcsname exists, overwrite it if reuse is True or get the next key
- if wcsname.strip() in wcsnames(f[wcsext].header).values():
- if reusekey:
- # try getting the key from an existing WCS with WCSNAME
- wkey = getKeyFromName(f[wcsext].header, wcsname)
- wname = wcsname
- if wkey == ' ':
- wkey = next_wcskey(f[wcsext].header)
- elif wkey is None:
- closefobj(fname, f)
- raise KeyError("Could not get a valid wcskey from wcsname %s" %wcsname)
- else:
- closefobj(fname, f)
- raise KeyError("Wcsname %s is aready used. \
- Run archiveWCS() with reusekey=True to overwrite this alternate WCS. \
- Alternatively choose another wcskey with altwcs.available_wcskeys() or\
- choose another wcsname." %wcsname)
- else:
- wkey = next_wcskey(f[wcsext].header)
- if wcsname.strip():
- wname = wcsname
- else:
- # determine which WCSNAME needs to be replicated in archived WCS
- wnames = wcsnames(f[wcsext].header)
- if 'O' in wnames: del wnames['O'] # we don't want OPUS/original
- if len(wnames) > 0:
- if ' ' in wnames:
- wname = wnames[' ']
- else:
- akeys = string.uppercase
- wname = "DEFAULT"
- for key in akeys[-1::]:
- if key in wnames:
- wname = wnames
- break
- else:
- wname = "DEFAULT"
- else:
- wkey = wcskey
- wname = wcsname
-
- for e in ext:
- hwcs = readAltWCS(f,e,wcskey=' ')
- if hwcs is None:
- continue
-
- wcsnamekey = 'WCSNAME' + wkey
- f[e].header[wcsnamekey] = wname
-
- try:
- old_wcsname=hwcs.pop('WCSNAME')
- except:
- pass
-
- for k in hwcs.keys():
- key = k[:7] + wkey
- f[e].header[key] = hwcs[k]
- closefobj(fname, f)
-
-def restore_from_to(f, fromext=None, toext=None, wcskey=" ", wcsname=" "):
- """
- Copy an alternate WCS from one extension as a primary WCS of another extension
-
- Reads in a WCS defined with wcskey and saves it as the primary WCS.
- Goes sequentially through the list of extensions in ext.
- Alternatively uses 'fromext' and 'toext'.
-
-
- Parameters
- ----------
- f: string or `astropy.io.fits.HDUList`
- a file name or a file object
- fromext: string
- extname from which to read in the alternate WCS, for example 'SCI'
- toext: string or python list
- extname or a list of extnames to which the WCS will be copied as
- primary, for example ['SCI', 'ERR', 'DQ']
- wcskey: a charater
- "A"-"Z" - Used for one of 26 alternate WCS definitions.
- or " " - find a key from WCSNAMe value
- wcsname: string (optional)
- if given and wcskey is " ", will try to restore by WCSNAME value
-
- See Also
- --------
- archiveWCS - copy the primary WCS as an alternate WCS
- restoreWCS - Copy a WCS with key "WCSKEY" to the primary WCS
-
- """
- if isinstance(f, str):
- fobj = fits.open(f, mode='update')
- else:
- fobj = f
-
- if not _parpasscheck(fobj, ext=None, wcskey=wcskey, fromext=fromext, toext=toext):
- closefobj(f, fobj)
- raise ValueError("Input parameters problem")
-
- # Interpret input 'ext' value to get list of extensions to process
- #ext = _buildExtlist(fobj, ext)
-
- if isinstance(toext, str):
- toext = [toext]
-
- # the case of an HDUList object in memory without an associated file
-
- #if fobj.filename() is not None:
- # name = fobj.filename()
-
- simplefits = fu.isFits(fobj)[1] is 'simple'
- if simplefits:
- wcskeyext = 0
- else:
- wcskeyext = 1
-
- if wcskey == " ":
- if wcsname.strip():
- wkey = getKeyFromName(fobj[wcskeyext].header, wcsname)
- if not wkey:
- closefobj(f, fobj)
- raise KeyError("Could not get a key from wcsname %s ." % wcsname)
- else:
- if wcskey not in wcskeys(fobj, ext=wcskeyext):
- print("Could not find alternate WCS with key %s in this file" % wcskey)
- closefobj(f, fobj)
- return
- wkey = wcskey
-
- countext = fu.countExtn(fobj, fromext)
- if not countext:
- raise KeyError("File does not have extension with extname %s", fromext)
- else:
- for i in range(1, countext+1):
- for toe in toext:
- _restore(fobj, fromextnum=i, fromextnam=fromext, toextnum=i, toextnam=toe, ukey=wkey)
-
- if fobj.filename() is not None:
- #fobj.writeto(name)
- closefobj(f, fobj)
-
-def restoreWCS(f, ext, wcskey=" ", wcsname=" "):
- """
- Copy a WCS with key "WCSKEY" to the primary WCS
-
- Reads in a WCS defined with wcskey and saves it as the primary WCS.
- Goes sequentially through the list of extensions in ext.
- Alternatively uses 'fromext' and 'toext'.
-
-
- Parameters
- ----------
- f : str or `astropy.io.fits.HDUList`
- file name or a file object
- ext : int, tuple, str, or list of integers or tuples (e.g.('sci',1))
- fits extensions to work with
- If a string is provided, it should specify the EXTNAME of extensions
- with WCSs to be archived
- wcskey : str
- "A"-"Z" - Used for one of 26 alternate WCS definitions.
- or " " - find a key from WCSNAMe value
- wcsname : str
- (optional) if given and wcskey is " ", will try to restore by WCSNAME value
-
- See Also
- --------
- archiveWCS - copy the primary WCS as an alternate WCS
- restore_from_to
-
- """
- if isinstance(f, str):
- fobj = fits.open(f, mode='update')
- else:
- fobj = f
-
- if not _parpasscheck(fobj, ext=ext, wcskey=wcskey):
- closefobj(f, fobj)
- raise ValueError("Input parameters problem")
-
- # Interpret input 'ext' value to get list of extensions to process
- ext = _buildExtlist(fobj, ext)
-
-
- # the case of an HDUList object in memory without an associated file
-
- #if fobj.filename() is not None:
- # name = fobj.filename()
-
- simplefits = fu.isFits(fobj)[1] is 'simple'
- if simplefits:
- wcskeyext = 0
- else:
- wcskeyext = 1
-
- if wcskey == " ":
- if wcsname.strip():
- wkey = getKeyFromName(fobj[wcskeyext].header, wcsname)
- if not wkey:
- closefobj(f, fobj)
- raise KeyError("Could not get a key from wcsname %s ." % wcsname)
- else:
- if wcskey not in wcskeys(fobj, ext=wcskeyext):
- #print "Could not find alternate WCS with key %s in this file" % wcskey
- closefobj(f, fobj)
- return
- wkey = wcskey
-
- for e in ext:
- _restore(fobj, wkey, fromextnum=e, verbose=False)
-
- if fobj.filename() is not None:
- #fobj.writeto(name)
- closefobj(f, fobj)
-
-def deleteWCS(fname, ext, wcskey=" ", wcsname=" "):
- """
- Delete an alternate WCS defined with wcskey.
- If wcskey is " " try to get a key from WCSNAME.
-
- Parameters
- ----------
- fname : str or a `astropy.io.fits.HDUList`
- ext : int, tuple, str, or list of integers or tuples (e.g.('sci',1))
- fits extensions to work with
- If a string is provided, it should specify the EXTNAME of extensions
- with WCSs to be archived
- wcskey : str
- one of 'A'-'Z' or " "
- wcsname : str
- Name of alternate WCS description
- """
- if isinstance(fname, str):
- fobj = fits.open(fname, mode='update')
- else:
- fobj = fname
-
- if not _parpasscheck(fobj, ext, wcskey, wcsname):
- closefobj(fname, fobj)
- raise ValueError("Input parameters problem")
-
- # Interpret input 'ext' value to get list of extensions to process
- ext = _buildExtlist(fobj, ext)
- # Do not allow deleting the original WCS.
- if wcskey == 'O':
- print("Wcskey 'O' is reserved for the original WCS and should not be deleted.")
- closefobj(fname, fobj)
- return
-
- wcskeyext = ext[0]
-
- if not wcskeys and not wcsname:
- raise KeyError("Either wcskey or wcsname should be specified")
-
- if wcskey == " ":
- # try getting the key from WCSNAME
- wkey = getKeyFromName(fobj[wcskeyext].header, wcsname)
- if not wkey:
- closefobj(fname, fobj)
- raise KeyError("Could not get a key: wcsname '%s' not found in header." % wcsname)
- else:
- if wcskey not in wcskeys(fobj[wcskeyext].header):
- closefobj(fname, fobj)
- raise KeyError("Could not find alternate WCS with key %s in this file" % wcskey)
- wkey = wcskey
-
- prexts = []
- for i in ext:
- hdr = fobj[i].header
- hwcs = readAltWCS(fobj,i,wcskey=wkey)
- if hwcs is None:
- continue
- for k in hwcs[::-1]:
- del hdr[k]
- #del hdr['ORIENT'+wkey]
- prexts.append(i)
- if prexts != []:
- print('Deleted all instances of WCS with key %s in extensions' % wkey, prexts)
- else:
- print("Did not find WCS with key %s in any of the extensions" % wkey)
- closefobj(fname, fobj)
-
-def _buildExtlist(fobj, ext):
- """
- Utility function to interpret the provided value of 'ext' and return a list
- of 'valid' values which can then be used by the rest of the functions in
- this module.
-
- Parameters
- ----------
- fobj: HDUList
- file to be examined
- ext: an int, a tuple, string, list of integers or tuples (e.g.('sci',1))
- fits extensions to work with
- If a string is provided, it should specify the EXTNAME of extensions
- with WCSs to be archived
- """
- if not isinstance(ext,list):
- if isinstance(ext,str):
- extstr = ext
- ext = []
- for extn in range(1, len(fobj)):
- if 'extname' in fobj[extn].header and fobj[extn].header['extname'] == extstr:
- ext.append(extn)
- elif isinstance(ext, int) or isinstance(ext, tuple):
- ext = [ext]
- else:
- raise KeyError("Valid extensions in 'ext' parameter need to be specified.")
- return ext
-
-def _restore(fobj, ukey, fromextnum,
- toextnum=None, fromextnam=None, toextnam=None, verbose=True):
- """
- fobj: string of HDUList
- ukey: string 'A'-'Z'
- wcs key
- fromextnum: int
- extver of extension from which to copy WCS
- fromextnam: string
- extname of extension from which to copy WCS
- toextnum: int
- extver of extension to which to copy WCS
- toextnam: string
- extname of extension to which to copy WCS
- """
- # create an extension tuple, e.g. ('SCI', 2)
- if fromextnam:
- fromextension = (fromextnam, fromextnum)
- else:
- fromextension = fromextnum
- if toextnum:
- if toextnam:
- toextension = (toextnam, toextnum)
- else:
- toextension =toextnum
- else:
- toextension = fromextension
-
- hwcs = readAltWCS(fobj,fromextension,wcskey=ukey,verbose=verbose)
- if hwcs is None:
- return
-
- for k in hwcs.keys():
- key = k[:-1]
- if key in fobj[toextension].header:
- #fobj[toextension].header.update(key=key, value = hwcs[k])
- fobj[toextension].header[key] = hwcs[k]
- else:
- continue
- if key == 'O' and 'TDDALPHA' in fobj[toextension].header:
- fobj[toextension].header['TDDALPHA'] = 0.0
- fobj[toextension].header['TDDBETA'] = 0.0
- if 'ORIENTAT' in fobj[toextension].header:
- norient = np.rad2deg(np.arctan2(hwcs['CD1_2'+'%s' %ukey], hwcs['CD2_2'+'%s' %ukey]))
- fobj[toextension].header['ORIENTAT'] = norient
- # Reset 2014 TDD keywords prior to computing new values (if any are computed)
- for kw in ['TDD_CYA','TDD_CYB','TDD_CXA','TDD_CXB']:
- if kw in fobj[toextension].header:
- fobj[toextension].header[kw] = 0.0
-
-#header operations
-def _check_headerpars(fobj, ext):
- if not isinstance(fobj, fits.Header) and not isinstance(fobj, fits.HDUList) \
- and not isinstance(fobj, str):
- raise ValueError("Expected a file name, a file object or a header\n")
-
- if not isinstance(fobj, fits.Header):
- #raise ValueError("Expected a valid ext parameter when input is a file")
- if not isinstance(ext, int) and not isinstance(ext, tuple):
- raise ValueError("Expected ext to be a number or a tuple, e.g. ('SCI', 1)\n")
-
-def _getheader(fobj, ext):
- if isinstance(fobj, str):
- hdr = fits.getheader(fobj,ext)
- elif isinstance(fobj, fits.Header):
- hdr = fobj
- else:
- hdr = fobj[ext].header
- return hdr
-
-def readAltWCS(fobj, ext, wcskey=' ',verbose=False):
- """ Reads in alternate WCS from specified extension
-
- Parameters
- ----------
- fobj : str, `astropy.io.fits.HDUList`
- fits filename or fits file object
- containing alternate/primary WCS(s) to be converted
- wcskey : str
- [" ",A-Z]
- alternate/primary WCS key that will be replaced by the new key
- ext : int
- fits extension number
-
- Returns
- -------
- hdr: fits.Header
- header object with ONLY the keywords for specified alternate WCS
- """
- if isinstance(fobj, str):
- fobj = fits.open(fobj)
-
- hdr = _getheader(fobj,ext)
- try:
- nwcs = pywcs.WCS(hdr, fobj=fobj, key=wcskey)
- except KeyError:
- if verbose:
- print('readAltWCS: Could not read WCS with key %s' %wcskey)
- print(' Skipping %s[%s]' % (fobj.filename(), str(ext)))
- return None
- hwcs = nwcs.to_header()
-
- if nwcs.wcs.has_cd():
- hwcs = pc2cd(hwcs, key=wcskey)
-
- return hwcs
-
-def convertAltWCS(fobj,ext,oldkey=" ",newkey=' '):
- """
- Translates the alternate/primary WCS with one key to an alternate/primary WCS with
- another key.
-
- Parameters
- ----------
- fobj : str, `astropy.io.fits.HDUList`, or `astropy.io.fits.Header`
- fits filename, fits file object or fits header
- containing alternate/primary WCS(s) to be converted
- ext : int
- extension number
- oldkey : str
- [" ",A-Z]
- alternate/primary WCS key that will be replaced by the new key
- newkey : str
- [" ",A-Z]
- new alternate/primary WCS key
-
- Returns
- -------
- hdr: `astropy.io.fits.Header`
- header object with keywords renamed from oldkey to newkey
- """
- hdr = readAltWCS(fobj,ext,wcskey=oldkey)
- if hdr is None:
- return None
- # Converting WCS to new key
- for card in hdr:
- if oldkey == ' ' or oldkey == '':
- cname = card
- else:
- cname = card.rstrip(oldkey)
- hdr.rename_key(card,cname+newkey,force=True)
-
- return hdr
-
-def wcskeys(fobj, ext=None):
- """
- Returns a list of characters used in the header for alternate
- WCS description with WCSNAME keyword
-
- Parameters
- ----------
- fobj : str, `astropy.io.fits.HDUList` or `astropy.io.fits.Header`
- fits file name, fits file object or fits header
- ext : int or None
- extension number
- if None, fobj must be a header
- """
- _check_headerpars(fobj, ext)
- hdr = _getheader(fobj, ext)
- names = hdr["WCSNAME*"]
- d = []
- for key in names:
- wkey = key.replace('WCSNAME','')
- if wkey == '': wkey = ' '
- d.append(wkey)
- return d
-
-def wcsnames(fobj, ext=None):
- """
- Returns a dictionary of wcskey: WCSNAME pairs
-
- Parameters
- ----------
- fobj : stri, `astropy.io.fits.HDUList` or `astropy.io.fits.Header`
- fits file name, fits file object or fits header
- ext : int or None
- extension number
- if None, fobj must be a header
-
- """
- _check_headerpars(fobj, ext)
- hdr = _getheader(fobj, ext)
- names = hdr["WCSNAME*"]
- d = {}
- for keyword, value in names.items():
- wkey = keyword.replace('WCSNAME','')
- if wkey == '': wkey = ' '
- d[wkey] = value
- return d
-
-def available_wcskeys(fobj, ext=None):
- """
- Returns a list of characters which are not used in the header
- with WCSNAME keyword. Any of them can be used to save a new
- WCS.
-
- Parameters
- ----------
- fobj : str, `astropy.io.fits.HDUList` or `astropy.io.fits.Header`
- fits file name, fits file object or fits header
- ext : int or None
- extension number
- if None, fobj must be a header
- """
- _check_headerpars(fobj, ext)
- hdr = _getheader(fobj, ext)
- all_keys = list(string.ascii_uppercase)
- used_keys = wcskeys(hdr)
- try:
- used_keys.remove(" ")
- except ValueError:
- pass
- [all_keys.remove(key) for key in used_keys]
- return all_keys
-
-def next_wcskey(fobj, ext=None):
- """
- Returns next available character to be used for an alternate WCS
-
- Parameters
- ----------
- fobj : str, `astropy.io.fits.HDUList` or `astropy.io.fits.Header`
- fits file name, fits file object or fits header
- ext : int or None
- extension number
- if None, fobj must be a header
- """
- _check_headerpars(fobj, ext)
- hdr = _getheader(fobj, ext)
- allkeys = available_wcskeys(hdr)
- if allkeys != []:
- return allkeys[0]
- else:
- return None
-
-def getKeyFromName(header, wcsname):
- """
- If WCSNAME is found in header, return its key, else return
- None. This is used to update an alternate WCS
- repeatedly and not generate new keys every time.
-
- Parameters
- ----------
- header : `astropy.io.fits.Header`
- wcsname : str
- value of WCSNAME
- """
- wkey = None
- names = wcsnames(header)
- wkeys = []
- for item in names.items():
- if item[1].lower() == wcsname.lower():
- wkeys.append(item[0])
- wkeys.sort()
- if len(wkeys) > 0:
- wkey = wkeys[-1]
- else:
- wkey = None
- return wkey
-
-def pc2cd(hdr, key=' '):
- """
- Convert a CD PC matrix to a CD matrix.
-
- WCSLIB (and PyWCS) recognizes CD keywords as input
- but converts them and works internally with the PC matrix.
- to_header() returns the PC matrix even if the i nput was a
- CD matrix. To keep input and output consistent we check
- for has_cd and convert the PC back to CD.
-
- Parameters
- ----------
- hdr: `astropy.io.fits.Header`
-
- """
- for c in ['1_1', '1_2', '2_1', '2_2']:
- try:
- val = hdr['PC'+c+'%s' % key]
- del hdr['PC'+c+ '%s' % key]
- except KeyError:
- if c=='1_1' or c == '2_2':
- val = 1.
- else:
- val = 0.
- #hdr.update(key='CD'+c+'%s' %key, value=val)
- hdr['CD{0}{1}'.format(c, key)] = val
- return hdr
-
-def _parpasscheck(fobj, ext, wcskey, fromext=None, toext=None, reusekey=False):
- """
- Check input parameters to altwcs functions
-
- fobj : str or `astropy.io.fits.HDUList` object
- a file name or a file object
- ext : int, a tuple, a python list of integers or a python list
- of tuples (e.g.('sci',1))
- fits extensions to work with
- wcskey : str
- "A"-"Z" or " "- Used for one of 26 alternate WCS definitions
- wcsname : str
- (optional)
- if given and wcskey is " ", will try to restore by WCSNAME value
- reusekey : bool
- A flag which indicates whether to reuse a wcskey in the header
- """
- if not isinstance(fobj, fits.HDUList):
- print("First parameter must be a fits file object or a file name.")
- return False
-
- # first one covers the case of an object created in memory
- # (e.g. headerlet) for which fileinfo returns None
- if fobj.fileinfo(0) is None:
- pass
- else:
- # an HDUList object with associated file
- if fobj.fileinfo(0)['filemode'] is not 'update':
- print("First parameter must be a file name or a file object opened in 'update' mode.")
- return False
-
- if not isinstance(ext, int) and not isinstance(ext, tuple) \
- and not isinstance(ext,str) \
- and not isinstance(ext, list) and ext is not None:
- print("Ext must be integer, tuple, string,a list of int extension numbers, \n\
- or a list of tuples representing a fits extension, for example ('sci', 1).")
- return False
-
- if not isinstance(fromext, str) and fromext is not None:
- print("fromext must be a string representing a valid extname")
- return False
-
- if not isinstance(toext, list) and not isinstance(toext, str) and \
- toext is not None :
- print("toext must be a string or a list of strings representing extname")
- return False
-
- if len(wcskey) != 1:
- print('Parameter wcskey must be a character - one of "A"-"Z" or " "')
- return False
-
- return True
-
-def closefobj(fname, f):
- """
- Functions in this module accept as input a file name or a file object.
- If the input was a file name (string) we close the object. If the user
- passed a file object we leave it to the user to close it.
- """
- if isinstance(fname, str):
- f.close()
-
-def mapFitsExt2HDUListInd(fname, extname):
- """
- Map FITS extensions with 'EXTNAME' to HDUList indexes.
- """
-
- if not isinstance(fname, fits.HDUList):
- f = fits.open(fname)
- close_file = True
- else:
- f = fname
- close_file = False
- d = {}
- for hdu in f:
- if 'EXTNAME' in hdu.header and hdu.header['EXTNAME'] == extname:
- extver = hdu.header['EXTVER']
- d[(extname, extver)] = f.index_of((extname, extver))
- if close_file:
- f.close()
- return d
diff --git a/lib/stwcs/wcsutil/convertwcs.py b/lib/stwcs/wcsutil/convertwcs.py
deleted file mode 100644
index a384eb1..0000000
--- a/lib/stwcs/wcsutil/convertwcs.py
+++ /dev/null
@@ -1,118 +0,0 @@
-from __future__ import print_function
-try:
- import stwcs
- from stwcs import wcsutil
-except:
- stwcs = None
-
-from stsci.tools import fileutil
-
-OPUS_WCSKEYS = ['OCRVAL1','OCRVAL2','OCRPIX1','OCRPIX2',
- 'OCD1_1','OCD1_2','OCD2_1','OCD2_2',
- 'OCTYPE1','OCTYPE2']
-
-
-def archive_prefix_OPUS_WCS(fobj,extname='SCI'):
- """ Identifies WCS keywords which were generated by OPUS and archived
- using a prefix of 'O' for all 'SCI' extensions in the file
-
- Parameters
- ----------
- fobj : str or `astropy.io.fits.HDUList`
- Filename or fits object of a file
-
- """
- if stwcs is None:
- print('=====================')
- print('The STWCS package is needed to convert an old-style OPUS WCS to an alternate WCS')
- print('=====================')
- raise ImportError
-
-
- closefits = False
- if isinstance(fobj,str):
- # A filename was provided as input
- fobj = fits.open(fobj,mode='update')
- closefits=True
-
- # Define the header
- ext = ('sci',1)
- hdr = fobj[ext].header
-
- numextn = fileutil.countExtn(fobj)
- extlist = []
- for e in range(1,numextn+1):
- extlist.append(('sci',e))
-
- # Insure that the 'O' alternate WCS is present
- if 'O' not in wcsutil.wcskeys(hdr):
- # if not, archive the Primary WCS as the default OPUS WCS
- wcsutil.archiveWCS(fobj,extlist, wcskey='O', wcsname='OPUS')
-
- # find out how many SCI extensions are in the image
- numextn = fileutil.countExtn(fobj,extname=extname)
- if numextn == 0:
- extname = 'PRIMARY'
-
- # create HSTWCS object from PRIMARY WCS
- wcsobj = wcsutil.HSTWCS(fobj,ext=ext,wcskey='O')
- # get list of WCS keywords
- wcskeys = list(wcsobj.wcs2header().keys())
-
- # For each SCI extension...
- for e in range(1,numextn+1):
- # Now, look for any WCS keywords with a prefix of 'O'
- for key in wcskeys:
- okey = 'O'+key[:7]
- hdr = fobj[(extname,e)].header
- if okey in hdr:
- # Update alternate WCS keyword with prefix-O OPUS keyword value
- hdr[key] = hdr[okey]
-
- if closefits:
- fobj.close()
-
-def create_prefix_OPUS_WCS(fobj,extname='SCI'):
- """ Creates alternate WCS with a prefix of 'O' for OPUS generated WCS values
- to work with old MultiDrizzle.
-
- Parameters
- ----------
- fobj : str or `astropy.io.fits.HDUList`
- Filename or fits object of a file
-
- Raises
- ------
- IOError:
- if input FITS object was not opened in 'update' mode
-
- """
- # List of O-prefix keywords to create
- owcskeys = OPUS_WCSKEYS
-
- closefits = False
- if isinstance(fobj,str):
- # A filename was provided as input
- fobj = fits.open(fobj, mode='update')
- closefits=True
- else:
- # check to make sure this FITS obj has been opened in update mode
- if fobj.fileinfo(0)['filemode'] != 'update':
- print('File not opened with "mode=update". Quitting...')
- raise IOError
-
- # check for existance of O-prefix WCS
- if owcskeys[0] not in fobj['sci',1].header:
-
- # find out how many SCI extensions are in the image
- numextn = fileutil.countExtn(fobj,extname=extname)
- if numextn == 0:
- extname = ''
- for extn in range(1,numextn+1):
- hdr = fobj[(extname,extn)].header
- for okey in owcskeys:
- hdr[okey] = hdr[okey[1:]+'O']
-
- # Close FITS image if we had to open it...
- if closefits:
- fobj.close()
diff --git a/lib/stwcs/wcsutil/getinput.py b/lib/stwcs/wcsutil/getinput.py
deleted file mode 100644
index 8ee1123..0000000
--- a/lib/stwcs/wcsutil/getinput.py
+++ /dev/null
@@ -1,62 +0,0 @@
-from astropy.io import fits
-from stsci.tools import irafglob, fileutil, parseinput
-
-def parseSingleInput(f=None, ext=None):
- if isinstance(f, str):
- # create an HSTWCS object from a filename
- if ext != None:
- filename = f
- if isinstance(ext,tuple):
- if ext[0] == '':
- extnum = ext[1] # handle ext=('',1)
- else:
- extnum = ext
- else:
- extnum = int(ext)
- elif ext == None:
- filename, ext = fileutil.parseFilename(f)
- ext = fileutil.parseExtn(ext)
- if ext[0] == '':
- extnum = int(ext[1]) #handle ext=('',extnum)
- else:
- extnum = ext
- phdu = fits.open(filename)
- hdr0 = phdu[0].header
- try:
- ehdr = phdu[extnum].header
- except (IndexError, KeyError) as e:
- raise e.__class__('Unable to get extension %s.' % extnum)
-
- elif isinstance(f, fits.HDUList):
- phdu = f
- if ext == None:
- extnum = 0
- else:
- extnum = ext
- ehdr = f[extnum].header
- hdr0 = f[0].header
- filename = hdr0.get('FILENAME', "")
-
- else:
- raise ValueError('Input must be a file name string or a'
- '`astropy.io.fits.HDUList` object')
-
- return filename, hdr0, ehdr, phdu
-
-
-def parseMultipleInput(input):
- if isinstance(input, str):
- if input[0] == '@':
- # input is an @ file
- filelist = irafglob.irafglob(input)
- else:
- try:
- filelist, output = parseinput.parseinput(input)
- except IOError: raise
- elif isinstance(input, list):
- if isinstance(input[0], wcsutil.HSTWCS):
- # a list of HSTWCS objects
- return input
- else:
- filelist = input[:]
- return filelist
diff --git a/lib/stwcs/wcsutil/headerlet.py b/lib/stwcs/wcsutil/headerlet.py
deleted file mode 100644
index c0dd9b0..0000000
--- a/lib/stwcs/wcsutil/headerlet.py
+++ /dev/null
@@ -1,2754 +0,0 @@
-"""
-This module implements headerlets.
-
-A headerlet serves as a mechanism for encapsulating WCS information
-which can be used to update the WCS solution of an image. The idea
-came up first from the desire for passing improved astrometric
-solutions for HST data and provide those solutions in a manner
-that would not require getting entirely new images from the archive
-when only the WCS information has been updated.
-
-"""
-
-from __future__ import absolute_import, division, print_function
-import os
-import sys
-import functools
-import logging
-import textwrap
-import copy
-import time
-
-import numpy as np
-from astropy.io import fits
-#import pywcs
-from astropy import wcs as pywcs
-from astropy.utils import lazyproperty
-
-from stsci.tools.fileutil import countExtn
-from stsci.tools import fileutil as fu
-from stsci.tools import parseinput
-
-from stwcs.updatewcs import utils
-from . import altwcs
-from . import wcscorr
-from .hstwcs import HSTWCS
-from .mappings import basic_wcs
-
-#### Logging support functions
-class FuncNameLoggingFormatter(logging.Formatter):
- def __init__(self, fmt=None, datefmt=None):
- if '%(funcName)s' not in fmt:
- fmt = '%(funcName)s' + fmt
- logging.Formatter.__init__(self, fmt=fmt, datefmt=datefmt)
-
- def format(self, record):
- record = copy.copy(record)
- if hasattr(record, 'funcName') and record.funcName == 'init_logging':
- record.funcName = ''
- else:
- record.funcName += ' '
- return logging.Formatter.format(self, record)
-
-
-logger = logging.getLogger(__name__)
-formatter = FuncNameLoggingFormatter("%(levelname)s: %(message)s")
-ch = logging.StreamHandler()
-ch.setFormatter(formatter)
-ch.setLevel(logging.CRITICAL)
-logger.addHandler(ch)
-logger.setLevel(logging.DEBUG)
-
-FITS_STD_KW = ['XTENSION', 'BITPIX', 'NAXIS', 'PCOUNT',
- 'GCOUNT', 'EXTNAME', 'EXTVER', 'ORIGIN',
- 'INHERIT', 'DATE', 'IRAF-TLM']
-
-DEFAULT_SUMMARY_COLS = ['HDRNAME', 'WCSNAME', 'DISTNAME', 'AUTHOR', 'DATE',
- 'SIPNAME', 'NPOLFILE', 'D2IMFILE', 'DESCRIP']
-COLUMN_DICT = {'vals': [], 'width': []}
-COLUMN_FMT = '{:<{width}}'
-
-
-def init_logging(funcname=None, level=100, mode='w', **kwargs):
- """
-
- Initialize logging for a function
-
- Parameters
- ----------
- funcname: string
- Name of function which will be recorded in log
- level: int, or bool, or string
- int or string : Logging level
- bool: False - switch off logging
- Text logging level for the message ("DEBUG", "INFO",
- "WARNING", "ERROR", "CRITICAL")
- mode: 'w' or 'a'
- attach to logfile ('a' or start a new logfile ('w')
-
- """
- for hndl in logger.handlers:
- if isinstance(hndl, logging.FileHandler):
- has_file_handler = True
- else:
- has_file_handler = False
- if level:
- if not has_file_handler:
- logname = 'headerlet.log'
- fh = logging.FileHandler(logname, mode=mode)
- fh.setFormatter(formatter)
- fh.setLevel(logging.DEBUG)
- logger.addHandler(fh)
- logger.info("%s: Starting %s with arguments:\n\t %s" %
- (time.asctime(), funcname, kwargs))
-
-
-def with_logging(func):
- @functools.wraps(func)
- def wrapped(*args, **kw):
- level = kw.get('logging', 100)
- mode = kw.get('logmode', 'w')
- func_args = kw.copy()
- if sys.version_info[0] >= 3:
- argnames = func.__code__.co_varnames
- else:
- argnames = func.func_code.co_varnames
-
- for argname, arg in zip(argnames, args):
- func_args[argname] = arg
-
- init_logging(func.__name__, level, mode, **func_args)
- return func(*args, **kw)
- return wrapped
-
-#### Utility functions
-def is_par_blank(par):
- return par in ['', ' ', 'INDEF', "None", None]
-
-def parse_filename(fname, mode='readonly'):
- """
- Interprets the input as either a filename of a file that needs to be opened
- or a PyFITS object.
-
- Parameters
- ----------
- fname : str, `astropy.io.fits.HDUList`
- Input pointing to a file or `astropy.io.fits.HDUList` object.
- An input filename (str) will be expanded as necessary to
- interpret any environmental variables
- included in the filename.
-
- mode : string
- Specifies what mode to use when opening the file, if it needs
- to open the file at all [Default: 'readonly']
-
- Returns
- -------
- fobj : `astropy.io.fits.HDUList`
- FITS file handle for input
-
- fname : str
- Name of input file
-
- close_fobj : bool
- Flag specifying whether or not fobj needs to be closed since it was
- opened by this function. This allows a program to know whether they
- need to worry about closing the FITS object as opposed to letting
- the higher level interface close the object.
-
- """
- close_fobj = False
- if not isinstance(fname, list):
- if sys.version_info[0] >= 3:
- is_string = isinstance(fname, str)
- else:
- is_string = isinstance(fname, basestring)
- if is_string:
- fname = fu.osfn(fname)
- fobj = fits.open(fname, mode=mode)
- close_fobj = True
- else:
- fobj = fname
- if hasattr(fobj, 'filename'):
- fname = fobj.filename()
- else:
- fname = ''
- return fobj, fname, close_fobj
-
-def get_headerlet_kw_names(fobj, kw='HDRNAME'):
- """
- Returns a list of specified keywords from all HeaderletHDU
- extensions in a science file.
-
- Parameters
- ----------
- fobj : str, `astropy.io.fits.HDUList`
- kw : str
- Name of keyword to be read and reported
- """
-
- fobj, fname, open_fobj = parse_filename(fobj)
-
- hdrnames = []
- for ext in fobj:
- if isinstance(ext, fits.hdu.base.NonstandardExtHDU):
- hdrnames.append(ext.header[kw])
-
- if open_fobj:
- fobj.close()
-
- return hdrnames
-
-def get_header_kw_vals(hdr, kwname, kwval, default=0):
- if kwval is None:
- if kwname in hdr:
- kwval = hdr[kwname]
- else:
- kwval = default
- return kwval
-
-@with_logging
-def find_headerlet_HDUs(fobj, hdrext=None, hdrname=None, distname=None,
- strict=True, logging=False, logmode='w'):
- """
- Returns all HeaderletHDU extensions in a science file that matches
- the inputs specified by the user. If no hdrext, hdrname or distname are
- specified, this function will return a list of all HeaderletHDU objects.
-
- Parameters
- ----------
- fobj : str, `astropy.io.fits.HDUList`
- Name of FITS file or open fits object (`astropy.io.fits.HDUList` instance)
- hdrext : int, tuple or None
- index number(EXTVER) or extension tuple of HeaderletHDU to be returned
- hdrname : string
- value of HDRNAME for HeaderletHDU to be returned
- distname : string
- value of DISTNAME for HeaderletHDUs to be returned
- strict : bool [Default: True]
- Specifies whether or not at least one parameter needs to be provided
- If False, all extension indices returned if hdrext, hdrname and distname
- are all None. If True and hdrext, hdrname, and distname are all None,
- raise an Exception requiring one to be specified.
- logging : boolean
- enable logging to a file called headerlet.log
- logmode : 'w' or 'a'
- log file open mode
-
- Returns
- -------
- hdrlets : list
- A list of all matching HeaderletHDU extension indices (could be just one)
-
- """
-
- get_all = False
- if hdrext is None and hdrname is None and distname is None:
- if not strict:
- get_all = True
- else:
- mess = """\n
- =====================================================
- No valid Headerlet extension specified.
- Either "hdrname", "hdrext", or "distname" needs to be specified.
- =====================================================
- """
- logger.critical(mess)
- raise ValueError
-
- fobj, fname, open_fobj = parse_filename(fobj)
-
- hdrlets = []
- if hdrext is not None and isinstance(hdrext, int):
- if hdrext in range(len(fobj)): # insure specified hdrext is in fobj
- if isinstance(fobj[hdrext], fits.hdu.base.NonstandardExtHDU) and \
- fobj[hdrext].header['EXTNAME'] == 'HDRLET':
- hdrlets.append(hdrext)
- else:
- for ext in fobj:
- if isinstance(ext, fits.hdu.base.NonstandardExtHDU):
- if get_all:
- hdrlets.append(fobj.index(ext))
- else:
- if hdrext is not None:
- if isinstance(hdrext, tuple):
- hdrextname = hdrext[0]
- hdrextnum = hdrext[1]
- else:
- hdrextname = 'HDRLET'
- hdrextnum = hdrext
- hdrext_match = ((hdrext is not None) and
- (hdrextnum == ext.header['EXTVER']) and
- (hdrextname == ext.header['EXTNAME']))
- hdrname_match = ((hdrname is not None) and
- (hdrname == ext.header['HDRNAME']))
- distname_match = ((distname is not None) and
- (distname == ext.header['DISTNAME']))
- if hdrext_match or hdrname_match or distname_match:
- hdrlets.append(fobj.index(ext))
-
- if open_fobj:
- fobj.close()
-
- if len(hdrlets) == 0:
- if hdrname:
- kwerr = 'hdrname'
- kwval = hdrname
- elif hdrext:
- kwerr = 'hdrext'
- kwval = hdrext
- else:
- kwerr = 'distname'
- kwval = distname
- message = """\n
- =====================================================
- No valid Headerlet extension found!'
- "%s" = %s not found in %s.' % (kwerr, kwval, fname)
- =====================================================
- """
- logger.critical(message)
- raise ValueError
-
- return hdrlets
-
-def verify_hdrname_is_unique(fobj, hdrname):
- """
- Verifies that no other HeaderletHDU extension has the specified hdrname.
-
- Parameters
- ----------
- fobj : str, `astropy.io.fits.HDUList`
- Name of FITS file or open fits file object
- hdrname : str
- value of HDRNAME for HeaderletHDU to be compared as unique
-
- Returns
- -------
- unique: bool
- If True, no other HeaderletHDU has the specified HDRNAME value
- """
- hdrnames_list = get_headerlet_kw_names(fobj)
- unique = not(hdrname in hdrnames_list)
-
- return unique
-
-def update_versions(sourcehdr, desthdr):
- """
- Update keywords which store version numbers
- """
- phdukw = {'PYWCSVER': 'Version of PYWCS used to updated the WCS',
- 'UPWCSVER': 'Version of STWCS used to updated the WCS'
- }
- for key in phdukw:
- try:
- desthdr[key] = (sourcehdr[key], sourcehdr.comments[key])
- except KeyError:
- desthdr[key] = (" ", phdukw[key])
-
-def update_ref_files(source, dest):
- """
- Update the reference files name in the primary header of 'dest'
- using values from 'source'
-
- Parameters
- ----------
- source : `astropy.io.fits.Header`
- dest : `astropy.io.fits.Header`
- """
- logger.info("Updating reference files")
- phdukw = {'NPOLFILE': True,
- 'IDCTAB': True,
- 'D2IMFILE': True,
- 'SIPNAME': True,
- 'DISTNAME': True}
-
- for key in phdukw:
- try:
- try:
- del dest[key]
- except:
- pass
- dest.set(key, source[key], source.comments[key])
- except KeyError:
- phdukw[key] = False
- return phdukw
-
-def print_summary(summary_cols, summary_dict, pad=2, maxwidth=None, idcol=None,
- output=None, clobber=True, quiet=False ):
- """
- Print out summary dictionary to STDOUT, and possibly an output file
-
- """
- nrows = None
- if idcol:
- nrows = len(idcol['vals'])
-
- # Find max width of each column
- column_widths = {}
- for kw in summary_dict:
- colwidth = np.array(summary_dict[kw]['width']).max()
- if maxwidth:
- colwidth = min(colwidth, maxwidth)
- column_widths[kw] = colwidth + pad
- if nrows is None:
- nrows = len(summary_dict[kw]['vals'])
-
- # print rows now
- outstr = ''
- # Start with column names
- if idcol:
- outstr += COLUMN_FMT.format(idcol['name'], width=idcol['width'] + pad)
- for kw in summary_cols:
- outstr += COLUMN_FMT.format(kw, width=column_widths[kw])
- outstr += '\n'
- # Now, add a row for each headerlet
- for row in range(nrows):
- if idcol:
- outstr += COLUMN_FMT.format(idcol['vals'][row],
- width=idcol['width']+pad)
- for kw in summary_cols:
- val = summary_dict[kw]['vals'][row][:(column_widths[kw]-pad)]
- outstr += COLUMN_FMT.format(val, width=column_widths[kw])
- outstr += '\n'
- if not quiet:
- print(outstr)
-
- # If specified, write info to separate text file
- write_file = False
- if output:
- output = fu.osfn(output) # Expand any environment variables in filename
- write_file = True
- if os.path.exists(output):
- if clobber:
- os.remove(output)
- else:
- print('WARNING: Not writing results to file!')
- print(' Output text file ', output, ' already exists.')
- print(' Set "clobber" to True or move file before trying again.')
- write_file = False
- if write_file:
- fout = open(output, mode='w')
- fout.write(outstr)
- fout.close()
-
-#### Private utility functions
-def _create_primary_HDU(fobj, fname, wcsext, destim, hdrname, wcsname,
- sipname, npolfile, d2imfile,
- nmatch,catalog, wcskey,
- author, descrip, history):
- # convert input values into valid FITS kw values
- if author is None:
- author = ''
- if descrip is None:
- descrip = ''
-
- sipname, idctab = utils.build_sipname(fobj, fname, sipname)
- logger.info("Setting sipname value to %s" % sipname)
-
- npolname, npolfile = utils.build_npolname(fobj, npolfile)
- logger.info("Setting npolfile value to %s" % npolname)
-
- d2imname, d2imfile = utils.build_d2imname(fobj,d2imfile)
- logger.info("Setting d2imfile value to %s" % d2imname)
-
- distname = utils.build_distname(sipname, npolname, d2imname)
- logger.info("Setting distname to %s" % distname)
-
- # open file and parse comments
- if history not in ['', ' ', None, 'INDEF'] and os.path.isfile(history):
- f = open(fu.osfn(history))
- history = f.readlines()
- f.close()
- else:
- history = ''
-
- rms_ra = fobj[wcsext].header.get("CRDER1"+wcskey, 0)
- rms_dec = fobj[wcsext].header.get("CRDER2"+wcskey, 0)
- if not nmatch:
- nmatch = fobj[wcsext].header.get("NMATCH"+wcskey, 0)
- if not catalog:
- catalog = fobj[wcsext].header.get('CATALOG'+wcskey, "")
- # get the version of STWCS used to create the WCS of the science file.
- #try:
- #upwcsver = fobj[0].header.cards[fobj[0].header.index('UPWCSVER')]
- #except KeyError:
- #upwcsver = pyfits.Card("UPWCSVER", " ",
- #"Version of STWCS used to update the WCS")
- #try:
- #pywcsver = fobj[0].header.cards[fobj[0].header.index('PYWCSVER')]
- #except KeyError:
- #pywcsver = pyfits.Card("PYWCSVER", " ",
- #"Version of PYWCS used to update the WCS")
- upwcsver = fobj[0].header.get('UPWCSVER', "")
- pywcsver = fobj[0].header.get('PYWCSVER', "")
- # build Primary HDU
- phdu = fits.PrimaryHDU()
- phdu.header['DESTIM'] = (destim, 'Destination observation root name')
- phdu.header['HDRNAME'] = (hdrname, 'Headerlet name')
- fmt = "%Y-%m-%dT%H:%M:%S"
- phdu.header['DATE'] = (time.strftime(fmt), 'Date FITS file was generated')
- phdu.header['WCSNAME'] = (wcsname, 'WCS name')
- phdu.header['DISTNAME'] = (distname, 'Distortion model name')
- phdu.header['SIPNAME'] = (sipname,
- 'origin of SIP polynomial distortion model')
- phdu.header['NPOLFILE'] = (npolfile,
- 'origin of non-polynmial distortion model')
- phdu.header['D2IMFILE'] = (d2imfile,
- 'origin of detector to image correction')
- phdu.header['IDCTAB'] = (idctab,
- 'origin of Polynomial Distortion')
- phdu.header['AUTHOR'] = (author, 'headerlet created by this user')
- phdu.header['DESCRIP'] = (descrip,
- 'Short description of headerlet solution')
- phdu.header['RMS_RA'] = (rms_ra,
- 'RMS in RA at ref pix of headerlet solution')
- phdu.header['RMS_DEC'] = (rms_dec,
- 'RMS in Dec at ref pix of headerlet solution')
- phdu.header['NMATCH'] = (nmatch,
- 'Number of sources used for headerlet solution')
- phdu.header['CATALOG'] = (catalog,
- 'Astrometric catalog used for headerlet '
- 'solution')
- phdu.header['UPWCSVER'] = (upwcsver, "Version of STWCS used to update the WCS")
- phdu.header['PYWCSVER'] = (pywcsver, "Version of PYWCS used to update the WCS")
-
- # clean up history string in order to remove whitespace characters that
- # would cause problems with FITS
- if isinstance(history, list):
- history_str = ''
- for line in history:
- history_str += line
- else:
- history_str = history
- history_lines = textwrap.wrap(history_str, width=70)
- for hline in history_lines:
- phdu.header.add_history(hline)
-
- return phdu
-
-
-#### Public Interface functions
-@with_logging
-def extract_headerlet(filename, output, extnum=None, hdrname=None,
- clobber=False, logging=True):
- """
- Finds a headerlet extension in a science file and writes it out as
- a headerlet FITS file.
-
- If both hdrname and extnum are given they should match, if not
- raise an Exception
-
- Parameters
- ----------
- filename: string or HDUList or Python list
- This specifies the name(s) of science file(s) from which headerlets
- will be extracted.
-
- String input formats supported include use of wild-cards, IRAF-style
- '@'-files (given as '@<filename>') and comma-separated list of names.
- An input filename (str) will be expanded as necessary to interpret
- any environmental variables included in the filename.
- If a list of filenames has been specified, it will extract a
- headerlet from the same extnum from all filenames.
- output: string
- Filename or just rootname of output headerlet FITS file
- If string does not contain '.fits', it will create a filename with
- '_hlet.fits' suffix
- extnum: int
- Extension number which contains the headerlet to be written out
- hdrname: string
- Unique name for headerlet, stored as the HDRNAME keyword
- It stops if a value is not provided and no extnum has been specified
- clobber: bool
- If output file already exists, this parameter specifies whether or not
- to overwrite that file [Default: False]
- logging: boolean
- enable logging to a file
-
- """
-
- if isinstance(filename, fits.HDUList):
- filename = [filename]
- else:
- filename, oname = parseinput.parseinput(filename)
-
- for f in filename:
- fobj, fname, close_fobj = parse_filename(f)
- frootname = fu.buildNewRootname(fname)
- if hdrname in ['', ' ', None, 'INDEF'] and extnum is None:
- if close_fobj:
- fobj.close()
- logger.critical("Expected a valid extnum or hdrname parameter")
- raise ValueError
- if hdrname is not None:
- extn_from_hdrname = find_headerlet_HDUs(fobj, hdrname=hdrname)[0]
- if extn_from_hdrname != extnum:
- logger.critical("hdrname and extnmu should refer to the same FITS extension")
- raise ValueError
- else:
- hdrhdu = fobj[extn_from_hdrname]
- else:
- hdrhdu = fobj[extnum]
-
- if not isinstance(hdrhdu, HeaderletHDU):
- logger.critical("Specified extension is not a headerlet")
- raise ValueError
-
- hdrlet = hdrhdu.headerlet
-
- if output is None:
- output = frootname
-
- if '.fits' in output:
- outname = output
- else:
- outname = '%s_hlet.fits' % output
-
- hdrlet.tofile(outname, clobber=clobber)
-
- if close_fobj:
- fobj.close()
-
-
-@with_logging
-def write_headerlet(filename, hdrname, output=None, sciext='SCI',
- wcsname=None, wcskey=None, destim=None,
- sipname=None, npolfile=None, d2imfile=None,
- author=None, descrip=None, history=None,
- nmatch=None, catalog=None,
- attach=True, clobber=False, logging=False):
-
- """
- Save a WCS as a headerlet FITS file.
-
- This function will create a headerlet, write out the headerlet to a
- separate headerlet file, then, optionally, attach it as an extension
- to the science image (if it has not already been archived)
-
- Either wcsname or wcskey must be provided; if both are given, they must
- match a valid WCS.
-
- Updates wcscorr if necessary.
-
- Parameters
- ----------
- filename: string or HDUList or Python list
- This specifies the name(s) of science file(s) from which headerlets
- will be created and written out.
- String input formats supported include use of wild-cards, IRAF-style
- '@'-files (given as '@<filename>') and comma-separated list of names.
- An input filename (str) will be expanded as necessary to interpret
- any environmental variables included in the filename.
- hdrname: string
- Unique name for this headerlet, stored as HDRNAME keyword
- output: string or None
- Filename or just rootname of output headerlet FITS file
- If string does not contain '.fits', it will create a filename
- starting with the science filename and ending with '_hlet.fits'.
- If None, a default filename based on the input filename will be
- generated for the headerlet FITS filename
- sciext: string
- name (EXTNAME) of extension that contains WCS to be saved
- wcsname: string
- name of WCS to be archived, if " ": stop
- wcskey: one of A...Z or " " or "PRIMARY"
- if " " or "PRIMARY" - archive the primary WCS
- destim: string
- DESTIM keyword
- if NOne, use ROOTNAME or science file name
- sipname: string or None (default)
- Name of unique file where the polynomial distortion coefficients were
- read from. If None, the behavior is:
- The code looks for a keyword 'SIPNAME' in the science header
- If not found, for HST it defaults to 'IDCTAB'
- If there is no SIP model the value is 'NOMODEL'
- If there is a SIP model but no SIPNAME, it is set to 'UNKNOWN'
- npolfile: string or None (default)
- Name of a unique file where the non-polynomial distortion was stored.
- If None:
- The code looks for 'NPOLFILE' in science header.
- If 'NPOLFILE' was not found and there is no npol model, it is set to 'NOMODEL'
- If npol model exists, it is set to 'UNKNOWN'
- d2imfile: string
- Name of a unique file where the detector to image correction was
- stored. If None:
- The code looks for 'D2IMFILE' in the science header.
- If 'D2IMFILE' is not found and there is no d2im correction,
- it is set to 'NOMODEL'
- If d2im correction exists, but 'D2IMFILE' is missing from science
- header, it is set to 'UNKNOWN'
- author: string
- Name of user who created the headerlet, added as 'AUTHOR' keyword
- to headerlet PRIMARY header
- descrip: string
- Short description of the solution provided by the headerlet
- This description will be added as the single 'DESCRIP' keyword
- to the headerlet PRIMARY header
- history: filename, string or list of strings
- Long (possibly multi-line) description of the solution provided
- by the headerlet. These comments will be added as 'HISTORY' cards
- to the headerlet PRIMARY header
- If filename is specified, it will format and attach all text from
- that file as the history.
- attach: bool
- Specify whether or not to attach this headerlet as a new extension
- It will verify that no other headerlet extension has been created with
- the same 'hdrname' value.
- clobber: bool
- If output file already exists, this parameter specifies whether or not
- to overwrite that file [Default: False]
- logging: boolean
- enable file logging
- """
-
- if isinstance(filename, fits.HDUList):
- filename = [filename]
- else:
- filename, oname = parseinput.parseinput(filename)
-
- for f in filename:
- if isinstance(f, str):
- fname = f
- else:
- fname = f.filename()
-
- if wcsname in [None, ' ', '', 'INDEF'] and wcskey is None:
- message = """\n
- No valid WCS found found in %s.
- A valid value for either "wcsname" or "wcskey"
- needs to be specified.
- """ % fname
- logger.critical(message)
- raise ValueError
-
- # Translate 'wcskey' value for PRIMARY WCS to valid altwcs value of ' '
- if wcskey == 'PRIMARY':
- wcskey = ' '
-
- if attach:
- umode = 'update'
- else:
- umode = 'readonly'
-
- fobj, fname, close_fobj = parse_filename(f, mode=umode)
-
- # Interpret sciext input for this file
- if isinstance(sciext, int):
- sciextlist = [sciext] # allow for specification of simple FITS header
- elif isinstance(sciext, str):
- numsciext = countExtn(fobj, sciext)
- if numsciext > 0:
- sciextlist = [tuple((sciext,i)) for i in range(1, numsciext+1)]
- else:
- sciextlist = [0]
- elif isinstance(sciext, list):
- sciextlist = sciext
- else:
- errstr = "Expected sciext to be a list of FITS extensions with science data\n"+\
- " a valid EXTNAME string, or an integer."
- logger.critical(errstr)
- raise ValueError
-
- wnames = altwcs.wcsnames(fobj,ext=sciextlist[0])
-
- # Insure that WCSCORR table has been created with all original
- # WCS's recorded prior to adding the headerlet WCS
- wcscorr.init_wcscorr(fobj)
-
- if wcsname is None:
- scihdr = fobj[sciextlist[0]].header
- wname = scihdr['wcsname'+wcskey]
- else:
- wname = wcsname
- if hdrname in [None, ' ', '']:
- hdrname = wcsname
-
- logger.critical('Creating the headerlet from image %s' % fname)
- hdrletobj = create_headerlet(fobj, sciext=sciextlist,
- wcsname=wname, wcskey=wcskey,
- hdrname=hdrname,
- sipname=sipname, npolfile=npolfile,
- d2imfile=d2imfile, author=author,
- descrip=descrip, history=history,
- nmatch=nmatch, catalog=catalog,
- logging=False)
-
- if attach:
- # Check to see whether or not a HeaderletHDU with
- #this hdrname already exists
- hdrnames = get_headerlet_kw_names(fobj)
- if hdrname not in hdrnames:
- hdrlet_hdu = HeaderletHDU.fromheaderlet(hdrletobj)
-
- if destim is not None:
- hdrlet_hdu.header['destim'] = destim
-
- fobj.append(hdrlet_hdu)
-
- # Update the WCSCORR table with new rows from the headerlet's WCSs
- wcscorr.update_wcscorr(fobj, source=hdrletobj,
- extname='SIPWCS', wcs_id=wname)
-
- utils.updateNEXTENDKw(fobj)
- fobj.flush()
- else:
- message = """
- Headerlet with hdrname %s already archived for WCS %s.
- No new headerlet appended to %s.
- """ % (hdrname, wname, fname)
- logger.critical(message)
-
- if close_fobj:
- logger.info('Closing image in write_headerlet()...')
- fobj.close()
-
- frootname = fu.buildNewRootname(fname)
-
- if output is None:
- # Generate default filename for headerlet FITS file
- outname = '{0}_hlet.fits'.format(frootname)
- else:
- outname = output
-
- if not outname.endswith('.fits'):
- outname = '{0}_{1}_hlet.fits'.format(frootname,outname)
-
- # If user specifies an output filename for headerlet, write it out
- hdrletobj.tofile(outname, clobber=clobber)
- logger.critical( 'Created Headerlet file %s ' % outname)
-
- del hdrletobj
-
-@with_logging
-def create_headerlet(filename, sciext='SCI', hdrname=None, destim=None,
- wcskey=" ", wcsname=None,
- sipname=None, npolfile=None, d2imfile=None,
- author=None, descrip=None, history=None,
- nmatch=None, catalog=None,
- logging=False, logmode='w'):
- """
- Create a headerlet from a WCS in a science file
- If both wcskey and wcsname are given they should match, if not
- raise an Exception
-
- Parameters
- ----------
- filename: string or HDUList
- Either a filename or PyFITS HDUList object for the input science file
- An input filename (str) will be expanded as necessary to interpret
- any environmental variables included in the filename.
- sciext: string or python list (default: 'SCI')
- Extension in which the science data with the linear WCS is.
- The headerlet will be created from these extensions.
- If string - a valid EXTNAME is expected
- If int - specifies an extension with a valid WCS, such as 0 for a
- simple FITS file
- If list - a list of FITS extension numbers or strings representing
- extension tuples, e.g. ('SCI, 1') is expected.
- hdrname: string
- value of HDRNAME keyword
- Takes the value from the HDRNAME<wcskey> keyword, if not available from WCSNAME<wcskey>
- It stops if neither is found in the science file and a value is not provided
- destim: string or None
- name of file this headerlet can be applied to
- if None, use ROOTNAME keyword
- wcskey: char (A...Z) or " " or "PRIMARY" or None
- a char representing an alternate WCS to be used for the headerlet
- if " ", use the primary (default)
- if None use wcsname
- wcsname: string or None
- if wcskey is None use wcsname specified here to choose an alternate WCS for the headerlet
- sipname: string or None (default)
- Name of unique file where the polynomial distortion coefficients were
- read from. If None, the behavior is:
- The code looks for a keyword 'SIPNAME' in the science header
- If not found, for HST it defaults to 'IDCTAB'
- If there is no SIP model the value is 'NOMODEL'
- If there is a SIP model but no SIPNAME, it is set to 'UNKNOWN'
- npolfile: string or None (default)
- Name of a unique file where the non-polynomial distortion was stored.
- If None:
- The code looks for 'NPOLFILE' in science header.
- If 'NPOLFILE' was not found and there is no npol model, it is set to 'NOMODEL'
- If npol model exists, it is set to 'UNKNOWN'
- d2imfile: string
- Name of a unique file where the detector to image correction was
- If None:
- The code looks for 'D2IMFILE' in the science header.
- If 'D2IMFILE' is not found and there is no d2im correction,
- it is set to 'NOMODEL'
- If d2im correction exists, but 'D2IMFILE' is missing from science
- header, it is set to 'UNKNOWN'
- author: string
- Name of user who created the headerlet, added as 'AUTHOR' keyword
- to headerlet PRIMARY header
- descrip: string
- Short description of the solution provided by the headerlet
- This description will be added as the single 'DESCRIP' keyword
- to the headerlet PRIMARY header
- history: filename, string or list of strings
- Long (possibly multi-line) description of the solution provided
- by the headerlet. These comments will be added as 'HISTORY' cards
- to the headerlet PRIMARY header
- If filename is specified, it will format and attach all text from
- that file as the history.
- nmatch: int (optional)
- Number of sources used in the new solution fit
- catalog: string (optional)
- Astrometric catalog used for headerlet solution
- logging: boolean
- enable file logging
- logmode: 'w' or 'a'
- log file open mode
-
- Returns
- -------
- Headerlet object
-
- """
- if wcskey == 'O':
- message = "Warning: 'O' is a reserved key for the original WCS. Quitting..."
- logger.info(message)
- return None
-
- fobj, fname, close_file = parse_filename(filename)
- # based on `sciext` create a list of (EXTNAME, EXTVER) tuples
- # of extensions with WCS to be saved in a headerlet
- sciext = get_extname_extver_list(fobj, sciext)
- logger.debug("Data extensions from which to create headerlet:\n\t %s"
- % (str(sciext)))
- if not sciext:
- logger.critical("No valid target extensions found in file %s" % fname)
- raise ValueError
-
- # Define extension to evaluate for verification of input parameters
- wcsext = sciext[0]
- logger.debug("sciext in create_headerlet is %s" % str(sciext))
- # Translate 'wcskey' value for PRIMARY WCS to valid altwcs value of ' '
- if wcskey == 'PRIMARY':
- wcskey = ' '
- logger.info("wcskey reset from 'PRIMARY' to ' '")
- wcskey = wcskey.upper()
- wcsnamekw = "".join(["WCSNAME", wcskey.upper()]).rstrip()
- hdrnamekw = "".join(["HDRNAME", wcskey.upper()]).rstrip()
-
- wnames = altwcs.wcsnames(fobj, ext=wcsext)
- if not wcsname:
- # User did not specify a value for 'wcsname'
- if wcsnamekw in fobj[wcsext].header:
- #check if there's a WCSNAME for this wcskey in the header
- wcsname = fobj[wcsext].header[wcsnamekw]
- logger.info("Setting wcsname from header[%s] to %s" % (wcsnamekw, wcsname))
- else:
- if hdrname not in ['', ' ', None, "INDEF"]:
- """
- If wcsname for this wcskey was not provided
- and WCSNAME<wcskey> does not exist in the header
- and hdrname is provided, then
- use hdrname as WCSNAME for the headerlet.
- """
- wcsname = hdrname
- logger.debug("Setting wcsname from hdrname to %s" % hdrname)
- else:
- if hdrnamekw in fobj[wcsext].header:
- wcsname = fobj[wcsext].header[hdrnamekw]
- logger.debug("Setting wcsname from header[%s] to %s" % (hdrnamekw, wcsname))
- else:
- message = """
- Required keywords 'HDRNAME' or 'WCSNAME' not found!
- Please specify a value for parameter 'hdrname'
- or update header with 'WCSNAME' keyword.
- """
- logger.critical(message)
- raise KeyError
- else:
- # Verify that 'wcsname' and 'wcskey' values specified by user reference
- # the same WCS
- wname = fobj[wcsext].header[wcsnamekw]
- if wcsname != wname:
- message = "\tInconsistent values for 'wcskey' and 'wcsname' specified!\n"
- message += " 'wcskey' = %s and 'wcsname' = %s. \n" % (wcskey, wcsname)
- message += "Actual value of %s found to be %s. \n" % (wcsnamekw, wname)
- logger.critical(message)
- raise KeyError
- wkeys = altwcs.wcskeys(fobj, ext=wcsext)
- if wcskey != ' ':
- if wcskey not in wkeys:
- logger.critical('No WCS with wcskey=%s found in extension %s. Skipping...' % (wcskey, str(wcsext)))
- raise ValueError("No WCS with wcskey=%s found in extension %s. Skipping...' % (wcskey, str(wcsext))")
-
- # get remaining required keywords
- if destim is None:
- if 'ROOTNAME' in fobj[0].header:
- destim = fobj[0].header['ROOTNAME']
- logger.info("Setting destim to rootname of the science file")
- else:
- destim = fname
- logger.info('DESTIM not provided')
- logger.info('Keyword "ROOTNAME" not found')
- logger.info('Using file name as DESTIM')
-
- if not hdrname:
- # check if HDRNAME<wcskey> is in header
- if hdrnamekw in fobj[wcsext].header:
- hdrname = fobj[wcsext].header[hdrnamekw]
- else:
- if wcsnamekw in fobj[wcsext].header:
- hdrname = fobj[wcsext].header[wcsnamekw]
- message = """
- Using default value for HDRNAME of "%s" derived from %s.
- """ % (hdrname, wcsnamekw)
- logger.info(message)
- logger.info("Setting hdrname to %s from header[%s]"
- % (hdrname, wcsnamekw))
- else:
- message = "Required keywords 'HDRNAME' or 'WCSNAME' not found"
- logger.critical(message)
- raise KeyError
-
-
-
- hdul = []
- phdu = _create_primary_HDU(fobj, fname, wcsext, destim, hdrname, wcsname,
- sipname, npolfile, d2imfile,
- nmatch, catalog, wcskey,
- author, descrip, history)
- hdul.append(phdu)
- wcsdvarr_extns = []
- """
- nd2i is a counter for d2i extensions to be used when the science file
- has an old d2i correction format. The old format did not write EXTVER
- kw for the d2i correction in the science header bu tthe new format expects
- them.
- """
- nd2i_extver = 1
- for ext in sciext:
- wkeys = altwcs.wcskeys(fobj, ext=ext)
- if wcskey != ' ':
- if wcskey not in wkeys:
- logger.debug(
- 'No WCS with wcskey=%s found in extension %s. '
- 'Skipping...' % (wcskey, str(ext)))
- raise ValueError("")
-
- hwcs = HSTWCS(fobj, ext=ext, wcskey=wcskey)
-
- whdul = hwcs.to_fits(relax=True, key=" ")
- if hasattr(hwcs, 'orientat'):
- orient_comment = "positions angle of image y axis (deg. e of n)"
- whdul[0].header['ORIENTAT'] = (hwcs.orientat, orient_comment)
-
- whdul[0].header.append(('TG_ENAME', ext[0], 'Target science data extname'))
- whdul[0].header.append(('TG_EVER', ext[1], 'Target science data extver'))
-
- if hwcs.wcs.has_cd():
- whdul[0].header = altwcs.pc2cd(whdul[0].header)
-
- idckw = hwcs._idc2hdr()
- whdul[0].header.extend(idckw)
-
- if hwcs.det2im1 or hwcs.det2im2:
- try:
- whdul[0].header.append(fobj[ext].header.cards['D2IMEXT'])
- except KeyError:
- pass
- whdul[0].header.extend(fobj[ext].header.cards['D2IMERR*'])
- if 'D2IM1.EXTVER' in whdul[0].header:
- try:
- whdul[0].header['D2IM1.EXTVER'] = fobj[ext].header['D2IM1.EXTVER']
- except KeyError:
- whdul[0].header['D2IM1.EXTVER'] = nd2i_extver
- nd2i_extver += 1
- if 'D2IM2.EXTVER' in whdul[0].header:
- try:
- whdul[0].header['D2IM2.EXTVER'] = fobj[ext].header['D2IM2.EXTVER']
- except KeyError:
- whdul[0].header['D2IM2.EXTVER'] = nd2i_extver
- nd2i_extver += 1
-
- if hwcs.cpdis1 or hwcs.cpdis2:
- whdul[0].header.extend(fobj[ext].header.cards['CPERR*'])
- try:
- whdul[0].header.append(fobj[ext].header.cards['NPOLEXT'])
- except KeyError:
- pass
- if 'DP1.EXTVER' in whdul[0].header:
- whdul[0].header['DP1.EXTVER'] = fobj[ext].header['DP1.EXTVER']
- if 'DP2.EXTVER' in whdul[0].header:
- whdul[0].header['DP2.EXTVER'] = fobj[ext].header['DP2.EXTVER']
- ihdu = fits.ImageHDU(header=whdul[0].header, name='SIPWCS')
-
- if ext[0] != "PRIMARY":
- ihdu.update_ext_version(fobj[ext].header['EXTVER'], comment='Extension version')
-
- hdul.append(ihdu)
-
- if hwcs.cpdis1:
- whdu = whdul[('WCSDVARR', 1)].copy()
- whdu.update_ext_version(fobj[ext].header['DP1.EXTVER'])
- hdul.append(whdu)
- if hwcs.cpdis2:
- whdu = whdul[('WCSDVARR', 2)].copy()
- whdu.update_ext_version(fobj[ext].header['DP2.EXTVER'])
- hdul.append(whdu)
-
- if hwcs.det2im1:
- whdu = whdul[('D2IMARR', 1)].copy()
- whdu.update_ext_version(ihdu.header['D2IM1.EXTVER'])
- hdul.append(whdu)
- if hwcs.det2im2:
- whdu = whdul[('D2IMARR', 2)].copy()
- whdu.update_ext_version(ihdu.header['D2IM2.EXTVER'])
- hdul.append(whdu)
-
-
- #if hwcs.det2im1 or hwcs.det2im2:
- #try:
- #darr = hdul[('D2IMARR', 1)]
- #except KeyError:
- #whdu = whdul[('D2IMARR')]
- #whdu.update_ext_version(1)
- #hdul.append(whdu)
- if close_file:
- fobj.close()
-
- hlet = Headerlet(hdul, logging=logging, logmode='a')
- hlet.init_attrs()
- return hlet
-
-@with_logging
-def apply_headerlet_as_primary(filename, hdrlet, attach=True, archive=True,
- force=False, logging=False, logmode='a'):
- """
- Apply headerlet 'hdrfile' to a science observation 'destfile' as the primary WCS
-
- Parameters
- ----------
- filename: string or list of strings
- File name(s) of science observation whose WCS solution will be updated
- hdrlet: string or list of strings
- Headerlet file(s), must match 1-to-1 with input filename(s)
- attach: boolean
- True (default): append headerlet to FITS file as a new extension.
- archive: boolean
- True (default): before updating, create a headerlet with the
- WCS old solution.
- force: boolean
- If True, this will cause the headerlet to replace the current PRIMARY
- WCS even if it has a different distortion model. [Default: False]
- logging: boolean
- enable file logging
- logmode: 'w' or 'a'
- log file open mode
- """
- if not isinstance(filename, list):
- filename = [filename]
- if not isinstance(hdrlet, list):
- hdrlet = [hdrlet]
- if len(hdrlet) != len(filename):
- logger.critical("Filenames must have matching headerlets. "
- "{0:d} filenames and {1:d} headerlets specified".format(len(filename),len(hdrlet)))
-
- for fname,h in zip(filename,hdrlet):
- print("Applying {0} as Primary WCS to {1}".format(h,fname))
- hlet = Headerlet.fromfile(h, logging=logging, logmode=logmode)
- hlet.apply_as_primary(fname, attach=attach, archive=archive,
- force=force)
-
-
-@with_logging
-def apply_headerlet_as_alternate(filename, hdrlet, attach=True, wcskey=None,
- wcsname=None, logging=False, logmode='w'):
- """
- Apply headerlet to a science observation as an alternate WCS
-
- Parameters
- ----------
- filename: string or list of strings
- File name(s) of science observation whose WCS solution will be updated
- hdrlet: string or list of strings
- Headerlet file(s), must match 1-to-1 with input filename(s)
- attach: boolean
- flag indicating if the headerlet should be attached as a
- HeaderletHDU to fobj. If True checks that HDRNAME is unique
- in the fobj and stops if not.
- wcskey: string
- Key value (A-Z, except O) for this alternate WCS
- If None, the next available key will be used
- wcsname: string
- Name to be assigned to this alternate WCS
- WCSNAME is a required keyword in a Headerlet but this allows the
- user to change it as desired.
- logging: boolean
- enable file logging
- logmode: 'a' or 'w'
- """
- if not isinstance(filename, list):
- filename = [filename]
- if not isinstance(hdrlet, list):
- hdrlet = [hdrlet]
- if len(hdrlet) != len(filename):
- logger.critical("Filenames must have matching headerlets. "
- "{0:d} filenames and {1:d} headerlets specified".format(len(filename),len(hdrlet)))
-
- for fname,h in zip(filename,hdrlet):
- print('Applying {0} as an alternate WCS to {1}'.format(h,fname))
- hlet = Headerlet.fromfile(h, logging=logging, logmode=logmode)
- hlet.apply_as_alternate(fname, attach=attach,
- wcsname=wcsname, wcskey=wcskey)
-
-
-@with_logging
-def attach_headerlet(filename, hdrlet, logging=False, logmode='a'):
- """
- Attach Headerlet as an HeaderletHDU to a science file
-
- Parameters
- ----------
- filename: HDUList or list of HDULists
- science file(s) to which the headerlet should be applied
- hdrlet: string, Headerlet object or list of strings or Headerlet objects
- string representing a headerlet file(s), must match 1-to-1 input filename(s)
- logging: boolean
- enable file logging
- logmode: 'a' or 'w'
- """
- if not isinstance(filename, list):
- filename = [filename]
- if not isinstance(hdrlet, list):
- hdrlet = [hdrlet]
- if len(hdrlet) != len(filename):
- logger.critical("Filenames must have matching headerlets. "
- "{0:d} filenames and {1:d} headerlets specified".format(len(filename),len(hdrlet)))
-
- for fname,h in zip(filename,hdrlet):
- print('Attaching {0} as Headerlet extension to {1}'.format(h,fname))
- hlet = Headerlet.fromfile(h, logging=logging, logmode=logmode)
- hlet.attach_to_file(fname,archive=True)
-
-
-@with_logging
-def delete_headerlet(filename, hdrname=None, hdrext=None, distname=None,
- logging=False, logmode='w'):
- """
- Deletes HeaderletHDU(s) with same HDRNAME from science files
-
- Notes
- -----
- One of hdrname, hdrext or distname should be given.
- If hdrname is given - delete a HeaderletHDU with a name HDRNAME from fobj.
- If hdrext is given - delete HeaderletHDU in extension.
- If distname is given - deletes all HeaderletHDUs with a specific distortion model from fobj.
- Updates wcscorr
-
- Parameters
- ----------
- filename: string, HDUList or list of strings
- Filename can be specified as a single filename or HDUList, or
- a list of filenames
- Each input filename (str) will be expanded as necessary to interpret
- any environmental variables included in the filename.
- hdrname: string or None
- HeaderletHDU primary header keyword HDRNAME
- hdrext: int, tuple or None
- HeaderletHDU FITS extension number
- tuple has the form ('HDRLET', 1)
- distname: string or None
- distortion model as specified in the DISTNAME keyword
- logging: boolean
- enable file logging
- logmode: 'a' or 'w'
- """
- if not isinstance(filename, list):
- filename = [filename]
-
- for f in filename:
- print("Deleting Headerlet from ",f)
- _delete_single_headerlet(f, hdrname=hdrname, hdrext=hdrext,
- distname=distname, logging=logging, logmode='a')
-
-def _delete_single_headerlet(filename, hdrname=None, hdrext=None, distname=None,
- logging=False, logmode='w'):
- """
- Deletes HeaderletHDU(s) from a SINGLE science file
-
- Notes
- -----
- One of hdrname, hdrext or distname should be given.
- If hdrname is given - delete a HeaderletHDU with a name HDRNAME from fobj.
- If hdrext is given - delete HeaderletHDU in extension.
- If distname is given - deletes all HeaderletHDUs with a specific distortion model from fobj.
- Updates wcscorr
-
- Parameters
- ----------
- filename: string or HDUList
- Either a filename or PyFITS HDUList object for the input science file
- An input filename (str) will be expanded as necessary to interpret
- any environmental variables included in the filename.
- hdrname: string or None
- HeaderletHDU primary header keyword HDRNAME
- hdrext: int, tuple or None
- HeaderletHDU FITS extension number
- tuple has the form ('HDRLET', 1)
- distname: string or None
- distortion model as specified in the DISTNAME keyword
- logging: boolean
- enable file logging
- logmode: 'a' or 'w'
- """
- hdrlet_ind = find_headerlet_HDUs(filename, hdrname=hdrname, hdrext=hdrext,
- distname=distname, logging=logging, logmode='a')
- if len(hdrlet_ind) == 0:
- message = """
- No HDUs deleted... No Headerlet HDUs found with '
- hdrname = %s
- hdrext = %s
- distname = %s
- Please review input parameters and try again.
- """ % (hdrname, str(hdrext), distname)
- logger.critical(message)
- return
-
- fobj, fname, close_fobj = parse_filename(filename, mode='update')
-
- # delete row(s) from WCSCORR table now...
- #
- #
- if hdrname not in ['', ' ', None, 'INDEF']:
- selections = {'hdrname': hdrname}
- elif hdrname in ['', ' ', None, 'INDEF'] and hdrext is not None:
- selections = {'hdrname': fobj[hdrext].header['hdrname']}
- else:
- selections = {'distname': distname}
- wcscorr.delete_wcscorr_row(fobj['WCSCORR'].data, selections)
-
- # delete the headerlet extension now
- for hdrind in hdrlet_ind:
- del fobj[hdrind]
-
- utils.updateNEXTENDKw(fobj)
- # Update file object with changes
- fobj.flush()
- # close file, if was opened by this function
- if close_fobj:
- fobj.close()
- logger.critical('Deleted headerlet from extension(s) %s ' % str(hdrlet_ind))
-
-
-def headerlet_summary(filename, columns=None, pad=2, maxwidth=None,
- output=None, clobber=True, quiet=False):
- """
-
- Print a summary of all HeaderletHDUs in a science file to STDOUT, and
- optionally to a text file
- The summary includes:
- HDRLET_ext_number HDRNAME WCSNAME DISTNAME SIPNAME NPOLFILE D2IMFILE
-
- Parameters
- ----------
- filename: string or HDUList
- Either a filename or PyFITS HDUList object for the input science file
- An input filename (str) will be expanded as necessary to interpret
- any environmental variables included in the filename.
- columns: list
- List of headerlet PRIMARY header keywords to report in summary
- By default (set to None), it will use the default set of keywords
- defined as the global list DEFAULT_SUMMARY_COLS
- pad: int
- Number of padding spaces to put between printed columns
- [Default: 2]
- maxwidth: int
- Maximum column width(not counting padding) for any column in summary
- By default (set to None), each column's full width will be used
- output: string (optional)
- Name of optional output file to record summary. This filename
- can contain environment variables.
- [Default: None]
- clobber: bool
- If True, will overwrite any previous output file of same name
- quiet: bool
- If True, will NOT report info to STDOUT
-
- """
- if columns is None:
- summary_cols = DEFAULT_SUMMARY_COLS
- else:
- summary_cols = columns
-
- summary_dict = {}
- for kw in summary_cols:
- summary_dict[kw] = copy.deepcopy(COLUMN_DICT)
-
- # Define Extension number column
- extnums_col = copy.deepcopy(COLUMN_DICT)
- extnums_col['name'] = 'EXTN'
- extnums_col['width'] = 6
-
- fobj, fname, close_fobj = parse_filename(filename)
- # find all HDRLET extensions and combine info into a single summary
- for extn in fobj:
- if 'extname' in extn.header and extn.header['extname'] == 'HDRLET':
- hdrlet_indx = fobj.index_of(('hdrlet', extn.header['extver']))
- try:
- ext_cols, ext_summary = extn.headerlet.summary(columns=summary_cols)
- extnums_col['vals'].append(hdrlet_indx)
- for kw in summary_cols:
- for key in COLUMN_DICT:
- summary_dict[kw][key].extend(ext_summary[kw][key])
- except:
- print("Skipping headerlet")
- print("Could not read Headerlet from extension ", hdrlet_indx)
-
- if close_fobj:
- fobj.close()
-
- # Print out the summary dictionary
- print_summary(summary_cols, summary_dict, pad=pad, maxwidth=maxwidth,
- idcol=extnums_col, output=output,
- clobber=clobber, quiet=quiet)
-
-
-@with_logging
-def restore_from_headerlet(filename, hdrname=None, hdrext=None, archive=True,
- force=False, logging=False, logmode='w'):
- """
- Restores a headerlet as a primary WCS
-
- Parameters
- ----------
- filename: string or HDUList
- Either a filename or PyFITS HDUList object for the input science file
- An input filename (str) will be expanded as necessary to interpret
- any environmental variables included in the filename.
- hdrname: string
- HDRNAME keyword of HeaderletHDU
- hdrext: int or tuple
- Headerlet extension number of tuple ('HDRLET',2)
- archive: boolean (default: True)
- When the distortion model in the headerlet is the same as the distortion model of
- the science file, this flag indicates if the primary WCS should be saved as an alternate
- nd a headerlet extension.
- When the distortion models do not match this flag indicates if the current primary and
- alternate WCSs should be archived as headerlet extensions and alternate WCS.
- force: boolean (default:False)
- When the distortion models of the headerlet and the primary do not match, and archive
- is False, this flag forces an update of the primary.
- logging: boolean
- enable file logging
- logmode: 'a' or 'w'
- """
-
- hdrlet_ind = find_headerlet_HDUs(filename, hdrext=hdrext, hdrname=hdrname)
-
- fobj, fname, close_fobj = parse_filename(filename, mode='update')
-
- if len(hdrlet_ind) > 1:
- if hdrext:
- kwerr = 'hdrext'
- kwval = hdrext
- else:
- kwerr = 'hdrname'
- kwval = hdrname
- message = """
- Multiple Headerlet extensions found with the same name.
- %d Headerlets with "%s" = %s found in %s.
- """% (len(hdrlet_ind), kwerr, kwval, fname)
- if close_fobj:
- fobj.close()
- logger.critical(message)
- raise ValueError
-
- hdrlet_indx = hdrlet_ind[0]
-
- # read headerlet from HeaderletHDU into memory
- if hasattr(fobj[hdrlet_ind[0]], 'hdulist'):
- hdrlet = fobj[hdrlet_indx].hdulist
- else:
- hdrlet = fobj[hdrlet_indx].headerlet # older convention in PyFITS
-
- # read in the names of the extensions which HeaderletHDU updates
- extlist = []
- for ext in hdrlet:
- if 'extname' in ext.header and ext.header['extname'] == 'SIPWCS':
- # convert from string to tuple or int
- sciext = eval(ext.header['sciext'])
- extlist.append(fobj[sciext])
- # determine whether distortion is the same
- current_distname = hdrlet[0].header['distname']
- same_dist = True
- if current_distname != fobj[0].header['distname']:
- same_dist = False
- if not archive and not force:
- if close_fobj:
- fobj.close()
- message = """
- Headerlet does not have the same distortion as image!
- Set "archive"=True to save old distortion model, or
- set "force"=True to overwrite old model with new.
- """
- logger.critical(message)
- raise ValueError
-
- # check whether primary WCS has been archived already
- # Use information from first 'SCI' extension
- priwcs_name = None
-
- scihdr = extlist[0].header
- sci_wcsnames = altwcs.wcsnames(scihdr).values()
- if 'hdrname' in scihdr:
- priwcs_hdrname = scihdr['hdrname']
- else:
- if 'wcsname' in scihdr:
- priwcs_hdrname = priwcs_name = scihdr['wcsname']
- else:
- if 'idctab' in scihdr:
- priwcs_hdrname = ''.join(['IDC_',
- utils.extract_rootname(scihdr['idctab'], suffix='_idc')])
- else:
- priwcs_hdrname = 'UNKNOWN'
- priwcs_name = priwcs_hdrname
- scihdr['WCSNAME'] = priwcs_name
-
- priwcs_unique = verify_hdrname_is_unique(fobj, priwcs_hdrname)
- if archive and priwcs_unique:
- if priwcs_unique:
- newhdrlet = create_headerlet(fobj, sciext=scihdr['extname'],
- hdrname=priwcs_hdrname)
- newhdrlet.attach_to_file(fobj)
- #
- # copy hdrlet as a primary
- #
- hdrlet.apply_as_primary(fobj, attach=False, archive=archive, force=force)
-
- utils.updateNEXTENDKw(fobj)
- fobj.flush()
- if close_fobj:
- fobj.close()
-
-
-@with_logging
-def restore_all_with_distname(filename, distname, primary, archive=True,
- sciext='SCI', logging=False, logmode='w'):
- """
- Restores all HeaderletHDUs with a given distortion model as alternate WCSs and a primary
-
- Parameters
- --------------
- filename: string or HDUList
- Either a filename or PyFITS HDUList object for the input science file
- An input filename (str) will be expanded as necessary to interpret
- any environmental variables included in the filename.
- distname: string
- distortion model as represented by a DISTNAME keyword
- primary: int or string or None
- HeaderletHDU to be restored as primary
- if int - a fits extension
- if string - HDRNAME
- if None - use first HeaderletHDU
- archive: boolean (default True)
- flag indicating if HeaderletHDUs should be created from the
- primary and alternate WCSs in fname before restoring all matching
- headerlet extensions
- logging: boolean
- enable file logging
- logmode: 'a' or 'w'
- """
-
- fobj, fname, close_fobj = parse_filename(filename, mode='update')
-
- hdrlet_ind = find_headerlet_HDUs(fobj, distname=distname)
- if len(hdrlet_ind) == 0:
- message = """
- No Headerlet extensions found with
-
- DISTNAME = %s in %s.
-
- For a full list of DISTNAMEs found in all headerlet extensions:
-
- get_headerlet_kw_names(fobj, kw='DISTNAME')
- """ % (distname, fname)
- if close_fobj:
- fobj.close()
- logger.critical(message)
- raise ValueError
-
- # Interpret 'primary' parameter input into extension number
- if primary is None:
- primary_ind = hdrlet_ind[0]
- elif isinstance(primary, int):
- primary_ind = primary
- else:
- primary_ind = None
- for ind in hdrlet_ind:
- if fobj[ind].header['hdrname'] == primary:
- primary_ind = ind
- break
- if primary_ind is None:
- if close_fobj:
- fobj.close()
- message = """
- No Headerlet extensions found with DISTNAME = %s in %s.
- """ % (primary, fname)
- logger.critical(message)
- raise ValueError
- # Check to see whether 'primary' HeaderletHDU has same distname as user
- # specified on input
-
- # read headerlet from HeaderletHDU into memory
- if hasattr(fobj[primary_ind], 'hdulist'):
- primary_hdrlet = fobj[primary_ind].hdulist
- else:
- primary_hdrlet = fobj[primary_ind].headerlet # older convention in PyFITS
- pri_distname = primary_hdrlet[0].header['distname']
- if pri_distname != distname:
- if close_fobj:
- fobj.close()
- message = """
- Headerlet extension to be used as PRIMARY WCS
- has "DISTNAME" = %s
- "DISTNAME" = %s was specified on input.
- All updated WCSs must have same DISTNAME. Quitting...'
- """ % (pri_distname, distname)
- logger.critical(message)
- raise ValueError
-
- # read in the names of the WCSs which the HeaderletHDUs will update
- wnames = altwcs.wcsnames(fobj[sciext, 1].header)
-
- # work out how many HeaderletHDUs will be used to update the WCSs
- numhlt = len(hdrlet_ind)
- hdrnames = get_headerlet_kw_names(fobj, kw='wcsname')
-
- # read in headerletHDUs and update WCS keywords
- for hlet in hdrlet_ind:
- if fobj[hlet].header['distname'] == distname:
- if hasattr(fobj[hlet], 'hdulist'):
- hdrlet = fobj[hlet].hdulist
- else:
- hdrlet = fobj[hlet].headerlet # older convention in PyFITS
- if hlet == primary_ind:
- hdrlet.apply_as_primary(fobj, attach=False,
- archive=archive, force=True)
- else:
- hdrlet.apply_as_alternate(fobj, attach=False,
- wcsname=hdrlet[0].header['wcsname'])
-
- utils.updateNEXTENDKw(fobj)
- fobj.flush()
- if close_fobj:
- fobj.close()
-
-
-@with_logging
-def archive_as_headerlet(filename, hdrname, sciext='SCI',
- wcsname=None, wcskey=None, destim=None,
- sipname=None, npolfile=None, d2imfile=None,
- author=None, descrip=None, history=None,
- nmatch=None, catalog=None,
- logging=False, logmode='w'):
- """
- Save a WCS as a headerlet extension and write it out to a file.
-
- This function will create a headerlet, attach it as an extension to the
- science image (if it has not already been archived) then, optionally,
- write out the headerlet to a separate headerlet file.
-
- Either wcsname or wcskey must be provided, if both are given, they must match a valid WCS
- Updates wcscorr if necessary.
-
- Parameters
- ----------
- filename: string or HDUList
- Either a filename or PyFITS HDUList object for the input science file
- An input filename (str) will be expanded as necessary to interpret
- any environmental variables included in the filename.
- hdrname: string
- Unique name for this headerlet, stored as HDRNAME keyword
- sciext: string
- name (EXTNAME) of extension that contains WCS to be saved
- wcsname: string
- name of WCS to be archived, if " ": stop
- wcskey: one of A...Z or " " or "PRIMARY"
- if " " or "PRIMARY" - archive the primary WCS
- destim: string
- DESTIM keyword
- if NOne, use ROOTNAME or science file name
- sipname: string or None (default)
- Name of unique file where the polynomial distortion coefficients were
- read from. If None, the behavior is:
- The code looks for a keyword 'SIPNAME' in the science header
- If not found, for HST it defaults to 'IDCTAB'
- If there is no SIP model the value is 'NOMODEL'
- If there is a SIP model but no SIPNAME, it is set to 'UNKNOWN'
- npolfile: string or None (default)
- Name of a unique file where the non-polynomial distortion was stored.
- If None:
- The code looks for 'NPOLFILE' in science header.
- If 'NPOLFILE' was not found and there is no npol model, it is set to 'NOMODEL'
- If npol model exists, it is set to 'UNKNOWN'
- d2imfile: string
- Name of a unique file where the detector to image correction was
- stored. If None:
- The code looks for 'D2IMFILE' in the science header.
- If 'D2IMFILE' is not found and there is no d2im correction,
- it is set to 'NOMODEL'
- If d2im correction exists, but 'D2IMFILE' is missing from science
- header, it is set to 'UNKNOWN'
- author: string
- Name of user who created the headerlet, added as 'AUTHOR' keyword
- to headerlet PRIMARY header
- descrip: string
- Short description of the solution provided by the headerlet
- This description will be added as the single 'DESCRIP' keyword
- to the headerlet PRIMARY header
- history: filename, string or list of strings
- Long (possibly multi-line) description of the solution provided
- by the headerlet. These comments will be added as 'HISTORY' cards
- to the headerlet PRIMARY header
- If filename is specified, it will format and attach all text from
- that file as the history.
- logging: boolean
- enable file folling
- logmode: 'w' or 'a'
- log file open mode
- """
-
- fobj, fname, close_fobj = parse_filename(filename, mode='update')
-
- if wcsname in [None, ' ', '', 'INDEF'] and wcskey is None:
- message = """
- No valid WCS found found in %s.
- A valid value for either "wcsname" or "wcskey"
- needs to be specified.
- """ % fname
- if close_fobj:
- fobj.close()
- logger.critical(message)
- raise ValueError
-
- # Translate 'wcskey' value for PRIMARY WCS to valid altwcs value of ' '
- if wcskey == 'PRIMARY':
- wcskey = ' '
- wcskey = wcskey.upper()
-
- numhlt = countExtn(fobj, 'HDRLET')
-
- if wcsname is None:
- scihdr = fobj[sciext, 1].header
- wcsname = scihdr['wcsname'+wcskey]
-
- if hdrname in [None, ' ', '']:
- hdrname = wcsname
-
- # Check to see whether or not a HeaderletHDU with this hdrname already
- # exists
- hdrnames = get_headerlet_kw_names(fobj)
- if hdrname not in hdrnames:
- hdrletobj = create_headerlet(fobj, sciext=sciext,
- wcsname=wcsname, wcskey=wcskey,
- hdrname=hdrname,
- sipname=sipname, npolfile=npolfile,
- d2imfile=d2imfile, author=author,
- descrip=descrip, history=history,
- nmatch=nmatch, catalog=catalog,
- logging=False)
- hlt_hdu = HeaderletHDU.fromheaderlet(hdrletobj)
-
- if destim is not None:
- hlt_hdu[0].header['destim'] = destim
-
- fobj.append(hlt_hdu)
-
- utils.updateNEXTENDKw(fobj)
- fobj.flush()
- else:
- message = """
- Headerlet with hdrname %s already archived for WCS %s
- No new headerlet appended to %s .
- """ % (hdrname, wcsname, fname)
- logger.critical(message)
-
- if close_fobj:
- fobj.close()
-
-#### Headerlet Class definitions
-class Headerlet(fits.HDUList):
- """
- A Headerlet class
- Ref: http://mediawiki.stsci.edu/mediawiki/index.php/Telescopedia:Headerlets
- """
-
- def __init__(self, hdus=[], file=None, logging=False, logmode='w'):
- """
- Parameters
- ----------
- hdus : list
- List of HDUs to be used to create the headerlet object itself
- file: string
- File-like object from which HDUs should be read
- logging: boolean
- enable file logging
- logmode: 'w' or 'a'
- for internal use only, indicates whether the log file
- should be open in attach or write mode
- """
- self.logging = logging
- init_logging('class Headerlet', level=logging, mode=logmode)
-
- super(Headerlet, self).__init__(hdus, file=file)
-
- def init_attrs(self):
- self.fname = self.filename()
- self.hdrname = self[0].header["HDRNAME"]
- self.wcsname = self[0].header["WCSNAME"]
- self.upwcsver = self[0].header.get("UPWCSVER", "")
- self.pywcsver = self[0].header.get("PYWCSVER", "")
- self.destim = self[0].header["DESTIM"]
- self.sipname = self[0].header["SIPNAME"]
- self.idctab = self[0].header["IDCTAB"]
- self.npolfile = self[0].header["NPOLFILE"]
- self.d2imfile = self[0].header["D2IMFILE"]
- self.distname = self[0].header["DISTNAME"]
-
- try:
- self.vafactor = self[("SIPWCS", 1)].header.get("VAFACTOR", 1) #None instead of 1?
- except (IndexError, KeyError):
- self.vafactor = self[0].header.get("VAFACTOR", 1) #None instead of 1?
- self.author = self[0].header["AUTHOR"]
- self.descrip = self[0].header["DESCRIP"]
-
- self.fit_kws = ['HDRNAME', 'NMATCH', 'CATALOG']
- self.history = ''
- # header['HISTORY'] returns an iterable of all HISTORY values
- if 'HISTORY' in self[0].header:
- for hist in self[0].header['HISTORY']:
- self.history += hist + '\n'
-
- self.d2imerr = 0
- self.axiscorr = 1
-
- # Overridden to support the Headerlet logging features
- @classmethod
- def fromfile(cls, fileobj, mode='readonly', memmap=False,
- save_backup=False, logging=False, logmode='w', **kwargs):
- hlet = super(cls, cls).fromfile(fileobj, mode, memmap, save_backup,
- **kwargs)
- if len(hlet) > 0:
- hlet.init_attrs()
- hlet.logging = logging
- init_logging('class Headerlet', level=logging, mode=logmode)
- return hlet
-
- @classmethod
- def fromstring(cls, data, **kwargs):
- hlet = super(cls, cls).fromstring(data, **kwargs)
- hlet.logging = logging
- init_logging('class Headerlet', level=logging, mode=logmode)
- return hlet
-
- def apply_as_primary(self, fobj, attach=True, archive=True, force=False):
- """
- Copy this headerlet as a primary WCS to fobj
-
- Parameters
- ----------
- fobj: string, HDUList
- science file to which the headerlet should be applied
- attach: boolean
- flag indicating if the headerlet should be attached as a
- HeaderletHDU to fobj. If True checks that HDRNAME is unique
- in the fobj and stops if not.
- archive: boolean (default is True)
- When the distortion model in the headerlet is the same as the
- distortion model of the science file, this flag indicates if
- the primary WCS should be saved as an alternate and a headerlet
- extension.
- When the distortion models do not match this flag indicates if
- the current primary and alternate WCSs should be archived as
- headerlet extensions and alternate WCS.
- force: boolean (default is False)
- When the distortion models of the headerlet and the primary do
- not match, and archive is False this flag forces an update
- of the primary
- """
- self.hverify()
- fobj, fname, close_dest = parse_filename(fobj, mode='update')
- if not self.verify_dest(fobj, fname):
- if close_dest:
- fobj.close()
- raise ValueError("Destination name does not match headerlet"
- "Observation %s cannot be updated with headerlet %s" % (fname, self.hdrname))
-
- # Check to see whether the distortion model in the destination
- # matches the distortion model in the headerlet being applied
-
- dname = self.get_destination_model(fobj)
- dist_models_equal = self.equal_distmodel(dname)
- if not dist_models_equal and not force:
- raise ValueError("Distortion models do not match"
- " To overwrite the distortion model, set force=True")
-
- orig_hlt_hdu = None
- numhlt = countExtn(fobj, 'HDRLET')
- hdrlet_extnames = get_headerlet_kw_names(fobj)
-
- # Insure that WCSCORR table has been created with all original
- # WCS's recorded prior to adding the headerlet WCS
- wcscorr.init_wcscorr(fobj)
-
-
- ### start archive
- # If archive has been specified
- # regardless of whether or not the distortion models are equal...
-
- numsip = countExtn(self, 'SIPWCS')
- sciext_list = []
- alt_hlethdu = []
- for i in range(1, numsip+1):
- sipheader = self[('SIPWCS', i)].header
- sciext_list.append((sipheader['TG_ENAME'], sipheader['TG_EVER']))
- target_ext = sciext_list[0]
- if archive:
- if 'wcsname' in fobj[target_ext].header:
- hdrname = fobj[target_ext].header['WCSNAME']
- wcsname = hdrname
- else:
- hdrname = fobj[0].header['ROOTNAME'] + '_orig'
- wcsname = None
- if hdrname not in hdrlet_extnames:
- # - if WCS has not been saved, write out WCS as headerlet extension
- # Create a headerlet for the original Primary WCS data in the file,
- # create an HDU from the original headerlet, and append it to
- # the file
- orig_hlt = create_headerlet(fobj, sciext=sciext_list, #[target_ext],
- wcsname=wcsname,
- hdrname=hdrname,
- logging=self.logging)
- orig_hlt_hdu = HeaderletHDU.fromheaderlet(orig_hlt)
- numhlt += 1
- orig_hlt_hdu.header['EXTVER'] = numhlt
- logger.info("Created headerlet %s to be attached to file" % hdrname)
- else:
- logger.info("Headerlet with name %s is already attached" % hdrname)
-
-
- if dist_models_equal:
- # Use the WCSNAME to determine whether or not to archive
- # Primary WCS as altwcs
- # wcsname = hwcs.wcs.name
- scihdr = fobj[target_ext].header
- if 'hdrname' in scihdr:
- priwcs_name = scihdr['hdrname']
- else:
- if 'wcsname' in scihdr:
- priwcs_name = scihdr['wcsname']
- else:
- if 'idctab' in scihdr:
- priwcs_name = ''.join(['IDC_',
- utils.extract_rootname(scihdr['idctab'],
- suffix='_idc')])
- else:
- priwcs_name = 'UNKNOWN'
- nextkey = altwcs.next_wcskey(fobj, ext=target_ext)
- altwcs.archiveWCS(fobj, ext=sciext_list, wcskey=nextkey,
- wcsname=priwcs_name)
- else:
-
- for hname in altwcs.wcsnames(fobj, ext=target_ext).values():
- if hname != 'OPUS' and hname not in hdrlet_extnames:
- # get HeaderletHDU for alternate WCS as well
- alt_hlet = create_headerlet(fobj, sciext=sciext_list,
- wcsname=hname, wcskey=wcskey,
- hdrname=hname, sipname=None,
- npolfile=None, d2imfile=None,
- author=None, descrip=None, history=None,
- logging=self.logging)
- numhlt += 1
- alt_hlet_hdu = HeaderletHDU.fromheaderlet(alt_hlet)
- alt_hlet_hdu.header['EXTVER'] = numhlt
- alt_hlethdu.append(alt_hlet_hdu)
- hdrlet_extnames.append(hname)
-
- self._del_dest_WCS_ext(fobj)
- for i in range(1, numsip+1):
- target_ext = sciext_list[i-1]
- self._del_dest_WCS(fobj, target_ext)
- sipwcs = HSTWCS(self, ('SIPWCS', i))
- idckw = sipwcs._idc2hdr()
- priwcs = sipwcs.to_fits(relax=True)
- numnpol = 1
- numd2im = 1
- if sipwcs.wcs.has_cd():
- priwcs[0].header = altwcs.pc2cd(priwcs[0].header)
- priwcs[0].header.extend(idckw)
- if 'crder1' in sipheader:
- for card in sipheader['crder*'].cards:
- priwcs[0].header.set(card.keyword, card.value, card.comment,
- after='WCSNAME')
- # Update WCS with HDRNAME as well
-
- for kw in ['SIMPLE', 'BITPIX', 'NAXIS', 'EXTEND']:
- try:
- priwcs[0].header.remove(kw)
- except ValueError:
- pass
-
- priwcs[0].header.set('WCSNAME', self[0].header['WCSNAME'], "")
- priwcs[0].header.set('WCSAXES', self[('SIPWCS', i)].header['WCSAXES'], "")
- priwcs[0].header.set('HDRNAME', self[0].header['HDRNAME'], "")
- if sipwcs.det2im1 or sipwcs.det2im2:
- try:
- d2imerr = self[('SIPWCS', i)].header['D2IMERR*']
- priwcs[0].header.extend(d2imerr)
- except KeyError:
- pass
- try:
- priwcs[0].header.append(self[('SIPWCS', i)].header.cards['D2IMEXT'])
- except KeyError:
- pass
- if 'D2IM1.EXTVER' in priwcs[0].header:
- priwcs[0].header['D2IM1.EXTVER'] = self[('SIPWCS', i)].header['D2IM1.EXTVER']
- priwcs[('D2IMARR', 1)].header['EXTVER'] = self[('SIPWCS', i)].header['D2IM1.EXTVER']
- if 'D2IM2.EXTVER' in priwcs[0].header:
- priwcs[0].header['D2IM2.EXTVER'] = self[('SIPWCS', i)].header['D2IM2.EXTVER']
- priwcs[('D2IMARR', 2)].header['EXTVER'] = self[('SIPWCS', i)].header['D2IM2.EXTVER']
- # D2IM1 will NOT exist for WFPC2 data...
- if 'D2IM1.EXTVER' in priwcs[0].header:
- # only set number of D2IM extensions to 2 if D2IM1 exists
- numd2im = 2
-
- if sipwcs.cpdis1 or sipwcs.cpdis2:
- try:
- cperr = self[('SIPWCS', i)].header['CPERR*']
- priwcs[0].header.extend(cperr)
- except KeyError:
- pass
- try:
- priwcs[0].header.append(self[('SIPWCS', i)].header.cards['NPOLEXT'])
- except KeyError:
- pass
- if 'DP1.EXTVER' in priwcs[0].header:
- priwcs[0].header['DP1.EXTVER'] = self[('SIPWCS', i)].header['DP1.EXTVER']
- priwcs[('WCSDVARR', 1)].header['EXTVER'] = self[('SIPWCS', i)].header['DP1.EXTVER']
- if 'DP2.EXTVER' in priwcs[0].header:
- priwcs[0].header['DP2.EXTVER'] = self[('SIPWCS', i)].header['DP2.EXTVER']
- priwcs[('WCSDVARR', 2)].header['EXTVER'] = self[('SIPWCS', i)].header['DP2.EXTVER']
- numnpol = 2
-
- fobj[target_ext].header.extend(priwcs[0].header)
- if sipwcs.cpdis1:
- whdu = priwcs[('WCSDVARR', (i-1)*numnpol+1)].copy()
- whdu.update_ext_version(self[('SIPWCS', i)].header['DP1.EXTVER'])
- fobj.append(whdu)
- if sipwcs.cpdis2:
- whdu = priwcs[('WCSDVARR', i*numnpol)].copy()
- whdu.update_ext_version(self[('SIPWCS', i)].header['DP2.EXTVER'])
- fobj.append(whdu)
- if sipwcs.det2im1: #or sipwcs.det2im2:
- whdu = priwcs[('D2IMARR', (i-1)*numd2im+1)].copy()
- whdu.update_ext_version(self[('SIPWCS', i)].header['D2IM1.EXTVER'])
- fobj.append(whdu)
- if sipwcs.det2im2:
- whdu = priwcs[('D2IMARR', i*numd2im)].copy()
- whdu.update_ext_version(self[('SIPWCS', i)].header['D2IM2.EXTVER'])
- fobj.append(whdu)
-
- update_versions(self[0].header, fobj[0].header)
- refs = update_ref_files(self[0].header, fobj[0].header)
- # Update the WCSCORR table with new rows from the headerlet's WCSs
- wcscorr.update_wcscorr(fobj, self, 'SIPWCS')
-
- # Append the original headerlet
- if archive and orig_hlt_hdu:
- fobj.append(orig_hlt_hdu)
- # Append any alternate WCS Headerlets
- if len(alt_hlethdu) > 0:
- for ahdu in alt_hlethdu:
- fobj.append(ahdu)
- if attach:
- # Finally, append an HDU for this headerlet
- self.attach_to_file(fobj)
- utils.updateNEXTENDKw(fobj)
- if close_dest:
- fobj.close()
-
-
- def apply_as_alternate(self, fobj, attach=True, wcskey=None, wcsname=None):
- """
- Copy this headerlet as an alternate WCS to fobj
-
- Parameters
- ----------
- fobj: string, HDUList
- science file/HDUList to which the headerlet should be applied
- attach: boolean
- flag indicating if the headerlet should be attached as a
- HeaderletHDU to fobj. If True checks that HDRNAME is unique
- in the fobj and stops if not.
- wcskey: string
- Key value (A-Z, except O) for this alternate WCS
- If None, the next available key will be used
- wcsname: string
- Name to be assigned to this alternate WCS
- WCSNAME is a required keyword in a Headerlet but this allows the
- user to change it as desired.
-
- """
- self.hverify()
- fobj, fname, close_dest = parse_filename(fobj, mode='update')
- if not self.verify_dest(fobj, fname):
- if close_dest:
- fobj.close()
- raise ValueError("Destination name does not match headerlet"
- "Observation %s cannot be updated with headerlet %s" % (fname, self.hdrname))
-
- # Verify whether this headerlet has the same distortion
- #found in the image being updated
- dname = self.get_destination_model(fobj)
- dist_models_equal = self.equal_distmodel(dname)
- if not dist_models_equal:
- raise ValueError("Distortion models do not match \n"
- "Headerlet: %s \n"
- "Destination file: %s\n"
- "attach_to_file() can be used to append this headerlet" %(self.distname, dname))
-
- # Insure that WCSCORR table has been created with all original
- # WCS's recorded prior to adding the headerlet WCS
- wcscorr.init_wcscorr(fobj)
-
- # determine value of WCSNAME to be used
- if wcsname is not None:
- wname = wcsname
- else:
- wname = self[0].header['WCSNAME']
- tg_ename = self[('SIPWCS', 1)].header['TG_ENAME']
- tg_ever = self[('SIPWCS', 1)].header['TG_EVER']
- # determine what alternate WCS this headerlet will be assigned to
- if wcskey is None:
- wkey = altwcs.next_wcskey(fobj[(tg_ename, tg_ever)].header)
- else:
- wcskey = wcskey.upper()
- available_keys = altwcs.available_wcskeys(fobj[(tg_ename, tg_ever)].header)
- if wcskey in available_keys:
- wkey = wcskey
- else:
- mess = "Observation %s already contains alternate WCS with key %s" % (fname, wcskey)
- logger.critical(mess)
- if close_dest:
- fobj.close()
- raise ValueError(mess)
- numsip = countExtn(self, 'SIPWCS')
- for idx in range(1, numsip + 1):
- siphdr = self[('SIPWCS', idx)].header
- tg_ext = (siphdr['TG_ENAME'], siphdr['TG_EVER'])
-
- fhdr = fobj[tg_ext].header
- hwcs = pywcs.WCS(siphdr, self)
- hwcs_header = hwcs.to_header(key=wkey)
- _idc2hdr(siphdr, fhdr, towkey=wkey)
- if hwcs.wcs.has_cd():
- hwcs_header = altwcs.pc2cd(hwcs_header, key=wkey)
-
- fhdr.extend(hwcs_header)
- fhdr['WCSNAME' + wkey] = wname
- # also update with HDRNAME (a non-WCS-standard kw)
- for kw in self.fit_kws:
- #fhdr.insert(wind, pyfits.Card(kw + wkey,
- # self[0].header[kw]))
- fhdr.append(fits.Card(kw + wkey, self[0].header[kw]))
- # Update the WCSCORR table with new rows from the headerlet's WCSs
- wcscorr.update_wcscorr(fobj, self, 'SIPWCS')
-
- if attach:
- self.attach_to_file(fobj)
- utils.updateNEXTENDKw(fobj)
-
- if close_dest:
- fobj.close()
-
- def attach_to_file(self, fobj, archive=False):
- """
- Attach Headerlet as an HeaderletHDU to a science file
-
- Parameters
- ----------
- fobj: string, HDUList
- science file/HDUList to which the headerlet should be applied
- archive: string
- Specifies whether or not to update WCSCORR table when attaching
-
- Notes
- -----
- The algorithm used by this method:
- - verify headerlet can be applied to this file (based on DESTIM)
- - verify that HDRNAME is unique for this file
- - attach as HeaderletHDU to fobj
-
- """
- self.hverify()
- fobj, fname, close_dest = parse_filename(fobj, mode='update')
- destver = self.verify_dest(fobj, fname)
- hdrver = self.verify_hdrname(fobj)
- if destver and hdrver:
-
- numhlt = countExtn(fobj, 'HDRLET')
- new_hlt = HeaderletHDU.fromheaderlet(self)
- new_hlt.header['extver'] = numhlt + 1
- fobj.append(new_hlt)
- utils.updateNEXTENDKw(fobj)
- else:
- message = "Headerlet %s cannot be attached to" % (self.hdrname)
- message += "observation %s" % (fname)
- if not destver:
- message += " * Image %s keyword ROOTNAME not equal to " % (fname)
- message += " DESTIM = '%s'\n" % (self.destim)
- if not hdrver:
- message += " * Image %s already has headerlet " % (fname)
- message += "with HDRNAME='%s'\n" % (self.hdrname)
- logger.critical(message)
- if close_dest:
- fobj.close()
-
- def info(self, columns=None, pad=2, maxwidth=None,
- output=None, clobber=True, quiet=False):
- """
- Prints a summary of this headerlet
- The summary includes:
- HDRNAME WCSNAME DISTNAME SIPNAME NPOLFILE D2IMFILE
-
- Parameters
- ----------
- columns: list
- List of headerlet PRIMARY header keywords to report in summary
- By default (set to None), it will use the default set of keywords
- defined as the global list DEFAULT_SUMMARY_COLS
- pad: int
- Number of padding spaces to put between printed columns
- [Default: 2]
- maxwidth: int
- Maximum column width(not counting padding) for any column in summary
- By default (set to None), each column's full width will be used
- output: string (optional)
- Name of optional output file to record summary. This filename
- can contain environment variables.
- [Default: None]
- clobber: bool
- If True, will overwrite any previous output file of same name
- quiet: bool
- If True, will NOT report info to STDOUT
-
- """
- summary_cols, summary_dict = self.summary(columns=columns)
- print_summary(summary_cols, summary_dict, pad=pad, maxwidth=maxwidth,
- idcol=None, output=output, clobber=clobber, quiet=quiet)
-
- def summary(self, columns=None):
- """
- Returns a summary of this headerlet as a dictionary
-
- The summary includes a summary of the distortion model as :
- HDRNAME WCSNAME DISTNAME SIPNAME NPOLFILE D2IMFILE
-
- Parameters
- ----------
- columns: list
- List of headerlet PRIMARY header keywords to report in summary
- By default(set to None), it will use the default set of keywords
- defined as the global list DEFAULT_SUMMARY_COLS
-
- Returns
- -------
- summary: dict
- Dictionary of values for summary
- """
- if columns is None:
- summary_cols = DEFAULT_SUMMARY_COLS
- else:
- summary_cols = columns
-
- # Initialize summary dict based on requested columns
- summary = {}
- for kw in summary_cols:
- summary[kw] = copy.deepcopy(COLUMN_DICT)
-
- # Populate the summary with headerlet values
- for kw in summary_cols:
- if kw in self[0].header:
- val = self[0].header[kw]
- else:
- val = 'INDEF'
- summary[kw]['vals'].append(val)
- summary[kw]['width'].append(max(len(val), len(kw)))
-
- return summary_cols, summary
-
- def hverify(self):
- """
- Verify the headerlet file is a valid fits file and has
- the required Primary Header keywords
- """
- self.verify()
- header = self[0].header
- assert('DESTIM' in header and header['DESTIM'].strip())
- assert('HDRNAME' in header and header['HDRNAME'].strip())
- assert('UPWCSVER' in header)
-
- def verify_hdrname(self, dest):
- """
- Verifies that the headerlet can be applied to the observation
-
- Reports whether or not this file already has a headerlet with this
- HDRNAME.
- """
- unique = verify_hdrname_is_unique(dest, self.hdrname)
- logger.debug("verify_hdrname() returned %s"%unique)
- return unique
-
- def get_destination_model(self, dest):
- """
- Verifies that the headerlet can be applied to the observation
-
- Determines whether or not the file specifies the same distortion
- model/reference files.
- """
- destim_opened = False
- if not isinstance(dest, fits.HDUList):
- destim = fits.open(dest)
- destim_opened = True
- else:
- destim = dest
- dname = destim[0].header['DISTNAME'] if 'distname' in destim[0].header \
- else self.build_distname(dest)
- if destim_opened:
- destim.close()
- return dname
-
- def equal_distmodel(self, dmodel):
- if dmodel != self[0].header['DISTNAME']:
- if self.logging:
- message = """
- Distortion model in headerlet not the same as destination model
- Headerlet model : %s
- Destination model: %s
- """ % (self[0].header['DISTNAME'], dmodel)
- logger.critical(message)
- return False
- else:
- return True
-
- def verify_dest(self, dest, fname):
- """
- verifies that the headerlet can be applied to the observation
-
- DESTIM in the primary header of the headerlet must match ROOTNAME
- of the science file (or the name of the destination file)
- """
- try:
- if not isinstance(dest, fits.HDUList):
- droot = fits.getval(dest, 'ROOTNAME')
- else:
- droot = dest[0].header['ROOTNAME']
- except KeyError:
- logger.debug("Keyword 'ROOTNAME' not found in destination file")
- droot = dest.split('.fits')[0]
- if droot == self.destim:
- logger.debug("verify_destim() returned True")
- return True
- else:
- logger.debug("verify_destim() returned False")
- logger.critical("Destination name does not match headerlet. "
- "Observation %s cannot be updated with headerlet %s" % (fname, self.hdrname))
- return False
-
- def build_distname(self, dest):
- """
- Builds the DISTNAME for dest based on reference file names.
- """
-
- try:
- npolfile = dest[0].header['NPOLFILE']
- except KeyError:
- npolfile = None
- try:
- d2imfile = dest[0].header['D2IMFILE']
- except KeyError:
- d2imfile = None
-
- sipname, idctab = utils.build_sipname(dest, dest, None)
- npolname, npolfile = utils.build_npolname(dest, npolfile)
- d2imname, d2imfile = utils.build_d2imname(dest, d2imfile)
- dname = utils.build_distname(sipname,npolname,d2imname)
- return dname
-
- def tofile(self, fname, destim=None, hdrname=None, clobber=False):
- """
- Write this headerlet to a file
-
- Parameters
- ----------
- fname: string
- file name
- destim: string (optional)
- provide a value for DESTIM keyword
- hdrname: string (optional)
- provide a value for HDRNAME keyword
- clobber: boolean
- a flag which allows to overwrte an existing file
- """
- if not destim or not hdrname:
- self.hverify()
- self.writeto(fname, clobber=clobber)
-
- def _del_dest_WCS(self, dest, ext=None):
- """
- Delete the WCS of a science file extension
- """
-
- logger.info("Deleting all WCSs of file %s" % dest.filename())
- numext = len(dest)
-
- if ext:
- fext = dest[ext]
- self._remove_d2im(fext)
- self._remove_sip(fext)
- self._remove_lut(fext)
- self._remove_primary_WCS(fext)
- self._remove_idc_coeffs(fext)
- self._remove_fit_values(fext)
- else:
- for idx in range(numext):
- # Only delete WCS from extensions which may have WCS keywords
- if ('XTENSION' in dest[idx].header and
- dest[idx].header['XTENSION'] == 'IMAGE'):
- self._remove_d2im(dest[idx])
- self._remove_sip(dest[idx])
- self._remove_lut(dest[idx])
- self._remove_primary_WCS(dest[idx])
- self._remove_idc_coeffs(dest[idx])
- self._remove_fit_values(dest[idx])
- self._remove_ref_files(dest[0])
- """
- if not ext:
- self._remove_alt_WCS(dest, ext=range(numext))
- else:
- self._remove_alt_WCS(dest, ext=ext)
- """
- def _del_dest_WCS_ext(self, dest):
- numwdvarr = countExtn(dest, 'WCSDVARR')
- numd2im = countExtn(dest, 'D2IMARR')
- if numwdvarr > 0:
- for idx in range(1, numwdvarr + 1):
- del dest[('WCSDVARR', idx)]
- if numd2im > 0:
- for idx in range(1, numd2im + 1):
- del dest[('D2IMARR', idx)]
-
- def _remove_ref_files(self, phdu):
- """
- phdu: Primary HDU
- """
- refkw = ['IDCTAB', 'NPOLFILE', 'D2IMFILE', 'SIPNAME', 'DISTNAME']
- for kw in refkw:
- try:
- del phdu.header[kw]
- except KeyError:
- pass
-
- def _remove_fit_values(self, ext):
- """
- Remove the any existing astrometric fit values from a FITS extension
- """
-
- logger.debug("Removing astrometric fit values from (%s, %s)"%
- (ext.name, ext.ver))
- dkeys = altwcs.wcskeys(ext.header)
- if 'O' in dkeys: dkeys.remove('O') # Do not remove wcskey='O' values
- for fitkw in ['NMATCH', 'CATALOG']:
- for k in dkeys:
- fkw = (fitkw+k).rstrip()
- if fkw in ext.header:
- del ext.header[fkw]
-
- def _remove_sip(self, ext):
- """
- Remove the SIP distortion of a FITS extension
- """
-
- logger.debug("Removing SIP distortion from (%s, %s)"
- % (ext.name, ext.ver))
- for prefix in ['A', 'B', 'AP', 'BP']:
- try:
- order = ext.header[prefix + '_ORDER']
- del ext.header[prefix + '_ORDER']
- except KeyError:
- continue
- for i in range(order + 1):
- for j in range(order + 1):
- key = prefix + '_%d_%d' % (i, j)
- try:
- del ext.header[key]
- except KeyError:
- pass
- try:
- del ext.header['IDCTAB']
- except KeyError:
- pass
-
- def _remove_lut(self, ext):
- """
- Remove the Lookup Table distortion of a FITS extension
- """
-
- logger.debug("Removing LUT distortion from (%s, %s)"
- % (ext.name, ext.ver))
- try:
- cpdis = ext.header['CPDIS*']
- except KeyError:
- return
- try:
- for c in range(1, len(cpdis) + 1):
- del ext.header['DP%s*...' % c]
- del ext.header[cpdis.cards[c - 1].keyword]
- del ext.header['CPERR*']
- del ext.header['NPOLFILE']
- del ext.header['NPOLEXT']
- except KeyError:
- pass
-
- def _remove_d2im(self, ext):
- """
- Remove the Detector to Image correction of a FITS extension
- """
-
- logger.debug("Removing D2IM correction from (%s, %s)"
- % (ext.name, ext.ver))
- try:
- d2imdis = ext.header['D2IMDIS*']
- except KeyError:
- return
- try:
- for c in range(1, len(d2imdis) + 1):
- del ext.header['D2IM%s*...' % c]
- del ext.header[d2imdis.cards[c - 1].keyword]
- del ext.header['D2IMERR*']
- del ext.header['D2IMFILE']
- del ext.header['D2IMEXT']
- except KeyError:
- pass
-
- def _remove_alt_WCS(self, dest, ext):
- """
- Remove Alternate WCSs of a FITS extension.
- A WCS with wcskey 'O' is never deleted.
- """
- dkeys = altwcs.wcskeys(dest[('SCI', 1)].header)
- for val in ['O', '', ' ']:
- if val in dkeys:
- dkeys.remove(val) # Never delete WCS with wcskey='O'
-
- logger.debug("Removing alternate WCSs with keys %s from %s"
- % (dkeys, dest.filename()))
- for k in dkeys:
- altwcs.deleteWCS(dest, ext=ext, wcskey=k)
-
- def _remove_primary_WCS(self, ext):
- """
- Remove the primary WCS of a FITS extension
- """
-
- logger.debug("Removing Primary WCS from (%s, %s)"
- % (ext.name, ext.ver))
- naxis = ext.header['NAXIS']
- for key in basic_wcs:
- for i in range(1, naxis + 1):
- try:
- del ext.header[key + str(i)]
- except KeyError:
- pass
- try:
- del ext.header['WCSAXES']
- except KeyError:
- pass
- try:
- del ext.header['WCSNAME']
- except KeyError:
- pass
-
- def _remove_idc_coeffs(self, ext):
- """
- Remove IDC coefficients of a FITS extension
- """
-
- logger.debug("Removing IDC coefficient from (%s, %s)"
- % (ext.name, ext.ver))
- coeffs = ['OCX10', 'OCX11', 'OCY10', 'OCY11', 'IDCSCALE']
- for k in coeffs:
- try:
- del ext.header[k]
- except KeyError:
- pass
-
-@with_logging
-def _idc2hdr(fromhdr, tohdr, towkey=' '):
- """
- Copy the IDC (HST specific) keywords from one header to another
-
- """
- # save some of the idc coefficients
- coeffs = ['OCX10', 'OCX11', 'OCY10', 'OCY11', 'IDCSCALE']
- for c in coeffs:
- try:
- tohdr[c+towkey] = fromhdr[c]
- logger.debug("Copied %s to header")
- except KeyError:
- continue
-
-
-def get_extname_extver_list(fobj, sciext):
- """
- Create a list of (EXTNAME, EXTVER) tuples
-
- Based on sciext keyword (see docstring for create_headerlet)
- walk throughh the file and convert extensions in `sciext` to
- valid (EXTNAME, EXTVER) tuples.
- """
- extlist = []
- if isinstance(sciext, int):
- if sciext == 0:
- extname = 'PRIMARY'
- extver = 1
- else:
- try:
- extname = fobj[sciext].header['EXTNAME']
- except KeyError:
- extname = ""
- try:
- extver = fobj[sciext].header['EXTVER']
- except KeyError:
- extver = 1
- extlist.append((extname, extver))
- elif isinstance(sciext, str):
- if sciext == 'PRIMARY':
- extname = "PRIMARY"
- extver = 1
- extlist.append((extname, extver))
- else:
- for ext in fobj:
- try:
- extname = ext.header['EXTNAME']
- except KeyError:
- continue
- if extname.upper() == sciext.upper():
- try:
- extver = ext.header['EXTVER']
- except KeyError:
- extver = 1
- extlist.append((extname, extver))
- elif isinstance(sciext, list):
- if isinstance(sciext[0], int):
- for i in sciext:
- try:
- extname = fobj[i].header['EXTNAME']
- except KeyError:
- if i == 0:
- extname = "PRIMARY"
- extver = 1
- else:
- extname = ""
- try:
- extver = fobj[i].header['EXTVER']
- except KeyError:
- extver = 1
- extlist.append((extname, extver))
- else:
- extlist = sciext[:]
- else:
- errstr = "Expected sciext to be a list of FITS extensions with science data\n"+\
- " a valid EXTNAME string, or an integer."
- logger.critical(errstr)
- raise ValueError
- return extlist
-
-
-class HeaderletHDU(fits.hdu.nonstandard.FitsHDU):
- """
- A non-standard extension HDU for encapsulating Headerlets in a file. These
- HDUs have an extension type of HDRLET and their EXTNAME is derived from the
- Headerlet's HDRNAME.
-
- The data itself is a FITS file embedded within the HDU data. The file name
- is derived from the HDRNAME keyword, and should be in the form
- `<HDRNAME>_hdr.fits`. If the COMPRESS keyword evaluates to `True`, the tar
- file is compressed with gzip compression.
-
- The structure of this HDU is the same as that proposed for the 'FITS'
- extension type proposed here:
- http://listmgr.cv.nrao.edu/pipermail/fitsbits/2002-April/thread.html
-
- The Headerlet contained in the HDU's data can be accessed by the
- `headerlet` attribute.
- """
-
- _extension = 'HDRLET'
-
- @lazyproperty
- def headerlet(self):
- """Return the encapsulated headerlet as a Headerlet object.
-
- This is similar to the hdulist property inherited from the FitsHDU
- class, though the hdulist property returns a normal HDUList object.
- """
-
- return Headerlet(self.hdulist)
-
- @classmethod
- def fromheaderlet(cls, headerlet, compress=False):
- """
- Creates a new HeaderletHDU from a given Headerlet object.
-
- Parameters
- ----------
- headerlet : `Headerlet`
- A valid Headerlet object.
-
- compress : bool, optional
- Gzip compress the headerlet data.
-
- Returns
- -------
- hlet : `HeaderletHDU`
- A `HeaderletHDU` object for the given `Headerlet` that can be
- attached as an extension to an existing `HDUList`.
- """
-
- # TODO: Perhaps check that the given object is in fact a valid
- # Headerlet
- hlet = cls.fromhdulist(headerlet, compress)
-
- # Add some more headerlet-specific keywords to the header
- phdu = headerlet[0]
-
- if 'SIPNAME' in phdu.header:
- sipname = phdu.header['SIPNAME']
- else:
- sipname = phdu.header['WCSNAME']
-
- hlet.header['HDRNAME'] = (phdu.header['HDRNAME'],
- phdu.header.comments['HDRNAME'])
- hlet.header['DATE'] = (phdu.header['DATE'],
- phdu.header.comments['DATE'])
- hlet.header['SIPNAME'] = (sipname, 'SIP distortion model name')
- hlet.header['WCSNAME'] = (phdu.header['WCSNAME'], 'WCS name')
- hlet.header['DISTNAME'] = (phdu.header['DISTNAME'],
- 'Distortion model name')
- hlet.header['NPOLFILE'] = (phdu.header['NPOLFILE'],
- phdu.header.comments['NPOLFILE'])
- hlet.header['D2IMFILE'] = (phdu.header['D2IMFILE'],
- phdu.header.comments['D2IMFILE'])
- hlet.header['EXTNAME'] = (cls._extension, 'Extension name')
-
- return hlet
-
-
-fits.register_hdu(HeaderletHDU)
diff --git a/lib/stwcs/wcsutil/hstwcs.py b/lib/stwcs/wcsutil/hstwcs.py
deleted file mode 100644
index bfebcfc..0000000
--- a/lib/stwcs/wcsutil/hstwcs.py
+++ /dev/null
@@ -1,988 +0,0 @@
-from __future__ import absolute_import, division, print_function # confidence high
-
-import os
-from astropy.wcs import WCS
-from astropy.io import fits
-from stwcs.distortion import models, coeff_converter
-import numpy as np
-from stsci.tools import fileutil
-
-from . import altwcs
-from . import getinput
-from . import mappings
-from . import instruments
-from .mappings import inst_mappings, ins_spec_kw
-from .mappings import basic_wcs
-
-__docformat__ = 'restructuredtext'
-
-#
-#### Utility functions copied from 'updatewcs.utils' to avoid circular imports
-#
-def extract_rootname(kwvalue,suffix=""):
- """ Returns the rootname from a full reference filename
-
- If a non-valid value (any of ['','N/A','NONE','INDEF',None]) is input,
- simply return a string value of 'NONE'
-
- This function will also replace any 'suffix' specified with a blank.
- """
- # check to see whether a valid kwvalue has been provided as input
- if kwvalue.strip() in ['','N/A','NONE','INDEF',None]:
- return 'NONE' # no valid value, so return 'NONE'
-
- # for a valid kwvalue, parse out the rootname
- # strip off any environment variable from input filename, if any are given
- if '$' in kwvalue:
- fullval = kwvalue[kwvalue.find('$')+1:]
- else:
- fullval = kwvalue
- # Extract filename without path from kwvalue
- fname = os.path.basename(fullval).strip()
-
- # Now, rip out just the rootname from the full filename
- rootname = fileutil.buildNewRootname(fname)
-
- # Now, remove any known suffix from rootname
- rootname = rootname.replace(suffix,'')
- return rootname.strip()
-
-def build_default_wcsname(idctab):
-
- idcname = extract_rootname(idctab,suffix='_idc')
- wcsname = 'IDC_' + idcname
- return wcsname
-
-
-class NoConvergence(Exception):
- """
- An error class used to report non-convergence and/or divergence of
- numerical methods. It is used to report errors in the iterative solution
- used by the :py:meth:`~stwcs.hstwcs.HSTWCS.all_world2pix`\ .
-
- Attributes
- ----------
-
- best_solution : numpy.array
- Best solution achieved by the method.
-
- accuracy : float
- Accuracy of the :py:attr:`best_solution`\ .
-
- niter : int
- Number of iterations performed by the numerical method to compute
- :py:attr:`best_solution`\ .
-
- divergent : None, numpy.array
- Indices of the points in :py:attr:`best_solution` array for which the
- solution appears to be divergent. If the solution does not diverge,
- `divergent` will be set to `None`.
-
- failed2converge : None, numpy.array
- Indices of the points in :py:attr:`best_solution` array for which the
- solution failed to converge within the specified maximum number
- of iterations. If there are no non-converging poits (i.e., if
- the required accuracy has been achieved for all points) then
- `failed2converge` will be set to `None`.
-
- """
- def __init__(self, *args, **kwargs):
- super(NoConvergence, self).__init__(*args)
-
- self.best_solution = kwargs.pop('best_solution', None)
- self.accuracy = kwargs.pop('accuracy', None)
- self.niter = kwargs.pop('niter', None)
- self.divergent = kwargs.pop('divergent', None)
- self.failed2converge= kwargs.pop('failed2converge', None)
-
-
-#
-#### HSTWCS Class definition
-#
-class HSTWCS(WCS):
-
- def __init__(self, fobj=None, ext=None, minerr=0.0, wcskey=" "):
- """
- Create a WCS object based on the instrument.
-
- In addition to basic WCS keywords this class provides
- instrument specific information needed in distortion computation.
-
- Parameters
- ----------
- fobj : str or `astropy.io.fits.HDUList` object or None
- file name, e.g j9irw4b1q_flt.fits
- fully qualified filename[EXTNAME,EXTNUM], e.g. j9irw4b1q_flt.fits[sci,1]
- `astropy.io.fits` file object, e.g fits.open('j9irw4b1q_flt.fits'), in which case the
- user is responsible for closing the file object.
- ext : int, tuple or None
- extension number
- if ext is tuple, it must be ("EXTNAME", EXTNUM), e.g. ("SCI", 2)
- if ext is None, it is assumed the data is in the primary hdu
- minerr : float
- minimum value a distortion correction must have in order to be applied.
- If CPERRja, CQERRja are smaller than minerr, the corersponding
- distortion is not applied.
- wcskey : str
- A one character A-Z or " " used to retrieve and define an
- alternate WCS description.
- """
-
- self.inst_kw = ins_spec_kw
- self.minerr = minerr
- self.wcskey = wcskey
-
- if fobj is not None:
- filename, hdr0, ehdr, phdu = getinput.parseSingleInput(f=fobj,
- ext=ext)
- self.filename = filename
- instrument_name = hdr0.get('INSTRUME', 'DEFAULT')
- if instrument_name == 'DEFAULT' or instrument_name not in list(inst_mappings.keys()):
- #['IRAF/ARTDATA','',' ','N/A']:
- self.instrument = 'DEFAULT'
- else:
- self.instrument = instrument_name
- # Set the correct reference frame
- refframe = determine_refframe(hdr0)
- ehdr['RADESYS'] = refframe
-
- WCS.__init__(self, ehdr, fobj=phdu, minerr=self.minerr,
- key=self.wcskey)
- if self.instrument == 'DEFAULT':
- self.pc2cd()
- # If input was a `astropy.io.fits.HDUList` object, it's the user's
- # responsibility to close it, otherwise, it's closed here.
- if not isinstance(fobj, fits.HDUList):
- phdu.close()
- self.setInstrSpecKw(hdr0, ehdr)
- self.readIDCCoeffs(ehdr)
- extname = ehdr.get('EXTNAME', '')
- extnum = ehdr.get('EXTVER', None)
- self.extname = (extname, extnum)
- else:
- # create a default HSTWCS object
- self.instrument = 'DEFAULT'
- WCS.__init__(self, minerr=self.minerr, key=self.wcskey)
- self.pc2cd()
- self.setInstrSpecKw()
- self.setPscale()
- self.setOrient()
-
- @property
- def naxis1(self):
- return self._naxis1
-
- @naxis1.setter
- def naxis1(self, value):
- self._naxis1 = value
-
- @property
- def naxis2(self):
- return self._naxis2
-
- @naxis2.setter
- def naxis2(self, value):
- self._naxis2 = value
-
- def readIDCCoeffs(self, header):
- """
- Reads in first order IDCTAB coefficients if present in the header
- """
- coeffs = ['ocx10', 'ocx11', 'ocy10', 'ocy11', 'idcscale',
- 'idcv2ref','idcv3ref', 'idctheta']
- for c in coeffs:
- self.__setattr__(c, header.get(c, None))
-
- def setInstrSpecKw(self, prim_hdr=None, ext_hdr=None):
- """
- Populate the instrument specific attributes:
-
- These can be in different headers but each instrument class has knowledge
- of where to look for them.
-
- Parameters
- ----------
- prim_hdr : `astropy.io.fits.Header`
- primary header
- ext_hdr : `astropy.io.fits.Header`
- extension header
-
- """
- if self.instrument in list(inst_mappings.keys()):
- inst_kl = inst_mappings[self.instrument]
- inst_kl = instruments.__dict__[inst_kl]
- insobj = inst_kl(prim_hdr, ext_hdr)
-
- for key in self.inst_kw:
- try:
- self.__setattr__(key, insobj.__getattribute__(key))
- except AttributeError:
- # Some of the instrument's attributes are recorded in the primary header and
- # were already set, (e.g. 'DETECTOR'), the code below is a check for that case.
- if not self.__getattribute__(key):
- raise
- else:
- pass
-
- else:
- raise KeyError("Unsupported instrument - %s" %self.instrument)
-
- def setPscale(self):
- """
- Calculates the plate scale from the CD matrix
- """
- try:
- cd11 = self.wcs.cd[0][0]
- cd21 = self.wcs.cd[1][0]
- self.pscale = np.sqrt(np.power(cd11,2)+np.power(cd21,2)) * 3600.
- except AttributeError:
- if self.wcs.has_cd():
- print("This file has a PC matrix. You may want to convert it \n \
- to a CD matrix, if reasonable, by running pc2.cd() method.\n \
- The plate scale can be set then by calling setPscale() method.\n")
- self.pscale = None
-
- def setOrient(self):
- """
- Computes ORIENTAT from the CD matrix
- """
- try:
- cd12 = self.wcs.cd[0][1]
- cd22 = self.wcs.cd[1][1]
- self.orientat = np.rad2deg(np.arctan2(cd12,cd22))
- except AttributeError:
- if self.wcs.has_cd():
- print("This file has a PC matrix. You may want to convert it \n \
- to a CD matrix, if reasonable, by running pc2.cd() method.\n \
- The orientation can be set then by calling setOrient() method.\n")
- self.pscale = None
-
- def updatePscale(self, scale):
- """
- Updates the CD matrix with a new plate scale
- """
- self.wcs.cd = self.wcs.cd/self.pscale*scale
- self.setPscale()
-
- def readModel(self, update=False, header=None):
- """
- Reads distortion model from IDCTAB.
-
- If IDCTAB is not found ('N/A', "", or not found on disk), then
- if SIP coefficients and first order IDCTAB coefficients are present
- in the header, restore the idcmodel from the header.
- If not - assign None to self.idcmodel.
-
- Parameters
- ----------
- header : `astropy.io.fits.Header`
- fits extension header
- update : bool (False)
- if True - record the following IDCTAB quantities as header keywords:
- CX10, CX11, CY10, CY11, IDCSCALE, IDCTHETA, IDCXREF, IDCYREF,
- IDCV2REF, IDCV3REF
- """
- if self.idctab in [None, '', ' ','N/A']:
- #Keyword idctab is not present in header - check for sip coefficients
- if header is not None and 'IDCSCALE' in header:
- self._readModelFromHeader(header)
- else:
- print("Distortion model is not available: IDCTAB=None\n")
- self.idcmodel = None
- elif not os.path.exists(fileutil.osfn(self.idctab)):
- if header is not None and 'IDCSCALE' in header:
- self._readModelFromHeader(header)
- else:
- print('Distortion model is not available: IDCTAB file %s not found\n' % self.idctab)
- self.idcmodel = None
- else:
- self.readModelFromIDCTAB(header=header, update=update)
-
- def _readModelFromHeader(self, header):
- # Recreate idc model from SIP coefficients and header kw
- print('Restoring IDC model from SIP coefficients\n')
- model = models.GeometryModel()
- cx, cy = coeff_converter.sip2idc(self)
- model.cx = cx
- model.cy = cy
- model.name = "sip"
- model.norder = header['A_ORDER']
-
- refpix = {}
- refpix['XREF'] = header['IDCXREF']
- refpix['YREF'] = header['IDCYREF']
- refpix['PSCALE'] = header['IDCSCALE']
- refpix['V2REF'] = header['IDCV2REF']
- refpix['V3REF'] = header['IDCV3REF']
- refpix['THETA'] = header['IDCTHETA']
- model.refpix = refpix
-
- self.idcmodel = model
-
-
- def readModelFromIDCTAB(self, header=None, update=False):
- """
- Read distortion model from idc table.
-
- Parameters
- ----------
- header : `astropy.io.fits.Header`
- fits extension header
- update : booln (False)
- if True - save teh following as header keywords:
- CX10, CX11, CY10, CY11, IDCSCALE, IDCTHETA, IDCXREF, IDCYREF,
- IDCV2REF, IDCV3REF
-
- """
- if self.date_obs == None:
- print('date_obs not available\n')
- self.idcmodel = None
- return
- if self.filter1 == None and self.filter2 == None:
- 'No filter information available\n'
- self.idcmodel = None
- return
-
- self.idcmodel = models.IDCModel(self.idctab,
- chip=self.chip, direction='forward', date=self.date_obs,
- filter1=self.filter1, filter2=self.filter2,
- offtab=self.offtab, binned=self.binned)
-
- if self.ltv1 != 0. or self.ltv2 != 0.:
- self.resetLTV()
-
- if update:
- if header==None:
- print('Update header with IDC model kw requested but header was not provided\n.')
- else:
- self._updatehdr(header)
-
- def resetLTV(self):
- """
- Reset LTV values for polarizer data
-
- The polarizer field is smaller than the detector field.
- The distortion coefficients are defined for the entire
- polarizer field and the LTV values are set as with subarray
- data. This may also be true for other special filters.
- This is a special case when the observation is considered
- a subarray in terms of detector field but a full frame in
- terms of distortion model.
- To avoid shifting the distortion coefficients the LTV values
- are reset to 0.
- """
- if self.naxis1 == self.idcmodel.refpix['XSIZE'] and \
- self.naxis2 == self.idcmodel.refpix['YSIZE']:
- self.ltv1 = 0.
- self.ltv2 = 0.
-
- def wcs2header(self, sip2hdr=False, idc2hdr=True, wcskey=None, relax=False):
- """
- Create a `astropy.io.fits.Header` object from WCS keywords.
-
- If the original header had a CD matrix, return a CD matrix,
- otherwise return a PC matrix.
-
- Parameters
- ----------
- sip2hdr : bool
- If True - include SIP coefficients
- """
-
- h = self.to_header(key=wcskey, relax=relax)
- if not wcskey:
- wcskey = self.wcs.alt
- if self.wcs.has_cd():
- h = altwcs.pc2cd(h, key=wcskey)
-
- if 'wcsname' not in h:
- if self.idctab is not None:
- wname = build_default_wcsname(self.idctab)
- else:
- wname = 'DEFAULT'
- h['wcsname{0}'.format(wcskey)] = wname
-
- if idc2hdr:
- for card in self._idc2hdr():
- h[card.keyword + wcskey] = (card.value, card.comment)
- try:
- del h['RESTFRQ']
- del h['RESTWAV']
- except KeyError: pass
-
- if sip2hdr and self.sip:
- for card in self._sip2hdr('a'):
- h[card.keyword] = (card.value, card.comment)
- for card in self._sip2hdr('b'):
- h[card.keyword] = (card.value, card.comment)
-
- try:
- ap = self.sip.ap
- except AssertionError:
- ap = None
- try:
- bp = self.sip.bp
- except AssertionError:
- bp = None
-
- if ap:
- for card in self._sip2hdr('ap'):
- h[card.keyword] = (card.value, card.comment)
- if bp:
- for card in self._sip2hdr('bp'):
- h[card.keyword] = (card.value, card.comment)
- return h
-
- def _sip2hdr(self, k):
- """
- Get a set of SIP coefficients in the form of an array
- and turn them into a `astropy.io.fits.Cardlist`.
- k - one of 'a', 'b', 'ap', 'bp'
- """
-
- cards = [] #fits.CardList()
- korder = self.sip.__getattribute__(k+'_order')
- cards.append(fits.Card(keyword=k.upper()+'_ORDER', value=korder))
- coeffs = self.sip.__getattribute__(k)
- ind = coeffs.nonzero()
- for i in range(len(ind[0])):
- card = fits.Card(keyword=k.upper()+'_'+str(ind[0][i])+'_'+str(ind[1][i]),
- value=coeffs[ind[0][i], ind[1][i]])
- cards.append(card)
- return cards
-
- def _idc2hdr(self):
- # save some of the idc coefficients
- coeffs = ['ocx10', 'ocx11', 'ocy10', 'ocy11', 'idcscale']
- cards = [] #fits.CardList()
- for c in coeffs:
- try:
- val = self.__getattribute__(c)
- except AttributeError:
- continue
- if val:
- cards.append(fits.Card(keyword=c, value=val))
- return cards
-
- def pc2cd(self):
- if not self.wcs.has_pc():
- self.wcs.pc = self.wcs.get_pc()
- self.wcs.cd = self.wcs.pc * self.wcs.cdelt[1]
-
- def all_world2pix(self, *args, **kwargs):
- """
- all_world2pix(*arg, accuracy=1.0e-4, maxiter=20, adaptive=False, \
-detect_divergence=True, quiet=False)
-
- Performs full inverse transformation using iterative solution
- on full forward transformation with complete distortion model.
-
- Parameters
- ----------
- accuracy : float, optional (Default = 1.0e-4)
- Required accuracy of the solution. Iteration terminates when the
- correction to the solution found during the previous iteration
- is smaller (in the sence of the L2 norm) than `accuracy`\ .
-
- maxiter : int, optional (Default = 20)
- Maximum number of iterations allowed to reach the solution.
-
- adaptive : bool, optional (Default = False)
- Specifies whether to adaptively select only points that did not
- converge to a solution whithin the required accuracy for the
- next iteration. Default is recommended for HST as well as most
- other instruments.
-
- .. note::
- The :py:meth:`all_world2pix` uses a vectorized implementation
- of the method of consecutive approximations (see `Notes`
- section below) in which it iterates over *all* input poits
- *regardless* until the required accuracy has been reached for
- *all* input points. In some cases it may be possible that
- *almost all* points have reached the required accuracy but
- there are only a few of input data points left for which
- additional iterations may be needed (this depends mostly on the
- characteristics of the geometric distortions for a given
- instrument). In this situation it may be
- advantageous to set `adaptive` = `True`\ in which
- case :py:meth:`all_world2pix` will continue iterating *only* over
- the points that have not yet converged to the required
- accuracy. However, for the HST's ACS/WFC detector, which has
- the strongest distortions of all HST instruments, testing has
- shown that enabling this option would lead to a about 10-30\%
- penalty in computational time (depending on specifics of the
- image, geometric distortions, and number of input points to be
- converted). Therefore, for HST instruments,
- it is recommended to set `adaptive` = `False`\ . The only
- danger in getting this setting wrong will be a performance
- penalty.
-
- .. note::
- When `detect_divergence` is `True`\ , :py:meth:`all_world2pix` \
- will automatically switch to the adaptive algorithm once
- divergence has been detected.
-
- detect_divergence : bool, optional (Default = True)
- Specifies whether to perform a more detailed analysis of the
- convergence to a solution. Normally :py:meth:`all_world2pix`
- may not achieve the required accuracy
- if either the `tolerance` or `maxiter` arguments are too low.
- However, it may happen that for some geometric distortions
- the conditions of convergence for the the method of consecutive
- approximations used by :py:meth:`all_world2pix` may not be
- satisfied, in which case consecutive approximations to the
- solution will diverge regardless of the `tolerance` or `maxiter`
- settings.
-
- When `detect_divergence` is `False`\ , these divergent points
- will be detected as not having achieved the required accuracy
- (without further details). In addition, if `adaptive` is `False`
- then the algorithm will not know that the solution (for specific
- points) is diverging and will continue iterating and trying to
- "improve" diverging solutions. This may result in NaN or Inf
- values in the return results (in addition to a performance
- penalties). Even when `detect_divergence` is
- `False`\ , :py:meth:`all_world2pix`\ , at the end of the iterative
- process, will identify invalid results (NaN or Inf) as "diverging"
- solutions and will raise :py:class:`NoConvergence` unless
- the `quiet` parameter is set to `True`\ .
-
- When `detect_divergence` is `True`\ , :py:meth:`all_world2pix` will
- detect points for
- which current correction to the coordinates is larger than
- the correction applied during the previous iteration **if** the
- requested accuracy **has not yet been achieved**\ . In this case,
- if `adaptive` is `True`, these points will be excluded from
- further iterations and if `adaptive`
- is `False`\ , :py:meth:`all_world2pix` will automatically
- switch to the adaptive algorithm.
-
- .. note::
- When accuracy has been achieved, small increases in
- current corrections may be possible due to rounding errors
- (when `adaptive` is `False`\ ) and such increases
- will be ignored.
-
- .. note::
- Setting `detect_divergence` to `True` will incurr about 5-10\%
- performance penalty (in our testing on ACS/WFC images).
- Because the benefits of enabling this feature outweigh
- the small performance penalty, it is recommended to set
- `detect_divergence` to `True`\ , unless extensive testing
- of the distortion models for images from specific
- instruments show a good stability of the numerical method
- for a wide range of coordinates (even outside the image
- itself).
-
- .. note::
- Indices of the diverging inverse solutions will be reported
- in the `divergent` attribute of the
- raised :py:class:`NoConvergence` object.
-
- quiet : bool, optional (Default = False)
- Do not throw :py:class:`NoConvergence` exceptions when the method
- does not converge to a solution with the required accuracy
- within a specified number of maximum iterations set by `maxiter`
- parameter. Instead, simply return the found solution.
-
- Raises
- ------
- NoConvergence
- The method does not converge to a
- solution with the required accuracy within a specified number
- of maximum iterations set by the `maxiter` parameter.
-
- Notes
- -----
- Inputs can either be (RA, Dec, origin) or (RADec, origin) where RA
- and Dec are 1-D arrays/lists of coordinates and RADec is an
- array/list of pairs of coordinates.
-
- Using the method of consecutive approximations we iterate starting
- with the initial approximation, which is computed using the
- non-distorion-aware :py:meth:`wcs_world2pix` (or equivalent).
-
- The :py:meth:`all_world2pix` function uses a vectorized implementation
- of the method of consecutive approximations and therefore it is
- highly efficient (>30x) when *all* data points that need to be
- converted from sky coordinates to image coordinates are passed at
- *once*\ . Therefore, it is advisable, whenever possible, to pass
- as input a long array of all points that need to be converted
- to :py:meth:`all_world2pix` instead of calling :py:meth:`all_world2pix`
- for each data point. Also see the note to the `adaptive` parameter.
-
- Examples
- --------
- >>> import stwcs
- >>> from astropy.io import fits
- >>> hdulist = fits.open('j94f05bgq_flt.fits')
- >>> w = stwcs.wcsutil.HSTWCS(hdulist, ext=('sci',1))
- >>> hdulist.close()
-
- >>> ra, dec = w.all_pix2world([1,2,3],[1,1,1],1); print(ra); print(dec)
- [ 5.52645241 5.52649277 5.52653313]
- [-72.05171776 -72.05171295 -72.05170814]
- >>> radec = w.all_pix2world([[1,1],[2,1],[3,1]],1); print(radec)
- [[ 5.52645241 -72.05171776]
- [ 5.52649277 -72.05171295]
- [ 5.52653313 -72.05170814]]
- >>> x, y = w.all_world2pix(ra,dec,1)
- >>> print(x)
- [ 1.00000233 2.00000232 3.00000233]
- >>> print(y)
- [ 0.99999997 0.99999997 0.99999998]
- >>> xy = w.all_world2pix(radec,1)
- >>> print(xy)
- [[ 1.00000233 0.99999997]
- [ 2.00000232 0.99999997]
- [ 3.00000233 0.99999998]]
- >>> xy = w.all_world2pix(radec,1, maxiter=3, accuracy=1.0e-10, \
-quiet=False)
- NoConvergence: 'HSTWCS.all_world2pix' failed to converge to requested \
-accuracy after 3 iterations.
-
- >>>
- Now try to use some diverging data:
- >>> divradec = w.all_pix2world([[1.0,1.0],[10000.0,50000.0],\
-[3.0,1.0]],1); print(divradec)
- [[ 5.52645241 -72.05171776]
- [ 7.15979392 -70.81405561]
- [ 5.52653313 -72.05170814]]
-
- >>> try:
- >>> xy = w.all_world2pix(divradec,1, maxiter=20, accuracy=1.0e-4, \
-adaptive=False, detect_divergence=True, quiet=False)
- >>> except stwcs.wcsutil.hstwcs.NoConvergence as e:
- >>> print("Indices of diverging points: {}".format(e.divergent))
- >>> print("Indices of poorly converging points: {}".format(e.failed2converge))
- >>> print("Best solution: {}".format(e.best_solution))
- >>> print("Achieved accuracy: {}".format(e.accuracy))
- >>> raise e
- Indices of diverging points:
- [1]
- Indices of poorly converging points:
- None
- Best solution:
- [[ 1.00006219e+00 9.99999288e-01]
- [ -1.99440907e+06 1.44308548e+06]
- [ 3.00006257e+00 9.99999316e-01]]
- Achieved accuracy:
- [[ 5.98554253e-05 6.79918148e-07]
- [ 8.59514088e+11 6.61703754e+11]
- [ 6.02334592e-05 6.59713067e-07]]
- Traceback (innermost last):
- File "<console>", line 8, in <module>
- NoConvergence: 'HSTWCS.all_world2pix' failed to converge to the requested accuracy.
- After 5 iterations, the solution is diverging at least for one input point.
-
- >>> try:
- >>> xy = w.all_world2pix(divradec,1, maxiter=20, accuracy=1.0e-4, \
-adaptive=False, detect_divergence=False, quiet=False)
- >>> except stwcs.wcsutil.hstwcs.NoConvergence as e:
- >>> print("Indices of diverging points: {}".format(e.divergent))
- >>> print("Indices of poorly converging points: {}".format(e.failed2converge))
- >>> print("Best solution: {}".format(e.best_solution))
- >>> print("Achieved accuracy: {}".format(e.accuracy))
- >>> raise e
- Indices of diverging points:
- [1]
- Indices of poorly converging points:
- None
- Best solution:
- [[ 1. 1.]
- [ nan nan]
- [ 3. 1.]]
- Achieved accuracy:
- [[ 0. 0.]
- [ nan nan]
- [ 0. 0.]]
- Traceback (innermost last):
- File "<console>", line 8, in <module>
- NoConvergence: 'HSTWCS.all_world2pix' failed to converge to the requested accuracy.
- After 20 iterations, the solution is diverging at least for one input point.
-
- """
- #####################################################################
- ## PROCESS ARGUMENTS: ##
- #####################################################################
- nargs = len(args)
-
- if nargs == 3:
- try:
- ra = np.asarray(args[0], dtype=np.float64)
- dec = np.asarray(args[1], dtype=np.float64)
- #assert( len(ra.shape) == 1 and len(dec.shape) == 1 )
- origin = int(args[2])
- vect1D = True
- except:
- raise TypeError("When providing three arguments, they must " \
- "be (Ra, Dec, origin) where Ra and Dec are " \
- "Nx1 vectors.")
- elif nargs == 2:
- try:
- rd = np.asarray(args[0], dtype=np.float64)
- #assert( rd.shape[1] == 2 )
- ra = rd[:,0]
- dec = rd[:,1]
- origin = int(args[1])
- vect1D = False
- except:
- raise TypeError("When providing two arguments, they must " \
- "be (RaDec, origin) where RaDec is a Nx2 array.")
- else:
- raise TypeError("Expected 2 or 3 arguments, {:d} given." \
- .format(nargs))
-
- # process optional arguments:
- accuracy = kwargs.pop('accuracy', 1.0e-4)
- maxiter = kwargs.pop('maxiter', 20)
- adaptive = kwargs.pop('adaptive', False)
- detect_divergence = kwargs.pop('detect_divergence', True)
- quiet = kwargs.pop('quiet', False)
-
- #####################################################################
- ## INITIALIZE ITERATIVE PROCESS: ##
- #####################################################################
- x0, y0 = self.wcs_world2pix(ra, dec, origin) # <-- initial approximation
- # (WCS based only)
-
- # see if an iterative solution is required (when any of the
- # non-CD-matrix corrections are present). If not required
- # return initial approximation (x0, y0).
- if self.sip is None and \
- self.cpdis1 is None and self.cpdis2 is None and \
- self.det2im1 is None and self.det2im2 is None:
- # no non-WCS corrections are detected - return
- # initial approximation
- if vect1D:
- return [x0, y0]
- else:
- return np.dstack([x0,y0])[0]
-
- x = x0.copy() # 0-order solution
- y = y0.copy() # 0-order solution
-
- # initial correction:
- dx, dy = self.pix2foc(x, y, origin)
- # If pix2foc does not apply all the required distortion
- # corrections then replace the above line with:
- #r0, d0 = self.all_pix2world(x, y, origin)
- #dx, dy = self.wcs_world2pix(r0, d0, origin )
- dx -= x0
- dy -= y0
-
- # update initial solution:
- x -= dx
- y -= dy
-
- # norn (L2) squared of the correction:
- dn2prev = dx**2+dy**2
- dn2 = dn2prev
-
- # prepare for iterative process
- iterlist = list(range(1, maxiter+1))
- accuracy2 = accuracy**2
- ind = None
- inddiv = None
-
- npts = x.shape[0]
-
- # turn off numpy runtime warnings for 'invalid' and 'over':
- old_invalid = np.geterr()['invalid']
- old_over = np.geterr()['over']
- np.seterr(invalid = 'ignore', over = 'ignore')
-
- #####################################################################
- ## NON-ADAPTIVE ITERATIONS: ##
- #####################################################################
- if not adaptive:
- for k in iterlist:
- # check convergence:
- if np.max(dn2) < accuracy2:
- break
-
- # find correction to the previous solution:
- dx, dy = self.pix2foc(x, y, origin)
- # If pix2foc does not apply all the required distortion
- # corrections then replace the above line with:
- #r0, d0 = self.all_pix2world(x, y, origin)
- #dx, dy = self.wcs_world2pix(r0, d0, origin )
- dx -= x0
- dy -= y0
-
- # update norn (L2) squared of the correction:
- dn2 = dx**2+dy**2
-
- # check for divergence (we do this in two stages
- # to optimize performance for the most common
- # scenario when succesive approximations converge):
- if detect_divergence:
- ind, = np.where(dn2 <= dn2prev)
- if ind.shape[0] < npts:
- inddiv, = np.where(
- np.logical_and(dn2 > dn2prev, dn2 >= accuracy2))
- if inddiv.shape[0] > 0:
- # apply correction only to the converging points:
- x[ind] -= dx[ind]
- y[ind] -= dy[ind]
- # switch to adaptive iterations:
- ind, = np.where((dn2 >= accuracy2) & \
- (dn2 <= dn2prev) & np.isfinite(dn2))
- iterlist = iterlist[k:]
- adaptive = True
- break
- #dn2prev[ind] = dn2[ind]
- dn2prev = dn2
-
- # apply correction:
- x -= dx
- y -= dy
-
- #####################################################################
- ## ADAPTIVE ITERATIONS: ##
- #####################################################################
- if adaptive:
- if ind is None:
- ind = np.asarray(list(range(npts)), dtype=np.int64)
-
- for k in iterlist:
- # check convergence:
- if ind.shape[0] == 0:
- break
-
- # find correction to the previous solution:
- dx[ind], dy[ind] = self.pix2foc(x[ind], y[ind], origin)
- # If pix2foc does not apply all the required distortion
- # corrections then replace the above line with:
- #r0[ind], d0[ind] = self.all_pix2world(x[ind], y[ind], origin)
- #dx[ind], dy[ind] = self.wcs_world2pix(r0[ind], d0[ind], origin)
- dx[ind] -= x0[ind]
- dy[ind] -= y0[ind]
-
- # update norn (L2) squared of the correction:
- dn2 = dx**2+dy**2
-
- # update indices of elements that still need correction:
- if detect_divergence:
- ind, = np.where((dn2 >= accuracy2) & (dn2 <= dn2prev))
- #ind = ind[np.where((dn2[ind] >= accuracy2) & (dn2[ind] <= dn2prev))]
- dn2prev[ind] = dn2[ind]
- else:
- ind, = np.where(dn2 >= accuracy2)
- #ind = ind[np.where(dn2[ind] >= accuracy2)]
-
- # apply correction:
- x[ind] -= dx[ind]
- y[ind] -= dy[ind]
-
- #####################################################################
- ## FINAL DETECTION OF INVALID, DIVERGING, ##
- ## AND FAILED-TO-CONVERGE POINTS ##
- #####################################################################
- # Identify diverging and/or invalid points:
- invalid = (((~np.isfinite(y)) | (~np.isfinite(x)) | \
- (~np.isfinite(dn2))) & \
- (np.isfinite(ra)) & (np.isfinite(dec)))
- # When detect_divergence==False, dn2prev is outdated (it is the
- # norm^2 of the very first correction). Still better than nothing...
- inddiv, = np.where(((dn2 >= accuracy2) & (dn2 > dn2prev)) | invalid)
- if inddiv.shape[0] == 0:
- inddiv = None
- # identify points that did not converge within
- # 'maxiter' iterations:
- if k >= maxiter:
- ind,= np.where((dn2 >= accuracy2) & (dn2 <= dn2prev) & (~invalid))
- if ind.shape[0] == 0:
- ind = None
- else:
- ind = None
-
- #####################################################################
- ## RAISE EXCEPTION IF DIVERGING OR TOO SLOWLY CONVERGING ##
- ## DATA POINTS HAVE BEEN DETECTED: ##
- #####################################################################
- # raise exception if diverging or too slowly converging
- if (ind is not None or inddiv is not None) and not quiet:
- if vect1D:
- sol = [x, y]
- err = [np.abs(dx), np.abs(dy)]
- else:
- sol = np.dstack( [x, y] )[0]
- err = np.dstack( [np.abs(dx), np.abs(dy)] )[0]
-
- # restore previous numpy error settings:
- np.seterr(invalid = old_invalid, over = old_over)
-
- if inddiv is None:
- raise NoConvergence("'HSTWCS.all_world2pix' failed to " \
- "converge to the requested accuracy after {:d} " \
- "iterations.".format(k), best_solution = sol, \
- accuracy = err, niter = k, failed2converge = ind, \
- divergent = None)
- else:
- raise NoConvergence("'HSTWCS.all_world2pix' failed to " \
- "converge to the requested accuracy.{0:s}" \
- "After {1:d} iterations, the solution is diverging " \
- "at least for one input point." \
- .format(os.linesep, k), best_solution = sol, \
- accuracy = err, niter = k, failed2converge = ind, \
- divergent = inddiv)
-
- #####################################################################
- ## FINALIZE AND FORMAT DATA FOR RETURN: ##
- #####################################################################
- # restore previous numpy error settings:
- np.seterr(invalid = old_invalid, over = old_over)
-
- if vect1D:
- return [x, y]
- else:
- return np.dstack( [x, y] )[0]
-
- def _updatehdr(self, ext_hdr):
- #kw2add : OCX10, OCX11, OCY10, OCY11
- # record the model in the header for use by pydrizzle
- ext_hdr['OCX10'] = self.idcmodel.cx[1,0]
- ext_hdr['OCX11'] = self.idcmodel.cx[1,1]
- ext_hdr['OCY10'] = self.idcmodel.cy[1,0]
- ext_hdr['OCY11'] = self.idcmodel.cy[1,1]
- ext_hdr['IDCSCALE'] = self.idcmodel.refpix['PSCALE']
- ext_hdr['IDCTHETA'] = self.idcmodel.refpix['THETA']
- ext_hdr['IDCXREF'] = self.idcmodel.refpix['XREF']
- ext_hdr['IDCYREF'] = self.idcmodel.refpix['YREF']
- ext_hdr['IDCV2REF'] = self.idcmodel.refpix['V2REF']
- ext_hdr['IDCV3REF'] = self.idcmodel.refpix['V3REF']
-
- def printwcs(self):
- """
- Print the basic WCS keywords.
- """
- print('WCS Keywords\n')
- print('CD_11 CD_12: %r %r' % (self.wcs.cd[0,0], self.wcs.cd[0,1]))
- print('CD_21 CD_22: %r %r' % (self.wcs.cd[1,0], self.wcs.cd[1,1]))
- print('CRVAL : %r %r' % (self.wcs.crval[0], self.wcs.crval[1]))
- print('CRPIX : %r %r' % (self.wcs.crpix[0], self.wcs.crpix[1]))
- print('NAXIS : %d %d' % (self.naxis1, self.naxis2))
- print('Plate Scale : %r' % self.pscale)
- print('ORIENTAT : %r' % self.orientat)
-
-
-def determine_refframe(phdr):
- """
- Determine the reference frame in standard FITS WCS terms.
-
- Parameters
- ----------
- phdr : `astropy.io.fits.Header`
- Primary Header of an HST observation
-
- In HST images the reference frame is recorded in the primary extension as REFFRAME.
- Values are "GSC1" which means FK5 or ICRS (for GSC2 observations).
- """
- try:
- refframe = phdr['REFFRAME']
- except KeyError:
- refframe = " "
- if refframe == "GSC1":
- refframe = "FK5"
- return refframe
diff --git a/lib/stwcs/wcsutil/instruments.py b/lib/stwcs/wcsutil/instruments.py
deleted file mode 100644
index f662513..0000000
--- a/lib/stwcs/wcsutil/instruments.py
+++ /dev/null
@@ -1,320 +0,0 @@
-from __future__ import absolute_import, division, print_function # confidence high
-
-from .mappings import ins_spec_kw
-
-class InstrWCS(object):
- """
- A base class for instrument specific keyword definition.
- It prvides a default implementation (modeled by ACS) for
- all set_kw methods.
- """
- def __init__(self, hdr0=None, hdr=None):
- self.exthdr = hdr
- self.primhdr = hdr0
- self.set_ins_spec_kw()
-
- def set_ins_spec_kw(self):
- """
- This method MUST call all set_kw methods.
- There should be a set_kw method for all kw listed in
- mappings.ins_spec_kw. TypeError handles the case when
- fobj='DEFAULT'.
- """
- self.set_idctab()
- self.set_offtab()
- self.set_date_obs()
- self.set_ra_targ()
- self.set_dec_targ()
- self.set_pav3()
- self.set_detector()
- self.set_filter1()
- self.set_filter2()
- self.set_vafactor()
- self.set_naxis1()
- self.set_naxis2()
- self.set_ltv1()
- self.set_ltv2()
- self.set_binned()
- self.set_chip()
- self.set_parity()
-
- def set_idctab(self):
- try:
- self.idctab = self.primhdr['IDCTAB']
- except (KeyError, TypeError):
- self.idctab = None
-
- def set_offtab(self):
- try:
- self.offtab = self.primhdr['OFFTAB']
- except (KeyError, TypeError):
- self.offtab = None
-
- def set_date_obs(self):
- try:
- self.date_obs = self.primhdr['DATE-OBS']
- except (KeyError, TypeError):
- self.date_obs = None
-
- def set_ra_targ(self):
- try:
- self.ra_targ = self.primhdr['RA-TARG']
- except (KeyError, TypeError):
- self.ra_targ = None
-
- def set_dec_targ(self):
- try:
- self.dec_targ = self.primhdr['DEC-TARG']
- except (KeyError, TypeError):
- self.dec_targ = None
-
- def set_pav3(self):
- try:
- self.pav3 = self.primhdr['PA_V3']
- except (KeyError, TypeError):
- self.pav3 = None
-
- def set_filter1(self):
- try:
- self.filter1 = self.primhdr['FILTER1']
- except (KeyError, TypeError):
- self.filter1 = None
-
- def set_filter2(self):
- try:
- self.filter2 = self.primhdr['FILTER2']
- except (KeyError, TypeError):
- self.filter2 = None
-
- def set_vafactor(self):
- try:
- self.vafactor = self.exthdr['VAFACTOR']
- except (KeyError, TypeError):
- self.vafactor = 1
-
- def set_naxis1(self):
- try:
- self.naxis1 = self.exthdr['naxis1']
- except (KeyError, TypeError):
- try:
- self.naxis1 = self.exthdr['npix1']
- except (KeyError, TypeError):
- self.naxis1 = None
-
- def set_naxis2(self):
- try:
- self.naxis2 = self.exthdr['naxis2']
- except (KeyError, TypeError):
- try:
- self.naxis2 = self.exthdr['npix2']
- except (KeyError, TypeError):
- self.naxis2 = None
-
- def set_ltv1(self):
- try:
- self.ltv1 = self.exthdr['LTV1']
- except (KeyError, TypeError):
- self.ltv1 = 0.0
-
- def set_ltv2(self):
- try:
- self.ltv2 = self.exthdr['LTV2']
- except (KeyError, TypeError):
- self.ltv2 = 0.0
-
- def set_binned(self):
- try:
- self.binned = self.exthdr['BINAXIS1']
- except (KeyError, TypeError):
- self.binned = 1
-
- def set_chip(self):
- try:
- self.chip = self.exthdr['CCDCHIP']
- except (KeyError, TypeError):
- self.chip = 1
-
- def set_parity(self):
- self.parity = [[1.0,0.0],[0.0,-1.0]]
-
- def set_detector(self):
- # each instrument has a different kw for detector and it can be
- # in a different header, so this is to be handled by the instrument classes
- self.detector = 'DEFAULT'
-
-class ACSWCS(InstrWCS):
- """
- get instrument specific kw
- """
-
- def __init__(self, hdr0, hdr):
- self.primhdr = hdr0
- self.exthdr = hdr
- InstrWCS.__init__(self,hdr0, hdr)
- self.set_ins_spec_kw()
-
- def set_detector(self):
- try:
- self.detector = self.primhdr['DETECTOR']
- except KeyError:
- print('ERROR: Detector kw not found.\n')
- raise
-
- def set_parity(self):
- parity = {'WFC':[[1.0,0.0],[0.0,-1.0]],
- 'HRC':[[-1.0,0.0],[0.0,1.0]],
- 'SBC':[[-1.0,0.0],[0.0,1.0]]}
-
- if self.detector not in list(parity.keys()):
- parity = InstrWCS.set_parity(self)
- else:
- self.parity = parity[self.detector]
-
-
-class WFPC2WCS(InstrWCS):
-
-
- def __init__(self, hdr0, hdr):
- self.primhdr = hdr0
- self.exthdr = hdr
- InstrWCS.__init__(self,hdr0, hdr)
- self.set_ins_spec_kw()
-
- def set_filter1(self):
- self.filter1 = self.primhdr.get('FILTNAM1', None)
- if self.filter1 == " " or self.filter1 == None:
- self.filter1 = 'CLEAR1'
-
- def set_filter2(self):
- self.filter2 = self.primhdr.get('FILTNAM2', None)
- if self.filter2 == " " or self.filter2 == None:
- self.filter2 = 'CLEAR2'
-
-
- def set_binned(self):
- mode = self.primhdr.get('MODE', 1)
- if mode == 'FULL':
- self.binned = 1
- elif mode == 'AREA':
- self.binned = 2
-
- def set_chip(self):
- self.chip = self.exthdr.get('DETECTOR', 1)
-
- def set_parity(self):
- self.parity = [[-1.0,0.],[0.,1.0]]
-
- def set_detector(self):
- try:
- self.detector = self.exthdr['DETECTOR']
- except KeyError:
- print('ERROR: Detector kw not found.\n')
- raise
-
-
-class WFC3WCS(InstrWCS):
- """
- Create a WFC3 detector specific class
- """
-
- def __init__(self, hdr0, hdr):
- self.primhdr = hdr0
- self.exthdr = hdr
- InstrWCS.__init__(self,hdr0, hdr)
- self.set_ins_spec_kw()
-
- def set_detector(self):
- try:
- self.detector = self.primhdr['DETECTOR']
- except KeyError:
- print('ERROR: Detector kw not found.\n')
- raise
-
- def set_filter1(self):
- self.filter1 = self.primhdr.get('FILTER', None)
- if self.filter1 == " " or self.filter1 == None:
- self.filter1 = 'CLEAR'
-
- def set_filter2(self):
- #Nicmos idc tables do not allow 2 filters.
- self.filter2 = 'CLEAR'
-
- def set_parity(self):
- parity = {'UVIS':[[-1.0,0.0],[0.0,1.0]],
- 'IR':[[-1.0,0.0],[0.0,1.0]]}
-
- if self.detector not in list(parity.keys()):
- parity = InstrWCS.set_parity(self)
- else:
- self.parity = parity[self.detector]
-
-class NICMOSWCS(InstrWCS):
- """
- Create a NICMOS specific class
- """
-
- def __init__(self, hdr0, hdr):
- self.primhdr = hdr0
- self.exthdr = hdr
- InstrWCS.__init__(self,hdr0, hdr)
- self.set_ins_spec_kw()
-
- def set_parity(self):
- self.parity = [[-1.0,0.],[0.,1.0]]
-
- def set_filter1(self):
- self.filter1 = self.primhdr.get('FILTER', None)
- if self.filter1 == " " or self.filter1 == None:
- self.filter1 = 'CLEAR'
-
- def set_filter2(self):
- #Nicmos idc tables do not allow 2 filters.
- self.filter2 = 'CLEAR'
-
- def set_chip(self):
- self.chip = self.detector
-
- def set_detector(self):
- try:
- self.detector = self.primhdr['CAMERA']
- except KeyError:
- print('ERROR: Detector kw not found.\n')
- raise
-
-class STISWCS(InstrWCS):
- """
- A STIS specific class
- """
-
- def __init__(self, hdr0, hdr):
- self.primhdr = hdr0
- self.exthdr = hdr
- InstrWCS.__init__(self,hdr0, hdr)
- self.set_ins_spec_kw()
-
- def set_parity(self):
- self.parity = [[-1.0,0.],[0.,1.0]]
-
- def set_filter1(self):
- self.filter1 = self.exthdr.get('OPT_ELEM', None)
- if self.filter1 == " " or self.filter1 == None:
- self.filter1 = 'CLEAR1'
-
- def set_filter2(self):
- self.filter2 = self.exthdr.get('FILTER', None)
- if self.filter2 == " " or self.filter2 == None:
- self.filter2 = 'CLEAR2'
-
- def set_detector(self):
- try:
- self.detector = self.primhdr['DETECTOR']
- except KeyError:
- print('ERROR: Detector kw not found.\n')
- raise
-
- def set_date_obs(self):
- try:
- self.date_obs = self.exthdr['DATE-OBS']
- except (KeyError, TypeError):
- self.date_obs = None
-
diff --git a/lib/stwcs/wcsutil/mappings.py b/lib/stwcs/wcsutil/mappings.py
deleted file mode 100644
index 24038bf..0000000
--- a/lib/stwcs/wcsutil/mappings.py
+++ /dev/null
@@ -1,29 +0,0 @@
-from __future__ import division # confidence high
-
-# This dictionary maps an instrument into an instrument class
-# The instrument class handles instrument specific keywords
-
-inst_mappings={'WFPC2': 'WFPC2WCS',
- 'ACS': 'ACSWCS',
- 'NICMOS': 'NICMOSWCS',
- 'STIS': 'STISWCS',
- 'WFC3': 'WFC3WCS',
- 'DEFAULT': 'InstrWCS'
- }
-
-
-# A list of instrument specific keywords
-# Every instrument class must have methods which define each of these
-# as class attributes.
-ins_spec_kw = [ 'idctab', 'offtab', 'date_obs', 'ra_targ', 'dec_targ', 'pav3', \
- 'detector', 'ltv1', 'ltv2', 'parity', 'binned','vafactor', \
- 'chip', 'naxis1', 'naxis2', 'filter1', 'filter2']
-
-# A list of keywords defined in the primary header.
-# The HSTWCS class sets this as attributes
-prim_hdr_kw = ['detector', 'offtab', 'idctab', 'date-obs',
- 'pa_v3', 'ra_targ', 'dec_targ']
-
-# These are the keywords which are archived before MakeWCS is run
-basic_wcs = ['CD1_', 'CD2_', 'CRVAL', 'CTYPE', 'CRPIX', 'CTYPE', 'CDELT', 'CUNIT']
-
diff --git a/lib/stwcs/wcsutil/mosaic.py b/lib/stwcs/wcsutil/mosaic.py
deleted file mode 100644
index 9d2d0a3..0000000
--- a/lib/stwcs/wcsutil/mosaic.py
+++ /dev/null
@@ -1,183 +0,0 @@
-from __future__ import division, print_function
-import numpy as np
-from matplotlib import pyplot as plt
-from astropy.io import fits
-import string
-
-from stsci.tools import parseinput, irafglob
-from stwcs.distortion import utils
-from stwcs import updatewcs, wcsutil
-from stwcs.wcsutil import altwcs
-
-def vmosaic(fnames, outwcs=None, ref_wcs=None, ext=None, extname=None, undistort=True, wkey='V', wname='VirtualMosaic', plot=False, clobber=False):
- """
- Create a virtual mosaic using the WCS of the input images.
-
- Parameters
- ----------
- fnames: a string or a list
- a string or a list of filenames, or a list of wcsutil.HSTWCS objects
- outwcs: an HSTWCS object
- if given, represents the output tangent plane
- if None, the output WCS is calculated from the input observations.
- ref_wcs: an HSTWCS object
- if output wcs is not given, this will be used as a reference for the
- calculation of the output WCS. If ref_wcs is None and outwcs is None,
- then the first observation in th einput list is used as reference.
- ext: an int, a tuple or a list
- an int - represents a FITS extension, e.g. 0 is the primary HDU
- a tuple - uses the notation (extname, extver), e.g. ('sci',1)
- Can be a list of integers or tuples representing FITS extensions
- extname: string
- the value of the EXTNAME keyword for the extensions to be used in the mosaic
- undistort: boolean (default: True)
- undistort (or not) the output WCS
- wkey: string
- default: 'V'
- one character A-Z to be used to record the virtual mosaic WCS as
- an alternate WCS in the headers of the input files.
- wname: string
- default: 'VirtualMosaic
- a string to be used as a WCSNAME value for the alternate WCS representign
- the virtual mosaic
- plot: boolean
- if True and matplotlib is installed will make a plot of the tangent plane
- and the location of the input observations.
- clobber: boolean
- This covers the case when an alternate WCS with the requested key
- already exists in the header of the input files.
- if clobber is True, it will be overwritten
- if False, it will compute the new one but will not write it to the headers.
-
- Notes
- -----
- The algorithm is:
- 1. If output WCS is not given it is calculated from the input WCSes.
- The first image is used as a reference, if no reference is given.
- This represents the virtual mosaic WCS.
- 2. For each input observation/chip, an HSTWCS object is created
- and its footprint on the sky is calculated (using only the four corners).
- 3. For each input observation the footprint is projected on the output
- tangent plane and the virtual WCS is recorded in the header.
- """
- wcsobjects = readWCS(fnames, ext, extname)
- if outwcs != None:
- outwcs = outwcs.deepcopy()
- else:
- if ref_wcs != None:
- outwcs = utils.output_wcs(wcsobjects, ref_wcs=ref_wcs, undistort=undistort)
- else:
- outwcs = utils.output_wcs(wcsobjects, undistort=undistort)
- if plot:
- outc=np.array([[0.,0], [outwcs._naxis1, 0],
- [outwcs._naxis1, outwcs._naxis2],
- [0, outwcs._naxis2], [0, 0]])
- plt.plot(outc[:,0], outc[:,1])
- for wobj in wcsobjects:
- outcorners = outwcs.wcs_world2pix(wobj.calc_footprint(),1)
- if plot:
- plt.plot(outcorners[:,0], outcorners[:,1])
- objwcs = outwcs.deepcopy()
- objwcs.wcs.crpix = objwcs.wcs.crpix - (outcorners[0])
- updatehdr(wobj.filename, objwcs,wkey=wkey, wcsname=wname, ext=wobj.extname, clobber=clobber)
- return outwcs
-
-def updatehdr(fname, wcsobj, wkey, wcsname, ext=1, clobber=False):
- hdr = fits.getheader(fname, ext=ext)
- all_keys = list(string.ascii_uppercase)
- if wkey.upper() not in all_keys:
- raise KeyError("wkey must be one character: A-Z")
- if wkey not in altwcs.available_wcskeys(hdr):
- if not clobber:
- raise ValueError("wkey %s is already in use. Use clobber=True to overwrite it or specify a different key." %wkey)
- else:
- altwcs.deleteWCS(fname, ext=ext, wcskey='V')
- f = fits.open(fname, mode='update')
-
- hwcs = wcs2header(wcsobj)
- wcsnamekey = 'WCSNAME' + wkey
- f[ext].header[wcsnamekey] = wcsname
- for k in hwcs:
- f[ext].header[k[:7]+wkey] = hwcs[k]
-
- f.close()
-
-def wcs2header(wcsobj):
-
- h = wcsobj.to_header()
-
- if wcsobj.wcs.has_cd():
- altwcs.pc2cd(h)
- h['CTYPE1'] = 'RA---TAN'
- h['CTYPE2'] = 'DEC--TAN'
- norient = np.rad2deg(np.arctan2(h['CD1_2'],h['CD2_2']))
- #okey = 'ORIENT%s' % wkey
- okey = 'ORIENT'
- h[okey] = norient
- return h
-
-def readWCS(input, exts=None, extname=None):
- if isinstance(input, str):
- if input[0] == '@':
- # input is an @ file
- filelist = irafglob.irafglob(input)
- else:
- try:
- filelist, output = parseinput.parseinput(input)
- except IOError: raise
- elif isinstance(input, list):
- if isinstance(input[0], wcsutil.HSTWCS):
- # a list of HSTWCS objects
- return input
- else:
- filelist = input[:]
- wcso = []
- fomited = []
- # figure out which FITS extension(s) to use
- if exts == None and extname == None:
- #Assume it's simple FITS and the data is in the primary HDU
- for f in filelist:
- try:
- wcso = wcsutil.HSTWCS(f)
- except AttributeError:
- fomited.append(f)
- continue
- elif exts != None and validateExt(exts):
- exts = [exts]
- for f in filelist:
- try:
- wcso.extend([wcsutil.HSTWCS(f, ext=e) for e in exts])
- except KeyError:
- fomited.append(f)
- continue
- elif extname != None:
- for f in filelist:
- fobj = fits.open(f)
- for i in range(len(fobj)):
- try:
- ename = fobj[i].header['EXTNAME']
- except KeyError:
- continue
- if ename.lower() == extname.lower():
- wcso.append(wcsutil.HSTWCS(f,ext=i))
- else:
- continue
- fobj.close()
- if fomited != []:
- print("These files were skipped:")
- for f in fomited:
- print(f)
- return wcso
-
-
-def validateExt(ext):
- if not isinstance(ext, int) and not isinstance(ext, tuple) \
- and not isinstance(ext, list):
- print("Ext must be integer, tuple, a list of int extension numbers, \
- or a list of tuples representing a fits extension, for example ('sci', 1).")
- return False
- else:
- return True
-
-
-
diff --git a/lib/stwcs/wcsutil/wcscorr.py b/lib/stwcs/wcsutil/wcscorr.py
deleted file mode 100644
index 3f9b7d5..0000000
--- a/lib/stwcs/wcsutil/wcscorr.py
+++ /dev/null
@@ -1,668 +0,0 @@
-from __future__ import absolute_import, division, print_function
-
-import os,copy
-import numpy as np
-from astropy.io import fits
-
-import stwcs
-from stwcs.wcsutil import altwcs
-from stwcs.updatewcs import utils
-from stsci.tools import fileutil
-from . import convertwcs
-
-DEFAULT_WCS_KEYS = ['CRVAL1','CRVAL2','CRPIX1','CRPIX2',
- 'CD1_1','CD1_2','CD2_1','CD2_2',
- 'CTYPE1','CTYPE2','ORIENTAT']
-DEFAULT_PRI_KEYS = ['HDRNAME','SIPNAME','NPOLNAME','D2IMNAME','DESCRIP']
-COL_FITSKW_DICT = {'RMS_RA':'sci.crder1','RMS_DEC':'sci.crder2',
- 'NMatch':'sci.nmatch','Catalog':'sci.catalog'}
-
-###
-### WCSEXT table related keyword archive functions
-###
-def init_wcscorr(input, force=False):
- """
- This function will initialize the WCSCORR table if it is not already present,
- and look for WCS keywords with a prefix of 'O' as the original OPUS
- generated WCS as the initial row for the table or use the current WCS
- keywords as initial row if no 'O' prefix keywords are found.
-
- This function will NOT overwrite any rows already present.
-
- This function works on all SCI extensions at one time.
- """
- # TODO: Create some sort of decorator or (for Python2.5) context for
- # opening a FITS file and closing it when done, if necessary
- if not isinstance(input, fits.HDUList):
- # input must be a filename, so open as `astropy.io.fits.HDUList` object
- fimg = fits.open(input, mode='update')
- need_to_close = True
- else:
- fimg = input
- need_to_close = False
-
- # Do not try to generate a WCSCORR table for a simple FITS file
- numsci = fileutil.countExtn(fimg)
- if len(fimg) == 1 or numsci == 0:
- return
-
- enames = []
- for e in fimg: enames.append(e.name)
- if 'WCSCORR' in enames:
- if not force:
- return
- else:
- del fimg['wcscorr']
- print('Initializing new WCSCORR table for ',fimg.filename())
-
- used_wcskeys = altwcs.wcskeys(fimg['SCI', 1].header)
-
- # define the primary columns of the WCSEXT table with initial rows for each
- # SCI extension for the original OPUS solution
- numwcs = len(used_wcskeys)
- if numwcs == 0: numwcs = 1
-
- # create new table with more rows than needed initially to make it easier to
- # add new rows later
- wcsext = create_wcscorr(descrip=True,numrows=numsci, padding=(numsci*numwcs) + numsci * 4)
- # Assign the correct EXTNAME value to this table extension
- wcsext.header['TROWS'] = (numsci * 2, 'Number of updated rows in table')
- wcsext.header['EXTNAME'] = ('WCSCORR', 'Table with WCS Update history')
- wcsext.header['EXTVER'] = 1
-
- # define set of WCS keywords which need to be managed and copied to the table
- wcs1 = stwcs.wcsutil.HSTWCS(fimg,ext=('SCI',1))
- idc2header = True
- if wcs1.idcscale is None:
- idc2header = False
- wcs_keywords = list(wcs1.wcs2header(idc2hdr=idc2header).keys())
-
- prihdr = fimg[0].header
- prihdr_keys = DEFAULT_PRI_KEYS
- pri_funcs = {'SIPNAME':stwcs.updatewcs.utils.build_sipname,
- 'NPOLNAME':stwcs.updatewcs.utils.build_npolname,
- 'D2IMNAME':stwcs.updatewcs.utils.build_d2imname}
-
- # Now copy original OPUS values into table
- for extver in range(1, numsci + 1):
- rowind = find_wcscorr_row(wcsext.data,
- {'WCS_ID': 'OPUS', 'EXTVER': extver,
- 'WCS_key':'O'})
- # There should only EVER be a single row for each extension with OPUS values
- rownum = np.where(rowind)[0][0]
- #print 'Archiving OPUS WCS in row number ',rownum,' in WCSCORR table for SCI,',extver
-
- hdr = fimg['SCI', extver].header
- # define set of WCS keywords which need to be managed and copied to the table
- if used_wcskeys is None:
- used_wcskeys = altwcs.wcskeys(hdr)
- # Check to see whether or not there is an OPUS alternate WCS present,
- # if so, get its values directly, otherwise, archive the PRIMARY WCS
- # as the OPUS values in the WCSCORR table
- if 'O' not in used_wcskeys:
- altwcs.archiveWCS(fimg,('SCI',extver),wcskey='O', wcsname='OPUS')
- wkey = 'O'
-
- wcs = stwcs.wcsutil.HSTWCS(fimg, ext=('SCI', extver), wcskey=wkey)
- wcshdr = wcs.wcs2header(idc2hdr=idc2header)
-
- if wcsext.data.field('CRVAL1')[rownum] != 0:
- # If we find values for these keywords already in the table, do not
- # overwrite them again
- print('WCS keywords already updated...')
- break
- for key in wcs_keywords:
- if key in wcsext.data.names:
- wcsext.data.field(key)[rownum] = wcshdr[(key+wkey)[:8]]
- # Now get any keywords from PRIMARY header needed for WCS updates
- for key in prihdr_keys:
- if key in prihdr:
- val = prihdr[key]
- else:
- val = ''
- wcsext.data.field(key)[rownum] = val
-
- # Now that we have archived the OPUS alternate WCS, remove it from the list
- # of used_wcskeys
- if 'O' in used_wcskeys:
- used_wcskeys.remove('O')
-
- # Now copy remaining alternate WCSs into table
- # TODO: Much of this appears to be redundant with update_wcscorr; consider
- # merging them...
- for uwkey in used_wcskeys:
- for extver in range(1, numsci + 1):
- hdr = fimg['SCI', extver].header
- wcs = stwcs.wcsutil.HSTWCS(fimg, ext=('SCI', extver),
- wcskey=uwkey)
- wcshdr = wcs.wcs2header()
- if 'WCSNAME' + uwkey not in wcshdr:
- wcsid = utils.build_default_wcsname(fimg[0].header['idctab'])
- else:
- wcsid = wcshdr['WCSNAME' + uwkey]
-
- # identify next empty row
- rowind = find_wcscorr_row(wcsext.data,
- selections={'wcs_id':['','0.0']})
- rows = np.where(rowind)
- if len(rows[0]) > 0:
- rownum = np.where(rowind)[0][0]
- else:
- print('No available rows found for updating. ')
-
- # Update selection columns for this row with relevant values
- wcsext.data.field('WCS_ID')[rownum] = wcsid
- wcsext.data.field('EXTVER')[rownum] = extver
- wcsext.data.field('WCS_key')[rownum] = uwkey
-
- # Look for standard WCS keyword values
- for key in wcs_keywords:
- if key in wcsext.data.names:
- wcsext.data.field(key)[rownum] = wcshdr[key + uwkey]
- # Now get any keywords from PRIMARY header needed for WCS updates
- for key in prihdr_keys:
- if key in pri_funcs:
- val = pri_funcs[key](fimg)[0]
- else:
- if key in prihdr:
- val = prihdr[key]
- else:
- val = ''
- wcsext.data.field(key)[rownum] = val
-
- # Append this table to the image FITS file
- fimg.append(wcsext)
- # force an update now
- # set the verify flag to 'warn' so that it will always succeed, but still
- # tell the user if PyFITS detects any problems with the file as a whole
- utils.updateNEXTENDKw(fimg)
-
- fimg.flush('warn')
-
- if need_to_close:
- fimg.close()
-
-
-def find_wcscorr_row(wcstab, selections):
- """
- Return an array of indices from the table (NOT HDU) 'wcstab' that matches the
- selections specified by the user.
-
- The row selection criteria must be specified as a dictionary with
- column name as key and value(s) representing the valid desired row values.
- For example, {'wcs_id':'OPUS','extver':2}.
- """
-
- mask = None
- for i in selections:
- icol = wcstab.field(i)
- if isinstance(icol,np.chararray): icol = icol.rstrip()
- selecti = selections[i]
- if not isinstance(selecti,list):
- if isinstance(selecti,str):
- selecti = selecti.rstrip()
- bmask = (icol == selecti)
- if mask is None:
- mask = bmask.copy()
- else:
- mask = np.logical_and(mask,bmask)
- del bmask
- else:
- for si in selecti:
- if isinstance(si,str):
- si = si.rstrip()
- bmask = (icol == si)
- if mask is None:
- mask = bmask.copy()
- else:
- mask = np.logical_or(mask,bmask)
- del bmask
-
- return mask
-
-
-def archive_wcs_file(image, wcs_id=None):
- """
- Update WCSCORR table with rows for each SCI extension to record the
- newly updated WCS keyword values.
- """
-
- if not isinstance(image, fits.HDUList):
- fimg = fits.open(image, mode='update')
- close_image = True
- else:
- fimg = image
- close_image = False
-
- update_wcscorr(fimg, wcs_id=wcs_id)
-
- if close_image:
- fimg.close()
-
-
-def update_wcscorr(dest, source=None, extname='SCI', wcs_id=None, active=True):
- """
- Update WCSCORR table with a new row or rows for this extension header. It
- copies the current set of WCS keywords as a new row of the table based on
- keyed WCSs as per Paper I Multiple WCS standard).
-
- Parameters
- ----------
- dest : HDUList
- The HDU list whose WCSCORR table should be appended to (the WCSCORR HDU
- must already exist)
- source : HDUList, optional
- The HDU list containing the extension from which to extract the WCS
- keywords to add to the WCSCORR table. If None, the dest is also used
- as the source.
- extname : str, optional
- The extension name from which to take new WCS keywords. If there are
- multiple extensions with that name, rows are added for each extension
- version.
- wcs_id : str, optional
- The name of the WCS to add, as in the WCSNAMEa keyword. If
- unspecified, all the WCSs in the specified extensions are added.
- active: bool, optional
- When True, indicates that the update should reflect an update of the
- active WCS information, not just appending the WCS to the file as a
- headerlet
- """
- if not isinstance(dest, fits.HDUList):
- dest = fits.open(dest,mode='update')
- fname = dest.filename()
-
- if source is None:
- source = dest
-
- if extname == 'PRIMARY':
- return
-
- numext = fileutil.countExtn(source, extname)
- if numext == 0:
- raise ValueError('No %s extensions found in the source HDU list.'
- % extname)
- # Initialize the WCSCORR table extension in dest if not already present
- init_wcscorr(dest)
- try:
- dest.index_of('WCSCORR')
- except KeyError:
- return
-
- # check to see whether or not this is an up-to-date table
- # replace with newly initialized table with current format
- old_table = dest['WCSCORR']
- wcscorr_cols = ['WCS_ID','EXTVER', 'SIPNAME',
- 'HDRNAME', 'NPOLNAME', 'D2IMNAME']
-
- for colname in wcscorr_cols:
- if colname not in old_table.data.columns.names:
- print("WARNING: Replacing outdated WCSCORR table...")
- outdated_table = old_table.copy()
- del dest['WCSCORR']
- init_wcscorr(dest)
- old_table = dest['WCSCORR']
- break
-
- # Current implementation assumes the same WCS keywords are in each
- # extension version; if this should not be assumed then this can be
- # modified...
- wcs_keys = altwcs.wcskeys(source[(extname, 1)].header)
- wcs_keys = [kk for kk in wcs_keys if kk]
- if ' ' not in wcs_keys: wcs_keys.append(' ') # Insure that primary WCS gets used
- # apply logic for only updating WCSCORR table with specified keywords
- # corresponding to the WCS with WCSNAME=wcs_id
- if wcs_id is not None:
- wnames = altwcs.wcsnames(source[(extname, 1)].header)
- wkeys = []
- for letter in wnames:
- if wnames[letter] == wcs_id:
- wkeys.append(letter)
- if len(wkeys) > 1 and ' ' in wkeys:
- wkeys.remove(' ')
- wcs_keys = wkeys
- wcshdr = stwcs.wcsutil.HSTWCS(source, ext=(extname, 1)).wcs2header()
- wcs_keywords = list(wcshdr.keys())
-
- if 'O' in wcs_keys:
- wcs_keys.remove('O') # 'O' is reserved for original OPUS WCS
-
- # create new table for hdr and populate it with the newly updated values
- new_table = create_wcscorr(descrip=True,numrows=0, padding=len(wcs_keys)*numext)
- prihdr = source[0].header
-
- # Get headerlet related keywords here
- sipname, idctab = utils.build_sipname(source, fname, "None")
- npolname, npolfile = utils.build_npolname(source, None)
- d2imname, d2imfile = utils.build_d2imname(source, None)
- if 'hdrname' in prihdr:
- hdrname = prihdr['hdrname']
- else:
- hdrname = ''
-
- idx = -1
- for wcs_key in wcs_keys:
- for extver in range(1, numext + 1):
- extn = (extname, extver)
- if 'SIPWCS' in extname and not active:
- tab_extver = 0 # Since it has not been added to the SCI header yet
- else:
- tab_extver = extver
- hdr = source[extn].header
- if 'WCSNAME'+wcs_key in hdr:
- wcsname = hdr['WCSNAME' + wcs_key]
- else:
- wcsname = utils.build_default_wcsname(hdr['idctab'])
-
- selection = {'WCS_ID': wcsname, 'EXTVER': tab_extver,
- 'SIPNAME':sipname, 'HDRNAME': hdrname,
- 'NPOLNAME': npolname, 'D2IMNAME':d2imname
- }
-
- # Ensure that an entry for this WCS is not already in the dest
- # table; if so just skip it
- rowind = find_wcscorr_row(old_table.data, selection)
- if np.any(rowind):
- continue
-
- idx += 1
-
- wcs = stwcs.wcsutil.HSTWCS(source, ext=extn, wcskey=wcs_key)
- wcshdr = wcs.wcs2header()
-
- # Update selection column values
- for key, val in selection.items():
- if key in new_table.data.names:
- new_table.data.field(key)[idx] = val
-
- for key in wcs_keywords:
- if key in new_table.data.names:
- new_table.data.field(key)[idx] = wcshdr[key + wcs_key]
-
- for key in DEFAULT_PRI_KEYS:
- if key in new_table.data.names and key in prihdr:
- new_table.data.field(key)[idx] = prihdr[key]
- # Now look for additional, non-WCS-keyword table column data
- for key in COL_FITSKW_DICT:
- fitkw = COL_FITSKW_DICT[key]
- # Interpret any 'pri.hdrname' or
- # 'sci.crpix1' formatted keyword names
- if '.' in fitkw:
- srchdr,fitkw = fitkw.split('.')
- if 'pri' in srchdr.lower(): srchdr = prihdr
- else: srchdr = source[extn].header
- else:
- srchdr = source[extn].header
-
- if fitkw+wcs_key in srchdr:
- new_table.data.field(key)[idx] = srchdr[fitkw+wcs_key]
-
-
- # If idx was never incremented, no rows were added, so there's nothing else
- # to do...
- if idx < 0:
- return
-
- # Now, we need to merge this into the existing table
- rowind = find_wcscorr_row(old_table.data, {'wcs_id':['','0.0']})
- old_nrows = np.where(rowind)[0][0]
- new_nrows = new_table.data.shape[0]
-
- # check to see if there is room for the new row
- if (old_nrows + new_nrows) > old_table.data.shape[0]-1:
- pad_rows = 2 * new_nrows
- # if not, create a new table with 'pad_rows' new empty rows
- upd_table = fits.new_table(old_table.columns,header=old_table.header,
- nrows=old_table.data.shape[0]+pad_rows)
- else:
- upd_table = old_table
- pad_rows = 0
- # Now, add
- for name in old_table.columns.names:
- if name in new_table.data.names:
- # reset the default values to ones specific to the row definitions
- for i in range(pad_rows):
- upd_table.data.field(name)[old_nrows+i] = old_table.data.field(name)[-1]
- # Now populate with values from new table
- upd_table.data.field(name)[old_nrows:old_nrows + new_nrows] = \
- new_table.data.field(name)
- upd_table.header['TROWS'] = old_nrows + new_nrows
-
- # replace old extension with newly updated table extension
- dest['WCSCORR'] = upd_table
-
-
-def restore_file_from_wcscorr(image, id='OPUS', wcskey=''):
- """ Copies the values of the WCS from the WCSCORR based on ID specified by user.
- The default will be to restore the original OPUS-derived values to the Primary WCS.
- If wcskey is specified, the WCS with that key will be updated instead.
- """
-
- if not isinstance(image, fits.HDUList):
- fimg = fits.open(image, mode='update')
- close_image = True
- else:
- fimg = image
- close_image = False
- numsci = fileutil.countExtn(fimg)
- wcs_table = fimg['WCSCORR']
- orig_rows = (wcs_table.data.field('WCS_ID') == 'OPUS')
- # create an HSTWCS object to figure out what WCS keywords need to be updated
- wcsobj = stwcs.wcsutil.HSTWCS(fimg,ext=('sci',1))
- wcshdr = wcsobj.wcs2header()
- for extn in range(1,numsci+1):
- # find corresponding row from table
- ext_rows = (wcs_table.data.field('EXTVER') == extn)
- erow = np.where(np.logical_and(ext_rows,orig_rows))[0][0]
- for key in wcshdr:
- if key in wcs_table.data.names: # insure that keyword is column in table
- tkey = key
-
- if 'orient' in key.lower():
- key = 'ORIENT'
- if wcskey == '':
- skey = key
- else:
- skey = key[:7]+wcskey
- fimg['sci',extn].header[skey] = wcs_table.data.field(tkey)[erow]
- for key in DEFAULT_PRI_KEYS:
- if key in wcs_table.data.names:
- if wcskey == '':
- pkey = key
- else:
- pkey = key[:7]+wcskey
- fimg[0].header[pkey] = wcs_table.data.field(key)[erow]
-
- utils.updateNEXTENDKw(fimg)
-
- # close the image now that the update has been completed.
- if close_image:
- fimg.close()
-
-
-def create_wcscorr(descrip=False, numrows=1, padding=0):
- """
- Return the basic definitions for a WCSCORR table.
- The dtype definitions for the string columns are set to the maximum allowed so
- that all new elements will have the same max size which will be automatically
- truncated to this limit upon updating (if needed).
-
- The table is initialized with rows corresponding to the OPUS solution
- for all the 'SCI' extensions.
- """
-
- trows = numrows + padding
- # define initialized arrays as placeholders for column data
- # TODO: I'm certain there's an easier way to do this... for example, simply
- # define the column names and formats, then create an empty array using
- # them as a dtype, then create the new table from that array.
- def_float64_zeros = np.array([0.0] * trows, dtype=np.float64)
- def_float64_ones = def_float64_zeros + 1.0
- def_float_col = {'format': 'D', 'array': def_float64_zeros.copy()}
- def_float1_col = {'format': 'D', 'array':def_float64_ones.copy()}
- def_str40_col = {'format': '40A',
- 'array': np.array([''] * trows, dtype='S40')}
- def_str24_col = {'format': '24A',
- 'array': np.array([''] * trows, dtype='S24')}
- def_int32_col = {'format': 'J',
- 'array': np.array([0]*trows,dtype=np.int32)}
-
- # If more columns are needed, simply add their definitions to this list
- col_names = [('HDRNAME', def_str24_col), ('SIPNAME', def_str24_col),
- ('NPOLNAME', def_str24_col), ('D2IMNAME', def_str24_col),
- ('CRVAL1', def_float_col), ('CRVAL2', def_float_col),
- ('CRPIX1', def_float_col), ('CRPIX2', def_float_col),
- ('CD1_1', def_float_col), ('CD1_2', def_float_col),
- ('CD2_1', def_float_col), ('CD2_2', def_float_col),
- ('CTYPE1', def_str24_col), ('CTYPE2', def_str24_col),
- ('ORIENTAT', def_float_col), ('PA_V3', def_float_col),
- ('RMS_RA', def_float_col), ('RMS_Dec', def_float_col),
- ('NMatch', def_int32_col), ('Catalog', def_str40_col)]
-
- # Define selector columns
- id_col = fits.Column(name='WCS_ID', format='40A',
- array=np.array(['OPUS'] * numrows + [''] * padding,
- dtype='S24'))
- extver_col = fits.Column(name='EXTVER', format='I',
- array=np.array(list(range(1, numrows + 1)),
- dtype=np.int16))
- wcskey_col = fits.Column(name='WCS_key', format='A',
- array=np.array(['O'] * numrows + [''] * padding,
- dtype='S'))
- # create list of remaining columns to be added to table
- col_list = [id_col, extver_col, wcskey_col] # start with selector columns
-
- for c in col_names:
- cdef = copy.deepcopy(c[1])
- col_list.append(fits.Column(name=c[0], format=cdef['format'],
- array=cdef['array']))
-
- if descrip:
- col_list.append(
- fits.Column(name='DESCRIP', format='128A',
- array=np.array(
- ['Original WCS computed by OPUS'] * numrows,
- dtype='S128')))
-
- # Now create the new table from the column definitions
- newtab = fits.new_table(fits.ColDefs(col_list), nrows=trows)
- # The fact that setting .name is necessary should be considered a bug in
- # pyfits.
- # TODO: Make sure this is fixed in pyfits, then remove this
- newtab.name = 'WCSCORR'
-
- return newtab
-
-def delete_wcscorr_row(wcstab,selections=None,rows=None):
- """
- Sets all values in a specified row or set of rows to default values
-
- This function will essentially erase the specified row from the table
- without actually removing the row from the table. This avoids the problems
- with trying to resize the number of rows in the table while preserving the
- ability to update the table with new rows again without resizing the table.
-
- Parameters
- ----------
- wcstab: object
- PyFITS binTable object for WCSCORR table
- selections: dict
- Dictionary of wcscorr column names and values to be used to select
- the row or set of rows to erase
- rows: int, list
- If specified, will specify what rows from the table to erase regardless
- of the value of 'selections'
- """
-
- if selections is None and rows is None:
- print('ERROR: Some row selection information must be provided!')
- print(' Either a row numbers or "selections" must be provided.')
- raise ValueError
-
- delete_rows = None
- if rows is None:
- if 'wcs_id' in selections and selections['wcs_id'] == 'OPUS':
- delete_rows = None
- print('WARNING: OPUS WCS information can not be deleted from WCSCORR table.')
- print(' This row will not be deleted!')
- else:
- rowind = find_wcscorr_row(wcstab, selections=selections)
- delete_rows = np.where(rowind)[0].tolist()
- else:
- if not isinstance(rows,list):
- rows = [rows]
- delete_rows = rows
-
- # Insure that rows pointing to OPUS WCS do not get deleted, even by accident
- for row in delete_rows:
- if wcstab['WCS_key'][row] == 'O' or wcstab['WCS_ID'][row] == 'OPUS':
- del delete_rows[delete_rows.index(row)]
-
- if delete_rows is None:
- return
-
- # identify next empty row
- rowind = find_wcscorr_row(wcstab, selections={'wcs_id':['','0.0']})
- last_blank_row = np.where(rowind)[0][-1]
-
- # copy values from blank row into user-specified rows
- for colname in wcstab.names:
- wcstab[colname][delete_rows] = wcstab[colname][last_blank_row]
-
-def update_wcscorr_column(wcstab, column, values, selections=None, rows=None):
- """
- Update the values in 'column' with 'values' for selected rows
-
- Parameters
- ----------
- wcstab: object
- PyFITS binTable object for WCSCORR table
- column: string
- Name of table column with values that need to be updated
- values: string, int, or list
- Value or set of values to copy into the selected rows for the column
- selections: dict
- Dictionary of wcscorr column names and values to be used to select
- the row or set of rows to erase
- rows: int, list
- If specified, will specify what rows from the table to erase regardless
- of the value of 'selections'
- """
- if selections is None and rows is None:
- print('ERROR: Some row selection information must be provided!')
- print(' Either a row numbers or "selections" must be provided.')
- raise ValueError
-
- if not isinstance(values, list):
- values = [values]
-
- update_rows = None
- if rows is None:
- if 'wcs_id' in selections and selections['wcs_id'] == 'OPUS':
- update_rows = None
- print('WARNING: OPUS WCS information can not be deleted from WCSCORR table.')
- print(' This row will not be deleted!')
- else:
- rowind = find_wcscorr_row(wcstab, selections=selections)
- update_rows = np.where(rowind)[0].tolist()
- else:
- if not isinstance(rows,list):
- rows = [rows]
- update_rows = rows
-
- if update_rows is None:
- return
-
- # Expand single input value to apply to all selected rows
- if len(values) > 1 and len(values) < len(update_rows):
- print('ERROR: Number of new values',len(values))
- print(' does not match number of rows',len(update_rows),' to be updated!')
- print(' Please enter either 1 value or the same number of values')
- print(' as there are rows to be updated.')
- print(' Table will not be updated...')
- raise ValueError
-
- if len(values) == 1 and len(values) < len(update_rows):
- values = values * len(update_rows)
- # copy values from blank row into user-specified rows
- for row in update_rows:
- wcstab[column][row] = values[row]
diff --git a/lib/stwcs/wcsutil/wcsdiff.py b/lib/stwcs/wcsutil/wcsdiff.py
deleted file mode 100644
index cfc2d66..0000000
--- a/lib/stwcs/wcsutil/wcsdiff.py
+++ /dev/null
@@ -1,150 +0,0 @@
-from __future__ import print_function
-from astropy import wcs as pywcs
-from collections import OrderedDict
-from astropy.io import fits
-from .headerlet import parse_filename
-import numpy as np
-
-def is_wcs_identical(scifile, file2, sciextlist, fextlist, scikey=" ",
- file2key=" ", verbose=False):
- """
- Compares the WCS solution of 2 files.
-
- Parameters
- ----------
- scifile: string
- name of file1 (usually science file)
- IRAF style extension syntax is accepted as well
- for example scifile[1] or scifile[sci,1]
- file2: string
- name of second file (for example headerlet)
- sciextlist - list
- a list of int or tuple ('SCI', 1), extensions in the first file
- fextlist - list
- a list of int or tuple ('SIPWCS', 1), extensions in the second file
- scikey: string
- alternate WCS key in scifile
- file2key: string
- alternate WCS key in file2
- verbose: boolean
- True: print to stdout
-
- Notes
- -----
- These can be 2 science observations or 2 headerlets
- or a science observation and a headerlet. The two files
- have the same WCS solution if the following are the same:
-
- - rootname/destim
- - primary WCS
- - SIP coefficients
- - NPOL distortion
- - D2IM correction
-
- """
- result = True
- diff = OrderedDict()
- fobj, fname, close_file = parse_filename(file2)
- sciobj, sciname, close_scifile = parse_filename(scifile)
- diff['file_names'] = [scifile, file2]
- if get_rootname(scifile) != get_rootname(file2):
- #logger.info('Rootnames do not match.')
- diff['rootname'] = ("%s: %s", "%s: %s") % (sciname, get_rootname(scifile), file2, get_rootname(file2))
- result = False
- for i, j in zip(sciextlist, fextlist):
- w1 = pywcs.WCS(sciobj[i].header, sciobj, key=scikey)
- w2 = pywcs.WCS(fobj[j].header, fobj, key=file2key)
- diff['extension'] = [get_extname_extnum(sciobj[i]), get_extname_extnum(fobj[j])]
- if not np.allclose(w1.wcs.crval, w2.wcs.crval, rtol=10**(-7)):
- #logger.info('CRVALs do not match')
- diff['CRVAL'] = w1.wcs.crval, w2.wcs.crval
- result = False
- if not np.allclose(w1.wcs.crpix, w2.wcs.crpix, rtol=10**(-7)):
- #logger.info('CRPIX do not match')
- diff ['CRPIX'] = w1.wcs.crpix, w2.wcs.crpix
- result = False
- if not np.allclose(w1.wcs.cd, w2.wcs.cd, rtol=10**(-7)):
- #logger.info('CDs do not match')
- diff ['CD'] = w1.wcs.cd, w2.wcs.cd
- result = False
- if not (np.array(w1.wcs.ctype) == np.array(w2.wcs.ctype)).all():
- #logger.info('CTYPEs do not match')
- diff ['CTYPE'] = w1.wcs.ctype, w2.wcs.ctype
- result = False
- if w1.sip or w2.sip:
- if (w2.sip and not w1.sip) or (w1.sip and not w2.sip):
- diff['sip'] = 'one sip extension is missing'
- result = False
- if not np.allclose(w1.sip.a, w2.sip.a, rtol=10**(-7)):
- diff['SIP_A'] = 'SIP_A differ'
- result = False
- if not np.allclose(w1.sip.b, w2.sip.b, rtol=10**(-7)):
- #logger.info('SIP coefficients do not match')
- diff ['SIP_B'] = (w1.sip.b, w2.sip.b)
- result = False
- if w1.cpdis1 or w2.cpdis1:
- if w1.cpdis1 and not w2.cpdis1 or w2.cpdis1 and not w1.cpdis1:
- diff['CPDIS1'] = "CPDIS1 missing"
- result=False
- if w1.cpdis2 and not w2.cpdis2 or w2.cpdis2 and not w1.cpdis2:
- diff['CPDIS2'] = "CPDIS2 missing"
- result = False
- if not np.allclose(w1.cpdis1.data, w2.cpdis1.data, rtol=10**(-7)):
- #logger.info('NPOL distortions do not match')
- diff ['CPDIS1_data'] = (w1.cpdis1.data, w2.cpdis1.data)
- result = False
- if not np.allclose(w1.cpdis2.data, w2.cpdis2.data, rtol=10**(-7)):
- #logger.info('NPOL distortions do not match')
- diff ['CPDIS2_data'] = (w1.cpdis2.data, w2.cpdis2.data)
- result = False
- if w1.det2im1 or w2.det2im1:
- if w1.det2im1 and not w2.det2im1 or \
- w2.det2im1 and not w1.det2im1:
- diff['DET2IM'] = "Det2im1 missing"
- result = False
- if not np.allclose(w1.det2im1.data, w2.det2im1.data, rtol=10**(-7)):
- #logger.info('Det2Im corrections do not match')
- diff ['D2IM1_data'] = (w1.det2im1.data, w2.det2im1.data)
- result = False
- if w1.det2im2 or w2.det2im2:
- if w1.det2im2 and not w2.det2im2 or \
- w2.det2im2 and not w1.det2im2:
- diff['DET2IM2'] = "Det2im2 missing"
- result = False
- if not np.allclose(w1.det2im2.data, w2.det2im2.data, rtol=10**(-7)):
- #logger.info('Det2Im corrections do not match')
- diff ['D2IM2_data'] = (w1.det2im2.data, w2.det2im2.data)
- result = False
- if not result and verbose:
- for key in diff:
- print(key, ":\t", diff[key][0], "\t", diff[key][1])
- if close_file:
- fobj.close()
- if close_scifile:
- sciobj.close()
- return result, diff
-
-def get_rootname(fname):
- """
- Returns the value of ROOTNAME or DESTIM
- """
-
- hdr = fits.getheader(fname)
- try:
- rootname = hdr['ROOTNAME']
- except KeyError:
- try:
- rootname = hdr['DESTIM']
- except KeyError:
- rootname = fname
- return rootname
-
-def get_extname_extnum(ext):
- """
- Return (EXTNAME, EXTNUM) of a FITS extension
- """
- extname = ""
- extnum=1
- extname = ext.header.get('EXTNAME', extname)
- extnum = ext.header.get('EXTVER', extnum)
- return (extname, extnum)