Esempio n. 1
0
        /// <summary>
        /// Vincenty のアルゴリズムで経緯度 origin から距離 distance だけ角度 angle の方位へ射影した経緯度を計算するよ
        /// </summary>
        /// <param name="origin">基点とする位置</param>
        /// <param name="distance">射影先までの距離</param>
        /// <param name="angle">射影する方位。北=0deg, 西=+90deg, 東=-90deg, 南=180deg。 atans(y,x) 的には 北向き=+x, 西向き=+y の系</param>
        /// <param name="planet">惑星。null の場合は WGS84</param>
        /// <returns>射影先の経緯度</returns>
        public static LonLat ProjectionTo
            (this ILonLatGettable origin
            , Length distance
            , PlaneAngle angle
            , IGeometricalSpecificationGettable planet = null
            )
        {
            planet = planet ?? GeometricalSpecification.Earth_WGS84;

            var SinAlphpa1 = angle.Sin();
            var CosAlpha1  = angle.Cos();

            var TanU1      = (1 - planet.Flattening) * origin.Latitude.Tan();
            var CosU1      = 1 / Math.Sqrt(1 + TanU1 * TanU1);
            var SinU1      = TanU1 * CosU1;
            var Sigma1     = Math.Atan2(TanU1, CosAlpha1);
            var SinAlpha   = CosU1 * SinAlphpa1;
            var CosSqAlpha = 1 - SinAlpha * SinAlpha;

            var ERSq = planet.EquatorialRadius._m * planet.EquatorialRadius._m;
            var ARSq = planet.AxialRadius._m * planet.AxialRadius._m;

            var uSq = CosSqAlpha * (ERSq - ARSq) / ARSq;
            var A   = 1 + uSq / 16384 * (4096 + uSq * (-768 + uSq * (320 - 175 * uSq)));
            var B   = uSq / 1024 * (256 + uSq * (-128 + uSq * (74 - 47 * uSq)));

            var Sigma      = distance._m / (planet.AxialRadius._m * A);
            var SigmaP     = 0.0;
            var SinSigma   = 0.0;
            var CosSigma   = 0.0;
            var Cos2SigmaM = 0.0;

            do
            {
                Cos2SigmaM = Math.Cos(2 * Sigma1 + Sigma);
                SinSigma   = Math.Sin(Sigma);
                CosSigma   = Math.Cos(Sigma);
                var DeltaSigma =
                    B * SinSigma * (Cos2SigmaM + B / 4 * (CosSigma * (-1 + 2 * Cos2SigmaM * Cos2SigmaM) -
                                                          B / 6 * Cos2SigmaM * (-3 + 4 * SinSigma * SinSigma) * (-3 + 4 * Cos2SigmaM * Cos2SigmaM)))
                ;
                SigmaP = Sigma;
                Sigma  = distance._m / (planet.AxialRadius._m * A) + DeltaSigma;
            } while (Math.Abs(Sigma - SigmaP) > 1e-12);

            var tmp    = SinU1 * SinSigma - CosU1 * CosSigma * CosAlpha1;
            var Phi2   = PlaneAngle.FromRadians(Math.Atan2(SinU1 * CosSigma + CosU1 * SinSigma * CosAlpha1, (1 - planet.Flattening) * Math.Sqrt(SinAlpha * SinAlpha + tmp * tmp)));
            var Labmda = Math.Atan2(SinSigma * SinAlphpa1, CosU1 * CosSigma - SinU1 * SinSigma * CosAlpha1);
            var C      = planet.Flattening / 16 * CosSqAlpha * (4 + planet.Flattening * (4 - 3 * CosSqAlpha));
            var L      = Labmda - (1 - C) * planet.Flattening * SinAlpha *
                         (Sigma + C * SinSigma * (Cos2SigmaM + C * CosSigma * (-1 + 2 * Cos2SigmaM * Cos2SigmaM)));
            var Lambda2 = PlaneAngle.FromRadians((origin.Longitude._rad + L + 3 * Math.PI) % (2 * Math.PI) - Math.PI);  // normalise to -180...+180

            //var revAz = Math.Atan2( SinAlpha, -tmp );
            return(new LonLat(Lambda2, Phi2));
        }
Esempio n. 2
0
 /// <summary>
 /// 経緯度の位置 a, b の惑星を楕円体近似した場合の距離が
 /// 許容範囲 tolerance 以下か判定
 /// </summary>
 /// <param name="a">経緯度の位置1つめ</param>
 /// <param name="b">経緯度の位置2つめ</param>
 /// <param name="tolerance">許容範囲</param>
 /// <param name="llda">経緯度の距離を計算するアルゴリズム</param>
 /// <param name="planet">楕円体近似した惑星の形状定義; null の場合は <see cref="GeometricalSpecification.Earth_WGS84"/> が採用されます </param>
 /// <returns>許容範囲以下なら true</returns>
 public static bool NearlyEqualsTo
     (this ILonLatGettable a
     , ILonLatGettable b
     , Length tolerance                         = null
     , LonLatDistanceAlgorithm llda             = LonLatDistanceAlgorithm.Haversine
     , IGeometricalSpecificationGettable planet = null
     )
 {
     return(a.DistanceTo(b, llda, planet) <= (tolerance ?? Length.Meter));
 }
Esempio n. 3
0
        /// <summary>
        /// 経緯度の位置 a と b の緯度方向の距離を計算する
        /// </summary>
        /// <param name="a">経緯度の位置1つめ</param>
        /// <param name="b">経緯度の位置2つめ</param>
        /// <param name="llda">経緯度の距離を計算するアルゴリズム</param>
        /// <param name="planet">楕円体近似した惑星の形状定義; null の場合は <see cref="GeometricalSpecification.Earth_WGS84"/> が採用されます </param>
        /// <returns>緯度方向の距離</returns>
        public static Length LatitudeDistanceTo
            (this ILonLatGettable a
            , ILonLatGettable b
            , LonLatDistanceAlgorithm llda             = LonLatDistanceAlgorithm.Haversine
            , IGeometricalSpecificationGettable planet = null
            )
        {
            var aa = new LonLat(PlaneAngle.Zero, a.Latitude);
            var bb = new LonLat(PlaneAngle.Zero, b.Latitude);

            return(aa.DistanceTo(bb, llda, planet));
        }
Esempio n. 4
0
        /// <summary>
        /// 経緯度の位置 a と b の中間の緯度における経度方向の距離を計算する
        /// </summary>
        /// <param name="a">経緯度の位置1つめ</param>
        /// <param name="b">経緯度の位置2つめ</param>
        /// <param name="llda">経緯度の距離を計算するアルゴリズム</param>
        /// <param name="planet">楕円体近似した惑星の形状定義; null の場合は <see cref="GeometricalSpecification.Earth_WGS84"/> が採用されます </param>
        /// <returns>経度方向の距離</returns>
        public static Length LongitudeDistanceTo
            (this ILonLatGettable a
            , ILonLatGettable b
            , LonLatDistanceAlgorithm llda             = LonLatDistanceAlgorithm.Haversine
            , IGeometricalSpecificationGettable planet = null
            )
        {
            var alat = (a.Latitude + b.Latitude) / 2.0;
            var aa   = new LonLat(a.Longitude, alat);
            var bb   = new LonLat(b.Longitude, alat);

            return(aa.DistanceTo(bb, llda, planet));
        }
Esempio n. 5
0
        /// <summary>
        /// DistanceTo の Haversine アルゴリズム版の実装詳細
        /// </summary>
        /// <param name="a">経緯度の位置1つめ</param>
        /// <param name="b">経緯度の位置2つめ</param>
        /// <param name="planet">楕円体近似した惑星の形状定義; null の場合は <see cref="GeometricalSpecification.Earth_WGS84"/> が採用されます </param>
        /// <returns>距離</returns>
        internal static Length DistanceToHaversine
            (this ILonLatGettable a
            , ILonLatGettable b
            , IGeometricalSpecificationGettable planet
            )
        {
            var r = planet.EquatorialRadius;

            var dlat = b.Latitude - a.Latitude;
            var dlon = b.Longitude - a.Longitude;

            var p =
                (dlat / 2.0).Sin() * (dlat / 2.0).Sin()
                + a.Latitude.Cos() * b.Latitude.Cos()
                * (dlon / 2.0).Sin() * (dlon / 2.0).Sin()
            ;

            var q = 2.0 * Math.Atan2(Math.Sqrt(p), Math.Sqrt(1.0 - p));

            return(r * q);
        }
Esempio n. 6
0
        /// <summary>
        /// 2つの経緯度 a, b について、
        /// 楕円体近似した惑星の形状定義 planet 上における
        /// 惑星表面上の地点間の距離を長さ次元で計算します
        /// </summary>
        /// <param name="a">経緯度の位置1つめ</param>
        /// <param name="b">経緯度の位置2つめ</param>
        /// <param name="llda">経緯度の距離を計算するアルゴリズム</param>
        /// <param name="planet">楕円体近似した惑星の形状定義; null の場合は <see cref="GeometricalSpecification.Earth_WGS84"/> が採用されます </param>
        /// <returns>距離</returns>
        public static Length DistanceTo
            (this ILonLatGettable a
            , ILonLatGettable b
            , LonLatDistanceAlgorithm llda             = LonLatDistanceAlgorithm.Haversine
            , IGeometricalSpecificationGettable planet = null
            )
        {
            planet = planet ?? GeometricalSpecification.Earth_WGS84;

            switch (llda)
            {
            case LonLatDistanceAlgorithm.Haversine:
                return(a.DistanceToHaversine(b, planet));

            case LonLatDistanceAlgorithm.Vincenty:
                return(a.DistanceToVincenty(b, planet));

            default:
                throw new NotImplementedException();
            }
        }
Esempio n. 7
0
        /// <summary>
        /// DistanceTo の Vincenty アルゴリズム版の実装詳細
        /// </summary>
        /// <param name="a">経緯度の位置1つめ</param>
        /// <param name="b">経緯度の位置2つめ</param>
        /// <param name="planet">楕円体近似した惑星の形状定義; null の場合は <see cref="GeometricalSpecification.Earth_WGS84"/> が採用されます </param>
        /// <returns>距離</returns>
        internal static Length DistanceToVincenty
            (this ILonLatGettable a
            , ILonLatGettable b
            , IGeometricalSpecificationGettable planet
            )
        {
            var L     = b.Longitude - a.Longitude;
            var U1    = Math.Atan((1.0 - planet.Flattening) * a.Latitude.Tan());
            var U2    = Math.Atan((1.0 - planet.Flattening) * b.Latitude.Tan());
            var SinU1 = Math.Sin(U1);
            var CosU1 = Math.Cos(U1);
            var SinU2 = Math.Sin(U2);
            var CosU2 = Math.Cos(U2);

            var Lambda  = L;
            var LambdaP = PlaneAngle.Zero;

            var IterationLimit = 256;

            var CosSqAlpha = 0.0;
            var Cos2SigmaM = 0.0;
            var CosSigma   = 0.0;
            var SinSigma   = 0.0;
            var Sigma      = 0.0;

            do
            {
                var SinLambda = Lambda.Sin();
                var CosLambda = Lambda.Cos();
                SinSigma =
                    Math.Sqrt
                        ((CosU2 * SinLambda) *
                        (CosU2 * SinLambda) +
                        (CosU1 * SinU2 - SinU1 * CosU2 * CosLambda) *
                        (CosU1 * SinU2 - SinU1 * CosU2 * CosLambda)
                        );

                if (SinSigma == 0)
                {
                    return(Length.Zero);
                }

                CosSigma = SinU1 * SinU2 + CosU1 * CosU2 * CosLambda;
                Sigma    = Math.Atan2(SinSigma, CosSigma);
                var SinAlpha = CosU1 * CosU2 * SinLambda / SinSigma;
                CosSqAlpha = 1 - SinAlpha * SinAlpha;
                Cos2SigmaM = CosSigma - 2 * SinU1 * SinU2 / CosSqAlpha;

                if (double.IsNaN(Cos2SigmaM))
                {
                    Cos2SigmaM = 0;
                }

                var C = planet.Flattening / 16.0 * CosSqAlpha * (4.0 + planet.Flattening * (4.0 - 3.0 * CosSqAlpha));
                LambdaP = Lambda;

                Lambda =
                    L
                    + PlaneAngle.FromRadians(1.0 - C)
                    * planet.Flattening
                    * SinAlpha
                    * (Sigma + C * SinSigma *
                       (Cos2SigmaM + C * CosSigma *
                        (-1.0 + 2.0 * Cos2SigmaM * Cos2SigmaM
                        )
                       )
                       )
                ;
            }while
            (!Lambda.NearlyEquals(LambdaP, PlaneAngle.FromRadians(0.5e-12)) &&
             --IterationLimit > 0
            );

            if (IterationLimit == 0)
            {
                return(Length.NaN);
            }

            var ERSq = planet.EquatorialRadius._m * planet.EquatorialRadius._m;
            var ARSq = planet.AxialRadius._m * planet.AxialRadius._m;

            var uSq = CosSqAlpha * (ERSq - ARSq) / ARSq;

            var A = 1.0 + uSq / 16384.0 * (4096.0 + uSq * (-768.0 + uSq * (320.0 - 175.0 * uSq)));
            var B = uSq / 1024.0 * (256.0 + uSq * (-128.0 + uSq * (74.0 - 47.0 * uSq)));

            var DeltaSigma =
                B
                * SinSigma
                * (Cos2SigmaM + B / 4.0 *
                   (CosSigma
                    * (-1 + 2 * Cos2SigmaM * Cos2SigmaM)
                    - B / 6 * Cos2SigmaM
                    * (-3 + 4 * SinSigma * SinSigma)
                    * (-3 + 4 * Cos2SigmaM * Cos2SigmaM)
                   )
                   )
            ;

            return(Length.From_m(planet.AxialRadius._m * A * (Sigma - DeltaSigma)));
        }