Exemple #1
0
 def testComputeAzAltFromPupilBase(self):
     """Test computeAzAltFromBasePupil with general values
     """
     # transform the pupil vector back to the base vector
     # using the computed internal az/alt position
     for vectorPupil, vectorBase, pupilMagFactor, baseMagFactor in itertools.product(
         ((1, 0, 0), (2, 1, 0), (2, 0, 1), (2, 0.7, -0.8)),
         ((1, 0, 0), (0, 1, 0), (1, -0.7, 0.8)),
         (1, 1000),
         (1, 1000),
     ):
         with self.subTest(vectorPupil=vectorPupil, vectorBase=vectorBase, pupilMagFactor=pupilMagFactor,
                           baseMagFactor=baseMagFactor):
             vectorPupilScaled = np.array(vectorPupil, dtype=float) * pupilMagFactor
             pupilMag = np.linalg.norm(vectorPupilScaled)
             vectorBaseScaled = np.array(vectorBase, dtype=float) * baseMagFactor
             pupilAzAlt = coordUtils.computeAzAltFromBasePupil(vectorPupil=vectorPupilScaled,
                                                               vectorBase=vectorBaseScaled)
             # Check the round trip; note that the magnitude
             # of the returned vector will equal
             # the magnitude of the input vector.
             vectorBaseRoundTrip = coordUtils.convertVectorFromPupilToBase(
                 vectorPupil=vectorPupilScaled,
                 pupilAzAlt=pupilAzAlt)
             vectorBaseRoundTripMag = np.linalg.norm(vectorBaseRoundTrip)
             self.assertAlmostEqual(vectorBaseRoundTripMag, pupilMag, delta=1e-15*pupilMag)
             spBase = SpherePoint(Vector3d(*vectorBase))
             spBaseRoundTrip = SpherePoint(Vector3d(*vectorBaseRoundTrip))
             sep = spBase.separation(spBaseRoundTrip)
             self.assertLess(sep.asRadians(), 2e-15)
Exemple #2
0
    def factory(cls, stamp_im, metadata, index):
        """This method is needed to service the FITS reader.
        We need a standard interface to construct objects like this.
        Parameters needed to construct this object are passed in via
        a metadata dictionary and then passed to the constructor of
        this class.  If lists of values are passed with the following
        keys, they will be passed to the constructor, otherwise dummy
        values will be passed: RA_DEG, DEC_DEG.  They should
        each point to lists of values.

        Parameters
        ----------
        stamp : `lsst.afw.image.MaskedImage`
            Pixel data to pass to the constructor
        metadata : `dict`
            Dictionary containing the information
            needed by the constructor.
        idx : `int`
            Index into the lists in ``metadata``

        Returns
        -------
        stamp : `Stamp`
            An instance of this class
        """
        if 'RA_DEG' in metadata and 'DEC_DEG' in metadata:
            return cls(stamp_im=stamp_im,
                       position=SpherePoint(
                           Angle(metadata.getArray('RA_DEG')[index], degrees),
                           Angle(metadata.getArray('DEC_DEG')[index],
                                 degrees)))
        else:
            return cls(stamp_im=stamp_im,
                       position=SpherePoint(Angle(numpy.nan),
                                            Angle(numpy.nan)))
    def testGetLongitudeValue(self):
        """Test if getLongitude() and getRa() return the expected value.
        """
        for lon, lat in self._dataset:
            for point in (
                SpherePoint(lon, lat),
                SpherePoint(lon.asDegrees(), lat.asDegrees(), degrees),
                SpherePoint(lon.asRadians(), lat.asRadians(), radians),
            ):
                self.assertIsInstance(point.getLongitude(), geom.Angle)
                # Behavior for non-finite points is undefined; depends on internal
                # data representation
                if point.isFinite():
                    self.assertGreaterEqual(point.getLongitude().asDegrees(), 0.0)
                    self.assertLess(point.getLongitude().asDegrees(), 360.0)

                    # Longitude not guaranteed to match input at pole
                    if not point.atPole():
                        # assertAnglesAlmostEqual handles angle wrapping internally
                        self.assertAnglesAlmostEqual(lon, point.getLongitude())
                        self.assertAnglesAlmostEqual(lon, point.getRa())

        # Vector construction should return valid longitude even in edge cases.
        point = SpherePoint(lsst.sphgeom.Vector3d(0.0, 0.0, -1.0))
        self.assertGreaterEqual(point.getLongitude().asDegrees(), 0.0)
        self.assertLess(point.getLongitude().asDegrees(), 360.0)
Exemple #4
0
 def setUp(self):
     datadir = self.getTestDataDir()
     self.repoPath = os.path.join(datadir, "DATA")
     self.calibPath = os.path.join(datadir, "CALIB")
     self.butler = dafPersist.Butler(root=self.repoPath,
                                     calibRoot=self.calibPath)
     self.size = (2112, 4644)
     self.dataId = {'visit': 1038843}
     self.filter = "i2"
     self.exposureTime = 615.037
     self.darkTime = 615.0
     self.dateAvg = DateTime(54771.610881712964, DateTime.MJD, DateTime.TAI)
     self.boresightRaDec = SpherePoint(135.40941055, -2.39999432, degrees)
     self.boresightAzAlt = SpherePoint(122.34, 52.02, degrees)
     self.boresightAirmass = 1.269
     self.boresightRotAngle = 0 * degrees
     self.rotType = RotType.SKY
     self.obs_longitude = -155.468876 * degrees
     self.obs_latitude = 19.825252 * degrees
     self.obs_elevation = 4215
     self.weath_airTemperature = 0.90
     self.weath_airPressure = 617.65 * 100  # 100 Pascal/millibar
     self.weath_humidity = 39.77
     # NOTE: if we deal with DM-8053 and get UT1 implemented, ERA will change slightly.
     self.era = 4.55388 * radians
Exemple #5
0
    def testReadLsstSkyWcsStripMetadata(self):
        metadata = self.makeMetadata()
        nKeys = len(metadata.toList())
        nToStrip = 11 + 6  # WCS "A" is also stripped
        frameSet1 = readLsstSkyWcs(metadata, strip=False)
        self.assertEqual(len(metadata.toList()), nKeys)

        crval = SpherePoint(metadata.getScalar("CRVAL1"),
                            metadata.getScalar("CRVAL2"), degrees)
        crvalRad = crval.getPosition(radians)
        desiredCrpix = self.getCrpix(metadata)
        computedCrpix = frameSet1.applyInverse(crvalRad)
        self.assertPairsAlmostEqual(desiredCrpix, computedCrpix)

        # read again, this time stripping metadata
        frameSet2 = readLsstSkyWcs(metadata, strip=True)
        self.assertEqual(len(metadata.toList()), nKeys - nToStrip)
        self.assertEqual(frameSet1, frameSet2)

        # having stripped the WCS keywords, we should not be able to generate
        # a WCS from what's left
        with self.assertRaises(lsst.pex.exceptions.TypeError):
            readLsstSkyWcs(metadata, strip=False)

        # try a full WCS with just CRPIX1 or 2 missing
        for i in (1, 2):
            metadata = self.makeMetadata()
            metadata.remove(f"CRPIX{i}")
            with self.assertRaises(lsst.pex.exceptions.TypeError):
                readLsstSkyWcs(metadata, strip=False)
Exemple #6
0
    def testFieldAngleToVector(self):
        sp00 = SpherePoint(0, 0, degrees)
        degList = (-90, -89.9, -20, 0, 10, 89.9, 90)
        for xdeg, ydeg, flipX in itertools.product(degList, degList, (False, True)):
            with self.subTest(xdeg=xdeg, ydeg=ydeg, flipX=flipX):
                xrad = xdeg * RAD_PER_DEG
                signx = -1 if flipX else 1
                testOrientation = xdeg != 0 or ydeg != 0
                yrad = ydeg * RAD_PER_DEG
                fieldAngle = (xrad, yrad)
                vector = coordUtils.fieldAngleToVector(fieldAngle, flipX)
                self.assertAlmostEqual(np.linalg.norm(vector), 1)
                if testOrientation:
                    # Orientation should match.
                    orientationFromFieldAngle = math.atan2(yrad, signx*xrad)*radians
                    # Field angle x = vector y, field angle y = vector z.
                    orientationFromVector = math.atan2(vector[2], vector[1])*radians
                    self.assertAnglesAlmostEqual(orientationFromVector, orientationFromFieldAngle)

                # Now test as spherical geometry.
                sp = SpherePoint(Vector3d(*vector))
                separation = sp00.separation(sp)
                predictedSeparation = math.hypot(xrad, yrad)*radians
                self.assertAnglesAlmostEqual(predictedSeparation, separation)
                if testOrientation:
                    bearing = sp00.bearingTo(sp)
                    self.assertAnglesAlmostEqual(orientationFromFieldAngle, bearing)

                # Test round trip through vectorToFieldAngle.
                fieldAngleFromVector = coordUtils.vectorToFieldAngle(vector, flipX)
                np.testing.assert_allclose(fieldAngleFromVector, fieldAngle, atol=1e-15)
Exemple #7
0
 def polar(row: Any) -> pandas.Series:
     v3d = Vector3d(row.x, row.y, row.z)
     sp = SpherePoint(v3d)
     return pandas.Series(
         [sp.getRa().asDegrees(),
          sp.getDec().asDegrees()],
         index=["ra", "decl"])
    def checkTransitive(self, delta):
        """Test if equality is transitive even for close points.

        This test prevents misuse of approximate floating-point
        equality -- if `__eq__` is implemented using AFP, then this
        test will fail for some value of `delta`. Testing multiple
        values is recommended.

        Parameters
        ----------
        delta : `number`
            The separation, in degrees, at which point equality may
            become intransitive.
        """
        for lon, lat in self._dataset:
            point1 = SpherePoint(lon - delta, lat)
            point2 = SpherePoint(lon, lat)
            point3 = SpherePoint(lon + delta, lat)

            self.assertTrue(point1 != point2
                            or point2 != point3
                            or point1 == point3)
            self.assertTrue(point3 != point1
                            or point1 != point2
                            or point3 == point2)
            self.assertTrue(point2 == point3
                            or point3 != point1
                            or point2 == point1)
 def pointSet(self):
     for lon, lat in self._dataset:
         for point in (
             SpherePoint(lon, lat),
             SpherePoint(lon.asDegrees(), lat.asDegrees(), degrees),
             SpherePoint(lon.asRadians(), lat.asRadians(), radians),
         ):
             yield point
Exemple #10
0
    def register(self, name, registry):
        """Add SkyMap, Tract, and Patch Dimension entries to the given Gen3
        Butler Registry.

        Parameters
        ----------
        name : `str`
            The name of the skymap.
        registry : `lsst.daf.butler.Registry`
            The registry to add to.
        """
        nxMax = 0
        nyMax = 0
        records = {
            "skymap": [],
            "tract": [],
            "patch": [],
        }
        for tractInfo in self:
            nx, ny = tractInfo.getNumPatches()
            nxMax = max(nxMax, nx)
            nyMax = max(nyMax, ny)
            region = tractInfo.getOuterSkyPolygon()
            centroid = SpherePoint(region.getCentroid())
            records["tract"].append({
                "skymap": name,
                "tract": tractInfo.getId(),
                "region": region,
                "ra": centroid.getRa().asDegrees(),
                "dec": centroid.getDec().asDegrees(),
            })
            for patchInfo in tractInfo:
                cellX, cellY = patchInfo.getIndex()
                records["patch"].append({
                    "skymap":
                    name,
                    "tract":
                    tractInfo.getId(),
                    "patch":
                    tractInfo.getSequentialPatchIndex(patchInfo),
                    "cell_x":
                    cellX,
                    "cell_y":
                    cellY,
                    "region":
                    patchInfo.getOuterSkyPolygon(tractInfo.getWcs()),
                })
        records["skymap"].append({
            "skymap": name,
            "hash": self.getSha1(),
            "tract_max": len(self),
            "patch_nx_max": nxMax,
            "patch_ny_max": nyMax,
        })
        with registry.transaction():
            for dimension, recordsForDimension in records.items():
                registry.insertDimensionData(dimension, *recordsForDimension)
Exemple #11
0
 def createVisitInfo():
     return lsst.afw.image.VisitInfo(
         10313423, 10.01, 11.02,
         DateTime(65321.1, DateTime.MJD,
                  DateTime.TAI), 12345.1, 45.1 * degrees,
         SpherePoint(23.1 * degrees, 73.2 * degrees),
         SpherePoint(134.5 * degrees, 33.3 * degrees), 1.73, 73.2 * degrees,
         lsst.afw.image.RotType.SKY,
         Observatory(11.1 * degrees, 22.2 * degrees, 0.333),
         Weather(1.1, 2.2, 34.5), "testCam")
    def testSeparationValueGeneric(self):
        """Test if separation() returns the correct value.
        """
        # This should cover arcs over the meridian, across the pole, etc.
        # Do not use sphgeom as an oracle, in case SpherePoint uses it
        # internally.
        for lon1, lat1 in self._dataset:
            point1 = SpherePoint(lon1, lat1)
            x1, y1, z1 = SpherePointTestSuite.toVector(lon1, lat1)
            for lon2, lat2 in self._dataset:
                point2 = SpherePoint(lon2, lat2)
                if lon1 != lon2 or lat1 != lat2:
                    # Numerically unstable at small angles, but that's ok.
                    x2, y2, z2 = SpherePointTestSuite.toVector(lon2, lat2)
                    expected = math.acos(x1*x2 + y1*y2 + z1*z2)
                else:
                    expected = 0.0

                sep = point1.separation(point2)
                self.assertIsInstance(sep, geom.Angle)
                if point1.isFinite() and point2.isFinite():
                    self.assertGreaterEqual(sep.asDegrees(), 0.0)
                    self.assertLessEqual(sep.asDegrees(), 180.0)
                    self.assertAlmostEqual(expected, sep.asRadians())
                    self.assertAnglesAlmostEqual(
                        sep, point2.separation(point1))
                else:
                    self.assertTrue(math.isnan(sep.asRadians()))
                    self.assertTrue(math.isnan(
                        point2.separation(point1).asRadians()))
Exemple #13
0
    def register(self, name, registry):
        """Add SkyMap, Tract, and Patch Dimension entries to the given Gen3
        Butler Registry.

        Parameters
        ----------
        name : `str`
            The name of the skymap.
        registry : `lsst.daf.butler.Registry`
            The registry to add to.
        """
        nxMax = 0
        nyMax = 0
        for tractInfo in self:
            nx, ny = tractInfo.getNumPatches()
            nxMax = max(nxMax, nx)
            nyMax = max(nyMax, ny)
        with registry.transaction():
            registry.addDimensionEntry(
                "skymap", {
                    "skymap": name,
                    "hash": self.getSha1(),
                    "tract_max": len(self),
                    "patch_nx_max": nxMax,
                    "patch_ny_max": nyMax
                })
            for tractInfo in self:
                region = tractInfo.getOuterSkyPolygon()
                centroid = SpherePoint(region.getCentroid())
                entry = ({
                    "skymap": name,
                    "tract": tractInfo.getId(),
                    "region": region,
                    "ra": centroid.getRa().asDegrees(),
                    "dec": centroid.getDec().asDegrees()
                })
                registry.addDimensionEntry("tract", entry)
                patchDataIdList = []
                for patchInfo in tractInfo:
                    cellX, cellY = patchInfo.getIndex()
                    patchDataIdList.append({
                        "skymap":
                        name,
                        "tract":
                        tractInfo.getId(),
                        "patch":
                        tractInfo.getSequentialPatchIndex(patchInfo),
                        "cell_x":
                        cellX,
                        "cell_y":
                        cellY,
                        "region":
                        patchInfo.getOuterSkyPolygon(tractInfo.getWcs())
                    })
                registry.addDimensionEntryList("patch", patchDataIdList)
 def testInitNArgFail(self):
     """Test incorrect calls to the SpherePoint constructor
     """
     with self.assertRaises(TypeError):
         SpherePoint("Rotund", "Bovine")
     with self.assertRaises(TypeError):
         SpherePoint(42)
     with self.assertRaises(TypeError):
         SpherePoint("ICRS", 34.0, -56.0)
     with self.assertRaises(TypeError):
         SpherePoint(34.0, -56.0)  # missing units
    def testGetVectorValue(self):
        """Test if getVector() returns the expected value.

        The test includes conformance to vector-angle conventions.
        """
        for lon, lat, vector in [
            (0.0*degrees, 0.0*degrees, lsst.sphgeom.Vector3d(1.0, 0.0, 0.0)),
            (90.0*degrees, 0.0*degrees, lsst.sphgeom.Vector3d(0.0, 1.0, 0.0)),
            (0.0*degrees, 90.0*degrees, lsst.sphgeom.Vector3d(0.0, 0.0, 1.0)),
        ]:
            for point in (
                SpherePoint(lon, lat),
                SpherePoint(lon.asDegrees(), lat.asDegrees(), degrees),
                SpherePoint(lon.asRadians(), lat.asRadians(), radians),
            ):
                newVector = point.getVector()
                self.assertIsInstance(newVector, lsst.sphgeom.UnitVector3d)
                for oldElement, newElement in zip(vector, newVector):
                    self.assertAlmostEqual(oldElement, newElement)

                # Convert back to spherical.
                newLon, newLat = SpherePoint(newVector)
                self.assertAlmostEqual(newLon.asDegrees(), lon.asDegrees())
                self.assertAlmostEqual(newLat.asDegrees(), lat.asDegrees())

        # Try some un-normalized ones, too.
        pointList = [
            ((0.0, 0.0), lsst.sphgeom.Vector3d(1.3, 0.0, 0.0)),
            ((90.0, 0.0), lsst.sphgeom.Vector3d(0.0, 1.2, 0.0)),
            ((0.0, 90.0), lsst.sphgeom.Vector3d(0.0, 0.0, 2.3)),
            ((0.0, 0.0), lsst.sphgeom.Vector3d(0.5, 0.0, 0.0)),
            ((90.0, 0.0), lsst.sphgeom.Vector3d(0.0, 0.7, 0.0)),
            ((0.0, 90.0), lsst.sphgeom.Vector3d(0.0, 0.0, 0.9)),
        ]

        for lonLat, vector in pointList:
            # Only convert from vector to spherical.
            point = SpherePoint(vector)
            newLon, newLat = point
            self.assertAlmostEqual(lonLat[0], newLon.asDegrees())
            self.assertAlmostEqual(lonLat[1], newLat.asDegrees())
            vector = lsst.sphgeom.Vector3d(point.getVector())
            self.assertAlmostEqual(1.0, vector.getSquaredNorm())

        # Ill-defined points should be all NaN after normalization
        cleanValues = [0.5, -0.3, 0.2]
        badValues = [nan, inf, -inf]
        for i in range(3):
            for badValue in badValues:
                values = cleanValues[:]
                values[i] = badValue
                nonFiniteVector = lsst.sphgeom.Vector3d(*values)
                for element in SpherePoint(nonFiniteVector).getVector():
                    self.assertTrue(math.isnan(element))
Exemple #16
0
    def _makeDiaObjects(self, sources, indices, dt):
        """Over-simplified implementation of source-to-object matching and
        new DiaObject generation.

        Currently matching is based on info passed along by source
        generator and does not even use DiaObjects from database.

        Parameters
        ----------
        sources : `numpy.array`
            (x, y, z) coordinates of sources, array dimension is (N, 3)
        indices : `numpy.array`
            array of indices of sources, 1-dim ndarray, transient sources
            have negative indices
        dt : `datetime.datetime`
            Visit time.

        Returns
        -------
        `afw.table.BaseCatalog`
        """

        schema = self._makeDiaObjectSchema()
        catalog = afwTable.SourceCatalog(schema)
        n_trans = 0
        for i in range(len(sources)):

            var_idx = int(indices[i])
            if var_idx == _OUTSIDER:
                continue

            xyz = sources[i]
            if var_idx >= _TRANSIENT_START_ID:
                n_trans += 1

            v3d = Vector3d(xyz[0], xyz[1], xyz[2])
            sp = SpherePoint(v3d)

            dir_v = UnitVector3d(v3d)
            pixelator = HtmPixelization(self.config.htm_level)
            index = pixelator.index(dir_v)

            record = catalog.addNew()
            record.set("id", var_idx)
            # record.set("validityStart", _utc_seconds(dt))
            # record.set("validityEnd", 0)
            # record.set("lastNonForcedSource", _utc_seconds(dt))
            record.set("coord_ra", sp.getRa())
            record.set("coord_dec", sp.getDec())
            record.set("pixelId", index)

        _LOG.info('found %s matching objects and %s transients/noise', len(catalog) - n_trans, n_trans)

        return catalog
 def __init__(self, config, maskInfo, cameraGeom):
     self.config = config
     self.maskInfo = maskInfo
     self.cameraGeom = cameraGeom
     self._fieldAngleToFocalPlane = cameraGeom.getTransform(
         FIELD_ANGLE, FOCAL_PLANE)
     # amount to add to default hole position to compute telescope rotator angle (pixels);
     # I found that a wide range of values works, with (1, 0) comfortably in that range
     self._holeDelta = Extent2D(1, 0)
     self._telAzAlt = SpherePoint(np.nan, np.nan, radians)
     self._telRot = np.nan * radians
     self._cbpAzAlt = SpherePoint(np.nan, np.nan, radians)
    def testRotatedAlias(self):
        """White-box test: all representations of a pole should rotate into the same point.
        """
        longitudes = [0.0, 90.0, 242.0]
        latitude = 90.0
        arcLen = 10.0
        pole = SpherePoint(90.0*degrees, 0.0*degrees)
        for longitude in longitudes:
            point = SpherePoint(longitude*degrees, latitude*degrees)
            newPoint = point.rotated(pole, arcLen*degrees)

            self.assertAlmostEqual(0.0, newPoint.getLongitude().asDegrees())
            self.assertAlmostEqual(80.0, newPoint.getLatitude().asDegrees())
    def testRotatedAlias(self):
        """White-box test: all representations of a pole should rotate into the same point.
        """
        longitudes = [0.0, 90.0, 242.0]
        latitude = 90.0
        arcLen = 10.0
        pole = SpherePoint(90.0*degrees, 0.0*degrees)
        for longitude in longitudes:
            point = SpherePoint(longitude*degrees, latitude*degrees)
            newPoint = point.rotated(pole, arcLen*degrees)

            self.assertAlmostEqual(0.0, newPoint.getLongitude().asDegrees())
            self.assertAlmostEqual(80.0, newPoint.getLatitude().asDegrees())
 def testBearingToFromPole(self):
     """Test if bearingTo() returns the expected value from a point at a pole
     """
     for long0Deg in (0, 55, 270):
         for atSouthPole in (False, True):
             lat0Deg = -90 if atSouthPole else 90
             sp0 = SpherePoint(long0Deg, lat0Deg, degrees)
             for long1Deg in (0, 55, 270):
                 for lat1Deg in (-89, 0, 89):
                     sp1 = SpherePoint(long1Deg, lat1Deg, degrees)
                     desiredBearing = ((long1Deg - long0Deg) - 90) * degrees
                     if atSouthPole:
                         desiredBearing *= -1
                     measuredBearing = sp0.bearingTo(sp1)
                     self.assertAnglesAlmostEqual(desiredBearing, measuredBearing)
    def testTicket1761(self):
        """Regression test for Ticket 1761.

        Checks for math errors caused by unnormalized vectors.
        """
        refPoint = SpherePoint(lsst.sphgeom.Vector3d(0, 1, 0))

        point1 = SpherePoint(lsst.sphgeom.Vector3d(0.1, 0.1, 0.1))
        point2 = SpherePoint(lsst.sphgeom.Vector3d(0.6, 0.6, 0.6))
        sep1 = refPoint.separation(point1)
        sep2 = refPoint.separation(point2)
        sepTrue = 54.735610317245339*degrees

        self.assertAnglesAlmostEqual(sepTrue, sep1)
        self.assertAnglesAlmostEqual(sepTrue, sep2)
 def testBearingToFromPole(self):
     """Test if bearingTo() returns the expected value from a point at a pole
     """
     for long0Deg in (0, 55, 270):
         for atSouthPole in (False, True):
             lat0Deg = -90 if atSouthPole else 90
             sp0 = SpherePoint(long0Deg, lat0Deg, degrees)
             for long1Deg in (0, 55, 270):
                 for lat1Deg in (-89, 0, 89):
                     sp1 = SpherePoint(long1Deg, lat1Deg, degrees)
                     desiredBearing = ((long1Deg - long0Deg) - 90) * degrees
                     if atSouthPole:
                         desiredBearing *= -1
                     measuredBearing = sp0.bearingTo(sp1)
                     self.assertAnglesAlmostEqual(desiredBearing, measuredBearing)
    def testTicket1761(self):
        """Regression test for Ticket 1761.

        Checks for math errors caused by unnormalized vectors.
        """
        refPoint = SpherePoint(lsst.sphgeom.Vector3d(0, 1, 0))

        point1 = SpherePoint(lsst.sphgeom.Vector3d(0.1, 0.1, 0.1))
        point2 = SpherePoint(lsst.sphgeom.Vector3d(0.6, 0.6, 0.6))
        sep1 = refPoint.separation(point1)
        sep2 = refPoint.separation(point2)
        sepTrue = 54.735610317245339*degrees

        self.assertAnglesAlmostEqual(sepTrue, sep1)
        self.assertAnglesAlmostEqual(sepTrue, sep2)
Exemple #24
0
 def testComputeAzAltFromPupilBaseWithVectorPupil100(self):
     """Test computeAzAltFromBasePupil with vectorPupil = (1, 0, 0),
     so internal az/alt points along vectorBase
     """
     vectorPupil = (1, 0, 0)
     for vectorBase, pupilMagFactor, baseMagFactor in itertools.product(
         ((1, 0, 0), (0, -1, 0), (0, -0.5, 0.5), (0.5, 0, 0.5), (1, 0.7, -0.8)),
         (1, 1000),
         (1, 1000),
     ):
         with self.subTest(vectorBase=vectorBase, pupilMagFactor=pupilMagFactor,
                           baseMagFactor=baseMagFactor):
             predictedPupilAzalt = SpherePoint(Vector3d(*vectorBase))
             vectorPupilScaled = np.array(vectorPupil, dtype=float) * pupilMagFactor
             vectorBaseScaled = np.array(vectorBase, dtype=float) * baseMagFactor
             pupilAzAlt = coordUtils.computeAzAltFromBasePupil(vectorPupil=vectorPupilScaled,
                                                               vectorBase=vectorBaseScaled)
             sep = pupilAzAlt.separation(predictedPupilAzalt)
             if sep.asRadians() > 1e-14:
                 print("Warning: sep={:0.5f} asec for vectorPupilScaled={}, vectorBaseScaled={}".format(
                       sep.asArcseconds(), vectorPupilScaled, vectorBaseScaled))
             # The worst error I see is 0.0026"
             # for vectorBase=(1, 0.7, -0.8).
             # That is worrisome, but acceptable.
             self.assertLess(sep.asArcseconds(), 0.01)
    def testSeparationPoles(self):
        """White-box test: all representations of a pole should have the same distance to another point.
        """
        southPole1 = SpherePoint(-30.0, -90.0, degrees)
        southPole2 = SpherePoint(183.0, -90.0, degrees)
        regularPoint = SpherePoint(42.0, 45.0, degrees)
        expectedSep = (45.0 + 90.0)*degrees

        self.assertAnglesAlmostEqual(
            expectedSep, southPole1.separation(regularPoint))
        self.assertAnglesAlmostEqual(
            expectedSep, regularPoint.separation(southPole1))
        self.assertAnglesAlmostEqual(
            expectedSep, southPole2.separation(regularPoint))
        self.assertAnglesAlmostEqual(
            expectedSep, regularPoint.separation(southPole2))
Exemple #26
0
 def testMakeSimpleWcsMetadata(self):
     crpix = Point2D(111.1, 222.2)
     crval = SpherePoint(45.6 * degrees, 12.3 * degrees)
     scale = 1 * arcseconds
     for orientation in (0 * degrees, 21 * degrees):
         cdMatrix = makeCdMatrix(scale=scale, orientation=orientation)
         for projection in ("TAN", "STG"):
             metadata = makeSimpleWcsMetadata(crpix=crpix,
                                              crval=crval,
                                              cdMatrix=cdMatrix,
                                              projection=projection)
             desiredLength = 11 if orientation == 0 * degrees else 13
             self.assertEqual(len(metadata.names()), desiredLength)
             self.assertEqual(metadata.getScalar("RADESYS"), "ICRS")
             self.assertFalse(metadata.exists("EQUINOX"))
             self.assertEqual(metadata.getScalar("CTYPE1"),
                              "RA---" + projection)
             self.assertEqual(metadata.getScalar("CTYPE2"),
                              "DEC--" + projection)
             for i in range(2):
                 self.assertAlmostEqual(metadata.getScalar(f"CRPIX{i + 1}"),
                                        crpix[i] + 1)
                 self.assertAlmostEqual(metadata.getScalar(f"CRVAL{i + 1}"),
                                        crval[i].asDegrees())
                 self.assertEqual(metadata.getScalar(f"CUNIT{i + 1}"),
                                  "deg")
             for i in range(2):
                 for j in range(2):
                     name = f"CD{i + 1}_{j + 1}"
                     if cdMatrix[i, j] != 0:
                         self.assertAlmostEqual(metadata.getScalar(name),
                                                cdMatrix[i, j])
                     else:
                         self.assertFalse(metadata.exists(name))
Exemple #27
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 #28
0
    def addPixCoords(self, fakeCat, wcs):
        """Add pixel coordinates to the catalog of fakes.

        Parameters
        ----------
        fakeCat : `pandas.core.frame.DataFrame`
                    The catalog of fake sources to be input
        wcs : `lsst.afw.geom.SkyWcs`
                    WCS to use to add fake sources

        Returns
        -------
        fakeCat : `pandas.core.frame.DataFrame`

        Notes
        -----
        The default option is to use the WCS information from the image. If the ``useUpdatedCalibs`` config
        option is set then it will use the updated WCS from jointCal.
        """

        ras = fakeCat[self.config.raColName].values
        decs = fakeCat[self.config.decColName].values
        skyCoords = [
            SpherePoint(ra, dec, radians) for (ra, dec) in zip(ras, decs)
        ]
        pixCoords = wcs.skyToPixel(skyCoords)
        xs = [coord.getX() for coord in pixCoords]
        ys = [coord.getY() for coord in pixCoords]
        fakeCat["x"] = xs
        fakeCat["y"] = ys

        return fakeCat
Exemple #29
0
def vectorToFieldAngle(vec, flipX):
    """Convert a vector to a pupil field angle.

    Parameters
    ----------
    vec : sequence of 3 `float`
        3-dimensional vector in the :ref:`pupil frame
        <lsst.cbp.pupil_frame>`; the magnitude is ignored,
        but must be large enough to compute an accurate unit vector.
    flipX : bool
        Set True if the x axis of the focal plane is flipped
        with respect to the pupil.

    Returns
    -------
    fieldAngle : `tuple` of 2 `float`
        x,y :ref:`pupil field angle <lsst.cbp.pupil_field_angle>`
        (radians).
    """
    sp = SpherePoint(Vector3d(*vec))
    amountRad = ZeroSpherePoint.separation(sp).asRadians()
    bearingRad = ZeroSpherePoint.bearingTo(sp).asRadians()
    xyrad = (amountRad * math.cos(bearingRad),
             amountRad * math.sin(bearingRad))
    return getFlippedPos(xyrad, flipX=flipX)
Exemple #30
0
    def _makeDiaSources(self, sources, indices, visit_id):
        """Generate catalog of DiaSources to store in a database

        Parameters
        ----------
        sources : `numpy.ndarray`
            (x, y, z) coordinates of sources, array dimension is (N, 3)
        indices : `numpy.array`
            array of indices of sources, 1-dim ndarray, transient sources
            have negative indices
        visit_id : `int`
            ID of the visit

        Returns
        -------
        `afw.table.BaseCatalog`
        """

        schema = self._makeDiaSourceSchema()
        catalog = afwTable.BaseCatalog(schema)
        for i in range(len(sources)):

            var_idx = int(indices[i])
            if var_idx == _OUTSIDER:
                continue

            xyz = sources[i]
            v3d = Vector3d(xyz[0], xyz[1], xyz[2])
            sp = SpherePoint(v3d)

            dir_v = UnitVector3d(v3d)
            pixelator = HtmPixelization(self.config.htm_level)
            index = pixelator.index(dir_v)

            self.lastSourceId += 1

            record = catalog.addNew()
            record.set("id", self.lastSourceId)
            record.set("ccdVisitId", visit_id)
            record.set("diaObjectId", var_idx)
            record.set("parent", 0)
            record.set("coord_ra", sp.getRa())
            record.set("coord_dec", sp.getDec())
            record.set("flags", 0)
            record.set("pixelId", index)

        return catalog
Exemple #31
0
 def testComputeAzAltFromPupilBaseWithBaseEqualsPupil(self):
     """Test computeAzAltFromBasePupil with baseVector=pupilVector,
     so the telescope will to internal az, alt=0
     """
     zeroSp = SpherePoint(0, 0, radians)
     for vector, pupilMagFactor, baseMagFactor in itertools.product(
         ((1, 0, 0), (0.1, -1, 0), (0.1, -0.5, 0.5), (0.5, 0, 0.5), (1, 0.7, -0.8)),
         (1, 1000),
         (1, 1000),
     ):
         with self.subTest(vector=vector, pupilMagFactor=pupilMagFactor, baseMagFactor=baseMagFactor):
             vectorPupil = np.array(vector, dtype=float) * pupilMagFactor
             vectorBase = np.array(vector, dtype=float) * baseMagFactor
             obs = coordUtils.computeAzAltFromBasePupil(vectorPupil=vectorPupil,
                                                        vectorBase=vectorBase)
             sep = zeroSp.separation(obs).asRadians()
             self.assertLess(sep, 1e-14)
    def testBearingToValueSingular(self):
        """White-box test: bearingTo() may be unstable if points are near opposite poles.

        This test is motivated by an error analysis of the `bearingTo`
        implementation. It may become irrelevant if the implementation
        changes.
        """
        southPole = SpherePoint(0.0*degrees, self.nextUp(-90.0*degrees))
        northPoleSame = SpherePoint(0.0*degrees, self.nextDown(90.0*degrees))
        # Don't let it be on exactly the opposite side.
        northPoleOpposite = SpherePoint(
            180.0*degrees, self.nextDown(northPoleSame.getLatitude()))

        self.assertAnglesAlmostEqual(southPole.bearingTo(northPoleSame),
                                     geom.HALFPI*geom.radians)
        self.assertAnglesAlmostEqual(southPole.bearingTo(northPoleOpposite),
                                     (geom.PI + geom.HALFPI)*geom.radians)
Exemple #33
0
    def register(self, name, registry):
        """Add SkyMap, Tract, and Patch Dimension entries to the given Gen3
        Butler Registry.

        Parameters
        ----------
        name : `str`
            The name of the skymap.
        registry : `lsst.daf.butler.Registry`
            The registry to add to.
        """
        nxMax = 0
        nyMax = 0
        for tractInfo in self:
            nx, ny = tractInfo.getNumPatches()
            nxMax = max(nxMax, nx)
            nyMax = max(nyMax, ny)
        with registry.transaction():
            registry.addDimensionEntry(
                "skymap",
                {"skymap": name,
                 "hash": self.getSha1(),
                 "tract_max": len(self),
                 "patch_nx_max": nxMax,
                 "patch_ny_max": nyMax}
            )
            for tractInfo in self:
                region = tractInfo.getOuterSkyPolygon()
                centroid = SpherePoint(region.getCentroid())
                entry = (
                    {"skymap": name, "tract": tractInfo.getId(),
                     "region": region,
                     "ra": centroid.getRa().asDegrees(),
                     "dec": centroid.getDec().asDegrees()}
                )
                registry.addDimensionEntry("tract", entry)
                patchDataIdList = []
                for patchInfo in tractInfo:
                    cellX, cellY = patchInfo.getIndex()
                    patchDataIdList.append(
                        {"skymap": name, "tract": tractInfo.getId(),
                         "patch": tractInfo.getSequentialPatchIndex(patchInfo),
                         "cell_x": cellX, "cell_y": cellY,
                         "region": patchInfo.getOuterSkyPolygon(tractInfo.getWcs())}
                    )
                registry.addDimensionEntryList("patch", patchDataIdList)
Exemple #34
0
 def setUp(self):
     self.butler = dafPersist.Butler(root=os.path.join(os.path.dirname(__file__), "data"))
     self.exposureTime = 15.0
     self.darkTime = 15.0
     dateObs = DateTime(49552.28496, DateTime.MJD, DateTime.TAI)
     self.dateAvg = DateTime(dateObs.nsecs(DateTime.TAI) + int(0.5e9*self.exposureTime), DateTime.TAI)
     self.boresightRaDec = SpherePoint(359.936019771151, -2.3356222648145, degrees)
     self.boresightAzAlt = SpherePoint(127.158246182602, 90 - 40.6736117075876, degrees)
     self.boresightAirmass = 1.31849492005496
     self.boresightRotAngle = -3.43228*degrees
     self.rotType = RotType.SKY
     self.obs_longitude = -70.749417*degrees
     self.obs_latitude = -30.244633*degrees
     self.obs_elevation = 2663.0
     self.weath_airTemperature = 5.0
     self.weath_airPressure = MakeRawVisitInfo.pascalFromMmHg(520.0)
     self.weath_humidity = 40.
 def testGetLatitudeValue(self):
     """Test if getLatitude() and getDec() return the expected value.
     """
     for lon, lat in self._dataset:
         for point in (
             SpherePoint(lon, lat),
             SpherePoint(lon.asDegrees(), lat.asDegrees(), degrees),
             SpherePoint(lon.asRadians(), lat.asRadians(), radians),
         ):
             self.assertIsInstance(point.getLatitude(), geom.Angle)
             # Behavior for non-finite points is undefined; depends on internal
             # data representation
             if point.isFinite():
                 self.assertGreaterEqual(point.getLatitude().asDegrees(), -90.0)
                 self.assertLessEqual(point.getLatitude().asDegrees(), 90.0)
                 self.assertAnglesAlmostEqual(lat, point.getLatitude())
                 self.assertAnglesAlmostEqual(lat, point.getDec())
    def testConvertVectorFromPupilToBase(self):
        """Test convertVectorFromPupilToBase and convertVectorFromBaseToPupil
        """
        cos30 = math.cos(30 * math.pi / 180)
        sin30 = math.sin(30 * math.pi / 180)
        for magMultiplier in (0.001, 1, 1000):
            for azAltDeg, vectorPupil, predictedVectorBase in (
                # at az=0, alt=0: base = pupil
                ((0, 0), (1, 0, 0), (1, 0, 0)),
                ((0, 0), (0, 1, 0), (0, 1, 0)),
                ((0, 0), (0, 0, -1), (0, 0, -1)),
                ((0, 0), (1, -1, 1), (1, -1, 1)),
                # at az=90, alt=0: base x = pupil -y, base y = pupil x, base z = pupil z
                ((90, 0), (1, 0, 0), (0, 1, 0)),
                ((90, 0), (0, 1, 0), (-1, 0, 0)),
                ((90, 0), (0, 0, -1), (0, 0, -1)),
                ((90, 0), (1, -1, 1), (1, 1, 1)),
                # at az=0, alt=90: base x = - pupil z, base y = pupil y, base z = pupil x
                ((0, 90), (1, 0, 0), (0, 0, 1)),
                ((0, 90), (0, 1, 0), (0, 1, 0)),
                ((0, 90), (0, 0, -1), (1, 0, 0)),
                ((0, 90), (1, -1, 1), (-1, -1, 1)),
                # at az=90, alt=90: base x = -pupil y, base y = - pupil z, base z = pupil x
                ((90, 90), (1, 0, 0), (0, 0, 1)),
                ((90, 90), (0, 1, 0), (-1, 0, 0)),
                ((90, 90), (0, 0, -1), (0, 1, 0)),
                ((90, 90), (1, -1, 1), (1, -1, 1)),
                # at az=0, alt=45:
                # base x = cos(30) * pupil x - sin(30) * pupil z
                # base y = pupil y
                # base z = sin(30) * pupil x + cos(30) * pupil z
                ((0, 30), (1, 0, 0), (cos30, 0, sin30)),
                ((0, 30), (0, 1, 0), (0, 1, 0)),
                ((0, 30), (0, 0, -1), (sin30, 0, -cos30)),
                ((0, 30), (1, -1, 1), (cos30 - sin30, -1, sin30 + cos30)),
                # at az=30, alt=0:
                # base x = cos(30) * pupil x - sin(30) * pupil y
                # base y = sin(30) * pupil x + cos(30) * pupil y
                # base z = pupil z
                ((30, 0), (1, 0, 0), (cos30, sin30, 0)),
                ((30, 0), (0, 1, 0), (-sin30, cos30, 0)),
                ((30, 0), (0, 0, -1), (0, 0, -1)),
                ((30, 0), (1, -1, 1), (cos30 + sin30, sin30 - cos30, 1)),
            ):
                vectorPupil = np.array(vectorPupil) * magMultiplier
                predictedVectorBase = np.array(predictedVectorBase) * magMultiplier
                pupilAzAlt = SpherePoint(*azAltDeg, degrees)
                vectorBase = coordUtils.convertVectorFromPupilToBase(vectorPupil=vectorPupil,
                                                                     pupilAzAlt=pupilAzAlt)
                atol = max(magMultiplier, 1) * 1e-15
                msg = "azAltDeg={}, vectorPupil={}".format(azAltDeg, vectorPupil)
                np.testing.assert_allclose(vectorBase, predictedVectorBase, atol=atol,
                                           err_msg=msg, verbose=True)

                vectorPupilRoundTrip = coordUtils.convertVectorFromBaseToPupil(vectorBase=vectorBase,
                                                                               pupilAzAlt=pupilAzAlt)
                np.testing.assert_allclose(vectorPupil, vectorPupilRoundTrip, atol=atol,
                                           err_msg=msg, verbose=True)
Exemple #37
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)
    def testSeparationValueGeneric(self):
        """Test if separation() returns the correct value.
        """
        # This should cover arcs over the meridian, across the pole, etc.
        # Do not use sphgeom as an oracle, in case SpherePoint uses it
        # internally.
        for lon1, lat1 in self._dataset:
            point1 = SpherePoint(lon1, lat1)
            x1, y1, z1 = SpherePointTestSuite.toVector(lon1, lat1)
            for lon2, lat2 in self._dataset:
                point2 = SpherePoint(lon2, lat2)
                if lon1 != lon2 or lat1 != lat2:
                    # Numerically unstable at small angles, but that's ok.
                    x2, y2, z2 = SpherePointTestSuite.toVector(lon2, lat2)
                    expected = math.acos(x1*x2 + y1*y2 + z1*z2)
                else:
                    expected = 0.0

                sep = point1.separation(point2)
                self.assertIsInstance(sep, geom.Angle)
                if point1.isFinite() and point2.isFinite():
                    self.assertGreaterEqual(sep.asDegrees(), 0.0)
                    self.assertLessEqual(sep.asDegrees(), 180.0)
                    self.assertAlmostEqual(expected, sep.asRadians())
                    self.assertAnglesAlmostEqual(
                        sep, point2.separation(point1))
                else:
                    self.assertTrue(math.isnan(sep.asRadians()))
                    self.assertTrue(math.isnan(
                        point2.separation(point1).asRadians()))
    def testGetLongitudeValue(self):
        """Test if getLongitude() and getRa() return the expected value.
        """
        for lon, lat in self._dataset:
            for point in (
                SpherePoint(lon, lat),
                SpherePoint(lon.asDegrees(), lat.asDegrees(), degrees),
                SpherePoint(lon.asRadians(), lat.asRadians(), radians),
            ):
                self.assertIsInstance(point.getLongitude(), geom.Angle)
                # Behavior for non-finite points is undefined; depends on internal
                # data representation
                if point.isFinite():
                    self.assertGreaterEqual(point.getLongitude().asDegrees(), 0.0)
                    self.assertLess(point.getLongitude().asDegrees(), 360.0)

                    # Longitude not guaranteed to match input at pole
                    if not point.atPole():
                        # assertAnglesAlmostEqual handles angle wrapping internally
                        self.assertAnglesAlmostEqual(lon, point.getLongitude())
                        self.assertAnglesAlmostEqual(lon, point.getRa())

        # Vector construction should return valid longitude even in edge cases.
        point = SpherePoint(lsst.sphgeom.Vector3d(0.0, 0.0, -1.0))
        self.assertGreaterEqual(point.getLongitude().asDegrees(), 0.0)
        self.assertLess(point.getLongitude().asDegrees(), 360.0)
    def testGetItemError(self):
        """Test if indexing correctly handles invalid input.
        """
        point = SpherePoint(lsst.sphgeom.Vector3d(1.0, 1.0, 1.0))

        with self.assertRaises(IndexError):
            point[2]
        with self.assertRaises(IndexError):
            point[-3]
 def testBearingToValueSameLongitude(self):
     """Test that bearingTo() returns +/- 90 for two points on the same longitude
     """
     for longDeg in (0, 55, 270):
         for lat0Deg in (-90, -5, 0, 44, 90):
             sp0 = SpherePoint(longDeg, lat0Deg, degrees)
             for lat1Deg in (-90, -41, 1, 41, 90):
                 if lat0Deg == lat1Deg:
                     continue
                 sp1 = SpherePoint(longDeg, lat1Deg, degrees)
                 if sp0.atPole() and sp1.atPole():
                     # the points are at opposite poles; any bearing may be returned
                     continue
                 bearing = sp0.bearingTo(sp1)
                 if lat1Deg > lat0Deg:
                     self.assertAnglesAlmostEqual(bearing, 90 * degrees)
                 else:
                     self.assertAnglesAlmostEqual(bearing, -90 * degrees)
    def testEquality(self):
        """Test if tests for equality treat SpherePoints as values.
        """
        # (In)equality is determined by value, not identity.
        # See DM-2347, DM-2465. These asserts are testing the
        # functionality of `==` and `!=` and should not be changed.
        for lon1, lat1 in self._dataset:
            point1 = SpherePoint(lon1, lat1)
            self.assertIsInstance(point1 == point1, bool)
            self.assertIsInstance(point1 != point1, bool)
            if point1.isFinite():
                self.assertTrue(point1 == point1)
                self.assertFalse(point1 != point1)

                pointCopy = copy.deepcopy(point1)
                self.assertIsNot(pointCopy, point1)
                self.assertEqual(pointCopy, point1)
                self.assertEqual(point1, pointCopy)
                self.assertFalse(pointCopy != point1)
                self.assertFalse(point1 != pointCopy)
            else:
                self.assertFalse(point1 == point1)
                self.assertTrue(point1 != point1)

            for lon2, lat2 in self._dataset:
                point2 = SpherePoint(lon2, lat2)
                if lon1 == lon2 and lat1 == lat2 and point1.isFinite() and point2.isFinite():
                    # note: the isFinite checks are needed because if longitude is infinite
                    # then the resulting SpherePoint has nan as its longitude, due to wrapping
                    self.assertFalse(point2 != point1)
                    self.assertFalse(point1 != point2)
                    self.assertTrue(point2 == point1)
                    self.assertTrue(point1 == point2)
                else:
                    self.assertTrue(point2 != point1)
                    self.assertTrue(point1 != point2)
                    self.assertFalse(point2 == point1)
                    self.assertFalse(point1 == point2)

        # Test for transitivity (may be assumed by algorithms).
        for delta in [10.0**(0.1*x) for x in range(-150, -49, 5)]:
            self.checkTransitive(delta*radians)
    def testOffsetTangentPlane(self):
        """Test offsets on a tangent plane (good for small angles)"""

        c0 = SpherePoint(0.0, 0.0, geom.degrees)

        for dRaDeg in (0.0123, 0.0, -0.0321):
            dRa = dRaDeg*geom.degrees
            for dDecDeg in (0.0543, 0.0, -0.0987):
                dDec = dDecDeg*geom.degrees
                c1 = SpherePoint(dRa, dDec)

                offset = c0.getTangentPlaneOffset(c1)

                # This more-or-less works for small angles because c0 is 0,0
                expectedOffset = [
                    math.tan(dRa.asRadians())*geom.radians,
                    math.tan(dDec.asRadians())*geom.radians,
                ]

                for i in range(2):
                    self.assertAnglesAlmostEqual(offset[i], expectedOffset[i])
    def testSeparationPoles(self):
        """White-box test: all representations of a pole should have the same distance to another point.
        """
        southPole1 = SpherePoint(-30.0, -90.0, degrees)
        southPole2 = SpherePoint(183.0, -90.0, degrees)
        regularPoint = SpherePoint(42.0, 45.0, degrees)
        expectedSep = (45.0 + 90.0)*degrees

        self.assertAnglesAlmostEqual(
            expectedSep, southPole1.separation(regularPoint))
        self.assertAnglesAlmostEqual(
            expectedSep, regularPoint.separation(southPole1))
        self.assertAnglesAlmostEqual(
            expectedSep, southPole2.separation(regularPoint))
        self.assertAnglesAlmostEqual(
            expectedSep, regularPoint.separation(southPole2))
    def testBearingToValueOnEquator(self):
        """Test if bearingTo() returns the expected value from a point on the equator
        """
        lon0 = 90.0
        lat0 = 0.0   # These tests only work from the equator.
        arcLen = 10.0

        trials = [
            # Along celestial equator
            dict(lon=lon0, lat=lat0, bearing=0.0,
                 lonEnd=lon0+arcLen, latEnd=lat0),
            # Along a meridian
            dict(lon=lon0, lat=lat0, bearing=90.0,
                 lonEnd=lon0, latEnd=lat0+arcLen),
            # 180 degree arc (should go to antipodal point)
            dict(lon=lon0, lat=lat0, bearing=45.0,
                 lonEnd=lon0+180.0, latEnd=-lat0),
            #
            dict(lon=lon0, lat=lat0, bearing=45.0,
                 lonEnd=lon0+90.0, latEnd=lat0 + 45.0),
            dict(lon=lon0, lat=lat0, bearing=225.0,
                 lonEnd=lon0-90.0, latEnd=lat0 - 45.0),
            dict(lon=lon0, lat=np.nextafter(-90.0, inf),
                 bearing=90.0, lonEnd=lon0, latEnd=0.0),
            dict(lon=lon0, lat=np.nextafter(-90.0, inf),
                 bearing=0.0, lonEnd=lon0 + 90.0, latEnd=0.0),
            # Argument at a pole should work
            dict(lon=lon0, lat=lat0, bearing=270.0, lonEnd=lon0, latEnd=-90.0),
            # Support for non-finite values
            dict(lon=lon0, lat=nan, bearing=nan, lonEnd=lon0, latEnd=45.0),
            dict(lon=lon0, lat=lat0, bearing=nan, lonEnd=nan, latEnd=90.0),
            dict(lon=inf, lat=lat0, bearing=nan, lonEnd=lon0, latEnd=42.0),
            dict(lon=lon0, lat=lat0, bearing=nan, lonEnd=-inf, latEnd=42.0),
        ]

        for trial in trials:
            origin = SpherePoint(trial['lon']*degrees, trial['lat']*degrees)
            end = SpherePoint(trial['lonEnd']*degrees, trial['latEnd']*degrees)
            bearing = origin.bearingTo(end)

            self.assertIsInstance(bearing, geom.Angle)
            if origin.isFinite() and end.isFinite():
                self.assertGreaterEqual(bearing.asDegrees(), 0.0)
                self.assertLess(bearing.asDegrees(), 360.0)
            if origin.separation(end).asDegrees() != 180.0:
                if not math.isnan(trial['bearing']):
                    self.assertAlmostEqual(
                        trial['bearing'], bearing.asDegrees(), 12)
                else:
                    self.assertTrue(math.isnan(bearing.asRadians()))
    def testSeparationValueAbsolute(self):
        """Test if separation() returns specific values.
        """
        # Test from "Meeus, p. 110" (test originally written for coord::Coord;
        # don't know exact reference)
        spica = SpherePoint(201.2983, -11.1614, degrees)
        arcturus = SpherePoint(213.9154, 19.1825, degrees)

        # Verify to precision of quoted distance and positions.
        self.assertAlmostEqual(
            32.7930, spica.separation(arcturus).asDegrees(), 4)

        # Verify small angles: along a constant ra, add an arcsec to spica dec.
        epsilon = 1.0*geom.arcseconds
        spicaPlus = SpherePoint(spica.getLongitude(),
                                spica.getLatitude() + epsilon)

        self.assertAnglesAlmostEqual(epsilon, spicaPlus.separation(spica))
 def testDefaultConstructor(self):
     sp = SpherePoint()
     self.assertTrue(math.isnan(sp.getLongitude()))
     self.assertTrue(math.isnan(sp.getLatitude()))
     self.assertFalse(sp.isFinite())
    def testOffsetValue(self):
        """Test if offset() returns the expected value.
        """
        # This should cover arcs over the meridian, across the pole, etc.
        for lon1, lat1 in self._dataset:
            point1 = SpherePoint(lon1, lat1)
            for lon2, lat2 in self._dataset:
                if lon1 == lon2 and lat1 == lat2:
                    continue
                point2 = SpherePoint(lon2, lat2)
                bearing = point1.bearingTo(point2)
                distance = point1.separation(point2)

                # offsetting point1 by bearing and distance should produce the same result as point2
                newPoint = point1.offset(bearing, distance)
                self.assertIsInstance(newPoint, SpherePoint)
                self.assertSpherePointsAlmostEqual(point2, newPoint)
                if newPoint.atPole():
                    self.assertAnglesAlmostEqual(newPoint.getLongitude(), 0*degrees)

                # measuring the separation and bearing from point1 to the new point
                # should produce the requested separation and bearing
                measuredDistance = point1.separation(newPoint)
                self.assertAnglesAlmostEqual(measuredDistance, distance)
                if abs(measuredDistance.asDegrees() - 180) > 1e-5:
                    # The two points are not opposite each other on the sphere,
                    # so the bearing has a well defined value
                    measuredBearing = point1.bearingTo(newPoint)
                    self.assertAnglesAlmostEqual(measuredBearing, bearing)

                # offset by a negative amount in the opposite direction should produce the same result
                newPoint2 = point1.offset(bearing + 180 * degrees, -distance)
                self.assertIsInstance(newPoint2, SpherePoint)
                # check angular separation (longitude is checked below)
                self.assertSpherePointsAlmostEqual(newPoint, newPoint2)

                if point1.isFinite() and point2.isFinite():
                    if not point2.atPole():
                        self.assertAnglesAlmostEqual(
                            point2.getLongitude(), newPoint.getLongitude())
                        self.assertAnglesAlmostEqual(
                            point2.getLongitude(), newPoint2.getLongitude())
                    self.assertAnglesAlmostEqual(
                        point2.getLatitude(), newPoint.getLatitude())
                    self.assertAnglesAlmostEqual(
                        point2.getLatitude(), newPoint2.getLatitude())
                else:
                    self.assertTrue(math.isnan(
                        newPoint.getLongitude().asRadians()))
                    self.assertTrue(math.isnan(
                        newPoint2.getLongitude().asRadians()))
                    self.assertTrue(math.isnan(
                        newPoint.getLatitude().asRadians()))
                    self.assertTrue(math.isnan(
                        newPoint2.getLatitude().asRadians()))

        # Test precision near the poles
        lon = 123.0*degrees
        almostPole = SpherePoint(lon, self.nextDown(90.0*degrees))
        goSouth = almostPole.offset(-90.0*degrees, 90.0*degrees)
        self.assertAnglesAlmostEqual(lon, goSouth.getLongitude())
        self.assertAnglesAlmostEqual(0.0*degrees, goSouth.getLatitude())
        goEast = almostPole.offset(0.0*degrees, 90.0*degrees)
        self.assertAnglesAlmostEqual(lon + 90.0*degrees, goEast.getLongitude())
        self.assertAnglesAlmostEqual(0.0*degrees, goEast.getLatitude())
    def testRotatedValue(self):
        """Test if rotated() returns the expected value.
        """
        # Try rotating about the equatorial pole (ie. along a parallel).
        longitude = 90.0
        latitudes = [0.0, 30.0, 60.0]
        arcLen = 10.0
        pole = SpherePoint(0.0*degrees, 90.0*degrees)
        for latitude in latitudes:
            point = SpherePoint(longitude*degrees, latitude*degrees)
            newPoint = point.rotated(pole, arcLen*degrees)

            self.assertIsInstance(newPoint, SpherePoint)
            self.assertAlmostEqual(
                longitude + arcLen, newPoint.getLongitude().asDegrees())
            self.assertAlmostEqual(
                latitude, newPoint.getLatitude().asDegrees())

        # Try with pole = vernal equinox and rotate up the 90 degree meridian.
        pole = SpherePoint(0.0*degrees, 0.0*degrees)
        for latitude in latitudes:
            point = SpherePoint(longitude*degrees, latitude*degrees)
            newPoint = point.rotated(pole, arcLen*degrees)

            self.assertAlmostEqual(
                longitude, newPoint.getLongitude().asDegrees())
            self.assertAlmostEqual(
                latitude + arcLen, newPoint.getLatitude().asDegrees())

        # Test accuracy close to coordinate pole
        point = SpherePoint(90.0*degrees, np.nextafter(90.0, -inf)*degrees)
        newPoint = point.rotated(pole, 90.0*degrees)
        self.assertAlmostEqual(270.0, newPoint.getLongitude().asDegrees())
        self.assertAlmostEqual(90.0 - np.nextafter(90.0, -inf),
                               newPoint.getLatitude().asDegrees())

        # Generic pole; can't predict position, but test for rotation
        # invariant.
        pole = SpherePoint(283.5*degrees, -23.6*degrees)
        for lon, lat in self._dataset:
            point = SpherePoint(lon, lat)
            dist = point.separation(pole)
            newPoint = point.rotated(pole, -32.4*geom.radians)

            self.assertNotAlmostEqual(point.getLongitude().asDegrees(),
                                      newPoint.getLongitude().asDegrees())
            self.assertNotAlmostEqual(point.getLatitude().asDegrees(),
                                      newPoint.getLatitude().asDegrees())
            self.assertAnglesAlmostEqual(dist, newPoint.separation(pole))

        # Non-finite values give undefined rotations
        for latitude in latitudes:
            point = SpherePoint(longitude*degrees, latitude*degrees)
            nanPoint = point.rotated(pole, nan*degrees)
            infPoint = point.rotated(pole, inf*degrees)

            self.assertTrue(math.isnan(nanPoint.getLongitude().asRadians()))
            self.assertTrue(math.isnan(nanPoint.getLatitude().asRadians()))
            self.assertTrue(math.isnan(infPoint.getLongitude().asRadians()))
            self.assertTrue(math.isnan(infPoint.getLatitude().asRadians()))

        # Non-finite points rotate into non-finite points
        for point in [
            SpherePoint(-inf*degrees, 1.0*radians),
            SpherePoint(32.0*degrees, nan*radians),
        ]:
            newPoint = point.rotated(pole, arcLen*degrees)
            self.assertTrue(math.isnan(nanPoint.getLongitude().asRadians()))
            self.assertTrue(math.isnan(nanPoint.getLatitude().asRadians()))
            self.assertTrue(math.isnan(infPoint.getLongitude().asRadians()))
            self.assertTrue(math.isnan(infPoint.getLatitude().asRadians()))

        # Rotation around non-finite poles undefined
        for latitude in latitudes:
            point = SpherePoint(longitude*degrees, latitude*degrees)
            for pole in [
                SpherePoint(-inf*degrees, 1.0*radians),
                SpherePoint(32.0*degrees, nan*radians),
            ]:
                newPoint = point.rotated(pole, arcLen*degrees)
                self.assertTrue(math.isnan(
                    nanPoint.getLongitude().asRadians()))
                self.assertTrue(math.isnan(nanPoint.getLatitude().asRadians()))
                self.assertTrue(math.isnan(
                    infPoint.getLongitude().asRadians()))
                self.assertTrue(math.isnan(infPoint.getLatitude().asRadians()))
    def testVector3dConstructor(self):
        # test poles
        for z in (-11.3, -1.1, 0.1, 2.5):  # arbitrary non-zero values
            sp = SpherePoint(lsst.sphgeom.Vector3d(0.0, 0.0, z))
            self.assertTrue(sp.atPole())
            self.assertEqual(sp.getLongitude().asRadians(), 0.0)
            if z < 0:
                self.assertAnglesAlmostEqual(sp.getLatitude(), -90 * degrees)
            else:
                self.assertAnglesAlmostEqual(sp.getLatitude(), 90 * degrees)

        spx = SpherePoint(lsst.sphgeom.Vector3d(11.1, 0.0, 0.0))
        self.assertAnglesAlmostEqual(spx.getLongitude(), 0.0 * degrees)
        self.assertAnglesAlmostEqual(spx.getLatitude(), 0.0 * degrees)

        spy = SpherePoint(lsst.sphgeom.Vector3d(0.0, 234234.5, 0.0))
        self.assertAnglesAlmostEqual(spy.getLongitude(), 90.0 * degrees)
        self.assertAnglesAlmostEqual(spy.getLatitude(), 0.0 * degrees)

        spxy = SpherePoint(lsst.sphgeom.Vector3d(7.5, -7.5, 0.0))
        self.assertAnglesAlmostEqual(spxy.getLongitude(), -45.0 * degrees)
        self.assertAnglesAlmostEqual(spxy.getLatitude(), 0.0 * degrees)

        spxz = SpherePoint(lsst.sphgeom.Vector3d(100.0, 0.0, -100.0))
        self.assertAnglesAlmostEqual(spxz.getLongitude(), 0.0 * degrees)
        self.assertAnglesAlmostEqual(spxz.getLatitude(), -45.0 * degrees)

        # Only one singularity: a vector of all zeros
        with self.assertRaises(pexEx.InvalidParameterError):
            SpherePoint(lsst.sphgeom.Vector3d(0.0, 0.0, 0.0))