def apply_wfc_tdd_coeffs(cx,cy,alpha,beta): ''' Apply the WFC TDD coefficients directly to the distortion coefficients. ''' # Initialize variables to be used theta_v2v3 = 2.234529 scale_idc = 0.05 scale_jay = 0.04973324715 idctheta = theta_v2v3 idcrad = fileutil.DEGTORAD(idctheta) #idctheta = fileutil.RADTODEG(N.arctan2(cx[1,0],cy[1,0])) # Pre-compute the entire correction prior to applying it to the coeffs mrotp = fileutil.buildRotMatrix(idctheta) mrotn = fileutil.buildRotMatrix(-idctheta) abmat = np.array([[beta,alpha],[alpha,beta]]) 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) icxy = np.dot(abmat2,[cx.ravel(),cy.ravel()]) icx = icxy[0] icy = icxy[1] icx.shape = cx.shape icy.shape = cy.shape return icx,icy
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("TDD Zero point correction for chip" "{0} defaulted to: {1}".format( 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("TDD Zero point correction for chip {0}: {1}".format( hwcs.chip, v23_corr)) return v23_corr
def wtraxy(self,pixpos,wcs,verbose=False): """ Converts input pixel position 'pixpos' into an X,Y position in WCS. Made this function compatible with list input, as well as single tuple input..apply """ # Insure that input wcs is centered for proper results # Added 1-Dec-2004. wcs.recenter() _ab,_cd = drutil.wcsfit(self,wcs) _orient = fileutil.RADTODEG(np.arctan2(_ab[1],_cd[0])) _scale = wcs.pscale/self.wcslin.pscale _xoff = _ab[2] _yoff = _cd[2] # changed from self.wcs.naxis[1/2] _naxis = (wcs.naxis1,wcs.naxis2) _rot_mat = fileutil.buildRotMatrix(_orient) if isinstance(pixpos, tuple): pixpos = [pixpos] _delta_x,_delta_y = self.apply(pixpos) if verbose: print('Raw corrected position: ',_delta_x,_delta_y) _delta_x += self.model.refpix['XDELTA'] _delta_y += self.model.refpix['YDELTA'] if verbose: print('Fully corrected position: ',_delta_x,_delta_y) _delta = np.zeros((len(pixpos),2),dtype=np.float32) _delta[:,0] = _delta_x _delta[:,1] = _delta_y # Need to 0.5 offset to xp,yp to compute the offset in the same way that # 'drizzle' computes it. #_xp = _naxis[0]/2. + 0.5 #_yp = _naxis[1]/2. + 0.5 _xp = _naxis[0]/2. _yp = _naxis[1]/2. _xt = _xoff + _xp _yt = _yoff + _yp if verbose: print('XSH,YSH: ',_xoff,_yoff) print('XDELTA,YDELTA: ',self.model.refpix['XDELTA'],self.model.refpix['YDELTA']) print('XREF,YREF: ',self.model.refpix['XREF'],self.model.refpix['YREF']) print('xt,yt: ',_xt,_yt,' based on xp,yp: ',_xp,_yp) _xy_corr = np.dot(_delta,_rot_mat) / _scale _delta[:,0] = _xy_corr[:,0] + _xt _delta[:,1] = _xy_corr[:,1] + _yt if len(pixpos) == 1: return _delta[0] else: return _delta
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
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
def getRotatedSize(corners, angle): """ Determine the size of a rotated (meta)image.""" if angle: _rotm = fileutil.buildRotMatrix(angle) # Rotate about the center _corners = np.dot(corners, _rotm) else: # If there is no rotation, simply return original values _corners = corners return computeRange(_corners)
def rotate_coeffs(cx,cy,rot,scale=1.0): ''' Rotate poly coeffs by 'rot' degrees. ''' mrot = fileutil.buildRotMatrix(rot)*scale rcxy = np.dot(mrot,[cx.ravel(),cy.ravel()]) rcx = rcxy[0] rcy = rcxy[1] rcx.shape = cx.shape rcy.shape = cy.shape return rcx,rcy
def rotatePos(pos, theta,offset=None,scale=None): if scale == None: scale = 1. if offset == None: offset = np.array([0.,0.],dtype=np.float64) mrot = buildRotMatrix(theta) xr = ((pos[0] * mrot[0][0]) + (pos[1]*mrot[0][1]) )/ scale + offset[0] yr = ((pos[0] * mrot[1][0]) + (pos[1]*mrot[1][1]) )/ scale + offset[1] return xr,yr
def update_linCD(cdmat, delta_rot=0.0, delta_scale=1.0, cx=[0.0, 1.0], cy=[1.0, 0.0]): """ Modify an existing linear CD matrix with rotation and/or scale changes and return a new CD matrix. If 'cx' and 'cy' are specified, it will return a distorted CD matrix. Only those terms which are varying need to be specified on input. """ rotmat = fileutil.buildRotMatrix(delta_rot) * delta_scale new_lincd = np.dot(cdmat, rotmat) cxymat = np.array([[cx[1], cx[0]], [cy[1], cy[0]]]) new_cd = np.dot(new_lincd, cxymat) return new_cd
def update_linCD(cdmat, delta_rot=0.0, delta_scale=1.0, cx=[0.0,1.0], cy=[1.0,0.0]): """ Modify an existing linear CD matrix with rotation and/or scale changes and return a new CD matrix. If 'cx' and 'cy' are specified, it will return a distorted CD matrix. Only those terms which are varying need to be specified on input. """ rotmat = fileutil.buildRotMatrix(delta_rot)*delta_scale new_lincd = np.dot(cdmat,rotmat) cxymat = np.array([[cx[1],cx[0]],[cy[1],cy[0]]]) new_cd = np.dot(new_lincd,cxymat) return new_cd
def getRotatedSize(corners,angle): """ Determine the size of a rotated (meta)image.""" # If there is no rotation, simply return original values if angle == 0.: _corners = corners else: # Find center #_xr,_yr = computeRange(corners) #_cen = ( ((_xr[1] - _xr[0])/2.)+_xr[0],((_yr[1]-_yr[0])/2.)+_yr[0]) _rotm = buildRotMatrix(angle) # Rotate about the center #_corners = np.dot(corners - _cen,_rotm) _corners = np.dot(corners,_rotm) return computeRange(_corners)
def getRotatedSize(corners, angle): """ Determine the size of a rotated (meta)image.""" # If there is no rotation, simply return original values if angle == 0.: _corners = corners else: # Find center #_xr,_yr = computeRange(corners) #_cen = ( ((_xr[1] - _xr[0])/2.)+_xr[0],((_yr[1]-_yr[0])/2.)+_yr[0]) _rotm = fileutil.buildRotMatrix(angle) # Rotate about the center #_corners = N.dot(corners - _cen,_rotm) _corners = np.dot(corners, _rotm) return computeRange(_corners)
def create_CD(orient, scale, cx=None, cy=None): """ Create a (un?)distorted CD matrix from the basic inputs. The 'cx' and 'cy' parameters, if given, provide the X and Y coefficients of the distortion as returned by reading the IDCTAB. Only the first 2 elements are used and should correspond to the 'OC[X/Y]10' and 'OC[X/Y]11' terms in that order as read from the expanded SIP headers. The units of 'scale' should be 'arcseconds/pixel' of the reference pixel. The value of 'orient' should be the absolute orientation on the sky of the reference pixel. """ cxymat = np.array([[cx[1], cx[0]], [cy[1], cy[0]]]) rotmat = fileutil.buildRotMatrix(orient) * scale/3600. new_cd = np.dot(rotmat, cxymat) return new_cd
def create_CD(orient, scale, cx=None, cy=None): """ Create a (un?)distorted CD matrix from the basic inputs. The 'cx' and 'cy' parameters, if given, provide the X and Y coefficients of the distortion as returned by reading the IDCTAB. Only the first 2 elements are used and should correspond to the 'OC[X/Y]10' and 'OC[X/Y]11' terms in that order as read from the expanded SIP headers. The units of 'scale' should be 'arcseconds/pixel' of the reference pixel. The value of 'orient' should be the absolute orientation on the sky of the reference pixel. """ cxymat = np.array([[cx[1],cx[0]],[cy[1],cy[0]]]) rotmat = fileutil.buildRotMatrix(orient)*scale/3600. new_cd = np.dot(rotmat,cxymat) return new_cd
def mergewcs(outwcs, customwcs, wcspars): """ Merge the WCS keywords from user specified values into a full HSTWCS object This function will essentially follow the same algorithm as used by updatehdr only it will use direct calls to updatewcs.Makewcs methods instead of using 'updatewcs' as a whole """ # start by working on a copy of the refwcs if outwcs.sip is not None: wcslin = stwcs.distortion.utils.undistortWCS(outwcs) outwcs.wcs.cd = wcslin.wcs.cd outwcs.wcs.set() outwcs.setOrient() outwcs.setPscale() else: wcslin = outwcs if customwcs is None: # update valid pars from wcspars if wcspars['crval1'] is not None: outwcs.wcs.crval = np.array([wcspars['crval1'], wcspars['crval2']]) if wcspars['crpix1'] is not None: outwcs.wcs.crpix = np.array([wcspars['crpix1'], wcspars['crpix2']]) if wcspars['naxis1'] is not None: outwcs._naxis1 = wcspars['naxis1'] outwcs._naxis2 = wcspars['naxis2'] outwcs.wcs.crpix = np.array( [outwcs._naxis1 / 2., outwcs._naxis2 / 2.]) pscale = wcspars['pscale'] orient = wcspars['orientat'] if pscale is not None or orient is not None: if pscale is None: pscale = wcslin.pscale if orient is None: orient = wcslin.orientat pix_ratio = pscale / wcslin.pscale delta_rot = wcslin.orientat - orient delta_rot_mat = fileutil.buildRotMatrix(delta_rot) outwcs.wcs.cd = np.dot(outwcs.wcs.cd, delta_rot_mat) * pix_ratio # apply model to new linear CD matrix apply_model(outwcs) else: # A new fully described WCS was provided in customwcs outwcs.wcs.cd = customwcs.wcs.cd outwcs.wcs.crval = customwcs.wcs.crval outwcs.wcs.crpix = customwcs.wcs.crpix outwcs._naxis1 = customwcs._naxis1 outwcs._naxis2 = customwcs._naxis2 return outwcs
def build_hstwcs(crval1, crval2, crpix1, crpix2, naxis1, naxis2, pscale, orientat): """ Create an HSTWCS object for a default instrument without distortion based on user provided parameter values. """ wcsout = wcsutil.HSTWCS() wcsout.wcs.crval = np.array([crval1, crval2]) wcsout.wcs.crpix = np.array([crpix1, crpix2]) wcsout.naxis1 = naxis1 wcsout.naxis2 = naxis2 wcsout.wcs.cd = fileutil.buildRotMatrix(orientat) * [-1, 1] * pscale / 3600.0 # Synchronize updates with astropy.wcs/WCSLIB objects wcsout.wcs.set() wcsout.setPscale() wcsout.setOrient() wcsout.wcs.ctype = ['RA---TAN', 'DEC--TAN'] return wcsout
def build_hstwcs(crval, crpix, naxis1, naxis2, pscale, orientat): """ Create an `~stwcs.wcsutil.HSTWCS` object for a default instrument without distortion based on user provided parameter values. """ wcsout = wcsutil.HSTWCS() wcsout.wcs.crval = crval.copy() wcsout.wcs.crpix = crpix.copy() wcsout.naxis1 = naxis1 wcsout.naxis2 = naxis2 wcsout.wcs.cd = fu.buildRotMatrix(orientat) * [-1, 1] * pscale / 3600.0 # Synchronize updates with astropy.wcs objects wcsout.wcs.set() wcsout.setPscale() wcsout.setOrient() wcsout.wcs.ctype = ['RA---TAN', 'DEC--TAN'] return wcsout
def build_hstwcs(crval1, crval2, crpix1, crpix2, naxis1, naxis2, pscale, orientat): """ Create an HSTWCS object for a default instrument without distortion based on user provided parameter values. """ wcsout = wcsutil.HSTWCS() wcsout.wcs.crval = np.array([crval1,crval2]) wcsout.wcs.crpix = np.array([crpix1,crpix2]) wcsout.naxis1 = naxis1 wcsout.naxis2 = naxis2 wcsout.wcs.cd = fileutil.buildRotMatrix(orientat)*[-1,1]*pscale/3600.0 # Synchronize updates with PyWCS/WCSLIB objects wcsout.wcs.set() wcsout.setPscale() wcsout.setOrient() wcsout.wcs.ctype = ['RA---TAN','DEC--TAN'] return wcsout
def mergewcs(outwcs, customwcs, wcspars): """ Merge the WCS keywords from user specified values into a full HSTWCS object This function will essentially follow the same algorithm as used by updatehdr only it will use direct calls to updatewcs.Makewcs methods instead of using 'updatewcs' as a whole """ # start by working on a copy of the refwcs if outwcs.sip is not None: wcslin = stwcs.distortion.utils.undistortWCS(outwcs) outwcs.wcs.cd = wcslin.wcs.cd outwcs.wcs.set() outwcs.setOrient() outwcs.setPscale() else: wcslin = outwcs if customwcs is None: # update valid pars from wcspars if wcspars['crval1'] is not None: outwcs.wcs.crval = np.array([wcspars['crval1'],wcspars['crval2']]) if wcspars['crpix1'] is not None: outwcs.wcs.crpix = np.array([wcspars['crpix1'],wcspars['crpix2']]) if wcspars['naxis1'] is not None: outwcs._naxis1 = wcspars['naxis1'] outwcs._naxis2 = wcspars['naxis2'] outwcs.wcs.crpix = np.array([outwcs._naxis1/2.,outwcs._naxis2/2.]) pscale = wcspars['pscale'] orient = wcspars['orientat'] if pscale is not None or orient is not None: if pscale is None: pscale = wcslin.pscale if orient is None: orient = wcslin.orientat pix_ratio = pscale/wcslin.pscale delta_rot = wcslin.orientat - orient delta_rot_mat = fileutil.buildRotMatrix(delta_rot) outwcs.wcs.cd = np.dot(outwcs.wcs.cd,delta_rot_mat)*pix_ratio # apply model to new linear CD matrix apply_model(outwcs) else: # A new fully described WCS was provided in customwcs outwcs.wcs.cd = customwcs.wcs.cd outwcs.wcs.crval = customwcs.wcs.crval outwcs.wcs.crpix = customwcs.wcs.crpix outwcs._naxis1 = customwcs._naxis1 outwcs._naxis2 = customwcs._naxis2 return outwcs
def fitlin_rscale(xy, uv, verbose=False): """ Performs a linear, orthogonal fit between matched lists of positions 'xy' (input) and 'uv' (output). Output: (same as for fit_arrays_general) """ mu = uv[:, 0].mean() mv = uv[:, 1].mean() mx = xy[:, 0].mean() my = xy[:, 1].mean() u = uv[:, 0] - mu v = uv[:, 1] - mv x = xy[:, 0] - mx y = xy[:, 1] - my Sxx = np.dot(x, x) Syy = np.dot(y, y) Sux = np.dot(u, x) Suy = np.dot(u, y) Svx = np.dot(v, x) Svy = np.dot(v, y) # implement parity check if (np.dot(Sux, Svy) > 0): p = 1 else: p = -1 XX = p * Sux + Svy YY = Suy - p * Svx # derive output values theta_deg = fileutil.RADTODEG(np.arctan2(YY, XX)) % 360.0 scale = np.sqrt(XX**2 + YY**2) / (Sxx + Syy) shift = (mu - mx, mv - my) if verbose: print('Linear RSCALE fit: rotation = ', theta_deg, ' scale = ', scale, ' offset = ', shift) coeffs = scale * fileutil.buildRotMatrix(-theta_deg) P = [coeffs[0, 0], coeffs[0, 1], shift[0]] Q = [coeffs[1, 1], coeffs[1, 0], shift[1]] return P, Q
def fitlin_rscale(xy,uv,verbose=False): """ Performs a linear, orthogonal fit between matched lists of positions 'xy' (input) and 'uv' (output). Output: (same as for fit_arrays_general) """ mu = uv[:,0].mean() mv = uv[:,1].mean() mx = xy[:,0].mean() my = xy[:,1].mean() u = uv[:,0] - mu v = uv[:,1] - mv x = xy[:,0] - mx y = xy[:,1] - my Sxx = np.dot(x,x) Syy = np.dot(y,y) Sux = np.dot(u,x) Suy = np.dot(u,y) Svx = np.dot(v,x) Svy = np.dot(v,y) # implement parity check if (np.dot(Sux,Svy) > 0): p = 1 else: p = -1 XX = p*Sux + Svy YY = Suy - p*Svx # derive output values theta_deg = np.rad2deg(np.arctan2(YY,XX))% 360.0 scale = np.sqrt(XX**2 + YY**2) / (Sxx+Syy) shift = (mu-mx,mv-my) if verbose: print('Linear RSCALE fit: rotation = ',theta_deg,' scale = ',scale,' offset = ',shift) coeffs = scale * fileutil.buildRotMatrix(-theta_deg) P = [coeffs[0,0],coeffs[0,1],shift[0]] Q = [coeffs[1,1],coeffs[1,0],shift[1]] return P,Q
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("TDD Zero point correction for chip" "{0} defaulted to: {1}".format(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("TDD Zero point correction for chip {0}: {1}".format(hwcs.chip, v23_corr)) return v23_corr
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()
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()
def _update(image,idctab,nimsets,apply_tdd=False, quiet=None,instrument=None,prepend=None,nrchip=None, nrext=None): tdd_xyref = {1: [2048, 3072], 2:[2048, 1024]} _prepend = prepend _dqname = None # Make a copy of the header for keyword access # This copy includes both Primary header and # extension header hdr = fileutil.getHeader(image) # Try to get the instrument if we don't have it already instrument = readKeyword(hdr,'INSTRUME') binned = 1 # Read in any specified OFFTAB, if present (WFPC2) offtab = readKeyword(hdr,'OFFTAB') dateobs = readKeyword(hdr,'DATE-OBS') if not quiet: print("OFFTAB, DATE-OBS: ",offtab,dateobs) print("-Updating image ",image) if not quiet: print("-Reading IDCTAB file ",idctab) # Get telescope orientation from image header # If PA_V# is not present of header, try to get it from the spt file pvt = readKeyword(hdr,'PA_V3') if pvt == None: sptfile = fileutil.buildNewRootname(image, extn='_spt.fits') if os.path.exists(sptfile): spthdr = fileutil.getHeader(sptfile) pvt = readKeyword(spthdr,'PA_V3') if pvt != None: pvt = float(pvt) else: print('PA_V3 keyword not found, WCS cannot be updated. Quitting ...') raise ValueError # Find out about instrument, detector & filters detector = readKeyword(hdr,'DETECTOR') Nrefchip=1 if instrument == 'WFPC2': filter1 = readKeyword(hdr,'FILTNAM1') filter2 = readKeyword(hdr,'FILTNAM2') mode = readKeyword(hdr,'MODE') if os.path.exists(fileutil.buildNewRootname(image, extn='_c1h.fits')): _dqname = fileutil.buildNewRootname(image, extn='_c1h.fits') dqhdr = pyfits.getheader(_dqname,1) dqext = readKeyword(dqhdr, 'EXTNAME') if mode == 'AREA': binned = 2 Nrefchip=nrchip elif instrument == 'NICMOS': filter1 = readKeyword(hdr,'FILTER') filter2 = None elif instrument == 'WFC3': filter1 = readKeyword(hdr,'FILTER') filter2 = None # use value of 'BINAXIS' keyword to set binning value for WFC3 data binned = readKeyword(hdr,'BINAXIS1') else: filter1 = readKeyword(hdr,'FILTER1') filter2 = readKeyword(hdr,'FILTER2') if filter1 == None or filter1.strip() == '': filter1 = 'CLEAR' else: filter1 = filter1.strip() if filter2 == None or filter2.strip() == '': filter2 = 'CLEAR' else: filter2 = filter2.strip() if filter1.find('CLEAR') == 0: filter1 = 'CLEAR' if filter2.find('CLEAR') == 0: filter2 = 'CLEAR' # Set up parity matrix for chip if instrument == 'WFPC2' or instrument =='STIS' or instrument == 'NICMOS': parity = PARITY[instrument] elif detector in PARITY: parity = PARITY[detector] else: raise ValueError('Detector ',detector, ' Not supported at this time. Exiting...') # Get the VAFACTOR keyword if it exists, otherwise set to 1.0 # we also need the reference pointing position of the target # as this is where _va_key = readKeyword(hdr,'VAFACTOR') if _va_key != None: VA_fac = float(_va_key) else: VA_fac=1.0 if not quiet: print('VA factor: ',VA_fac) #ra_targ = float(readKeyword(hdr,'RA_TARG')) #dec_targ = float(readKeyword(hdr,'DEC_TARG')) # Get the chip number _c = readKeyword(hdr,'CAMERA') _s = readKeyword(hdr,'CCDCHIP') _d = readKeyword(hdr,'DETECTOR') if _c != None and str(_c).isdigit(): chip = int(_c) elif _s == None and _d == None: chip = 1 else: if _s: chip = int(_s) elif str(_d).isdigit(): chip = int(_d) else: chip = 1 # For the ACS/WFC case the chip number doesn't match the image # extension nr = 1 if (instrument == 'ACS' and detector == 'WFC') or (instrument == 'WFC3' and detector == 'UVIS'): if nimsets > 1: Nrefchip = 2 else: Nrefchip = chip elif instrument == 'NICMOS': Nrefchip = readKeyword(hdr,'CAMERA') elif instrument == 'WFPC2': nr = nrext else: if nimsets > 1: nr = Nrefchip if not quiet: print("-PA_V3 : ",pvt," CHIP #",chip) # Extract the appropriate information from the IDCTAB #fx,fy,refpix,order=fileutil.readIDCtab(idctab,chip=chip,direction='forward', # filter1=filter1,filter2=filter2,offtab=offtab,date=dateobs) idcmodel = models.IDCModel(idctab, chip=chip, direction='forward', date=dateobs, filter1=filter1, filter2=filter2, offtab=offtab, binned=binned, tddcorr=apply_tdd) fx = idcmodel.cx fy = idcmodel.cy refpix = idcmodel.refpix order = idcmodel.norder # Determine whether to perform time-dependent correction # Construct matrices neded to correct the zero points for TDD if apply_tdd: #alpha,beta = mutil.compute_wfc_tdd_coeffs(dateobs,skew_coeffs) alpha = refpix['TDDALPHA'] beta = refpix['TDDBETA'] tdd = N.array([[beta, alpha], [alpha, -beta]]) mrotp = fileutil.buildRotMatrix(2.234529)/2048. else: alpha = 0.0 beta = 0.0 # Get the original image WCS Old=wcsutil.WCSObject(image,prefix=_prepend) # Reset the WCS keywords to original archived values. Old.restore() # # Look for any subarray offset # ltv1,ltv2 = drutil.getLTVOffsets(image) # # If reference point is not centered on distortion model # shift coefficients to be applied relative to observation # reference position # offsetx = Old.crpix1 - ltv1 - refpix['XREF'] offsety = Old.crpix2 - ltv2 - refpix['YREF'] shiftx = refpix['XREF'] + ltv1 shifty = 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. if ltv1 != 0. or ltv2 != 0.: fx,fy = idcmodel.shift(idcmodel.cx,idcmodel.cy,offsetx,offsety) # Extract the appropriate information for reference chip ridcmodel = models.IDCModel(idctab, chip=Nrefchip, direction='forward', date=dateobs, filter1=filter1, filter2=filter2, offtab=offtab, binned=binned, tddcorr=apply_tdd) rfx = ridcmodel.cx rfy = ridcmodel.cy rrefpix = ridcmodel.refpix rorder = ridcmodel.norder """ rfx,rfy,rrefpix,rorder=mutil.readIDCtab(idctab,chip=Nrefchip, direction='forward', filter1=filter1,filter2=filter2,offtab=offtab, date=dateobs,tddcorr=apply_tdd) """ # Create the reference image name rimage = image.split('[')[0]+"[sci,%d]" % nr if not quiet: print("Reference image: ",rimage) # Create the tangent plane WCS on which the images are defined # This is close to that of the reference chip R=wcsutil.WCSObject(rimage) R.write_archive(rimage) R.restore() # Reacd in declination of target (for computing orientation at aperture) # Note that this is from the reference image #dec = float(fileutil.getKeyword(rimage,'CRVAL2')) #crval1 = float(fileutil.getKeyword(rimage,'CRVAL1')) #crval1 = float(R.crval1) #crval2 = dec dec = float(R.crval2) # Get an approximate reference position on the sky rref = (rrefpix['XREF']+ltvoffx, rrefpix['YREF']+ltvoffy) crval1,crval2=R.xy2rd(rref) if apply_tdd: # Correct zero points for TDD tddscale = (R.pscale/fx[1][1]) rxy0 = N.array([[tdd_xyref[Nrefchip][0]-2048.],[ tdd_xyref[Nrefchip][1]-2048.]]) xy0 = N.array([[tdd_xyref[chip][0]-2048.], [tdd_xyref[chip][1]-2048.]]) rv23_corr = N.dot(mrotp,N.dot(tdd,rxy0))*tddscale v23_corr = N.dot(mrotp,N.dot(tdd,xy0))*tddscale else: rv23_corr = N.array([[0],[0]]) v23_corr = N.array([[0],[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 v2ref = rrefpix['V2REF'] + rv23_corr[0][0]*0.05 v3ref = rrefpix['V3REF'] - rv23_corr[1][0]*0.05 v2 = refpix['V2REF'] + v23_corr[0][0]*0.05 v3 = refpix['V3REF'] - v23_corr[1][0] *0.05 pv = wcsutil.troll(pvt,dec,v2ref,v3ref) # Add the chip rotation angle if rrefpix['THETA']: pv += rrefpix['THETA'] # Set values for the rest of the reference WCS R.crval1=crval1 R.crval2=crval2 R.crpix1=0.0 + offshiftx R.crpix2=0.0 + offshifty R_scale=rrefpix['PSCALE']/3600.0 R.cd11=parity[0][0] * cos(pv*pi/180.0)*R_scale R.cd12=parity[0][0] * -sin(pv*pi/180.0)*R_scale R.cd21=parity[1][1] * sin(pv*pi/180.0)*R_scale R.cd22=parity[1][1] * cos(pv*pi/180.0)*R_scale ##print R R_cdmat = N.array([[R.cd11,R.cd12],[R.cd21,R.cd22]]) if not quiet: print(" Reference Chip Scale (arcsec/pix): ",rrefpix['PSCALE']) # Offset and angle in V2/V3 from reference chip to # new chip(s) - converted to reference image pixels off = sqrt((v2-v2ref)**2 + (v3-v3ref)**2)/(R_scale*3600.0) # Here we must include the PARITY if v3 == v3ref: theta=0.0 else: theta = atan2(parity[0][0]*(v2-v2ref),parity[1][1]*(v3-v3ref)) if rrefpix['THETA']: theta += rrefpix['THETA']*pi/180.0 dX=(off*sin(theta)) + offshiftx dY=(off*cos(theta)) + offshifty # Check to see whether we are working with GEIS or FITS input _fname,_iextn = fileutil.parseFilename(image) if _fname.find('.fits') < 0: # Input image is NOT a FITS file, so # build a FITS name for it's copy. _fitsname = fileutil.buildFITSName(_fname) else: _fitsname = None # Create a new instance of a WCS if _fitsname == None: _new_name = image else: _new_name = _fitsname+'['+str(_iextn)+']' #New=wcsutil.WCSObject(_new_name,new=yes) New = Old.copy() # Calculate new CRVALs and CRPIXs New.crval1,New.crval2=R.xy2rd((dX,dY)) New.crpix1=refpix['XREF'] + ltvoffx New.crpix2=refpix['YREF'] + ltvoffy # Account for subarray offset # Angle of chip relative to chip if refpix['THETA']: dtheta = refpix['THETA'] - rrefpix['THETA'] else: dtheta = 0.0 # Create a small vector, in reference image pixel scale # There is no parity effect here ??? delXX=fx[1,1]/R_scale/3600. delYX=fy[1,1]/R_scale/3600. delXY=fx[1,0]/R_scale/3600. delYY=fy[1,0]/R_scale/3600. # Convert to radians rr=dtheta*pi/180.0 # Rotate the vectors dXX= cos(rr)*delXX - sin(rr)*delYX dYX= sin(rr)*delXX + cos(rr)*delYX dXY= cos(rr)*delXY - sin(rr)*delYY dYY= sin(rr)*delXY + cos(rr)*delYY # Transform to sky coordinates a,b=R.xy2rd((dX+dXX,dY+dYX)) c,d=R.xy2rd((dX+dXY,dY+dYY)) # Calculate the new CDs and convert to degrees New.cd11=diff_angles(a,New.crval1)*cos(New.crval2*pi/180.0) New.cd12=diff_angles(c,New.crval1)*cos(New.crval2*pi/180.0) New.cd21=diff_angles(b,New.crval2) New.cd22=diff_angles(d,New.crval2) # Apply the velocity aberration effect if applicable if VA_fac != 1.0: # First shift the CRVALs apart # New.crval1 = ra_targ + VA_fac*(New.crval1 - ra_targ) # New.crval2 = dec_targ + VA_fac*(New.crval2 - dec_targ) # First shift the CRVALs apart # This is now relative to the reference chip, not the # target position. New.crval1 = R.crval1 + VA_fac*diff_angles(New.crval1, R.crval1) New.crval2 = R.crval2 + VA_fac*diff_angles(New.crval2, R.crval2) # and scale the CDs New.cd11 = New.cd11*VA_fac New.cd12 = New.cd12*VA_fac New.cd21 = New.cd21*VA_fac New.cd22 = New.cd22*VA_fac New_cdmat = N.array([[New.cd11,New.cd12],[New.cd21,New.cd22]]) # Store new one # archive=yes specifies to also write out archived WCS keywords # overwrite=no specifies do not overwrite any pre-existing archived keywords New.write(fitsname=_new_name,overwrite=no,quiet=quiet,archive=yes) if _dqname: _dq_iextn = _iextn.replace('sci', dqext.lower()) _new_dqname = _dqname +'['+_dq_iextn+']' dqwcs = wcsutil.WCSObject(_new_dqname) dqwcs.write(fitsname=_new_dqname, wcs=New,overwrite=no,quiet=quiet, archive=yes) """ Convert distortion coefficients into SIP style values and write out to image (assumed to be FITS). """ #First the CD matrix: f = refpix['PSCALE']/3600.0 a = fx[1,1]/3600.0 b = fx[1,0]/3600.0 c = fy[1,1]/3600.0 d = fy[1,0]/3600.0 det = (a*d - b*c)*refpix['PSCALE'] # Write to header fimg = fileutil.openImage(_new_name,mode='update') _new_root,_nextn = fileutil.parseFilename(_new_name) _new_extn = fileutil.getExtn(fimg,_nextn) # Transform the higher-order coefficients for n in range(order+1): for m in range(order+1): if n >= m and n>=2: # Form SIP-style keyword names Akey="A_%d_%d" % (m,n-m) Bkey="B_%d_%d" % (m,n-m) # Assign them values Aval= f*(d*fx[n,m]-b*fy[n,m])/det Bval= f*(a*fy[n,m]-c*fx[n,m])/det _new_extn.header.update(Akey,Aval) _new_extn.header.update(Bkey,Bval) # Update the SIP flag keywords as well #iraf.hedit(image,"CTYPE1","RA---TAN-SIP",verify=no,show=no) #iraf.hedit(image,"CTYPE2","DEC--TAN-SIP",verify=no,show=no) _new_extn.header.update("CTYPE1","RA---TAN-SIP") _new_extn.header.update("CTYPE2","DEC--TAN-SIP") # Finally we also need the order #iraf.hedit(image,"A_ORDER","%d" % order,add=yes,verify=no,show=no) #iraf.hedit(image,"B_ORDER","%d" % order,add=yes,verify=no,show=no) _new_extn.header.update("A_ORDER",order) _new_extn.header.update("B_ORDER",order) # Update header with additional keywords required for proper # interpretation of SIP coefficients by PyDrizzle. _new_extn.header.update("IDCSCALE",refpix['PSCALE']) _new_extn.header.update("IDCV2REF",refpix['V2REF']) _new_extn.header.update("IDCV3REF",refpix['V3REF']) _new_extn.header.update("IDCTHETA",refpix['THETA']) _new_extn.header.update("OCX10",fx[1][0]) _new_extn.header.update("OCX11",fx[1][1]) _new_extn.header.update("OCY10",fy[1][0]) _new_extn.header.update("OCY11",fy[1][1]) #_new_extn.header.update("TDDXOFF",rv23_corr[0][0] - v23_corr[0][0]) #_new_extn.header.update("TDDYOFF",-(rv23_corr[1][0] - v23_corr[1][0])) # Report time-dependent coeffs, if computed if instrument == 'ACS' and detector == 'WFC': _new_extn.header.update("TDDALPHA",alpha) _new_extn.header.update("TDDBETA",beta) # Close image now fimg.close() del fimg
def computeOffsets(self,parity=None,refchip=None): """ This version of 'computeOffsets' calculates the zero-point shifts to be included in the distortion coefficients table used by 'drizzle'. It REQUIRES a parity matrix to convert from V2/V3 coordinates into detector image X/Y coordinates. This matrix will be specific to each detector. """ vref = [] # Check to see if any chip-to-chip offsets need to be computed at all if len(self.members) == 1: refp = self.members[0].geometry.model.refpix refp['XDELTA'] = 0. refp['YDELTA'] = 0. #refp['centered'] = yes return # Set up the parity matrix here for a SINGLE chip if parity == None: # Use class defined dictionary as default parity = self.PARITY # Get reference chip information ref_model=None for member in self.members: if not refchip or refchip == int(member.chip): ref_model = member.geometry.model ref_scale = ref_model.refpix['PSCALE'] ref_v2v3 = np.array([ref_model.refpix['V2REF'],ref_model.refpix['V3REF']]) ref_theta = ref_model.refpix['THETA'] if ref_theta == None: ref_theta = 0.0 ref_pmat = np.dot(fileutil.buildRotMatrix(ref_theta), member.parity) ref_xy = (ref_model.refpix['XREF'],ref_model.refpix['YREF']) break if not ref_model: ref_scale = 1.0 ref_theta = 0.0 ref_v2v3 = [0.,0.] ref_xy = [0.,0.] ref_pmat = np.array([[1.,0.],[0.,1.0]]) # Now compute the offset for each chip # Compute position of each chip's common point relative # to the output chip's reference position. for member in self.members: in_model = member.geometry.model refp = in_model.refpix pscale = in_model.pscale memwcs = member.geometry.wcs v2v3 = np.array([in_model.refpix['V2REF'],in_model.refpix['V3REF']]) scale = refp['PSCALE'] theta = refp['THETA'] if theta == None: theta = 0.0 chipcen = ( (memwcs.naxis1/2.) + memwcs.offset_x, (memwcs.naxis2/2.) + memwcs.offset_y) """ Changed two lines below starting with '##'. The reasoning is that this function computes the offsets between the chips in an observation in model space based on a reference chip. That's why xypos shoulf be coomputed in reference chip space and offset_xy (X/YDELTA) should be chip specific. """ ##xypos = np.dot(ref_pmat,v2v3-ref_v2v3) / scale + ref_xy xypos = np.dot(ref_pmat,v2v3-ref_v2v3) / ref_scale #+ ref_xy chiprot = fileutil.buildRotMatrix(theta - ref_theta) offcen = ((refp['XREF'] - chipcen[0]), (refp['YREF'] - chipcen[1])) # Update member's geometry model with computed # reference position... #refp['XDELTA'] = vref[i][0] - v2com + chip.geometry.delta_x #refp['YDELTA'] = vref[i][1] - v3com + chip.geometry.delta_y ##offset_xy = np.dot(chiprot,xypos-offcen)*scale/ref_scale offset_xy = np.dot(chiprot,xypos)*ref_scale/scale - offcen if 'empty_model' in refp and refp['empty_model'] == True: offset_xy[0] += ref_xy[0] * scale/ref_scale offset_xy[1] += ref_xy[1] * scale/ref_scale refp['XDELTA'] = offset_xy[0] refp['YDELTA'] = offset_xy[1] # Only set centered to yes for full exposures... if member.geometry.wcs.subarray != yes: refp['centered'] = no else: refp['centered'] = yes
def getRange(members,ref_wcs,verbose=None): xma,yma = [],[] xmi,ymi = [],[] #nref_x, nref_y = [], [] # Compute corrected positions of each chip's common point crpix = (ref_wcs.crpix1,ref_wcs.crpix2) ref_rot = ref_wcs.orient _rot = ref_wcs.orient - members[0].geometry.wcslin.orient for member in members: # Need to make sure this is populated with proper defaults # for ALL exposures! _model = member.geometry.model _wcs = member.geometry.wcs _wcslin = member.geometry.wcslin _theta = _wcslin.orient - ref_rot # Need to scale corner positions to final output scale _scale =_wcslin.pscale/ ref_wcs.pscale # Compute the corrected,scaled corner positions for each chip #xyedge = member.calcNewEdges(pscale=ref_wcs.pscale) xypos = member.geometry.calcNewCorners() * _scale if _theta != 0.0: #rotate coordinates to match output orientation # Now, rotate new coord _mrot = buildRotMatrix(_theta) xypos = np.dot(xypos,_mrot) _oxmax = np.maximum.reduce(xypos[:,0]) _oymax = np.maximum.reduce(xypos[:,1]) _oxmin = np.minimum.reduce(xypos[:,0]) _oymin = np.minimum.reduce(xypos[:,1]) # Update the corners attribute of the member with the # positions of the computed, distortion-corrected corners #member.corners['corrected'] = np.array([(_oxmin,_oymin),(_oxmin,_oymax),(_oxmax,_oymin),(_oxmax,_oymax)],dtype=np.float64) member.corners['corrected'] = xypos xma.append(_oxmax) yma.append(_oymax) xmi.append(_oxmin) ymi.append(_oymin) #nrefx = (_oxmin+_oxmax) * ref_wcs.pscale/ _wcslin.pscale #nrefy = (_oymin+_oymax) * ref_wcs.pscale/ _wcslin.pscale #nref_x.append(nrefx) #nref_y.append(nrefy) #if _rot != 0.: # mrot = buildRotMatrix(_rot) # nref = np.dot(np.array([nrefx,nrefy]),_mrot) # Determine the full size of the metachip xmax = np.maximum.reduce(xma) ymax = np.maximum.reduce(yma) ymin = np.minimum.reduce(ymi) xmin = np.minimum.reduce(xmi) # Compute offset from center that distortion correction shifts the image. # This accounts for the fact that the output is no longer symmetric around # the reference position... # Scale by ratio of plate-scales so that DELTAs are always in input frame # """ Keep the computation of nref in reference chip space. Using the ratio below is almost correct for ACS and wrong for WFPC2 subarrays """ ##_ratio = ref_wcs.pscale / _wcslin.pscale ##nref = ( (xmin + xmax)*_ratio, (ymin + ymax)*_ratio ) nref = ( (xmin + xmax), (ymin + ymax)) #print 'nref_x, nref_y', nref_x, nref_y #nref = (np.maximum.reduce(nref_x), np.maximum.reduce(nref_y)) if _rot != 0.: _mrot = buildRotMatrix(_rot) nref = np.dot(nref,_mrot) # Now, compute overall size based on range of pixels and offset from center. #xsize = int(xmax - xmin + nref[0]) #ysize = int(ymax - ymin + nref[1]) # Add '2' to each dimension to allow for fractional pixels at the # edge of the image. Also, 'drizzle' centers the output, so # adding 2 only expands image by 1 row on each edge. # An additional two is added to accomodate floating point errors in drizzle. xsize = int(ceil(xmax)) - int(floor(xmin)) ysize = int(ceil(ymax)) - int(floor(ymin)) meta_range = {} meta_range = {'xmin':xmin,'xmax':xmax,'ymin':ymin,'ymax':ymax,'nref':nref} meta_range['xsize'] = xsize meta_range['ysize'] = ysize if verbose: print('Meta_WCS:') print(' NREF :',nref) print(' X range :',xmin,xmax) print(' Y range :',ymin,ymax) print(' Computed Size: ',xsize,ysize) return meta_range
def mergeWCS(default_wcs,user_pars): """ Merges the user specified WCS values given as dictionary derived from the input configObj object with the output PyWCS object computed using distortion.output_wcs(). The user_pars dictionary needs to have the following set of keys:: user_pars = {'ra':None,'dec':None,'scale':None,'rot':None, 'outnx':None,'outny':None,'crpix1':None,'crpix2':None} """ # # Start by making a copy of the input WCS... # outwcs = default_wcs.deepcopy() # If there are no user set parameters, just return a copy of the original WCS merge = False for upar in user_pars.values(): if upar is not None: merge = True break if not merge: return outwcs if ('ra' not in user_pars) or user_pars['ra'] == None: _crval = None else: _crval = (user_pars['ra'],user_pars['dec']) if ('scale' not in user_pars) or user_pars['scale'] == None: _ratio = 1.0 _psize = None # Need to resize the WCS for any changes in pscale else: _ratio = outwcs.pscale / user_pars['scale'] _psize = user_pars['scale'] if ('rot' not in user_pars) or user_pars['rot'] == None: _orient = None _delta_rot = 0. else: _orient = user_pars['rot'] _delta_rot = outwcs.orientat - user_pars['rot'] _mrot = fileutil.buildRotMatrix(_delta_rot) if ('outnx' not in user_pars) or user_pars['outnx'] == None: _corners = np.array([[0.,0.],[outwcs._naxis1,0.],[0.,outwcs._naxis2],[outwcs._naxis1,outwcs._naxis2]]) _corners -= (outwcs._naxis1/2.,outwcs._naxis2/2.) _range = util.getRotatedSize(_corners,_delta_rot) shape = ((_range[0][1] - _range[0][0])*_ratio,(_range[1][1]-_range[1][0])*_ratio) old_shape = (outwcs._naxis1*_ratio,outwcs._naxis2*_ratio) _crpix = (shape[0]/2., shape[1]/2.) else: shape = [user_pars['outnx'],user_pars['outny']] if user_pars['crpix1'] == None: _crpix = (shape[0]/2.,shape[1]/2.) else: _crpix = [user_pars['crpix1'],user_pars['crpix2']] # Set up the new WCS based on values from old one. # Update plate scale outwcs.wcs.cd = outwcs.wcs.cd / _ratio outwcs.pscale /= _ratio #Update orientation outwcs.rotateCD(_delta_rot) outwcs.orientat += -_delta_rot # Update size outwcs._naxis1 = int(shape[0]) outwcs._naxis2 = int(shape[1]) # Update reference position outwcs.wcs.crpix = np.array(_crpix,dtype=np.float64) if _crval is not None: outwcs.wcs.crval = np.array(_crval,dtype=np.float64) return outwcs