def testWarnings(self):
     """Test that approximateWcs raises a UserWarning when it cannot achieve desired tolerance"""
     radialTransform = afwGeom.makeRadialTransform([0, 2.0, 3.0])
     wcs = afwGeom.makeModifiedWcs(pixelTransform=radialTransform, wcs=self.tanWcs,
                                   modifyActualPixels=False)
     with self.assertRaises(UserWarning):
         approximateWcs(wcs=wcs, bbox=self.bbox, order=2)
Esempio n. 2
0
    def doTest(self, name, transform, order=3, doPlot=False):
        """Add the specified distorting transform to a TAN WCS and fit it

        The resulting WCS pixelToSky method acts as follows:
            pixelToSky(transform.applyForward(pixels))
        """
        wcs = afwGeom.makeModifiedWcs(pixelTransform=transform,
                                      wcs=self.tanWcs,
                                      modifyActualPixels=False)

        fitWcs = approximateWcs(
            wcs=wcs,
            bbox=self.bbox,
            order=order,
        )

        if doPlot:
            self.plotWcs(wcs, fitWcs, self.bbox, transform)

        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)
Esempio n. 3
0
 def testWarnings(self):
     """Test that approximateWcs raises a UserWarning when it cannot achieve desired tolerance"""
     radialTransform = afwGeom.makeRadialTransform([0, 2.0, 3.0])
     wcs = afwGeom.makeModifiedWcs(pixelTransform=radialTransform,
                                   wcs=self.tanWcs,
                                   modifyActualPixels=False)
     with self.assertRaises(UserWarning):
         approximateWcs(wcs=wcs, bbox=self.bbox, order=2)
    def doTest(self, pixelsToTanPixels, order=3):
        """Test using pixelsToTanPixels to distort the source positions
        """
        distortedWcs = afwGeom.makeModifiedWcs(pixelTransform=pixelsToTanPixels, wcs=self.tanWcs,
                                               modifyActualPixels=False)
        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.separation(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)
    def doTest(self, pixelsToTanPixels):
        """Test using pixelsToTanPixels to distort the source positions
        """
        distortedWcs = afwGeom.makeModifiedWcs(pixelTransform=pixelsToTanPixels, wcs=self.tanWcs,
                                               modifyActualPixels=False)
        self.exposure.setWcs(distortedWcs)
        sourceCat = self.makeSourceCat(distortedWcs)
        print("number of stars =", len(sourceCat))
        config = ANetAstrometryTask.ConfigClass()
        solver = ANetAstrometryTask(config=config, refObjLoader=self.refObjLoader,
                                    schema=self.makeSourceSchema())
        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*lsst.geom.arcseconds, maxDiffPix=0.02)

        srcCoordKey = afwTable.CoordKey(sourceCat.schema["coord"])
        refCoordKey = afwTable.CoordKey(results.refCat.schema["coord"])
        maxAngSep = 0*lsst.geom.radians
        maxPixSep = 0
        for refObj, src, d in results.matches:
            refCoord = refObj.get(refCoordKey)
            refPixPos = fitWcs.skyToPixel(refCoord)
            srcCoord = src.get(srcCoordKey)
            srcPixPos = src.getCentroid()

            angSep = refCoord.separation(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.005)
        self.assertLess(maxPixSep, 0.03)

        # try again, but without fitting the WCS
        config.forceKnownWcs = True
        solverNoFit = ANetAstrometryTask(config=config, refObjLoader=self.refObjLoader,
                                         schema=self.makeSourceSchema())
        self.exposure.setWcs(distortedWcs)
        noFitResults = solverNoFit.run(
            sourceCat=sourceCat,
            exposure=self.exposure,
        )
        self.assertGreater(len(noFitResults.refCat), 300)
Esempio n. 6
0
    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.makeRadialTransform([0.0, 1.1, 0.0004])
        self.distortedWcs = afwGeom.makeModifiedWcs(pixelTransform=pixelsToTanPixels,
                                                    wcs=self.wcs,
                                                    modifyActualPixels=False)

        def applyDistortion(src):
            out = src.table.copyRecord(src)
            out.set(out.table.getCentroidKey(),
                    pixelsToTanPixels.applyInverse(src.getCentroid()))
            return out

        self.singleTestInstance(self.filename, applyDistortion)
    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.makeRadialTransform([0.0, 1.1, 0.0004])
        self.distortedWcs = afwGeom.makeModifiedWcs(pixelTransform=pixelsToTanPixels,
                                                    wcs=self.wcs,
                                                    modifyActualPixels=False)

        def applyDistortion(src):
            out = src.table.copyRecord(src)
            out.set(out.table.getCentroidKey(),
                    pixelsToTanPixels.applyInverse(src.getCentroid()))
            return out

        self.singleTestInstance(self.filename, applyDistortion)
Esempio n. 8
0
def rotateExposure(exp, nDegrees, kernelName='lanczos4', logger=None):
    """Rotate an exposure by nDegrees clockwise.

    Parameters
    ----------
    exp : `lsst.afw.image.exposure.Exposure`
        The exposure to rotate
    nDegrees : `float`
        Number of degrees clockwise to rotate by
    kernelName : `str`
        Name of the warping kernel, used to instantiate the warper.
    logger : `lsst.log.Log`
        Logger for logging warnings

    Returns
    -------
    rotatedExp : `lsst.afw.image.exposure.Exposure`
        A copy of the input exposure, rotated by nDegrees
    """
    nDegrees = nDegrees % 360

    if not logger:
        logger = lsstLog.getLogger('atmospec.utils')

    wcs = exp.getWcs()
    if not wcs:
        logger.warn(
            "Can't rotate exposure without a wcs - returning exp unrotated")
        return exp.clone(
        )  # return a clone so it's always returning a copy as this is what default does

    warper = afwMath.Warper(kernelName)
    if isinstance(exp, afwImage.ExposureU):
        # TODO: remove once this bug is fixed - DM-20258
        logger.info('Converting ExposureU to ExposureF due to bug')
        logger.info('Remove this workaround after DM-20258')
        exp = afwImage.ExposureF(exp, deep=True)

    affineRotTransform = geom.AffineTransform.makeRotation(nDegrees *
                                                           geom.degrees)
    transformP2toP2 = afwGeom.makeTransform(affineRotTransform)
    rotatedWcs = afwGeom.makeModifiedWcs(transformP2toP2, wcs, False)

    rotatedExp = warper.warpExposure(rotatedWcs, exp)
    # rotatedExp.setXY0(geom.Point2I(0, 0))  # TODO: check no longer required
    return rotatedExp
    def setUp(self):
        # Make fake sources
        self.nSources = 10
        self.bbox = geom.Box2I(geom.Point2I(0, 0),
                               geom.Extent2I(1024, 1153))
        self.xyLoc = 100
        dataset = measTests.TestDataset(self.bbox)
        for srcIdx in range(self.nSources):
            dataset.addSource(100000.0,
                              geom.Point2D(srcIdx*self.xyLoc,
                                           srcIdx*self.xyLoc))
        schema = dataset.makeMinimalSchema()
        schema.addField("base_PixelFlags_flag", type="Flag")
        schema.addField("base_PixelFlags_flag_offimage", type="Flag")
        self.exposure, catalog = dataset.realize(
            10.0, schema, randomSeed=1234)
        for src in catalog:
            src.setCoord(self.exposure.getWcs().pixelToSky(src.getCentroid()))
        # Non-invertible WCS to test robustness to distortions.
        # Coefficients transform (x - 1e-7*x^3 -> x, y -> y); see docs for PolyMap.
        pixelCoeffs = np.array([[-1.0e-7, 1, 3, 0],
                                [1.0, 1, 1, 0],
                                [1.0, 2, 0, 1],
                                ])
        self.exposure.setWcs(afwGeom.makeModifiedWcs(
            afwGeom.TransformPoint2ToPoint2(ast.PolyMap(pixelCoeffs, 2, options="IterInverse=1")),
            self.exposure.wcs,
            modifyActualPixels=False
        ))

        # Convert to task required format
        self.testDiaSources = catalog.asAstropy().to_pandas()
        self.testDiaSources.rename(columns={"coord_ra": "ra",
                                            "coord_dec": "decl"},
                                   inplace=True,)
        self.testDiaSources.loc[:, "ra"] = np.rad2deg(self.testDiaSources["ra"])
        self.testDiaSources.loc[:, "decl"] = np.rad2deg(self.testDiaSources["decl"])
        self.testDiaSources["ssObjectId"] = 0

        # Grab a subset to treat as solar system objects
        self.testSsObjects = self.testDiaSources[2:8].reset_index()
        # Assign them ids starting from 1.
        self.testSsObjects.loc[:, "ssObjectId"] = np.arange(
            1, len(self.testSsObjects) + 1, dtype=int,)
        self.testSsObjects["Err(arcsec)"] = np.ones(len(self.testSsObjects))
Esempio n. 10
0
    def testMakeModifiedWcsNoActualPixels(self):
        """Test makeModifiedWcs on a SkyWcs that has no ACTUAL_PIXELS frame
        """
        cdMatrix = makeCdMatrix(scale=self.scale)
        originalWcs = makeSkyWcs(crpix=self.crpix,
                                 crval=self.crvalList[0],
                                 cdMatrix=cdMatrix)
        originalFrameDict = originalWcs.getFrameDict()

        # make an arbitrary but reasonable transform to insert using makeModifiedWcs
        pixelTransform = makeRadialTransform([0.0, 1.0, 0.0, 0.0011])
        # the result of the insertion should be as follows
        desiredPixelsToSky = pixelTransform.then(originalWcs.getTransform())

        pixPointList = (  # arbitrary but reasonable
            Point2D(0.0, 0.0),
            Point2D(1000.0, 0.0),
            Point2D(0.0, 2000.0),
            Point2D(-1111.0, -2222.0),
        )
        for modifyActualPixels in (False, True):
            modifiedWcs = makeModifiedWcs(
                pixelTransform=pixelTransform,
                wcs=originalWcs,
                modifyActualPixels=modifyActualPixels)
            modifiedFrameDict = modifiedWcs.getFrameDict()
            skyList = modifiedWcs.pixelToSky(pixPointList)

            # compare pixels to sky
            desiredSkyList = desiredPixelsToSky.applyForward(pixPointList)
            self.assertSpherePointListsAlmostEqual(skyList, desiredSkyList)

            # compare pixels to IWC
            pixelsToIwc = TransformPoint2ToPoint2(
                modifiedFrameDict.getMapping("PIXELS", "IWC"))
            desiredPixelsToIwc = TransformPoint2ToPoint2(
                pixelTransform.getMapping().then(
                    originalFrameDict.getMapping("PIXELS", "IWC")))
            self.assertPairListsAlmostEqual(
                pixelsToIwc.applyForward(pixPointList),
                desiredPixelsToIwc.applyForward(pixPointList))

            self.checkNonFitsWcs(modifiedWcs)
    def doTest(self, name, transform, order=3, doPlot=False):
        """Add the specified distorting transform to a TAN WCS and fit it

        The resulting WCS pixelToSky method acts as follows:
            pixelToSky(transform.applyForward(pixels))
        """
        wcs = afwGeom.makeModifiedWcs(pixelTransform=transform,
                                      wcs=self.tanWcs,
                                      modifyActualPixels=False)

        fitWcs = approximateWcs(
            wcs=wcs,
            bbox=self.bbox,
            order=order,
        )

        if doPlot:
            self.plotWcs(wcs, fitWcs, self.bbox, transform)

        msg = "ERROR: %s failed with order %s" % (name, order)
        self.assertWcsAlmostEqualOverBBox(wcs, fitWcs, self.bbox,
                                          maxDiffSky=0.001*lsst.geom.arcseconds, maxDiffPix=0.02, msg=msg)
Esempio n. 12
0
    def testMakeModifiedWcsWithActualPixels(self):
        """Test makeModifiedWcs on a SkyWcs that has an ACTUAL_PIXELS frame
        """
        cdMatrix = makeCdMatrix(scale=self.scale)
        baseWcs = makeSkyWcs(crpix=self.crpix,
                             crval=self.crvalList[0],
                             cdMatrix=cdMatrix)
        # model actual pixels to pixels as an arbitrary zoom factor;
        # this is not realistic, but is fine for a unit test
        actualPixelsToPixels = TransformPoint2ToPoint2(ast.ZoomMap(2, 0.72))
        originalWcs = addActualPixelsFrame(baseWcs, actualPixelsToPixels)
        originalFrameDict = originalWcs.getFrameDict()

        # make an arbitrary but reasonable transform to insert using makeModifiedWcs
        pixelTransform = makeRadialTransform([0.0, 1.0, 0.0, 0.0011
                                              ])  # arbitrary but reasonable

        pixPointList = (  # arbitrary but reasonable
            Point2D(0.0, 0.0),
            Point2D(1000.0, 0.0),
            Point2D(0.0, 2000.0),
            Point2D(-1111.0, -2222.0),
        )
        for modifyActualPixels in (True, False):
            modifiedWcs = makeModifiedWcs(
                pixelTransform=pixelTransform,
                wcs=originalWcs,
                modifyActualPixels=modifyActualPixels)
            modifiedFrameDict = modifiedWcs.getFrameDict()
            self.assertEqual(
                modifiedFrameDict.getFrame(modifiedFrameDict.BASE).domain,
                "ACTUAL_PIXELS")
            modifiedActualPixelsToPixels = \
                TransformPoint2ToPoint2(modifiedFrameDict.getMapping("ACTUAL_PIXELS", "PIXELS"))
            modifiedPixelsToIwc = TransformPoint2ToPoint2(
                modifiedFrameDict.getMapping("PIXELS", "IWC"))

            # compare pixels to sky
            skyList = modifiedWcs.pixelToSky(pixPointList)
            if modifyActualPixels:
                desiredPixelsToSky = pixelTransform.then(
                    originalWcs.getTransform())
            else:
                originalPixelsToSky = \
                    TransformPoint2ToSpherePoint(originalFrameDict.getMapping("PIXELS", "SKY"))
                desiredPixelsToSky = actualPixelsToPixels.then(
                    pixelTransform).then(originalPixelsToSky)
            desiredSkyList = desiredPixelsToSky.applyForward(pixPointList)
            self.assertSpherePointListsAlmostEqual(skyList, desiredSkyList)

            # compare ACTUAL_PIXELS to PIXELS and PIXELS to IWC
            if modifyActualPixels:
                # check that ACTUAL_PIXELS to PIXELS has been modified as expected
                desiredActualPixelsToPixels = pixelTransform.then(
                    actualPixelsToPixels)
                self.assertPairListsAlmostEqual(
                    modifiedActualPixelsToPixels.applyForward(pixPointList),
                    desiredActualPixelsToPixels.applyForward(pixPointList))

                # check that PIXELS to IWC is unchanged
                originalPixelsToIwc = TransformPoint2ToPoint2(
                    originalFrameDict.getMapping("PIXELS", "IWC"))
                self.assertPairListsAlmostEqual(
                    modifiedPixelsToIwc.applyForward(pixPointList),
                    originalPixelsToIwc.applyForward(pixPointList))

            else:
                # check that ACTUAL_PIXELS to PIXELS is unchanged
                self.assertPairListsAlmostEqual(
                    actualPixelsToPixels.applyForward(pixPointList),
                    actualPixelsToPixels.applyForward(pixPointList))

                # check that PIXELS to IWC has been modified as expected
                desiredPixelsToIwc = TransformPoint2ToPoint2(
                    pixelTransform.getMapping().then(
                        originalFrameDict.getMapping("PIXELS", "IWC")))
                self.assertPairListsAlmostEqual(
                    modifiedPixelsToIwc.applyForward(pixPointList),
                    desiredPixelsToIwc.applyForward(pixPointList))

            self.checkNonFitsWcs(modifiedWcs)
    def doTest(self, pixelsToTanPixels, order=3):
        """Test using pixelsToTanPixels to distort the source positions
        """
        distortedWcs = afwGeom.makeModifiedWcs(pixelTransform=pixelsToTanPixels, wcs=self.tanWcs,
                                               modifyActualPixels=False)
        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*lsst.geom.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 = 0*lsst.geom.radians
        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.separation(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.0038)
        self.assertLess(maxPixSep, 0.021)

        # try again, invoking the reference selector
        config.referenceSelector.doUnresolved = True
        config.referenceSelector.unresolved.name = 'resolved'
        solverRefSelect = AstrometryTask(config=config, refObjLoader=self.refObjLoader)
        self.exposure.setWcs(distortedWcs)
        resultsRefSelect = solverRefSelect.run(
            sourceCat=sourceCat,
            exposure=self.exposure,
        )
        self.assertLess(len(resultsRefSelect.matches), len(results.matches))

        # try again, but without fitting the WCS, no reference selector
        config.referenceSelector.doUnresolved = False
        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)

        # try once again, without fitting the WCS, with the reference selector
        # (this goes through a different code path)
        config.referenceSelector.doUnresolved = True
        solverNoFitRefSelect = AstrometryTask(config=config, refObjLoader=self.refObjLoader)
        resultsNoFitRefSelect = solverNoFitRefSelect.run(
            sourceCat=sourceCat,
            exposure=self.exposure,
        )
        self.assertLess(len(resultsNoFitRefSelect.matches), len(resultsNoFit.matches))