diff --git a/stellarphot/core.py b/stellarphot/core.py index 7e842074..e350c70f 100644 --- a/stellarphot/core.py +++ b/stellarphot/core.py @@ -5,6 +5,7 @@ from astropy.table import Column, QTable, Table from astropy.time import Time from astropy.units import Quantity +from astropy.wcs import WCS from astroquery.vizier import Vizier @@ -386,7 +387,7 @@ def clean(self, remove_rows_with_mask=False, **other_restrictions): comparison_func = comparisons[results.group(1)] comparison_value = results.group(2) new_keepers = comparison_func(self[column], - float(comparison_value)) + float(comparison_value)) keepers = keepers & new_keepers return self[keepers] @@ -867,7 +868,7 @@ def _tidy_vizier_catalog(data, mag_column_regex, color_column_regex): passband in zip(mag_err_cols, passbands)} # All columns except those we have renamed should be preserved, so make - # a list of them for us in wide_to_long. + # a list of them for use in wide_to_long. id_columns = set(data.colnames) - set(orig_cols) - set(mag_err_cols) # Make the input data into a Pandas DataFrame @@ -976,7 +977,7 @@ def from_vizier(cls, else: # Find the center of the frame shape = (header_or_center['NAXIS2'], header_or_center['NAXIS1']) - center = header_or_center.wcs.pixel_to_world(shape[1] / 2, + center = WCS(header_or_center).pixel_to_world(shape[1] / 2, shape[0] / 2) # Get catalog via cone search @@ -1012,7 +1013,7 @@ def from_vizier(cls, # desired. if clip_by_frame: cat_coords = SkyCoord(ra=cat['ra'], dec=cat['dec']) - x, y = header_or_center.wcs.all_world2pix(cat_coords.ra, cat_coords.dec, 0) + x, y = WCS(header_or_center).all_world2pix(cat_coords.ra, cat_coords.dec, 0) in_x = (x >= padding) & (x <= header_or_center.wcs.pixel_shape[0] - padding) in_y = (y >= padding) & (y <= header_or_center.wcs.pixel_shape[1] - padding) in_fov = in_x & in_y @@ -1021,6 +1022,47 @@ def from_vizier(cls, return cat +def apass_dr9(header_or_center, + radius=1 * u.degree, + clip_by_frame=False, + padding=100): + """ + Return the items from APASS DR9 that are within the search radius and + (optionally) within the field of view of a frame. + + Parameters + ---------- + header_or_center : FITS header or `astropy.coordinates.SkyCoord` + Either a FITS header with WCS information or a `SkyCoord` object. + The center of the frame or the input coordinate is the center + of the cone search. + + radius : `astropy.units.Quantity`, optional + Radius around which to search. + + clip_by_frame : bool, optional + If ``True``, only return items that are within the field of view + of the frame. Default is ``True``. + + padding : int, optional + Coordinates need to be at least this many pixels in from the edge + of the frame to be considered in the field of view. Default value + is 100. + + """ + apass_colnames = { + 'recno': 'id', # There is no APASS ID, this is the one generated by Vizier + 'RAJ2000': 'ra', + 'DEJ2000': 'dec', + } + return CatalogData.from_vizier(header_or_center, + 'II/336/apass9', + radius=radius, + clip_by_frame=clip_by_frame, + padding=padding, + colname_map=apass_colnames) + + class SourceListData(BaseEnhancedTable): """ A class to hold information on the source lists to pass to diff --git a/stellarphot/utils/tests/data/all_apass_ey_uma_sorted_ra_first_20.fits b/stellarphot/tests/data/all_apass_ey_uma_sorted_ra_first_20.fits similarity index 100% rename from stellarphot/utils/tests/data/all_apass_ey_uma_sorted_ra_first_20.fits rename to stellarphot/tests/data/all_apass_ey_uma_sorted_ra_first_20.fits diff --git a/stellarphot/utils/tests/data/sample_wcs_ey_uma.fits b/stellarphot/tests/data/sample_wcs_ey_uma.fits similarity index 100% rename from stellarphot/utils/tests/data/sample_wcs_ey_uma.fits rename to stellarphot/tests/data/sample_wcs_ey_uma.fits diff --git a/stellarphot/tests/test_core.py b/stellarphot/tests/test_core.py index 32cf2a01..dc201fb4 100644 --- a/stellarphot/tests/test_core.py +++ b/stellarphot/tests/test_core.py @@ -3,13 +3,15 @@ from astropy import units as u from astropy.table import Table, Column from astropy.time import Time -from astropy.io import ascii +from astropy.io import ascii, fits from astropy.coordinates import EarthLocation, SkyCoord +from astropy.nddata import CCDData from astropy.utils.data import get_pkg_data_filename +from astropy.wcs import WCS from pydantic import ValidationError from stellarphot.core import (Camera, BaseEnhancedTable, PhotometryData, - CatalogData, SourceListData) + CatalogData, SourceListData, apass_dr9) def test_camera_attributes(): @@ -567,6 +569,32 @@ def prepare_cat(cat): assert my_cat['Type'][0] == 'SRB' +def test_find_apass(): + CCD_SHAPE = [2048, 3073] + # This is really checking from APASS DR9 on Vizier, or at least that + # is where the "expected" data is drawn from. + expected_all = Table.read(get_pkg_data_filename('data/all_apass_ey_uma_sorted_ra_first_20.fits')) + + wcs_file = get_pkg_data_filename('data/sample_wcs_ey_uma.fits') + wcs = WCS(fits.open(wcs_file)[0].header) + wcs.pixel_shape = list(reversed(CCD_SHAPE)) + ccd = CCDData(data=np.zeros(CCD_SHAPE), wcs=wcs, unit='adu') + + # Turn this into an HDU to get the standard FITS image keywords + ccd_im = ccd.to_hdu() + all_apass = apass_dr9(ccd_im[0].header, radius=1 * u.deg) + + # Reference data was sorted by RA, first 20 entries kept + # There are 6 magnitude or color columns, so 6 * 20 = 120 rows + # in the resulting table. + all_apass.sort('ra') + all_apass = all_apass[:120] + + # It is hard to imagine the RAs matching and other entries not matching, + # so just check the RAs. + assert set(ra.value for ra in all_apass['ra']) == set(expected_all['RAJ2000']) + + # Load test apertures test_sl_data = ascii.read(get_pkg_data_filename('data/test_sourcelist.ecsv'), format='ecsv', diff --git a/stellarphot/utils/tests/test_catalog_search.py b/stellarphot/utils/tests/test_catalog_search.py index 7fe87b8b..61059f2b 100644 --- a/stellarphot/utils/tests/test_catalog_search.py +++ b/stellarphot/utils/tests/test_catalog_search.py @@ -109,25 +109,3 @@ def test_catalog_search_with_coord_and_frame_clip_fails(): _ = catalog_search(cen_coord, [4096, 4096], 'B/vsx/vsx', clip_by_frame=True) assert 'To clip entries by frame' in str(e.value) - - -def test_find_apass(): - # This is really checking from APASS DR9 on Vizier, or at least that - # is where the "expected" data is drawn from. - expected_all = Table.read(get_pkg_data_filename('data/all_apass_ey_uma_sorted_ra_first_20.fits')) - expected_low_error = Table.read(get_pkg_data_filename('data/low_error_apass_ey_uma_sorted_ra_first_20.fits')) - wcs_file = get_pkg_data_filename('data/sample_wcs_ey_uma.fits') - wcs = WCS(fits.open(wcs_file)[0].header) - wcs.pixel_shape = list(reversed(CCD_SHAPE)) - ccd = CCDData(data=np.zeros(CCD_SHAPE), wcs=wcs, unit='adu') - all_apass, apass_low_error = find_apass_stars(ccd) - # print(all_apass) - # REference data was sorted by RA, first 20 entries kept - all_apass.sort('RAJ2000') - all_apass = all_apass[:20] - apass_low_error.sort('RAJ2000') - apass_low_error = apass_low_error[:20] - # It is hard to imagine the RAs matching and other entries not matching, - # so just check the RAs. - assert all(all_apass['RAJ2000'] == expected_all['RAJ2000']) - assert all(apass_low_error['RAJ2000'] == expected_low_error['RAJ2000'])