예제 #1
0
    def makeSourceCat(self, distortedWcs):
        """Make a source catalog by reading the position reference stars and distorting the positions
        """
        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
        sourceCat = afwTable.SourceCatalog(sourceSchema)
        sourceCentroidKey = afwTable.Point2DKey(sourceSchema["slot_Centroid"])
        sourceInstFluxKey = sourceSchema["slot_ApFlux_instFlux"].asKey()
        sourceInstFluxErrKey = sourceSchema["slot_ApFlux_instFluxErr"].asKey()

        sourceCat.reserve(len(refCat))
        for refObj in refCat:
            src = sourceCat.addNew()
            src.set(sourceCentroidKey, refObj.get(refCentroidKey))
            src.set(sourceInstFluxKey, refObj.get(refFluxRKey))
            src.set(sourceInstFluxErrKey, refObj.get(refFluxRKey) / 100)
        return sourceCat
예제 #2
0
    def setUp(self):
        # make a nominal match list where the distances are 0; test can then modify
        # source centroid, reference coord or distance field for each match, as desired
        self.wcs = afwGeom.makeSkyWcs(crpix=lsst.geom.Point2D(1500, 1500),
                                      crval=lsst.geom.SpherePoint(215.5, 53.0, lsst.geom.degrees),
                                      cdMatrix=afwGeom.makeCdMatrix(scale=5.1e-5*lsst.geom.degrees))
        self.bboxD = lsst.geom.Box2D(lsst.geom.Point2D(10, 100), lsst.geom.Extent2D(1000, 1500))
        self.numMatches = 25

        sourceSchema = afwTable.SourceTable.makeMinimalSchema()
        # add centroid (and many other unwanted fields) to sourceSchema
        SingleFrameMeasurementTask(schema=sourceSchema)
        self.sourceCentroidKey = afwTable.Point2DKey(sourceSchema["slot_Centroid"])
        self.sourceCat = afwTable.SourceCatalog(sourceSchema)

        refSchema = afwTable.SourceTable.makeMinimalSchema()
        self.refCoordKey = afwTable.CoordKey(refSchema["coord"])
        self.refCat = afwTable.SourceCatalog(refSchema)

        self.matchList = []

        np.random.seed(5)
        pixPointList = [lsst.geom.Point2D(pos) for pos in
                        np.random.random_sample([self.numMatches, 2])*self.bboxD.getDimensions() +
                        self.bboxD.getMin()]
        for pixPoint in pixPointList:
            src = self.sourceCat.addNew()
            src.set(self.sourceCentroidKey, pixPoint)
            ref = self.refCat.addNew()
            ref.set(self.refCoordKey, self.wcs.pixelToSky(pixPoint))

            match = afwTable.ReferenceMatch(ref, src, 0)
            self.matchList.append(match)
예제 #3
0
    def loadData(self, rangePix=3000, numPoints=25):
        """Load catalogs and make the match list

        This is a separate function so data can be reloaded if fitting more than once
        (each time a WCS is fit it may update the source catalog, reference catalog and match list)
        """
        refSchema = LoadReferenceObjectsTask.makeMinimalSchema(
            filterNameList=["r"], addIsPhotometric=True, addCentroid=True)
        self.refCat = afwTable.SimpleCatalog(refSchema)
        srcSchema = afwTable.SourceTable.makeMinimalSchema()
        SingleFrameMeasurementTask(schema=srcSchema)
        self.srcCoordKey = afwTable.CoordKey(srcSchema["coord"])
        self.srcCentroidKey = afwTable.Point2DKey(srcSchema["slot_Centroid"])
        self.srcCentroidKey_xErr = srcSchema["slot_Centroid_xErr"].asKey()
        self.srcCentroidKey_yErr = srcSchema["slot_Centroid_yErr"].asKey()
        self.sourceCat = afwTable.SourceCatalog(srcSchema)

        self.matches = []

        for i in np.linspace(0., rangePix, numPoints):
            for j in np.linspace(0., rangePix, numPoints):
                src = self.sourceCat.addNew()
                refObj = self.refCat.addNew()

                src.set(self.srcCentroidKey, lsst.geom.Point2D(i, j))
                src.set(self.srcCentroidKey_xErr, 0.1)
                src.set(self.srcCentroidKey_yErr, 0.1)

                c = self.tanWcs.pixelToSky(i, j)
                refObj.setCoord(c)

                self.matches.append(self.MatchClass(refObj, src, 0.0))
 def makeCatalog(self, apCorrScale=1.0, numSources=5):
     sourceCat = afwTable.SourceCatalog(self.schema)
     fluxName = self.name + "_flux"
     flagName = self.name + "_flag"
     fluxSigmaName = self.name + "_fluxSigma"
     apFluxName = self.apname + "_flux"
     apFlagName = self.apname + "_flag"
     apFluxSigmaName = self.apname + "_fluxSigma"
     fluxKey = self.schema.find(fluxName).key
     flagKey = self.schema.find(flagName).key
     fluxSigmaKey = self.schema.find(fluxSigmaName).key
     apFluxKey = self.schema.find(apFluxName).key
     apFlagKey = self.schema.find(apFlagName).key
     apFluxSigmaKey = self.schema.find(apFluxSigmaName).key
     centroidKey = afwTable.Point2DKey(self.schema["slot_Centroid"])
     inputFilterFlagKey = self.schema.find(self.meas_apCorr_task.config.sourceSelector.active.field).key
     x = np.random.rand(numSources)*self.exposure.getWidth() + self.exposure.getX0()
     y = np.random.rand(numSources)*self.exposure.getHeight() + self.exposure.getY0()
     for _i in range(numSources):
         source_test_flux = 5.1
         source_test_centroid = afwGeom.Point2D(x[_i], y[_i])
         source = sourceCat.addNew()
         source.set(fluxKey, source_test_flux)
         source.set(apFluxKey, source_test_flux * apCorrScale)
         source.set(centroidKey, source_test_centroid)
         source.set(fluxSigmaKey, 0.)
         source.set(apFluxSigmaKey, 0.)
         source.set(flagKey, False)
         source.set(apFlagKey, False)
         source.set(inputFilterFlagKey, True)
     return(sourceCat)
    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 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)
예제 #7
0
 def __init__(self, config, name, schemaMapper, metadata):
     ForcedPlugin.__init__(self, config, name, schemaMapper, metadata)
     schema = schemaMapper.editOutputSchema()
     # Allocate x and y fields, join these into a single FunctorKey for ease-of-use.
     xKey = schema.addField(name + "_x", type="D", doc="transformed reference centroid column",
                            units="pixel")
     yKey = schema.addField(name + "_y", type="D", doc="transformed reference centroid row",
                            units="pixel")
     self.centroidKey = afwTable.Point2DKey(xKey, yKey)
예제 #8
0
def initializeSourceCatalog(schema=None, name=None, flux=None, sigma=None, centroid=None):
    fluxName = name + "_flux"
    fluxSigmaName = name + "_fluxSigma"
    fluxKey = schema.find(fluxName).key
    centroidKey = afwTable.Point2DKey(schema["slot_Centroid"])
    sourceCat = afwTable.SourceCatalog(schema)
    source = sourceCat.addNew()
    source.set(fluxKey, flux)
    source.set(fluxSigmaName, sigma)
    source.set(centroidKey, centroid)
    return(sourceCat)
예제 #9
0
def initializeSourceCatalog(schema=None, name=None, instFlux=None, sigma=None, centroid=None):
    instFluxName = name + "_instFlux"
    instFluxErrName = name + "_instFluxErr"
    instFluxKey = schema.find(instFluxName).key
    centroidKey = afwTable.Point2DKey(schema["slot_Centroid"])
    sourceCat = afwTable.SourceCatalog(schema)
    source = sourceCat.addNew()
    source.set(instFluxKey, instFlux)
    source.set(instFluxErrName, sigma)
    source.set(centroidKey, centroid)
    return(sourceCat)
    def setUp(self):
        # make a nominal match list where the distances are 0; test can then modify
        # source centroid, reference coord or distance field for each match, as desired
        ctrPix = afwGeom.Point2I(1500, 1500)
        metadata = PropertySet()
        metadata.set("RADECSYS", "FK5")
        metadata.set("EQUINOX", 2000.0)
        metadata.set("CTYPE1", "RA---TAN")
        metadata.set("CTYPE2", "DEC--TAN")
        metadata.set("CUNIT1", "deg")
        metadata.set("CUNIT2", "deg")
        metadata.set("CRVAL1", 215.5)
        metadata.set("CRVAL2", 53.0)
        metadata.set("CRPIX1", ctrPix[0] + 1)
        metadata.set("CRPIX2", ctrPix[1] + 1)
        metadata.set("CD1_1", 5.1e-05)
        metadata.set("CD1_2", 0.0)
        metadata.set("CD2_2", -5.1e-05)
        metadata.set("CD2_1", 0.0)
        self.wcs = afwImage.makeWcs(metadata)
        self.bboxD = afwGeom.Box2D(afwGeom.Point2D(10, 100),
                                   afwGeom.Extent2D(1000, 1500))
        self.numMatches = 25

        sourceSchema = afwTable.SourceTable.makeMinimalSchema()
        # add centroid (and many other unwanted fields) to sourceSchema
        SingleFrameMeasurementTask(schema=sourceSchema)
        self.sourceCentroidKey = afwTable.Point2DKey(
            sourceSchema["slot_Centroid"])
        self.sourceCat = afwTable.SourceCatalog(sourceSchema)

        refSchema = afwTable.SourceTable.makeMinimalSchema()
        self.refCoordKey = afwTable.CoordKey(refSchema["coord"])
        self.refCat = afwTable.SourceCatalog(refSchema)

        self.matchList = []

        np.random.seed(5)
        pixPointList = [
            afwGeom.Point2D(pos)
            for pos in np.random.random_sample([self.numMatches, 2]) *
            self.bboxD.getDimensions() + self.bboxD.getMin()
        ]
        for pixPoint in pixPointList:
            src = self.sourceCat.addNew()
            src.set(self.sourceCentroidKey, pixPoint)
            ref = self.refCat.addNew()
            ref.set(self.refCoordKey, self.wcs.pixelToSky(pixPoint))

            match = afwTable.ReferenceMatch(ref, src, 0)
            self.matchList.append(match)
 def testSourceNotUsed(self):
     """ Check that a source outside the bounding box is flagged as not used (False)."""
     fluxName = self.name + "_flux"
     apCorrFlagKey = self.schema.find("apcorr_" + self.name + "_used").key
     sourceCat = self.makeCatalog()
     source = sourceCat.addNew()
     source_test_flux = 5.1
     source_test_centroid = afwGeom.Point2D(15, 7.1)
     fluxKey = self.schema.find(fluxName).key
     centroidKey = afwTable.Point2DKey(self.schema["slot_Centroid"])
     source.set(fluxKey, source_test_flux)
     source.set(centroidKey, source_test_centroid)
     self.meas_apCorr_task.run(catalog=sourceCat, exposure=self.exposure)
     self.assertFalse(sourceCat[apCorrFlagKey][-1])
예제 #12
0
    def checkResults(self, fitRes, catsUpdated):
        """Check results

        @param[in] fitRes  a object with two fields:
            - wcs  fit TAN-SIP WCS, an lsst.afw.geom.SkyWcs
            - scatterOnSky  median on-sky scatter, an lsst.afw.geom.Angle
        @param[in] catsUpdated  if True then coord field of self.sourceCat and centroid fields of self.refCat
            have been updated
        """
        self.assertLess(fitRes.scatterOnSky.asArcseconds(), 0.001)
        tanSipWcs = fitRes.wcs
        maxAngSep = 0 * lsst.geom.radians
        maxPixSep = 0
        refCoordKey = afwTable.CoordKey(self.refCat.schema["coord"])
        if catsUpdated:
            refCentroidKey = afwTable.Point2DKey(
                self.refCat.schema["centroid"])
        maxDistErr = 0 * lsst.geom.radians
        for refObj, src, distRad in self.matches:
            srcPixPos = src.get(self.srcCentroidKey)
            refCoord = refObj.get(refCoordKey)
            if catsUpdated:
                refPixPos = refObj.get(refCentroidKey)
                srcCoord = src.get(self.srcCoordKey)
            else:
                refPixPos = tanSipWcs.skyToPixel(refCoord)
                srcCoord = tanSipWcs.pixelToSky(srcPixPos)

            angSep = refCoord.separation(srcCoord)
            dist = distRad * lsst.geom.radians
            distErr = abs(dist - angSep)
            maxDistErr = max(maxDistErr, distErr)
            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.001)
        self.assertLess(maxPixSep, 0.005)
        if catsUpdated:
            allowedDistErr = 1e-7
        else:
            allowedDistErr = 0.001
        self.assertLess(
            maxDistErr.asArcseconds(), allowedDistErr,
            "Computed distance in match list is off by %s arcsec" %
            (maxDistErr.asArcseconds(), ))
예제 #13
0
    def setUp(self):
        crval = IcrsCoord(afwGeom.PointD(44., 45.))
        crpix = afwGeom.PointD(0, 0)

        arcsecPerPixel = 1 / 3600.0
        CD11 = arcsecPerPixel
        CD12 = 0
        CD21 = 0
        CD22 = arcsecPerPixel

        self.tanWcs = makeWcs(crval, crpix, CD11, CD12, CD21, CD22)

        S = 300
        N = 5

        if self.MatchClass == afwTable.ReferenceMatch:
            refSchema = LoadReferenceObjectsTask.makeMinimalSchema(
                filterNameList=["r"], addFluxSigma=True, addIsPhotometric=True)
            self.refCat = afwTable.SimpleCatalog(refSchema)
        elif self.MatchClass == afwTable.SourceMatch:
            refSchema = afwTable.SourceTable.makeMinimalSchema()
            self.refCat = afwTable.SourceCatalog(refSchema)
        else:
            raise RuntimeError("Unsupported MatchClass=%r" %
                               (self.MatchClass, ))
        srcSchema = afwTable.SourceTable.makeMinimalSchema()
        SingleFrameMeasurementTask(schema=srcSchema)
        self.refCoordKey = afwTable.CoordKey(refSchema["coord"])
        self.srcCoordKey = afwTable.CoordKey(srcSchema["coord"])
        self.srcCentroidKey = afwTable.Point2DKey(srcSchema["slot_Centroid"])
        self.sourceCat = afwTable.SourceCatalog(srcSchema)
        self.origSourceCat = afwTable.SourceCatalog(
            srcSchema)  # undistorted copy
        self.matches = []

        for i in np.linspace(0., S, N):
            for j in np.linspace(0., S, N):
                src = self.sourceCat.addNew()
                refObj = self.refCat.addNew()

                src.set(self.srcCentroidKey, afwGeom.Point2D(i, j))

                c = self.tanWcs.pixelToSky(afwGeom.Point2D(i, j))
                refObj.setCoord(c)

                self.matches.append(self.MatchClass(refObj, src, 0.0))
예제 #14
0
    def loadData(self, rangePix=3000, numPoints=25):
        """Load catalogs and make the match list

        This is a separate function so data can be reloaded if fitting more than once
        (each time a WCS is fit it may update the source catalog, reference catalog and match list)
        """
        if self.MatchClass == afwTable.ReferenceMatch:
            refSchema = LoadReferenceObjectsTask.makeMinimalSchema(
                filterNameList=["r"], addIsPhotometric=True, addCentroid=True)
            self.refCat = afwTable.SimpleCatalog(refSchema)
        elif self.MatchClass == afwTable.SourceMatch:
            refSchema = afwTable.SourceTable.makeMinimalSchema()
            self.refCat = afwTable.SourceCatalog(refSchema)
        else:
            raise RuntimeError("Unsupported MatchClass=%r" %
                               (self.MatchClass, ))
        srcSchema = afwTable.SourceTable.makeMinimalSchema()
        SingleFrameMeasurementTask(schema=srcSchema)
        self.srcCoordKey = afwTable.CoordKey(srcSchema["coord"])
        self.srcCentroidKey = afwTable.Point2DKey(srcSchema["slot_Centroid"])
        self.srcCentroidKey_xErr = srcSchema["slot_Centroid_xErr"].asKey()
        self.srcCentroidKey_yErr = srcSchema["slot_Centroid_yErr"].asKey()
        self.sourceCat = afwTable.SourceCatalog(srcSchema)

        self.matches = []

        for i in np.linspace(0., rangePix, numPoints):
            for j in np.linspace(0., rangePix, numPoints):
                src = self.sourceCat.addNew()
                refObj = self.refCat.addNew()

                src.set(self.srcCentroidKey, lsst.geom.Point2D(i, j))
                src.set(self.srcCentroidKey_xErr, 0.1)
                src.set(self.srcCentroidKey_yErr, 0.1)

                c = self.tanWcs.pixelToSky(i, j)
                refObj.setCoord(c)

                if False:
                    print(
                        "x,y = (%.1f, %.1f) pixels -- RA,Dec = (%.3f, %.3f) deg"
                        % (i, j, c.toFk5().getRa().asDegrees(),
                           c.toFk5().getDec().asDegrees()))

                self.matches.append(self.MatchClass(refObj, src, 0.0))
예제 #15
0
    def checkResults(self, fitRes, catsUpdated):
        """Check results.
        """
        self.assertLess(fitRes.scatterOnSky.asArcseconds(), 0.001)
        affineWcs = fitRes.wcs
        maxAngSep = 0 * lsst.geom.radians
        maxPixSep = 0
        refCoordKey = afwTable.CoordKey(self.refCat.schema["coord"])
        if catsUpdated:
            refCentroidKey = afwTable.Point2DKey(
                self.refCat.schema["centroid"])
        maxDistErr = 0 * lsst.geom.radians
        for refObj, src, distRad in self.matches:
            srcPixPos = src.get(self.srcCentroidKey)
            refCoord = refObj.get(refCoordKey)
            if catsUpdated:
                refPixPos = refObj.get(refCentroidKey)
                srcCoord = src.get(self.srcCoordKey)
            else:
                refPixPos = affineWcs.skyToPixel(refCoord)
                srcCoord = affineWcs.pixelToSky(srcPixPos)

            angSep = refCoord.separation(srcCoord)
            dist = distRad * lsst.geom.radians
            distErr = abs(dist - angSep)
            maxDistErr = max(maxDistErr, distErr)
            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.001)
        self.assertLess(maxPixSep, 0.005)
        if catsUpdated:
            allowedDistErr = 1e-7
        else:
            allowedDistErr = 0.001
        self.assertLess(
            maxDistErr.asArcseconds(), allowedDistErr,
            "Computed distance in match list is off by %s arcsec" %
            (maxDistErr.asArcseconds(), ))
예제 #16
0
    def setUp(self):
        crval = afwGeom.SpherePoint(44, 45, afwGeom.degrees)
        crpix = afwGeom.PointD(0, 0)

        scale = 1 * afwGeom.arcseconds
        self.tanWcs = afwGeom.makeSkyWcs(
            crpix=crpix,
            crval=crval,
            cdMatrix=afwGeom.makeCdMatrix(scale=scale))

        S = 300
        N = 5

        if self.MatchClass == afwTable.ReferenceMatch:
            refSchema = LoadReferenceObjectsTask.makeMinimalSchema(
                filterNameList=["r"], addFluxSigma=True, addIsPhotometric=True)
            self.refCat = afwTable.SimpleCatalog(refSchema)
        elif self.MatchClass == afwTable.SourceMatch:
            refSchema = afwTable.SourceTable.makeMinimalSchema()
            self.refCat = afwTable.SourceCatalog(refSchema)
        else:
            raise RuntimeError("Unsupported MatchClass=%r" %
                               (self.MatchClass, ))
        srcSchema = afwTable.SourceTable.makeMinimalSchema()
        SingleFrameMeasurementTask(schema=srcSchema)
        self.refCoordKey = afwTable.CoordKey(refSchema["coord"])
        self.srcCoordKey = afwTable.CoordKey(srcSchema["coord"])
        self.srcCentroidKey = afwTable.Point2DKey(srcSchema["slot_Centroid"])
        self.sourceCat = afwTable.SourceCatalog(srcSchema)
        self.origSourceCat = afwTable.SourceCatalog(
            srcSchema)  # undistorted copy
        self.matches = []

        for i in np.linspace(0., S, N):
            for j in np.linspace(0., S, N):
                src = self.sourceCat.addNew()
                refObj = self.refCat.addNew()

                src.set(self.srcCentroidKey, afwGeom.Point2D(i, j))

                c = self.tanWcs.pixelToSky(afwGeom.Point2D(i, j))
                refObj.setCoord(c)

                self.matches.append(self.MatchClass(refObj, src, 0.0))
    def _trimToBBox(refCat, bbox, wcs):
        """!Remove objects outside a given pixel-based bbox and set centroid and hasCentroid fields

        @param[in,out] refCat  a catalog of objects (an lsst.afw.table.SimpleCatalog,
            or other table type that has fields "coord", "centroid" and "hasCentroid").
            The "coord" field is read.
            The "centroid" and "hasCentroid" fields are set.
        @param[in] bbox  pixel region (an afwImage.Box2D)
        @param[in] wcs  WCS used to convert sky position to pixel position (an lsst.afw.math.WCS)

        @return a catalog of reference objects in bbox, with centroid and hasCentroid fields set
        """
        afwTable.updateRefCentroids(wcs, refCat)
        centroidKey = afwTable.Point2DKey(refCat.schema["centroid"])
        retStarCat = type(refCat)(refCat.table)
        for star in refCat:
            point = star.get(centroidKey)
            if bbox.contains(point):
                retStarCat.append(star)
        return retStarCat
    def _trimToBBox(refCat, bbox, wcs):
        """!Remove objects outside a given pixel-based bbox and set centroid and hasCentroid fields

        @param[in] refCat  a catalog of objects (an lsst.afw.table.SimpleCatalog,
            or other table type that supports getCoord() on records)
        @param[in] bbox  pixel region (an afwImage.Box2D)
        @param[in] wcs  WCS used to convert sky position to pixel position (an lsst.afw.math.WCS)

        @return a catalog of reference objects in bbox, with centroid and hasCentroid fields set
        """
        centroidKey = afwTable.Point2DKey(refCat.schema["centroid"])
        hasCentroidKey = refCat.schema["hasCentroid"].asKey()
        retStarCat = type(refCat)(refCat.table)
        for star in refCat:
            point = wcs.skyToPixel(star.getCoord())
            if bbox.contains(point):
                star.set(centroidKey, point)
                star.set(hasCentroidKey, True)
                retStarCat.append(star)
        return retStarCat
    def testRejectBlends(self):
        """Test the PcaPsfDeterminerTask blend removal."""
        """
        We give it a single blended source, asking it to remove blends,
        and check that it barfs in the expected way.
        """

        psfDeterminerClass = measAlg.psfDeterminerRegistry["pca"]
        config = psfDeterminerClass.ConfigClass()
        config.doRejectBlends = True
        psfDeterminer = psfDeterminerClass(config=config)

        schema = afwTable.SourceTable.makeMinimalSchema()
        # Use The single frame measurement task to populate the schema with standard keys
        measBase.SingleFrameMeasurementTask(schema)
        catalog = afwTable.SourceCatalog(schema)
        source = catalog.addNew()

        # Make the source blended, with necessary information to calculate pca
        spanShift = afwGeom.Point2I(54, 123)
        spans = afwGeom.SpanSet.fromShape(6, offset=spanShift)
        foot = afwDetection.Footprint(spans, self.exposure.getBBox())
        foot.addPeak(45, 123, 6)
        foot.addPeak(47, 126, 5)
        source.setFootprint(foot)
        centerKey = afwTable.Point2DKey(source.schema['slot_Centroid'])
        shapeKey = afwTable.QuadrupoleKey(schema['slot_Shape'])
        source.set(centerKey, afwGeom.Point2D(46, 124))
        source.set(shapeKey, afwGeom.Quadrupole(1.1, 2.2, 1))

        candidates = [measAlg.makePsfCandidate(source, self.exposure)]
        metadata = dafBase.PropertyList()

        with self.assertRaises(RuntimeError) as cm:
            psfDeterminer.determinePsf(self.exposure, candidates, metadata)
        self.assertEqual(str(cm.exception),
                         "All PSF candidates removed as blends")
예제 #20
0
def approximateWcs(wcs,
                   bbox,
                   order=3,
                   nx=20,
                   ny=20,
                   iterations=3,
                   skyTolerance=0.001 * afwGeom.arcseconds,
                   pixelTolerance=0.02,
                   useTanWcs=False):
    """Approximate an existing WCS as a TAN-SIP WCS

    The fit is performed by evaluating the WCS at a uniform grid of points within a bounding box.

    @param[in] wcs  wcs to approximate
    @param[in] bbox  the region over which the WCS will be fit
    @param[in] order  order of SIP fit
    @param[in] nx  number of grid points along x
    @param[in] ny  number of grid points along y
    @param[in] iterations number of times to iterate over fitting
    @param[in] skyTolerance maximum allowed difference in world coordinates between
               input wcs and approximate wcs (default is 0.001 arcsec)
    @param[in] pixelTolerance maximum allowed difference in pixel coordinates between
               input wcs and approximate wcs (default is 0.02 pixels)
    @param[in] useTanWcs  send a TAN version of wcs to the fitter? It is documented to require that,
        but I don't think the fitter actually cares
    @return the fit TAN-SIP WCS
    """
    if useTanWcs:
        crCoord = wcs.getSkyOrigin()
        crPix = wcs.getPixelOrigin()
        cdMat = wcs.getCDMatrix()
        tanWcs = afwImage.makeWcs(crCoord, crPix, cdMat[0, 0], cdMat[0, 1],
                                  cdMat[1, 0], cdMat[1, 1])
    else:
        tanWcs = wcs

    # create a matchList consisting of a grid of points covering the bbox
    refSchema = afwTable.SimpleTable.makeMinimalSchema()
    refCoordKey = afwTable.CoordKey(refSchema["coord"])
    refCat = afwTable.SimpleCatalog(refSchema)

    sourceSchema = afwTable.SourceTable.makeMinimalSchema()
    SingleFrameMeasurementTask(schema=sourceSchema)  # expand the schema
    sourceCentroidKey = afwTable.Point2DKey(sourceSchema["slot_Centroid"])

    sourceCat = afwTable.SourceCatalog(sourceSchema)

    matchList = []

    bboxd = afwGeom.Box2D(bbox)
    for x in np.linspace(bboxd.getMinX(), bboxd.getMaxX(), nx):
        for y in np.linspace(bboxd.getMinY(), bboxd.getMaxY(), ny):
            pixelPos = afwGeom.Point2D(x, y)
            skyCoord = wcs.pixelToSky(pixelPos)

            refObj = refCat.addNew()
            refObj.set(refCoordKey, skyCoord)

            source = sourceCat.addNew()
            source.set(sourceCentroidKey, pixelPos)

            matchList.append(afwTable.ReferenceMatch(refObj, source, 0.0))

    # The TAN-SIP fitter is fitting x and y separately, so we have to iterate to make it converge
    for indx in range(iterations):
        sipObject = makeCreateWcsWithSip(matchList, tanWcs, order, bbox)
        tanWcs = sipObject.getNewWcs()
    fitWcs = sipObject.getNewWcs()

    mockTest = _MockTestCase()
    assertWcsAlmostEqualOverBBox(mockTest,
                                 wcs,
                                 fitWcs,
                                 bbox,
                                 maxDiffSky=skyTolerance,
                                 maxDiffPix=pixelTolerance)

    return fitWcs
def approximateWcs(wcs,
                   camera_wrapper=None,
                   detector_name=None,
                   obs_metadata=None,
                   order=3,
                   nx=20,
                   ny=20,
                   iterations=3,
                   skyTolerance=0.001 * afwGeom.arcseconds,
                   pixelTolerance=0.02):
    """Approximate an existing WCS as a TAN-SIP WCS

    The fit is performed by evaluating the WCS at a uniform grid of points within a bounding box.

    @param[in] wcs  wcs to approximate
    @param[in] camera_wrapper is an instantiation of GalSimCameraWrapper
    @param[in] detector_name is the name of the detector
    @param[in] obs_metadata is an ObservationMetaData characterizing the telescope pointing
    @param[in] order  order of SIP fit
    @param[in] nx  number of grid points along x
    @param[in] ny  number of grid points along y
    @param[in] iterations number of times to iterate over fitting
    @param[in] skyTolerance maximum allowed difference in world coordinates between
               input wcs and approximate wcs (default is 0.001 arcsec)
    @param[in] pixelTolerance maximum allowed difference in pixel coordinates between
               input wcs and approximate wcs (default is 0.02 pixels)
    @return the fit TAN-SIP WCS
    """
    tanWcs = wcs

    # create a matchList consisting of a grid of points covering the bbox
    refSchema = afwTable.SimpleTable.makeMinimalSchema()
    refCoordKey = afwTable.CoordKey(refSchema["coord"])
    refCat = afwTable.SimpleCatalog(refSchema)

    sourceSchema = afwTable.SourceTable.makeMinimalSchema()
    SingleFrameMeasurementTask(schema=sourceSchema)  # expand the schema
    sourceCentroidKey = afwTable.Point2DKey(sourceSchema["slot_Centroid"])

    sourceCat = afwTable.SourceCatalog(sourceSchema)

    # 20 March 2017
    # the 'try' block is how it works in swig;
    # the 'except' block is how it works in pybind11
    try:
        matchList = afwTable.ReferenceMatchVector()
    except AttributeError:
        matchList = []

    bbox = camera_wrapper.getBBox(detector_name)
    bboxd = afwGeom.Box2D(bbox)

    for x in np.linspace(bboxd.getMinX(), bboxd.getMaxX(), nx):
        for y in np.linspace(bboxd.getMinY(), bboxd.getMaxY(), ny):
            pixelPos = afwGeom.Point2D(x, y)

            ra, dec = camera_wrapper.raDecFromPixelCoords(
                np.array([x]),
                np.array([y]),
                detector_name,
                obs_metadata=obs_metadata,
                epoch=2000.0,
                includeDistortion=True)

            skyCoord = afwGeom.SpherePoint(ra[0], dec[0], LsstGeom.degrees)

            refObj = refCat.addNew()
            refObj.set(refCoordKey, skyCoord)

            source = sourceCat.addNew()
            source.set(sourceCentroidKey, pixelPos)

            matchList.append(afwTable.ReferenceMatch(refObj, source, 0.0))

    # The TAN-SIP fitter is fitting x and y separately, so we have to iterate to make it converge
    for indx in range(iterations):
        sipObject = makeCreateWcsWithSip(matchList, tanWcs, order, bbox)
        tanWcs = sipObject.getNewWcs()
    fitWcs = sipObject.getNewWcs()

    return fitWcs
def approximateWcs(wcs,
                   bbox,
                   camera=None,
                   detector=None,
                   obs_metadata=None,
                   order=3,
                   nx=20,
                   ny=20,
                   iterations=3,
                   skyTolerance=0.001 * afwGeom.arcseconds,
                   pixelTolerance=0.02,
                   useTanWcs=False):
    """Approximate an existing WCS as a TAN-SIP WCS

    The fit is performed by evaluating the WCS at a uniform grid of points within a bounding box.

    @param[in] wcs  wcs to approximate
    @param[in] bbox  the region over which the WCS will be fit
    @param[in] camera is an instantiation of afw.cameraGeom.camera
    @param[in] detector is a detector from camera
    @param[in] obs_metadata is an ObservationMetaData characterizing the telescope pointing
    @param[in] order  order of SIP fit
    @param[in] nx  number of grid points along x
    @param[in] ny  number of grid points along y
    @param[in] iterations number of times to iterate over fitting
    @param[in] skyTolerance maximum allowed difference in world coordinates between
               input wcs and approximate wcs (default is 0.001 arcsec)
    @param[in] pixelTolerance maximum allowed difference in pixel coordinates between
               input wcs and approximate wcs (default is 0.02 pixels)
    @param[in] useTanWcs  send a TAN version of wcs to the fitter? It is documented to require that,
        but I don't think the fitter actually cares
    @return the fit TAN-SIP WCS
    """
    if useTanWcs:
        crCoord = wcs.getSkyOrigin()
        crPix = wcs.getPixelOrigin()
        cdMat = wcs.getCDMatrix()
        tanWcs = afwImage.makeWcs(crCoord, crPix, cdMat[0, 0], cdMat[0, 1],
                                  cdMat[1, 0], cdMat[1, 1])
    else:
        tanWcs = wcs

    # create a matchList consisting of a grid of points covering the bbox
    refSchema = afwTable.SimpleTable.makeMinimalSchema()
    refCoordKey = afwTable.CoordKey(refSchema["coord"])
    refCat = afwTable.SimpleCatalog(refSchema)

    sourceSchema = afwTable.SourceTable.makeMinimalSchema()
    SingleFrameMeasurementTask(schema=sourceSchema)  # expand the schema
    sourceCentroidKey = afwTable.Point2DKey(sourceSchema["slot_Centroid"])

    sourceCat = afwTable.SourceCatalog(sourceSchema)

    # 20 March 2017
    # the 'try' block is how it works in swig;
    # the 'except' block is how it works in pybind11
    try:
        matchList = afwTable.ReferenceMatchVector()
    except AttributeError:
        matchList = []

    bboxd = afwGeom.Box2D(bbox)
    for x in np.linspace(bboxd.getMinX(), bboxd.getMaxX(), nx):
        for y in np.linspace(bboxd.getMinY(), bboxd.getMaxY(), ny):
            pixelPos = afwGeom.Point2D(x, y)

            ra, dec = raDecFromPixelCoords(np.array([x]),
                                           np.array([y]), [detector.getName()],
                                           camera=camera,
                                           obs_metadata=obs_metadata,
                                           epoch=2000.0,
                                           includeDistortion=True)

            skyCoord = afwCoord.Coord(afwGeom.Point2D(ra[0], dec[0]))

            refObj = refCat.addNew()
            refObj.set(refCoordKey, skyCoord)

            source = sourceCat.addNew()
            source.set(sourceCentroidKey, pixelPos)

            matchList.append(afwTable.ReferenceMatch(refObj, source, 0.0))

    # The TAN-SIP fitter is fitting x and y separately, so we have to iterate to make it converge
    for indx in range(iterations):
        sipObject = makeCreateWcsWithSip(matchList, tanWcs, order, bbox)
        tanWcs = sipObject.getNewWcs()
    fitWcs = sipObject.getNewWcs()

    return fitWcs
예제 #23
0
    def singleTestInstance(self, filename, distortFunc, doPlot=False):
        sourceCat = self.loadSourceCatalog(self.filename)
        refCat = self.computePosRefCatalog(sourceCat)
        distortedCat = distort.distortList(sourceCat, distortFunc)

        if doPlot:
            import matplotlib.pyplot as plt

            undistorted = [
                self.wcs.skyToPixel(
                    self.distortedWcs.pixelToSky(ss.getCentroid()))
                for ss in distortedCat
            ]
            refs = [self.wcs.skyToPixel(ss.getCoord()) for ss in refCat]

            def plot(catalog, symbol):
                plt.plot([ss.getX() for ss in catalog],
                         [ss.getY() for ss in catalog], symbol)

            plot(distortedCat, 'b+')  # Distorted positions: blue +
            plot(undistorted, 'g+')  # Undistorted positions: green +
            plot(refs, 'rx')  # Reference catalog: red x
            # The green + should overlap with the red x, because that's how
            # MatchPessimisticB does it.

            plt.show()

        sourceCat = distortedCat

        matchRes = self.MatchPessimisticB.matchObjectsToSources(
            refCat=refCat,
            sourceCat=sourceCat,
            wcs=self.distortedWcs,
            refFluxField="r_flux",
        )
        matches = matchRes.matches
        if doPlot:
            measAstrom.plotAstrometry(matches=matches,
                                      refCat=refCat,
                                      sourceCat=sourceCat)
        self.assertEqual(len(matches), 183)

        refCoordKey = afwTable.CoordKey(refCat.schema["coord"])
        srcCoordKey = afwTable.CoordKey(sourceCat.schema["coord"])
        refCentroidKey = afwTable.Point2DKey(refCat.getSchema()["centroid"])
        maxDistErr = afwGeom.Angle(0)

        for refObj, source, distRad in matches:
            sourceCoord = source.get(srcCoordKey)
            refCoord = refObj.get(refCoordKey)
            predDist = sourceCoord.angularSeparation(refCoord)
            distErr = abs(predDist - distRad * afwGeom.radians)
            maxDistErr = max(distErr, maxDistErr)

            if refObj.getId() != source.getId():
                refCentroid = refObj.get(refCentroidKey)
                sourceCentroid = source.getCentroid()
                radius = math.hypot(*(refCentroid - sourceCentroid))
                self.fail(
                    "ID mismatch: %s at %s != %s at %s; error = %0.1f pix" %
                    (refObj.getId(), refCentroid, source.getId(),
                     sourceCentroid, radius))

        self.assertLess(maxDistErr.asArcseconds(), 1e-7)
예제 #24
0
    def singleTestInstance(self, filename, distortFunc, doPlot=False):
        sourceCat = self.loadSourceCatalog(self.filename)
        refCat = self.computePosRefCatalog(sourceCat)

        # Apply source selector to sourceCat, using the astrometry config defaults
        tempConfig = measAstrom.AstrometryTask.ConfigClass()
        tempConfig.matcher.retarget(measAstrom.MatchOptimisticBTask)
        tempConfig.sourceSelector["matcher"].excludePixelFlags = False
        tempSolver = measAstrom.AstrometryTask(config=tempConfig,
                                               refObjLoader=None)
        sourceSelection = tempSolver.sourceSelector.run(sourceCat)

        distortedCat = distort.distortList(sourceSelection.sourceCat,
                                           distortFunc)

        if doPlot:
            import matplotlib.pyplot as plt
            undistorted = [
                self.wcs.skyToPixel(
                    self.distortedWcs.pixelToSky(ss.getCentroid()))
                for ss in distortedCat
            ]
            refs = [self.wcs.skyToPixel(ss.getCoord()) for ss in refCat]

            def plot(catalog, symbol):
                plt.plot([ss.getX() for ss in catalog],
                         [ss.getY() for ss in catalog], symbol)

            # plot(sourceCat, 'k+') # Original positions: black +
            plot(distortedCat, 'b+')  # Distorted positions: blue +
            plot(undistorted, 'g+')  # Undistorted positions: green +
            plot(refs, 'rx')  # Reference catalog: red x
            # The green + should overlap with the red x, because that's how matchOptimisticB does it.
            # The black + happens to overlap with those also, but that's beside the point.
            plt.show()

        sourceCat = distortedCat

        matchRes = self.matchOptimisticB.matchObjectsToSources(
            refCat=refCat,
            sourceCat=sourceCat,
            wcs=self.distortedWcs,
            sourceFluxField='slot_ApFlux_instFlux',
            refFluxField="r_flux",
        )
        matches = matchRes.matches
        if doPlot:
            measAstrom.plotAstrometry(matches=matches,
                                      refCat=refCat,
                                      sourceCat=sourceCat)
        self.assertEqual(len(matches), 183)

        refCoordKey = afwTable.CoordKey(refCat.schema["coord"])
        srcCoordKey = afwTable.CoordKey(sourceCat.schema["coord"])
        refCentroidKey = afwTable.Point2DKey(refCat.getSchema()["centroid"])
        maxDistErr = 0 * lsst.geom.radians
        for refObj, source, distRad in matches:
            sourceCoord = source.get(srcCoordKey)
            refCoord = refObj.get(refCoordKey)
            predDist = sourceCoord.separation(refCoord)
            distErr = abs(predDist - distRad * lsst.geom.radians)
            maxDistErr = max(distErr, maxDistErr)

            if refObj.getId() != source.getId():
                refCentroid = refObj.get(refCentroidKey)
                sourceCentroid = source.getCentroid()
                radius = math.hypot(*(refCentroid - sourceCentroid))
                self.fail(
                    "ID mismatch: %s at %s != %s at %s; error = %0.1f pix" %
                    (refObj.getId(), refCentroid, source.getId(),
                     sourceCentroid, radius))

        self.assertLess(maxDistErr.asArcseconds(), 1e-7)