def checkBasics(self, transform): """Check round trip and linearization of transform """ for fromPoint in self.fromIter(): toPoint = transform.forwardTransform(fromPoint) roundTripPoint = transform.reverseTransform(toPoint) for i in range(2): self.assertAlmostEqual(fromPoint[i], roundTripPoint[i]) for deltaFrom in ( Extent2D(0), Extent2D(0.1, -0.1), Extent2D(-0.15, 0.1), ): tweakedFromPoint = fromPoint + deltaFrom tweakedToPoint = transform.forwardTransform(tweakedFromPoint) linToPoint = transform.linearizeForwardTransform(fromPoint)( tweakedFromPoint) linRoundTripPoint = transform.linearizeReverseTransform( toPoint)(tweakedToPoint) for i in range(2): self.assertAlmostEqual(tweakedToPoint[i], linToPoint[i], places=2) self.assertAlmostEqual(tweakedFromPoint[i], linRoundTripPoint[i], places=2)
def testFullAffine(self): """Test affine = AffineXYTransform with just linear coefficients """ affineClass = xyTransformRegistry["affine"] affineConfig = affineClass.ConfigClass() affineConfig.translation = (-2.1, 3.4) rotAng = 0.832 # radians xScale = 3.7 yScale = 45.3 affineConfig.linear = ( math.cos(rotAng) * xScale, math.sin(rotAng) * yScale, -math.sin(rotAng) * xScale, math.cos(rotAng) * yScale, ) with lsst.utils.tests.getTempFilePath(".py") as filePath: self.checkConfig(affineClass, affineConfig, filePath) affine = affineClass(affineConfig) for fromPoint in self.fromIter(): toPoint = affine.forwardTransform(fromPoint) predToPoint = Point2D( affineConfig.linear[0] * fromPoint[0] + affineConfig.linear[1] * fromPoint[1], affineConfig.linear[2] * fromPoint[0] + affineConfig.linear[3] * fromPoint[1], ) predToPoint = predToPoint + Extent2D(*affineConfig.translation) for i in range(2): self.assertAlmostEqual(toPoint[i], predToPoint[i])
def testLinearizeMethods(self): skyWcs = makeSkyWcs(self.metadata) # use a sky position near, but not at, the WCS origin sky00 = skyWcs.getSkyOrigin().offset(45 * degrees, 1.2 * degrees) pix00 = skyWcs.skyToPixel(sky00) for skyUnit in (degrees, radians): linPixToSky1 = skyWcs.linearizePixelToSky( sky00, skyUnit) # should match inverse of linSkyToPix1 linPixToSky2 = skyWcs.linearizePixelToSky( pix00, skyUnit) # should match inverse of linSkyToPix1 linSkyToPix1 = skyWcs.linearizeSkyToPixel(sky00, skyUnit) linSkyToPix2 = skyWcs.linearizeSkyToPixel( pix00, skyUnit) # should match linSkyToPix1 for pixel in (pix00, pix00 + Extent2D(1000, -1230)): linSky = linPixToSky1(pixel) self.assertPairsAlmostEqual(linPixToSky2(pixel), linSky) self.assertPairsAlmostEqual(linSkyToPix1(linSky), pixel) self.assertPairsAlmostEqual(linSkyToPix2(linSky), pixel) sky00Doubles = sky00.getPosition(skyUnit) pix00gApprox = linSkyToPix1(sky00Doubles) self.assertPairsAlmostEqual(pix00gApprox, pix00) self.assertAlmostEqual(pix00.getX(), pix00gApprox.getX()) self.assertAlmostEqual(pix00.getY(), pix00gApprox.getY()) pixelScale = skyWcs.getPixelScale(pix00) pixelArea = pixelScale.asAngularUnits(skyUnit)**2 predictedPixelArea = 1 / linSkyToPix1.getLinear( ).computeDeterminant() self.assertAlmostEqual(pixelArea, predictedPixelArea)
def testReadOldTanFits(self): """Test reading a FITS file containing data for an lsst::afw::image::TanWcs That file was made using the same metadata follows (like self.metadata without the distortion) """ tanMetadata = PropertyList() # the following was fit using CreateWcsWithSip from meas_astrom # and is valid over this bbox: (minimum=(0, 0), maximum=(3030, 3030)) # This same metadata was used to create testdata/oldTanSipwWs.fits for name, value in ( ("RADESYS", "ICRS"), ("CTYPE1", "RA---TAN"), ("CTYPE2", "DEC--TAN"), ("CRPIX1", 1531.1824767147), ("CRPIX2", 1531.1824767147), ("CRVAL1", 43.035511801383), ("CRVAL2", 44.305697682784), ("CUNIT1", "deg"), ("CUNIT2", "deg"), ("CD1_1", 0.00027493991598151), ("CD1_2", -3.2758487104158e-06), ("CD2_1", 3.2301310675830e-06), ("CD2_2", 0.00027493937506632), ): tanMetadata.set(name, value) dataDir = os.path.join(os.path.split(__file__)[0], "data") filePath = os.path.join(dataDir, "oldTanWcs.fits") wcsFromFits = SkyWcs.readFits(filePath) wcsFromMetadata = makeSkyWcs(tanMetadata) bbox = Box2D(Point2D(-1000, -1000), Extent2D(3000, 3000)) self.assertWcsAlmostEqualOverBBox(wcsFromFits, wcsFromMetadata, bbox)
def checkMakeFlippedWcs(self, skyWcs, skyAtol=1e-7 * arcseconds): """Check makeFlippedWcs on the provided WCS """ # make an arbitrary bbox, but one that includes zero in one axis # and does not include zero in the other axis # the center of the bbox is used as the center of flipping # and the corners of the bbox are the input positions that are tested bbox = Box2D(Point2D(-100, 1000), Extent2D(2000, 1501)) # dict of (isRight, isTop): position minPos = bbox.getMin() maxPos = bbox.getMax() center = bbox.getCenter() cornerDict = { (False, False): minPos, (False, True): Point2D(minPos[0], maxPos[1]), (True, False): Point2D(maxPos[0], minPos[1]), (True, True): maxPos, } for flipLR, flipTB in itertools.product((False, True), (False, True)): flippedWcs = makeFlippedWcs(wcs=skyWcs, flipLR=flipLR, flipTB=flipTB, center=center) # the center is unchanged self.assertSpherePointsAlmostEqual(skyWcs.pixelToSky(center), flippedWcs.pixelToSky(center), maxSep=skyAtol) for isR, isT in itertools.product((False, True), (False, True)): origPos = cornerDict[(isR, isT)] flippedPos = cornerDict[(isR ^ flipLR, isT ^ flipTB)] self.assertSpherePointsAlmostEqual( skyWcs.pixelToSky(origPos), flippedWcs.pixelToSky(flippedPos), maxSep=skyAtol)
def testAgainstAstropyWcs(self): skyWcs = makeSkyWcs(self.metadata, strip=False) header = makeLimitedFitsHeader(self.metadata) astropyWcs = astropy.wcs.WCS(header) bbox = Box2D(Point2D(-1000, -1000), Extent2D(3000, 3000)) self.assertSkyWcsAstropyWcsAlmostEqual(skyWcs=skyWcs, astropyWcs=astropyWcs, bbox=bbox)
def makeRandomPoint(self, *args, **kwds): """Draw a random Point2D within a Box2I. All arguments are forwarded directly to the Box2I constructor, allowing the caller to pass a fully-constructed Box2I, a (Point2I, Point2I) pair, or a (Point2I, Extent2I) pair. """ bboxD = Box2D(Box2I(*args, **kwds)) return bboxD.getMin() + Extent2D(bboxD.getWidth() * self.rng.rand(), bboxD.getHeight() * self.rng.rand())
def testReadOldTanSipFits(self): """Test reading a FITS file containing data for an lsst::afw::image::TanWcs That file was made using the same metadata as this test """ dataDir = os.path.join(os.path.split(__file__)[0], "data") filePath = os.path.join(dataDir, "oldTanSipWcs.fits") wcsFromFits = SkyWcs.readFits(filePath) wcsFromMetadata = makeSkyWcs(self.metadata) bbox = Box2D(Point2D(-1000, -1000), Extent2D(3000, 3000)) self.assertWcsAlmostEqualOverBBox(wcsFromFits, wcsFromMetadata, bbox)
def testTranslateAffine(self): """Test affine = AffineXYTransform with just translation coefficients """ affineClass = xyTransformRegistry["affine"] affineConfig = affineClass.ConfigClass() affineConfig.translation = (1.2, -3.4) with lsst.utils.tests.getTempFilePath(".py") as filePath: self.checkConfig(affineClass, affineConfig, filePath) affine = affineClass(affineConfig) for fromPoint in self.fromIter(): toPoint = affine.forwardTransform(fromPoint) predToPoint = fromPoint + Extent2D(*affineConfig.translation) for i in range(2): self.assertAlmostEqual(toPoint[i], predToPoint[i])
def testGetIntermediateWorldCoordsToSky(self): """Test getIntermediateWorldCoordsToSky and getPixelToIntermediateWorldCoords """ crpix = Extent2D( self.metadata.get("CRPIX1") - 1, self.metadata.get("CRPIX2") - 1) skyWcs = makeSkyWcs(self.metadata, strip=False) for simplify in (False, True): pixelToIwc = getPixelToIntermediateWorldCoords(skyWcs, simplify) iwcToSky = getIntermediateWorldCoordsToSky(skyWcs, simplify) self.assertTrue(isinstance(pixelToIwc, TransformPoint2ToPoint2)) self.assertTrue(isinstance(iwcToSky, TransformPoint2ToSpherePoint)) if simplify: self.assertTrue(pixelToIwc.getMapping().isSimple) self.assertTrue(iwcToSky.getMapping().isSimple) # else the mapping may have already been simplified inside the WCS, # so don't assert isSimple is false # check that the chained transforms produce the same results as the WCS # in the forward and inverse direction pixPosList = [] for dx in (0, 1000): for dy in (0, 1000): pixPosList.append(Point2D(dx, dy) + crpix) iwcPosList = pixelToIwc.applyForward(pixPosList) skyPosList = iwcToSky.applyForward(iwcPosList) self.assertSpherePointListsAlmostEqual( skyPosList, skyWcs.pixelToSky(pixPosList)) self.assertPairListsAlmostEqual( pixelToIwc.applyInverse(iwcToSky.applyInverse(skyPosList)), skyWcs.skyToPixel(skyPosList)) self.assertPairListsAlmostEqual(iwcPosList, iwcToSky.applyInverse(skyPosList)) self.assertPairListsAlmostEqual( pixPosList, pixelToIwc.applyInverse(iwcPosList)) # compare extracted pixelToIwc to a version of pixelToIwc computed directly from the metadata ourPixelToIwc = makeSipPixelToIwc(self.metadata) self.assertPairListsAlmostEqual( pixelToIwc.applyForward(pixPosList), ourPixelToIwc.applyForward(pixPosList)) # compare extracted iwcToPixel to a version of iwcToPixel computed directly from the metadata ourIwcToPixel = makeSipIwcToPixel(self.metadata) self.assertPairListsAlmostEqual( pixelToIwc.applyInverse(iwcPosList), ourIwcToPixel.applyForward(iwcPosList))
def testInverted(self): """Test inverted = InvertedXYTransform """ invertedClass = xyTransformRegistry["inverted"] invertedConfig = invertedClass.ConfigClass() affineClass = xyTransformRegistry["affine"] invertedConfig.transform.retarget(affineClass) affineConfig = invertedConfig.transform affineConfig.translation = (1.2, -3.4) with lsst.utils.tests.getTempFilePath(".py") as filePath: self.checkConfig(invertedClass, invertedConfig, filePath) inverted = invertedClass(invertedConfig) self.checkBasics(inverted) for fromPoint in self.fromIter(): toPoint = inverted.forwardTransform(fromPoint) predToPoint = fromPoint - Extent2D(*invertedConfig.transform.translation) for i in range(2): self.assertAlmostEqual(toPoint[i], predToPoint[i])
def testReadV1Catalog(self): testDir = os.path.dirname(__file__) v1CatalogPath = os.path.join( testDir, "data", "exposure_catalog_v1.fits") catV1 = lsst.afw.table.ExposureCatalog.readFits(v1CatalogPath) self.assertEqual(self.cat[0].get(self.ka), catV1[0].get(self.ka)) self.assertEqual(self.cat[0].get(self.kb), catV1[0].get(self.kb)) self.comparePsfs(self.cat[0].getPsf(), catV1[0].getPsf()) bbox = Box2D(Point2D(0, 0), Extent2D(2000, 2000)) self.assertWcsAlmostEqualOverBBox(self.cat[0].getWcs(), catV1[0].getWcs(), bbox) self.assertEqual(self.cat[1].get(self.ka), catV1[1].get(self.ka)) self.assertEqual(self.cat[1].get(self.kb), catV1[1].get(self.kb)) self.assertEqual(self.cat[1].getWcs(), catV1[1].getWcs()) self.assertIsNone(self.cat[1].getPsf()) self.assertIsNone(self.cat[1].getPhotoCalib()) self.assertEqual(self.cat[0].getPhotoCalib(), catV1[0].getPhotoCalib()) self.assertIsNone(catV1[0].getVisitInfo()) self.assertIsNone(catV1[1].getVisitInfo())
def testAgainstAstropyWcs(self): bbox = Box2D(Point2D(-1000, -1000), Extent2D(2000, 2000)) for crval, orientation, flipX, projection in itertools.product( self.crvalList, self.orientationList, (False, True), ("TAN", "STG", "CEA", "AIT")): cdMatrix = makeCdMatrix(scale=self.scale, orientation=orientation, flipX=flipX) metadata = makeSimpleWcsMetadata(crpix=self.crpix, crval=crval, cdMatrix=cdMatrix, projection=projection) header = makeLimitedFitsHeader(metadata) astropyWcs = astropy.wcs.WCS(header) skyWcs = makeSkyWcs(crpix=self.crpix, crval=crval, cdMatrix=cdMatrix, projection=projection) # Most projections only seem to agree to within 1e-4 in the round trip test self.assertSkyWcsAstropyWcsAlmostEqual(skyWcs=skyWcs, astropyWcs=astropyWcs, bbox=bbox)
def checkTanWcs(self, crval, orientation, flipX): """Construct a pure TAN SkyWcs and check that it operates as specified Parameters ---------- crval : `lsst.afw.coord.IcrsCoord` Desired reference sky position. Must not be at either pole. orientation : `lsst.afw.geom.Angle` Position angle of focal plane +Y, measured from N through E. At 0 degrees, +Y is along N and +X is along E/W if flipX false/true At 90 degrees, +Y is along E and +X is along S/N if flipX false/true flipX : `bool` Flip x axis? See `orientation` for details. Returns ------- wcs : `lsst.afw.geom.SkyWcs` The generated pure TAN SkyWcs """ cdMatrix = makeCdMatrix(scale=self.scale, orientation=orientation, flipX=flipX) wcs = SkyWcs( crpix=self.crpix, crval=crval, cdMatrix=cdMatrix, ) self.checkPersistence(wcs) xoffAng = 180 * degrees if flipX else 0 * degrees def safeOffset(crval, direction, amount): try: offsetCoord = crval.toIcrs() offsetCoord.offset(direction, amount) return offsetCoord except Exception: return IcrsCoord(crval[0] + direction, crval - amount) pixelList = [ Point2D(self.crpix[0], self.crpix[1]), Point2D(self.crpix[0] + 1, self.crpix[1]), Point2D(self.crpix[0], self.crpix[1] + 1), ] skyList = wcs.applyForward(pixelList) # check pixels to sky predSkyList = [ crval, safeOffset(crval, xoffAng - orientation, self.scale), safeOffset(crval, 90 * degrees - orientation, self.scale), ] self.assertCoordListsAlmostEqual(predSkyList, skyList) self.assertCoordListsAlmostEqual(predSkyList, wcs.pixelToSky(pixelList)) for pixel, predSky in zip(pixelList, predSkyList): self.assertCoordsAlmostEqual(predSky, wcs.pixelToSky(pixel)) anglePair = wcs.pixelToSky(*pixel) self.assertCoordsAlmostEqual(predSky, IcrsCoord(*anglePair)) # check sky to pixels self.assertPairListsAlmostEqual(pixelList, wcs.applyInverse(skyList)) self.assertPairListsAlmostEqual(pixelList, wcs.applyInverse(skyList)) for pixel, sky in zip(pixelList, skyList): self.assertPairsAlmostEqual(pixel, wcs.skyToPixel(sky)) xyPair = wcs.skyToPixel(*sky) self.assertPairsAlmostEqual(pixel, Point2D(*xyPair)) crval = wcs.getSkyOrigin() self.assertCoordsAlmostEqual(crval, crval, maxDiff=self.tinyAngle) crpix = wcs.getPixelOrigin() self.assertPairsAlmostEqual(crpix, self.crpix, maxDiff=self.tinyPixels) self.assertFloatsAlmostEqual(wcs.getCdMatrix(), cdMatrix) pixelScale = wcs.getPixelScale(self.crpix) self.assertAnglesAlmostEqual(self.scale, pixelScale, maxDiff=self.tinyAngle) # Compute a WCS with the pixel origin shifted by an arbitrary amount # The resulting sky origin should not change offset = Extent2D(500, -322) # arbitrary shiftedWcs = wcs.copyAtShiftedPixelOrigin(offset) predShiftedPixelOrigin = self.crpix + offset self.assertPairsAlmostEqual(shiftedWcs.getPixelOrigin(), predShiftedPixelOrigin, maxDiff=self.tinyPixels) self.assertCoordsAlmostEqual(shiftedWcs.getSkyOrigin(), crval, maxDiff=self.tinyAngle) shiftedPixelList = [p + offset for p in pixelList] shiftedSkyList = shiftedWcs.applyForward(shiftedPixelList) self.assertCoordListsAlmostEqual(skyList, shiftedSkyList, maxDiff=self.tinyAngle) return wcs
def setUp(self): metadata = PropertyList() # the following was fit using CreateWcsWithSip from meas_astrom # and is valid over this bbox: (minimum=(0, 0), maximum=(3030, 3030)) # This same metadata was used to create testdata/oldTanSipwWs.fits for name, value in ( ("RADESYS", "ICRS"), ("CTYPE1", "RA---TAN-SIP"), ("CTYPE2", "DEC--TAN-SIP"), ("CRPIX1", 1531.1824767147), ("CRPIX2", 1531.1824767147), ("CRVAL1", 43.035511801383), ("CRVAL2", 44.305697682784), ("CUNIT1", "deg"), ("CUNIT2", "deg"), ("CD1_1", 0.00027493991598151), ("CD1_2", -3.2758487104158e-06), ("CD2_1", 3.2301310675830e-06), ("CD2_2", 0.00027493937506632), ("A_ORDER", 5), ("A_0_2", -1.7769487466972e-09), ("A_0_3", 5.3745894718340e-13), ("A_0_4", -7.2921116596880e-17), ("A_0_5", 8.6947236956136e-21), ("A_1_1", 5.4246387438098e-08), ("A_1_2", -1.5689083084641e-12), ("A_1_3", 1.2424130500997e-16), ("A_1_4", 3.9982572658006e-20), ("A_2_0", 4.9268299826160e-08), ("A_2_1", 1.6365657558495e-12), ("A_2_2", 1.1976983061953e-16), ("A_2_3", -1.7262037266467e-19), ("A_3_0", -5.9235031179999e-13), ("A_3_1", -3.4444326387310e-16), ("A_3_2", 1.4377441160800e-19), ("A_4_0", 1.8736407845095e-16), ("A_4_1", 2.9213314172884e-20), ("A_5_0", -5.3601346091084e-20), ("B_ORDER", 5), ("B_0_2", 4.9268299822979e-08), ("B_0_3", -5.9235032026906e-13), ("B_0_4", 1.8736407776035e-16), ("B_0_5", -5.3601341373220e-20), ("B_1_1", 5.4246387435453e-08), ("B_1_2", 1.6365657531115e-12), ("B_1_3", -3.4444326228808e-16), ("B_1_4", 2.9213312399941e-20), ("B_2_0", -1.7769487494962e-09), ("B_2_1", -1.5689082999319e-12), ("B_2_2", 1.1976983393279e-16), ("B_2_3", 1.4377441169892e-19), ("B_3_0", 5.3745894237186e-13), ("B_3_1", 1.2424130479929e-16), ("B_3_2", -1.7262036838229e-19), ("B_4_0", -7.2921117326608e-17), ("B_4_1", 3.9982566975450e-20), ("B_5_0", 8.6947240592408e-21), ("AP_ORDER", 6), ("AP_0_0", -5.4343024221207e-11), ("AP_0_1", 5.5722265946666e-12), ("AP_0_2", 1.7769484042400e-09), ("AP_0_3", -5.3773609554820e-13), ("AP_0_4", 7.3035278852156e-17), ("AP_0_5", -8.7151153799062e-21), ("AP_0_6", 3.2535945427624e-27), ("AP_1_0", -3.8944805432871e-12), ("AP_1_1", -5.4246388067582e-08), ("AP_1_2", 1.5741716194971e-12), ("AP_1_3", -1.2447067748187e-16), ("AP_1_4", -3.9960260822306e-20), ("AP_1_5", 1.1297941471380e-26), ("AP_2_0", -4.9268299293185e-08), ("AP_2_1", -1.6256111849359e-12), ("AP_2_2", -1.1973373130440e-16), ("AP_2_3", 1.7266948205700e-19), ("AP_2_4", -3.7059606160753e-26), ("AP_3_0", 5.9710911995811e-13), ("AP_3_1", 3.4464427650041e-16), ("AP_3_2", -1.4381853884204e-19), ("AP_3_3", -7.6527426974322e-27), ("AP_4_0", -1.8748435698960e-16), ("AP_4_1", -2.9267280226373e-20), ("AP_4_2", 4.8004317051259e-26), ("AP_5_0", 5.3657330221120e-20), ("AP_5_1", -1.6904065766661e-27), ("AP_6_0", -1.9484495120493e-26), ("BP_ORDER", 6), ("BP_0_0", -5.4291220607725e-11), ("BP_0_1", -3.8944871307931e-12), ("BP_0_2", -4.9268299290361e-08), ("BP_0_3", 5.9710912831833e-13), ("BP_0_4", -1.8748435594265e-16), ("BP_0_5", 5.3657325543368e-20), ("BP_0_6", -1.9484577299247e-26), ("BP_1_0", 5.5722051513577e-12), ("BP_1_1", -5.4246388065000e-08), ("BP_1_2", -1.6256111821465e-12), ("BP_1_3", 3.4464427499767e-16), ("BP_1_4", -2.9267278448109e-20), ("BP_1_5", -1.6904244067295e-27), ("BP_2_0", 1.7769484069376e-09), ("BP_2_1", 1.5741716110182e-12), ("BP_2_2", -1.1973373446176e-16), ("BP_2_3", -1.4381853893526e-19), ("BP_2_4", 4.8004294492911e-26), ("BP_3_0", -5.3773609074713e-13), ("BP_3_1", -1.2447067726801e-16), ("BP_3_2", 1.7266947774875e-19), ("BP_3_3", -7.6527556667042e-27), ("BP_4_0", 7.3035279660505e-17), ("BP_4_1", -3.9960255158200e-20), ("BP_4_2", -3.7059659675039e-26), ("BP_5_0", -8.7151157361284e-21), ("BP_5_1", 1.1297944388060e-26), ("BP_6_0", 3.2535788867488e-27), ): metadata.set(name, value) self.metadata = metadata self.bbox = Box2D(Point2D(-1000, -1000), Extent2D(3000, 3000))
def checkTanWcs(self, crval, orientation, flipX): """Construct a pure TAN SkyWcs and check that it operates as specified Parameters ---------- crval : `lsst.afw.geom.SpherePoint` Desired reference sky position. Must not be at either pole. orientation : `lsst.afw.geom.Angle` Position angle of focal plane +Y, measured from N through E. At 0 degrees, +Y is along N and +X is along E/W if flipX false/true At 90 degrees, +Y is along E and +X is along S/N if flipX false/true flipX : `bool` Flip x axis? See `orientation` for details. Returns ------- wcs : `lsst.afw.geom.SkyWcs` The generated pure TAN SkyWcs """ cdMatrix = makeCdMatrix(scale=self.scale, orientation=orientation, flipX=flipX) wcs = makeSkyWcs(crpix=self.crpix, crval=crval, cdMatrix=cdMatrix) self.checkPersistence(wcs, bbox=self.bbox) self.checkMakeFlippedWcs(wcs) self.assertTrue(wcs.isFits) self.assertEqual(wcs.isFlipped, bool(flipX)) xoffAng = 0 * degrees if flipX else 180 * degrees pixelList = [ Point2D(self.crpix[0], self.crpix[1]), Point2D(self.crpix[0] + 1, self.crpix[1]), Point2D(self.crpix[0], self.crpix[1] + 1), ] skyList = wcs.pixelToSky(pixelList) # check pixels to sky predSkyList = [ crval, crval.offset(xoffAng - orientation, self.scale), crval.offset(90 * degrees - orientation, self.scale), ] self.assertSpherePointListsAlmostEqual(predSkyList, skyList) self.assertSpherePointListsAlmostEqual(predSkyList, wcs.pixelToSky(pixelList)) for pixel, predSky in zip(pixelList, predSkyList): self.assertSpherePointsAlmostEqual(predSky, wcs.pixelToSky(pixel)) self.assertSpherePointsAlmostEqual( predSky, wcs.pixelToSky(pixel[0], pixel[1])) # check sky to pixels self.assertPairListsAlmostEqual(pixelList, wcs.skyToPixel(skyList)) self.assertPairListsAlmostEqual(pixelList, wcs.skyToPixel(skyList)) for pixel, sky in zip(pixelList, skyList): self.assertPairsAlmostEqual(pixel, wcs.skyToPixel(sky)) # self.assertPairsAlmostEqual(pixel, wcs.skyToPixel(sky[0], sky[1])) # check CRVAL round trip self.assertSpherePointsAlmostEqual(wcs.getSkyOrigin(), crval, maxSep=self.tinyAngle) crpix = wcs.getPixelOrigin() self.assertPairsAlmostEqual(crpix, self.crpix, maxDiff=self.tinyPixels) self.assertFloatsAlmostEqual(wcs.getCdMatrix(), cdMatrix) pixelScale = wcs.getPixelScale() self.assertAnglesAlmostEqual(self.scale, pixelScale, maxDiff=self.tinyAngle) pixelScale = wcs.getPixelScale(self.crpix) self.assertAnglesAlmostEqual(self.scale, pixelScale, maxDiff=self.tinyAngle) # check that getFitsMetadata can operate at high precision # and has axis order RA, Dec fitsMetadata = wcs.getFitsMetadata(True) self.assertEqual(fitsMetadata.get("CTYPE1")[0:4], "RA--") self.assertEqual(fitsMetadata.get("CTYPE2")[0:4], "DEC-") # Compute a WCS with the pixel origin shifted by an arbitrary amount # The resulting sky origin should not change offset = Extent2D(500, -322) # arbitrary shiftedWcs = wcs.copyAtShiftedPixelOrigin(offset) self.assertTrue(shiftedWcs.isFits) predShiftedPixelOrigin = self.crpix + offset self.assertPairsAlmostEqual(shiftedWcs.getPixelOrigin(), predShiftedPixelOrigin, maxDiff=self.tinyPixels) self.assertSpherePointsAlmostEqual(shiftedWcs.getSkyOrigin(), crval, maxSep=self.tinyAngle) shiftedPixelList = [p + offset for p in pixelList] shiftedSkyList = shiftedWcs.pixelToSky(shiftedPixelList) self.assertSpherePointListsAlmostEqual(skyList, shiftedSkyList, maxSep=self.tinyAngle) # Check that the shifted WCS can be round tripped as FITS metadata shiftedMetadata = shiftedWcs.getFitsMetadata(precise=True) shiftedWcsCopy = makeSkyWcs(shiftedMetadata) shiftedBBox = Box2D(predShiftedPixelOrigin, predShiftedPixelOrigin + Extent2I(2000, 2000)) self.assertWcsAlmostEqualOverBBox(shiftedWcs, shiftedWcsCopy, shiftedBBox) wcsCopy = SkyWcs.readString(wcs.writeString()) self.assertTrue(wcsCopy.isFits) return wcs