def create_frames(): """ Create the coordinate frames in the NIRSPEC WCS pipeline. These are "detector", "gwa", "slit_frame", "msa_frame", "oteip", "v2v3", "world". """ det = cf.Frame2D(name='detector', axes_order=(0, 1)) sca = cf.Frame2D(name='sca', axes_order=(0, 1)) gwa = cf.Frame2D(name="gwa", axes_order=(0, 1), unit=(u.rad, u.rad), axes_names=('alpha_in', 'beta_in')) msa_spatial = cf.Frame2D(name='msa_spatial', axes_order=(0, 1), unit=(u.m, u.m), axes_names=('x_msa', 'y_msa')) slit_spatial = cf.Frame2D(name='slit_spatial', axes_order=(0, 1), unit=("", ""), axes_names=('x_slit', 'y_slit')) sky = cf.CelestialFrame(name='sky', axes_order=(0, 1), reference_frame=coord.ICRS()) v2v3_spatial = cf.Frame2D(name='v2v3_spatial', axes_order=(0, 1), unit=(u.deg, u.deg), axes_names=('V2', 'V3')) # The oteip_to_v23 incorporates a scale to convert the spectral units from # meters to microns. So the v2v3 output frame will be in u.deg, u.deg, u.micron spec = cf.SpectralFrame(name='spectral', axes_order=(2,), unit=(u.micron,), axes_names=('wavelength',)) v2v3 = cf.CompositeFrame([v2v3_spatial, spec], name='v2v3') slit_frame = cf.CompositeFrame([slit_spatial, spec], name='slit_frame') msa_frame = cf.CompositeFrame([msa_spatial, spec], name='msa_frame') oteip_spatial = cf.Frame2D(name='oteip', axes_order=(0, 1), unit=(u.deg, u.deg), axes_names=('X_OTEIP', 'Y_OTEIP')) oteip = cf.CompositeFrame([oteip_spatial, spec], name='oteip') world = cf.CompositeFrame([sky, spec], name='world') return det, sca, gwa, slit_frame, msa_frame, oteip, v2v3, world
def ifu(input_model, reference_files): """ Create the WCS pipeline for a MIRI IFU observation. Goes from 0-indexed detector pixels (0,0) middle of lower left reference pixel to V2,V3 in arcsec. Parameters ---------- input_model : `jwst.datamodels.ImagingModel` Data model. reference_files : dict Dictionary {reftype: reference file name}. """ #reference_files = {'distortion': 'jwst_miri_distortion_00001.asdf', #files must hold 2 channels each #'specwcs': 'jwst_miri_specwcs_00001.asdf', #'regions': 'jwst_miri_regions_00001.asdf', #'wavelengthrange': 'jwst_miri_wavelengthrange_0001.asdf'} # Define reference frames detector = cf.Frame2D(name='detector', axes_order=(0, 1), unit=(u.pix, u.pix)) alpha_beta = cf.Frame2D(name='alpha_beta_spatial', axes_order=(0, 1), unit=(u.arcsec, u.arcsec), axes_names=('alpha', 'beta')) spec_local = cf.SpectralFrame(name='alpha_beta_spectral', axes_order=(2, ), unit=(u.micron, ), axes_names=('lambda', )) miri_focal = cf.CompositeFrame([alpha_beta, spec_local], name='alpha_beta') v23_spatial = cf.Frame2D(name='V2_V3_spatial', axes_order=(0, 1), unit=(u.deg, u.deg), axes_names=('v2', 'v3')) spec = cf.SpectralFrame(name='spectral', axes_order=(2, ), unit=(u.micron, ), axes_names=('lambda', )) v2v3 = cf.CompositeFrame([v23_spatial, spec], name='v2v3') icrs = cf.CelestialFrame(name='icrs', reference_frame=coord.ICRS(), axes_order=(0, 1), unit=(u.deg, u.deg), axes_names=('RA', 'DEC')) world = cf.CompositeFrame([icrs, spec], name='world') # Define the actual transforms det2abl = (detector_to_abl(input_model, reference_files)).rename("detector_to_abl") abl2v2v3l = (abl_to_v2v3l(input_model, reference_files)).rename("abl_to_v2v3l") tel2sky = pointing.v23tosky(input_model) & models.Identity(1) # Put the transforms together into a single transform shape = input_model.data.shape det2abl.bounding_box = ((-0.5, shape[0] - 0.5), (-0.5, shape[1] - 0.5)) pipeline = [(detector, det2abl), (miri_focal, abl2v2v3l), (v2v3, tel2sky), (world, None)] return pipeline
def create_coord_frames(): gdetector = cf.Frame2D(name='grism_detector', axes_order=(0, 1), unit=(u.pix, u.pix)) detector = cf.Frame2D(name='full_detector', axes_order=(0, 1), axes_names=('dx', 'dy'), unit=(u.pix, u.pix)) v2v3_spatial = cf.Frame2D(name='v2v3_spatial', axes_order=(0, 1), axes_names=('v2', 'v3'), unit=(u.deg, u.deg)) v2v3vacorr_spatial = cf.Frame2D(name='v2v3vacorr_spatial', axes_order=(0, 1), axes_names=('v2', 'v3'), unit=(u.arcsec, u.arcsec)) sky_frame = cf.CelestialFrame(reference_frame=coord.ICRS(), name='icrs') spec = cf.SpectralFrame(name='spectral', axes_order=(2, ), unit=(u.micron, ), axes_names=('wavelength', )) frames = { 'grism_detector': gdetector, 'direct_image': cf.CompositeFrame([detector, spec], name='direct_image'), 'v2v3': cf.CompositeFrame([v2v3_spatial, spec], name='v2v3'), 'v2v3vacorr': cf.CompositeFrame([v2v3vacorr_spatial, spec], name='v2v3vacorr'), 'world': cf.CompositeFrame([sky_frame, spec], name='world') } return frames
def ifu(input_model, reference_files): """ Create the WCS pipeline for a MIRI IFU observation. """ #reference_files = {'distortion': 'jwst_miri_distortion_00001.asdf', #files must hold 2 channels each #'specwcs': 'jwst_miri_specwcs_00001.asdf', #'regions': 'jwst_miri_regions_00001.asdf', #'v2v3': 'jwst_miri_v2v3_00001.asdf' #'wavelengthrange': 'jwst_miri_wavelengthrange_0001.asdf'} detector = cf.Frame2D(name='detector', axes_order=(0, 1), unit=(u.pix, u.pix)) alpha_beta = cf.Frame2D(name='alpha_beta_spatial', axes_order=(0, 1), unit=(u.arcsec, u.arcsec), axes_names=('alpha', 'beta')) spec_local = cf.SpectralFrame(name='alpha_beta_spectral', axes_order=(2,), unit=(u.micron,), axes_names=('lambda',)) miri_focal = cf.CompositeFrame([alpha_beta, spec_local], name='alpha_beta') xyan_spatial = cf.Frame2D(name='Xan_Yan_spatial', axes_order=(0, 1), unit=(u.arcmin, u.arcmin), axes_names=('v2', 'v3')) spec = cf.SpectralFrame(name='Xan_Yan_spectral', axes_order=(2,), unit=(u.micron,), axes_names=('lambda',)) xyan = cf.CompositeFrame([xyan_spatial, spec], name='Xan_Yan') v23_spatial = cf.Frame2D(name='V2_V3_spatial', axes_order=(0, 1), unit=(u.arcmin, u.arcmin), axes_names=('v2', 'v3')) spec = cf.SpectralFrame(name='V2_v3_spectral', axes_order=(2,), unit=(u.micron,), axes_names=('lambda',)) v2v3 = cf.CompositeFrame([v23_spatial, spec], name='V2_V3') icrs = cf.CelestialFrame(name='icrs', reference_frame=coord.ICRS(), axes_order=(0, 1), unit=(u.deg, u.deg), axes_names=('RA', 'DEC')) sky = cf.CompositeFrame([icrs, spec], name='sky_and_wavelength') det2alpha_beta = (detector_to_alpha_beta(input_model, reference_files)).rename( "detector_to_alpha_beta") ab2xyan = (alpha_beta2XanYan(input_model, reference_files)).rename("alpha_beta_to_Xan_Yan") xyan2v23 = models.Identity(1) & (models.Shift(7.8) | models.Scale(-1)) & models.Identity(1) fitswcs_transform = pointing.create_fitswcs_transform(input_model) & models.Identity(1) pipeline = [(detector, det2alpha_beta), (miri_focal, ab2xyan), (xyan, xyan2v23), (v2v3, fitswcs_transform), (sky, None) ] return pipeline
def lrs(input_model, reference_files): """ The LRS-FIXEDSLIT and LRS-SLITLESS WCS pipeline. Notes ----- It includes three coordinate frames - "detector", "v2v3" and "world". "v2v3" and "world" each have (spatial, spatial, spectral) components. Uses the "specwcs" and "distortion" reference files. """ # Define the various coordinate frames. # Original detector frame detector = cf.Frame2D(name='detector', axes_order=(0, 1), unit=(u.pix, u.pix)) # Spectral component spec = cf.SpectralFrame(name='spec', axes_order=(2,), unit=(u.micron,), axes_names=('lambda',)) # v2v3 spatial component v2v3_spatial = cf.Frame2D(name='v2v3_spatial', axes_order=(0, 1), unit=(u.arcsec, u.arcsec), axes_names=('v2', 'v3')) v2v3vacorr_spatial = cf.Frame2D( name='v2v3vacorr_spatial', axes_order=(0, 1), unit=(u.arcsec, u.arcsec), axes_names=('v2', 'v3') ) # v2v3 spatial+spectra v2v3 = cf.CompositeFrame([v2v3_spatial, spec], name='v2v3') v2v3vacorr = cf.CompositeFrame([v2v3vacorr_spatial, spec], name='v2v3vacorr') # 'icrs' frame which is the spatial sky component icrs = cf.CelestialFrame(name='icrs', reference_frame=coord.ICRS(), axes_order=(0, 1), unit=(u.deg, u.deg), axes_names=('RA', 'DEC')) # Final 'world' composite frame with spatial and spectral components world = cf.CompositeFrame(name="world", frames=[icrs, spec]) # Create the transforms dettotel = lrs_distortion(input_model, reference_files) v2v3tosky = pointing.v23tosky(input_model) teltosky = v2v3tosky & models.Identity(1) # Compute differential velocity aberration (DVA) correction: va_corr = pointing.dva_corr_model( va_scale=input_model.meta.velocity_aberration.scale_factor, v2_ref=input_model.meta.wcsinfo.v2_ref, v3_ref=input_model.meta.wcsinfo.v3_ref ) & models.Identity(1) # Put the transforms together into a single pipeline pipeline = [(detector, dettotel), (v2v3, va_corr), (v2v3vacorr, teltosky), (world, None)] return pipeline
def ifu(input_model, reference_files): """ The MIRI MRS WCS pipeline. It has the following coordinate frames: "detector", "alpha_beta", "v2v3", "world". It uses the "distortion", "regions", "specwcs" and "wavelengthrange" reference files. """ # Define coordinate frames. detector = cf.Frame2D(name='detector', axes_order=(0, 1), unit=(u.pix, u.pix)) alpha_beta = cf.Frame2D(name='alpha_beta_spatial', axes_order=(0, 1), unit=(u.arcsec, u.arcsec), axes_names=('alpha', 'beta')) spec_local = cf.SpectralFrame(name='alpha_beta_spectral', axes_order=(2, ), unit=(u.micron, ), axes_names=('lambda', )) miri_focal = cf.CompositeFrame([alpha_beta, spec_local], name='alpha_beta') v23_spatial = cf.Frame2D(name='V2_V3_spatial', axes_order=(0, 1), unit=(u.arcsec, u.arcsec), axes_names=('v2', 'v3')) spec = cf.SpectralFrame(name='spectral', axes_order=(2, ), unit=(u.micron, ), axes_names=('lambda', )) v2v3 = cf.CompositeFrame([v23_spatial, spec], name='v2v3') icrs = cf.CelestialFrame(name='icrs', reference_frame=coord.ICRS(), axes_order=(0, 1), unit=(u.deg, u.deg), axes_names=('RA', 'DEC')) world = cf.CompositeFrame([icrs, spec], name='world') # Define the actual transforms det2abl = (detector_to_abl(input_model, reference_files)).rename("detector_to_abl") abl2v2v3l = (abl_to_v2v3l(input_model, reference_files)).rename("abl_to_v2v3l") tel2sky = pointing.v23tosky(input_model) & models.Identity(1) # Put the transforms together into a single transform shape = input_model.data.shape det2abl.bounding_box = ((-0.5, shape[0] - 0.5), (-0.5, shape[1] - 0.5)) pipeline = [(detector, det2abl), (miri_focal, abl2v2v3l), (v2v3, tel2sky), (world, None)] return pipeline
def fitswcs_to_gwcs(hdr): """ Create and return a gWCS object from a FITS header. If it can't construct one, it should quietly return None. """ # Type of CoordinateFrame to construct for a FITS keyword frame_mapping = {'WAVE': cf.SpectralFrame} # coordinate names for CelestialFrame coordinate_outputs = {'alpha_C', 'delta_C'} # transform = gw.make_fitswcs_transform(hdr) try: transform = make_fitswcs_transform(hdr) except Exception as e: return None outputs = transform.outputs naxes = transform.n_inputs axes_names = ('x', 'y', 'z', 'u', 'v', 'w')[:naxes] in_frame = cf.CoordinateFrame(naxes=naxes, axes_type=['SPATIAL'] * naxes, axes_order=tuple(range(naxes)), name="pixels", axes_names=axes_names, unit=[u.pix] * naxes) out_frames = [] for i, output in enumerate(outputs): unit_name = hdr.get(f'CUNIT{i+1}') try: unit = u.Unit(unit_name) except TypeError: unit = None try: frame = frame_mapping[output[:4].upper()](axes_order=(i,), unit=unit, axes_names=(output,), name=output) except KeyError: if output in coordinate_outputs: continue frame = cf.CoordinateFrame(naxes=1, axes_type=("SPATIAL",), axes_order=(i,), unit=unit, axes_names=(output,), name=output) out_frames.append(frame) if coordinate_outputs.issubset(outputs): frame_name = hdr.get('RADESYS') or hdr.get('RADECSYS') # FK5, etc. try: ref_frame = getattr(coord, frame_name)() # TODO? Work out how to stuff EQUINOX and OBS-TIME into the frame except (AttributeError, TypeError): ref_frame = None axes_order = (outputs.index('alpha_C'), outputs.index('delta_C')) # Call it 'world' if there are no other axes, otherwise 'sky' name = 'SKY' if len(outputs) > 2 else 'world' cel_frame = cf.CelestialFrame(reference_frame=ref_frame, name=name, axes_order=axes_order) out_frames.append(cel_frame) out_frame = (out_frames[0] if len(out_frames) == 1 else cf.CompositeFrame(out_frames, name='world')) return gWCS([(in_frame, transform), (out_frame, None)])
def _new_output_frame(self, axes): """ remove the frames for all axes and return a new output frame. This method assumes axes has already been sanitized for non-separable axes. """ frames = self._get_frames() axes_map = self._get_axes_map(frames) frames = list(frames) for axis in axes: drop_frame = axes_map[axis] # If we are removing coupled axes we might have already removed the frame if drop_frame in frames: frames.remove(drop_frame) # We now need to reindex the axes_order of all the frames to account # for any removed axes. for i, frame in enumerate(frames): if i == 0: start = i else: axes_order = frames[i-1].axes_order start = axes_order[-1] # Start can either be an int or a list/tuple here. if not isinstance(start, int): start = start[-1] # pragma: no cover # I can't work out how to hit this. # Increment start for the next frame. start += 1 frame._axes_order = tuple(range(start, start + frame.naxes)) if len(frames) == 1: return frames[0] else: return cf.CompositeFrame(frames, name=self.gwcs.output_frame.name)
def niriss_soss_set_input(model, order_number): """ Get the right model given the order number. Parameters ---------- model - Input model order_number - the, well, order number desired Returns ------- WCS - the WCS corresponding to the order_number """ # Make sure the order number is correct. if order_number < 1 or order_number > 3: raise ValueError('Order must be between 1 and 3') # Return the correct transform based on the order_number obj = model.meta.wcs.forward_transform.get_model(order_number) # use the size of the input subarray7 detector = cf.Frame2D(name='detector', axes_order=(0, 1), unit=(u.pix, u.pix)) spec = cf.SpectralFrame(name='spectral', axes_order=(2,), unit=(u.micron,), axes_names=('wavelength',)) sky = cf.CelestialFrame(reference_frame=coord.ICRS(), axes_names=('ra', 'dec'), axes_order=(0, 1), unit=(u.deg, u.deg), name='sky') world = cf.CompositeFrame([sky, spec], name='world') pipeline = [(detector, obj), (world, None) ] return wcs.WCS(pipeline)
def generate_s3d_wcs(): """ create a fake gwcs for a cube """ # create input /output frames detector = cf.CoordinateFrame(name='detector', axes_order=(0,1,2), axes_names=['x', 'y', 'z'], axes_type=['spatial', 'spatial', 'spatial'], naxes=3, unit=['pix', 'pix', 'pix']) sky = cf.CelestialFrame(reference_frame=coord.ICRS(), name='sky', axes_names=("RA", "DEC")) spec = cf.SpectralFrame(name='spectral', unit=['um'], axes_names=['wavelength'], axes_order=(2,)) world = cf.CompositeFrame(name="world", frames=[sky, spec]) # create fake transform to at least get a bounding box # for the s3d jwst loader # shape 30,10,10 (spec, y, x) crpix1, crpix2, crpix3 = 5, 5, 15 # (x, y, spec) crval1, crval2, crval3 = 1, 1, 1 cdelt1, cdelt2, cdelt3 = 0.01, 0.01, 0.05 shift = models.Shift(-crpix2) & models.Shift(-crpix1) scale = models.Multiply(cdelt2) & models.Multiply(cdelt1) proj = models.Pix2Sky_TAN() skyrot = models.RotateNative2Celestial(crval2, 90 + crval1, 180) celestial = shift | scale | proj | skyrot wave_model = models.Shift(-crpix3) | models.Multiply(cdelt3) | models.Shift(crval3) transform = models.Mapping((2, 0, 1)) | celestial & wave_model | models.Mapping((1, 2, 0)) # bounding box based on shape (30,10,10) in test transform.bounding_box = ((0, 29), (0, 9), (0, 9)) # create final wcs pipeline = [(detector, transform), (world, None)] return WCS(pipeline)
def identity_gwcs_3d(): """ A simple 1-1 gwcs that converts from pixels to arcseconds """ identity = (TwoDScale(1 * u.arcsec / u.pixel) & m.Multiply(1 * u.nm / u.pixel)) sky_frame = cf.CelestialFrame( axes_order=(0, 1), name='helioprojective', reference_frame=Helioprojective(obstime="2018-01-01"), axes_names=("longitude", "latitude"), unit=(u.arcsec, u.arcsec), axis_physical_types=("custom:pos.helioprojective.lon", "custom:pos.helioprojective.lat")) wave_frame = cf.SpectralFrame(axes_order=(2, ), unit=u.nm, axes_names=("wavelength", )) frame = cf.CompositeFrame([sky_frame, wave_frame]) detector_frame = cf.CoordinateFrame(name="detector", naxes=3, axes_order=(0, 1, 2), axes_type=("pixel", "pixel", "pixel"), axes_names=("x", "y", "z"), unit=(u.pix, u.pix, u.pix)) wcs = gwcs.wcs.WCS(forward_transform=identity, output_frame=frame, input_frame=detector_frame) wcs.pixel_shape = (10, 20, 30) wcs.array_shape = wcs.pixel_shape[::-1] return wcs
def identity_gwcs_4d(): """ A simple 1-1 gwcs that converts from pixels to arcseconds """ identity = (m.Multiply(1 * u.arcsec / u.pixel) & m.Multiply(1 * u.arcsec / u.pixel) & m.Multiply(1 * u.nm / u.pixel) & m.Multiply(1 * u.nm / u.pixel)) sky_frame = cf.CelestialFrame( axes_order=(0, 1), name='helioprojective', reference_frame=Helioprojective(obstime="2018-01-01")) wave_frame = cf.SpectralFrame(axes_order=(2, ), unit=u.nm) time_frame = cf.TemporalFrame(axes_order=(3, ), unit=u.s) frame = cf.CompositeFrame([sky_frame, wave_frame, time_frame]) detector_frame = cf.CoordinateFrame(name="detector", naxes=4, axes_order=(0, 1, 2, 3), axes_type=("pixel", "pixel", "pixel", "pixel"), axes_names=("x", "y", "z", "s"), unit=(u.pix, u.pix, u.pix, u.pix)) return gwcs.wcs.WCS(forward_transform=identity, output_frame=frame, input_frame=detector_frame)
def _generate_wcs_transform(dispaxis): """Create mock gwcs.WCS object for resampled s2d data""" detector = cf.Frame2D(name='detector', axes_order=(0, 1), unit=(u.pix, u.pix)) icrs = cf.CelestialFrame(name='icrs', reference_frame=coord.ICRS(), axes_order=(0, 1), unit=(u.deg, u.deg), axes_names=('RA', 'DEC')) spec = cf.SpectralFrame(name='spec', axes_order=(2, ), unit=(u.micron, ), axes_names=('lambda', )) world = cf.CompositeFrame(name="world", frames=[icrs, spec]) if dispaxis == 1: mapping = models.Mapping((0, 1, 0)) if dispaxis == 2: mapping = models.Mapping((0, 1, 1)) transform = mapping | (models.Const1D(42) & models.Const1D(42) & (models.Shift(30) | models.Scale(0.1))) pipeline = [(detector, transform), (world, None)] wcs = WCS(pipeline) return wcs
def test_composite_frame(tmpdir): icrs = coord.ICRS() fk5 = coord.FK5() cel1 = cf.CelestialFrame(reference_frame=icrs) cel2 = cf.CelestialFrame(reference_frame=fk5) spec1 = cf.SpectralFrame(name='freq', unit=[ u.Hz, ], axes_order=(2, )) spec2 = cf.SpectralFrame(name='wave', unit=[ u.m, ], axes_order=(2, )) comp1 = cf.CompositeFrame([cel1, spec1]) comp2 = cf.CompositeFrame([cel2, spec2]) comp = cf.CompositeFrame( [comp1, cf.SpectralFrame(axes_order=(3, ), unit=(u.m, ))]) tree = {'comp1': comp1, 'comp2': comp2, 'comp': comp} helpers.assert_roundtrip_tree(tree, tmpdir)
def create_frames(): """ Create the coordinate frames in the NIRSPEC WCS pipeline. These are "detector", "gwa", "slit_frame", "msa_frame", "oteip", "v2v3", "world". """ det = cf.Frame2D(name='detector', axes_order=(0, 1)) gwa = cf.Frame2D(name="gwa", axes_order=(0, 1), unit=(u.rad, u.rad), axes_names=('alpha_in', 'beta_in')) msa_spatial = cf.Frame2D(name='msa_spatial', axes_order=(0, 1), unit=(u.m, u.m), axes_names=('x_msa', 'y_msa')) slit_spatial = cf.Frame2D(name='slit_spatial', axes_order=(0, 1), unit=("", ""), axes_names=('x_slit', 'y_slit')) #sky = cf.CelestialFrame(name='sky', axes_order=(0, 1), reference_frame=coord.ICRS()) spec = cf.SpectralFrame(name='spectral', axes_order=(2, ), unit=(u.m, ), axes_names=('wavelength', )) v2v3_spatial = cf.Frame2D(name='V2V3_spatial', axes_order=(0, 1), unit=(u.arcsec, u.arcsec), axes_names=('V2', 'V3')) v2v3 = cf.CompositeFrame([v2v3_spatial, spec], name='v2v3') slit_frame = cf.CompositeFrame([slit_spatial, spec], name='slit_frame') msa_frame = cf.CompositeFrame([msa_spatial, spec], name='msa_frame') oteip_spatial = cf.Frame2D(name='OTEIP_spatial', axes_order=(0, 1), unit=(u.arcsec, u.arcsec), axes_names=('X_OTEIP', 'Y_OTEIP')) oteip = cf.CompositeFrame([oteip_spatial, spec], name='oteip') #world = cf.CompositeFrame([sky, spec], name='world') return det, gwa, slit_frame, msa_frame, oteip, v2v3
def frame_from_model(wcsinfo): """ Initialize a coordinate frame based on values in model.meta.wcsinfo. Parameters ---------- wcsinfo : `~stdatamodels.DataModel` or dict Either one of the JWST data models or a dict with model.meta.wcsinfo. Returns ------- frame : `~coordinate_frames.CoordinateFrame` """ if isinstance(wcsinfo, DataModel): wcsinfo = wcsinfo_from_model(wcsinfo) wcsaxes = wcsinfo['WCSAXES'] celestial_axes, spectral_axes, other = gwutils.get_axes(wcsinfo) cunit = wcsinfo['CUNIT'] frames = [] if celestial_axes: ref_frame = coords.ICRS() celestial = cf.CelestialFrame(name='sky', axes_order=tuple(celestial_axes), reference_frame=ref_frame, unit=cunit[celestial_axes], axes_names=('RA', 'DEC')) frames.append(celestial) if spectral_axes: spec = cf.SpectralFrame(name='spectral', axes_order=tuple(spectral_axes), unit=cunit[spectral_axes], axes_names=('wavelength', )) frames.append(spec) if other: # Make sure these are strings and not np.str_ objects. axes_names = tuple([str(name) for name in wcsinfo['CTYPE'][other]]) name = "_".join(wcsinfo['CTYPE'][other]) spatial = cf.Frame2D(name=name, axes_order=tuple(other), unit=cunit[other], axes_names=axes_names) frames.append(spatial) if wcsaxes == 2: return frames[0] elif wcsaxes == 3: world = cf.CompositeFrame(frames, name='world') return world else: raise ValueError("WCSAXES can be 2 or 3, got {0}".format(wcsaxes))
def lrs(input_model, reference_files): """ Create the WCS pipeline for a MIRI fixed slit observation. reference_files = {"specwcs": 'MIRI_FM_MIRIMAGE_P750L_DISTORTION_04.02.00.fits'} """ detector = cf.Frame2D(name='detector', axes_order=(0, 1), unit=(u.pix, u.pix)) focal_spatial = cf.Frame2D(name='focal', axes_order=(0, 1), unit=(u.arcmin, u.arcmin)) sky = cf.CelestialFrame(reference_frame=coord.ICRS()) spec = cf.SpectralFrame(name='wavelength', axes_order=(2, ), unit=(u.micron, ), axes_names=('lambda', )) focal = cf.CompositeFrame([focal_spatial, spec]) ref = fits.open(reference_files['specwcs']) ldata = ref[1].data if input_model.meta.exposure.type.lower() == 'mir_lrs-fixedslit': zero_point = ref[1].header['imx'], ref[1].header['imy'] elif input_model.meta.exposure.type.lower() == 'mir_lrs-slitless': #zero_point = ref[1].header['imysltl'], ref[1].header['imxsltl'] #zero point in reference file is wrong # This should eb moved eventually to the reference file. zero_point = [35, 442] #[35, 763] # account for subarray lrsdata = np.array([l for l in ldata]) x0 = lrsdata[:, 3] x1 = lrsdata[:, 5] y0 = lrsdata[:, 4] domain = [{ 'lower': x0.min() + zero_point[0], 'upper': x1.max() + zero_point[0] }, { 'lower': (y0.min() + zero_point[1]), 'upper': (y0.max() + zero_point[1]) }] log.info("Setting domain to {0}".format(domain)) lrs_wav_model = jwmodels.LRSWavelength(lrsdata, zero_point) ref.close() angle = np.arctan(0.00421924) spatial = models.Rotation2D(angle) det2focal = models.Mapping((0, 1, 0, 1)) | spatial & lrs_wav_model det2focal.meta['domain'] = domain pipeline = [(detector, det2focal), (focal, None)] #(sky, None) #] return pipeline
def gwcs_3d(): detector_frame = cf.CoordinateFrame( name="detector", naxes=3, axes_order=(0, 1, 2), axes_type=("pixel", "pixel", "pixel"), axes_names=("x", "y", "z"), unit=(u.pix, u.pix, u.pix)) sky_frame = cf.CelestialFrame(reference_frame=Helioprojective(), name='hpc') spec_frame = cf.SpectralFrame(name="spectral", axes_order=(2, ), unit=u.nm) out_frame = cf.CompositeFrame(frames=(sky_frame, spec_frame)) return WCS(forward_transform=spatial_like_model(), input_frame=detector_frame, output_frame=out_frame)
def create_spectral_wcs(ra, dec, wavelength): """Assign a WCS for sky coordinates and a table of wavelengths Parameters ---------- ra: float The right ascension (in degrees) at the nominal location of the entrance aperture (slit). dec: float The declination (in degrees) at the nominal location of the entrance aperture. wavelength: ndarray The wavelength in microns at each pixel of the extracted spectrum. Returns: -------- wcs: a gwcs.wcs.WCS object This takes a float or sequence of float and returns a tuple of the right ascension, declination, and wavelength (or sequence of wavelengths) at the pixel(s) specified by the input argument. """ # Only the first coordinate is used. input_frame = cf.Frame2D(axes_order=(0, 1), unit=(u.pix, u.pix), name="pixel_frame") sky = cf.CelestialFrame(name='sky', axes_order=(0, 1), reference_frame=coord.ICRS()) spec = cf.SpectralFrame(name='spectral', axes_order=(2, ), unit=(u.micron, ), axes_names=('wavelength', )) pixel = np.arange(len(wavelength), dtype=np.float) tab = Mapping((0, 0, 0)) | \ Const1D(ra) & Const1D(dec) & Tabular1D(pixel, wavelength) world = cf.CompositeFrame([sky, spec], name='world') pipeline = [(input_frame, tab), (world, None)] return WCS(pipeline)
def niriss_soss(input_model, reference_files): """ The NIRISS SOSS pipeline includes 3 coordinate frames - detector, focal plane and sky reference_files={'specwcs': 'soss_wavelengths_configuration.asdf'} """ # Get the target RA and DEC, they will be used for setting the WCS RA and DEC based on a conversation # with Kevin Volk. try: target_ra = float(input_model['meta.target.ra']) target_dec = float(input_model['meta.target.dec']) except: # There was an error getting the target RA and DEC, so we are not going to continue. raise ValueError('Problem getting the TARG_RA or TARG_DEC from input model {}'.format(input_model)) # Define the frames detector = cf.Frame2D(name='detector', axes_order=(0, 1), unit=(u.pix, u.pix)) spec = cf.SpectralFrame(name='spectral', axes_order=(2,), unit=(u.micron,), axes_names=('wavelength',)) sky = cf.CelestialFrame(reference_frame=coord.ICRS(), axes_names=('ra', 'dec'), axes_order=(0, 1), unit=(u.deg, u.deg), name='sky') world = cf.CompositeFrame([sky, spec], name='world') try: with AsdfFile.open(reference_files['specwcs']) as wl: wl1 = wl.tree[1].copy() wl2 = wl.tree[2].copy() wl3 = wl.tree[3].copy() except Exception as e: raise IOError('Error reading wavelength correction from {}'.format(reference_files['specwcs'])) cm_order1 = (Mapping((0, 1, 0, 1)) | (Const1D(target_ra) & Const1D(target_dec) & wl1)).rename('Order1') cm_order2 = (Mapping((0, 1, 0, 1)) | (Const1D(target_ra) & Const1D(target_dec) & wl2)).rename('Order2') cm_order3 = (Mapping((0, 1, 0, 1)) | (Const1D(target_ra) & Const1D(target_dec) & wl3)).rename('Order3') # Define the transforms, they should accept (x,y) and return (ra, dec, lambda) soss_model = NirissSOSSModel([1, 2, 3], [cm_order1, cm_order2, cm_order3]).rename('3-order SOSS Model') # Define the pipeline based on the frames and models above. pipeline = [(detector, soss_model), (world, None) ] return pipeline
def frame(self): """ The gWCS coordinate frame for all the lookup tables. """ if len(self._table_coords) == 1: return self._table_coords[0].frame else: frames = [t.frame for t in self._table_coords] # We now have to set the axes_order of all the frames so that we # have one consistent WCS with the correct number of pixel # dimensions. ind = 0 for f in frames: new_ind = ind + f.naxes f._axes_order = tuple(range(ind, new_ind)) ind = new_ind return cf.CompositeFrame(frames)
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 niriss_soss_set_input(model, order_number): """ Extract a WCS fr a specific spectral order. Parameters ---------- model : `~jwst.datamodels.ImageModel` An instance of an ImageModel order_number : int the spectral order Returns ------- WCS - the WCS corresponding to the spectral order. """ # Make sure the spectral order is available. if order_number < 1 or order_number > 3: raise ValueError('Order must be between 1 and 3') # Return the correct transform based on the order_number obj = model.meta.wcs.forward_transform.get_model(order_number) # use the size of the input subarray detector = cf.Frame2D(name='detector', axes_order=(0, 1), unit=(u.pix, u.pix)) spec = cf.SpectralFrame(name='spectral', axes_order=(2, ), unit=(u.micron, ), axes_names=('wavelength', )) sky = cf.CelestialFrame(reference_frame=coord.ICRS(), axes_names=('ra', 'dec'), axes_order=(0, 1), unit=(u.deg, u.deg), name='sky') world = cf.CompositeFrame([sky, spec], name='world') pipeline = [(detector, obj), (world, None)] return wcs.WCS(pipeline)
def frame_from_model(wcsinfo): wcsaxes = wcsinfo['WCSAXES'] spatial_axes, spectral_axes = gwutils.get_axes(wcsinfo) cunit = wcsinfo['CUNIT'] ref_frame = coords.ICRS() sky = cf.CelestialFrame(name='sky', axes_order=tuple(spatial_axes), reference_frame=ref_frame, unit=cunit[spatial_axes], axes_names=('RA', 'DEC')) if wcsaxes == 2: return sky elif wcsaxes == 3: spec = cf.SpectralFrame(name='spectral', axes_order=tuple(spectral_axes), unit=cunit[spectral_axes[0]], axes_names=('wavelength', )) world = cf.CompositeFrame([sky, spec], name='world') return world else: raise ValueError("WCSAXES can be 2 or 3, got {0}".format(wcsaxes))
def identity_gwcs_4d(): """ A simple 1-1 gwcs that converts from pixels to arcseconds """ identity = (TwoDScale(1 * u.arcsec / u.pixel) & m.Multiply(1 * u.nm / u.pixel) & m.Multiply(1 * u.s / u.pixel)) sky_frame = cf.CelestialFrame( axes_order=(0, 1), name='helioprojective', reference_frame=Helioprojective(obstime="2018-01-01"), unit=(u.arcsec, u.arcsec), axis_physical_types=("custom:pos.helioprojective.lon", "custom:pos.helioprojective.lat")) wave_frame = cf.SpectralFrame(axes_order=(2, ), unit=u.nm) time_frame = cf.TemporalFrame(Time("2020-01-01T00:00", format="isot", scale="utc"), axes_order=(3, ), unit=u.s) frame = cf.CompositeFrame([sky_frame, wave_frame, time_frame]) detector_frame = cf.CoordinateFrame(name="detector", naxes=4, axes_order=(0, 1, 2, 3), axes_type=("pixel", "pixel", "pixel", "pixel"), axes_names=("x", "y", "z", "s"), unit=(u.pix, u.pix, u.pix, u.pix)) wcs = gwcs.wcs.WCS(forward_transform=identity, output_frame=frame, input_frame=detector_frame) wcs.pixel_shape = (10, 20, 30, 40) wcs.array_shape = wcs.pixel_shape[::-1] return wcs
def test_composite_many_base_frame(): q_frame_1 = cf.CoordinateFrame(name='distance', axes_order=(0, ), naxes=1, axes_type="SPATIAL", unit=(u.m, )) q_frame_2 = cf.CoordinateFrame(name='distance', axes_order=(1, ), naxes=1, axes_type="SPATIAL", unit=(u.m, )) frame = cf.CompositeFrame([q_frame_1, q_frame_2]) wao_classes = frame._world_axis_object_classes assert len(wao_classes) == 2 assert not set(wao_classes.keys()).difference({"SPATIAL", "SPATIAL1"}) wao_components = frame._world_axis_object_components assert len(wao_components) == 2 assert not {c[0] for c in wao_components}.difference({"SPATIAL", "SPATIAL1"})
def identity_gwcs_5d_stokes(identity_gwcs_4d): stokes_frame = cf.StokesFrame(axes_order=(4, )) stokes_model = generate_lookup_table([0, 1, 2, 3] * u.one, interpolation='nearest') transform = identity_gwcs_4d.forward_transform frame = cf.CompositeFrame(identity_gwcs_4d.output_frame.frames + [stokes_frame]) detector_frame = cf.CoordinateFrame(name="detector", naxes=5, axes_order=(0, 1, 2, 3, 4), axes_type=("pixel", "pixel", "pixel", "pixel", "pixel"), axes_names=("x", "y", "z", "t", "s"), unit=(u.pix, u.pix, u.pix, u.pix, u.pix)) wcs = gwcs.wcs.WCS(forward_transform=transform & stokes_model, output_frame=frame, input_frame=detector_frame) wcs.pixel_shape = (10, 20, 30, 40, 4) wcs.array_shape = wcs.pixel_shape[::-1] return wcs
def build_interpolated_output_wcs(self, refmodel=None): """ Create a spatial/spectral WCS output frame using all the input models Creates output frame by linearly fitting RA, Dec along the slit and producing a lookup table to interpolate wavelengths in the dispersion direction. Parameters ---------- refmodel : `~jwst.datamodels.DataModel` The reference input image from which the fiducial WCS is created. If not specified, the first image in self.input_models is used. Returns ------- output_wcs : `~gwcs.WCS` object A gwcs WCS object defining the output frame WCS """ # for each input model convert slit x,y to ra,dec,lam # use first input model to set spatial scale # use center of appended ra and dec arrays to set up # center of final ra,dec # append all ra,dec, wavelength array for each slit # use first model to initialize wavelenth array # append wavelengths that fall outside the endpoint of # of wavelength array when looping over additional data all_wavelength = [] all_ra_slit = [] all_dec_slit = [] for im, model in enumerate(self.input_models): wcs = model.meta.wcs bb = wcs.bounding_box grid = wcstools.grid_from_bounding_box(bb) ra, dec, lam = np.array(wcs(*grid)) spectral_axis = find_dispersion_axis(model) spatial_axis = spectral_axis ^ 1 # Compute the wavelength array, trimming NaNs from the ends # In many cases, a whole slice is NaNs, so ignore those warnings warnings.simplefilter("ignore") wavelength_array = np.nanmedian(lam, axis=spectral_axis) warnings.resetwarnings() wavelength_array = wavelength_array[~np.isnan(wavelength_array)] # We need to estimate the spatial sampling to use for the output WCS. # Tt is assumed the spatial sampling is the same for all the input # models. So we can use the first input model to set the spatial # sampling. # Steps to do this for first input model: # 1. find the middle of the spectrum in wavelength # 2. Pull out the ra and dec at the center of the slit. # 3. Find the mean ra,dec and the center of the slit this will # represent the tangent point # 4. Convert ra,dec -> tangent plane projection: x_tan,y_tan # 5. using x_tan, y_tan perform a linear fit to find spatial sampling # first input model sets intializes wavelength array and defines # the spatial scale of the output wcs if im == 0: for iw in wavelength_array: all_wavelength.append(iw) lam_center_index = int( (bb[spectral_axis][1] - bb[spectral_axis][0]) / 2) if spatial_axis == 0: ra_center = ra[lam_center_index, :] dec_center = dec[lam_center_index, :] else: ra_center = ra[:, lam_center_index] dec_center = dec[:, lam_center_index] # find the ra and dec for this slit using center of slit ra_center_pt = np.nanmean(ra_center) dec_center_pt = np.nanmean(dec_center) if resample_utils.is_sky_like(model.meta.wcs.output_frame): # convert ra and dec to tangent projection tan = Pix2Sky_TAN() native2celestial = RotateNative2Celestial( ra_center_pt, dec_center_pt, 180) undist2sky1 = tan | native2celestial # Filter out RuntimeWarnings due to computed NaNs in the WCS warnings.simplefilter("ignore") # at this center of slit find x,y tangent projection - x_tan, y_tan x_tan, y_tan = undist2sky1.inverse(ra, dec) warnings.resetwarnings() else: # for non sky-like output frames, no need to do tangent plane projections # but we still use the same variables x_tan, y_tan = ra, dec # pull out data from center if spectral_axis == 0: x_tan_array = x_tan.T[lam_center_index] y_tan_array = y_tan.T[lam_center_index] else: x_tan_array = x_tan[lam_center_index] y_tan_array = y_tan[lam_center_index] x_tan_array = x_tan_array[~np.isnan(x_tan_array)] y_tan_array = y_tan_array[~np.isnan(y_tan_array)] # estimate the spatial sampling fitter = LinearLSQFitter() fit_model = Linear1D() xstop = x_tan_array.shape[0] / self.pscale_ratio xstep = 1 / self.pscale_ratio ystop = y_tan_array.shape[0] / self.pscale_ratio ystep = 1 / self.pscale_ratio pix_to_xtan = fitter(fit_model, np.arange(0, xstop, xstep), x_tan_array) pix_to_ytan = fitter(fit_model, np.arange(0, ystop, ystep), y_tan_array) # append all ra and dec values to use later to find min and max # ra and dec ra_use = ra.flatten() ra_use = ra_use[~np.isnan(ra_use)] dec_use = dec.flatten() dec_use = dec_use[~np.isnan(dec_use)] all_ra_slit.append(ra_use) all_dec_slit.append(dec_use) # now check wavelength array to see if we need to add to it this_minw = np.min(wavelength_array) this_maxw = np.max(wavelength_array) all_minw = np.min(all_wavelength) all_maxw = np.max(all_wavelength) if this_minw < all_minw: addpts = wavelength_array[wavelength_array < all_minw] for ip in range(len(addpts)): all_wavelength.append(addpts[ip]) if this_maxw > all_maxw: addpts = wavelength_array[wavelength_array > all_maxw] for ip in range(len(addpts)): all_wavelength.append(addpts[ip]) # done looping over set of models all_ra = np.hstack(all_ra_slit) all_dec = np.hstack(all_dec_slit) all_wave = np.hstack(all_wavelength) all_wave = all_wave[~np.isnan(all_wave)] all_wave = np.sort(all_wave, axis=None) # Tabular interpolation model, pixels -> lambda wavelength_array = np.unique(all_wave) # Check if the data is MIRI LRS FIXED Slit. If it is then # the wavelength array needs to be flipped so that the resampled # dispersion direction matches the disperion direction on the detector. if self.input_models[0].meta.exposure.type == 'MIR_LRS-FIXEDSLIT': wavelength_array = np.flip(wavelength_array, axis=None) step = 1 / self.pscale_ratio stop = wavelength_array.shape[0] / self.pscale_ratio points = np.arange(0, stop, step) pix_to_wavelength = Tabular1D(points=points, lookup_table=wavelength_array, bounds_error=False, fill_value=None, name='pix2wavelength') # Tabular models need an inverse explicitly defined. # If the wavelength array is decending instead of ascending, both # points and lookup_table need to be reversed in the inverse transform # for scipy.interpolate to work properly points = wavelength_array lookup_table = np.arange(0, stop, step) if not np.all(np.diff(wavelength_array) > 0): points = points[::-1] lookup_table = lookup_table[::-1] pix_to_wavelength.inverse = Tabular1D(points=points, lookup_table=lookup_table, bounds_error=False, fill_value=None, name='wavelength2pix') # For the input mapping, duplicate the spatial coordinate mapping = Mapping((spatial_axis, spatial_axis, spectral_axis)) # Sometimes the slit is perpendicular to the RA or Dec axis. # For example, if the slit is perpendicular to RA, that means # the slope of pix_to_xtan will be nearly zero, so make sure # mapping.inverse uses pix_to_ytan.inverse. The auto definition # of mapping.inverse is to use the 2nd spatial coordinate, i.e. Dec. if np.isclose(pix_to_ytan.slope, 0, atol=1e-8): mapping_tuple = (0, 1) # Account for vertical or horizontal dispersion on detector if spatial_axis: mapping.inverse = Mapping(mapping_tuple[::-1]) else: mapping.inverse = Mapping(mapping_tuple) # The final transform # redefine the ra, dec center tangent point to include all data # check if all_ra crosses 0 degress - this makes it hard to # define the min and max ra correctly all_ra = wrap_ra(all_ra) ra_min = np.amin(all_ra) ra_max = np.amax(all_ra) ra_center_final = (ra_max + ra_min) / 2.0 dec_min = np.amin(all_dec) dec_max = np.amax(all_dec) dec_center_final = (dec_max + dec_min) / 2.0 tan = Pix2Sky_TAN() if len(self.input_models ) == 1: # single model use ra_center_pt to be consistent # with how resample was done before ra_center_final = ra_center_pt dec_center_final = dec_center_pt if resample_utils.is_sky_like(model.meta.wcs.output_frame): native2celestial = RotateNative2Celestial(ra_center_final, dec_center_final, 180) undist2sky = tan | native2celestial # find the spatial size of the output - same in x,y x_tan_all, _ = undist2sky.inverse(all_ra, all_dec) else: x_tan_all, _ = all_ra, all_dec x_min = np.amin(x_tan_all) x_max = np.amax(x_tan_all) x_size = int(np.ceil((x_max - x_min) / np.absolute(pix_to_xtan.slope))) # single model use size of x_tan_array # to be consistent with method before if len(self.input_models) == 1: x_size = len(x_tan_array) # define the output wcs if resample_utils.is_sky_like(model.meta.wcs.output_frame): transform = mapping | (pix_to_xtan & pix_to_ytan | undist2sky) & pix_to_wavelength else: transform = mapping | (pix_to_xtan & pix_to_ytan) & pix_to_wavelength det = cf.Frame2D(name='detector', axes_order=(0, 1)) if resample_utils.is_sky_like(model.meta.wcs.output_frame): sky = cf.CelestialFrame(name='sky', axes_order=(0, 1), reference_frame=coord.ICRS()) else: sky = cf.Frame2D( name=f'resampled_{model.meta.wcs.output_frame.name}', axes_order=(0, 1)) spec = cf.SpectralFrame(name='spectral', axes_order=(2, ), unit=(u.micron, ), axes_names=('wavelength', )) world = cf.CompositeFrame([sky, spec], name='world') pipeline = [(det, transform), (world, None)] output_wcs = WCS(pipeline) # import ipdb; ipdb.set_trace() # compute the output array size in WCS axes order, i.e. (x, y) output_array_size = [0, 0] output_array_size[spectral_axis] = int( np.ceil(len(wavelength_array) / self.pscale_ratio)) output_array_size[spatial_axis] = int( np.ceil(x_size / self.pscale_ratio)) # turn the size into a numpy shape in (y, x) order self.data_size = tuple(output_array_size[::-1]) bounding_box = resample_utils.wcs_bbox_from_shape(self.data_size) output_wcs.bounding_box = bounding_box return output_wcs
def test_round_trip_gwcs(tmpdir): """ Add a 2-step gWCS instance to NDAstroData, save to disk, reload & compare. """ from gwcs import coordinate_frames as cf from gwcs import WCS arr = np.zeros((10, 10), dtype=np.float32) ad1 = astrodata.create(fits.PrimaryHDU(), [fits.ImageHDU(arr, name='SCI')]) # Transformation from detector pixels to pixels in some reference row, # removing relative distortions in wavelength: det_frame = cf.Frame2D(name='det_mosaic', axes_names=('x', 'y'), unit=(u.pix, u.pix)) dref_frame = cf.Frame2D(name='dist_ref_row', axes_names=('xref', 'y'), unit=(u.pix, u.pix)) # A made-up example model that looks vaguely like some real distortions: fdist = models.Chebyshev2D(2, 2, c0_0=4.81125, c1_0=5.43375, c0_1=-0.135, c1_1=-0.405, c0_2=0.30375, c1_2=0.91125, x_domain=[0., 9.], y_domain=[0., 9.]) # This is not an accurate inverse, but will do for this test: idist = models.Chebyshev2D(2, 2, c0_0=4.89062675, c1_0=5.68581232, c2_0=-0.00590263, c0_1=0.11755526, c1_1=0.35652358, c2_1=-0.01193828, c0_2=-0.29996306, c1_2=-0.91823397, c2_2=0.02390594, x_domain=[-1.5, 12.], y_domain=[0., 9.]) # The resulting 2D co-ordinate mapping from detector to ref row pixels: distrans = models.Mapping((0, 1, 1)) | (fdist & models.Identity(1)) distrans.inverse = models.Mapping((0, 1, 1)) | (idist & models.Identity(1)) # Transformation from reference row pixels to linear, row-stacked spectra: spec_frame = cf.SpectralFrame(axes_order=(0, ), unit=u.nm, axes_names='lambda', name='wavelength') row_frame = cf.CoordinateFrame(1, 'SPATIAL', axes_order=(1, ), unit=u.pix, axes_names='y', name='row') rss_frame = cf.CompositeFrame([spec_frame, row_frame]) # Toy wavelength model & approximate inverse: fwcal = models.Chebyshev1D(2, c0=500.075, c1=0.05, c2=0.001, domain=[0, 9]) iwcal = models.Chebyshev1D(2, c0=4.59006292, c1=4.49601817, c2=-0.08989608, domain=[500.026, 500.126]) # The resulting 2D co-ordinate mapping from ref pixels to wavelength: wavtrans = fwcal & models.Identity(1) wavtrans.inverse = iwcal & models.Identity(1) # The complete WCS chain for these 2 transformation steps: ad1[0].nddata.wcs = WCS([(det_frame, distrans), (dref_frame, wavtrans), (rss_frame, None)]) # Save & re-load the AstroData instance with its new WCS attribute: testfile = str(tmpdir.join('round_trip_gwcs.fits')) ad1.write(testfile) ad2 = astrodata.open(testfile) wcs1 = ad1[0].nddata.wcs wcs2 = ad2[0].nddata.wcs # # Temporary workaround for issue #9809, to ensure the test is correct: # wcs2.forward_transform[1].x_domain = (0, 9) # wcs2.forward_transform[1].y_domain = (0, 9) # wcs2.forward_transform[3].domain = (0, 9) # wcs2.backward_transform[0].domain = (500.026, 500.126) # wcs2.backward_transform[3].x_domain = (-1.5, 12.) # wcs2.backward_transform[3].y_domain = (0, 9) # Did we actually get a gWCS instance back? assert isinstance(wcs2, WCS) # Do the transforms have the same number of submodels, with the same types, # degrees, domains & parameters? Here the inverse gets checked redundantly # as both backward_transform and forward_transform.inverse, but it would be # convoluted to ensure that both are correct otherwise (since the transforms # get regenerated as new compound models each time they are accessed). compare_models(wcs1.forward_transform, wcs2.forward_transform) compare_models(wcs1.backward_transform, wcs2.backward_transform) # Do the instances have matching co-ordinate frames? for f in wcs1.available_frames: assert repr(getattr(wcs1, f)) == repr(getattr(wcs2, f)) # Also compare a few transformed values, as the "proof of the pudding": y, x = np.mgrid[0:9:2, 0:9:2] np.testing.assert_allclose(wcs1(x, y), wcs2(x, y), rtol=1e-7, atol=0.) y, w = np.mgrid[0:9:2, 500.025:500.12:0.0225] np.testing.assert_allclose(wcs1.invert(w, y), wcs2.invert(w, y), rtol=1e-7, atol=0.)
def main(): path = Path('~/sunpy/data/jsocflare/').expanduser() files = glob.glob(str(path / '*.fits')) # requestid = 'JSOC_20180831_1097' requestid = None if not files: if requestid: c = JSOCClient() filesd = c.get_request(requestid, path=str(path), overwrite=False).wait() files = [] for f in filesd.values(): files.append(f['path']) else: results = Fido.search( a.jsoc.Time('2017-09-06T12:00:00', '2017-09-06T12:02:00'), a.jsoc.Series('aia.lev1_euv_12s'), a.jsoc.Segment('image'), a.jsoc.Notify("*****@*****.**")) print(results) files = Fido.fetch(results, path=str(path)) files.sort() files = np.array(files) # For each image get: # the index inds = [] # the time times = [] # the dt from the first image seconds = [] # the wavelength waves = [] for i, filepath in enumerate(files): with fits.open(filepath) as hdul: header = hdul[1].header time = parse_time(header['DATE-OBS']) if i == 0: root_header = header start_time = time inds.append(i) times.append(time) seconds.append((time - start_time).total_seconds()) waves.append(header['WAVELNTH']) # Construct an array and sort it by wavelength and time arr = np.array((inds, seconds, waves)).T sorter = np.lexsort((arr[:, 1], arr[:, 2])) # Using this double-sorted array get the list indicies list_sorter = np.array(arr[sorter][:, 0], dtype=int) # Calculate the desired shape of the output array n_waves = len(list(set(waves))) shape = (n_waves, len(files) // n_waves) # Construct a 2D array of filenames cube = files[list_sorter].reshape(shape) # Extract a list of coordinates in time and wavelength # this assumes all wavelength images are taken at the same time time_coords = np.array([t.isoformat() for t in times])[list_sorter].reshape(shape)[0, :] wave_coords = np.array(waves)[list_sorter].reshape(shape)[:, 0] smap0 = sunpy.map.Map(files[0]) spatial = map_to_transform(smap0) timemodel = generate_lookup_table(lookup_table=seconds[:shape[1]] * u.s) wavemodel = generate_lookup_table(lookup_table=waves[:shape[0]] * u.AA) hcubemodel = spatial & timemodel & wavemodel wave_frame = cf.SpectralFrame(axes_order=(3, ), unit=u.AA, name="wavelength", axes_names=("wavelength", )) time_frame = cf.TemporalFrame(axes_order=(2, ), unit=u.s, reference_time=Time(time_coords[0]), name="time", axes_names=("time", )) sky_frame = cf.CelestialFrame(axes_order=(0, 1), name='helioprojective', reference_frame=smap0.coordinate_frame, axes_names=("helioprojective longitude", "helioprojective latitude")) sky_frame = cf.CompositeFrame([sky_frame, time_frame, wave_frame]) detector_frame = cf.CoordinateFrame(name="detector", naxes=4, axes_order=(0, 1, 2, 3), axes_type=("pixel", "pixel", "pixel", "pixel"), axes_names=("x", "y", "time", "wavelength"), unit=(u.pix, u.pix, u.pix, u.pix)) wcs = gwcs.wcs.WCS(forward_transform=hcubemodel, input_frame=detector_frame, output_frame=sky_frame) print(repr(wcs)) print(wcs(*[1 * u.pix] * 4, with_units=True)) ea = references_from_filenames(cube, relative_to=str(path)) tree = { 'gwcs': wcs, 'dataset': ea, } with asdf.AsdfFile(tree) as ff: # ff.write_to("test.asdf") filename = str(path / "aia_{}.asdf".format(time_coords[0])) ff.write_to(filename) print("Saved to : {}".format(filename)) # import sys; sys.exit(0) from dkist.dataset import Dataset ds = Dataset.from_directory(str(path)) print(repr(ds)) print(repr(ds.wcs)) print(ds.wcs(*[1 * u.pix] * 4, with_units=True))