def from_tree(cls, node, ctx): import gwcs steps = [(x['frame'], x.get('transform')) for x in node['steps']] name = node['name'] return gwcs.WCS(steps, name=name)
def wcs(self): """ A gWCS object representing all the coordinates. """ model = self.model return gwcs.WCS(forward_transform=model, input_frame=_generate_generic_frame(model.n_inputs, u.pix), output_frame=self.frame)
def _make_reference_gwcs_wcs(fits_hdr): hdr = fits.Header.fromfile(get_pkg_data_filename(fits_hdr)) fw = fitswcs.WCS(hdr) unit_conv = Scale(1.0 / 3600.0, name='arcsec_to_deg_1D') unit_conv = unit_conv & unit_conv unit_conv.name = 'arcsec_to_deg_2D' unit_conv_inv = Scale(3600.0, name='deg_to_arcsec_1D') unit_conv_inv = unit_conv_inv & unit_conv_inv unit_conv_inv.name = 'deg_to_arcsec_2D' c2s = CartesianToSpherical(name='c2s', wrap_lon_at=180) s2c = SphericalToCartesian(name='s2c', wrap_lon_at=180) c2tan = ((Mapping((0, 1, 2), name='xyz') / Mapping( (0, 0, 0), n_inputs=3, name='xxx')) | Mapping((1, 2), name='xtyt')) c2tan.name = 'Cartesian 3D to TAN' tan2c = (Mapping((0, 0, 1), n_inputs=2, name='xtyt2xyz') | (Const1D(1, name='one') & Identity(2, name='I(2D)'))) tan2c.name = 'TAN to cartesian 3D' tan2c.inverse = c2tan c2tan.inverse = tan2c aff = AffineTransformation2D(matrix=fw.wcs.cd) offx = Shift(-fw.wcs.crpix[0]) offy = Shift(-fw.wcs.crpix[1]) s = 5e-6 scale = Scale(s) & Scale(s) det2tan = (offx & offy) | scale | tan2c | c2s | unit_conv_inv taninv = s2c | c2tan tan = Pix2Sky_TAN() n2c = RotateNative2Celestial(fw.wcs.crval[0], fw.wcs.crval[1], 180) wcslin = unit_conv | taninv | scale.inverse | aff | tan | n2c sky_frm = cf.CelestialFrame(reference_frame=coord.ICRS()) det_frm = cf.Frame2D(name='detector') v2v3_frm = cf.Frame2D(name="v2v3", unit=(u.arcsec, u.arcsec), axes_names=('x', 'y'), axes_order=(0, 1)) pipeline = [(det_frm, det2tan), (v2v3_frm, wcslin), (sky_frm, None)] gw = gwcs.WCS(input_frame=det_frm, output_frame=sky_frm, forward_transform=pipeline) gw.crpix = fw.wcs.crpix gw.crval = fw.wcs.crval gw.bounding_box = ((-0.5, fw.pixel_shape[0] - 0.5), (-0.5, fw.pixel_shape[1] - 0.5)) return gw
def hdu_to_imagemodel(in_hdu): """ Workaround for initializing a `jwst.datamodels.ImageModel` from a normal FITS ImageHDU that could contain HST header keywords and unexpected WCS definition. TBD Parameters ---------- in_hdu : `astropy.io.fits.ImageHDU` Returns ------- img : `jwst.datamodels.ImageModel` """ from astropy.io.fits import ImageHDU, HDUList from astropy.coordinates import ICRS from jwst.datamodels import util import gwcs hdu = ImageHDU(data=in_hdu.data, header=in_hdu.header) new_header = strip_telescope_header(hdu.header) hdu.header = new_header # Initialize data model img = util.open(HDUList([hdu])) # Initialize GWCS tform = gwcs.wcs.utils.make_fitswcs_transform(new_header) hwcs = gwcs.WCS(forward_transform=tform, output_frame=ICRS()) #gwcs.CelestialFrame()) sh = hdu.data.shape hwcs.bounding_box = ((-0.5, sh[0] - 0.5), (-0.5, sh[1] - 0.5)) # Put gWCS in meta, where blot/drizzle expect to find it img.meta.wcs = hwcs return img
def gwcs_from_headers(headers): """ Given a list of headers build a gwcs. Parameters ---------- headers : `list` A list of headers. These are expected to have already been validated. """ header = headers[0] pixel_frame = build_pixel_frame(header) builder = TransformBuilder(headers) world_frame = cf.CompositeFrame(builder.frames) return gwcs.WCS(forward_transform=builder.transform, input_frame=pixel_frame, output_frame=world_frame)
def blot(self): """MAIN FUNCTION Resamples the input image onto the WCS associated with the given instrument/apertures """ # Get detector names from the aperture list if isinstance(self.aperture, str): self.aperture = [self.aperture] self.detector = [element.split('_')[0] for element in self.aperture] # Make sure detector, ra, dec, and roll have same number # of elements if ((len(self.center_dec) != len(self.center_ra)) | \ (len(self.center_dec) != len(self.pav3))): raise ValueError(('WARNING: center_ra, center_dec ' 'and pav3 all must have the same number ' 'of elements.')) if type(self.blotfile) == str: input_mod = datamodels.ImageModel(self.blotfile) outbase = self.blotfile elif type(self.blotfile) == datamodels.image.ImageModel: # Not a great solution at the moment. Save # imagemodel instance so that you have a fits # file from which the header can be retrieved input_mod = copy(self.blotfile) self.blotfile.save('temp.fits') self.blotfile = 'temp.fits' outbase = 'mosaic' else: raise ValueError('WARNING: unrecognized type for blotfile') # Create a GWCS object from the input file's header input_header = fits.getheader(self.blotfile, ext=1) transform = gwcs.utils.make_fitswcs_transform(input_header) input_mod.meta.wcs = gwcs.WCS(forward_transform=transform, output_frame='world') # Filter and pupil information filtername = input_mod.meta.instrument.filter try: pupil = input_mod.meta.instrument.pupil except: pupil = 'CLEAR' # Get position angle of input data input_pav3 = input_mod.meta.wcsinfo.roll_ref # Name of temporary file output for set_telescope_pointing # to work on shellname = 'temp_wcs_container.fits' blist = [] for (aper, det, ra, dec, roll) in \ zip(self.aperture, self.detector, self.center_ra, self.center_dec, self.pav3): # Get aperture-specific info self.siaf = get_instance(self.instrument)[aper] # Create datamodel with appropriate metadata bmodel = self.make_model(det, ra, dec, input_pav3, filtername, pupil) bmodel.save(shellname, overwrite=True) # Use set_telescope_pointing to compute local roll # angle and PC matrix stp.add_wcs(shellname, roll=roll) bmodel = datamodels.open(shellname) # Now we need to run assign_wcs step so that these # files get a gwcs object attached to them. if self.distortion_file is not None: bmodel = AssignWcsStep.call( bmodel, override_distortion=self.distortion_file) else: bmodel = AssignWcsStep.call(bmodel) # Add to the list of data model instances to blot to blist.append(bmodel) # Place the model instances to blot to in a ModelContainer blot_list = container.ModelContainer(blist) # Blot the image to each of the WCSs in the blot_list pars = {'sinscl': 1.0, 'interp': 'poly5'} reffiles = {} blotter = outlier_detection.OutlierDetection(blot_list, reffiles=reffiles, **pars) blotter.input_models = blot_list self.blotted_datamodels = blotter.blot_median(input_mod)
def set_correction(self, matrix=[[1, 0], [0, 1]], shift=[0, 0], ref_tpwcs=None, meta=None, **kwargs): """ Sets a tangent-plane correction of the GWCS object according to the provided liniar parameters. In addition, this function updates the ``meta`` attribute of the `JWSTgWCS` object with the values of keyword arguments except for the argument ``meta`` which is *merged* with the *attribute* ``meta``. Parameters ---------- matrix: list, numpy.ndarray A ``2x2`` array or list of lists coefficients representing scale, rotation, and/or skew transformations. shift: list, numpy.ndarray A list of two coordinate shifts to be applied to coordinates *after* ``matrix`` transformations are applied. ref_tpwcs: TPWCS, None, optional A reference WCS of the type ``TPWCS`` that provides the tangent plane in which corrections (``matrix`` and ``shift``) were defined. When not provided (i.e., set to `None`), it is assumed that the transformations are being applied directly to *this* image WCS' tangent plane. meta: dict, None, optional Dictionary that will be merged to the object's ``meta`` fields. **kwargs: optional parameters Optional parameters for the WCS corrector. `JWSTgWCS` ignores these arguments (except for storing them in the ``meta`` attribute). """ frms = self._wcs.available_frames if ref_tpwcs is None: matrix = np.array(matrix, dtype=np.double) shift = np.array(shift, dtype=np.double) else: # compute linear transformation from the tangent plane used for # alignment to the tangent plane of this wcs: r, t = _tp2tp(ref_tpwcs, self) matrix = np.linalg.multi_dot([r, matrix, inv(r)]).astype(np.double) shift = (np.dot(r, shift) - np.dot(matrix, t) + t).astype( np.double) # if original WCS did not have tangent-plane corrections, create # new correction and add it to the WCs pipeline: if self._tpcorr is None: self._tpcorr = JWSTgWCS._tpcorr_init( v2_ref=self._wcsinfo['v2_ref'] / 3600.0, v3_ref=self._wcsinfo['v3_ref'] / 3600.0, roll_ref=self._wcsinfo['roll_ref']) JWSTgWCS._tpcorr_combine_affines(self._tpcorr, matrix, _ARCSEC2RAD * np.asarray(shift)) self._partial_tpcorr = JWSTgWCS._v2v3_to_tpcorr_from_full( self._tpcorr) idx_v2v3 = frms.index(self._v23name) pipeline = deepcopy(self._wcs.pipeline) pf, pt = pipeline[idx_v2v3] pipeline[idx_v2v3] = (pf, deepcopy(self._tpcorr)) frm_v2v3corr = deepcopy(pf) frm_v2v3corr.name = 'v2v3corr' pipeline.insert(idx_v2v3 + 1, (frm_v2v3corr, pt)) self._wcs = gwcs.WCS(pipeline, name=self._owcs.name) self._v23name = 'v2v3corr' else: # combine old and new corrections into a single one and replace # old transformation with the combined correction transformation: JWSTgWCS._tpcorr_combine_affines(self._tpcorr, matrix, _ARCSEC2RAD * np.asarray(shift)) self._partial_tpcorr = JWSTgWCS._v2v3_to_tpcorr_from_full( self._tpcorr) idx_v2v3 = frms.index(self._v23name) pipeline = deepcopy(self._wcs.pipeline) pipeline[idx_v2v3 - 1] = (pipeline[idx_v2v3 - 1].frame, deepcopy(self._tpcorr)) self._wcs = gwcs.WCS(pipeline, name=self._owcs.name) # reset definitions of the transformations from detector/world # coordinates to the tangent plane: self._update_transformations() # save linear transformation info to the meta attribute: super().set_correction(matrix=matrix, shift=shift, meta=meta, **kwargs)
def blot(self): # Make sure detector, ra, dec, and roll have same number # of elements if ((len(self.detector) != len(self.center_ra)) | \ (len(self.detector) != len(self.center_dec)) | \ (len(self.detector) != len(self.pav3))): print('WARNING: detector, center_ra, center_dec') print('and pav3 all must have the same number') print('of elements.') sys.exit() if type(self.blotfile) == str: input_mod = datamodels.ImageModel(self.blotfile) outbase = self.blotfile # Create a GWCS object from the input file's header #input_header = fits.getheader(self.blotfile) #transform = gwcs.utils.make_fitswcs_transform(header) #input_mod.meta.wcs = gwcs.WCS(transform) elif type(self.blotfile) == datamodels.image.ImageModel: # Not a great solution at the moment. Save # imagemodel instance so that you have a fits # file from which the header can be retrieved input_mod = copy(self.blotfile) self.blotfile.save('temp.fits') self.blotfile = 'temp.fits' outbase = 'mosaic' else: print('WARNING: unrecognized type for blotfile') sys.exit() # Create a GWCS object from the input file's header input_header = fits.getheader(self.blotfile, ext=1) transform = gwcs.utils.make_fitswcs_transform(input_header) input_mod.meta.wcs = gwcs.WCS(forward_transform=transform, output_frame='world') # Filter and pupil information filter = input_mod.meta.instrument.filter try: pupil = input_mod.meta.instrument.pupil except: pupil = 'CLEAR' # get position angle of input data input_pav3 = input_mod.meta.wcsinfo.roll_ref # parity is always -1 for nircam parity = -1 # Name of temporary file output for set_telescope_pointing # to work on shellname = 'temp_wcs_container.fits' blist = [] for (det,ra,dec,roll) in \ zip(self.detector,self.center_ra,\ self.center_dec,self.pav3): # get detector-specific info v2ref, v3ref, v3ang = self.get_siaf_info(det) # create datamodel with appropriate metadata bmodel = self.make_model(det, ra, dec, v2ref, v3ref, v3ang, parity, input_pav3, filter, pupil) #shellname = 'wcs_model_to_blot_to_{}_{}_{}_{}.fits'.format(det,ra,dec,roll) bmodel.save(shellname, overwrite=True) #tmpname = 'wcs_model_to_blot_to_BASEMODEL_{}_{}_{}_{}.fits'.format(det,ra,dec,roll) #bmodel.save(tmpname,overwrite=True) # use set_telescope_pointing to compute local roll # angle and PC matrix stp.add_wcs(shellname, roll=roll) bmodel = datamodels.open(shellname) # Now we need to run assign_wcs step so that these # files get a gwcs object attached to them. dist_reffile = [s for s in self.distfiles if det in s][0] bmodel = AssignWcsStep.call(bmodel, override_distortion=dist_reffile) #tmpname = 'wcs_model_to_blot_to_ASSIGNWCS_{}_{}_{}_{}.fits'.format(det,ra,dec,roll) #bmodel.save(tmpname,overwrite=True) # Add to the list of data model instances to blot to blist.append(bmodel) #place the model instances to blot to in a ModelContainer blot_list = container.ModelContainer(blist) #blot the image to each of the WCSs in the blot_list pars = {'sinscl': 1.0, 'interp': 'poly5'} reffiles = {} mm = outlier_detection.OutlierDetection(blot_list, reffiles=reffiles, **pars) blotted_datamodels = outlier_detection.blot_median( input_mod, blot_list, **mm.outlierpars) for (bltted,det,ra,dec,roll) in \ zip(blotted_datamodels,self.detector,self.center_ra,\ self.center_dec,self.pav3): if self.outfile is None: self.outfile = 'blotted_from_{}_to_{}_{}_{}_{}.fits'.format( outbase, det, ra, dec, roll) bltted.save(self.outfile)
def set_correction(self, matrix=[[1, 0], [0, 1]], shift=[0, 0], meta=None, **kwargs): """ Sets a tangent-plane correction of the GWCS object according to the provided liniar parameters. In addition, this function updates the ``meta`` attribute of the `JWSTgWCS` object with the values of keyword arguments except for the argument ``meta`` which is *merged* with the *attribute* ``meta``. Parameters ---------- matrix : list, numpy.ndarray A ``2x2`` array or list of lists coefficients representing scale, rotation, and/or skew transformations. shift : list, numpy.ndarray A list of two coordinate shifts to be applied to coordinates *before* ``matrix`` transformations are applied. meta : dict, None, optional Dictionary that will be merged to the object's ``meta`` fields. **kwargs : optional parameters Optional parameters for the WCS corrector. `JWSTgWCS` ignores these arguments (except for storing them in the ``meta`` attribute). """ frms = self._wcs.available_frames # if original WCS did not have tangent-plane corrections, create # new correction and add it to the WCs pipeline: if self._tpcorr is None: self._tpcorr = TPCorr( v2ref=self._wcsinfo['v2_ref'] / 3600.0, v3ref=self._wcsinfo['v3_ref'] / 3600.0, roll=self._wcsinfo['roll_ref'], matrix=matrix, shift=shift, name='tangent-plane linear correction' ) idx_v2v3 = frms.index(self._v23name) pipeline = deepcopy(self._wcs.pipeline) pf, pt = pipeline[idx_v2v3] pipeline[idx_v2v3] = (pf, deepcopy(self._tpcorr)) frm_v2v3corr = deepcopy(pf) frm_v2v3corr.name = 'v2v3corr' pipeline.insert(idx_v2v3 + 1, (frm_v2v3corr, pt)) self._wcs = gwcs.WCS(pipeline, name=self._owcs.name) self._v23name = 'v2v3corr' else: # combine old and new corrections into a single one and replace # old transformation with the combined correction transformation: tpcorr2 = self._tpcorr.__class__( v2ref=self._tpcorr.v2ref, v3ref=self._tpcorr.v3ref, roll=self._tpcorr.roll, matrix=matrix, shift=shift, name='tangent-plane linear correction' ) self._tpcorr = tpcorr2.combine(tpcorr2, self._tpcorr) idx_v2v3 = frms.index(self._v23name) pipeline = deepcopy(self._wcs.pipeline) pipeline[idx_v2v3 - 1] = (pipeline[idx_v2v3 - 1][0], deepcopy(self._tpcorr)) self._wcs = gwcs.WCS(pipeline, name=self._owcs.name) # reset definitions of the transformations from detector/world # coordinates to the tangent plane: self._update_transformations() # save linear transformation info to the meta attribute: self._meta['matrix'] = matrix self._meta['shift'] = shift if meta is not None: self._meta.update(meta)
def _make_gwcs_wcs(fits_hdr): hdr = fits.Header.fromfile(get_pkg_data_filename(fits_hdr)) fw = fitswcs.WCS(hdr) a_order = hdr['A_ORDER'] a_coeff = {} for i in range(a_order + 1): for j in range(a_order + 1 - i): key = 'A_{:d}_{:d}'.format(i, j) if key in hdr: a_coeff[key] = hdr[key] b_order = hdr['B_ORDER'] b_coeff = {} for i in range(b_order + 1): for j in range(b_order + 1 - i): key = 'B_{:d}_{:d}'.format(i, j) if key in hdr: b_coeff[key] = hdr[key] cx = {"c" + k[2:]: v for k, v in a_coeff.items()} cy = {"c" + k[2:]: v for k, v in b_coeff.items()} sip_distortion = ((Shift(-fw.wcs.crpix[0]) & Shift(-fw.wcs.crpix[1])) | Mapping((0, 1, 0, 1)) | (polynomial.Polynomial2D(a_order, **cx, c1_0=1) & polynomial.Polynomial2D(b_order, **cy, c0_1=1)) | (Shift(fw.wcs.crpix[0]) & Shift(fw.wcs.crpix[1]))) y, x = np.indices(fw.array_shape) unit_conv = Scale(1.0 / 3600.0, name='arcsec_to_deg_1D') unit_conv = unit_conv & unit_conv unit_conv.name = 'arcsec_to_deg_2D' unit_conv_inv = Scale(3600.0, name='deg_to_arcsec_1D') unit_conv_inv = unit_conv_inv & unit_conv_inv unit_conv_inv.name = 'deg_to_arcsec_2D' c2s = CartesianToSpherical(name='c2s', wrap_lon_at=180) s2c = SphericalToCartesian(name='s2c', wrap_lon_at=180) c2tan = ((Mapping((0, 1, 2), name='xyz') / Mapping( (0, 0, 0), n_inputs=3, name='xxx')) | Mapping((1, 2), name='xtyt')) c2tan.name = 'Cartesian 3D to TAN' tan2c = (Mapping((0, 0, 1), n_inputs=2, name='xtyt2xyz') | (Const1D(1, name='one') & Identity(2, name='I(2D)'))) tan2c.name = 'TAN to cartesian 3D' tan2c.inverse = c2tan c2tan.inverse = tan2c aff = AffineTransformation2D(matrix=fw.wcs.cd) offx = Shift(-fw.wcs.crpix[0]) offy = Shift(-fw.wcs.crpix[1]) s = 5e-6 scale = Scale(s) & Scale(s) sip_distortion |= (offx & offy) | scale | tan2c | c2s | unit_conv_inv taninv = s2c | c2tan tan = Pix2Sky_TAN() n2c = RotateNative2Celestial(fw.wcs.crval[0], fw.wcs.crval[1], 180) wcslin = unit_conv | taninv | scale.inverse | aff | tan | n2c sky_frm = cf.CelestialFrame(reference_frame=coord.ICRS()) det_frm = cf.Frame2D(name='detector') v2v3_frm = cf.Frame2D(name="v2v3", unit=(u.arcsec, u.arcsec), axes_names=('x', 'y'), axes_order=(0, 1)) pipeline = [(det_frm, sip_distortion), (v2v3_frm, wcslin), (sky_frm, None)] gw = gwcs.WCS(input_frame=det_frm, output_frame=sky_frm, forward_transform=pipeline) gw.crpix = fw.wcs.crpix gw.crval = fw.wcs.crval gw.bounding_box = ((-0.5, fw.pixel_shape[0] - 0.5), (-0.5, fw.pixel_shape[1] - 0.5)) # sanity check: for _ in range(100): x = np.random.randint(1, fw.pixel_shape[0]) y = np.random.randint(1, fw.pixel_shape[1]) assert np.allclose(gw(x, y), fw.all_pix2world(x, y, 1), rtol=0, atol=1e-11) return gw
def wcs(self): return gwcs.WCS(forward_transform=self.model, input_frame=self._generate_generic_frame( self.model.n_inputs, u.pix), output_frame=self.frame)