Exemple #1
0
 def test_Box2D_repr(self):
     from lsst.geom import Box2D, Point2D, Extent2D
     print(repr(Box2D()))
     self.assertEqual(eval(repr(Box2D())), Box2D())
     self.assertEqual(
         eval(repr(Box2D(Point2D(1.0, 2.0), Extent2D(3.0, 4.0)))),
         Box2D(Point2D(1.0, 2.0), Extent2D(3.0, 4.0)))
Exemple #2
0
 def setUp(self):
     # Test geometry:
     #
     # -100,99                99,99
     #     +--------------------+
     #     |AAAAAAAAAACCCCCDDDDD|    A == only in epoch A
     #     |AAAAAAAAAACCCCCDDDDD|    B == only in epoch B
     #     |AAAAAAAAAACCCCCDDDDD|    C == in both epoch A and epoch B
     #     |AAAAAAAAAACCCCCDDDDD|    D == in epoch A; in B's bbox but outside its ValidPolygon
     #     |AAAAAAAAAACCCCCDDDDD|
     #     |          BBBBBBBBBB|    All WCSs have the same CRVAL and CD.
     #     |          BBBBBBBBBB|
     #     |          BBBBBBBBBB|    Coadd has CRPIX=(0, 0)
     #     |          BBBBBBBBBB|    Epoch A has CRPIX=(0, -50)
     #     |          BBBBBBBBBB|    Epoch B has CRPIX=(-50, 0)
     #     +--------------------+
     # -100,-100             99,-100
     #
     self.rng = np.random.RandomState(50)
     crval = SpherePoint(45.0, 45.0, degrees)
     cdMatrix = makeCdMatrix(scale=5E-5 * degrees, flipX=True)
     self.wcsCoadd = makeSkyWcs(crpix=Point2D(0.0, 0.0),
                                crval=crval,
                                cdMatrix=cdMatrix)
     self.wcsA = makeSkyWcs(crpix=Point2D(0.0, -50.0),
                            crval=crval,
                            cdMatrix=cdMatrix)
     self.wcsB = makeSkyWcs(crpix=Point2D(-50.0, 0.0),
                            crval=crval,
                            cdMatrix=cdMatrix)
     self.bboxCoadd = Box2I(Point2I(-100, -100), Point2I(99, 99))
     self.bboxA = Box2I(Point2I(-100, -50), Point2I(99, 49))
     self.bboxB = Box2I(Point2I(-50, -100), Point2I(49, 99))
     self.polygonA = None
     polygonD = Polygon(Box2D(Box2I(Point2I(0, 0), Point2I(49, 99))))
     self.polygonB, = polygonD.symDifference(Polygon(Box2D(self.bboxB)))
     self.curveA = makeRandomTransmissionCurve(self.rng)
     self.curveB = makeRandomTransmissionCurve(self.rng)
     self.weightA = 0.6
     self.weightB = 0.2
     schema = ExposureTable.makeMinimalSchema()
     weightKey = schema.addField("weight",
                                 type=float,
                                 doc="relative weight of image in Coadd")
     catalog = ExposureCatalog(schema)
     recordA = catalog.addNew()
     recordA[weightKey] = self.weightA
     recordA.setWcs(self.wcsA)
     recordA.setValidPolygon(self.polygonA)
     recordA.setBBox(self.bboxA)
     recordA.setTransmissionCurve(self.curveA)
     recordB = catalog.addNew()
     recordB[weightKey] = self.weightB
     recordB.setWcs(self.wcsB)
     recordB.setValidPolygon(self.polygonB)
     recordB.setBBox(self.bboxB)
     recordB.setTransmissionCurve(self.curveB)
     self.curveCoadd = makeCoaddTransmissionCurve(self.wcsCoadd, catalog)
Exemple #3
0
def generateSummaryStats(cat, colName, skymap, plotInfo):
    """Generate a summary statistic in each patch

    Parameters
    ----------
    cat : `pandas.core.frame.DataFrame`
    colName : `str`
    skymap : `lsst.skymap.ringsSkyMap.RingsSkyMap`
    plotInfo : `dict`

    Returns
    -------
    patchInfoDict : `dict`
    """

    # TODO: what is the more generic type of skymap?
    tractInfo = skymap.generateTract(plotInfo["tract"])
    tractWcs = tractInfo.getWcs()

    # For now also convert the gen 2 patchIds to gen 3

    patchInfoDict = {}
    for patch in cat.patchId.unique():
        if patch is None:
            continue
        # Once the objectTable_tract catalogues are using gen 3 patches
        # this will go away
        onPatch = (cat["patchId"] == patch)
        stat = np.nanmedian(cat[colName].values[onPatch])
        try:
            patchTuple = (int(patch.split(",")[0]), int(patch.split(",")[-1]))
            patchInfo = tractInfo.getPatchInfo(patchTuple)
            gen3PatchId = tractInfo.getSequentialPatchIndex(patchInfo)
        except AttributeError:
            # For native gen 3 tables the patches don't need converting
            # When we are no longer looking at the gen 2 -> gen 3
            # converted repos we can tidy this up
            gen3PatchId = patch
            patchInfo = tractInfo.getPatchInfo(patch)

        corners = Box2D(patchInfo.getInnerBBox()).getCorners()
        skyCoords = tractWcs.pixelToSky(corners)

        patchInfoDict[gen3PatchId] = (skyCoords, stat)

    tractCorners = Box2D(tractInfo.getBBox()).getCorners()
    skyCoords = tractWcs.pixelToSky(tractCorners)
    patchInfoDict["tract"] = (skyCoords, np.nan)

    return patchInfoDict
Exemple #4
0
 def testMultiPlaneFitsReaders(self):
     """Run tests for MaskedImageFitsReader and ExposureFitsReader.
     """
     metadata = PropertyList()
     metadata.add("FIVE", 5)
     metadata.add("SIX", 6.0)
     wcs = makeSkyWcs(Point2D(2.5, 3.75),
                      SpherePoint(40.0 * degrees, 50.0 * degrees),
                      np.array([[1E-5, 0.0], [0.0, -1E-5]]))
     defineFilter("test_readers_filter", lambdaEff=470.0)
     calib = PhotoCalib(2.5E4)
     psf = GaussianPsf(21, 21, 8.0)
     polygon = Polygon(Box2D(self.bbox))
     apCorrMap = ApCorrMap()
     visitInfo = VisitInfo(exposureTime=5.0)
     transmissionCurve = TransmissionCurve.makeIdentity()
     coaddInputs = CoaddInputs(ExposureTable.makeMinimalSchema(),
                               ExposureTable.makeMinimalSchema())
     detector = DetectorWrapper().detector
     record = coaddInputs.ccds.addNew()
     record.setWcs(wcs)
     record.setPhotoCalib(calib)
     record.setPsf(psf)
     record.setValidPolygon(polygon)
     record.setApCorrMap(apCorrMap)
     record.setVisitInfo(visitInfo)
     record.setTransmissionCurve(transmissionCurve)
     record.setDetector(detector)
     for n, dtypeIn in enumerate(self.dtypes):
         with self.subTest(dtypeIn=dtypeIn):
             exposureIn = Exposure(self.bbox, dtype=dtypeIn)
             shape = exposureIn.image.array.shape
             exposureIn.image.array[:, :] = np.random.randint(low=1,
                                                              high=5,
                                                              size=shape)
             exposureIn.mask.array[:, :] = np.random.randint(low=1,
                                                             high=5,
                                                             size=shape)
             exposureIn.variance.array[:, :] = np.random.randint(low=1,
                                                                 high=5,
                                                                 size=shape)
             exposureIn.setMetadata(metadata)
             exposureIn.setWcs(wcs)
             exposureIn.setFilter(Filter("test_readers_filter"))
             exposureIn.setFilterLabel(
                 FilterLabel(physical="test_readers_filter"))
             exposureIn.setPhotoCalib(calib)
             exposureIn.setPsf(psf)
             exposureIn.getInfo().setValidPolygon(polygon)
             exposureIn.getInfo().setApCorrMap(apCorrMap)
             exposureIn.getInfo().setVisitInfo(visitInfo)
             exposureIn.getInfo().setTransmissionCurve(transmissionCurve)
             exposureIn.getInfo().setCoaddInputs(coaddInputs)
             exposureIn.setDetector(detector)
             with lsst.utils.tests.getTempFilePath(".fits") as fileName:
                 exposureIn.writeFits(fileName)
                 self.checkMaskedImageFitsReader(exposureIn, fileName,
                                                 self.dtypes[n:])
                 self.checkExposureFitsReader(exposureIn, fileName,
                                              self.dtypes[n:])
Exemple #5
0
    def trimFakeCat(self, fakeCat, image, wcs):
        """Trim the fake cat to about the size of the input image.

        Parameters
        ----------
        fakeCat : `pandas.core.frame.DataFrame`
                    The catalog of fake sources to be input
        image : `lsst.afw.image.exposure.exposure.ExposureF`
                    The image into which the fake sources should be added
        wcs : `lsst.afw.geom.skyWcs.skyWcs.SkyWcs`
                    WCS to use to add fake sources

        Returns
        -------
        fakeCat : `pandas.core.frame.DataFrame`
                    The original fakeCat trimmed to the area of the image
        """

        bbox = Box2D(image.getBBox())
        corners = bbox.getCorners()

        skyCorners = wcs.pixelToSky(corners)
        region = ConvexPolygon([s.getVector() for s in skyCorners])

        def trim(row):
            coord = SpherePoint(row[self.config.raColName],
                                row[self.config.decColName], radians)
            return region.contains(coord.getVector())

        return fakeCat[fakeCat.apply(trim, axis=1)]
Exemple #6
0
    def trimFakeCat(self, fakeCat, image, wcs):
        """Trim the fake cat to about the size of the input image.

        `fakeCat` must be processed with addPixCoords before using this method.

        Parameters
        ----------
        fakeCat : `pandas.core.frame.DataFrame`
                    The catalog of fake sources to be input
        image : `lsst.afw.image.exposure.exposure.ExposureF`
                    The image into which the fake sources should be added
        wcs : `lsst.afw.geom.SkyWcs`
                    WCS to use to add fake sources

        Returns
        -------
        fakeCat : `pandas.core.frame.DataFrame`
                    The original fakeCat trimmed to the area of the image
        """

        bbox = Box2D(image.getBBox())

        def trim(row):
            return bbox.contains(row["x"], row["y"])

        return fakeCat[fakeCat.apply(trim, axis=1)]
Exemple #7
0
    def _trimFakeCat(self, fakeCat, image):
        """Trim the fake cat to about the size of the input image.

        Parameters
        ----------
        fakeCat : `pandas.core.frame.DataFrame`
            The catalog of fake sources to be input
        image : `lsst.afw.image.exposure.exposure.ExposureF`
            The image into which the fake sources should be added

        Returns
        -------
        fakeCat : `pandas.core.frame.DataFrame`
            The original fakeCat trimmed to the area of the image
        """
        wcs = image.getWcs()

        bbox = Box2D(image.getBBox())

        def trim(row):
            coord = SpherePoint(row[self.config.raColName],
                                row[self.config.decColName], radians)
            cent = wcs.skyToPixel(coord)
            return bbox.contains(cent)

        return fakeCat[fakeCat.apply(trim, axis=1)]
Exemple #8
0
    def _calculate_region_from_dataset_metadata(self, obsInfo, header, FormatterClass):
        """Calculate the sky region covered by the supplied observation
        information.

        Parameters
        ----------
        obsInfo : `~astro_metadata_translator.ObservationInfo`
            Summary information of this dataset.
        header : `Mapping`
            Header from the dataset.
        FormatterClass: `type` as subclass of  `FitsRawFormatterBase`
            Formatter class that should be used to compute the spatial region.

        Returns
        -------
        region : `lsst.sphgeom.ConvexPolygon`
            Region of sky covered by this observation.
        """
        if obsInfo.visit_id is not None and obsInfo.tracking_radec is not None:
            formatter = FormatterClass.fromMetadata(metadata=header, obsInfo=obsInfo)
            visitInfo = formatter.makeVisitInfo()
            detector = self.camera[obsInfo.detector_num]
            wcs = formatter.makeWcs(visitInfo, detector)
            pixBox = Box2D(detector.getBBox())
            if self.config.padRegionAmount > 0:
                pixBox.grow(self.config.padRegionAmount)
            pixCorners = pixBox.getCorners()
            sphCorners = [wcs.pixelToSky(point).getVector() for point in pixCorners]
            region = ConvexPolygon(sphCorners)
        else:
            region = None
        return region
Exemple #9
0
    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 testBasics(self):
        """Test construction of a discrete sky map
        """
        butler = Butler(inputs=self.inPath,
                        outputs={
                            'root': self.outPath,
                            'mode': 'rw'
                        })
        coordList = []  # list of sky coords of all corners of all calexp
        for dataId in (
                dict(visit=1, filter="g"),
                dict(visit=2, filter="g"),
                dict(visit=3, filter="r"),
        ):
            rawImage = butler.get("raw", dataId)
            # fake calexp by simply copying raw data; the task just cares about its bounding box
            # (which is slightly larger for raw, but that doesn't matter for this test)
            calexp = rawImage
            butler.put(calexp, "calexp", dataId)
            calexpWcs = calexp.getWcs()
            calexpBoxD = Box2D(calexp.getBBox())
            coordList += [
                calexpWcs.pixelToSky(corner)
                for corner in calexpBoxD.getCorners()
            ]

        # use the calexp to make a sky map
        retVal = MakeDiscreteSkyMapTask.parseAndRun(
            args=[self.inPath, "--output", self.outPath, "--id", "filter=g^r"],
            config=self.config,
            doReturnResults=True,
        )
        self.assertEqual(len(retVal.resultList), 1)
        skyMap = retVal.resultList[0].result.skyMap
        self.assertEqual(type(skyMap), DiscreteSkyMap)
        self.assertEqual(len(skyMap), 1)
        tractInfo = skyMap[0]
        self.assertEqual(tractInfo.getId(), 0)
        self.assertEqual(tractInfo.getNumPatches(), Extent2I(3, 3))
        tractWcs = tractInfo.getWcs()
        tractBoxD = Box2D(tractInfo.getBBox())
        for skyPoint in coordList:
            self.assertTrue(tractBoxD.contains(tractWcs.skyToPixel(skyPoint)))
def extractCtorArgs(md):
    wcs = makeSkyWcs(makePropertyListFromDict(md))
    kwds = {
        "pixelToIwc": getPixelToIntermediateWorldCoords(wcs),
        "bbox":
        Box2D(Box2I(Point2I(0, 0), Extent2I(md["NAXES1"], md["NAXES2"]))),
        "crpix":
        Point2D(md["CRPIX1"] - 1.0,
                md["CRPIX2"] - 1.0),  # -1 for LSST vs. FITS conventions
        "cd": np.array([[md["CD1_1"], md["CD1_2"]], [md["CD2_1"],
                                                     md["CD2_2"]]]),
    }
    return kwds
 def insertObservationRegions(self, registry, datastore):
     """Add spatial regions for visit-detector combinations.
     """
     sql = (
         "SELECT wcs.instrument AS instrument, wcs.visit AS visit, wcs.detector AS detector, "
         "        wcs.dataset_id AS wcs, metadata.dataset_id AS metadata "
         "    FROM dataset wcs "
         "        INNER JOIN dataset_collection wcs_collection "
         "            ON (wcs.dataset_id = wcs_collection.dataset_id) "
         "        INNER JOIN dataset metadata "
         "            ON (wcs.instrument = metadata.instrument "
         "                AND wcs.visit = metadata.visit "
         "                AND wcs.detector = metadata.detector) "
         "        INNER JOIN dataset_collection metadata_collection "
         "            ON (metadata.dataset_id = metadata_collection.dataset_id) "
         "    WHERE wcs_collection.collection = :collection "
         "          AND metadata_collection.collection = :collection "
         "          AND wcs.dataset_type_name = :wcs_name"
         "          AND metadata.dataset_type_name = :metadata_name")
     log = Log.getLogger("lsst.daf.butler.gen2convert")
     for config in self.config["regions"]:
         log.info("Adding observation regions using %s from %s.",
                  config["DatasetType"], config["collection"])
         visits = {}
         for row in registry.query(
                 sql,
                 collection=config["collection"],
                 wcs_name="{}.wcs".format(config["DatasetType"]),
                 metadata_name="{}.metadata".format(config["DatasetType"])):
             wcsRef = registry.getDataset(row["wcs"])
             metadataRef = registry.getDataset(row["metadata"])
             wcs = datastore.get(wcsRef)
             metadata = datastore.get(metadataRef)
             bbox = Box2D(bboxFromMetadata(metadata))
             bbox.grow(config["padding"])
             region = ConvexPolygon([
                 sp.getVector() for sp in wcs.pixelToSky(bbox.getCorners())
             ])
             registry.setDimensionRegion(
                 {k: row[k]
                  for k in ("instrument", "visit", "detector")},
                 region=region,
                 update=False)
             visits.setdefault((row["instrument"], row["visit"]),
                               []).extend(region.getVertices())
         for (instrument, visit), vertices in visits.items():
             region = ConvexPolygon(vertices)
             registry.setDimensionRegion(instrument=instrument,
                                         visit=visit,
                                         region=region)
Exemple #13
0
def getPatchInner(sources, patchInfo):
    """Set a flag for each source if it is in the innerBBox of a patch.

    Parameters
    ----------
    sources : `lsst.afw.table.SourceCatalog`
        A sourceCatalog with pre-calculated centroids.
    patchInfo : `lsst.skymap.PatchInfo`
        Information about a `SkyMap` `Patch`.

    Returns
    --------
    isPatchInner : array-like of `bool`
        `True` for each source that has a centroid
        in the inner region of a patch.
    """
    # Extract the centroid position for all the sources
    x = sources["slot_Centroid_x"]
    y = sources["slot_Centroid_y"]
    centroidFlag = sources["slot_Centroid_flag"]

    # set inner flags for each source and set primary flags for
    # sources with no children (or all sources if deblend info not available)
    innerFloatBBox = Box2D(patchInfo.getInnerBBox())
    inInner = innerFloatBBox.contains(x, y)

    # When the centroider fails, we can still fall back to the peak,
    # but we don't trust that quite as much -
    # so we use a slightly smaller box for the patch comparison.
    shrunkInnerFloatBBox = Box2D(innerFloatBBox)
    shrunkInnerFloatBBox.grow(-1)
    inShrunkInner = shrunkInnerFloatBBox.contains(x, y)

    # Flag sources contained in the inner region of a patch
    isPatchInner = (centroidFlag & inShrunkInner) | (~centroidFlag & inInner)
    return isPatchInner
Exemple #14
0
def calculateSipWcsHeader(wcs, order, bbox, spacing, header=None):
    """Generate a SIP WCS header approximating a given ``SkyWcs``

    Parameters
    ----------
    wcs : `lsst.afw.geom.SkyWcs`
        World Coordinate System to approximate as SIP.
    order : `int`
        SIP order (equal to the maximum sum of the polynomial exponents).
    bbox : `lsst.geom.Box2I`
        Bounding box over which to approximate the ``wcs``.
    spacing : `float`
        Spacing between sample points.
    header : `lsst.daf.base.PropertyList`, optional
        Header to which to add SIP WCS keywords.

    Returns
    -------
    header : `lsst.daf.base.PropertyList`
        Header including SIP WCS keywords.

    Examples
    --------
    >>> header = calculateSipWcsHeader(exposure.getWcs(), 3, exposure.getBBox(), 20)
    >>> sipWcs = SkyWcs(header)
    """
    transform = getPixelToIntermediateWorldCoords(wcs)
    crpix = wcs.getPixelOrigin()
    cdMatrix = wcs.getCdMatrix()
    crval = wcs.getSkyOrigin()
    gridNum = Extent2I(int(bbox.getWidth() / spacing + 0.5),
                       int(bbox.getHeight() / spacing + 0.5))

    sip = SipApproximation(transform, crpix, cdMatrix, Box2D(bbox), gridNum,
                           order)

    md = makeTanSipMetadata(sip.getPixelOrigin(), crval, sip.getCdMatrix(),
                            sip.getA(), sip.getB(), sip.getAP(), sip.getBP())

    if header is not None:
        header.combine(md)
    else:
        header = md

    return header
Exemple #15
0
def makeSkyPolygonFromBBox(bbox, wcs):
    """Make an on-sky polygon from a bbox and a SkyWcs

    Parameters
    ----------
    bbox : `lsst.geom.Box2I` or `lsst.geom.Box2D`
        Bounding box of region, in pixel coordinates
    wcs : `lsst.afw.geom.SkyWcs`
        Celestial WCS

    Returns
    -------
    polygon : `lsst.sphgeom.ConvexPolygon`
        On-sky region
    """
    pixelPoints = Box2D(bbox).getCorners()
    skyPoints = wcs.pixelToSky(pixelPoints)
    return ConvexPolygon.convexHull([sp.getVector() for sp in skyPoints])
Exemple #16
0
 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())
Exemple #17
0
    def _trimFakeCat(self, fakeCat, image):
        """Trim the fake cat to the exact size of the input image.

        Parameters
        ----------
        fakeCat : `pandas.core.frame.DataFrame`
            The catalog of fake sources that was input
        image : `lsst.afw.image.exposure.exposure.ExposureF`
            The image into which the fake sources were added
        Returns
        -------
        fakeCat : `pandas.core.frame.DataFrame`
            The original fakeCat trimmed to the area of the image
        """

        # fakeCat must be processed with _addPixCoords before trimming
        if ('x' not in fakeCat.columns) or ('y' not in fakeCat.columns):
            fakeCat = self._addPixCoords(fakeCat, image)

        # Prefilter in ra/dec to avoid cases where the wcs incorrectly maps
        # input fakes which are really off the chip onto it.
        ras = fakeCat[self.config.ra_col].values * u.rad
        decs = fakeCat[self.config.dec_col].values * u.rad

        isContainedRaDec = image.containsSkyCoords(ras, decs, padding=0)

        # now use the exact pixel BBox to filter to only fakes that were inserted
        xs = fakeCat["x"].values
        ys = fakeCat["y"].values

        bbox = Box2D(image.getBBox())
        isContainedXy = xs >= bbox.minX
        isContainedXy &= xs <= bbox.maxX
        isContainedXy &= ys >= bbox.minY
        isContainedXy &= ys <= bbox.maxY

        return fakeCat[isContainedRaDec & isContainedXy]
Exemple #18
0
    def run(self,
            sources,
            skyMap=None,
            tractInfo=None,
            patchInfo=None,
            includeDeblend=True):
        """Set is-patch-inner, is-tract-inner and is-primary flags on sources.
        For coadded imaging, the is-primary flag returns True when an object
        has no children, is in the inner region of a coadd patch, is in the
        inner region of a coadd trach, and is not detected in a pseudo-filter
        (e.g., a sky_object).
        For single frame imaging, the is-primary flag returns True when a
        source has no children and is not a sky source.

        Parameters
        ----------
        sources : `lsst.afw.table.SourceCatalog`
            A sourceTable. Reads in centroid fields and an nChild field.
            Writes is-patch-inner, is-tract-inner, and is-primary flags.
        skyMap : `lsst.skymap.BaseSkyMap`
            Sky tessellation object
        tractInfo : `lsst.skymap.TractInfo`
            Tract object
        patchInfo : `lsst.skymap.PatchInfo`
            Patch object
        includeDeblend : `bool`
            Include deblend information in isPrimary?
        """
        nChildKey = None
        if includeDeblend:
            nChildKey = self.schema.find(self.config.nChildKeyName).key

        # coadd case
        if not self.isSingleFrame:
            # set inner flags for each source and set primary flags for sources with no children
            # (or all sources if deblend info not available)
            innerFloatBBox = Box2D(patchInfo.getInnerBBox())

            # When the centroider fails, we can still fall back to the peak, but we don't trust
            # that quite as much - so we use a slightly smaller box for the patch comparison.
            # That's trickier for the tract comparison, so we just use the peak without extra
            # care there.
            shrunkInnerFloatBBox = Box2D(innerFloatBBox)
            shrunkInnerFloatBBox.grow(-1)

            pseudoFilterKeys = []
            for filt in self.config.pseudoFilterList:
                try:
                    pseudoFilterKeys.append(
                        self.schema.find("merge_peak_%s" % filt).getKey())
                except Exception:
                    self.log.warn(
                        "merge_peak is not set for pseudo-filter %s" % filt)

            tractId = tractInfo.getId()
            for source in sources:
                centroidPos = source.getCentroid()
                if numpy.any(numpy.isnan(centroidPos)):
                    continue
                if source.getCentroidFlag():
                    # Use a slightly smaller box to guard against bad centroids (see above)
                    isPatchInner = shrunkInnerFloatBBox.contains(centroidPos)
                else:
                    isPatchInner = innerFloatBBox.contains(centroidPos)
                source.setFlag(self.isPatchInnerKey, isPatchInner)

                skyPos = source.getCoord()
                sourceInnerTractId = skyMap.findTract(skyPos).getId()
                isTractInner = sourceInnerTractId == tractId
                source.setFlag(self.isTractInnerKey, isTractInner)

                if nChildKey is None or source.get(nChildKey) == 0:
                    for pseudoFilterKey in pseudoFilterKeys:
                        if source.get(pseudoFilterKey):
                            isPseudo = True
                            break
                    else:
                        isPseudo = False

                    source.setFlag(
                        self.isPrimaryKey, isPatchInner and isTractInner
                        and not isPseudo)

        # single frame case
        else:
            hasSkySources = True if "sky_source" in sources.schema else False
            for source in sources:
                hasNoChildren = True if nChildKey is None or source.get(
                    nChildKey) == 0 else False
                isSkySource = False
                if hasSkySources:
                    if source["sky_source"]:
                        isSkySource = True
                source.setFlag(self.isPrimaryKey, hasNoChildren
                               and not isSkySource)
    def run(self, sources, skyMap, tractInfo, patchInfo, includeDeblend=True):
        """Set is-primary and related flags on sources

        @param[in,out] sources   a SourceTable
            - reads centroid fields and an nChild field
            - writes is-patch-inner, is-tract-inner and is-primary flags
        @param[in] skyMap   sky tessellation object (subclass of lsst.skymap.BaseSkyMap)
        @param[in] tractInfo   tract object (subclass of lsst.skymap.TractInfo)
        @param[in] patchInfo   patch object (subclass of lsst.skymap.PatchInfo)
        @param[in] includeDeblend   include deblend information in isPrimary?
        """
        nChildKey = None
        if includeDeblend:
            nChildKey = self.schema.find(self.config.nChildKeyName).key

        # set inner flags for each source and set primary flags for sources with no children
        # (or all sources if deblend info not available)
        innerFloatBBox = Box2D(patchInfo.getInnerBBox())

        # When the centroider fails, we can still fall back to the peak, but we don't trust
        # that quite as much - so we use a slightly smaller box for the patch comparison.
        # That's trickier for the tract comparison, so we just use the peak without extra
        # care there.
        shrunkInnerFloatBBox = Box2D(innerFloatBBox)
        shrunkInnerFloatBBox.grow(-1)

        pseudoFilterKeys = []
        for filt in self.config.pseudoFilterList:
            try:
                pseudoFilterKeys.append(
                    self.schema.find("merge_peak_%s" % filt).getKey())
            except Exception:
                self.log.warn("merge_peak is not set for pseudo-filter %s" %
                              filt)

        tractId = tractInfo.getId()
        for source in sources:
            centroidPos = source.getCentroid()
            if numpy.any(numpy.isnan(centroidPos)):
                continue
            if source.getCentroidFlag():
                # Use a slightly smaller box to guard against bad centroids (see above)
                isPatchInner = shrunkInnerFloatBBox.contains(centroidPos)
            else:
                isPatchInner = innerFloatBBox.contains(centroidPos)
            source.setFlag(self.isPatchInnerKey, isPatchInner)

            skyPos = source.getCoord()
            sourceInnerTractId = skyMap.findTract(skyPos).getId()
            isTractInner = sourceInnerTractId == tractId
            source.setFlag(self.isTractInnerKey, isTractInner)

            if nChildKey is None or source.get(nChildKey) == 0:
                for pseudoFilterKey in pseudoFilterKeys:
                    if source.get(pseudoFilterKey):
                        isPseudo = True
                        break
                else:
                    isPseudo = False

                source.setFlag(self.isPrimaryKey, isPatchInner and isTractInner
                               and not isPseudo)
Exemple #20
0
    def testSetPupilFieldAngleZero(self):
        """Test setPupilFieldAngle for zero field angle and various points on the pupil
        """
        for pupilPos in ((0, 5000), (-5000, 0), (5000, -5000)):
            with self.subTest(pupilPos=pupilPos):
                self.cco.setPupilFieldAngle(pupilPos=pupilPos)

                # the telescope should be pointing in the opposite direction of the CBP
                telDir = self.cco.telAzAltInternal.getVector()
                cbpDir = self.cco.cbpAzAltInternal.getVector()
                negativeCbpDir = -np.array(cbpDir, dtype=float)
                np.testing.assert_allclose(telDir, negativeCbpDir, atol=1e-15)

                # beam 0 should be pointed to the center of the pupil, normal to the pupil
                # and land on the center of the focal plane, which is also the center of detector D0
                beamInfo0 = self.cco[0]
                self.assertEqual(beamInfo0.name, "beam0")
                self.assertPairsAlmostEqual(beamInfo0.holePos, (0, 0))
                self.assertTrue(beamInfo0.isOnPupil)
                self.assertTrue(beamInfo0.isOnFocalPlane)
                self.assertTrue(beamInfo0.isOnDetector)
                self.assertTrue(beamInfo0.isVisible)
                self.assertPairsAlmostEqual(beamInfo0.focalPlanePos, (0, 0), maxDiff=self.maxFocalPlanePosErr)
                self.assertPairsAlmostEqual(beamInfo0.focalFieldAngle, (0, 0),
                                            maxDiff=self.maxFieldAngleErrRad)
                self.assertPairsAlmostEqual(beamInfo0.pupilFieldAngle, (0, 0),
                                            maxDiff=self.maxFieldAngleErrRad)
                self.assertPairsAlmostEqual(beamInfo0.pupilPos, pupilPos, maxDiff=self.maxPupilPosErr)
                self.assertEqual(beamInfo0.detectorName, "D0")
                bboxd = Box2D(self.cco.cameraGeom["D0"].getBBox())
                detectorCtrPos = bboxd.getCenter()
                detector34Pos = bboxd.getMin() + bboxd.getDimensions()*0.75
                self.assertPairsAlmostEqual(beamInfo0.detectorPos, detectorCtrPos,
                                            maxDiff=self.maxDetectorPosErr)

                # beam 1 should land on detector D0, 3/4 of the way from LL to UR
                beamInfo1 = self.cco["beam1"]
                self.assertEqual(beamInfo1.name, "beam1")
                self.assertTrue(beamInfo1.isOnDetector)
                self.assertEqual(beamInfo1.detectorName, "D0")
                self.assertPairsAlmostEqual(beamInfo1.detectorPos, detector34Pos,
                                            maxDiff=self.maxDetectorPosErr)

                # beam 2 should land on the center of detector D1
                beamInfo2 = self.cco["beam2"]
                self.assertEqual(beamInfo2.name, "beam2")
                self.assertTrue(beamInfo2.isOnDetector)
                self.assertEqual(beamInfo2.detectorName, "D1")
                self.assertPairsAlmostEqual(beamInfo2.detectorPos, detectorCtrPos,
                                            maxDiff=self.maxDetectorPosErr)

                # beam 3 should land on detector D1, 3/4 of the way from LL to UR
                beamInfo3 = self.cco["beam3"]
                self.assertEqual(beamInfo3.name, "beam3")
                self.assertTrue(beamInfo3.isOnDetector)
                self.assertEqual(beamInfo3.detectorName, "D1")
                self.assertPairsAlmostEqual(beamInfo3.detectorPos, detector34Pos,
                                            maxDiff=self.maxDetectorPosErr)

                # beam 4 should land on the center of detector D2
                beamInfo4 = self.cco["beam4"]
                self.assertEqual(beamInfo4.name, "beam4")
                self.assertTrue(beamInfo4.isOnDetector)
                self.assertEqual(beamInfo4.detectorName, "D2")
                self.assertPairsAlmostEqual(beamInfo4.detectorPos, detectorCtrPos,
                                            maxDiff=self.maxDetectorPosErr)

                # beam 5 should land on detector D2, 3/4 of the way from LL to UR
                beamInfo5 = self.cco["beam5"]
                self.assertEqual(beamInfo5.name, "beam5")
                self.assertTrue(beamInfo5.isOnDetector)
                self.assertEqual(beamInfo5.detectorName, "D2")
                self.assertPairsAlmostEqual(beamInfo5.detectorPos, detector34Pos,
                                            maxDiff=self.maxDetectorPosErr)
Exemple #21
0
    def testSetPupilFieldAngleTrivial(self):
        """Test setPupilFieldAngle for the trivial case of hole 0
        aimed perpendicular to the center of the pupil
        """
        self.cco.setPupilFieldAngle(pupilPos=(0, 0))

        # the telescope should be pointed at the center of the CBP and vice-versa
        # NOTE: It would be nice to get better than the 0.0028" that I measure
        self.assertSpherePointsAlmostEqual(self.cco.telAzAltInternal,
                                           SpherePoint(Vector3d(*self.cco.config.cbpPosition)),
                                           maxSep=0.01*arcseconds)
        self.assertSpherePointsAlmostEqual(self.cco.cbpAzAltInternal,
                                           SpherePoint(Vector3d(*(-self.cco.config.cbpPosition))),
                                           maxSep=0.01*arcseconds)

        self.assertAnglesAlmostEqual(self.cco.telRotInternal, 0*degrees)

        # beam 0 should be pointed to the center of the pupil, normal to the pupil
        # and land on the center of the focal plane and the center of detector D0
        beamInfo0 = self.cco[0]
        self.assertEqual(beamInfo0.name, "beam0")
        self.assertPairsAlmostEqual(beamInfo0.holePos, (0, 0))
        self.assertFalse(beamInfo0.isOnPupil)  # blocked by the central obscuration
        self.assertTrue(beamInfo0.isOnFocalPlane)
        self.assertTrue(beamInfo0.isOnDetector)
        self.assertFalse(beamInfo0.isVisible)  # blocked by the central obscuration
        self.assertPairsAlmostEqual(beamInfo0.focalPlanePos, (0, 0), maxDiff=self.maxFocalPlanePosErr)
        self.assertPairsAlmostEqual(beamInfo0.focalFieldAngle, (0, 0), maxDiff=self.maxFieldAngleErrRad)
        self.assertPairsAlmostEqual(beamInfo0.pupilFieldAngle, (0, 0), maxDiff=self.maxFieldAngleErrRad)
        self.assertPairsAlmostEqual(beamInfo0.pupilPos, (0, 0), maxDiff=self.maxPupilPosErr)
        self.assertEqual(beamInfo0.detectorName, "D0")
        bboxd = Box2D(self.cco.cameraGeom["D0"].getBBox())
        detectorCtrPos = bboxd.getCenter()
        detector34Pos = bboxd.getMin() + bboxd.getDimensions()*0.75
        self.assertPairsAlmostEqual(beamInfo0.detectorPos, detectorCtrPos, maxDiff=self.maxDetectorPosErr)

        # beam 1 should land on detector D0, 3/4 of the way from LL to UR
        beamInfo1 = self.cco[1]
        self.assertEqual(beamInfo1.name, "beam1")
        self.assertTrue(beamInfo1.isOnDetector)
        self.assertEqual(beamInfo1.detectorName, "D0")
        self.assertPairsAlmostEqual(beamInfo1.detectorPos, detector34Pos, maxDiff=self.maxDetectorPosErr)

        # beam 2 should land on the center of detector D1
        beamInfo2 = self.cco[2]
        self.assertEqual(beamInfo2.name, "beam2")
        self.assertTrue(beamInfo2.isOnDetector)
        self.assertEqual(beamInfo2.detectorName, "D1")
        self.assertPairsAlmostEqual(beamInfo2.detectorPos, detectorCtrPos, maxDiff=self.maxDetectorPosErr)

        # beam 3 should land on detector D1, 3/4 of the way from LL to UR
        beamInfo3 = self.cco[3]
        self.assertEqual(beamInfo3.name, "beam3")
        self.assertTrue(beamInfo3.isOnDetector)
        self.assertEqual(beamInfo3.detectorName, "D1")
        self.assertPairsAlmostEqual(beamInfo3.detectorPos, detector34Pos, maxDiff=self.maxDetectorPosErr)

        # beam 4 should land on the center of detector D2
        beamInfo4 = self.cco[4]
        self.assertEqual(beamInfo4.name, "beam4")
        self.assertTrue(beamInfo4.isOnDetector)
        self.assertEqual(beamInfo4.detectorName, "D2")
        self.assertPairsAlmostEqual(beamInfo4.detectorPos, detectorCtrPos, maxDiff=self.maxDetectorPosErr)

        # beam 5 should land on detector D2, 3/4 of the way from LL to UR
        # The measured error is 5e-7 pixels, which is small enough not to worry.
        # I strongly suspect it is due to inaccuracy in the inverse of the
        # field angle to focal plane transform.
        beamInfo5 = self.cco[5]
        self.assertEqual(beamInfo5.name, "beam5")
        self.assertTrue(beamInfo5.isOnDetector)
        self.assertEqual(beamInfo5.detectorName, "D2")
        self.assertPairsAlmostEqual(beamInfo5.detectorPos, detector34Pos, maxDiff=self.maxDetectorPosErr)
Exemple #22
0
    def computeExposureBounds(
            self,
            exposure: DimensionRecord,
            *,
            collections: Any = None) -> Dict[int, List[UnitVector3d]]:
        """Compute the lists of unit vectors on the sphere that correspond to
        the sky positions of detector corners.

        Parameters
        ----------
        exposure : `DimensionRecord`
            Dimension record for the exposure.
        collections : Any, optional
            Collections to be searched for raws and camera geometry, overriding
            ``self.butler.collections``.
            Can be any of the types supported by the ``collections`` argument
            to butler construction.

        Returns
        -------
        bounds : `dict`
            Dictionary mapping detector ID to a list of unit vectors on the
            sphere representing that detector's corners projected onto the sky.
        """
        if collections is None:
            collections = self.butler.collections
        camera, versioned = loadCamera(self.butler,
                                       exposure.dataId,
                                       collections=collections)
        if not versioned and self.config.requireVersionedCamera:
            raise LookupError(
                f"No versioned camera found for exposure {exposure.dataId}.")

        # Derive WCS from boresight information -- if available in registry
        use_registry = True
        try:
            orientation = lsst.geom.Angle(exposure.sky_angle,
                                          lsst.geom.degrees)
            radec = lsst.geom.SpherePoint(
                lsst.geom.Angle(exposure.tracking_ra, lsst.geom.degrees),
                lsst.geom.Angle(exposure.tracking_dec, lsst.geom.degrees))
        except AttributeError:
            use_registry = False

        if use_registry:
            if self.config.detectorId is None:
                detectorId = next(camera.getIdIter())
            else:
                detectorId = self.config.detectorId
            wcsDetector = camera[detectorId]

            # Ask the raw formatter to create the relevant WCS
            # This allows flips to be taken into account
            instrument = self.getInstrument(exposure.instrument)
            rawFormatter = instrument.getRawFormatter({"detector": detectorId})
            wcs = rawFormatter.makeRawSkyWcsFromBoresight(
                radec, orientation, wcsDetector)

        else:
            if self.config.detectorId is None:
                wcsRefsIter = self.butler.registry.queryDatasets(
                    "raw.wcs", dataId=exposure.dataId, collections=collections)
                if not wcsRefsIter:
                    raise LookupError(
                        f"No raw.wcs datasets found for data ID {exposure.dataId} "
                        f"in collections {collections}.")
                wcsRef = next(iter(wcsRefsIter))
                wcsDetector = camera[wcsRef.dataId["detector"]]
                wcs = self.butler.getDirect(wcsRef)
            else:
                wcsDetector = camera[self.config.detectorId]
                wcs = self.butler.get("raw.wcs",
                                      dataId=exposure.dataId,
                                      detector=self.config.detectorId,
                                      collections=collections)
        fpToSky = wcsDetector.getTransform(FOCAL_PLANE,
                                           PIXELS).then(wcs.getTransform())
        bounds = {}
        for detector in camera:
            pixelsToSky = detector.getTransform(PIXELS,
                                                FOCAL_PLANE).then(fpToSky)
            pixCorners = Box2D(detector.getBBox().dilatedBy(
                self.config.padding)).getCorners()
            bounds[detector.getId()] = [
                skyCorner.getVector()
                for skyCorner in pixelsToSky.applyForward(pixCorners)
            ]
        return bounds
Exemple #23
0
    def testSampleAt(self):
        """Test the behavior of TransmissionCurve.sampleAt on the subclass
        returned by makeCoaddTransmissionCurve.
        """
        wavelengths = np.linspace(4000, 7000, 200)

        # Points in coadd coordinates in each of the distinct regions
        point0 = self.makeRandomPoint(Point2I(-100, -100), Point2I(-1, -1))
        pointA = self.makeRandomPoint(Point2I(-100, 0), Point2I(-1, 99))
        pointB = self.makeRandomPoint(Point2I(0, -100), Point2I(99, -1))
        pointC = self.makeRandomPoint(Point2I(0, 0), Point2I(49, 99))
        pointD = self.makeRandomPoint(Point2I(50, 0), Point2I(99, 99))
        points = [point0, pointA, pointB, pointC, pointD]

        # The same points, in sky coordinates
        coords = [self.wcsCoadd.pixelToSky(point) for point in points]

        # The same points, in Epoch A's coordinates
        point0A, pointAA, pointBA, pointCA, pointDA = [
            self.wcsA.skyToPixel(coord) for coord in coords
        ]
        self.assertFalse(Box2D(self.bboxA).contains(point0A))
        self.assertTrue(Box2D(self.bboxA).contains(pointAA))
        self.assertFalse(Box2D(self.bboxA).contains(pointBA))
        self.assertTrue(Box2D(self.bboxA).contains(pointCA))
        self.assertTrue(Box2D(self.bboxA).contains(pointDA))

        # The same points, in Epoch B's coordinates
        point0B, pointAB, pointBB, pointCB, pointDB = [
            self.wcsB.skyToPixel(coord) for coord in coords
        ]
        self.assertFalse(Box2D(self.bboxB).contains(point0B))
        self.assertFalse(Box2D(self.bboxB).contains(pointAB))
        self.assertTrue(Box2D(self.bboxB).contains(pointBB))
        self.assertTrue(Box2D(self.bboxB).contains(pointCB))
        self.assertTrue(Box2D(self.bboxB).contains(pointDB))
        self.assertTrue(self.polygonB.contains(pointBB))
        self.assertTrue(self.polygonB.contains(pointCB))
        self.assertFalse(self.polygonB.contains(pointDB))

        # Test that we can't compute throughputs in region 0 (where there are no inputs)
        self.assertRaises(InvalidParameterError, self.curveCoadd.sampleAt,
                          point0, wavelengths)

        # Test throughputs in region A (only Epoch A contributes)
        throughputA1 = self.curveCoadd.sampleAt(pointA, wavelengths)
        throughputA2 = self.curveA.sampleAt(pointAA, wavelengths)
        self.assertFloatsAlmostEqual(throughputA1, throughputA2)

        # Test throughputs in region B (only Epoch B contributes)
        throughputB1 = self.curveCoadd.sampleAt(pointB, wavelengths)
        throughputB2 = self.curveB.sampleAt(pointBB, wavelengths)
        self.assertFloatsAlmostEqual(throughputB1, throughputB2)

        # Test throughputs in region C (both epochs contribute)
        throughputC1 = self.curveCoadd.sampleAt(pointC, wavelengths)
        throughputC2 = self.curveA.sampleAt(pointCA, wavelengths)
        throughputC3 = self.curveB.sampleAt(pointCB, wavelengths)
        self.assertFloatsAlmostEqual(throughputC1,
                                     throughputC2 * 0.75 + throughputC3 * 0.25)

        # Test throughputs in region D (only Epoch A contributes)
        throughputD1 = self.curveCoadd.sampleAt(pointD, wavelengths)
        throughputD2 = self.curveA.sampleAt(pointDA, wavelengths)
        self.assertFloatsAlmostEqual(throughputD1, throughputD2)