def pixelCoordinatesFromRaDec(self, ra, dec): """ Convert RA, Dec into pixel coordinates on this detector @param [in] ra is a numpy array or a float indicating RA in radians @param [in] dec is a numpy array or a float indicating Dec in radians @param [out] xPix is a numpy array indicating the x pixel coordinate @param [out] yPix is a numpy array indicating the y pixel coordinate """ nameList = [self.name] if type(ra) is numpy.ndarray: nameList = nameList*len(ra) raLocal=ra decLocal=dec else: raLocal = numpy.array([ra]) decLocal = numpy.array([dec]) xPix, yPix = _pixelCoordsFromRaDec(raLocal, decLocal, chipNames=nameList, obs_metadata=self._obs_metadata, epoch=self._epoch, camera=self._afwCamera) return xPix, yPix
def pixelCoordinatesFromRaDec(self, ra, dec): """ Convert RA, Dec into pixel coordinates on this detector @param [in] ra is a numpy array or a float indicating RA in radians @param [in] dec is a numpy array or a float indicating Dec in radians @param [out] xPix is a numpy array indicating the x pixel coordinate @param [out] yPix is a numpy array indicating the y pixel coordinate """ nameList = [self.name] if type(ra) is np.ndarray: nameList = nameList * len(ra) raLocal = ra decLocal = dec else: raLocal = np.array([ra]) decLocal = np.array([dec]) xPix, yPix = _pixelCoordsFromRaDec(raLocal, decLocal, chipName=nameList, obs_metadata=self._obs_metadata, epoch=self._epoch, camera=self._afwCamera) return xPix, yPix
def _xy(self, ra, dec): """ This is a method required by the GalSim WCS API Convert ra, dec in radians into x, y in pixel space with crpix subtracted. """ chipNameList = [self.afwDetector.getName()] if type(ra) is np.ndarray: chipNameList = chipNameList * len(ra) xx, yy = _pixelCoordsFromRaDec(ra=ra, dec=dec, chipName=chipNameList, obs_metadata=self.obs_metadata, epoch=self.epoch, camera=self.afwCamera) if type(ra) is np.ndarray: return (xx - self.crpix1, yy - self.crpix2) else: return (xx[0] - self.crpix1, yy - self.crpix2)
def _pixelCoordsFromRaDec(self, ra, dec, pm_ra=None, pm_dec=None, parallax=None, v_rad=None, obs_metadata=None, chipName=None, epoch=2000.0, includeDistortion=True): """ Get the pixel positions (or nan if not on a chip) for objects based on their RA, and Dec (in radians) Parameters ---------- ra is in radians in the International Celestial Reference System. Can be either a float or a numpy array. dec is in radians in the International Celestial Reference System. Can be either a float or a numpy array. pm_ra is proper motion in RA multiplied by cos(Dec) (radians/yr) Can be a numpy array or a number or None (default=None). pm_dec is proper motion in dec (radians/yr) Can be a numpy array or a number or None (default=None). parallax is parallax in radians Can be a numpy array or a number or None (default=None). v_rad is radial velocity (km/s) Can be a numpy array or a number or None (default=None). obs_metadata is an ObservationMetaData characterizing the telescope pointing. epoch is the epoch in Julian years of the equinox against which RA is measured. Default is 2000. chipName designates the names of the chips on which the pixel coordinates will be reckoned. Can be either single value, an array, or None. If an array, there must be as many chipNames as there are (RA, Dec) pairs. If a single value, all of the pixel coordinates will be reckoned on the same chip. If None, this method will calculate which chip each(RA, Dec) pair actually falls on, and return pixel coordinates for each (RA, Dec) pair on the appropriate chip. Default is None. includeDistortion is a boolean. If True (default), then this method will return the true pixel coordinates with optical distortion included. If False, this method will return TAN_PIXEL coordinates, which are the pixel coordinates with estimated optical distortion removed. See the documentation in afw.cameraGeom for more details. Returns ------- a 2-D numpy array in which the first row is the x pixel coordinate and the second row is the y pixel coordinate """ return coordUtils._pixelCoordsFromRaDec( ra, dec, pm_ra=pm_ra, pm_dec=pm_dec, parallax=parallax, v_rad=v_rad, obs_metadata=obs_metadata, chipName=chipName, camera=self._camera, epoch=epoch, includeDistortion=includeDistortion)
def testUtilityMethods(self): """ Generate a catalog using the methods from AstrometryUtils.py and CameraUtils.py. Read that data in, and then recalculate the values 'by hand' to make sure that they are consistent. """ catName = os.path.join(getPackageDir('sims_catUtils'), 'tests', 'scratchSpace', 'AstrometryUtilityCatalog.txt') if os.path.exists(catName): os.unlink(catName) self.cat.write_catalog(catName) dtype = [('id', int), ('raICRS', float), ('decICRS', float), ('parallax', float), ('radial_velocity', float), ('x_pupil', float), ('y_pupil', float), ('chipName', str, 11), ('xPix', float), ('yPix', float), ('xFocalPlane', float), ('yFocalPlane', float)] baselineData = np.genfromtxt(catName, dtype=dtype, delimiter=';') self.assertGreater(len(baselineData), 0) pupilTest = _pupilCoordsFromRaDec(baselineData['raICRS'], baselineData['decICRS'], parallax=baselineData['parallax'], v_rad=baselineData['radial_velocity'], obs_metadata=self.obs_metadata, epoch=2000.0) for (xxtest, yytest, xx, yy) in \ zip(pupilTest[0], pupilTest[1], baselineData['x_pupil'], baselineData['y_pupil']): self.assertAlmostEqual(xxtest, xx, 6) self.assertAlmostEqual(yytest, yy, 6) focalTest = focalPlaneCoordsFromPupilCoords(pupilTest[0], pupilTest[1], camera=self.cat.camera) focalRa = _focalPlaneCoordsFromRaDec(baselineData['raICRS'], baselineData['decICRS'], parallax=baselineData['parallax'], v_rad=baselineData['radial_velocity'], epoch=self.cat.db_obj.epoch, obs_metadata=self.cat.obs_metadata, camera=self.cat.camera) for (xxtest, yytest, xxra, yyra, xx, yy) in \ zip(focalTest[0], focalTest[1], focalRa[0], focalRa[1], baselineData['xFocalPlane'], baselineData['yFocalPlane']): self.assertAlmostEqual(xxtest, xx, 6) self.assertAlmostEqual(yytest, yy, 6) self.assertAlmostEqual(xxra, xx, 6) self.assertAlmostEqual(yyra, yy, 6) pixTest = pixelCoordsFromPupilCoords(pupilTest[0], pupilTest[1], camera=self.cat.camera) pixTestRaDec = _pixelCoordsFromRaDec(baselineData['raICRS'], baselineData['decICRS'], parallax=baselineData['parallax'], v_rad=baselineData['radial_velocity'], epoch=self.cat.db_obj.epoch, obs_metadata=self.cat.obs_metadata, camera=self.cat.camera) for (xxtest, yytest, xxra, yyra, xx, yy) in \ zip(pixTest[0], pixTest[1], pixTestRaDec[0], pixTestRaDec[1], baselineData['xPix'], baselineData['yPix']): if not np.isnan(xx) and not np.isnan(yy): self.assertAlmostEqual(xxtest, xx, 5) self.assertAlmostEqual(yytest, yy, 5) self.assertAlmostEqual(xxra, xx, 5) self.assertAlmostEqual(yyra, yy, 5) else: np.testing.assert_equal(xx, np.NaN) np.testing.assert_equal(yy, np.NaN) np.testing.assert_equal(xxra, np.NaN) np.testing.assert_equal(yyra, np.NaN) np.testing.assert_equal(xxtest, np.NaN) np.testing.assert_equal(yytest, np.NaN) nameTest = chipNameFromPupilCoords(pupilTest[0], pupilTest[1], camera=self.cat.camera) nameRA = _chipNameFromRaDec(baselineData['raICRS'], baselineData['decICRS'], epoch=self.cat.db_obj.epoch, obs_metadata=self.cat.obs_metadata, camera=self.cat.camera) is_none = 0 for (ntest, nra, ncontrol) in zip(nameTest, nameRA, baselineData['chipName']): if ncontrol != 'None': self.assertEqual(ntest, ncontrol) self.assertEqual(nra, ncontrol) else: is_none += 1 self.assertIsNone(ntest) self.assertIsNone(nra) self.assertGreater(is_none, 0) self.assertLess(is_none, len(baselineData)) if os.path.exists(catName): os.unlink(catName)
def test_pixel_coords_from_ra_dec_radians(self): """ Test that _pixelCoordsFromRaDec and _pixelCoordsFromRaDecLSST agree """ raP = 74.2 decP = 13.0 obs = ObservationMetaData(pointingRA=raP, pointingDec=decP, rotSkyPos=13.0, mjd=43441.0) n_obj = 1000 rng = np.random.RandomState(83241) rr = rng.random_sample(n_obj) * 1.75 theta = rng.random_sample(n_obj) * 2.0 * np.pi ra_list = np.radians(raP + rr * np.cos(theta)) dec_list = np.radians(decP + rr * np.sin(theta)) x_pix, y_pix = _pixelCoordsFromRaDec(ra_list, dec_list, obs_metadata=obs, camera=self.camera) self.assertLessEqual(len(np.where(np.isnan(x_pix))[0]), n_obj / 10) self.assertLessEqual(len(np.where(np.isnan(y_pix))[0]), n_obj / 10) x_pix_test, y_pix_test = _pixelCoordsFromRaDecLSST(ra_list, dec_list, obs_metadata=obs) np.testing.assert_array_equal(x_pix, x_pix_test) np.testing.assert_array_equal(y_pix, y_pix_test) # test when we force a chipName x_pix, y_pix = _pixelCoordsFromRaDec(ra_list, dec_list, chipName=['R:2,2 S:1,1'], obs_metadata=obs, camera=self.camera) self.assertLessEqual(len(np.where(np.isnan(x_pix))[0]), n_obj / 10) self.assertLessEqual(len(np.where(np.isnan(y_pix))[0]), n_obj / 10) x_pix_test, y_pix_test = _pixelCoordsFromRaDecLSST( ra_list, dec_list, chipName=['R:2,2 S:1,1'], obs_metadata=obs) np.testing.assert_array_equal(x_pix, x_pix_test) np.testing.assert_array_equal(y_pix, y_pix_test) # test without distortion x_pix, y_pix = _pixelCoordsFromRaDec(ra_list, dec_list, obs_metadata=obs, camera=self.camera, includeDistortion=False) self.assertLessEqual(len(np.where(np.isnan(x_pix))[0]), n_obj / 10) self.assertLessEqual(len(np.where(np.isnan(y_pix))[0]), n_obj / 10) x_pix_test, y_pix_test = _pixelCoordsFromRaDecLSST( ra_list, dec_list, obs_metadata=obs, includeDistortion=False) np.testing.assert_array_equal(x_pix, x_pix_test) np.testing.assert_array_equal(y_pix, y_pix_test) # test that exceptions are raised when incomplete ObservationMetaData are used obs = ObservationMetaData(pointingRA=raP, pointingDec=decP, mjd=59580.0) with self.assertRaises(RuntimeError) as context: _pixelCoordsFromRaDecLSST(ra_list, dec_list, obs_metadata=obs) self.assertIn("rotSkyPos", context.exception.args[0]) obs = ObservationMetaData(pointingRA=raP, pointingDec=decP, rotSkyPos=35.0) with self.assertRaises(RuntimeError) as context: _pixelCoordsFromRaDecLSST(ra_list, dec_list, obs_metadata=obs) self.assertIn("mjd", context.exception.args[0]) with self.assertRaises(RuntimeError) as context: _pixelCoordsFromRaDecLSST(ra_list, dec_list) self.assertIn("ObservationMetaData", context.exception.args[0]) # check that exceptions are raised when ra_list, dec_list are of the wrong shape obs = ObservationMetaData(pointingRA=raP, pointingDec=decP, rotSkyPos=24.0, mjd=43000.0) with self.assertRaises(RuntimeError) as context: _pixelCoordsFromRaDecLSST(ra_list, dec_list[:5], obs_metadata=obs) self.assertIn("pixelCoordsFromRaDecLSST", context.exception.args[0])
def check_placement(self, imageName, raList, decList, fwhmList, countList, gain, detector, camera, obs, epoch=2000.0): """ Read in a FITS image and a list of objects meant to be on that image. Verify that the objects were placed at the correct pixel by counting up all of the flux within 2 fwhm of each object's expected location and verifying it with the counts expected for that object. @param [in] imageName is the name of the FITS file to be read in @param [in] raList is a numpy array of the RA coordinates of the objects in the image (in radians) @param [in] decList is a numpy array of the Dec coordinates of the objects in the image (in radians) @param [in] fwhmList is a list of the Full Width at Half Maximum of each object in arcseconds @param [in] countList is a list of the counts expected for each object @param [in] gain is the gain of the detector (electrons per ADU) @param [in] detector is an instantiation of the afw.cameraGeom Detector class characterizing the detector corresponding to this image @param [in] camera is an instantiation of the afw.cameraGeom Camera class characterizing the camera to which detector belongs @param [in] obs is an instantiation of ObservationMetaData characterizing the telescope pointing @param [in] epoch is the epoch in Julian years of the equinox against which RA and Dec are measured. Raises an exception of the counts detected for each object differs from the expected amount by more than 3 sigma. """ im = afwImage.ImageF(imageName).getArray() activePixels = np.where(im > 1.0e-10) # I know this seems backwards, but the way numpy handles arrays, # the first index is the row (i.e. the y coordinate) imXList = activePixels[1] imYList = activePixels[0] nameList = [detector.getName()]*len(raList) xPixList, yPixList = _pixelCoordsFromRaDec(raList, decList, chipName=nameList, camera=camera, obs_metadata=obs, epoch=epoch) for rr, dd, xx, yy, fwhm, cc in \ zip(raList, decList, xPixList, yPixList, fwhmList, countList): countSigma = np.sqrt(cc/gain) imNameList = [detector.getName()]*len(imXList) raImList, decImList = _raDecFromPixelCoords(imXList, imYList, imNameList, camera=camera, obs_metadata=obs, epoch=epoch) distanceList = arcsecFromRadians(haversine(raImList, decImList, rr, dd)) fluxArray = np.array([im[imYList[ix]][imXList[ix]] for ix in range(len(distanceList)) if distanceList[ix] < 2.0*fwhm]) totalFlux = fluxArray.sum() msg = 'totalFlux %e should be %e diff/sigma %e' \ % (totalFlux, cc, np.abs(totalFlux-cc)/countSigma) self.assertLess(np.abs(totalFlux-cc), 3.0*countSigma, msg=msg)
def test_generic_camera_wrapper(self): """ Test that GalSimCameraWrapper wraps its methods as expected. This is mostly to catch changes in afw API. """ camera = camTestUtils.CameraWrapper().camera camera_wrapper = GalSimCameraWrapper(camera) obs_mjd = ObservationMetaData(mjd=60000.0) ra, dec = raDecFromAltAz(35.0, 112.0, obs_mjd) obs = ObservationMetaData(pointingRA=ra, pointingDec=dec, mjd=obs_mjd.mjd, rotSkyPos=22.4) rng = np.random.RandomState(8124) for detector in camera: name = detector.getName() bbox = camera[name].getBBox() bbox_wrapper = camera_wrapper.getBBox(name) self.assertEqual(bbox.getMinX(), bbox_wrapper.getMinX()) self.assertEqual(bbox.getMaxX(), bbox_wrapper.getMaxX()) self.assertEqual(bbox.getMinY(), bbox_wrapper.getMinY()) self.assertEqual(bbox.getMaxY(), bbox_wrapper.getMaxY()) center_point = camera[name].getCenter(FOCAL_PLANE) pixel_system = camera[name].makeCameraSys(PIXELS) center_pix = camera.transform(center_point, FOCAL_PLANE, pixel_system) center_pix_wrapper = camera_wrapper.getCenterPixel(name) self.assertEqual(center_pix.getX(), center_pix_wrapper.getX()) self.assertEqual(center_pix.getY(), center_pix_wrapper.getY()) pupil_system = camera[name].makeCameraSys(FIELD_ANGLE) center_pupil = camera.transform(center_point, FOCAL_PLANE, pupil_system) center_pupil_wrapper = camera_wrapper.getCenterPupil(name) self.assertEqual(center_pupil.getX(), center_pupil_wrapper.getX()) self.assertEqual(center_pupil.getY(), center_pupil_wrapper.getY()) corner_pupil_wrapper = camera_wrapper.getCornerPupilList(name) corner_point_list = camera[name].getCorners(FOCAL_PLANE) for point in corner_point_list: point_pupil = camera.transform(point, FOCAL_PLANE, pupil_system) dd_min = 1.0e10 for wrapper_point in corner_pupil_wrapper: dd = np.sqrt( (point_pupil.getX() - wrapper_point.getX())**2 + (point_pupil.getY() - wrapper_point.getY())**2) if dd < dd_min: dd_min = dd self.assertLess(dd_min, 1.0e-20) xpix_min = None xpix_max = None ypix_min = None ypix_max = None focal_to_tan_pix = camera[name].getTransform( FOCAL_PLANE, TAN_PIXELS) for point in corner_point_list: pixel_point = focal_to_tan_pix.applyForward(point) xx = pixel_point.getX() yy = pixel_point.getY() if xpix_min is None or xx < xpix_min: xpix_min = xx if ypix_min is None or yy < ypix_min: ypix_min = yy if xpix_max is None or xx > xpix_max: xpix_max = xx if ypix_max is None or yy > ypix_max: ypix_max = yy pix_bounds_wrapper = camera_wrapper.getTanPixelBounds(name) self.assertEqual(pix_bounds_wrapper[0], xpix_min) self.assertEqual(pix_bounds_wrapper[1], xpix_max) self.assertEqual(pix_bounds_wrapper[2], ypix_min) self.assertEqual(pix_bounds_wrapper[3], ypix_max) x_pup = rng.random_sample(10) * 0.005 - 0.01 y_pup = rng.random_sample(10) * 0.005 - 0.01 x_pix, y_pix = pixelCoordsFromPupilCoords(x_pup, y_pup, chipName=name, camera=camera) (x_pix_wrapper, y_pix_wrapper) = camera_wrapper.pixelCoordsFromPupilCoords( x_pup, y_pup, name, obs) nan_x = np.where(np.isnan(x_pix)) self.assertEqual(len(nan_x[0]), 0) np.testing.assert_array_equal(x_pix, x_pix_wrapper) np.testing.assert_array_equal(y_pix, y_pix_wrapper) x_pix = rng.random_sample(10) * 100.0 - 200.0 y_pix = rng.random_sample(10) * 100.0 - 200.0 x_pup, y_pup = pupilCoordsFromPixelCoords(x_pix, y_pix, chipName=name, camera=camera) (x_pup_wrapper, y_pup_wrapper) = camera_wrapper.pupilCoordsFromPixelCoords( x_pix, y_pix, name, obs) nan_x = np.where(np.isnan(x_pup)) self.assertEqual(len(nan_x[0]), 0) np.testing.assert_array_equal(x_pup, x_pup_wrapper) np.testing.assert_array_equal(y_pup, y_pup_wrapper) ra, dec = raDecFromPixelCoords(x_pix, y_pix, name, camera=camera, obs_metadata=obs) (ra_wrapper, dec_wrapper) = camera_wrapper.raDecFromPixelCoords( x_pix, y_pix, name, obs) nan_ra = np.where(np.isnan(ra)) self.assertEqual(len(nan_ra[0]), 0) np.testing.assert_array_equal(ra, ra_wrapper) np.testing.assert_array_equal(dec, dec_wrapper) ra, dec = _raDecFromPixelCoords(x_pix, y_pix, name, camera=camera, obs_metadata=obs) (ra_wrapper, dec_wrapper) = camera_wrapper._raDecFromPixelCoords( x_pix, y_pix, name, obs) nan_ra = np.where(np.isnan(ra)) self.assertEqual(len(nan_ra[0]), 0) np.testing.assert_array_equal(ra, ra_wrapper) np.testing.assert_array_equal(dec, dec_wrapper) ra = obs.pointingRA + (rng.random_sample(10) * 150.0 - 100.0) / 160.0 dec = obs.pointingDec + (rng.random_sample(10) * 150.0 - 100.0) / 160.0 x_pix, y_pix = pixelCoordsFromRaDec(ra, dec, chipName=name, camera=camera, obs_metadata=obs) (x_pix_wrapper, y_pix_wrapper) = camera_wrapper.pixelCoordsFromRaDec( ra, dec, chipName=name, obs_metadata=obs) nan_x = np.where(np.isnan(x_pix)) self.assertEqual(len(nan_x[0]), 0) np.testing.assert_array_equal(x_pix, x_pix_wrapper) np.testing.assert_array_equal(y_pix, y_pix_wrapper) ra = np.radians(ra) dec = np.radians(dec) x_pix, y_pix = _pixelCoordsFromRaDec(ra, dec, chipName=name, camera=camera, obs_metadata=obs) (x_pix_wrapper, y_pix_wrapper) = camera_wrapper._pixelCoordsFromRaDec( ra, dec, chipName=name, obs_metadata=obs) nan_x = np.where(np.isnan(x_pix)) self.assertEqual(len(nan_x[0]), 0) np.testing.assert_array_equal(x_pix, x_pix_wrapper) np.testing.assert_array_equal(y_pix, y_pix_wrapper) del camera
def check_placement(self, imageName, raList, decList, fwhmList, countList, gain, detector, camera, obs, epoch=2000.0): """ Read in a FITS image and a list of objects meant to be on that image. Verify that the objects were placed at the correct pixel by counting up all of the flux within 2 fwhm of each object's expected location and verifying it with the counts expected for that object. @param [in] imageName is the name of the FITS file to be read in @param [in] raList is a numpy array of the RA coordinates of the objects in the image (in radians) @param [in] decList is a numpy array of the Dec coordinates of the objects in the image (in radians) @param [in] fwhmList is a list of the Full Width at Half Maximum of each object in arcseconds @param [in] countList is a list of the counts expected for each object @param [in] gain is the gain of the detector (electrons per ADU) @param [in] detector is an instantiation of the afw.cameraGeom Detector class characterizing the detector corresponding to this image @param [in] camera is an instantiation of the afw.cameraGeom Camera class characterizing the camera to which detector belongs @param [in] obs is an instantiation of ObservationMetaData characterizing the telescope pointing @param [in] epoch is the epoch in Julian years of the equinox against which RA and Dec are measured. Raises an exception of the counts detected for each object differs from the expected amount by more than 3 sigma. """ im = afwImage.ImageF(imageName).getArray() activePixels = numpy.where(im>1.0e-10) # I know this seems backwards, but the way numpy handles arrays, # the first index is the row (i.e. the y coordinate) imXList = activePixels[1] imYList = activePixels[0] nameList = [detector.getName()]*len(raList) xPixList, yPixList = _pixelCoordsFromRaDec(raList, decList, chipNames=nameList, camera=camera, obs_metadata=obs, epoch=epoch) for rr, dd, xx, yy, fwhm, cc in \ zip(raList, decList, xPixList, yPixList, fwhmList, countList): countSigma = numpy.sqrt(cc/gain) imNameList = [detector.getName()]*len(imXList) raImList, decImList = _raDecFromPixelCoords(imXList, imYList, imNameList, camera=camera, obs_metadata=obs, epoch=epoch) distanceList = arcsecFromRadians(haversine(raImList, decImList, rr, dd)) fluxArray = numpy.array( [im[imYList[ix]][imXList[ix]] \ for ix in range(len(distanceList)) \ if distanceList[ix]<2.0*fwhm] ) totalFlux = fluxArray.sum() self.assertTrue(numpy.abs(totalFlux-cc)<3.0*countSigma)
def testUtilityMethods(self): """ Generate a catalog using the methods from AstrometryUtils.py and CameraUtils.py. Read that data in, and then recalculate the values 'by hand' to make sure that they are consistent. """ self.cat.write_catalog("AstrometryTestCatalog.txt") dtype = [('id',int), ('raPhoSim',float), ('decPhoSim',float), ('raObserved',float), ('decObserved',float), ('x_pupil',float), ('y_pupil',float), ('chipName',str,11), ('xPix',float), ('yPix',float), ('xFocalPlane',float), ('yFocalPlane',float)] baselineData = numpy.loadtxt('AstrometryTestCatalog.txt', dtype=dtype, delimiter=';') pupilTest = _pupilCoordsFromRaDec(baselineData['raObserved'], baselineData['decObserved'], obs_metadata=self.obs_metadata, epoch=2000.0) for (xxtest, yytest, xx, yy) in \ zip(pupilTest[0], pupilTest[1], baselineData['x_pupil'], baselineData['y_pupil']): self.assertAlmostEqual(xxtest,xx,6) self.assertAlmostEqual(yytest,yy,6) focalTest = focalPlaneCoordsFromPupilCoords(pupilTest[0], pupilTest[1], camera=self.cat.camera) focalRa = _focalPlaneCoordsFromRaDec(baselineData['raObserved'], baselineData['decObserved'], epoch=self.cat.db_obj.epoch, obs_metadata=self.cat.obs_metadata, camera=self.cat.camera) for (xxtest, yytest, xxra, yyra, xx, yy) in \ zip(focalTest[0], focalTest[1], focalRa[0], focalRa[1], baselineData['xFocalPlane'], baselineData['yFocalPlane']): self.assertAlmostEqual(xxtest,xx,6) self.assertAlmostEqual(yytest,yy,6) self.assertAlmostEqual(xxra,xx,6) self.assertAlmostEqual(yyra,yy,6) pixTest = pixelCoordsFromPupilCoords(pupilTest[0], pupilTest[1], camera=self.cat.camera) pixTestRaDec = _pixelCoordsFromRaDec(baselineData['raObserved'], baselineData['decObserved'], epoch=self.cat.db_obj.epoch, obs_metadata=self.cat.obs_metadata, camera=self.cat.camera) for (xxtest, yytest, xxra, yyra, xx, yy) in \ zip(pixTest[0], pixTest[1], pixTestRaDec[0], pixTestRaDec[1], baselineData['xPix'], baselineData['yPix']): if not numpy.isnan(xx) and not numpy.isnan(yy): self.assertAlmostEqual(xxtest,xx,5) self.assertAlmostEqual(yytest,yy,5) self.assertAlmostEqual(xxra,xx,5) self.assertAlmostEqual(yyra,yy,5) else: self.assertTrue(numpy.isnan(xx)) self.assertTrue(numpy.isnan(yy)) self.assertTrue(numpy.isnan(xxra)) self.assertTrue(numpy.isnan(yyra)) self.assertTrue(numpy.isnan(xxtest)) self.assertTrue(numpy.isnan(yytest)) nameTest = chipNameFromPupilCoords(pupilTest[0], pupilTest[1], camera=self.cat.camera) nameRA = _chipNameFromRaDec(baselineData['raObserved'], baselineData['decObserved'], epoch=self.cat.db_obj.epoch, obs_metadata=self.cat.obs_metadata, camera=self.cat.camera) for (ntest, nra, ncontrol) in zip(nameTest, nameRA, baselineData['chipName']): if ncontrol != 'None': self.assertEqual(ntest,ncontrol) self.assertEqual(nra,ncontrol) else: self.assertTrue(ntest is None) self.assertTrue(nra is None) if os.path.exists("AstrometryTestCatalog.txt"): os.unlink("AstrometryTestCatalog.txt")
def tanWcsFromDetector(afwDetector, afwCamera, obs_metadata, epoch): """ Take an afw.cameraGeom detector and return a WCS which approximates the focal plane as perfectly flat (i.e. it ignores optical distortions that the telescope may impose on the image) @param [in] afwDetector is an instantiation of afw.cameraGeom's Detector class which characterizes the detector for which you wish to return th WCS @param [in] afwCamera is an instantiation of afw.cameraGeom's Camera class which characterizes the camera containing afwDetector @param [in] obs_metadata is an instantiation of ObservationMetaData characterizing the telescope's current pointing @param [in] epoch is the epoch in Julian years of the equinox against which RA and Dec are measured @param [out] tanWcs is an instantiation of afw.image's TanWcs class representing the WCS of the detector as if there were no optical distortions imposed by the telescope. """ xTanPixMin, xTanPixMax, \ yTanPixMin, yTanPixMax = _getTanPixelBounds(afwDetector, afwCamera) xPixList = [] yPixList = [] nameList = [] # dx and dy are set somewhat heuristically # setting them eqal to 0.1(max-min) lead to errors # on the order of 0.7 arcsec in the WCS dx = 0.5*(xTanPixMax-xTanPixMin) dy = 0.5*(yTanPixMax-yTanPixMin) for xx in np.arange(xTanPixMin, xTanPixMax+0.5*dx, dx): for yy in np.arange(yTanPixMin, yTanPixMax+0.5*dy, dy): xPixList.append(xx) yPixList.append(yy) nameList.append(afwDetector.getName()) raList, decList = _raDecFromPixelCoords(np.array(xPixList), np.array(yPixList), nameList, camera=afwCamera, obs_metadata=obs_metadata, epoch=epoch, includeDistortion=False) crPix1, crPix2 = _pixelCoordsFromRaDec(obs_metadata._pointingRA, obs_metadata._pointingDec, chipName=afwDetector.getName(), camera=afwCamera, obs_metadata=obs_metadata, epoch=epoch, includeDistortion=False) lonList, latList = _nativeLonLatFromPointing(raList, decList, obs_metadata._pointingRA, obs_metadata._pointingDec) # convert from native longitude and latitude to intermediate world coordinates # according to equations (12), (13), (54) and (55) of # # Calabretta and Greisen (2002), A&A 395, p. 1077 # radiusList = 180.0/(np.tan(latList)*np.pi) uList = radiusList*np.sin(lonList) vList = -radiusList*np.cos(lonList) delta_xList = xPixList - crPix1 delta_yList = yPixList - crPix2 bVector = np.array([ (delta_xList*uList).sum(), (delta_yList*uList).sum(), (delta_xList*vList).sum(), (delta_yList*vList).sum() ]) offDiag = (delta_yList*delta_xList).sum() xsq = np.power(delta_xList, 2).sum() ysq = np.power(delta_yList, 2).sum() aMatrix = np.array([ [xsq, offDiag, 0.0, 0.0], [offDiag, ysq, 0.0, 0.0], [0.0, 0.0, xsq, offDiag], [0.0, 0.0, offDiag, ysq] ]) coeffs = np.linalg.solve(aMatrix, bVector) fitsHeader = dafBase.PropertyList() fitsHeader.set("RADESYS", "ICRS") fitsHeader.set("EQUINOX", epoch) fitsHeader.set("CRVAL1", obs_metadata.pointingRA) fitsHeader.set("CRVAL2", obs_metadata.pointingDec) fitsHeader.set("CRPIX1", crPix1+1) # the +1 is because LSST uses 0-indexed images fitsHeader.set("CRPIX2", crPix2+1) # FITS files use 1-indexed images fitsHeader.set("CTYPE1", "RA---TAN") fitsHeader.set("CTYPE2", "DEC--TAN") fitsHeader.setDouble("CD1_1", coeffs[0]) fitsHeader.setDouble("CD1_2", coeffs[1]) fitsHeader.setDouble("CD2_1", coeffs[2]) fitsHeader.setDouble("CD2_2", coeffs[3]) tanWcs = afwImage.cast_TanWcs(afwImage.makeWcs(fitsHeader)) return tanWcs
def testUtilityMethods(self): """ Generate a catalog using the methods from AstrometryUtils.py and CameraUtils.py. Read that data in, and then recalculate the values 'by hand' to make sure that they are consistent. """ with lsst.utils.tests.getTempFilePath('.txt') as catName: self.cat.write_catalog(catName) dtype = [('id', int), ('raICRS', float), ('decICRS', float), ('parallax', float), ('radial_velocity', float), ('x_pupil', float), ('y_pupil', float), ('chipName', str, 11), ('xPix', float), ('yPix', float), ('xFocalPlane', float), ('yFocalPlane', float)] baselineData = np.genfromtxt(catName, dtype=dtype, delimiter=';') self.assertGreater(len(baselineData), 0) pupilTest = _pupilCoordsFromRaDec(baselineData['raICRS'], baselineData['decICRS'], parallax=baselineData['parallax'], v_rad=baselineData['radial_velocity'], obs_metadata=self.obs_metadata, epoch=2000.0) for (xxtest, yytest, xx, yy) in \ zip(pupilTest[0], pupilTest[1], baselineData['x_pupil'], baselineData['y_pupil']): self.assertAlmostEqual(xxtest, xx, 6) self.assertAlmostEqual(yytest, yy, 6) focalTest = focalPlaneCoordsFromPupilCoords(pupilTest[0], pupilTest[1], camera=self.cat.camera) focalRa = _focalPlaneCoordsFromRaDec(baselineData['raICRS'], baselineData['decICRS'], parallax=baselineData['parallax'], v_rad=baselineData['radial_velocity'], epoch=self.cat.db_obj.epoch, obs_metadata=self.cat.obs_metadata, camera=self.cat.camera) for (xxtest, yytest, xxra, yyra, xx, yy) in \ zip(focalTest[0], focalTest[1], focalRa[0], focalRa[1], baselineData['xFocalPlane'], baselineData['yFocalPlane']): self.assertAlmostEqual(xxtest, xx, 6) self.assertAlmostEqual(yytest, yy, 6) self.assertAlmostEqual(xxra, xx, 6) self.assertAlmostEqual(yyra, yy, 6) pixTest = pixelCoordsFromPupilCoords(pupilTest[0], pupilTest[1], camera=self.cat.camera) pixTestRaDec = _pixelCoordsFromRaDec(baselineData['raICRS'], baselineData['decICRS'], parallax=baselineData['parallax'], v_rad=baselineData['radial_velocity'], epoch=self.cat.db_obj.epoch, obs_metadata=self.cat.obs_metadata, camera=self.cat.camera) for (xxtest, yytest, xxra, yyra, xx, yy) in \ zip(pixTest[0], pixTest[1], pixTestRaDec[0], pixTestRaDec[1], baselineData['xPix'], baselineData['yPix']): if not np.isnan(xx) and not np.isnan(yy): self.assertAlmostEqual(xxtest, xx, 5) self.assertAlmostEqual(yytest, yy, 5) self.assertAlmostEqual(xxra, xx, 5) self.assertAlmostEqual(yyra, yy, 5) else: np.testing.assert_equal(xx, np.NaN) np.testing.assert_equal(yy, np.NaN) np.testing.assert_equal(xxra, np.NaN) np.testing.assert_equal(yyra, np.NaN) np.testing.assert_equal(xxtest, np.NaN) np.testing.assert_equal(yytest, np.NaN) nameTest = chipNameFromPupilCoords(pupilTest[0], pupilTest[1], camera=self.cat.camera) nameRA = _chipNameFromRaDec(baselineData['raICRS'], baselineData['decICRS'], epoch=self.cat.db_obj.epoch, obs_metadata=self.cat.obs_metadata, camera=self.cat.camera) is_none = 0 for (ntest, nra, ncontrol) in zip(nameTest, nameRA, baselineData['chipName']): if ncontrol != 'None': self.assertEqual(ntest, ncontrol) self.assertEqual(nra, ncontrol) else: is_none += 1 self.assertIsNone(ntest) self.assertIsNone(nra) self.assertGreater(is_none, 0) self.assertLess(is_none, len(baselineData))
def tanWcsFromDetector(afwDetector, afwCamera, obs_metadata, epoch): """ Take an afw.cameraGeom detector and return a WCS which approximates the focal plane as perfectly flat (i.e. it ignores optical distortions that the telescope may impose on the image) @param [in] afwDetector is an instantiation of afw.cameraGeom's Detector class which characterizes the detector for which you wish to return th WCS @param [in] afwCamera is an instantiation of afw.cameraGeom's Camera class which characterizes the camera containing afwDetector @param [in] obs_metadata is an instantiation of ObservationMetaData characterizing the telescope's current pointing @param [in] epoch is the epoch in Julian years of the equinox against which RA and Dec are measured @param [out] tanWcs is an instantiation of afw.image's TanWcs class representing the WCS of the detector as if there were no optical distortions imposed by the telescope. """ xTanPixMin, xTanPixMax, \ yTanPixMin, yTanPixMax = _getTanPixelBounds(afwDetector, afwCamera) xPixList = [] yPixList = [] nameList = [] # dx and dy are set somewhat heuristically # setting them eqal to 0.1(max-min) lead to errors # on the order of 0.7 arcsec in the WCS dx = 0.5 * (xTanPixMax - xTanPixMin) dy = 0.5 * (yTanPixMax - yTanPixMin) for xx in np.arange(xTanPixMin, xTanPixMax + 0.5 * dx, dx): for yy in np.arange(yTanPixMin, yTanPixMax + 0.5 * dy, dy): xPixList.append(xx) yPixList.append(yy) nameList.append(afwDetector.getName()) raList, decList = _raDecFromPixelCoords(np.array(xPixList), np.array(yPixList), nameList, camera=afwCamera, obs_metadata=obs_metadata, epoch=epoch, includeDistortion=False) crPix1, crPix2 = _pixelCoordsFromRaDec(obs_metadata._pointingRA, obs_metadata._pointingDec, chipName=afwDetector.getName(), camera=afwCamera, obs_metadata=obs_metadata, epoch=epoch, includeDistortion=False) lonList, latList = _nativeLonLatFromPointing(raList, decList, obs_metadata._pointingRA, obs_metadata._pointingDec) # convert from native longitude and latitude to intermediate world coordinates # according to equations (12), (13), (54) and (55) of # # Calabretta and Greisen (2002), A&A 395, p. 1077 # radiusList = 180.0 / (np.tan(latList) * np.pi) uList = radiusList * np.sin(lonList) vList = -radiusList * np.cos(lonList) delta_xList = xPixList - crPix1 delta_yList = yPixList - crPix2 bVector = np.array([ (delta_xList * uList).sum(), (delta_yList * uList).sum(), (delta_xList * vList).sum(), (delta_yList * vList).sum() ]) offDiag = (delta_yList * delta_xList).sum() xsq = np.power(delta_xList, 2).sum() ysq = np.power(delta_yList, 2).sum() aMatrix = np.array([[xsq, offDiag, 0.0, 0.0], [offDiag, ysq, 0.0, 0.0], [0.0, 0.0, xsq, offDiag], [0.0, 0.0, offDiag, ysq]]) coeffs = np.linalg.solve(aMatrix, bVector) fitsHeader = dafBase.PropertyList() fitsHeader.set("RADESYS", "ICRS") fitsHeader.set("EQUINOX", epoch) fitsHeader.set("CRVAL1", obs_metadata.pointingRA) fitsHeader.set("CRVAL2", obs_metadata.pointingDec) fitsHeader.set("CRPIX1", crPix1 + 1) # the +1 is because LSST uses 0-indexed images fitsHeader.set("CRPIX2", crPix2 + 1) # FITS files use 1-indexed images fitsHeader.set("CTYPE1", "RA---TAN") fitsHeader.set("CTYPE2", "DEC--TAN") fitsHeader.setDouble("CD1_1", coeffs[0]) fitsHeader.setDouble("CD1_2", coeffs[1]) fitsHeader.setDouble("CD2_1", coeffs[2]) fitsHeader.setDouble("CD2_2", coeffs[3]) # 20 March 2017 # the 'try' block is required by the SWIG stack; # the 'except' block is required by the pybind11 stack. try: tanWcs = afwImage.cast_TanWcs(afwImage.makeWcs(fitsHeader)) except AttributeError: tanWcs = afwImage.makeWcs(fitsHeader) return tanWcs
def test_pixel_coords_from_ra_dec_radians(self): """ Test that _pixelCoordsFromRaDec and _pixelCoordsFromRaDecLSST agree """ raP = 74.2 decP = 13.0 obs = ObservationMetaData(pointingRA=raP, pointingDec=decP, rotSkyPos=13.0, mjd=43441.0) n_obj = 1000 rng = np.random.RandomState(83241) rr = rng.random_sample(n_obj)*1.75 theta = rng.random_sample(n_obj)*2.0*np.pi ra_list = np.radians(raP + rr*np.cos(theta)) dec_list = np.radians(decP + rr*np.sin(theta)) x_pix, y_pix = _pixelCoordsFromRaDec(ra_list, dec_list, obs_metadata=obs, camera=self.camera, includeDistortion=False) self.assertLessEqual(len(np.where(np.isnan(x_pix))[0]), n_obj/10) self.assertLessEqual(len(np.where(np.isnan(y_pix))[0]), n_obj/10) x_pix_test, y_pix_test = _pixelCoordsFromRaDecLSST(ra_list, dec_list, obs_metadata=obs, includeDistortion=False) try: np.testing.assert_array_equal(x_pix, x_pix_test) np.testing.assert_array_equal(y_pix, y_pix_test) except AssertionError: n_problematic = 0 for xx, yy, xt, yt in zip(x_pix, y_pix, x_pix_test, y_pix_test): if xx!=xt or yy!=yt: if (not np.isnan(xx) and not np.isnan(xt) and not np.isnan(yy) and not np.isnan(yt)): print(xx,yy,xt,yt) n_problematic += 1 if n_problematic>0: raise # test when we force a chipName x_pix, y_pix = _pixelCoordsFromRaDec(ra_list, dec_list, chipName=['R:2,2 S:1,1'], obs_metadata=obs, camera=self.camera, includeDistortion=False) self.assertLessEqual(len(np.where(np.isnan(x_pix))[0]), n_obj/10) self.assertLessEqual(len(np.where(np.isnan(y_pix))[0]), n_obj/10) x_pix_test, y_pix_test = _pixelCoordsFromRaDecLSST(ra_list, dec_list, chipName=['R:2,2 S:1,1'], obs_metadata=obs, includeDistortion=False) np.testing.assert_array_equal(x_pix, x_pix_test) np.testing.assert_array_equal(y_pix, y_pix_test) # test without distortion x_pix, y_pix = _pixelCoordsFromRaDec(ra_list, dec_list, obs_metadata=obs, camera=self.camera, includeDistortion=False) self.assertLessEqual(len(np.where(np.isnan(x_pix))[0]), n_obj/10) self.assertLessEqual(len(np.where(np.isnan(y_pix))[0]), n_obj/10) x_pix_test, y_pix_test = _pixelCoordsFromRaDecLSST(ra_list, dec_list, obs_metadata=obs, includeDistortion=False) try: np.testing.assert_array_equal(x_pix, x_pix_test) np.testing.assert_array_equal(y_pix, y_pix_test) except AssertionError: n_problematic = 0 for xx, yy, xt, yt in zip(x_pix, y_pix, x_pix_test, y_pix_test): if xx!=xt or yy!=yt: if (not np.isnan(xx) and not np.isnan(xt) and not np.isnan(yy) and not np.isnan(yt)): print(xx,yy,xt,yt) n_problematic += 1 if n_problematic>0: raise # test that exceptions are raised when incomplete ObservationMetaData are used obs = ObservationMetaData(pointingRA=raP, pointingDec=decP, mjd=59580.0) with self.assertRaises(RuntimeError) as context: _pixelCoordsFromRaDecLSST(ra_list, dec_list, obs_metadata=obs) self.assertIn("rotSkyPos", context.exception.args[0]) obs = ObservationMetaData(pointingRA=raP, pointingDec=decP, rotSkyPos=35.0) with self.assertRaises(RuntimeError) as context: _pixelCoordsFromRaDecLSST(ra_list, dec_list, obs_metadata=obs) self.assertIn("mjd", context.exception.args[0]) with self.assertRaises(RuntimeError) as context: _pixelCoordsFromRaDecLSST(ra_list, dec_list) self.assertIn("ObservationMetaData", context.exception.args[0]) # check that exceptions are raised when ra_list, dec_list are of the wrong shape obs = ObservationMetaData(pointingRA=raP, pointingDec=decP, rotSkyPos=24.0, mjd=43000.0) with self.assertRaises(RuntimeError) as context: _pixelCoordsFromRaDecLSST(ra_list, dec_list[:5], obs_metadata=obs) self.assertIn("same length", context.exception.args[0])