示例#1
0
        /// <summary>
        /// The general position routine. <see cref="Position(double, out double, out double)"/> is defined in term so this function.
        /// </summary>
        /// <param name="s12">distance between point 1 and point 2 (meters); it can be negative.</param>
        /// <param name="outmask">a bitor'ed combination of <see cref="GeodesicFlags"/> values specifying which of the following parameters should be set.</param>
        /// <param name="lat2">latitude of point 2 (degrees).</param>
        /// <param name="lon2">longitude of point 2 (degrees).</param>
        /// <param name="S12">area under the rhumb line (meters^2).</param>
        /// <remarks>
        /// The <see cref="GeodesicFlags"/> values possible for <paramref name="outmask"/> are
        /// <list type="bullet">
        /// <item>outmask |= <see cref="GeodesicFlags.Latitude"/> for the latitude <paramref name="lat2"/>;</item>
        /// <item>outmask |= <see cref="GeodesicFlags.Longitude"/> for the longitude <paramref name="lon2"/>;</item>
        /// <item>outmask |= <see cref="GeodesicFlags.Area"/> for the area <paramref name="S12"/>;</item>
        /// <item>outmask |= <see cref="GeodesicFlags.All"/> for all of the above;</item>
        /// <item>outmask |= <see cref="GeodesicFlags.LongUnroll"/> to unroll <paramref name="lon2"/> instead of wrapping it into the range [−180°, 180°].</item>
        /// </list>
        /// With the <see cref="GeodesicFlags.LongUnroll"/> bit set, the quantity <paramref name="lon2"/> − <i>lon1</i> indicates
        /// how many times and in what sense the rhumb line encircles the ellipsoid.
        /// <para>
        /// If s12 is large enough that the rhumb line crosses a pole,
        /// the longitude of point 2 is indeterminate (a <see cref="double.NaN"/> is returned for <paramref name="lon2"/> and <paramref name="S12"/>).
        /// </para>
        /// </remarks>
        public void GenPosition(double s12, GeodesicFlags outmask, out double lat2, out double lon2, out double S12)
        {
            double
                mu12 = s12 * _calp * 90 / _rh._ell.QuarterMeridian,
                mu2 = _mu1 + mu12;
            double psi2, lat2x, lon2x;

            lat2 = lon2 = S12 = double.NaN;

            if (Abs(mu2) <= 90)
            {
                if (_calp != 0)
                {
                    lat2x = _rh._ell.InverseRectifyingLatitude(mu2);
                    var psi12 = _rh.DRectifyingToIsometric(mu2 * Degree,
                                                           _mu1 * Degree) * mu12;
                    lon2x = _salp * psi12 / _calp;
                    psi2  = _psi1 + psi12;
                }
                else
                {
                    lat2x = _lat1;
                    lon2x = _salp * s12 / (_r1 * Degree);
                    psi2  = _psi1;
                }
                if (outmask.HasFlag(GeodesicFlags.Area))
                {
                    S12 = _rh._c2 * lon2x *
                          _rh.MeanSinXi(_psi1 * Degree, psi2 * Degree);
                }
                lon2x = outmask.HasFlag(GeodesicFlags.LongUnroll) ? _lon1 + lon2x :
                        AngNormalize(AngNormalize(_lon1) + lon2x);
            }
            else
            {
                // Reduce to the interval [-180, 180)
                mu2 = AngNormalize(mu2);
                // Deal with points on the anti-meridian
                if (Abs(mu2) > 90)
                {
                    mu2 = AngNormalize(180 - mu2);
                }
                lat2x = _rh._ell.InverseRectifyingLatitude(mu2);
                lon2x = double.NaN;
                if (outmask.HasFlag(GeodesicFlags.Area))
                {
                    S12 = double.NaN;
                }
            }
            if (outmask.HasFlag(GeodesicFlags.Latitude))
            {
                lat2 = lat2x;
            }
            if (outmask.HasFlag(GeodesicFlags.Longitude))
            {
                lon2 = lon2x;
            }
        }
示例#2
0
        /// <summary>
        /// Constructor for <see cref="PolygonArea{T}"/>.
        /// </summary>
        /// <param name="earth">the <see cref="IGeodesicLike"/> object to use for geodesic calculations.</param>
        /// <param name="polyline">
        /// if <see langword="true"/> that treat the points as defining a polyline instead of a polygon (default = <see langword="false"/>).
        /// </param>
        public PolygonArea(T earth, bool polyline = false)
        {
            _earth    = earth;
            _area0    = _earth.EllipsoidArea;
            _polyline = polyline;
            _mask     = GeodesicFlags.Latitude | GeodesicFlags.Longitude | GeodesicFlags.Distance |
                        (_polyline ? GeodesicFlags.None : GeodesicFlags.Area | GeodesicFlags.LongUnroll);

            Clear();
        }
示例#3
0
 /// <summary>
 /// Compute the position of point 2 which is a distance <paramref name="s12"/> (meters) from point 1.
 /// </summary>
 /// <param name="s12">distance between point 1 and point 2 (meters); it can be negative.</param>
 /// <param name="outmask">
 /// a bitor'ed combination of <see cref="GeodesicFlags"/> values specifying
 /// which of the properties in returned <see cref="DirectRhumbResult"/> instance should be set.
 /// </param>
 /// <returns>A <see cref="DirectRhumbResult"/> instance containing the result of the calcutation.</returns>
 /// <remarks>
 /// The <see cref="GeodesicFlags"/> values possible for <paramref name="outmask"/> are
 /// <list type="bullet">
 /// <item><i>outmask</i> |= <see cref="GeodesicFlags.Latitude"/> for the latitude returned in <see cref="DirectRhumbResult.Latitude"/>;</item>
 /// <item><i>outmask</i> |= <see cref="GeodesicFlags.Longitude"/> for the longitude returned in  <see cref="DirectRhumbResult.Longitude"/>;</item>
 /// <item><i>outmask</i> |= <see cref="GeodesicFlags.Area"/> for the area returned in  <see cref="RhumbResult.Area"/>;</item>
 /// <item><i>outmask</i> |= <see cref="GeodesicFlags.All"/> for all of the above;</item>
 /// <item><i>outmask</i> |= <see cref="GeodesicFlags.LongUnroll"/> to unroll <see cref="DirectRhumbResult.Longitude"/> instead of wrapping it into the range [−180°, 180°].</item>
 /// </list>
 /// With the <see cref="GeodesicFlags.LongUnroll"/> bit set, the quantity <see cref="DirectRhumbResult.Longitude"/> − <i>lon1</i> indicates
 /// how many times and in what sense the rhumb line encircles the ellipsoid.
 /// <para>
 /// If <paramref name="s12"/> is large enough that the rhumb line crosses a pole,
 /// the longitude of point 2 is indeterminate (a <see cref="double.NaN"/> is returned for <see cref="DirectRhumbResult.Longitude"/> and <see cref="RhumbResult.Area"/>).
 /// </para>
 /// </remarks>
 public DirectRhumbResult Position(double s12, GeodesicFlags outmask = GeodesicFlags.All)
 {
     GenPosition(s12, outmask, out var lat2, out var lon2, out var S12);
     return(new DirectRhumbResult
     {
         Latitude = lat2,
         Longitude = lon2,
         Area = S12
     });
 }
示例#4
0
 internal GeodesicLine(Geodesic g,
                       double lat1, double lon1,
                       double azi1, double salp1, double calp1,
                       GeodesicFlags caps, bool arcmode, double s13_a13)
 {
     LineInit(g, lat1, lon1, azi1, salp1, calp1, caps,
              ref tiny_, ref _lat1, ref _lon1, ref _azi1, ref _salp1, ref _calp1, ref _a, ref _f, ref _b, ref _c2, ref _f1,
              ref _dn1, ref _salp0, ref _calp0, ref _ssig1, ref _somg1, ref _csig1, ref _comg1, ref _k2, ref _A1m1, ref _B11,
              ref _stau1, ref _ctau1, ref _A2m1, ref _B21, ref _A3c, ref _B31, ref _A4, ref _B41, ref _caps);
     SetDistance(arcmode, s13_a13);
 }
示例#5
0
 internal GeodesicLineExact(GeodesicExact g,
                            double lat1, double lon1,
                            double azi1, double salp1, double calp1,
                            GeodesicFlags caps, bool arcmode, double s13_a13)
 {
     LineInit(g, lat1, lon1, azi1, salp1, calp1, caps, ref _lat1, ref _lon1, ref _azi1, ref _salp1, ref _calp1,
              ref _a, ref _f, ref _b, ref _c2, ref _f1, ref _e2, ref _caps, ref _dn1, ref _salp0, ref _calp0, ref _ssig1, ref _csig1,
              ref _somg1, ref _comg1, ref _cchi1, ref _k2, ref _E0, ref _E1, ref _stau1, ref _ctau1, ref _D0, ref _D1, ref _H0, ref _H1,
              ref _A4, ref _B41, ref _s13, ref _a13);
     SetDistance(arcmode, s13_a13);
 }
示例#6
0
 /// <summary>
 /// Constructor for a exact geodesic line staring at latitude <i>lat1</i>, longitude <i>lon1</i>,
 /// and azimuth <i>azi1</i> (all in degrees).
 /// </summary>
 /// <param name="g">
 /// A <see cref="GeodesicExact"/> object used to compute the necessary information about the <see cref="GeodesicLineExact"/>.
 /// </param>
 /// <param name="lat1">latitude of point 1 (degrees).</param>
 /// <param name="lon1">longitude of point 1 (degrees).</param>
 /// <param name="azi1">azimuth at point 1 (degrees).</param>
 /// <param name="caps">bitor'ed combination of <see cref="GeodesicFlags"/> values specifying the capabilities
 /// the <see cref="GeodesicLineExact"/> object should possess, i.e., which quantities can be returned in calls to
 /// <see cref="GeodesicLineBase.Position(double, out double, out double, out double, out double, out double, out double, out double)"/>.</param>
 /// <remarks>
 /// <i>lat1</i> should be in the range [−90°, 90°].
 /// <para>
 /// The <see cref="GeodesicFlags"/> values possible for <i>caps</i> are
 /// <list type="bullet">
 /// <item><i>caps</i> |= <see cref="GeodesicFlags.Latitude"/> for the latitude <i>lat2</i>; this is added automatically;</item>
 /// <item><i>caps</i> |= <see cref="GeodesicFlags.Longitude"/> for the latitude <i>lon2</i>;</item>
 /// <item><i>caps</i> |= <see cref="GeodesicFlags.Azimuth"/> for the latitude <i>azi2</i>;this is added automatically;</item>
 /// <item><i>caps</i> |= <see cref="GeodesicFlags.Distance"/> for the distance <i>s12</i>;</item>
 /// <item><i>caps</i> |= <see cref="GeodesicFlags.ReducedLength"/> for the reduced length <i>m12</i>;</item>
 /// <item><i>caps</i> |= <see cref="GeodesicFlags.GeodesicScale"/> for the geodesic scales <i>M12</i> and <i>M21</i>;</item>
 /// <item><i>caps</i> |= <see cref="GeodesicFlags.Area"/> for the area <i>S12</i>;</item>
 /// <item><i>caps</i> |= <see cref="GeodesicFlags.DistanceIn"/>
 /// permits the length of the geodesic to be given in terms of <i>s12</i>;
 /// without this capability the length can only be specified in terms of arc length;</item>
 /// <item><i>caps</i> |= <see cref="GeodesicFlags.All"/> for all of the above.</item>
 /// </list>
 /// The default value of <i>caps</i> is <see cref="GeodesicFlags.All"/>.
 /// </para>
 /// <para>
 /// If the point is at a pole, the azimuth is defined by keeping lon1 fixed,
 /// writing <i>lat1</i> = ±(90° − ε), and taking the limit ε → 0+.
 /// </para>
 /// </remarks>
 public GeodesicLineExact(GeodesicExact g,
                          double lat1, double lon1, double azi1,
                          GeodesicFlags caps)
 {
     azi1 = AngNormalize(azi1);
     // Guard against underflow in salp0.  Also -0 is converted to +0.
     SinCosd(AngRound(azi1), out var salp1, out var calp1);
     LineInit(g, lat1, lon1, azi1, salp1, calp1, caps, ref _lat1, ref _lon1, ref _azi1, ref _salp1, ref _calp1,
              ref _a, ref _f, ref _b, ref _c2, ref _f1, ref _e2, ref _caps, ref _dn1, ref _salp0, ref _calp0, ref _ssig1, ref _csig1,
              ref _somg1, ref _comg1, ref _cchi1, ref _k2, ref _E0, ref _E1, ref _stau1, ref _ctau1, ref _D0, ref _D1, ref _H0, ref _H1,
              ref _A4, ref _B41, ref _s13, ref _a13);
 }
示例#7
0
        /// <summary>
        /// Constructor for a geodesic line staring at latitude <i>lat1</i>, longitude <i>lon1</i>,
        /// and azimuth <i>azi1</i> (all in degrees).
        /// </summary>
        /// <param name="g">
        /// A <see cref="Geodesic"/> object used to compute the necessary information about the <see cref="GeodesicLine"/>.
        /// </param>
        /// <param name="lat1">latitude of point 1 (degrees).</param>
        /// <param name="lon1">longitude of point 1 (degrees).</param>
        /// <param name="azi1">azimuth at point 1 (degrees).</param>
        /// <param name="caps">bitor'ed combination of <see cref="GeodesicFlags"/> values specifying the capabilities
        /// the <see cref="GeodesicLine"/> object should possess, i.e., which quantities can be returned in calls to
        /// <see cref="IGeodesicLine.Position(double, out double, out double, out double, out double, out double, out double, out double)"/>.</param>
        /// <remarks>
        /// <i>lat1</i> should be in the range [−90°, 90°].
        /// <para>
        /// The <see cref="GeodesicFlags"/> values possible for <i>caps</i> are
        /// <list type="bullet">
        /// <item><i>caps</i> |= <see cref="GeodesicFlags.Latitude"/> for the latitude <i>lat2</i>; this is added automatically;</item>
        /// <item><i>caps</i> |= <see cref="GeodesicFlags.Longitude"/> for the latitude <i>lon2</i>;</item>
        /// <item><i>caps</i> |= <see cref="GeodesicFlags.Azimuth"/> for the latitude <i>azi2</i>;this is added automatically;</item>
        /// <item><i>caps</i> |= <see cref="GeodesicFlags.Distance"/> for the distance <i>s12</i>;</item>
        /// <item><i>caps</i> |= <see cref="GeodesicFlags.ReducedLength"/> for the reduced length <i>m12</i>;</item>
        /// <item><i>caps</i> |= <see cref="GeodesicFlags.GeodesicScale"/> for the geodesic scales <i>M12</i> and <i>M21</i>;</item>
        /// <item><i>caps</i> |= <see cref="GeodesicFlags.Area"/> for the area <i>S12</i>;</item>
        /// <item><i>caps</i> |= <see cref="GeodesicFlags.DistanceIn"/>
        /// permits the length of the geodesic to be given in terms of <i>s12</i>;
        /// without this capability the length can only be specified in terms of arc length;</item>
        /// <item><i>caps</i> |= <see cref="GeodesicFlags.All"/> for all of the above.</item>
        /// </list>
        /// The default value of <i>caps</i> is <see cref="GeodesicFlags.All"/>.
        /// </para>
        /// <para>
        /// If the point is at a pole, the azimuth is defined by keeping lon1 fixed,
        /// writing <i>lat1</i> = ±(90° − ε), and taking the limit ε → 0+.
        /// </para>
        /// </remarks>
        public GeodesicLine(Geodesic g, double lat1, double lon1, double azi1,
                            GeodesicFlags caps = GeodesicFlags.All)
        {
            azi1 = AngNormalize(azi1);

            // Guard against underflow in salp0.  Also -0 is converted to +0.
            SinCosd(AngRound(azi1), out var salp1, out var calp1);
            LineInit(g, lat1, lon1, azi1, salp1, calp1, caps,
                     ref tiny_, ref _lat1, ref _lon1, ref _azi1, ref _salp1, ref _calp1, ref _a, ref _f, ref _b, ref _c2, ref _f1,
                     ref _dn1, ref _salp0, ref _calp0, ref _ssig1, ref _somg1, ref _csig1, ref _comg1, ref _k2, ref _A1m1, ref _B11,
                     ref _stau1, ref _ctau1, ref _A2m1, ref _B21, ref _A3c, ref _B31, ref _A4, ref _B41, ref _caps);
        }
示例#8
0
        private void Lengths(EllipticFunction E,
                             double sig12,
                             double ssig1, double csig1, double dn1,
                             double ssig2, double csig2, double dn2,
                             double cbet1, double cbet2, GeodesicFlags outmask,
                             out double s12b, out double m12b, out double m0,
                             out double M12, out double M21)
        {
            // Return m12b = (reduced length)/_b; also calculate s12b = distance/_b,
            // and m0 = coefficient of secular term in expression for reduced length.
            s12b = m12b = m0 = M12 = M21 = double.NaN;

            outmask = outmask.Flags();
            // outmask & DISTANCE: set s12b
            // outmask & REDUCEDLENGTH: set m12b & m0
            // outmask & GEODESICSCALE: set M12 & M21

            // It's OK to have repeated dummy arguments,
            // e.g., s12b = m0 = M12 = M21 = dummy

            if (outmask.HasAny(GeodesicFlags.Distance))
            {
                // Missing a factor of _b
                s12b = E.E() / (PI / 2) *
                       (sig12 + (E.DeltaE(ssig2, csig2, dn2) - E.DeltaE(ssig1, csig1, dn1)));
            }
            if (outmask.HasAny(GeodesicFlags.ReducedLength | GeodesicFlags.GeodesicScale))
            {
                double
                    m0x = -E.K2 * E.D() / (PI / 2),
                    J12 = m0x *
                          (sig12 + (E.DeltaD(ssig2, csig2, dn2) - E.DeltaD(ssig1, csig1, dn1)));
                if (outmask.HasAny(GeodesicFlags.ReducedLength))
                {
                    m0 = m0x;
                    // Missing a factor of _b.  Add parens around (csig1 * ssig2) and
                    // (ssig1 * csig2) to ensure accurate cancellation in the case of
                    // coincident points.
                    m12b = dn2 * (csig1 * ssig2) - dn1 * (ssig1 * csig2) -
                           csig1 * csig2 * J12;
                }
                if (outmask.HasAny(GeodesicFlags.GeodesicScale))
                {
                    var csig12 = csig1 * csig2 + ssig1 * ssig2;
                    var t      = _ep2 * (cbet1 - cbet2) * (cbet1 + cbet2) / (dn1 + dn2);
                    M12 = csig12 + (t * ssig2 - csig2 * J12) * ssig1 / dn1;
                    M21 = csig12 - (t * ssig1 - csig1 * J12) * ssig2 / dn2;
                }
            }
        }
示例#9
0
        /// <inheritdoc/>
        public override double GenDirect(double lat1, double lon1, double azi1,
                                         bool arcmode, double s12_a12, GeodesicFlags outmask,
                                         out double lat2, out double lon2, out double azi2,
                                         out double s12, out double m12, out double M12, out double M21,
                                         out double S12)
        {
            // Automatically supply DISTANCE_IN if necessary
            if (!arcmode)
            {
                outmask |= GeodesicFlags.DistanceIn;
            }

            return(new GeodesicLineExact(this, lat1, lon1, azi1, outmask)
                   .                    // Note the dot!
                   GenPosition(arcmode, s12_a12, outmask,
                               out lat2, out lon2, out azi2, out s12, out m12, out M12, out M21, out S12));
        }
示例#10
0
        /// <inheritdoc/>
        public DirectGeodesicResult ArcPosition(double a12, GeodesicFlags outmask = GeodesicFlags.All)
        {
            var a12_ = GenPosition(true, a12, outmask,
                                   out var lat2, out var lon2, out var azi2, out var s12, out var m12, out var M12, out var M21, out var S12);

            return(new DirectGeodesicResult
            {
                ArcLength = a12_,
                Area = S12,
                Azimuth2 = azi2,
                Distance = s12,
                GeodesicScale12 = M12,
                GeodesicScale21 = M21,
                Latitude = lat2,
                Longitude = lon2,
                ReducedLength = m12
            });
        }
示例#11
0
        /// <inheritdoc/>
        public override double GenInverse(double lat1, double lon1, double lat2, double lon2,
                                          GeodesicFlags outmask,
                                          out double s12, out double azi1, out double azi2,
                                          out double m12, out double M12, out double M21, out double S12)
        {
            outmask = outmask.Flags();
            var a12 = GenInverse(lat1, lon1, lat2, lon2,
                                 outmask, out s12, out var salp1, out var calp1, out var salp2, out var calp2,
                                 out m12, out M12, out M21, out S12);

            if (outmask.HasAny(GeodesicFlags.Azimuth))
            {
                azi1 = Atan2d(salp1, calp1);
                azi2 = Atan2d(salp2, calp2);
            }
            else
            {
                azi1 = azi2 = double.NaN;
            }

            return(a12);
        }
示例#12
0
        /// <inheritdoc/>
        public InverseGeodesicResult Inverse(double lat1, double lon1, double lat2, double lon2, GeodesicFlags outmask = GeodesicFlags.All)
        {
            var a12 = GenInverse(lat1, lon1, lat2, lon2, outmask,
                                 out var s12, out var azi1, out var azi2, out var m12, out var M12, out var M21, out var S12);

            return(new InverseGeodesicResult
            {
                ArcLength = a12,
                Area = S12,
                Azimuth1 = azi1,
                Azimuth2 = azi2,
                Distance = s12,
                GeodesicScale12 = M12,
                GeodesicScale21 = M21,
                ReducedLength = m12
            });
        }
示例#13
0
        private double GenInverse(double lat1, double lon1, double lat2, double lon2,
                                  GeodesicFlags outmask, out double s12,
                                  out double salp1, out double calp1, out double salp2, out double calp2,
                                  out double m12, out double M12, out double M21, out double S12)
        {
            m12   = M12 = M21 = s12 = S12 = double.NaN;
            salp1 = calp1 = salp2 = calp2 = double.NaN;

            // Compute longitude difference (AngDiff does this carefully).  Result is
            // in [-180, 180] but -180 is only for west-going geodesics.  180 is for
            // east-going and meridional geodesics.
            var lon12 = AngDiff(lon1, lon2, out var lon12s);
            // Make longitude difference positive.
            int lonsign = lon12 >= 0 ? 1 : -1;

            // If very close to being on the same half-meridian, then make it so.
            lon12  = lonsign * AngRound(lon12);
            lon12s = AngRound((180 - lon12) - lonsign * lon12s);
            double
                lam12 = lon12 * Degree,
                slam12, clam12;

            if (lon12 > 90)
            {
                SinCosd(lon12s, out slam12, out clam12);
                clam12 = -clam12;
            }
            else
            {
                SinCosd(lon12, out slam12, out clam12);
            }

            // If really close to the equator, treat as on equator.
            lat1 = AngRound(LatFix(lat1));
            lat2 = AngRound(LatFix(lat2));
            // Swap points so that point with higher (abs) latitude is point 1
            // If one latitude is a nan, then it becomes lat1.
            int swapp = Abs(lat1) < Abs(lat2) || double.IsNaN(lat2) ? -1 : 1;

            if (swapp < 0)
            {
                lonsign *= -1;
                Swap(ref lat1, ref lat2);
            }
            // Make lat1 <= 0
            int latsign = lat1 < 0 ? 1 : -1;

            lat1 *= latsign;
            lat2 *= latsign;
            // Now we have
            //
            //     0 <= lon12 <= 180
            //     -90 <= lat1 <= 0
            //     lat1 <= lat2 <= -lat1
            //
            // longsign, swapp, latsign register the transformation to bring the
            // coordinates to this canonical form.  In all cases, 1 means no change was
            // made.  We make these transformations so that there are few cases to
            // check, e.g., on verifying quadrants in atan2.  In addition, this
            // enforces some symmetries in the results returned.

            double s12x = 0, m12x = 0;
            // Initialize for the meridian.  No longitude calculation is done in this
            // case to let the parameter default to 0.
            var E = new EllipticFunction(-_ep2);

            SinCosd(lat1, out var sbet1, out var cbet1); sbet1 *= _f1;
            // Ensure cbet1 = +epsilon at poles; doing the fix on beta means that sig12
            // will be <= 2*tiny for two points at the same pole.
            Norm(ref sbet1, ref cbet1); cbet1 = Max(tiny_, cbet1);

            SinCosd(lat2, out var sbet2, out var cbet2); sbet2 *= _f1;
            // Ensure cbet2 = +epsilon at poles
            Norm(ref sbet2, ref cbet2); cbet2 = Max(tiny_, cbet2);

            // If cbet1 < -sbet1, then cbet2 - cbet1 is a sensitive measure of the
            // |bet1| - |bet2|.  Alternatively (cbet1 >= -sbet1), abs(sbet2) + sbet1 is
            // a better measure.  This logic is used in assigning calp2 in Lambda12.
            // Sometimes these quantities vanish and in that case we force bet2 = +/-
            // bet1 exactly.  An example where is is necessary is the inverse problem
            // 48.522876735459 0 -48.52287673545898293 179.599720456223079643
            // which failed with Visual Studio 10 (Release and Debug)

            if (cbet1 < -sbet1)
            {
                if (cbet2 == cbet1)
                {
                    sbet2 = sbet2 < 0 ? sbet1 : -sbet1;
                }
            }
            else
            {
                if (Abs(sbet2) == -sbet1)
                {
                    cbet2 = cbet1;
                }
            }

            double
                dn1 = (_f >= 0 ? Sqrt(1 + _ep2 * Sq(sbet1)) :
                       Sqrt(1 - _e2 * Sq(cbet1)) / _f1),
                dn2 = (_f >= 0 ? Sqrt(1 + _ep2 * Sq(sbet2)) :
                       Sqrt(1 - _e2 * Sq(cbet2)) / _f1);

            double a12 = double.NaN, sig12;

            bool meridian = lat1 == -90 || slam12 == 0;

            if (meridian)
            {
                // Endpoints are on a single full meridian, so the geodesic might lie on
                // a meridian.

                calp1 = clam12; salp1 = slam12; // Head to the target longitude
                calp2 = 1; salp2 = 0;           // At the target we're heading north

                double
                // tan(bet) = tan(sig) * cos(alp)
                    ssig1 = sbet1, csig1 = calp1 * cbet1,
                    ssig2 = sbet2, csig2 = calp2 * cbet2;

                // sig12 = sig2 - sig1
                sig12 = Atan2(Max(0, csig1 * ssig2 - ssig1 * csig2),
                              csig1 * csig2 + ssig1 * ssig2);
                {
                    Lengths(E, sig12, ssig1, csig1, dn1, ssig2, csig2, dn2,
                            cbet1, cbet2, outmask | GeodesicFlags.ReducedLength,
                            out s12x, out m12x, out _, out M12, out M21);
                }
                // Add the check for sig12 since zero length geodesics might yield m12 <
                // 0.  Test case was
                //
                //    echo 20.001 0 20.001 0 | GeodSolve -i
                //
                // In fact, we will have sig12 > pi/2 for meridional geodesic which is
                // not a shortest path.
                if (sig12 < 1 || m12x >= 0)
                {
                    // Need at least 2, to handle 90 0 90 180
                    if (sig12 < 3 * tiny_ ||
                        // Prevent negative s12 or m12 for short lines
                        (sig12 < tol0_ && (s12x < 0 || m12x < 0)))
                    {
                        sig12 = m12x = s12x = 0;
                    }
                    m12x *= _b;
                    s12x *= _b;
                    a12   = sig12 / Degree;
                }
                else
                {
                    // m12 < 0, i.e., prolate and too close to anti-podal
                    meridian = false;
                }
            }

            // somg12 > 1 marks that it needs to be calculated
            double omg12 = 0, somg12 = 2, comg12 = 0;

            if (!meridian &&
                sbet1 == 0 &&   // and sbet2 == 0
                (_f <= 0 || lon12s >= _f * 180))
            {
                // Geodesic runs along equator
                calp1 = calp2 = 0; salp1 = salp2 = 1;
                s12x  = _a * lam12;
                sig12 = omg12 = lam12 / _f1;
                m12x  = _b * Sin(sig12);
                if (outmask.HasAny(GeodesicFlags.GeodesicScale))
                {
                    M12 = M21 = Cos(sig12);
                }
                a12 = lon12 / _f1;
            }
            else if (!meridian)
            {
                // Now point1 and point2 belong within a hemisphere bounded by a
                // meridian and geodesic is neither meridional or equatorial.

                // Figure a starting point for Newton's method
                sig12 = InverseStart(E, sbet1, cbet1, dn1, sbet2, cbet2, dn2,
                                     lam12, slam12, clam12,
                                     out salp1, out calp1, out salp2, out calp2, out var dnm);

                if (sig12 >= 0)
                {
                    // Short lines (InverseStart sets salp2, calp2, dnm)
                    s12x = sig12 * _b * dnm;
                    m12x = Sq(dnm) * _b * Sin(sig12 / dnm);
                    if (outmask.HasAny(GeodesicFlags.GeodesicScale))
                    {
                        M12 = M21 = Cos(sig12 / dnm);
                    }
                    a12   = sig12 / Degree;
                    omg12 = lam12 / (_f1 * dnm);
                }
                else
                {
                    // Newton's method.  This is a straightforward solution of f(alp1) =
                    // lambda12(alp1) - lam12 = 0 with one wrinkle.  f(alp) has exactly one
                    // root in the interval (0, pi) and its derivative is positive at the
                    // root.  Thus f(alp) is positive for alp > alp1 and negative for alp <
                    // alp1.  During the course of the iteration, a range (alp1a, alp1b) is
                    // maintained which brackets the root and with each evaluation of
                    // f(alp) the range is shrunk, if possible.  Newton's method is
                    // restarted whenever the derivative of f is negative (because the new
                    // value of alp1 is then further from the solution) or if the new
                    // estimate of alp1 lies outside (0,pi); in this case, the new starting
                    // guess is taken to be (alp1a + alp1b) / 2.
                    //
                    // initial values to suppress warnings (if loop is executed 0 times)
                    double ssig1 = 0, csig1 = 0, ssig2 = 0, csig2 = 0, domg12 = 0;
                    uint   numit = 0;
                    // Bracketing range
                    double salp1a = tiny_, calp1a = 1, salp1b = tiny_, calp1b = -1;
                    for (bool tripn = false, tripb = false;
                         numit < maxit2_ || GEOGRAPHICLIB_PANIC;
                         ++numit)
                    {
                        // 1/4 meridian = 10e6 m and random input.  max err is estimated max
                        // error in nm (checking solution of inverse problem by direct
                        // solution).  iter is mean and sd of number of iterations
                        //
                        //           max   iter
                        // log2(b/a) err mean  sd
                        //    -7     387 5.33 3.68
                        //    -6     345 5.19 3.43
                        //    -5     269 5.00 3.05
                        //    -4     210 4.76 2.44
                        //    -3     115 4.55 1.87
                        //    -2      69 4.35 1.38
                        //    -1      36 4.05 1.03
                        //     0      15 0.01 0.13
                        //     1      25 5.10 1.53
                        //     2      96 5.61 2.09
                        //     3     318 6.02 2.74
                        //     4     985 6.24 3.22
                        //     5    2352 6.32 3.44
                        //     6    6008 6.30 3.45
                        //     7   19024 6.19 3.30
                        double dv;
                        var    v = Lambda12(sbet1, cbet1, dn1, sbet2, cbet2, dn2, salp1, calp1,
                                            slam12, clam12,
                                            out salp2, out calp2, out sig12, out ssig1, out csig1, out ssig2, out csig2,
                                            E, out domg12, numit < maxit1_, out dv);
                        // Reversed test to allow escape with NaNs
                        if (tripb || !(Abs(v) >= (tripn ? 8 : 1) * tol0_))
                        {
                            break;
                        }
                        // Update bracketing values
                        if (v > 0 && (numit > maxit1_ || calp1 / salp1 > calp1b / salp1b))
                        {
                            salp1b = salp1; calp1b = calp1;
                        }
                        else if (v < 0 && (numit > maxit1_ || calp1 / salp1 < calp1a / salp1a))
                        {
                            salp1a = salp1; calp1a = calp1;
                        }
                        if (numit < maxit1_ && dv > 0)
                        {
                            var
                                dalp1 = -v / dv;
                            double
                                sdalp1 = Sin(dalp1), cdalp1 = Cos(dalp1),
                                nsalp1 = salp1 * cdalp1 + calp1 * sdalp1;
                            if (nsalp1 > 0 && Abs(dalp1) < PI)
                            {
                                calp1 = calp1 * cdalp1 - salp1 * sdalp1;
                                salp1 = nsalp1;
                                Norm(ref salp1, ref calp1);
                                // In some regimes we don't get quadratic convergence because
                                // slope -> 0.  So use convergence conditions based on epsilon
                                // instead of sqrt(epsilon).
                                tripn = Abs(v) <= 16 * tol0_;
                                continue;
                            }
                        }
                        // Either dv was not positive or updated value was outside legal
                        // range.  Use the midpoint of the bracket as the next estimate.
                        // This mechanism is not needed for the WGS84 ellipsoid, but it does
                        // catch problems with more eccentric ellipsoids.  Its efficacy is
                        // such for the WGS84 test set with the starting guess set to alp1 =
                        // 90deg:
                        // the WGS84 test set: mean = 5.21, sd = 3.93, max = 24
                        // WGS84 and random input: mean = 4.74, sd = 0.99
                        salp1 = (salp1a + salp1b) / 2;
                        calp1 = (calp1a + calp1b) / 2;
                        Norm(ref salp1, ref calp1);
                        tripn = false;
                        tripb = (Abs(salp1a - salp1) + (calp1a - calp1) < tolb_ ||
                                 Abs(salp1 - salp1b) + (calp1 - calp1b) < tolb_);
                    }
                    {
                        Lengths(E, sig12, ssig1, csig1, dn1, ssig2, csig2, dn2,
                                cbet1, cbet2, outmask, out s12x, out m12x, out _, out M12, out M21);
                    }
                    m12x *= _b;
                    s12x *= _b;
                    a12   = sig12 / Degree;
                    if (outmask.HasAny(GeodesicFlags.GeodesicScale))
                    {
                        // omg12 = lam12 - domg12
                        double sdomg12 = Sin(domg12), cdomg12 = Cos(domg12);
                        somg12 = slam12 * cdomg12 - clam12 * sdomg12;
                        comg12 = clam12 * cdomg12 + slam12 * sdomg12;
                    }
                }
            }

            if (outmask.HasAny(GeodesicFlags.Distance))
            {
                s12 = 0 + s12x;           // Convert -0 to 0
            }
            if (outmask.HasAny(GeodesicFlags.ReducedLength))
            {
                m12 = 0 + m12x;           // Convert -0 to 0
            }
            if (outmask.HasAny(GeodesicFlags.Area))
            {
                double
                // From Lambda12: sin(alp1) * cos(bet1) = sin(alp0)
                    salp0 = salp1 * cbet1,
                    calp0 = Hypot(calp1, salp1 * sbet1); // calp0 > 0
                double alp12;
                if (calp0 != 0 && salp0 != 0)
                {
                    double
                    // From Lambda12: tan(bet) = tan(sig) * cos(alp)
                        ssig1 = sbet1, csig1 = calp1 * cbet1,
                        ssig2 = sbet2, csig2 = calp2 * cbet2,
                        k2  = Sq(calp0) * _ep2,
                        eps = k2 / (2 * (1 + Sqrt(1 + k2)) + k2),
                    // Multiplier = a^2 * e^2 * cos(alpha0) * sin(alpha0).
                        A4 = Sq(_a) * calp0 * salp0 * _e2;
                    Norm(ref ssig1, ref csig1);
                    Norm(ref ssig2, ref csig2);
                    Span <double> C4a = stackalloc double[nC4_];
                    C4f(eps, C4a);
                    double
                        B41 = CosSeries(ssig1, csig1, C4a, nC4_),
                        B42 = CosSeries(ssig2, csig2, C4a, nC4_);
                    S12 = A4 * (B42 - B41);
                }
                else
                {
                    // Avoid problems with indeterminate sig1, sig2 on equator
                    S12 = 0;
                }

                if (!meridian)
                {
                    if (somg12 > 1)
                    {
                        somg12 = Sin(omg12); comg12 = Cos(omg12);
                    }
                }

                if (!meridian &&
                    // omg12 < 3/4 * pi
                    comg12 > -0.7071 && // Long difference not too big
                    sbet2 - sbet1 < 1.75)
                {                       // Lat difference not too big
                  // Use tan(Gamma/2) = tan(omg12/2)
                  // * (tan(bet1/2)+tan(bet2/2))/(1+tan(bet1/2)*tan(bet2/2))
                  // with tan(x/2) = sin(x)/(1+cos(x))
                    double domg12 = 1 + comg12, dbet1 = 1 + cbet1, dbet2 = 1 + cbet2;
                    alp12 = 2 * Atan2(somg12 * (sbet1 * dbet2 + sbet2 * dbet1),
                                      domg12 * (sbet1 * sbet2 + dbet1 * dbet2));
                }
                else
                {
                    // alp12 = alp2 - alp1, used in atan2 so no need to normalize
                    double
                        salp12 = salp2 * calp1 - calp2 * salp1,
                        calp12 = calp2 * calp1 + salp2 * salp1;
                    // The right thing appears to happen if alp1 = +/-180 and alp2 = 0, viz
                    // salp12 = -0 and alp12 = -180.  However this depends on the sign
                    // being attached to 0 correctly.  The following ensures the correct
                    // behavior.
                    if (salp12 == 0 && calp12 < 0)
                    {
                        salp12 = tiny_ * calp1;
                        calp12 = -1;
                    }
                    alp12 = Atan2(salp12, calp12);
                }
                S12 += _c2 * alp12;
                S12 *= swapp * lonsign * latsign;
                // Convert -0 to 0
                S12 += 0;
            }

            // Convert calp, salp to azimuth accounting for lonsign, swapp, latsign.
            if (swapp < 0)
            {
                Swap(ref salp1, ref salp2);
                Swap(ref calp1, ref calp2);
                if (outmask.HasAny(GeodesicFlags.GeodesicScale))
                {
                    Swap(ref M12, ref M21);
                }
            }

            salp1 *= swapp * lonsign; calp1 *= swapp * latsign;
            salp2 *= swapp * lonsign; calp2 *= swapp * latsign;

            // Returned value in [0, 180]
            return(a12);
        }
示例#14
0
        /// <inheritdoc/>
        public override IGeodesicLine InverseLine(double lat1, double lon1, double lat2, double lon2, GeodesicFlags caps = GeodesicFlags.All)
        {
            double
                a12 = GenInverse(lat1, lon1, lat2, lon2,
                                 // No need to specify AZIMUTH here
                                 0u, out _, out var salp1, out var calp1, out var salp2, out var calp2,
                                 out _, out _, out _, out _),
                azi1 = Atan2d(salp1, calp1);

            // Ensure that a12 can be converted to a distance
            if (caps.Flags().HasAny(GeodesicFlags.DistanceIn))
            {
                caps |= GeodesicFlags.Distance;
            }
            return(new GeodesicLineExact(this, lat1, lon1, azi1, salp1, calp1, caps, true, a12));
        }
示例#15
0
 /// <inheritdoc/>
 public abstract double GenInverse(double lat1, double lon1, double lat2, double lon2, GeodesicFlags outmask, out double s12, out double azi1,
                                   out double azi2, out double m12, out double M12, out double M21, out double S12);
示例#16
0
        /// <inheritdoc/>
        public override double GenPosition(bool arcmode, double s12_a12, GeodesicFlags outmask,
                                           out double lat2, out double lon2, out double azi2,
                                           out double s12, out double m12, out double M12, out double M21,
                                           out double S12)
        {
            lat2     = lon2 = azi2 = s12 = m12 = M12 = M21 = S12 = double.NaN;
            outmask &= _caps & (GeodesicFlags)GeodesicCapability.OutMask;
            if (!(arcmode || _caps.HasAny(GeodesicFlags.DistanceIn)))
            {
                // Uninitialized or impossible distance calculation requested
                return(double.NaN);
            }

            // Avoid warning about uninitialized B12.
            double sig12, ssig12, csig12, B12 = 0, AB1 = 0;

            if (arcmode)
            {
                // Interpret s12_a12 as spherical arc length
                sig12 = s12_a12 * Degree;
                SinCosd(s12_a12, out ssig12, out csig12);
            }
            else
            {
                // Interpret s12_a12 as distance
                double
                    tau12 = s12_a12 / (_b * (1 + _A1m1)),
                    s     = Sin(tau12),
                    c     = Cos(tau12);
                // tau2 = tau1 + tau12
                B12 = -Geodesic.SinCosSeries(true,
                                             _stau1 * c + _ctau1 * s,
                                             _ctau1 * c - _stau1 * s,
                                             _C1pa.Span, nC1p_);
                sig12  = tau12 - (B12 - _B11);
                ssig12 = Sin(sig12); csig12 = Cos(sig12);
                if (Abs(_f) > 0.01)
                {
                    // Reverted distance series is inaccurate for |f| > 1/100, so correct
                    // sig12 with 1 Newton iteration.  The following table shows the
                    // approximate maximum error for a = WGS_a() and various f relative to
                    // GeodesicExact.
                    //     erri = the error in the inverse solution (nm)
                    //     errd = the error in the direct solution (series only) (nm)
                    //     errda = the error in the direct solution
                    //             (series + 1 Newton) (nm)
                    //
                    //       f     erri  errd errda
                    //     -1/5    12e6 1.2e9  69e6
                    //     -1/10  123e3  12e6 765e3
                    //     -1/20   1110 108e3  7155
                    //     -1/50  18.63 200.9 27.12
                    //     -1/100 18.63 23.78 23.37
                    //     -1/150 18.63 21.05 20.26
                    //      1/150 22.35 24.73 25.83
                    //      1/100 22.35 25.03 25.31
                    //      1/50  29.80 231.9 30.44
                    //      1/20   5376 146e3  10e3
                    //      1/10  829e3  22e6 1.5e6
                    //      1/5   157e6 3.8e9 280e6
                    double
                        ssig2_ = _ssig1 * csig12 + _csig1 * ssig12,
                        csig2_ = _csig1 * csig12 - _ssig1 * ssig12;
                    B12 = Geodesic.SinCosSeries(true, ssig2_, csig2_, _C1a.Span, nC1_);
                    double serr = (1 + _A1m1) * (sig12 + (B12 - _B11)) - s12_a12 / _b;
                    sig12  = sig12 - serr / Sqrt(1 + _k2 * Sq(ssig2_));
                    ssig12 = Sin(sig12); csig12 = Cos(sig12);
                    // Update B12 below
                }
            }

            double ssig2, csig2, sbet2, cbet2, salp2, calp2;

            // sig2 = sig1 + sig12
            ssig2 = _ssig1 * csig12 + _csig1 * ssig12;
            csig2 = _csig1 * csig12 - _ssig1 * ssig12;
            var dn2 = Sqrt(1 + _k2 * Sq(ssig2));

            if (outmask.HasAny(GeodesicFlags.Distance | GeodesicFlags.ReducedLength | GeodesicFlags.GeodesicScale))
            {
                if (arcmode || Abs(_f) > 0.01)
                {
                    B12 = Geodesic.SinCosSeries(true, ssig2, csig2, _C1a.Span, nC1_);
                }
                AB1 = (1 + _A1m1) * (B12 - _B11);
            }
            // sin(bet2) = cos(alp0) * sin(sig2)
            sbet2 = _calp0 * ssig2;
            // Alt: cbet2 = hypot(csig2, salp0 * ssig2);
            cbet2 = Hypot(_salp0, _calp0 * csig2);
            if (cbet2 == 0)
            {
                // I.e., salp0 = 0, csig2 = 0.  Break the degeneracy in this case
                cbet2 = csig2 = tiny_;
            }
            // tan(alp0) = cos(sig2)*tan(alp2)
            salp2 = _salp0; calp2 = _calp0 * csig2; // No need to normalize

            if (outmask.HasAny(GeodesicFlags.Distance))
            {
                s12 = arcmode ? _b * ((1 + _A1m1) * sig12 + AB1) : s12_a12;
            }

            if (outmask.HasAny(GeodesicFlags.Longitude))
            {
                // tan(omg2) = sin(alp0) * tan(sig2)
                double somg2 = _salp0 * ssig2, comg2 = csig2, // No need to normalize
                       E = CopySign(1, _salp0);               // east-going?
                                                              // omg12 = omg2 - omg1
                var omg12 = outmask.HasAny(GeodesicFlags.LongUnroll)
                  ? E * (sig12
                         - (Atan2(ssig2, csig2) - Atan2(_ssig1, _csig1))
                         + (Atan2(E * somg2, comg2) - Atan2(E * _somg1, _comg1)))
                  : Atan2(somg2 * _comg1 - comg2 * _somg1,
                          comg2 * _comg1 + somg2 * _somg1);
                var lam12 = omg12 + _A3c *
                            (sig12 + (Geodesic.SinCosSeries(true, ssig2, csig2, _C3a.Span, nC3_ - 1)
                                      - _B31));
                var lon12 = lam12 / Degree;
                lon2 = outmask.HasAny(GeodesicFlags.LongUnroll) ? _lon1 + lon12 :
                       AngNormalize(AngNormalize(_lon1) + AngNormalize(lon12));
            }

            if (outmask.HasAny(GeodesicFlags.Latitude))
            {
                lat2 = Atan2d(sbet2, _f1 * cbet2);
            }

            if (outmask.HasAny(GeodesicFlags.Azimuth))
            {
                azi2 = Atan2d(salp2, calp2);
            }

            if (outmask.HasAny(GeodesicFlags.ReducedLength | GeodesicFlags.GeodesicScale))
            {
                double
                    B22 = Geodesic.SinCosSeries(true, ssig2, csig2, _C2a.Span, nC2_),
                    AB2 = (1 + _A2m1) * (B22 - _B21),
                    J12 = (_A1m1 - _A2m1) * sig12 + (AB1 - AB2);
                if (outmask.HasAny(GeodesicFlags.ReducedLength))
                {
                    // Add parens around (_csig1 * ssig2) and (_ssig1 * csig2) to ensure
                    // accurate cancellation in the case of coincident points.
                    m12 = _b * ((dn2 * (_csig1 * ssig2) - _dn1 * (_ssig1 * csig2))
                                - _csig1 * csig2 * J12);
                }
                if (outmask.HasAny(GeodesicFlags.GeodesicScale))
                {
                    var t = _k2 * (ssig2 - _ssig1) * (ssig2 + _ssig1) / (_dn1 + dn2);
                    M12 = csig12 + (t * ssig2 - csig2 * J12) * _ssig1 / _dn1;
                    M21 = csig12 - (t * _ssig1 - _csig1 * J12) * ssig2 / dn2;
                }
            }

            if (outmask.HasAny(GeodesicFlags.Area))
            {
                var
                       B42 = Geodesic.SinCosSeries(false, ssig2, csig2, _C4a.Span, nC4_);
                double salp12, calp12;
                if (_calp0 == 0 || _salp0 == 0)
                {
                    // alp12 = alp2 - alp1, used in atan2 so no need to normalize
                    salp12 = salp2 * _calp1 - calp2 * _salp1;
                    calp12 = calp2 * _calp1 + salp2 * _salp1;
                    // We used to include here some patch up code that purported to deal
                    // with nearly meridional geodesics properly.  However, this turned out
                    // to be wrong once _salp1 = -0 was allowed (via
                    // Geodesic::InverseLine).  In fact, the calculation of {s,c}alp12
                    // was already correct (following the IEEE rules for handling signed
                    // zeros).  So the patch up code was unnecessary (as well as
                    // dangerous).
                }
                else
                {
                    // tan(alp) = tan(alp0) * sec(sig)
                    // tan(alp2-alp1) = (tan(alp2) -tan(alp1)) / (tan(alp2)*tan(alp1)+1)
                    // = calp0 * salp0 * (csig1-csig2) / (salp0^2 + calp0^2 * csig1*csig2)
                    // If csig12 > 0, write
                    //   csig1 - csig2 = ssig12 * (csig1 * ssig12 / (1 + csig12) + ssig1)
                    // else
                    //   csig1 - csig2 = csig1 * (1 - csig12) + ssig12 * ssig1
                    // No need to normalize
                    salp12 = _calp0 * _salp0 *
                             (csig12 <= 0 ? _csig1 * (1 - csig12) + ssig12 * _ssig1 :
                              ssig12 * (_csig1 * ssig12 / (1 + csig12) + _ssig1));
                    calp12 = Sq(_salp0) + Sq(_calp0) * _csig1 * csig2;
                }
                S12 = _c2 * Atan2(salp12, calp12) + _A4 * (B42 - _B41);
            }

            return(arcmode ? s12_a12 : sig12 / Degree);
        }
示例#17
0
 /// <inheritdoc/>
 public abstract IGeodesicLine GenDirectLine(double lat1, double lon1, double azi1, bool arcmode,
                                             double s12_a12, GeodesicFlags caps = GeodesicFlags.All);
示例#18
0
 /// <inheritdoc/>
 public abstract IGeodesicLine InverseLine(double lat1, double lon1, double lat2, double lon2, GeodesicFlags caps = GeodesicFlags.All);
示例#19
0
        /// <inheritdoc/>
        public override IGeodesicLine GenDirectLine(double lat1, double lon1, double azi1, bool arcmode, double s12_a12, GeodesicFlags caps = GeodesicFlags.All)
        {
            azi1 = AngNormalize(azi1);

            // Guard against underflow in salp0.  Also -0 is converted to +0.
            SinCosd(AngRound(azi1), out var salp1, out var calp1);

            // Automatically supply DISTANCE_IN if necessary
            if (!arcmode)
            {
                caps |= GeodesicFlags.DistanceIn;
            }

            return(new GeodesicLineExact(this, lat1, lon1, azi1, salp1, calp1,
                                         caps, arcmode, s12_a12));
        }
示例#20
0
 /// <inheritdoc/>
 public abstract IGeodesicLine Line(double lat1, double lon1, double azi1, GeodesicFlags caps = GeodesicFlags.All);
示例#21
0
 /// <inheritdoc/>
 public IGeodesicLine DirectLine(double lat1, double lon1, double azi1, double s12,
                                 GeodesicFlags caps = GeodesicFlags.All) => GenDirectLine(lat1, lon1, azi1, false, s12, caps);
示例#22
0
 /// <inheritdoc/>
 public IGeodesicLine ArcDirectLine(double lat1, double lon1, double azi1, double a12,
                                    GeodesicFlags caps = GeodesicFlags.All) => GenDirectLine(lat1, lon1, azi1, true, a12, caps);
示例#23
0
 /// <inheritdoc/>
 public DirectGeodesicResult ArcDirect(double lat1, double lon1, double azi1, double a12, GeodesicFlags outmask = GeodesicFlags.All)
 => Line(lat1, lon1, azi1, outmask).ArcPosition(a12, outmask);
示例#24
0
 /// <summary>
 /// Verifies whether <paramref name="src"/> and <paramref name="caps"/> has any common flag.
 /// </summary>
 /// <param name="src"></param>
 /// <param name="caps"></param>
 /// <returns></returns>
 public static bool HasAny(this GeodesicFlags src, GeodesicCapability caps) => (uint)(src & (GeodesicFlags)caps) != 0;
示例#25
0
        private void LineInit(Geodesic g,
                              double lat1, double lon1,
                              double azi1, double salp1, double calp1,
                              GeodesicFlags caps,
                              ref double tiny_, ref double _lat1, ref double _lon1, ref double _azi1, ref double _salp1, ref double _calp1,
                              ref double _a, ref double _f, ref double _b, ref double _c2, ref double _f1, ref double _dn1, ref double _salp0,
                              ref double _calp0, ref double _ssig1, ref double _somg1, ref double _csig1, ref double _comg1, ref double _k2,
                              ref double _A1m1, ref double _B11, ref double _stau1, ref double _ctau1, ref double _A2m1, ref double _B21,
                              ref double _A3c, ref double _B31, ref double _A4, ref double _B41, ref GeodesicFlags _caps)
        {
            tiny_  = g.tiny_;
            _lat1  = LatFix(lat1);
            _lon1  = lon1;
            _azi1  = azi1;
            _salp1 = salp1;
            _calp1 = calp1;
            _a     = g._a;
            _f     = g._f;
            _b     = g._b;
            _c2    = g._c2;
            _f1    = g._f1;

            // Always allow latitude and azimuth and unrolling of longitude
            _caps = caps | GeodesicFlags.Latitude | GeodesicFlags.Azimuth | GeodesicFlags.LongUnroll;

            SinCosd(AngRound(_lat1), out var sbet1, out var cbet1);
            sbet1 *= _f1;

            // Ensure cbet1 = +epsilon at poles
            Norm(ref sbet1, ref cbet1);
            cbet1 = Max(tiny_, cbet1);
            _dn1  = Sqrt(1 + g._ep2 * Sq(sbet1));

            // Evaluate alp0 from sin(alp1) * cos(bet1) = sin(alp0),
            _salp0 = _salp1 * cbet1; // alp0 in [0, pi/2 - |bet1|]
                                     // Alt: calp0 = hypot(sbet1, calp1 * cbet1).  The following
                                     // is slightly better (consider the case salp1 = 0).
            _calp0 = Hypot(_calp1, _salp1 * sbet1);
            // Evaluate sig with tan(bet1) = tan(sig1) * cos(alp1).
            // sig = 0 is nearest northward crossing of equator.
            // With bet1 = 0, alp1 = pi/2, we have sig1 = 0 (equatorial line).
            // With bet1 =  pi/2, alp1 = -pi, sig1 =  pi/2
            // With bet1 = -pi/2, alp1 =  0 , sig1 = -pi/2
            // Evaluate omg1 with tan(omg1) = sin(alp0) * tan(sig1).
            // With alp0 in (0, pi/2], quadrants for sig and omg coincide.
            // No atan2(0,0) ambiguity at poles since cbet1 = +epsilon.
            // With alp0 = 0, omg1 = 0 for alp1 = 0, omg1 = pi for alp1 = pi.
            _ssig1 = sbet1; _somg1 = _salp0 * sbet1;
            _csig1 = _comg1 = sbet1 != 0 || _calp1 != 0 ? cbet1 * _calp1 : 1;
            Norm(ref _ssig1, ref _csig1); // sig1 in (-pi, pi]
                                          // Math::norm(_somg1, _comg1); -- don't need to normalize!

            _k2 = Sq(_calp0) * g._ep2;
            var eps = _k2 / (2 * (1 + Sqrt(1 + _k2)) + _k2);

            if (_caps.Capabilities().HasFlag(GeodesicCapability.C1))
            {
                _A1m1 = Geodesic.A1m1f(eps);
                Geodesic.C1f(eps, _C1a.Span);
                _B11 = Geodesic.SinCosSeries(true, _ssig1, _csig1, _C1a.Span, nC1_);
                double s = Sin(_B11), c = Cos(_B11);
                // tau1 = sig1 + B11
                _stau1 = _ssig1 * c + _csig1 * s;
                _ctau1 = _csig1 * c - _ssig1 * s;
                // Not necessary because C1pa reverts C1a
                //    _B11 = -SinCosSeries(true, _stau1, _ctau1, _C1pa, nC1p_);
            }

            if (_caps.Capabilities().HasFlag(GeodesicCapability.C1p))
            {
                Geodesic.C1pf(eps, _C1pa.Span);
            }

            if (_caps.Capabilities().HasFlag(GeodesicCapability.C2))
            {
                _A2m1 = Geodesic.A2m1f(eps);
                Geodesic.C2f(eps, _C2a.Span);
                _B21 = Geodesic.SinCosSeries(true, _ssig1, _csig1, _C2a.Span, nC2_);
            }

            if (_caps.Capabilities().HasFlag(GeodesicCapability.C3))
            {
                g.C3f(eps, _C3a.Span);
                _A3c = -_f *_salp0 *g.A3f(eps);

                _B31 = Geodesic.SinCosSeries(true, _ssig1, _csig1, _C3a.Span, nC3_ - 1);
            }

            if (_caps.Capabilities().HasFlag(GeodesicCapability.C4))
            {
                g.C4f(eps, _C4a.Span);
                // Multiplier = a^2 * e^2 * cos(alpha0) * sin(alpha0)
                _A4  = Sq(_a) * _calp0 * _salp0 * g._e2;
                _B41 = Geodesic.SinCosSeries(false, _ssig1, _csig1, _C4a.Span, nC4_);
            }

            _a13 = _s13 = double.NaN;
        }
示例#26
0
 /// <inheritdoc/>
 public override IGeodesicLine Line(double lat1, double lon1, double azi1, GeodesicFlags caps = GeodesicFlags.All)
 => new GeodesicLineExact(this, lat1, lon1, azi1, caps);
示例#27
0
 /// <inheritdoc/>
 public abstract double GenPosition(bool arcmode, double s12_a12, GeodesicFlags outmask,
                                    out double lat2, out double lon2, out double azi2, out double s12, out double m12, out double M12, out double M21, out double S12);
示例#28
0
 /// <inheritdoc/>
 public abstract double GenDirect(double lat1, double lon1, double azi1, bool arcmode, double s12_a12,
                                  GeodesicFlags outmask, out double lat2, out double lon2, out double azi2, out double s12, out double m12,
                                  out double M12, out double M21, out double S12);
示例#29
0
 /// <inheritdoc/>
 public bool HasCapability(GeodesicFlags testcaps) => Capabilities.Flags().HasFlag(testcaps);
示例#30
0
 /// <summary>
 /// Verifies whether <paramref name="src"/> and <paramref name="flags"/> has any common flag.
 /// </summary>
 /// <param name="src"></param>
 /// <param name="flags"></param>
 /// <returns></returns>
 public static bool HasAny(this GeodesicFlags src, GeodesicFlags flags) => (uint)(src & flags) != 0;