def testTransform(self): """Test pixelToSky, skyToPixel, getTanWcs and getPixelToTanPixel """ pixelsToTanPixels = afwGeom.RadialXYTransform([0, 1.001, 0.00003]) distortedWcs = afwImage.DistortedTanWcs(self.tanWcs, pixelsToTanPixels) tanWcsCopy = distortedWcs.getTanWcs() pixToTanCopy = distortedWcs.getPixelToTanPixel() for x in (0, 1000, 5000): for y in (0, 560, 2000): pixPos = afwGeom.Point2D(x, y) tanPixPos = pixelsToTanPixels.forwardTransform(pixPos) tanPixPosCopy = pixToTanCopy.forwardTransform(pixPos) self.assertEqual(tanPixPos, tanPixPosCopy) predSky = self.tanWcs.pixelToSky(tanPixPos) predSkyCopy = tanWcsCopy.pixelToSky(tanPixPos) self.assertEqual(predSky, predSkyCopy) measSky = distortedWcs.pixelToSky(pixPos) self.assertLess( predSky.angularSeparation(measSky).asRadians(), 1e-7) pixPosRoundTrip = distortedWcs.skyToPixel(measSky) for i in range(2): self.assertAlmostEqual(pixPos[i], pixPosRoundTrip[i])
def testLargeDistortion(self): # This transform is about as extreme as I can get: # using 0.0005 in the last value appears to produce numerical issues. # It produces a maximum deviation of 459 pixels, which should be sufficient. pixelsToTanPixels = afwGeom.RadialXYTransform([0.0, 1.1, 0.0004]) self.distortedWcs = afwImage.DistortedTanWcs(self.wcs, pixelsToTanPixels) def applyDistortion(src): out = src.table.copyRecord(src) out.set(out.table.getCentroidKey(), pixelsToTanPixels.reverseTransform(src.getCentroid())) return out self.singleTestInstance(self.filename, applyDistortion)
def testUsedFlag(self): """Test that the solver will record number of sources used to table if it is passed a schema on initialization. """ distortedWcs = afwImage.DistortedTanWcs(self.tanWcs, afwGeom.IdentityXYTransform()) self.exposure.setWcs(distortedWcs) loadRes = self.refObjLoader.loadPixelBox(bbox=self.bbox, wcs=distortedWcs, filterName="r") refCat = loadRes.refCat refCentroidKey = afwTable.Point2DKey(refCat.schema["centroid"]) refFluxRKey = refCat.schema["r_flux"].asKey() sourceSchema = afwTable.SourceTable.makeMinimalSchema() measBase.SingleFrameMeasurementTask( schema=sourceSchema) # expand the schema config = AstrometryTask.ConfigClass() config.wcsFitter.order = 2 config.wcsFitter.numRejIter = 0 # schema must be passed to the solver task constructor solver = AstrometryTask(config=config, refObjLoader=self.refObjLoader, schema=sourceSchema) sourceCat = afwTable.SourceCatalog(sourceSchema) sourceCentroidKey = afwTable.Point2DKey(sourceSchema["slot_Centroid"]) sourceFluxKey = sourceSchema["slot_ApFlux_flux"].asKey() sourceFluxSigmaKey = sourceSchema["slot_ApFlux_fluxSigma"].asKey() for refObj in refCat: src = sourceCat.addNew() src.set(sourceCentroidKey, refObj.get(refCentroidKey)) src.set(sourceFluxKey, refObj.get(refFluxRKey)) src.set(sourceFluxSigmaKey, refObj.get(refFluxRKey) / 100) results = solver.run( sourceCat=sourceCat, exposure=self.exposure, ) # check that the used flag is set the right number of times count = 0 for source in sourceCat: if source.get('calib_astrometryUsed'): count += 1 self.assertEqual(count, len(results.matches))
def testBasics(self): pixelsToTanPixels = afwGeom.RadialXYTransform([0, 1.001, 0.00003]) distortedWcs = afwImage.DistortedTanWcs(self.tanWcs, pixelsToTanPixels) tanWcsCopy = distortedWcs.getTanWcs() self.assertEqual(self.tanWcs, tanWcsCopy) self.assertFalse(self.tanWcs.hasDistortion()) self.assertTrue(distortedWcs.hasDistortion()) try: self.tanWcs == distortedWcs self.fail("== should not be implemented for DistortedTanWcs") except Exception: pass try: distortedWcs == self.tanWcs self.fail("== should not be implemented for DistortedTanWcs") except Exception: pass
def doTest(self, name, xyTransform, order=3, doPlot=False): """Create a DistortedTanWcs from the specified transform and fit it """ wcs = afwImage.DistortedTanWcs(self.tanWcs, xyTransform) fitWcs = approximateWcs( wcs=wcs, bbox=self.bbox, order=order, ) if doPlot: self.plotWcs(wcs, fitWcs, self.bbox, xyTransform) msg = "ERROR: %s failed with order %s" % (name, order) self.assertWcsAlmostEqualOverBBox(wcs, fitWcs, self.bbox, maxDiffSky=0.001 * afwGeom.arcseconds, maxDiffPix=0.02, msg=msg)
def testWarnings(self): """Test that approximateWcs raises a UserWarning when it cannot achieve desired tolerance""" radialTransform = afwGeom.RadialXYTransform([0, 2.0, 3.0]) wcs = afwImage.DistortedTanWcs(self.tanWcs, radialTransform) with self.assertRaises(UserWarning): approximateWcs(wcs=wcs, bbox=self.bbox, order=2)
def testGetDistortedWcs(self): """Test utils.getDistortedWcs """ dw = DetectorWrapper() detector = dw.detector # the standard case: the exposure's WCS is pure TAN WCS and distortion information is available; # return a DistortedTanWcs exposure = afwImage.ExposureF(10, 10) exposure.setDetector(detector) exposure.setWcs(self.tanWcs) self.assertFalse(self.tanWcs.hasDistortion()) outWcs = getDistortedWcs(exposure.getInfo()) self.assertTrue(outWcs.hasDistortion()) self.assertIsInstance(outWcs, afwImage.DistortedTanWcs) del exposure # avoid accidental reuse del outWcs # return the original WCS if the exposure's WCS has distortion pixelsToTanPixels = afwGeom.RadialXYTransform([0, 1.001, 0.00003]) distortedWcs = afwImage.DistortedTanWcs(self.tanWcs, pixelsToTanPixels) self.assertTrue(distortedWcs.hasDistortion()) exposure = afwImage.ExposureF(10, 10) exposure.setWcs(distortedWcs) exposure.setDetector(detector) outWcs = getDistortedWcs(exposure.getInfo()) self.assertTrue(outWcs.hasDistortion()) self.assertIsInstance(outWcs, afwImage.DistortedTanWcs) del exposure del distortedWcs del outWcs # raise an exception if exposure has no WCS exposure = afwImage.ExposureF(10, 10) exposure.setDetector(detector) with self.assertRaises(Exception): getDistortedWcs(exposure.getInfo()) del exposure # return the original pure TAN WCS if the exposure has no detector exposure = afwImage.ExposureF(10, 10) exposure.setWcs(self.tanWcs) outWcs = getDistortedWcs(exposure.getInfo()) self.assertFalse(outWcs.hasDistortion()) self.assertIsInstance(outWcs, afwImage.TanWcs) self.assertNotIsInstance(outWcs, afwImage.DistortedTanWcs) del exposure del outWcs # return the original pure TAN WCS if the exposure's detector has no # TAN_PIXELS transform def removeTanPixels(detectorWrapper): tanPixSys = detector.makeCameraSys(TAN_PIXELS) detectorWrapper.transMap.pop(tanPixSys) detectorNoTanPix = DetectorWrapper(modFunc=removeTanPixels).detector exposure = afwImage.ExposureF(10, 10) exposure.setWcs(self.tanWcs) exposure.setDetector(detectorNoTanPix) outWcs = getDistortedWcs(exposure.getInfo()) self.assertFalse(outWcs.hasDistortion()) self.assertIsInstance(outWcs, afwImage.TanWcs) self.assertNotIsInstance(outWcs, afwImage.DistortedTanWcs) del exposure del outWcs
def doTest(self, pixelsToTanPixels, order=3): """Test using pixelsToTanPixels to distort the source positions """ distortedWcs = afwImage.DistortedTanWcs(self.tanWcs, pixelsToTanPixels) self.exposure.setWcs(distortedWcs) sourceCat = self.makeSourceCat(distortedWcs) config = AstrometryTask.ConfigClass() config.wcsFitter.order = order config.wcsFitter.numRejIter = 0 solver = AstrometryTask(config=config, refObjLoader=self.refObjLoader) results = solver.run( sourceCat=sourceCat, exposure=self.exposure, ) fitWcs = self.exposure.getWcs() self.assertRaises(Exception, self.assertWcsAlmostEqualOverBBox, fitWcs, distortedWcs) self.assertWcsAlmostEqualOverBBox(distortedWcs, fitWcs, self.bbox, maxDiffSky=0.01 * afwGeom.arcseconds, maxDiffPix=0.02) srcCoordKey = afwTable.CoordKey(sourceCat.schema["coord"]) refCoordKey = afwTable.CoordKey(results.refCat.schema["coord"]) refCentroidKey = afwTable.Point2DKey(results.refCat.schema["centroid"]) maxAngSep = afwGeom.Angle(0) maxPixSep = 0 for refObj, src, d in results.matches: refCoord = refObj.get(refCoordKey) refPixPos = refObj.get(refCentroidKey) srcCoord = src.get(srcCoordKey) srcPixPos = src.getCentroid() angSep = refCoord.angularSeparation(srcCoord) maxAngSep = max(maxAngSep, angSep) pixSep = math.hypot(*(srcPixPos - refPixPos)) maxPixSep = max(maxPixSep, pixSep) print("max angular separation = %0.4f arcsec" % (maxAngSep.asArcseconds(), )) print("max pixel separation = %0.3f" % (maxPixSep, )) self.assertLess(maxAngSep.asArcseconds(), 0.0026) self.assertLess(maxPixSep, 0.015) # try again, but without fitting the WCS config.forceKnownWcs = True solverNoFit = AstrometryTask(config=config, refObjLoader=self.refObjLoader) self.exposure.setWcs(distortedWcs) resultsNoFit = solverNoFit.run( sourceCat=sourceCat, exposure=self.exposure, ) self.assertIsNone(resultsNoFit.scatterOnSky) # fitting should result in matches that are at least as good # (strictly speaking fitting might result in a larger match list with # some outliers, but in practice this test passes) meanFitDist = np.mean([match.distance for match in results.matches]) meanNoFitDist = np.mean( [match.distance for match in resultsNoFit.matches]) self.assertLessEqual(meanFitDist, meanNoFitDist)