Esempio n. 1
0
        /// <summary>
        /// The great circle distance in radians between two spherical coordinates.
        /// This function uses the Haversine formula.
        /// For math details, see:
        ///     https://en.wikipedia.org/wiki/Haversine_formula
        ///     https://www.movable-type.co.uk/scripts/latlong.html
        /// </summary>
        /// <param name="a">the first lat/lng pair (in radians)</param>
        /// <param name="b">the second lat/lng pair (in radians)</param>
        /// <returns>
        /// the great circle distance in radians between a and b
        /// </returns>
        /// <!--
        /// geoCoord.c
        /// double H3_EXPORT(pointDistRads)
        /// -->
        public static decimal DistanceToRadians(this GeoCoord a, GeoCoord b)
        {
            decimal sinLat = DecimalEx.Sin((b.Latitude - a.Latitude) / 2.0m);
            decimal sinLng = DecimalEx.Sin((b.Longitude - a.Longitude) / 2.0m);
            decimal p      = sinLat * sinLat + DecimalEx.Cos(a.Latitude) *
                             DecimalEx.Cos(b.Latitude) * sinLng * sinLng;

            return(2 * DecimalEx.ATan2(DecimalEx.Sqrt(p), DecimalEx.Sqrt(1 - p)));
        }
Esempio n. 2
0
 /// <summary>
 /// Determines the azimuth to p2 from p1 in radians
 /// </summary>
 /// <param name="p1">The first spherical coordinates</param>
 /// <param name="p2">The second spherical coordinates</param>
 /// <returns>The azimuth in radians from p1 to p2</returns>
 /// <!--
 /// geoCoord.c
 /// double _geoAzimuthRads
 /// -->
 internal static decimal AzimuthRadiansTo(this GeoCoord p1, GeoCoord p2)
 {
     return
         (DecimalEx.ATan2
          (
              DecimalEx.Cos(p2.Latitude) * DecimalEx.Sin(p2.Longitude - p1.Longitude),
              DecimalEx.Cos(p1.Latitude) * DecimalEx.Sin(p2.Latitude) -
              DecimalEx.Sin(p1.Latitude) * DecimalEx.Cos(p2.Latitude) *
              DecimalEx.Cos(p2.Longitude - p1.Longitude)
          ));
 }
Esempio n. 3
0
        /// <summary>
        /// Determines the center point in spherical coordinates of a cell given by 2D
        /// hex coordinates on a particular icosahedral face.
        /// </summary>
        /// <param name="v">The 2D hex coordinates of the cell</param>
        /// <param name="face">The icosahedral face upon which the 2D hex coordinate system is centered</param>
        /// <param name="res">The H3 resolution of the cell</param>
        /// <param name="substrate">
        /// Indicates whether or not this grid is actually a substrate
        /// grid relative to the specified resolution.
        /// </param>
        /// <returns>The spherical coordinates of the cell center point</returns>
        /// <!--
        /// faceIjk.c
        /// void _hex2dToGeo
        /// -->
        public static GeoCoord ToGeoCoord(this Vec2d v, int face, int res, int substrate)
        {
            // calculate (r, theta) in hex2d
            decimal r          = v.Magnitude;
            bool    bSubstrate = substrate != 0;

            if (r < Constants.H3.EPSILON)
            {
                return(Constants.FaceIjk.FaceCenterGeo[face]);
            }

            decimal theta = DecimalEx.ATan2(v.Y, v.X);

            // scale for current resolution length u
            for (var i = 0; i < res; i++)
            {
                r /= Constants.FaceIjk.MSqrt7;
            }

            // scale accordingly if this is a substrate grid
            if (substrate != 0)
            {
                r /= 3.0m;
                if (res.IsResClassIii())
                {
                    r /= Constants.FaceIjk.MSqrt7;
                }
            }

            r *= Constants.H3.RES0_U_GNOMONIC;

            // perform inverse gnomonic scaling of r
            r = DecimalEx.ATan(r);

            // adjust theta for Class III
            // if a substrate grid, then it's already been adjusted for Class III
            if (!bSubstrate && res.IsResClassIii())
            {
                theta = (theta + Constants.H3.M_AP7_ROT_RADS).NormalizeRadians();
            }

            // find theta as an azimuth
            theta = (Constants.FaceIjk.FaceAxesAzRadsCii[face, 0] - theta).NormalizeRadians();

            // now find the point at (r,theta) from the face center
            return(Constants.FaceIjk.FaceCenterGeo[face]
                   .GetAzimuthDistancePoint(theta, r));
        }
Esempio n. 4
0
 public void Test(decimal y, decimal x, decimal expected, decimal tolerance)
 {
     tolerance = Helper.GetScaledTolerance(expected, (int)tolerance, true);
     Assert.That(DecimalEx.ATan2(y, x), Is.EqualTo(expected).Within(tolerance));
 }
Esempio n. 5
0
        /// <summary>
        /// Computes the point on the sphere a specified azimuth and distance from
        /// another point.
        /// </summary>
        /// <param name="p1">The first spherical coordinates.</param>
        /// <param name="azimuth">The desired azimuth from p1.</param>
        /// <param name="distance">The desired distance from p1, must be non-negative.</param>
        /// <returns>The spherical coordinates at the desired azimuth and distance from p1.</returns>
        /// <!--
        /// geoCoord.c
        /// void _geoAzDistanceRads
        /// -->
        internal static GeoCoord GetAzimuthDistancePoint(this GeoCoord p1, decimal azimuth, decimal distance)
        {
            if (distance < Constants.H3.EPSILON)
            {
                return(p1);
            }

            azimuth = azimuth.NormalizeRadians().ConstrainToPiAccuracy();
            var p2 = new GeoCoord();

            // check for due north/south azimuth
            if (azimuth < Constants.H3.EPSILON ||
                Math.Abs(azimuth - Constants.H3.M_PI) < Constants.H3.EPSILON)
            {
                if (azimuth < Constants.H3.EPSILON) // due north
                {
                    p2 = p2.SetLatitude(p1.Latitude + distance);
                }
                else // due south
                {
                    p2 = p2.SetLatitude(p1.Latitude - distance);
                }

                if (Math.Abs(p2.Latitude - Constants.H3.M_PI_2) < Constants.H3.EPSILON) // north pole
                {
                    p2 = new GeoCoord(Constants.H3.M_PI_2, 0.0m);
                }
                else if (Math.Abs(p2.Latitude + Constants.H3.M_PI_2) < Constants.H3.EPSILON) // south pole
                {
                    p2 = new GeoCoord(-Constants.H3.M_PI_2, 0.0m);
                }
                else
                {
                    p2 = p2.SetLongitude(p1.Longitude.ConstrainLongitude());
                }
            }
            else // Not due north or south
            {
                decimal sinLatitude = DecimalEx.Sin(p1.Latitude) * DecimalEx.Cos(distance) +
                                      DecimalEx.Cos(p1.Latitude) * DecimalEx.Sin(distance) * DecimalEx.Cos(azimuth);
                sinLatitude = sinLatitude.ConstrainToPiAccuracy();
                if (sinLatitude > 1.0m)
                {
                    sinLatitude = 1.0m;
                }

                if (sinLatitude < -1.0m)
                {
                    sinLatitude = 1.0m;
                }

                p2 = p2.SetLatitude(DecimalEx.ASin(sinLatitude).ConstrainToPiAccuracy());

                if (Math.Abs(p2.Latitude - Constants.H3.M_PI_2) < Constants.H3.EPSILON) // north pole
                {
                    p2 = new GeoCoord(Constants.H3.M_PI_2, 0.0m);
                }
                else if (Math.Abs(p2.Latitude + Constants.H3.M_PI_2) < Constants.H3.EPSILON) // south pole
                {
                    p2 = new GeoCoord(-Constants.H3.M_PI_2, 0.0m);
                }
                else
                {
                    decimal sinLongitude = DecimalEx.Sin(azimuth) * DecimalEx.Sin(distance) / DecimalEx.Cos(p2.Latitude);
                    decimal cosLongitude = (DecimalEx.Cos(distance) - DecimalEx.Sin(p1.Latitude) * DecimalEx.Sin(p2.Latitude)) /
                                           DecimalEx.Cos(p1.Latitude) / DecimalEx.Cos(p2.Latitude);
                    if (sinLongitude > 1.0m)
                    {
                        sinLongitude = 1.0m;
                    }

                    if (sinLongitude < -1.0m)
                    {
                        sinLongitude = -1.0m;
                    }

                    if (cosLongitude > 1.0m)
                    {
                        cosLongitude = 1.0m;
                    }

                    if (cosLongitude < -1.0m)
                    {
                        cosLongitude = -1.0m;
                    }

                    p2 = p2.SetLongitude
                         (
                        (p1.Longitude + DecimalEx.ATan2(sinLongitude, cosLongitude))
                        .ConstrainLongitude().ConstrainToPiAccuracy()
                         );
                }
            }

            return(p2);
        }