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))
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))
def testToUnitXZY(self): """Test that the numpy-vectorized transformation from (lat, lon) to (x, y, z) matches SpherePoint.getVector(). """ for units in (degrees, radians): scale = float(180.0 * degrees) / float(1.0 * units) lon = scale * np.random.rand(5, 3) lat = scale * (np.random.rand(5, 3) - 0.5) x, y, z = SpherePoint.toUnitXYZ(longitude=lon, latitude=lat, units=units) for i in range(lon.shape[0]): for j in range(lon.shape[1]): s = SpherePoint(lon[i, j], lat[i, j], units) u1 = s.getVector() u2 = lsst.sphgeom.UnitVector3d(x=x[i, j], y=y[i, j], z=z[i, j]) self.assertFloatsAlmostEqual(np.array(u1, dtype=float), np.array(u2, dtype=float))
def computeAzAltFromBasePupil(vectorBase, vectorPupil): """Compute az/alt from a vector in the base frame and the same vector in the pupil frame. Parameters ---------- vectorBase : `iterable` of three `float` 3-dimensional vector in the :ref:`base frame <lsst.cbp.base_frame>`. vectorPupil : `iterable` of `float` The same vector in the :ref:`pupil frame <lsst.cbp.pupil_frame>`. This vector should be within 45 degrees or so of the optical axis for accurate results. Returns ------- pupilAzAlt : `lsst.geom.SpherePoint` Pointing of the pupil frame as :ref:`internal azimuth, altitude <lsst.cbp.internal_angles>`. Raises ------ ValueError If vectorPupil x <= 0 Notes ----- The magnitude of each vector is ignored, except that a reasonable magnitude is required in order to compute an accurate unit vector. """ if vectorPupil[0] <= 0: raise ValueError("vectorPupil x must be > 0: {}".format(vectorPupil)) # Compute telescope altitude using: # # base z = sin(alt) pupil x + cos(alt) pupil z # # One way to derive this is from the last row of an Euler rotation # matrix listed in the comments for convertVectorFromBaseToPupil. spBase = SpherePoint(Vector3d(*vectorBase)) spPupil = SpherePoint(Vector3d(*vectorPupil)) xb, yb, zb = spBase.getVector() xp, yp, zp = spPupil.getVector() factor = 1 / math.fsum((xp**2, zp**2)) addend1 = xp * zb addend2 = zp * math.sqrt(math.fsum((xp**2, zp**2, -zb**2))) if zp == 0: sinAlt = zb / xp else: sinAlt = factor * (addend1 - addend2) alt = math.asin(sinAlt) * radians # Consider the spherical triangle connecting the telescope pointing # (pupil frame x axis), the vector, and zenith (the base frame z axis). # The length of all sides is known: # - sideA is the side connecting the vector to the pupil frame x axis # (since 0, 0 is a unit vector pointing along pupil frame x); # - sideB is the side connecting telescope pointing to the zenith # - sideC is the side connecting the vector to the zenith # # Solve for angleA, the angle between the sides at the zenith; # that angle is the difference in azimuth between the telescope pointing # and the azimuth of the base vector. sideA = SpherePoint(0, 0, radians).separation(spPupil).asRadians() sideB = math.pi / 2 - alt.asRadians() sideC = math.pi / 2 - spBase[1].asRadians() # sideA can be small or zero so use a half angle formula # sides B and C will always be well away from 0 and 180 degrees semiPerimeter = 0.5 * math.fsum((sideA, sideB, sideC)) sinHalfAngleA = math.sqrt( math.sin(semiPerimeter - sideB) * math.sin(semiPerimeter - sideC) / (math.sin(sideB) * math.sin(sideC))) daz = 2 * math.asin(sinHalfAngleA) * radians if spPupil[0].wrapCtr() > 0: daz = -daz az = spBase[0] + daz global _RecordError if _RecordError: # to study sources of numerical imprecision global _ErrorLimitArcsec, _ErrorList sp = SpherePoint(az, alt) vectorBaseRT = convertVectorFromPupilToBase(vectorPupil, sp) errorArcsec = SpherePoint(Vector3d(*vectorBaseRT)).separation( SpherePoint(Vector3d(*vectorBase))).asArcseconds() if errorArcsec > _ErrorLimitArcsec: _ErrorList.append((errorArcsec, vectorBase, vectorPupil)) return SpherePoint(az, alt)
def trim(row): coord = SpherePoint(row[self.config.raColName], row[self.config.decColName], radians) return region.contains(coord.getVector())