Beispiel #1
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)
    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 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 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 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 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 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 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 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 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 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())