def fitswcs_nonlinear(header): """ Create a WCS linear transform from a FITS header. Parameters ---------- header : astropy.io.fits.Header or dict FITS Header or dict with basic FITS WCS keywords. """ if isinstance(header, fits.Header): wcs_info = read_wcs_from_header(header) elif isinstance(header, dict): wcs_info = header else: raise TypeError("Expected a FITS Header or a dict.") projcode = get_projcode(wcs_info) projection = create_projection_transform(projcode).rename(projcode) # Create the sky rotation transform sky_axes, _ = get_axes(wcs_info) phip, lonp = [wcs_info['CRVAL'][i] for i in sky_axes] # TODO: write "def compute_lonpole(projcode, l)" # Set a defaul tvalue for now thetap = 180 n2c = astmodels.RotateNative2Celestial(phip, lonp, thetap, name="crval") return projection | n2c
def time_init_7_no_units(): m = (models.Shift(-10.5) & models.Shift(-13.2) | models.AffineTransformation2D(matrix=[[1, 0], [0, 1]], translation=[0, 0]) | models.Scale(.01) & models.Scale(.04) | models.Pix2Sky_TAN() | models.RotateNative2Celestial(5.6, -72.05, 180))
def test_roundtrip_sky_rotaion(inp): lon, lat, lon_pole = 42 * u.deg, (43 * u.deg).to( u.arcsec), (44 * u.deg).to(u.rad) n2c = models.RotateNative2Celestial(lon, lat, lon_pole) c2n = models.RotateCelestial2Native(lon, lat, lon_pole) assert_quantity_allclose(n2c.inverse(*n2c(*inp)), inp, atol=1e-13 * u.deg) assert_quantity_allclose(c2n.inverse(*c2n(*inp)), inp, atol=1e-13 * u.deg)
def gwcs_simple_imaging_units(): shift_by_crpix = models.Shift(-2048 * u.pix) & models.Shift(-1024 * u.pix) matrix = np.array([[1.290551569736E-05, 5.9525007864732E-06], [5.0226382102765E-06, -1.2644844123757E-05]]) rotation = models.AffineTransformation2D(matrix * u.deg, translation=[0, 0] * u.deg) rotation.input_units_equivalencies = { "x": u.pixel_scale(1 * u.deg / u.pix), "y": u.pixel_scale(1 * u.deg / u.pix) } rotation.inverse = models.AffineTransformation2D( np.linalg.inv(matrix) * u.pix, translation=[0, 0] * u.pix) rotation.inverse.input_units_equivalencies = { "x": u.pixel_scale(1 * u.pix / u.deg), "y": u.pixel_scale(1 * u.pix / u.deg) } tan = models.Pix2Sky_TAN() celestial_rotation = models.RotateNative2Celestial(5.63056810618 * u.deg, -72.05457184279 * u.deg, 180 * u.deg) det2sky = shift_by_crpix | rotation | tan | celestial_rotation det2sky.name = "linear_transform" detector_frame = cf.Frame2D(name="detector", axes_names=("x", "y"), unit=(u.pix, u.pix)) sky_frame = cf.CelestialFrame(reference_frame=coord.ICRS(), name='icrs', unit=(u.deg, u.deg)) pipeline = [(detector_frame, det2sky), (sky_frame, None)] return wcs.WCS(pipeline)
def setup(self): aff = models.AffineTransformation2D(matrix=[[1, 0], [0, 1]], translation=[0, 0]) self.model = (models.Shift(-10.5) & models.Shift(-13.2) | aff | models.Scale(.01) & models.Scale(.04) | models.Pix2Sky_TAN() | models.RotateNative2Celestial(5.6, -72.05, 180))
def test_simple_gwcs(): # https://gwcs.readthedocs.io/en/latest/#getting-started shift_by_crpix = models.Shift(-(2048 - 1) * u.pix) & models.Shift(-(1024 - 1) * u.pix) matrix = np.array([[1.290551569736E-05, 5.9525007864732E-06], [5.0226382102765E-06, -1.2644844123757E-05]]) rotation = models.AffineTransformation2D(matrix * u.deg, translation=[0, 0] * u.deg) rotation.input_units_equivalencies = {"x": u.pixel_scale(1 * (u.deg / u.pix)), "y": u.pixel_scale(1 * (u.deg / u.pix))} rotation.inverse = models.AffineTransformation2D(np.linalg.inv(matrix) * u.pix, translation=[0, 0] * u.pix) rotation.inverse.input_units_equivalencies = {"x": u.pixel_scale(1 * (u.pix / u.deg)), "y": u.pixel_scale(1 * (u.pix / u.deg))} tan = models.Pix2Sky_TAN() celestial_rotation = models.RotateNative2Celestial( 5.63056810618 * u.deg, -72.05457184279 * u.deg, 180 * u.deg) det2sky = shift_by_crpix | rotation | tan | celestial_rotation det2sky.name = "linear_transform" detector_frame = cf.Frame2D(name="detector", axes_names=("x", "y"), unit=(u.pix, u.pix)) sky_frame = cf.CelestialFrame(reference_frame=ICRS(), name='icrs', unit=(u.deg, u.deg)) pipeline = [(detector_frame, det2sky), (sky_frame, None)] w = gwcs.wcs.WCS(pipeline) result = wcs_utils.get_compass_info(w, (1024, 2048), r_fac=0.25) assert_allclose(result[:-1], (1024.0, 512.0, 1131.0265005852038, 279.446189124443, 1262.0057201165127, 606.2863901330095, 155.2870478938214, -86.89813081941797)) assert not result[-1]
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 test1(tmpdir, ret=False): shift_by_crpix = models.Shift(-2048 * u.pix) & models.Shift(-1024 * u.pix) matrix = np.array([[1.290551569736E-05, 5.9525007864732E-06], [5.0226382102765E-06, -1.2644844123757E-05]]) rotation = models.AffineTransformation2D(matrix * u.deg, translation=[0, 0] * u.deg) rotation.input_units_equivalencies = { "x": u.pixel_scale(1 * u.deg / u.pix), "y": u.pixel_scale(1 * u.deg / u.pix) } rotation.inverse = models.AffineTransformation2D( np.linalg.inv(matrix) * u.pix, translation=[0, 0] * u.pix) rotation.inverse.input_units_equivalencies = { "x": u.pixel_scale(1 * u.pix / u.deg), "y": u.pixel_scale(1 * u.pix / u.deg) } tan = models.Pix2Sky_TAN() celestial_rotation = models.RotateNative2Celestial(5.63056810618 * u.deg, -72.05457184279 * u.deg, 180 * u.deg) det2sky = shift_by_crpix | rotation | tan | celestial_rotation det2sky.name = "linear_transform" detector_frame = cf.Frame2D(name="detector", axes_names=("x", "y"), unit=(u.pix, u.pix)) sky_frame = cf.CelestialFrame(reference_frame=coords.ICRS(), name='icrs', unit=(u.deg, u.deg)) pipeline = [(detector_frame, det2sky), (sky_frame, None)] wcsobj = gwcs.wcs.WCS(pipeline) wcs_set = WcsSet(default=wcsobj, extra=wcsobj) tree = {'wcs_set': wcs_set} if ret: return wcs_set helpers.assert_roundtrip_tree(tree, tmpdir)
def generate_celestial_transform(crpix: Union[Iterable[float], u.Quantity], cdelt: Union[Iterable[float], u.Quantity], pc: Union[ArrayLike, u.Quantity], crval: Union[Iterable[float], u.Quantity], lon_pole: Union[float, u.Quantity] = None, projection: Model = m.Pix2Sky_TAN()) -> CompoundModel: """ Create a simple celestial transform from FITS like parameters. Supports unitful or unitless parameters, but if any parameters have units all must have units, if parameters are unitless they are assumed to be in degrees. Parameters ---------- crpix The reference pixel (a length two array). crval The world coordinate at the reference pixel (a length two array). pc The rotation matrix for the affine transform. If specifying parameters with units this should have celestial (``u.deg``) units. lon_pole The longitude of the celestial pole, defaults to 180 degrees. projection The map projection to use, defaults to ``TAN``. Notes ----- This function has not been tested with more complex projections. Ensure that your lon_pole is correct for your projection. """ spatial_unit = None if hasattr(crval[0], "unit"): spatial_unit = crval[0].unit # TODO: Note this assumption is only valid for certain projections. if lon_pole is None: lon_pole = 180 if spatial_unit is not None: lon_pole = u.Quantity(lon_pole, unit=spatial_unit) # Make translation unitful if all parameters have units translation = (0, 0) if spatial_unit is not None: translation *= pc.unit # If we have units then we need to convert all things to Quantity # as they might be Parameter classes crpix = u.Quantity(crpix) cdelt = u.Quantity(cdelt) crval = u.Quantity(crval) lon_pole = u.Quantity(lon_pole) pc = u.Quantity(pc) shift = m.Shift(-crpix[0]) & m.Shift(-crpix[1]) scale = m.Multiply(cdelt[0]) & m.Multiply(cdelt[1]) rot = m.AffineTransformation2D(pc, translation=translation) skyrot = m.RotateNative2Celestial(crval[0], crval[1], lon_pole) return shift | scale | rot | projection | skyrot
def gwcs_7d_complex_mapping(): """ Useful features of this WCS (axes indices here are 0-based): - includes two celestial axes: input (0, 1) maps to world (2 - RA, 1 - Dec) - includes one separable frame with one axis: 4 -> 2 - includes one frame with 3 input and 4 output axes (1 degenerate), with separable world axes (3, 5) and (0, 6). """ offx = models.Shift(-64, name='x_translation') offy = models.Shift(-32, name='y_translation') cd = np.array([[1.2906, 0.59532], [0.50222, -1.2645]]) aff = models.AffineTransformation2D(matrix=1e-5 * cd, name='rotation') aff2 = models.AffineTransformation2D(matrix=cd, name='rotation2') wcslin = (offx & offy) | aff tan = models.Pix2Sky_TAN(name='tangent_projection') n2c = models.RotateNative2Celestial(5.630568, -72.0546, 180, name='skyrot') icrs = cf.CelestialFrame(reference_frame=coord.ICRS(), name='sky', axes_order=(2, 1)) spec = cf.SpectralFrame(name='wave', unit=[u.m], axes_order=(4, ), axes_names=('lambda', )) cmplx = cf.CoordinateFrame( name="complex", naxes=4, axes_order=(3, 5, 0, 6), axis_physical_types=(['em.wl', 'em.wl', 'time', 'time']), axes_type=("SPATIAL", "SPATIAL", "TIME", "TIME"), axes_names=("x", "y", "t", 'tau'), unit=(u.m, u.m, u.second, u.second)) comp_frm = cf.CompositeFrame(frames=[icrs, spec, cmplx], name='TEST 7D') wcs_forward = ( (wcslin & models.Shift(-3.14) & models.Scale(2.7) & aff2) | (tan & models.Identity(1) & models.Identity(1) & models.Identity(2)) | (n2c & models.Identity(1) & models.Identity(1) & models.Identity(2)) | models.Mapping((3, 1, 0, 4, 2, 5, 3))) detector_frame = cf.CoordinateFrame(name="detector", naxes=6, axes_order=(0, 1, 2, 3, 4, 5), axes_type=("pixel", "pixel", "pixel", "pixel", "pixel", "pixel"), unit=(u.pix, u.pix, u.pix, u.pix, u.pix, u.pix)) pipeline = [('detector', wcs_forward), (comp_frm, None)] w = wcs.WCS(forward_transform=wcs_forward, output_frame=comp_frm, input_frame=detector_frame) w.bounding_box = ((0, 15), (0, 31), (0, 20), (0, 10), (0, 10), (0, 1)) w.array_shape = (2, 11, 11, 21, 32, 16) w.pixel_shape = (16, 32, 21, 11, 11, 2) return w
def setup(self): aff = models.AffineTransformation2D(matrix=[[1, 0], [0, 1]] * u.arcsec, translation=[0, 0] * u.arcsec) aff.input_units_equivalencies = {'x': u.pixel_scale(1 * u.arcsec/u.pix), 'y': u.pixel_scale(1 * u.arcsec/u.pix)} self.model = (models.Shift(-10.5 * u.pix) & models.Shift(-13.2 * u.pix) | aff | models.Scale(.01 * u.arcsec) & models.Scale(.04 * u.deg) | models.Pix2Sky_TAN() | models.RotateNative2Celestial(5.6 * u.deg, -72.05 * u.deg, 180 * u.deg))
def test_to_fits_sip_pc_normalization(gwcs_simple_imaging_units, matrix_type): y, x = np.mgrid[:1024:10, :1024:10] xflat = np.ravel(x[1:-1, 1:-1]) yflat = np.ravel(y[1:-1, 1:-1]) bounding_box = ((0, 1024), (0, 1024)) # create a simple imaging WCS without distortions: cdmat = np.array([[1.29e-5, 5.95e-6], [5.02e-6, -1.26e-5]]) aff = models.AffineTransformation2D(matrix=cdmat, name='rotation') offx = models.Shift(-501, name='x_translation') offy = models.Shift(-501, name='y_translation') wcslin = (offx & offy) | aff n2c = models.RotateNative2Celestial(5.63, -72.05, 180, name='sky_rotation') tan = models.Pix2Sky_TAN(name='tangent_projection') wcs_forward = wcslin | tan | n2c sky_cs = cf.CelestialFrame(reference_frame=coord.ICRS(), name='sky') pipeline = [('detector', wcs_forward), (sky_cs, None)] wcs_lin = wcs.WCS( input_frame=cf.Frame2D(name='detector'), output_frame=sky_cs, forward_transform=pipeline ) _, _, celestial_group = wcs_lin._separable_groups(detect_celestial=True) fits_wcs = wcs_lin._to_fits_sip( celestial_group=celestial_group, keep_axis_position=False, bounding_box=bounding_box, max_pix_error=0.1, degree=None, max_inv_pix_error=0.1, inv_degree=None, npoints=32, crpix=None, projection='TAN', matrix_type=matrix_type, verbose=False ) fitssip = astwcs.WCS(fits_wcs) fitsvalx, fitsvaly = fitssip.wcs_pix2world(xflat, yflat, 0) inv_fitsvalx, inv_fitsvaly = fitssip.wcs_world2pix(fitsvalx, fitsvaly, 0) gwcsvalx, gwcsvaly = wcs_lin(xflat, yflat) assert_allclose(gwcsvalx, fitsvalx, atol=4e-11, rtol=0) assert_allclose(gwcsvaly, fitsvaly, atol=4e-11, rtol=0) assert_allclose(xflat, inv_fitsvalx, atol=5e-9, rtol=0) assert_allclose(yflat, inv_fitsvaly, atol=5e-9, rtol=0)
def gwcs_spec_cel_time_4d(): """ A complex 4D mixed celestial + spectral + time WCS. """ # spectroscopic frame: wave_model = models.Shift(-5) | models.Multiply(3.7) | models.Shift(20) wave_model.bounding_box = (7, 50) wave_frame = cf.SpectralFrame(name='wave', unit=u.m, axes_order=(0, ), axes_names=('lambda', )) # time frame: time_model = models.Identity(1) # models.Linear1D(10, 0) time_frame = cf.TemporalFrame(Time("2010-01-01T00:00"), name='time', unit=u.s, axes_order=(3, )) # Values from data/acs.hdr: crpix = (12, 13) crval = (5.63, -72.05) cd = [[1.291E-05, 5.9532E-06], [5.02215E-06, -1.2645E-05]] aff = models.AffineTransformation2D(matrix=cd, name='rotation') offx = models.Shift(-crpix[0], name='x_translation') offy = models.Shift(-crpix[1], name='y_translation') wcslin = models.Mapping((1, 0)) | (offx & offy) | aff tan = models.Pix2Sky_TAN(name='tangent_projection') n2c = models.RotateNative2Celestial(*crval, 180, name='sky_rotation') cel_model = wcslin | tan | n2c icrs = cf.CelestialFrame(reference_frame=coord.ICRS(), name='sky', axes_order=(2, 1)) wcs_forward = wave_model & cel_model & time_model comp_frm = cf.CompositeFrame(frames=[wave_frame, icrs, time_frame], name='TEST 4D FRAME') detector_frame = cf.CoordinateFrame(name="detector", naxes=4, axes_order=(0, 1, 2, 3), axes_type=("pixel", "pixel", "pixel", "pixel"), unit=(u.pix, u.pix, u.pix, u.pix)) w = wcs.WCS(forward_transform=wcs_forward, output_frame=comp_frm, input_frame=detector_frame) w.bounding_box = ((0, 63), (0, 127), (0, 255), (0, 9)) w.array_shape = (10, 256, 128, 64) w.pixel_shape = (64, 128, 256, 10) return w
def _sky_transform(skycoord, projection): """ A sky transform is a projection, followed by a rotation on the sky. """ _verify_projection(projection) lon_pole = _compute_lon_pole(skycoord, projection) if isinstance(skycoord, coord.SkyCoord): lon, lat = skycoord.spherical.lon, skycoord.spherical.lat else: lon, lat = skycoord sky_rotation = models.RotateNative2Celestial(lon, lat, lon_pole) return projection | sky_rotation
def load_wcs(input_model, reference_files={}): """ Create a gWCS object and store it in ``Model.meta``. Parameters ---------- input_model : `~jwst.datamodels.DataModel` The exposure. reference_files : dict A dict {reftype: reference_file_name} containing all reference files that apply to this exposure. """ if "wcsinfo" not in input_model.meta: input_model.meta.cal_step.assign_wcs = "SKIPPED" log.warning("assign_wcs: SKIPPED") return input_model else: output_model = input_model.copy() shift_by_crpix = models.Shift( -(input_model.meta.wcsinfo.crpix1 - 1) * u.pix ) & models.Shift(-(input_model.meta.wcsinfo.crpix2 - 1) * u.pix) pix2sky = getattr( models, "Pix2Sky_{}".format(input_model.meta.wcsinfo.ctype1[-3:]) )() celestial_rotation = models.RotateNative2Celestial( input_model.meta.wcsinfo.crval1 * u.deg, input_model.meta.wcsinfo.crval2 * u.deg, 180 * u.deg, ) pix2sky.input_units_equivalencies = { "x": u.pixel_scale(input_model.meta.wcsinfo.cdelt1 * u.deg / u.pix), "y": u.pixel_scale(input_model.meta.wcsinfo.cdelt2 * u.deg / u.pix), } det2sky = shift_by_crpix | pix2sky | celestial_rotation det2sky.name = "linear_transform" detector_frame = cf.Frame2D( name="detector", axes_names=("x", "y"), unit=(u.pix, u.pix) ) sky_frame = cf.CelestialFrame( reference_frame=getattr( coord, input_model.meta.coordinates.reference_frame )(), name="sky_frame", unit=(u.deg, u.deg), ) pipeline = [(detector_frame, det2sky), (sky_frame, None)] wcs = WCS(pipeline) output_model.meta.wcs = wcs output_model.meta.cal_step.assign_wcs = "COMPLETE" return output_model
def gwcs_cube_with_separable_time(request): """ A mixed celestial + time WCS. """ cube_size = (64, 32, 128) axes_order = request.param time_axes_order = (axes_order.index(2), ) cel_axes_order = (axes_order.index(0), axes_order.index(1)) detector_frame = cf.CoordinateFrame(name="detector", naxes=3, axes_order=(0, 1, 2), axes_type=("pixel", "pixel", "pixel"), unit=(u.pix, u.pix, u.pix)) # time frame: time_model = models.Identity(1) # models.Linear1D(10, 0) time_frame = cf.TemporalFrame(Time("2010-01-01T00:00"), name='time', unit=u.s, axes_order=time_axes_order) # Values from data/acs.hdr: crpix = (12, 13) crval = (5.63, -72.05) cd = [[1.291E-05, 5.9532E-06], [5.02215E-06, -1.2645E-05]] aff = models.AffineTransformation2D(matrix=cd, name='rotation') offx = models.Shift(-crpix[0], name='x_translation') offy = models.Shift(-crpix[1], name='y_translation') wcslin = models.Mapping((1, 0)) | (offx & offy) | aff tan = models.Pix2Sky_TAN(name='tangent_projection') n2c = models.RotateNative2Celestial(*crval, 180, name='sky_rotation') cel_model = wcslin | tan | n2c icrs = cf.CelestialFrame(reference_frame=coord.ICRS(), name='sky', axes_order=cel_axes_order) wcs_forward = (cel_model & time_model) | models.Mapping(axes_order) comp_frm = cf.CompositeFrame(frames=[icrs, time_frame], name='TEST 3D FRAME WITH TIME') w = wcs.WCS(forward_transform=wcs_forward, output_frame=comp_frm, input_frame=detector_frame) w.bounding_box = tuple((0, k - 1) for k in cube_size) w.pixel_shape = cube_size w.array_shape = w.pixel_shape[::-1] return w
def test_attributes(): n2c = models.RotateNative2Celestial(20016 * u.arcsec, -72.3 * u.deg, np.pi * u.rad) assert_allclose(n2c.lat.value, -72.3) assert_allclose(n2c.lat._raw_value, -1.2618730491919001) assert_allclose(n2c.lon.value, 20016) assert_allclose(n2c.lon._raw_value, 0.09704030641088472) assert_allclose(n2c.lon_pole.value, np.pi) assert_allclose(n2c.lon_pole._raw_value, np.pi) assert (n2c.lon.unit is u.Unit("arcsec")) assert (n2c._param_metrics['lon']['raw_unit'] is u.Unit("rad")) assert (n2c.lat.unit is u.Unit("deg")) assert (n2c._param_metrics['lat']['raw_unit'] is u.Unit("rad")) assert (n2c.lon_pole.unit is u.Unit("rad")) assert (n2c._param_metrics['lon_pole']['raw_unit'] is u.Unit("rad"))
def gwcs_cube_with_separable_spectral(request): cube_size = (128, 64, 100) axes_order = request.param spectral_axes_order = (axes_order.index(2), ) cel_axes_order = (axes_order.index(0), axes_order.index(1)) # Values from data/acs.hdr: crpix = (64, 32) crval = (5.63056810618, -72.0545718428) cd = [[1.29058667557984E-05, 5.95320245884555E-06], [5.02215195623825E-06, -1.2645010396976E-05]] aff = models.AffineTransformation2D(matrix=cd, name='rotation') offx = models.Shift(-crpix[0], name='x_translation') offy = models.Shift(-crpix[1], name='y_translation') wcslin = (offx & offy) | aff tan = models.Pix2Sky_TAN(name='tangent_projection') n2c = models.RotateNative2Celestial(*crval, 180, name='sky_rotation') icrs = cf.CelestialFrame(reference_frame=coord.ICRS(), name='sky', axes_order=cel_axes_order) spec = cf.SpectralFrame(name='wave', unit=[ u.m, ], axes_order=spectral_axes_order, axes_names=('lambda', )) comp_frm = cf.CompositeFrame(frames=[icrs, spec], name='TEST 3D FRAME WITH SPECTRAL AXIS') wcs_forward = ((wcslin & models.Identity(1)) | (tan & models.Identity(1)) | (n2c & models.Identity(1)) | models.Mapping(axes_order)) detector_frame = cf.CoordinateFrame(name="detector", naxes=3, axes_order=(0, 1, 2), axes_type=("pixel", "pixel", "pixel"), unit=(u.pix, u.pix, u.pix)) w = wcs.WCS(forward_transform=wcs_forward, output_frame=comp_frm, input_frame=detector_frame) w.bounding_box = tuple((0, k - 1) for k in cube_size) w.pixel_shape = cube_size w.array_shape = w.pixel_shape[::-1] return w, axes_order
def setup_class(self): hdr = fits.Header.fromtextfile(get_pkg_data_filename("data/acs.hdr"), endcard=False) #warnings.filterwarnings("ignore", message="^The WCS transformation has more axes (2)", # module="astropy.wcs.wcs") self.fitsw = astwcs.WCS(hdr) a_coeff = hdr['A_*'] a_order = a_coeff.pop('A_ORDER') b_coeff = hdr['B_*'] b_order = b_coeff.pop('B_ORDER') crpix = [hdr['CRPIX1'], hdr['CRPIX2']] distortion = models.SIP( crpix, a_order, b_order, a_coeff, b_coeff, name='sip_distorion') + models.Identity(2) cdmat = np.array([[hdr['CD1_1'], hdr['CD1_2']], [hdr['CD2_1'], hdr['CD2_2']]]) aff = models.AffineTransformation2D(matrix=cdmat, name='rotation') offx = models.Shift(-hdr['CRPIX1'], name='x_translation') offy = models.Shift(-hdr['CRPIX2'], name='y_translation') wcslin = (offx & offy) | aff phi = hdr['CRVAL1'] lon = hdr['CRVAL2'] theta = 180 n2c = models.RotateNative2Celestial(phi, lon, theta, name='sky_rotation') tan = models.Pix2Sky_TAN(name='tangent_projection') sky_cs = cf.CelestialFrame(reference_frame=coord.ICRS(), name='sky') det = cf.Frame2D('detector') wcs_forward = wcslin | tan | n2c pipeline = [('detector', distortion), ('focal', wcs_forward), (sky_cs, None)] self.wcs = wcs.WCS(input_frame=det, output_frame=sky_cs, forward_transform=pipeline) nx, ny = (5, 2) x = np.linspace(0, 1, nx) y = np.linspace(0, 1, ny) self.xv, self.yv = np.meshgrid(x, y)
def test_against_wcslib(inp): w = wcs.WCS() crval = [202.4823228, 47.17511893] w.wcs.crval = crval w.wcs.ctype = ['RA---TAN', 'DEC--TAN'] lonpole = 180 tan = models.Pix2Sky_TAN() n2c = models.RotateNative2Celestial(crval[0], crval[1], lonpole) c2n = models.RotateCelestial2Native(crval[0], crval[1], lonpole) m = tan | n2c minv = c2n | tan.inverse radec = w.wcs_pix2world(inp[0], inp[1], 1) xy = w.wcs_world2pix(radec[0], radec[1], 1) assert_allclose(m(*inp), radec, atol=1e-12) assert_allclose(minv(*radec), xy, atol=1e-12)
def create_asdf_ref_files(fname): #f = fits.open('../j94f05bgq_flt.fits') f = fits.open(fname) from gwcs import util whdr = util.read_wcs_from_header(f[1].header) crpix = whdr['CRPIX'] shift = models.Shift(crpix[0]) & models.Shift(crpix[1]) rotation = models.AffineTransformation2D(matrix=whdr['PC']) cdelt = whdr['CDELT'] scale = models.Scale(cdelt[0]) & models.Scale(cdelt[1]) #print util.get_projcode(whdr['CTYPE'][0]) tan = models.Pix2Sky_TAN() crval = whdr['CRVAL'] n2c = models.RotateNative2Celestial(crval[0], crval[1], 180) foc2sky = (shift | rotation | scale | tan | n2c).rename('foc2sky') fasdf = AsdfFile() fasdf.tree = {'model': foc2sky} fasdf.write_to('foc2sky.asdf')
def gwcs_3d_galactic_spectral(): """ This fixture has the axes ordered as lat, spectral, lon. """ # lat,wav,lon crpix1, crpix2, crpix3 = 29, 39, 44 crval1, crval2, crval3 = 10, 20, 25 cdelt1, cdelt2, cdelt3 = -0.01, 0.5, 0.01 shift = models.Shift(-crpix3) & models.Shift(-crpix1) scale = models.Multiply(cdelt3) & models.Multiply(cdelt1) proj = models.Pix2Sky_CAR() skyrot = models.RotateNative2Celestial(crval3, 90 + crval1, 180) celestial = shift | scale | proj | skyrot wave_model = models.Shift(-crpix2) | models.Multiply( cdelt2) | models.Shift(crval2) transform = models.Mapping( (2, 0, 1)) | celestial & wave_model | models.Mapping((1, 2, 0)) transform.bounding_box = ((5, 50), (-2, 45), (-1, 35)) sky_frame = cf.CelestialFrame(axes_order=(2, 0), reference_frame=coord.Galactic(), axes_names=("Longitude", "Latitude")) wave_frame = cf.SpectralFrame(axes_order=(1, ), unit=u.Hz, axes_names=("Frequency", )) 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"), unit=(u.pix, u.pix, u.pix)) owcs = wcs.WCS(forward_transform=transform, output_frame=frame, input_frame=detector_frame) owcs.array_shape = (30, 20, 10) owcs.pixel_shape = (10, 20, 30) return owcs
def from_file(cls, header, dist_json): """ header : fits.Header dist_json : json file """ if not isinstance(header, fits.Header): raise TypeError("Header must be a fits.Header object") fitswcs = util.read_wcs_from_header(header) wcs_linear = WCSLinearTransform.from_header(header) projection = create_projection_transform(fitswcs) # Create a RotateNative2Celestial transform using the Euler angles phip, lonp = fitswcs['CRVAL'] # Expand this - currently valid for zenithal projections only thetap = 180 # write "def compute_lonpole(projcode, l)" n2c = astmodels.RotateNative2Celestial(phip, lonp, thetap) if dist_json is not None: distortion = transform_from_json(dist_info['regions'][0]) else: distortion = None return cls(wcs_linear, projection, n2c, distortion)
return m1 test_models = [ astmodels.Identity(2), astmodels.Polynomial1D(2, c0=1, c1=2, c2=3), astmodels.Polynomial2D(1, c0_0=1, c0_1=2, c1_0=3), astmodels.Shift(2.), astmodels.Hermite1D(2, c0=2, c1=3, c2=0.5), astmodels.Legendre1D(2, c0=2, c1=3, c2=0.5), astmodels.Chebyshev1D(2, c0=2, c1=3, c2=0.5), astmodels.Chebyshev2D(1, 1, c0_0=1, c0_1=2, c1_0=3), astmodels.Legendre2D(1, 1, c0_0=1, c0_1=2, c1_0=3), astmodels.Hermite2D(1, 1, c0_0=1, c0_1=2, c1_0=3), astmodels.Scale(3.4), astmodels.RotateNative2Celestial(5.63, -72.5, 180), astmodels.Multiply(3), astmodels.Multiply(10 * u.m), astmodels.RotateCelestial2Native(5.63, -72.5, 180), astmodels.EulerAngleRotation(23, 14, 2.3, axes_order='xzx'), astmodels.Mapping((0, 1), n_inputs=3), astmodels.Shift(2. * u.deg), astmodels.Scale(3.4 * u.deg), astmodels.RotateNative2Celestial(5.63 * u.deg, -72.5 * u.deg, 180 * u.deg), astmodels.RotateCelestial2Native(5.63 * u.deg, -72.5 * u.deg, 180 * u.deg), astmodels.RotationSequence3D([1.2, 2.3, 3.4, .3], 'xyzx'), astmodels.SphericalRotationSequence([1.2, 2.3, 3.4, .3], 'xyzy'), custom_and_analytical_inverse(), ] math_models = []
try: import astropy except ImportError: HAS_ASTROPY = False test_models = [] else: HAS_ASTROPY = True from astropy.modeling import models as astmodels test_models = [ astmodels.Identity(2), astmodels.Polynomial1D(2, c0=1, c1=2, c2=3), astmodels.Polynomial2D(1, c0_0=1, c0_1=2, c1_0=3), astmodels.Shift(2.), astmodels.Scale(3.4), astmodels.RotateNative2Celestial(5.63, -72.5, 180), astmodels.RotateCelestial2Native(5.63, -72.5, 180), astmodels.EulerAngleRotation(23, 14, 2.3, axes_order='xzx'), astmodels.Mapping((0, 1), n_inputs=3) ] import pytest from ....tests import helpers from .... import util from ..basic import DomainType @pytest.mark.skipif('not HAS_ASTROPY') def test_transforms_compound(tmpdir):
def make_gwcs(shape, galactic=False): """ Create a simple celestial gWCS object in the ICRS coordinate frame. This function requires the `gwcs <https://github.com/spacetelescope/gwcs>`_ package. Parameters ---------- shape : 2-tuple of int The shape of the 2D array to be used with the output `~gwcs.wcs.WCS` object. galactic : bool, optional If `True`, then the output WCS will be in the Galactic coordinate frame. If `False` (default), then the output WCS will be in the ICRS coordinate frame. Returns ------- wcs : `gwcs.wcs.WCS` object The generalized world coordinate system (WCS) transformation. See Also -------- make_wcs, make_imagehdu Notes ----- The `make_wcs` function returns an equivalent WCS transformation to this one, but as an `astropy.wcs.WCS` object. Examples -------- >>> from photutils.datasets import make_gwcs >>> shape = (100, 100) >>> gwcs = make_gwcs(shape) >>> print(gwcs) From Transform -------- ---------------- detector linear_transform icrs None """ from gwcs import wcs as gwcs_wcs from gwcs import coordinate_frames as cf rho = np.pi / 3. scale = 0.1 / 3600. # 0.1 arcsec/pixel in deg/pix shift_by_crpix = (models.Shift((-shape[1] / 2) + 1) & models.Shift((-shape[0] / 2) + 1)) cd_matrix = np.array([[-scale * np.cos(rho), scale * np.sin(rho)], [scale * np.sin(rho), scale * np.cos(rho)]]) rotation = models.AffineTransformation2D(cd_matrix, translation=[0, 0]) rotation.inverse = models.AffineTransformation2D(np.linalg.inv(cd_matrix), translation=[0, 0]) tan = models.Pix2Sky_TAN() celestial_rotation = models.RotateNative2Celestial(197.8925, -1.36555556, 180.0) det2sky = shift_by_crpix | rotation | tan | celestial_rotation det2sky.name = 'linear_transform' detector_frame = cf.Frame2D(name='detector', axes_names=('x', 'y'), unit=(u.pix, u.pix)) if galactic: sky_frame = cf.CelestialFrame(reference_frame=coord.Galactic(), name='galactic', unit=(u.deg, u.deg)) else: sky_frame = cf.CelestialFrame(reference_frame=coord.ICRS(), name='icrs', unit=(u.deg, u.deg)) pipeline = [(detector_frame, det2sky), (sky_frame, None)] return gwcs_wcs.WCS(pipeline)
def _attach_static_distortion(self, adinputs=None): """ This primitive modifies the WCS of its input AD objects to include the static distortion read from the lookup table. It will ignore any ADs which have CoordinateFrames named "static" on all the extensions, and raise an OSError if any ADs have "static" on some (but not all) extensions. """ log = self.log # Check the inputs haven't been mosaicked or tiled mosaic_kws = { self.timestamp_keys[prim] for prim in ("mosaicDetectors", "tileArrays") } if any(mosaic_kws.intersection(ad.phu) for ad in adinputs): raise ValueError(f"Inputs to {self.myself()} must not have been " "mosaicked or tiled.") static_corrections = gsdi.STATIC_CORRECTIONS sdmodels = [] sdsubmodels = {} for direction in ("forward", "backward"): sdsubmodels[direction] = [] for component in static_corrections[direction]: model_type = getattr(models, component["model"]) for arr in component["parameters"]: for ordinate in "xy": pars = arr[ordinate] max_xpower = max([int(k[1]) for k in pars]) max_ypower = max([int(k[-1]) for k in pars]) if component["model"] == "Polynomial2D": degree = {"degree": max(max_xpower, max_ypower)} else: degree = { "xdegree": max_xpower, "ydegree": max_ypower } sdsubmodels[direction].append( model_type(**degree, **pars)) for index, pixref in enumerate(static_corrections["pixel_references"]): sdmodel = (models.Mapping((0, 1, 0, 1)) | (reduce( Model.__add__, sdsubmodels["forward"][index * 2::8]) & reduce( Model.__add__, sdsubmodels["forward"][index * 2 + 1::8]))) sdmodel.inverse = (models.Mapping((0, 1, 0, 1)) | (reduce( Model.__add__, sdsubmodels["backward"][index * 2::8]) & reduce( Model.__add__, sdsubmodels["backward"][index * 2 + 1::8]))) xref, yref = sdmodel.inverse(0, 0) if 0 < xref < 2048 and 0 < yref < 2048: ref_location = (index, xref - 1, yref - 1 ) # store 0-indexed pixel location sdmodels.append(sdmodel) for ad in adinputs: applied_static = sum("static" in ext.wcs.available_frames for ext in ad) if applied_static not in (0, len(ad)): raise OSError( f"Some (but not all) extensions in {ad.filename}" " have had the static disortion correction applied") # No-op silently if applied_static == len(ad): continue ref_wcs = ad[ref_location[0]].wcs ra, dec = ref_wcs(*ref_location[1:]) for ext, arrsec, sdmodel in zip(ad, ad.array_section(), sdmodels): # Include ROI shift sdmodel = (models.Shift(arrsec.x1 + 1) & models.Shift(arrsec.y1 + 1) | sdmodel) static_frame = cf.Frame2D(unit=(u.arcsec, u.arcsec), name="static") sky_model = models.Scale(1 / 3600) & models.Scale(1 / 3600) # the PA header keyword isn't good enough wcs_dict = adwcs.gwcs_to_fits(ad[ref_location[0]].nddata) pa = np.arctan2(wcs_dict["CD2_1"] - wcs_dict["CD1_2"], wcs_dict["CD1_1"] + wcs_dict["CD2_2"]) if abs(pa) > 0.00175: # radians ~ 0.01 degrees # Rotation2D() breaks things because it explicitly converts # the Column objects to Quantity objects, which retain units # of "pix" that Pix2Sky objects to. The AffineTransformation2D # does some multiplications (like Scale) which are agnostic to # the presence or absence of units. sky_model |= models.AffineTransformation2D( matrix=[[math.cos(pa), -math.sin(pa)], [math.sin(pa), math.cos(pa)]]) sky_model |= models.Pix2Sky_TAN( ) | models.RotateNative2Celestial(ra, dec, 180) ext.wcs = gWCS([(ext.wcs.input_frame, sdmodel), (static_frame, sky_model), (ext.wcs.output_frame, None)]) return adinputs
import numpy as np try: import astropy except ImportError: HAS_ASTROPY = False test_models = [] else: HAS_ASTROPY = True from astropy.utils import minversion ASTROPY_13 = minversion(astropy, "1.3.dev16506") from astropy.modeling import models as astmodels test_models = [astmodels.Identity(2), astmodels.Polynomial1D(2, c0=1, c1=2, c2=3), astmodels.Polynomial2D(1, c0_0=1, c0_1=2, c1_0=3), astmodels.Shift(2.), astmodels.Scale(3.4), astmodels.RotateNative2Celestial(5.63, -72.5, 180), astmodels.RotateCelestial2Native(5.63, -72.5, 180), astmodels.EulerAngleRotation(23, 14, 2.3, axes_order='xzx'), astmodels.Mapping((0, 1), n_inputs=3)] import pytest from ....tests import helpers from .... import util from ..basic import DomainType @pytest.mark.skipif('not HAS_ASTROPY') def test_transforms_compound(tmpdir): tree = {
def test_roundtrip_sky_rotation(inp): lon, lat, lon_pole = 42, 43, 44 n2c = models.RotateNative2Celestial(lon, lat, lon_pole) c2n = models.RotateCelestial2Native(lon, lat, lon_pole) assert_allclose(n2c.inverse(*n2c(*inp)), inp, atol=1e-13) assert_allclose(c2n.inverse(*c2n(*inp)), inp, atol=1e-13)
def test_native_celestial_lat90(): n2c = models.RotateNative2Celestial(1, 90, 0) alpha, delta = n2c(1, 1) assert_allclose(delta, 1) assert_allclose(alpha, 182)