Пример #1
0
        /**
         * The general position function.  {@link #Position(double, int) Position}
         * and {@link #ArcPosition(double, int) ArcPosition} are defined in terms of
         * this function.
         * <p>
         * @param arcmode bool flag determining the meaning of the Second
         *   parameter; if arcmode is false, then the GeodesicLine object must have
         *   been constructed with <i>caps</i> |= {@link GeodesicMask#DISTANCE_IN}.
         * @param s12_a12 if <i>arcmode</i> is false, this is the distance between
         *   point 1 and point 2 (meters); otherwise it is the arc length between
         *   point 1 and point 2 (degrees); it can be negative.
         * @param outmask a bitor'ed combination of {@link GeodesicMask} values
         *   specifying which results should be returned.
         * @return a {@link GeodesicData} object with the requested results.
         * <p>
         * The {@link GeodesicMask} values possible for <i>outmask</i> are
         * <ul>
         * <li>
         *   <i>outmask</i> |= GeodesicMask.LATITUDE for the latitude <i>lat2</i>.
         * <li>
         *   <i>outmask</i> |= GeodesicMask.LONGITUDE for the latitude <i>lon2</i>.
         * <li>
         *   <i>outmask</i> |= GeodesicMask.AZIMUTH for the latitude <i>azi2</i>.
         * <li>
         *   <i>outmask</i> |= GeodesicMask.DISTANCE for the distance <i>s12</i>.
         * <li>
         *   <i>outmask</i> |= GeodesicMask.REDUCEDLENGTH for the reduced length
         *   <i>m12</i>.
         * <li>
         *   <i>outmask</i> |= GeodesicMask.GEODESICSCALE for the geodesic scales
         *   <i>M12</i> and <i>M21</i>.
         * <li>
         *   <i>outmask</i> |= GeodesicMask.AREA for the Area <i>S12</i>.
         * </ul>
         * <p>
         * Requesting a value which the GeodesicLine object is not capable of
         * computing is not an error; Double.NaN is returned instead.
         **********************************************************************/
        public GeodesicData Position(bool arcmode, double s12_a12, int outmask)
        {
            outmask &= _caps & GeodesicMask.OUT_ALL;
            GeodesicData r = new GeodesicData();

            if (!(Init() &&
                  (arcmode ||
                   (_caps & GeodesicMask.DISTANCE_IN & GeodesicMask.OUT_ALL) != 0)))
            {
                // Uninitialized or impossible distance calculation requested
                return(r);
            }
            r.lat1 = _lat1; r.lon1 = _lon1; r.azi1 = _azi1;

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

            if (arcmode)
            {
                // Interpret s12_a12 as spherical arc length
                r.a12 = s12_a12;
                sig12 = s12_a12 * GeoMath.Degree;
                double s12a = Math.Abs(s12_a12);
                s12a  -= 180 * Math.Floor(s12a / 180);
                ssig12 = s12a == 0 ? 0 : Math.Sin(sig12);
                csig12 = s12a == 90 ? 0 : Math.Cos(sig12);
            }
            else
            {
                // Interpret s12_a12 as distance
                r.s12 = s12_a12;
                double
                    tau12 = s12_a12 / (_b * (1 + _A1m1)),
                    s     = Math.Sin(tau12),
                    c     = Math.Cos(tau12);
                // tau2 = tau1 + tau12
                B12 = -Geodesic.SinCosSeries(true,
                                             _stau1 * c + _ctau1 * s,
                                             _ctau1 * c - _stau1 * s,
                                             _C1pa);
                sig12  = tau12 - (B12 - _B11);
                r.a12  = sig12 / GeoMath.Degree;
                ssig12 = Math.Sin(sig12); csig12 = Math.Cos(sig12);
                if (Math.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);
                    double serr = (1 + _A1m1) * (sig12 + (B12 - _B11)) - s12_a12 / _b;
                    sig12  = sig12 - serr / Math.Sqrt(1 + _k2 * GeoMath.Sq(ssig2));
                    ssig12 = Math.Sin(sig12); csig12 = Math.Cos(sig12);
                    // Update B12 below
                }
            }

            double omg12, lam12, lon12;
            double ssig3, csig3, sbet3, cbet3, somg3, comg3, salp3, calp3;

            // sig2 = sig1 + sig12
            ssig3 = _ssig1 * csig12 + _csig1 * ssig12;
            csig3 = _csig1 * csig12 - _ssig1 * ssig12;
            double dn2 = Math.Sqrt(1 + _k2 * GeoMath.Sq(ssig3));

            if ((outmask & (GeodesicMask.DISTANCE | GeodesicMask.REDUCEDLENGTH |
                            GeodesicMask.GEODESICSCALE)) != 0)
            {
                if (arcmode || Math.Abs(_f) > 0.01)
                {
                    B12 = Geodesic.SinCosSeries(true, ssig3, csig3, _C1a);
                }
                AB1 = (1 + _A1m1) * (B12 - _B11);
            }
            // sin(bet2) = cos(alp0) * sin(sig2)
            sbet3 = _calp0 * ssig3;
            // Alt: cbet3 = Hypot(csig3, salp0 * ssig3);
            cbet3 = GeoMath.Hypot(_salp0, _calp0 * csig3);
            if (cbet3 == 0)
            {
                // I.e., salp0 = 0, csig3 = 0.  Break the degeneracy in this case
                cbet3 = csig3 = Geodesic.tiny_;
            }
            // tan(omg2) = sin(alp0) * tan(sig2)
            somg3 = _salp0 * ssig3; comg3 = csig3;  // No need to normalize
            // tan(alp0) = cos(sig2)*tan(alp2)
            salp3 = _salp0; calp3 = _calp0 * csig3; // No need to normalize
            // omg12 = omg2 - omg1
            omg12 = Math.Atan2(somg3 * _comg1 - comg3 * _somg1,
                               comg3 * _comg1 + somg3 * _somg1);

            if ((outmask & GeodesicMask.DISTANCE) != 0 && arcmode)
            {
                r.s12 = _b * ((1 + _A1m1) * sig12 + AB1);
            }

            if ((outmask & GeodesicMask.LONGITUDE) != 0)
            {
                lam12 = omg12 + _A3c *
                        (sig12 + (Geodesic.SinCosSeries(true, ssig3, csig3, _C3a)
                                  - _B31));
                lon12 = lam12 / GeoMath.Degree;
                // Use GeoMath.AngNormalize2 because longitude might have wrapped
                // multiple times.
                lon12  = GeoMath.AngNormalize2(lon12);
                r.lon2 = GeoMath.AngNormalize(_lon1 + lon12);
            }

            if ((outmask & GeodesicMask.LATITUDE) != 0)
            {
                r.lat2 = Math.Atan2(sbet3, _f1 * cbet3) / GeoMath.Degree;
            }

            if ((outmask & GeodesicMask.AZIMUTH) != 0)
            {
                // minus signs give range [-180, 180). 0- converts -0 to +0.
                r.azi2 = 0 - Math.Atan2(-salp3, calp3) / GeoMath.Degree;
            }

            if ((outmask &
                 (GeodesicMask.REDUCEDLENGTH | GeodesicMask.GEODESICSCALE)) != 0)
            {
                double
                    B22 = Geodesic.SinCosSeries(true, ssig3, csig3, _C2a),
                    AB2 = (1 + _A2m1) * (B22 - _B21),
                    J12 = (_A1m1 - _A2m1) * sig12 + (AB1 - AB2);
                if ((outmask & GeodesicMask.REDUCEDLENGTH) != 0)
                {
                    // Add parens around (_csig1 * ssig3) and (_ssig1 * csig3) to ensure
                    // accurate cancellation in the case of coincident points.
                    r.m12 = _b * ((dn2 * (_csig1 * ssig3) - _dn1 * (_ssig1 * csig3))
                                  - _csig1 * csig3 * J12);
                }
                if ((outmask & GeodesicMask.GEODESICSCALE) != 0)
                {
                    double t = _k2 * (ssig3 - _ssig1) * (ssig3 + _ssig1) / (_dn1 + dn2);
                    r.M12 = csig12 + (t * ssig3 - csig3 * J12) * _ssig1 / _dn1;
                    r.M21 = csig12 - (t * _ssig1 - _csig1 * J12) * ssig3 / dn2;
                }
            }

            if ((outmask & GeodesicMask.AREA) != 0)
            {
                double
                       B42 = Geodesic.SinCosSeries(false, ssig3, csig3, _C4a);
                double salp12, calp12;
                if (_calp0 == 0 || _salp0 == 0)
                {
                    // alp12 = alp2 - alp1, used in atan2 so no need to normalized
                    salp12 = salp3 * _calp1 - calp3 * _salp1;
                    calp12 = calp3 * _calp1 + salp3 * _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 = Geodesic.tiny_ * _calp1;
                        calp12 = -1;
                    }
                }
                else
                {
                    // tan(alp) = tan(alp0) * sec(sig)
                    // tan(alp2-alp1) = (tan(alp2) -tan(alp1)) / (tan(alp2)*tan(alp1)+1)
                    // = calp0 * salp0 * (csig1-csig3) / (salp0^2 + calp0^2 * csig1*csig3)
                    // If csig12 > 0, write
                    //   csig1 - csig3 = ssig12 * (csig1 * ssig12 / (1 + csig12) + ssig1)
                    // else
                    //   csig1 - csig3 = 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 = GeoMath.Sq(_salp0) + GeoMath.Sq(_calp0) * _csig1 * csig3;
                }
                r.S12 = _c2 * Math.Atan2(salp12, calp12) + _A4 * (B42 - _B41);
            }

            return(r);
        }