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)
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)
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
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)
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)
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
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)
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()))
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))
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 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 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))
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))
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:])
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
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)
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
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)
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 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)
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 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 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))