def test_wcs_dropping(): wcs = WCS(naxis=4) wcs.wcs.pc = np.zeros([4, 4]) np.fill_diagonal(wcs.wcs.pc, np.arange(1, 5)) pc = wcs.wcs.pc # for later use below dropped = wcs.dropaxis(0) assert np.all(dropped.wcs.get_pc().diagonal() == np.array([2, 3, 4])) dropped = wcs.dropaxis(1) assert np.all(dropped.wcs.get_pc().diagonal() == np.array([1, 3, 4])) dropped = wcs.dropaxis(2) assert np.all(dropped.wcs.get_pc().diagonal() == np.array([1, 2, 4])) dropped = wcs.dropaxis(3) assert np.all(dropped.wcs.get_pc().diagonal() == np.array([1, 2, 3])) wcs = WCS(naxis=4) wcs.wcs.cd = pc dropped = wcs.dropaxis(0) assert np.all(dropped.wcs.get_pc().diagonal() == np.array([2, 3, 4])) dropped = wcs.dropaxis(1) assert np.all(dropped.wcs.get_pc().diagonal() == np.array([1, 3, 4])) dropped = wcs.dropaxis(2) assert np.all(dropped.wcs.get_pc().diagonal() == np.array([1, 2, 4])) dropped = wcs.dropaxis(3) assert np.all(dropped.wcs.get_pc().diagonal() == np.array([1, 2, 3]))
def test_distortion_correlations(): filename = get_pkg_data_filename('../../tests/data/sip.fits') w = WCS(filename) assert_equal(w.axis_correlation_matrix, True) # Changing PC to an identity matrix doesn't change anything since # distortions are still present. w.wcs.pc = [[1, 0], [0, 1]] assert_equal(w.axis_correlation_matrix, True) # Nor does changing the name of the axes to make them non-celestial w.wcs.ctype = ['X', 'Y'] assert_equal(w.axis_correlation_matrix, True) # However once we turn off the distortions the matrix changes w.sip = None assert_equal(w.axis_correlation_matrix, [[True, False], [False, True]]) # If we go back to celestial coordinates then the matrix is all True again w.wcs.ctype = ['RA---TAN', 'DEC--TAN'] assert_equal(w.axis_correlation_matrix, True) # Or if we change to X/Y but have a non-identity PC w.wcs.pc = [[0.9, -0.1], [0.1, 0.9]] w.wcs.ctype = ['X', 'Y'] assert_equal(w.axis_correlation_matrix, True)
def test_skycoord_to_pixel_swapped(): # Regression test for a bug that caused skycoord_to_pixel and # pixel_to_skycoord to not work correctly if the axes were swapped in the # WCS. # Import astropy.coordinates here to avoid circular imports from astropy.coordinates import SkyCoord header = get_pkg_data_contents('maps/1904-66_TAN.hdr', encoding='binary') wcs = WCS(header) wcs_swapped = wcs.sub([WCSSUB_LATITUDE, WCSSUB_LONGITUDE]) ref = SkyCoord(0.1 * u.deg, -89. * u.deg, frame='icrs') xp1, yp1 = skycoord_to_pixel(ref, wcs) xp2, yp2 = skycoord_to_pixel(ref, wcs_swapped) assert_allclose(xp1, xp2) assert_allclose(yp1, yp2) # WCS is in FK5 so we need to transform back to ICRS new1 = pixel_to_skycoord(xp1, yp1, wcs).transform_to('icrs') new2 = pixel_to_skycoord(xp1, yp1, wcs_swapped).transform_to('icrs') assert_allclose(new1.ra.degree, new2.ra.degree) assert_allclose(new1.dec.degree, new2.dec.degree)
def test_slice(): mywcs = WCS(naxis=2) mywcs.wcs.crval = [1, 1] mywcs.wcs.cdelt = [0.1, 0.1] mywcs.wcs.crpix = [1, 1] mywcs._naxis = [1000, 500] pscale = 0.1 # from cdelt slice_wcs = mywcs.slice([slice(1, None), slice(0, None)]) assert np.all(slice_wcs.wcs.crpix == np.array([1, 0])) assert slice_wcs._naxis == [1000, 499] # test that CRPIX maps to CRVAL: assert_allclose( slice_wcs.wcs_pix2world(*slice_wcs.wcs.crpix, 1), slice_wcs.wcs.crval, rtol=0.0, atol=1e-6 * pscale ) slice_wcs = mywcs.slice([slice(1, None, 2), slice(0, None, 4)]) assert np.all(slice_wcs.wcs.crpix == np.array([0.625, 0.25])) assert np.all(slice_wcs.wcs.cdelt == np.array([0.4, 0.2])) assert slice_wcs._naxis == [250, 250] slice_wcs = mywcs.slice([slice(None, None, 2), slice(0, None, 2)]) assert np.all(slice_wcs.wcs.cdelt == np.array([0.2, 0.2])) assert slice_wcs._naxis == [500, 250] # Non-integral values do not alter the naxis attribute slice_wcs = mywcs.slice([slice(50.), slice(20.)]) assert slice_wcs._naxis == [1000, 500] slice_wcs = mywcs.slice([slice(50.), slice(20)]) assert slice_wcs._naxis == [20, 500] slice_wcs = mywcs.slice([slice(50), slice(20.5)]) assert slice_wcs._naxis == [1000, 50]
def test_slice_fitsorder(): mywcs = WCS(naxis=2) mywcs.wcs.crval = [1, 1] mywcs.wcs.cdelt = [0.1, 0.1] mywcs.wcs.crpix = [1, 1] slice_wcs = mywcs.slice([slice(1, None), slice(0, None)], numpy_order=False) assert np.all(slice_wcs.wcs.crpix == np.array([0, 1])) slice_wcs = mywcs.slice([slice(1, None, 2), slice(0, None, 4)], numpy_order=False) assert np.all(slice_wcs.wcs.crpix == np.array([0.25, 0.625])) assert np.all(slice_wcs.wcs.cdelt == np.array([0.2, 0.4])) slice_wcs = mywcs.slice([slice(1, None, 2)], numpy_order=False) assert np.all(slice_wcs.wcs.crpix == np.array([0.25, 1])) assert np.all(slice_wcs.wcs.cdelt == np.array([0.2, 0.1]))
def pix2world(wcsHeader, width, height, startX=0, startY=0, corner=True, ascartesian=False): """ Calculate RA, Dec coordinates of a given pixel coordinate rectangle. ra, dec = pix2world(..) ra and dec are indexed as [y,x] Each array element contains the RA,Dec coords of the top left corner of the given pixel if corner==True, otherwise the coords of the pixel center. If corner==True, an additional row and column exists at the bottom and right so that it is possible to get the bottom and right corner values for those pixels. :param dictionary wcsHeader: WCS header :param width: width of rectangle :param height: height of rectangle :param startX: x coordinate of rectangle, can be negative :param startY: y coordinate of rectangle, can be negative If ascartesian=False: :rtype: tuple(ra, dec) with arrays of shape (height+1,width+1) if corner==True, else (height,width) If ascartesian=True: :rtype: array of shape (height[+1],width[+1],3) with x,y,z order """ if corner: startX -= 0.5 # top left corner instead of pixel center startY -= 0.5 x, y = np.meshgrid(np.arange(startX,startX+width+corner), np.arange(startY,startY+height+corner)) # check if TAN projection and use our fast version, otherwise fall-back to astropy if wcsHeader['CTYPE1'] == 'RA---TAN' and wcsHeader['CTYPE2'] == 'DEC--TAN' and \ wcsHeader['LATPOLE'] == 0.0: res = tan_pix2world(wcsHeader, x, y, 0, ascartesian=ascartesian) else: wcs = WCS(wcsHeader) ra, dec = wcs.all_pix2world(x, y, 0, ra_dec_order=True) if ascartesian: np.deg2rad(ra, ra) np.deg2rad(dec, dec) res = spherical_to_cartesian(None, dec, ra, astuple=False) else: res = ra, dec return res
def wcsTest(): wcsPath = getResourcePath('ISS030-E-102170_dc.wcs') header = readHeader(wcsPath) # the pixel coordinates of which we want the world coordinates x, y = np.meshgrid(np.arange(0,4000), np.arange(0,2000)) x = x.ravel() y = y.ravel() # first, calculate using astropy as reference wcs = WCS(header) t0 = time.time() ra_astropy, dec_astropy = wcs.wcs_pix2world(x, y, 0) print('astropy wcs:', time.time()-t0, 's') # now, check against our own implementation #tan_pix2world(header, x, y, 0) # warmup t0 = time.time() ra, dec = tan_pix2world(header, x, y, 0) print('own wcs:', time.time()-t0, 's') assert_almost_equal([ra, dec], [ra_astropy, dec_astropy])
def test_slice_with_sip(): mywcs = WCS(naxis=2) mywcs.wcs.crval = [1, 1] mywcs.wcs.cdelt = [0.1, 0.1] mywcs.wcs.crpix = [1, 1] mywcs._naxis = [1000, 500] mywcs.wcs.ctype = ['RA---TAN-SIP', 'DEC--TAN-SIP'] a = np.array( [[0, 0, 5.33092692e-08, 3.73753773e-11, -2.02111473e-13], [0, 2.44084308e-05, 2.81394789e-11, 5.17856895e-13, 0.0], [-2.41334657e-07, 1.29289255e-10, 2.35753629e-14, 0.0, 0.0], [-2.37162007e-10, 5.43714947e-13, 0.0, 0.0, 0.0], [ -2.81029767e-13, 0.0, 0.0, 0.0, 0.0]] ) b = np.array( [[0, 0, 2.99270374e-05, -2.38136074e-10, 7.23205168e-13], [0, -1.71073858e-07, 6.31243431e-11, -5.16744347e-14, 0.0], [6.95458963e-06, -3.08278961e-10, -1.75800917e-13, 0.0, 0.0], [3.51974159e-11, 5.60993016e-14, 0.0, 0.0, 0.0], [-5.92438525e-13, 0.0, 0.0, 0.0, 0.0]] ) mywcs.sip = Sip(a, b, None, None, mywcs.wcs.crpix) mywcs.wcs.set() pscale = 0.1 # from cdelt slice_wcs = mywcs.slice([slice(1, None), slice(0, None)]) # test that CRPIX maps to CRVAL: assert_allclose( slice_wcs.all_pix2world(*slice_wcs.wcs.crpix, 1), slice_wcs.wcs.crval, rtol=0.0, atol=1e-6 * pscale ) slice_wcs = mywcs.slice([slice(1, None, 2), slice(0, None, 4)]) # test that CRPIX maps to CRVAL: assert_allclose( slice_wcs.all_pix2world(*slice_wcs.wcs.crpix, 1), slice_wcs.wcs.crval, rtol=0.0, atol=1e-6 * pscale )
def recomputeXylsPixelPositions(originalXylsPath, originalWcsPath, newWcsPathOrHeader): """ Return pixel coordinates valid for `newWcsPathOrHeader` for the reference stars found in `originalXylsPath` (belonging to `originalWcsPath`). :rtype: tuple (x,y) with x and y being ndarrays """ # Step 1: compute RA,Dec of reference stars (as this is not stored in .xyls) originalWCS = WCS(readHeader(originalWcsPath)) x, y = readXy(originalXylsPath) ra, dec = originalWCS.all_pix2world(x, y, 0) # Step 2: compute pixel positions of reference stars in new WCS solution if isinstance(newWcsPathOrHeader, string_types): newWCS = WCS(readHeader(newWcsPathOrHeader)) else: newWCS = WCS(newWcsPathOrHeader) # all_world2pix raised a NoConvergenc error # As we don't use SIP, we don't need to use all_world2pix. # wcs_world2pix doesn't support any distortion correction. xNew, yNew = newWCS.wcs_world2pix(ra, dec, 0) return xNew,yNew
def test_wcs_swapping(): wcs = WCS(naxis=4) wcs.wcs.pc = np.zeros([4, 4]) np.fill_diagonal(wcs.wcs.pc, np.arange(1, 5)) pc = wcs.wcs.pc # for later use below swapped = wcs.swapaxes(0, 1) assert np.all(swapped.wcs.get_pc().diagonal() == np.array([2, 1, 3, 4])) swapped = wcs.swapaxes(0, 3) assert np.all(swapped.wcs.get_pc().diagonal() == np.array([4, 2, 3, 1])) swapped = wcs.swapaxes(2, 3) assert np.all(swapped.wcs.get_pc().diagonal() == np.array([1, 2, 4, 3])) wcs = WCS(naxis=4) wcs.wcs.cd = pc swapped = wcs.swapaxes(0, 1) assert np.all(swapped.wcs.get_pc().diagonal() == np.array([2, 1, 3, 4])) swapped = wcs.swapaxes(0, 3) assert np.all(swapped.wcs.get_pc().diagonal() == np.array([4, 2, 3, 1])) swapped = wcs.swapaxes(2, 3) assert np.all(swapped.wcs.get_pc().diagonal() == np.array([1, 2, 4, 3]))
def test_wcs_to_celestial_frame_extend(): mywcs = WCS(naxis=2) mywcs.wcs.ctype = ['XOFFSET', 'YOFFSET'] mywcs.wcs.set() with pytest.raises(ValueError): wcs_to_celestial_frame(mywcs) class OffsetFrame: pass def identify_offset(wcs): if wcs.wcs.ctype[0].endswith('OFFSET') and wcs.wcs.ctype[1].endswith('OFFSET'): return OffsetFrame() with custom_wcs_to_frame_mappings(identify_offset): frame = wcs_to_celestial_frame(mywcs) assert isinstance(frame, OffsetFrame) # Check that things are back to normal after the context manager with pytest.raises(ValueError): wcs_to_celestial_frame(mywcs)
def test_custom_ctype_to_ucd_mappings(): wcs = WCS(naxis=1) wcs.wcs.ctype = ['SPAM'] assert wcs.world_axis_physical_types == [None] # Check simple behavior with custom_ctype_to_ucd_mapping({'APPLE': 'food.fruit'}): assert wcs.world_axis_physical_types == [None] with custom_ctype_to_ucd_mapping({ 'APPLE': 'food.fruit', 'SPAM': 'food.spam' }): assert wcs.world_axis_physical_types == ['food.spam'] # Check nesting with custom_ctype_to_ucd_mapping({'SPAM': 'food.spam'}): with custom_ctype_to_ucd_mapping({'APPLE': 'food.fruit'}): assert wcs.world_axis_physical_types == ['food.spam'] with custom_ctype_to_ucd_mapping({'APPLE': 'food.fruit'}): with custom_ctype_to_ucd_mapping({'SPAM': 'food.spam'}): assert wcs.world_axis_physical_types == ['food.spam'] # Check priority in nesting with custom_ctype_to_ucd_mapping({'SPAM': 'notfood'}): with custom_ctype_to_ucd_mapping({'SPAM': 'food.spam'}): assert wcs.world_axis_physical_types == ['food.spam'] with custom_ctype_to_ucd_mapping({'SPAM': 'food.spam'}): with custom_ctype_to_ucd_mapping({'SPAM': 'notfood'}): assert wcs.world_axis_physical_types == ['notfood']
def dendro_import_hdf5(filename): """Import 'filename' and construct a dendrogram from it""" import h5py from astropy.wcs.wcs import WCS log.debug('Loading HDF5 file from disk...') with h5py.File(filename, 'r') as h5f: newick = h5f['newick'][()] data = h5f['data'][()] index_map = h5f['index_map'][()] params = {} if 'min_value' in h5f.attrs: params['min_value'] = h5f.attrs['min_value'] params['min_delta'] = h5f.attrs['min_delta'] params['min_npix'] = h5f.attrs['min_npix'] try: wcs = WCS(h5f['wcs_header'][()]) except KeyError: wcs = None log.debug('Parsing dendrogram...') return parse_dendrogram(newick, data, index_map, params, wcs)
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_slice_with_sip(): mywcs = WCS(naxis=2) mywcs.wcs.crval = [1, 1] mywcs.wcs.cdelt = [0.1, 0.1] mywcs.wcs.crpix = [1, 1] mywcs._naxis = [1000, 500] mywcs.wcs.ctype = ['RA---TAN-SIP', 'DEC--TAN-SIP'] a = np.array( [[0, 0, 5.33092692e-08, 3.73753773e-11, -2.02111473e-13], [0, 2.44084308e-05, 2.81394789e-11, 5.17856895e-13, 0.0], [-2.41334657e-07, 1.29289255e-10, 2.35753629e-14, 0.0, 0.0], [-2.37162007e-10, 5.43714947e-13, 0.0, 0.0, 0.0], [-2.81029767e-13, 0.0, 0.0, 0.0, 0.0]] ) b = np.array( [[0, 0, 2.99270374e-05, -2.38136074e-10, 7.23205168e-13], [0, -1.71073858e-07, 6.31243431e-11, -5.16744347e-14, 0.0], [6.95458963e-06, -3.08278961e-10, -1.75800917e-13, 0.0, 0.0], [3.51974159e-11, 5.60993016e-14, 0.0, 0.0, 0.0], [-5.92438525e-13, 0.0, 0.0, 0.0, 0.0]] ) mywcs.sip = Sip(a, b, None, None, mywcs.wcs.crpix) mywcs.wcs.set() pscale = 0.1 # from cdelt slice_wcs = mywcs.slice([slice(1, None), slice(0, None)]) # test that CRPIX maps to CRVAL: assert_allclose( slice_wcs.all_pix2world(*slice_wcs.wcs.crpix, 1), slice_wcs.wcs.crval, rtol=0.0, atol=1e-6 * pscale ) slice_wcs = mywcs.slice([slice(1, None, 2), slice(0, None, 4)]) # test that CRPIX maps to CRVAL: assert_allclose( slice_wcs.all_pix2world(*slice_wcs.wcs.crpix, 1), slice_wcs.wcs.crval, rtol=0.0, atol=1e-6 * pscale )
CRVAL2 = 20 CRVAL3 = 25 CRPIX1 = 30 CRPIX2 = 40 CRPIX3 = 45 CDELT1 = -0.1 CDELT2 = 0.5 CDELT3 = 0.1 CUNIT1 = deg CUNIT2 = Hz CUNIT3 = deg """ with warnings.catch_warnings(): warnings.simplefilter('ignore', VerifyWarning) WCS_SPECTRAL_CUBE = WCS(Header.fromstring(HEADER_SPECTRAL_CUBE, sep='\n')) WCS_SPECTRAL_CUBE.pixel_bounds = [(-1, 11), (-2, 18), (5, 15)] def test_invalid_slices(): with pytest.raises(IndexError): SlicedLowLevelWCS(WCS_SPECTRAL_CUBE, [None, None, [False, False, False]]) with pytest.raises(IndexError): SlicedLowLevelWCS(WCS_SPECTRAL_CUBE, [None, None, slice(None, None, 2)]) with pytest.raises(IndexError): SlicedLowLevelWCS(WCS_SPECTRAL_CUBE, [None, None, 1000.100])
def getCatalogStars(header, limit=500, limitFactor=2.5, maxVmag=None, retVmag=False, retry=1): """ Queries the Vizier catalog and retrieves stars for the sky area as defined by the given WCS header. :param header: FITS WCS header, must include IMAGEW and IMAGEH :param limit: maximum number of stars to return (optional) :param limitFactor: how much more stars to query for; The search region is a circle. To reach the desired limit of returned stars after filtering stars outside the image bounds more stars have to be queried for initially. :param maxVmag: maximum magnitude of stars (optional) :param retVmag: if true, include Vmag in the result tuple :param retry: how many times to retry in case of errors (e.g. network problems) :rtype: tuple (x, y) or (x, y, vmag) sorted by decreasing brightness, origin (0,0) Note that vmag is a masked array and can contain masked values. """ column_filters = {} if maxVmag: column_filters['VTmag'] = '<' + str(maxVmag) w, h = header['IMAGEW'], header['IMAGEH'] # Step 1: query stars in tycho-2 online catalog, ordered by Vmag # We add a small border here. This is useful for # circling stars in an image, such that half circles # are drawn at the image corners instead of suddenly disappearing # circles. catalog = 'I/259/tyc2' centerRa, centerDec = getCenterRADec(header) border = 0.01 * w radiusBorder = getPixelScale(header)*border radius = getRadius(header) + radiusBorder if limit: # we have to query more stars as our search region is a circle # and we are filtering stars out afterwards row_limit = int(limitFactor*limit) else: row_limit = -1 print('Querying Vizier...') v = Vizier(columns=['_RAJ2000', '_DEJ2000', '+VTmag'], column_filters=column_filters, row_limit=row_limit) try: result = v.query_region(coord.SkyCoord(ra=centerRa, dec=centerDec, unit=(u.deg, u.deg), frame='icrs'), radius=radius*u.deg, catalog=catalog)[0] except Exception as e: if retry > 0: print(repr(e)) print('retrying...') time.sleep(2) # astroquery may have stored a corrupt response in its cache, # so we try again without using the cache # see https://github.com/astropy/astroquery/issues/465 with suspend_cache(Vizier): return getCatalogStars(header, limit, limitFactor, maxVmag, retVmag, retry-1) print('Vizier query_region: ra={}, dec={}, radius={}, column_filters={}, row_limit={}, catalog={}'. format(centerRa, centerDec, radius, column_filters, row_limit, catalog), file=sys.stderr) raise vmag = result['VTmag'] ra = result['_RAJ2000'] dec = result['_DEJ2000'] print(len(vmag), 'stars received') # Step 2: compute pixel coordinates for stars wcs = WCS(header) x, y = wcs.wcs_world2pix(ra, dec, 0) # Step 3: remove stars outside the image bounds # As above, we leave a small border here. inside = (-border <= y) & (y < h+border) & (-border <= x) & (x < w+border) x = x[inside] y = y[inside] vmag = vmag[inside] print(len(vmag), 'stars left after filtering') if limit and len(vmag) < limit: print('NOTE: limit of {} not reached, debug info follows'.format(limit), file=sys.stderr) print('Vizier query_region: ra={}, dec={}, radius={}, column_filters={}, row_limit={}, catalog={}'. format(centerRa, centerDec, radius, column_filters, row_limit, catalog), file=sys.stderr) print('filter: border={}, width={}, height={}'.format(border,w,h)) # Step 4: apply limit by removing the faintest stars if limit: x = x[:limit] y = y[:limit] vmag = vmag[:limit] if retVmag: return x, y, vmag else: return x, y
def test_has_celestial(ctype, cel): mywcs = WCS(naxis=len(ctype)) mywcs.wcs.ctype = ctype assert mywcs.has_celestial == cel
def test_pixscale_cd(): mywcs = WCS(naxis=2) mywcs.wcs.cd = [[-0.1, 0], [0, 0.2]] mywcs.wcs.ctype = ['RA---TAN', 'DEC--TAN'] assert_almost_equal(proj_plane_pixel_scales(mywcs), (0.1, 0.2))
def identify_offset(frame, projection=None): if isinstance(frame, OffsetFrame): wcs = WCS(naxis=2) wcs.wcs.ctype = ['XOFFSET', 'YOFFSET'] return wcs
def test_wcs_to_celestial_frame(): # Import astropy.coordinates here to avoid circular imports from astropy.coordinates.builtin_frames import ICRS, ITRS, FK5, FK4, Galactic mywcs = WCS(naxis=2) mywcs.wcs.set() with pytest.raises(ValueError) as exc: assert wcs_to_celestial_frame(mywcs) is None assert exc.value.args[0] == "Could not determine celestial frame corresponding to the specified WCS object" mywcs = WCS(naxis=2) mywcs.wcs.ctype = ['XOFFSET', 'YOFFSET'] mywcs.wcs.set() with pytest.raises(ValueError): assert wcs_to_celestial_frame(mywcs) is None mywcs = WCS(naxis=2) mywcs.wcs.ctype = ['RA---TAN', 'DEC--TAN'] mywcs.wcs.set() frame = wcs_to_celestial_frame(mywcs) assert isinstance(frame, ICRS) mywcs = WCS(naxis=2) mywcs.wcs.ctype = ['RA---TAN', 'DEC--TAN'] mywcs.wcs.equinox = 1987. mywcs.wcs.set() print(mywcs.to_header()) frame = wcs_to_celestial_frame(mywcs) assert isinstance(frame, FK5) assert frame.equinox == Time(1987., format='jyear') mywcs = WCS(naxis=2) mywcs.wcs.ctype = ['RA---TAN', 'DEC--TAN'] mywcs.wcs.equinox = 1982 mywcs.wcs.set() frame = wcs_to_celestial_frame(mywcs) assert isinstance(frame, FK4) assert frame.equinox == Time(1982., format='byear') mywcs = WCS(naxis=2) mywcs.wcs.ctype = ['GLON-SIN', 'GLAT-SIN'] mywcs.wcs.set() frame = wcs_to_celestial_frame(mywcs) assert isinstance(frame, Galactic) mywcs = WCS(naxis=2) mywcs.wcs.ctype = ['TLON-CAR', 'TLAT-CAR'] mywcs.wcs.dateobs = '2017-08-17T12:41:04.430' mywcs.wcs.set() frame = wcs_to_celestial_frame(mywcs) assert isinstance(frame, ITRS) assert frame.obstime == Time('2017-08-17T12:41:04.430') for equinox in [np.nan, 1987, 1982]: mywcs = WCS(naxis=2) mywcs.wcs.ctype = ['RA---TAN', 'DEC--TAN'] mywcs.wcs.radesys = 'ICRS' mywcs.wcs.equinox = equinox mywcs.wcs.set() frame = wcs_to_celestial_frame(mywcs) assert isinstance(frame, ICRS) # Flipped order mywcs = WCS(naxis=2) mywcs.wcs.ctype = ['DEC--TAN', 'RA---TAN'] mywcs.wcs.set() frame = wcs_to_celestial_frame(mywcs) assert isinstance(frame, ICRS) # More than two dimensions mywcs = WCS(naxis=3) mywcs.wcs.ctype = ['DEC--TAN', 'VELOCITY', 'RA---TAN'] mywcs.wcs.set() frame = wcs_to_celestial_frame(mywcs) assert isinstance(frame, ICRS) mywcs = WCS(naxis=3) mywcs.wcs.ctype = ['GLAT-CAR', 'VELOCITY', 'GLON-CAR'] mywcs.wcs.set() frame = wcs_to_celestial_frame(mywcs) assert isinstance(frame, Galactic)
def test_unrecognized_unit(): # TODO: Determine whether the following behavior is desirable wcs = WCS(naxis=1) with pytest.warns(UnitsWarning): wcs.wcs.cunit = ['bananas // sekonds'] assert wcs.world_axis_units == ['bananas // sekonds']
def test_celestial(): mywcs = WCS(naxis=4) mywcs.wcs.ctype = ['RA---TAN', 'DEC--TAN', 'VOPT', 'STOKES'] cel = mywcs.celestial assert tuple(cel.wcs.ctype) == ('RA---TAN', 'DEC--TAN') assert cel.axis_type_names == ['RA', 'DEC']
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
def test_phys_type_polarization(header_polarized): w = WCS(header_polarized) assert w.world_axis_physical_types[2] == 'phys.polarization.stokes'
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 time_1d_wcs(header_time_1d): with warnings.catch_warnings(): warnings.simplefilter('ignore', FITSFixedWarning) return WCS(header_time_1d)
def test_wcs_to_celestial_frame(): # Import astropy.coordinates here to avoid circular imports from astropy.coordinates.builtin_frames import ICRS, ITRS, FK5, FK4, Galactic mywcs = WCS(naxis=2) mywcs.wcs.set() with pytest.raises(ValueError, match="Could not determine celestial frame " "corresponding to the specified WCS object"): assert wcs_to_celestial_frame(mywcs) is None mywcs = WCS(naxis=2) mywcs.wcs.ctype = ['XOFFSET', 'YOFFSET'] mywcs.wcs.set() with pytest.raises(ValueError): assert wcs_to_celestial_frame(mywcs) is None mywcs = WCS(naxis=2) mywcs.wcs.ctype = ['RA---TAN', 'DEC--TAN'] mywcs.wcs.set() frame = wcs_to_celestial_frame(mywcs) assert isinstance(frame, ICRS) mywcs = WCS(naxis=2) mywcs.wcs.ctype = ['RA---TAN', 'DEC--TAN'] mywcs.wcs.equinox = 1987. mywcs.wcs.set() print(mywcs.to_header()) frame = wcs_to_celestial_frame(mywcs) assert isinstance(frame, FK5) assert frame.equinox == Time(1987., format='jyear') mywcs = WCS(naxis=2) mywcs.wcs.ctype = ['RA---TAN', 'DEC--TAN'] mywcs.wcs.equinox = 1982 mywcs.wcs.set() frame = wcs_to_celestial_frame(mywcs) assert isinstance(frame, FK4) assert frame.equinox == Time(1982., format='byear') mywcs = WCS(naxis=2) mywcs.wcs.ctype = ['GLON-SIN', 'GLAT-SIN'] mywcs.wcs.set() frame = wcs_to_celestial_frame(mywcs) assert isinstance(frame, Galactic) mywcs = WCS(naxis=2) mywcs.wcs.ctype = ['TLON-CAR', 'TLAT-CAR'] mywcs.wcs.dateobs = '2017-08-17T12:41:04.430' mywcs.wcs.set() frame = wcs_to_celestial_frame(mywcs) assert isinstance(frame, ITRS) assert frame.obstime == Time('2017-08-17T12:41:04.430') for equinox in [np.nan, 1987, 1982]: mywcs = WCS(naxis=2) mywcs.wcs.ctype = ['RA---TAN', 'DEC--TAN'] mywcs.wcs.radesys = 'ICRS' mywcs.wcs.equinox = equinox mywcs.wcs.set() frame = wcs_to_celestial_frame(mywcs) assert isinstance(frame, ICRS) # Flipped order mywcs = WCS(naxis=2) mywcs.wcs.ctype = ['DEC--TAN', 'RA---TAN'] mywcs.wcs.set() frame = wcs_to_celestial_frame(mywcs) assert isinstance(frame, ICRS) # More than two dimensions mywcs = WCS(naxis=3) mywcs.wcs.ctype = ['DEC--TAN', 'VELOCITY', 'RA---TAN'] mywcs.wcs.set() frame = wcs_to_celestial_frame(mywcs) assert isinstance(frame, ICRS) mywcs = WCS(naxis=3) mywcs.wcs.ctype = ['GLAT-CAR', 'VELOCITY', 'GLON-CAR'] mywcs.wcs.set() frame = wcs_to_celestial_frame(mywcs) assert isinstance(frame, Galactic)
from astropy.tests.helper import assert_quantity_allclose from astropy.units import Quantity from astropy.coordinates import ICRS, FK5, Galactic, SkyCoord from astropy.io.fits import Header from astropy.io.fits.verify import VerifyWarning from astropy.units.core import UnitsWarning from astropy.utils.data import get_pkg_data_filename from astropy.wcs.wcs import WCS, FITSFixedWarning from astropy.wcs.wcsapi.fitswcs import custom_ctype_to_ucd_mapping from astropy.wcs._wcs import __version__ as wcsver ############################################################################### # The following example is the simplest WCS with default values ############################################################################### WCS_EMPTY = WCS(naxis=1) WCS_EMPTY.wcs.crpix = [1] def test_empty(): wcs = WCS_EMPTY # Low-level API assert wcs.pixel_n_dim == 1 assert wcs.world_n_dim == 1 assert wcs.array_shape is None assert wcs.pixel_shape is None assert wcs.world_axis_physical_types == [None] assert wcs.world_axis_units == ['']
def getCatalogStars(header, limit=500, limitFactor=2.5, maxVmag=None, retVmag=False, retry=1): """ Queries the Vizier catalog and retrieves stars for the sky area as defined by the given WCS header. :param header: FITS WCS header, must include IMAGEW and IMAGEH :param limit: maximum number of stars to return (optional) :param limitFactor: how much more stars to query for; The search region is a circle. To reach the desired limit of returned stars after filtering stars outside the image bounds more stars have to be queried for initially. :param maxVmag: maximum magnitude of stars (optional) :param retVmag: if true, include Vmag in the result tuple :param retry: how many times to retry in case of errors (e.g. network problems) :rtype: tuple (x, y) or (x, y, vmag) sorted by decreasing brightness, origin (0,0) Note that vmag is a masked array and can contain masked values. """ column_filters = {} if maxVmag: column_filters['VTmag'] = '<' + str(maxVmag) w, h = header['IMAGEW'], header['IMAGEH'] # Step 1: query stars in tycho-2 online catalog, ordered by Vmag # We add a small border here. This is useful for # circling stars in an image, such that half circles # are drawn at the image corners instead of suddenly disappearing # circles. catalog = 'I/259/tyc2' centerRa, centerDec = getCenterRADec(header) border = 0.01 * w radiusBorder = getPixelScale(header) * border radius = getRadius(header) + radiusBorder if limit: # we have to query more stars as our search region is a circle # and we are filtering stars out afterwards row_limit = int(limitFactor * limit) else: row_limit = -1 print('Querying Vizier...') v = Vizier(columns=['_RAJ2000', '_DEJ2000', '+VTmag'], column_filters=column_filters, row_limit=row_limit) try: result = v.query_region(coord.SkyCoord(ra=centerRa, dec=centerDec, unit=(u.deg, u.deg), frame='icrs'), radius=radius * u.deg, catalog=catalog)[0] except Exception as e: if retry > 0: print(repr(e)) print('retrying...') time.sleep(2) # astroquery may have stored a corrupt response in its cache, # so we try again without using the cache # see https://github.com/astropy/astroquery/issues/465 with suspend_cache(Vizier): return getCatalogStars(header, limit, limitFactor, maxVmag, retVmag, retry - 1) print( 'Vizier query_region: ra={}, dec={}, radius={}, column_filters={}, row_limit={}, catalog={}' .format(centerRa, centerDec, radius, column_filters, row_limit, catalog), file=sys.stderr) raise vmag = result['VTmag'] ra = result['_RAJ2000'] dec = result['_DEJ2000'] print(len(vmag), 'stars received') # Step 2: compute pixel coordinates for stars wcs = WCS(header) x, y = wcs.wcs_world2pix(ra, dec, 0) # Step 3: remove stars outside the image bounds # As above, we leave a small border here. inside = (-border <= y) & (y < h + border) & (-border <= x) & (x < w + border) x = x[inside] y = y[inside] vmag = vmag[inside] print(len(vmag), 'stars left after filtering') if limit and len(vmag) < limit: print( 'NOTE: limit of {} not reached, debug info follows'.format(limit), file=sys.stderr) print( 'Vizier query_region: ra={}, dec={}, radius={}, column_filters={}, row_limit={}, catalog={}' .format(centerRa, centerDec, radius, column_filters, row_limit, catalog), file=sys.stderr) print('filter: border={}, width={}, height={}'.format(border, w, h)) # Step 4: apply limit by removing the faintest stars if limit: x = x[:limit] y = y[:limit] vmag = vmag[:limit] if retVmag: return x, y, vmag else: return x, y
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)