def test_spectralcoord_frame(header_spectral_frames): # This is a test to check the numerical results of transformations between # different velocity frames. We simply make sure that the returned # SpectralCoords are in the right frame but don't check the transformations # since this is already done in test_spectralcoord_accuracy # in astropy.coordinates. with iers.conf.set_temp('auto_download', False): obstime = Time(f"2009-05-04T04:44:23", scale='utc') header = header_spectral_frames.copy() header['MJD-OBS'] = obstime.mjd header['CRVAL1'] = 16.33211 header['CRVAL2'] = -34.2221 header['OBSGEO-L'] = 144.2 header['OBSGEO-B'] = -20.2 header['OBSGEO-H'] = 0. # We start off with a WCS defined in topocentric frequency with pytest.warns(FITSFixedWarning): wcs_topo = WCS(header) # We convert a single pixel coordinate to world coordinates and keep only # the second high level object - a SpectralCoord: sc_topo = wcs_topo.pixel_to_world(0, 0, 31)[1] # We check that this is in topocentric frame with zero velocities assert isinstance(sc_topo, SpectralCoord) assert isinstance(sc_topo.observer, ITRS) assert sc_topo.observer.obstime.isot == obstime.isot assert_equal(sc_topo.observer.data.differentials['s'].d_xyz.value, 0) observatory = EarthLocation.from_geodetic( 144.2, -20.2).get_itrs(obstime=obstime).transform_to(ICRS()) assert observatory.separation_3d(sc_topo.observer.transform_to( ICRS())) < 1 * u.km for specsys, expected_frame in VELOCITY_FRAMES.items(): header['SPECSYS'] = specsys with pytest.warns(FITSFixedWarning): wcs = WCS(header) sc = wcs.pixel_to_world(0, 0, 31)[1] # Now transform to the expected velocity frame, which should leave # the spectral coordinate unchanged sc_check = sc.with_observer_stationary_relative_to(expected_frame) assert_quantity_allclose(sc.quantity, sc_check.quantity)
def test_pixel_to_world_itrs(x_in, y_in): """Regression test for https://github.com/astropy/astropy/pull/9609""" with pytest.warns(None) as w: wcs = WCS({ 'NAXIS': 2, 'CTYPE1': 'TLON-CAR', 'CTYPE2': 'TLAT-CAR', 'RADESYS': 'ITRS ', 'DATE-OBS': '2017-08-17T12:41:04.444' }) if Version(_wcs.__version__) >= Version('7.4'): assert len(w) == 1 msg = str(w[0].message) assert "'datfix' made the change 'Set MJD-OBS to 57982.528524 from DATE-OBS'." in msg else: assert len(w) == 0 # This shouldn't raise an exception. coord = wcs.pixel_to_world(x_in, y_in) # Check round trip transformation. x, y = wcs.world_to_pixel(coord) np.testing.assert_almost_equal(x, x_in) np.testing.assert_almost_equal(y, y_in)
def assert_time_at(header, position, jd1, jd2, scale, format): wcs = WCS(header) time = wcs.pixel_to_world(position) assert_allclose(time.jd1, jd1, rtol=1e-10) assert_allclose(time.jd2, jd2, rtol=1e-10) assert time.format == format assert time.scale == scale
def test_pixel_to_world_itrs(x_in, y_in): """Regression test for https://github.com/astropy/astropy/pull/9609""" if Version(_wcs.__version__) >= Version('7.4'): ctx = pytest.warns( FITSFixedWarning, match= r"'datfix' made the change 'Set MJD-OBS to 57982\.528524 from DATE-OBS'\." ) else: ctx = nullcontext() with ctx: wcs = WCS({ 'NAXIS': 2, 'CTYPE1': 'TLON-CAR', 'CTYPE2': 'TLAT-CAR', 'RADESYS': 'ITRS ', 'DATE-OBS': '2017-08-17T12:41:04.444' }) # This shouldn't raise an exception. coord = wcs.pixel_to_world(x_in, y_in) # Check round trip transformation. x, y = wcs.world_to_pixel(coord) np.testing.assert_almost_equal(x, x_in) np.testing.assert_almost_equal(y, y_in)
def assert_time_at(header, position, jd1, jd2, scale, format): with warnings.catch_warnings(): warnings.simplefilter('ignore', FITSFixedWarning) wcs = WCS(header) time = wcs.pixel_to_world(position) assert_allclose(time.jd1, jd1, rtol=1e-10) assert_allclose(time.jd2, jd2, rtol=1e-10) assert time.format == format assert time.scale == scale
def test_time_1d_roundtrip(header_time_1d, scale): # Check that coordinates round-trip pixel_in = np.arange(3, 10) header_time_1d['CTYPE1'] = scale.upper() wcs = WCS(header_time_1d) # Simple test time = wcs.pixel_to_world(pixel_in) pixel_out = wcs.world_to_pixel(time) assert_allclose(pixel_in, pixel_out) # Test with an intermediate change to a different scale/format time = wcs.pixel_to_world(pixel_in).tdb time.format = 'isot' pixel_out = wcs.world_to_pixel(time) assert_allclose(pixel_in, pixel_out)
def test_time_1d_location_missing(header_time_1d_no_obs): # Check what happens when no location is present wcs = WCS(header_time_1d_no_obs) with pytest.warns(UserWarning, match='Missing or incomplete observer location ' 'information, setting location in Time to None'): time = wcs.pixel_to_world(10) assert time.location is None
def test_time_1d_location_geocenter(header_time_1d_no_obs): header_time_1d_no_obs['TREFPOS'] = 'GEOCENTER' wcs = WCS(header_time_1d_no_obs) time = wcs.pixel_to_world(10) x, y, z = time.location.to_geocentric() assert_allclose(x.to_value(u.m), 0) assert_allclose(y.to_value(u.m), 0) assert_allclose(z.to_value(u.m), 0)
def test_time_1d_unsupported_ctype(header_time_1d_no_obs): # For cases that we don't support yet, e.g. UT(...), use Time and drop sub-scale # Case where the MJDREF is split into two for high precision header_time_1d_no_obs['CTYPE1'] = 'UT(WWV)' wcs = WCS(header_time_1d_no_obs) with pytest.warns(UserWarning, match="Dropping unsupported sub-scale WWV from scale UT"): time = wcs.pixel_to_world(10) assert isinstance(time, Time)
def test_time_1d_location_incomplete(header_time_1d_noobs): # Check what happens when location information is incomplete header_time_1d_noobs['OBSGEO-L'] = 10. wcs = WCS(header_time_1d_noobs) with pytest.warns(UserWarning, match='Missing or incomplete observer location ' 'information, setting location in Time to None'): time = wcs.pixel_to_world(10) assert time.location is None
def test_time_1d_location_unsupported(header_time_1d_no_obs): # Check what happens when TREFPOS is unsupported header_time_1d_no_obs['TREFPOS'] = 'BARYCENTER' wcs = WCS(header_time_1d_no_obs) with pytest.warns(UserWarning, match="Observation location 'barycenter' is not " "supported, setting location in Time to None"): time = wcs.pixel_to_world(10) assert time.location is None
def test_time_1d_location_geodetic(header_time_1d): # Make sure that the location is correctly returned (geodetic case) wcs = WCS(header_time_1d) time = wcs.pixel_to_world(10) lon, lat, alt = time.location.to_geodetic() # FIXME: alt won't work for now because ERFA doesn't implement the IAU 1976 # ellipsoid (https://github.com/astropy/astropy/issues/9420) assert_allclose(lon.degree, -20) assert_allclose(lat.degree, -70)
def test_time_1d_high_precision(header_time_1d): # Case where the MJDREF is split into two for high precision del header_time_1d['MJDREF'] header_time_1d['MJDREFI'] = 52000. header_time_1d['MJDREFF'] = 1e-11 wcs = WCS(header_time_1d) time = wcs.pixel_to_world(10) # Here we have to use a very small rtol to really test that MJDREFF is # taken into account assert_allclose(time.jd1, 2452001.0, rtol=1e-12) assert_allclose(time.jd2, -0.5 + 25 / 3600 / 24 + 1e-11, rtol=1e-13)
def test_pixel_to_world_itrs(x_in, y_in): """Regression test for https://github.com/astropy/astropy/pull/9609""" wcs = WCS({'NAXIS': 2, 'CTYPE1': 'TLON-CAR', 'CTYPE2': 'TLAT-CAR', 'RADESYS': 'ITRS ', 'DATE-OBS': '2017-08-17T12:41:04.444'}) # This shouldn't raise an exception. coord = wcs.pixel_to_world(x_in, y_in) # Check round trip transformation. x, y = wcs.world_to_pixel(coord) np.testing.assert_almost_equal(x, x_in) np.testing.assert_almost_equal(y, y_in)
def test_time_1d_location_incomplete(header_time_1d_no_obs): # Check what happens when location information is incomplete header_time_1d_no_obs['OBSGEO-L'] = 10. with warnings.catch_warnings(): warnings.simplefilter('ignore', FITSFixedWarning) wcs = WCS(header_time_1d_no_obs) with pytest.warns(UserWarning, match='Missing or incomplete observer location ' 'information, setting location in Time to None'): time = wcs.pixel_to_world(10) assert time.location is None
def test_time_1d_location_geocentric(header_time_1d_noobs): # Make sure that the location is correctly returned (geocentric case) header = header_time_1d_noobs header['OBSGEO-X'] = 10 header['OBSGEO-Y'] = -20 header['OBSGEO-Z'] = 30 wcs = WCS(header) time = wcs.pixel_to_world(10) x, y, z = time.location.to_geocentric() assert_allclose(x.to_value(u.m), 10) assert_allclose(y.to_value(u.m), -20) assert_allclose(z.to_value(u.m), 30)
def test_time_1d_location_geocentric(header_time_1d_no_obs): # Make sure that the location is correctly returned (geocentric case) header = header_time_1d_no_obs header['OBSGEO-X'] = 10 header['OBSGEO-Y'] = -20 header['OBSGEO-Z'] = 30 with warnings.catch_warnings(): warnings.simplefilter('ignore', FITSFixedWarning) wcs = WCS(header) time = wcs.pixel_to_world(10) x, y, z = time.location.to_geocentric() assert_allclose(x.to_value(u.m), 10) assert_allclose(y.to_value(u.m), -20) assert_allclose(z.to_value(u.m), 30)
def test_fit_wcs_from_points(header_str, crval, sip_degree, user_proj_point, exp_max_dist, exp_std_dist): header = fits.Header.fromstring(header_str, sep='\n') header["CRVAL1"] = crval true_wcs = WCS(header, relax=True) # Getting the pixel coordinates x, y = np.meshgrid(list(range(10)), list(range(10))) x = x.flatten() y = y.flatten() # Calculating the true sky positions world_pix = true_wcs.pixel_to_world(x, y) # which projection point to use if user_proj_point: proj_point = world_pix[0] projlon = proj_point.data.lon.deg projlat = proj_point.data.lat.deg else: proj_point = 'center' # Fitting the wcs fit_wcs = fit_wcs_from_points((x, y), world_pix, proj_point=proj_point, sip_degree=sip_degree) # Validate that the true sky coordinates # match sky coordinates calculated from the wcs fit world_pix_new = fit_wcs.pixel_to_world(x, y) dists = world_pix.separation(world_pix_new) assert dists.max() < exp_max_dist assert np.std(dists) < exp_std_dist if user_proj_point: assert (fit_wcs.wcs.crval == [projlon, projlat]).all()
def test_different_ctypes(header_spectral_frames, ctype3, observer): header = header_spectral_frames.copy() header['CTYPE3'] = ctype3 header['CRVAL3'] = 0.1 header['CDELT3'] = 0.001 if ctype3[0] == 'V': header['CUNIT3'] = 'm s-1' else: header['CUNIT3'] = '' header['RESTWAV'] = 1.420405752E+09 header['MJD-OBS'] = 55197 if observer: header['OBSGEO-L'] = 144.2 header['OBSGEO-B'] = -20.2 header['OBSGEO-H'] = 0. header['SPECSYS'] = 'BARYCENT' with warnings.catch_warnings(): warnings.simplefilter('ignore', FITSFixedWarning) wcs = WCS(header) skycoord, spectralcoord = wcs.pixel_to_world(0, 0, 31) assert isinstance(spectralcoord, SpectralCoord) if observer: pix = wcs.world_to_pixel(skycoord, spectralcoord) else: with pytest.warns(AstropyUserWarning, match='No observer defined on WCS'): pix = wcs.world_to_pixel(skycoord, spectralcoord) assert_allclose(pix, [0, 0, 31], rtol=1e-6)
def test_fit_wcs_from_points(): header_str_linear = """ XTENSION= 'IMAGE ' / Image extension BITPIX = -32 / array data type NAXIS = 2 / number of array dimensions NAXIS1 = 50 NAXIS2 = 50 PCOUNT = 0 / number of parameters GCOUNT = 1 / number of groups RADESYS = 'ICRS ' EQUINOX = 2000.0 WCSAXES = 2 CTYPE1 = 'RA---TAN' CTYPE2 = 'DEC--TAN' CRVAL1 = 250.3497414839765 CRVAL2 = 2.280925599609063 CRPIX1 = 1045.0 CRPIX2 = 1001.0 CD1_1 = -0.005564478186178 CD1_2 = -0.001042099258152 CD2_1 = 0.00118144146585 CD2_2 = -0.005590816683583 """ header_str_sip = """ XTENSION= 'IMAGE ' / Image extension BITPIX = -32 / array data type NAXIS = 2 / number of array dimensions NAXIS1 = 50 NAXIS2 = 50 PCOUNT = 0 / number of parameters GCOUNT = 1 / number of groups RADESYS = 'ICRS ' EQUINOX = 2000.0 WCSAXES = 2 CTYPE1 = 'RA---TAN-SIP' CTYPE2 = 'DEC--TAN-SIP' CRVAL1 = 250.3497414839765 CRVAL2 = 2.280925599609063 CRPIX1 = 1045.0 CRPIX2 = 1001.0 CD1_1 = -0.005564478186178 CD1_2 = -0.001042099258152 CD2_1 = 0.00118144146585 CD2_2 = -0.005590816683583 A_ORDER = 2 B_ORDER = 2 A_2_0 = 2.02451189234E-05 A_0_2 = 3.317603337918E-06 A_1_1 = 1.73456334971071E-05 B_2_0 = 3.331330003472E-06 B_0_2 = 2.04247482482589E-05 B_1_1 = 1.71476710804143E-05 AP_ORDER= 2 BP_ORDER= 2 AP_1_0 = 0.000904700296389636 AP_0_1 = 0.000627660715584716 AP_2_0 = -2.023482905861E-05 AP_0_2 = -3.332285841011E-06 AP_1_1 = -1.731636633824E-05 BP_1_0 = 0.000627960882053211 BP_0_1 = 0.000911222886084808 BP_2_0 = -3.343918167224E-06 BP_0_2 = -2.041598249021E-05 BP_1_1 = -1.711876336719E-05 A_DMAX = 44.72893589844534 B_DMAX = 44.62692873032506 """ # A known header that failed before header_str_prob = """ NAXIS = 2 / number of array dimensions WCSAXES = 2 / Number of coordinate axes CRPIX1 = 1024.5 / Pixel coordinate of reference point CRPIX2 = 1024.5 / Pixel coordinate of reference point CD1_1 = -1.7445934400771E-05 / Coordinate transformation matrix element CD1_2 = -4.9826985362578E-08 / Coordinate transformation matrix element CD2_1 = -5.0068838822312E-08 / Coordinate transformation matrix element CD2_2 = 1.7530614610951E-05 / Coordinate transformation matrix element CTYPE1 = 'RA---TAN' / Right ascension, gnomonic projection CTYPE2 = 'DEC--TAN' / Declination, gnomonic projection CRVAL1 = 5.8689341666667 / [deg] Coordinate value at reference point CRVAL2 = -71.995508583333 / [deg] Coordinate value at reference point """ header_linear = fits.Header.fromstring(header_str_linear, sep='\n') header_sip = fits.Header.fromstring(header_str_sip, sep='\n') header_prob = fits.Header.fromstring(header_str_prob, sep='\n') true_wcs_linear = WCS(header_linear, relax=True) true_wcs_sip = WCS(header_sip, relax=True) true_wcs_prob = WCS(header_prob, relax=True) # Getting the pixel coordinates x, y = np.meshgrid(list(range(10)), list(range(10))) x = x.flatten() y = y.flatten() # Calculating the true sky positions world_pix_linear = true_wcs_linear.pixel_to_world(x, y) world_pix_sip = true_wcs_sip.pixel_to_world(x, y) world_pix_prob = true_wcs_prob.pixel_to_world(x, y) # Fitting the wcs, no distortion. fit_wcs_linear = fit_wcs_from_points((x, y), world_pix_linear, proj_point='center', sip_degree=None) # Fitting the wcs, with distortion. fit_wcs_sip = fit_wcs_from_points((x, y), world_pix_sip, proj_point='center', sip_degree=2) # Fitting the problematic WCS fit_wcs_prob = fit_wcs_from_points((x, y), world_pix_prob, proj_point='center', sip_degree=None) # Validate that the true sky coordinates calculated with `true_wcs_linear` # match sky coordinates calculated from the wcs fit with only linear terms world_pix_linear_new = fit_wcs_linear.pixel_to_world(x, y) dists = world_pix_linear.separation(world_pix_linear_new) assert dists.max() < 7e-5 * u.deg assert np.std(dists) < 2.5e-5 * u.deg # Validate that the true sky coordinates calculated with `true_wcs_sip` # match the sky coordinates calculated from the wcs fit with SIP of same # degree (2) world_pix_sip_new = fit_wcs_sip.pixel_to_world(x, y) dists = world_pix_sip.separation(world_pix_sip_new) assert dists.max() < 7e-6 * u.deg assert np.std(dists) < 2.5e-6 * u.deg # Validate that the true sky coordinates calculated from the problematic # WCS match world_pix_prob_new = fit_wcs_prob.pixel_to_world(x, y) dists = world_pix_prob.separation(world_pix_prob_new) assert dists.max() < 7e-6 * u.deg assert np.std(dists) < 2.5e-6 * u.deg # Test 360->0 degree crossover header_linear["CRVAL1"] = 352.3497414839765 header_sip["CRVAL1"] = 352.3497414839765 header_prob["CRVAL1"] = 352.3497414839765 true_wcs_linear = WCS(header_linear, relax=True) true_wcs_sip = WCS(header_sip, relax=True) true_wcs_prob = WCS(header_prob) # Calculating the true sky positions world_pix_linear = true_wcs_linear.pixel_to_world(x, y) world_pix_sip = true_wcs_sip.pixel_to_world(x, y) world_pix_prob = true_wcs_prob.pixel_to_world(x, y) # Fitting the wcs, no distortion. fit_wcs_linear = fit_wcs_from_points((x, y), world_pix_linear, proj_point='center', sip_degree=None) # Fitting the wcs, with distortion. fit_wcs_sip = fit_wcs_from_points((x, y), world_pix_sip, proj_point='center', sip_degree=2) # Fitting the problem WCS fit_wcs_prob = fit_wcs_from_points((x, y), world_pix_prob, proj_point='center', sip_degree=None) # Validate that the true sky coordinates calculated with `true_wcs_linear` # match sky coordinates calculated from the wcs fit with only linear terms world_pix_linear_new = fit_wcs_linear.pixel_to_world(x, y) dists = world_pix_linear.separation(world_pix_linear_new) assert dists.max() < 7e-5 * u.deg assert np.std(dists) < 2.5e-5 * u.deg # Validate fit with SIP world_pix_sip_new = fit_wcs_sip.pixel_to_world(x, y) dists = world_pix_sip.separation(world_pix_sip_new) assert dists.max() < 7e-6 * u.deg assert np.std(dists) < 2.5e-6 * u.deg # Validate the problematic WCS world_pix_prob_new = fit_wcs_prob.pixel_to_world(x, y) dists = world_pix_prob.separation(world_pix_prob_new) assert dists.max() < 7e-6 * u.deg assert np.std(dists) < 2.5e-6 * u.deg # Test CRPIX bounds requirement wcs_str = """ WCSAXES = 2 / Number of coordinate axes CRPIX1 = 1045.0 / Pixel coordinate of reference point CRPIX2 = 1001.0 / Pixel coordinate of reference point PC1_1 = 0.00056205870415378 / Coordinate transformation matrix element PC1_2 = -0.00569181083243 / Coordinate transformation matrix element PC2_1 = 0.0056776810932466 / Coordinate transformation matrix element PC2_2 = 0.0004208048403273 / Coordinate transformation matrix element CDELT1 = 1.0 / [deg] Coordinate increment at reference point CDELT2 = 1.0 / [deg] Coordinate increment at reference point CUNIT1 = 'deg' / Units of coordinate increment and value CUNIT2 = 'deg' / Units of coordinate increment and value CTYPE1 = 'RA---TAN' / Right ascension, gnomonic projection CTYPE2 = 'DEC--TAN' / Declination, gnomonic projection CRVAL1 = 104.57797893504 / [deg] Coordinate value at reference point CRVAL2 = -74.195502593322 / [deg] Coordinate value at reference point LONPOLE = 180.0 / [deg] Native longitude of celestial pole LATPOLE = -74.195502593322 / [deg] Native latitude of celestial pole TIMESYS = 'TDB' / Time scale TIMEUNIT= 'd' / Time units DATEREF = '1858-11-17' / ISO-8601 fiducial time MJDREFI = 0.0 / [d] MJD of fiducial time, integer part MJDREFF = 0.0 / [d] MJD of fiducial time, fractional part DATE-OBS= '2019-03-27T03:30:13.832Z' / ISO-8601 time of observation MJD-OBS = 58569.145993426 / [d] MJD of observation MJD-OBS = 58569.145993426 / [d] MJD at start of observation TSTART = 1569.6467941661 / [d] Time elapsed since fiducial time at start DATE-END= '2019-03-27T04:00:13.831Z' / ISO-8601 time at end of observation MJD-END = 58569.166826748 / [d] MJD at end of observation TSTOP = 1569.6676274905 / [d] Time elapsed since fiducial time at end TELAPSE = 0.02083332443 / [d] Elapsed time (start to stop) TIMEDEL = 0.020833333333333 / [d] Time resolution TIMEPIXR= 0.5 / Reference position of timestamp in binned data RADESYS = 'ICRS' / Equatorial coordinate system """ wcs_header = fits.Header.fromstring(wcs_str, sep='\n') ffi_wcs = WCS(wcs_header) yi, xi = (1000, 1000) y, x = (10, 200) center_coord = SkyCoord(ffi_wcs.all_pix2world([[xi + x // 2, yi + y // 2]], 0), unit='deg')[0] ypix, xpix = [arr.flatten() for arr in np.mgrid[xi:xi + x, yi:yi + y]] world_pix = SkyCoord(*ffi_wcs.all_pix2world(xpix, ypix, 0), unit='deg') fit_wcs = fit_wcs_from_points((ypix, xpix), world_pix, proj_point='center') assert (fit_wcs.wcs.crpix.astype(int) == [1100, 1005]).all() assert fit_wcs.pixel_shape == (200, 10)
def test_spectral_1d(header_spectral_1d, ctype1, observer): # This is a regression test for issues that happened with 1-d WCS # where the target is not defined but observer is. header = header_spectral_1d.copy() header['CTYPE1'] = ctype1 header['CRVAL1'] = 0.1 header['CDELT1'] = 0.001 if ctype1[0] == 'V': header['CUNIT1'] = 'm s-1' else: header['CUNIT1'] = '' header['RESTWAV'] = 1.420405752E+09 header['MJD-OBS'] = 55197 if observer: header['OBSGEO-L'] = 144.2 header['OBSGEO-B'] = -20.2 header['OBSGEO-H'] = 0. header['SPECSYS'] = 'BARYCENT' with warnings.catch_warnings(): warnings.simplefilter('ignore', FITSFixedWarning) wcs = WCS(header) # First ensure that transformations round-trip spectralcoord = wcs.pixel_to_world(31) assert isinstance(spectralcoord, SpectralCoord) assert spectralcoord.target is None assert (spectralcoord.observer is not None) is observer if observer: expected_message = 'No target defined on SpectralCoord' else: expected_message = 'No observer defined on WCS' with pytest.warns(AstropyUserWarning, match=expected_message): pix = wcs.world_to_pixel(spectralcoord) assert_allclose(pix, [31], rtol=1e-6) # Also make sure that we can convert a SpectralCoord on which the observer # is not defined but the target is. with pytest.warns(AstropyUserWarning, match='No velocity defined on frame'): spectralcoord_no_obs = SpectralCoord( spectralcoord.quantity, doppler_rest=spectralcoord.doppler_rest, doppler_convention=spectralcoord.doppler_convention, target=ICRS(10 * u.deg, 20 * u.deg, distance=1 * u.kpc)) if observer: expected_message = 'No observer defined on SpectralCoord' else: expected_message = 'No observer defined on WCS' with pytest.warns(AstropyUserWarning, match=expected_message): pix2 = wcs.world_to_pixel(spectralcoord_no_obs) assert_allclose(pix2, [31], rtol=1e-6) # And finally check case when both observer and target are defined on the # SpectralCoord with pytest.warns(AstropyUserWarning, match='No velocity defined on frame'): spectralcoord_no_obs = SpectralCoord( spectralcoord.quantity, doppler_rest=spectralcoord.doppler_rest, doppler_convention=spectralcoord.doppler_convention, observer=ICRS(10 * u.deg, 20 * u.deg, distance=0 * u.kpc), target=ICRS(10 * u.deg, 20 * u.deg, distance=1 * u.kpc)) if observer: pix3 = wcs.world_to_pixel(spectralcoord_no_obs) else: with pytest.warns(AstropyUserWarning, match='No observer defined on WCS'): pix3 = wcs.world_to_pixel(spectralcoord_no_obs) assert_allclose(pix3, [31], rtol=1e-6)
def test_fit_wcs_from_points(): header_str_linear = """ XTENSION= 'IMAGE ' / Image extension BITPIX = -32 / array data type NAXIS = 2 / number of array dimensions NAXIS1 = 50 NAXIS2 = 50 PCOUNT = 0 / number of parameters GCOUNT = 1 / number of groups RADESYS = 'ICRS ' EQUINOX = 2000.0 WCSAXES = 2 CTYPE1 = 'RA---TAN' CTYPE2 = 'DEC--TAN' CRVAL1 = 250.3497414839765 CRVAL2 = 2.280925599609063 CRPIX1 = 1045.0 CRPIX2 = 1001.0 CD1_1 = -0.005564478186178 CD1_2 = -0.001042099258152 CD2_1 = 0.00118144146585 CD2_2 = -0.005590816683583 """ header_str_sip = """ XTENSION= 'IMAGE ' / Image extension BITPIX = -32 / array data type NAXIS = 2 / number of array dimensions NAXIS1 = 50 NAXIS2 = 50 PCOUNT = 0 / number of parameters GCOUNT = 1 / number of groups RADESYS = 'ICRS ' EQUINOX = 2000.0 WCSAXES = 2 CTYPE1 = 'RA---TAN-SIP' CTYPE2 = 'DEC--TAN-SIP' CRVAL1 = 250.3497414839765 CRVAL2 = 2.280925599609063 CRPIX1 = 1045.0 CRPIX2 = 1001.0 CD1_1 = -0.005564478186178 CD1_2 = -0.001042099258152 CD2_1 = 0.00118144146585 CD2_2 = -0.005590816683583 A_ORDER = 2 B_ORDER = 2 A_2_0 = 2.02451189234E-05 A_0_2 = 3.317603337918E-06 A_1_1 = 1.73456334971071E-05 B_2_0 = 3.331330003472E-06 B_0_2 = 2.04247482482589E-05 B_1_1 = 1.71476710804143E-05 AP_ORDER= 2 BP_ORDER= 2 AP_1_0 = 0.000904700296389636 AP_0_1 = 0.000627660715584716 AP_2_0 = -2.023482905861E-05 AP_0_2 = -3.332285841011E-06 AP_1_1 = -1.731636633824E-05 BP_1_0 = 0.000627960882053211 BP_0_1 = 0.000911222886084808 BP_2_0 = -3.343918167224E-06 BP_0_2 = -2.041598249021E-05 BP_1_1 = -1.711876336719E-05 A_DMAX = 44.72893589844534 B_DMAX = 44.62692873032506 """ header_linear = fits.Header.fromstring(header_str_linear, sep='\n') header_sip = fits.Header.fromstring(header_str_sip, sep='\n') true_wcs_linear = WCS(header_linear, relax=True) true_wcs_sip = WCS(header_sip, relax=True) # Getting the pixel coordinates x, y = np.meshgrid(list(range(10)), list(range(10))) x = x.flatten() y = y.flatten() # Calculating the true sky positions world_pix_linear = true_wcs_linear.pixel_to_world(x, y) world_pix_sip = true_wcs_sip.pixel_to_world(x, y) # Fitting the wcs, no distortion. fit_wcs_linear = fit_wcs_from_points((x, y), world_pix_linear, proj_point='center', sip_degree=None) # Fitting the wcs, with distortion. fit_wcs_sip = fit_wcs_from_points((x, y), world_pix_sip, proj_point='center', sip_degree=2) # Validate that the true sky coordinates calculated with `true_wcs_linear` # match sky coordinates calculated from the wcs fit with only linear terms world_pix_linear_new = fit_wcs_linear.pixel_to_world(x, y) dists = world_pix_linear.separation(world_pix_linear_new) assert dists.max() < 7e-5 * u.deg assert np.std(dists) < 2.5e-5 * u.deg # Validate that the true sky coordinates calculated with `true_wcs_sip` # match the sky coordinates calculated from the wcs fit with SIP of same # degree (2) world_pix_sip_new = fit_wcs_sip.pixel_to_world(x, y) dists = world_pix_sip.separation(world_pix_sip_new) assert dists.max() < 7e-6 * u.deg assert np.std(dists) < 2.5e-6 * u.deg # Test 360->0 degree crossover header_linear["CRVAL1"] = 352.3497414839765 header_sip["CRVAL1"] = 352.3497414839765 true_wcs_linear = WCS(header_linear, relax=True) true_wcs_sip = WCS(header_sip, relax=True) # Calculating the true sky positions world_pix_linear = true_wcs_linear.pixel_to_world(x, y) world_pix_sip = true_wcs_sip.pixel_to_world(x, y) # Fitting the wcs, no distortion. fit_wcs_linear = fit_wcs_from_points((x, y), world_pix_linear, proj_point='center', sip_degree=None) # Fitting the wcs, with distortion. fit_wcs_sip = fit_wcs_from_points((x, y), world_pix_sip, proj_point='center', sip_degree=2) # Validate that the true sky coordinates calculated with `true_wcs_linear` # match sky coordinates calculated from the wcs fit with only linear terms world_pix_linear_new = fit_wcs_linear.pixel_to_world(x, y) dists = world_pix_linear.separation(world_pix_linear_new) assert dists.max() < 7e-5 * u.deg assert np.std(dists) < 2.5e-5 * u.deg