예제 #1
0
        /**
         * Forward projection, from geographic to Lambert conformal conic.
         *
         * @param[in] lon0 central meridian longitude (degrees).
         * @param[in] lat latitude of point (degrees).
         * @param[in] lon longitude of point (degrees).
         * @param[out] x easting of point (meters).
         * @param[out] y northing of point (meters).
         * @param[out] gamma meridian convergence at point (degrees).
         * @param[out] k azimuthal scale of projection at point; the radial
         *   scale is the 1/\e k.
         *
         * The latitude origin is given by AlbersEqualArea::LatitudeOrigin().  No
         * false easting or northing is added and \e lat should be in the range
         * [−90°, 90°].  The values of \e x and \e y returned for
         * points which project to infinity (i.e., one or both of the poles) will
         * be large but finite.
         **********************************************************************/
        public void Forward(double lon0, double lat, double lon,
                            out double x, out double y, out double gamma, out double k)
        {
            double e;

            lon  = GeoMath.AngDiff(lon0, lon, out e);
            lat *= _sign;
            double sphi, cphi;

            GeoMath.Sincosd(GeoMath.LatFix(lat) * _sign, out sphi, out cphi);
            cphi = Math.Max(epsx_, cphi);
            double
                lam = lon * GeoMath.Degree,
                tphi = sphi / cphi, txi = txif(tphi), sxi = txi / hyp(txi),
                dq = _qZ * Dsn(txi, _txi0, sxi, _sxi0) * (txi - _txi0),
                drho = -_a * dq / (Math.Sqrt(_m02 - _n0 * dq) + _nrho0 / _a),
                theta = _k2 * _n0 * lam, stheta = Math.Sin(theta), ctheta = Math.Cos(theta),
                t = _nrho0 + _n0 * drho;

            x = t * (_n0 != 0 ? stheta / _n0 : _k2 * lam) / _k0;
            y = (_nrho0 *
                 (_n0 != 0 ?
                  (ctheta < 0 ? 1 - ctheta : GeoMath.Square(stheta) / (1 + ctheta)) / _n0 :
                  0)
                 - drho * ctheta) / _k0;
            k     = _k0 * (t != 0 ? t * hyp(_fm * tphi) / _a : 1);
            y    *= _sign;
            gamma = _sign * theta / GeoMath.Degree;
        }
예제 #2
0
        /**
         * Reset the origin.
         *
         * @param[in] lat0 latitude at origin (degrees).
         * @param[in] lon0 longitude at origin (degrees).
         * @param[in] h0 height above ellipsoid at origin (meters); default 0.
         *
         * \e lat0 should be in the range [&minus;90&deg;, 90&deg;].
         **********************************************************************/
        public void Reset(double lat0, double lon0, double h0 = 0)
        {
            _lat0 = GeoMath.LatFix(lat0);
            _lon0 = GeoMath.AngNormalize(lon0);
            _h0   = h0;
            _earth.Forward(_lat0, _lon0, _h0, out _x0, out _y0, out _z0);
            double sphi, cphi, slam, clam;

            GeoMath.Sincosd(_lat0, out sphi, out cphi);
            GeoMath.Sincosd(_lon0, out slam, out clam);
            Geocentric.Rotation(sphi, cphi, slam, clam, _r);
        }
예제 #3
0
        internal void IntForward(double lat, double lon, double h, out double X, out double Y, out double Z,
                                 double[] M)
        {
            double sphi, cphi, slam, clam;

            GeoMath.Sincosd(GeoMath.LatFix(lat), out sphi, out cphi);
            GeoMath.Sincosd(lon, out slam, out clam);
            double n = _a / Math.Sqrt(1 - _e2 * GeoMath.Square(sphi));

            Z  = (_e2m * n + h) * sphi;
            X  = (n + h) * cphi;
            Y  = X * slam;
            X *= clam;
            if (M != null)
            {
                Rotation(sphi, cphi, slam, clam, M);
            }
        }
예제 #4
0
        private void LineInit(Geodesic g,
                              double lat1, double lon1,
                              double azi1, double salp1, double calp1,
                              int caps)
        {
            _lat1  = GeoMath.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 = ((int)caps | (int)GeodesicMask.LATITUDE | (int)GeodesicMask.AZIMUTH | (int)GeodesicMask.LONG_UNROLL); //TODO: check this

            double cbet1, sbet1;

            GeoMath.Sincosd(GeoMath.AngRound(_lat1), out sbet1, out cbet1); sbet1 *= _f1;
            // Ensure cbet1 = +epsilon at poles
            GeoMath.Norm(ref sbet1, ref cbet1);
            cbet1 = Math.Max(Geodesic.tiny_, cbet1);
            _dn1  = Math.Sqrt(1 + g._ep2 * GeoMath.Square(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 = GeoMath.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 Math.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;
            GeoMath.Norm(ref _ssig1, ref _csig1); // sig1 in (-pi, pi]
                                                  // Math::norm(_somg1, _comg1); -- don't need to normalize!

            _k2 = GeoMath.Square(_calp0) * g._ep2;
            double eps = _k2 / (2 * (1 + Math.Sqrt(1 + _k2)) + _k2);

            if ((_caps & GeodesicMask.CAP_C1) != 0)
            {
                _C1a  = new double[nC1_ + 1];
                _A1m1 = GeodesicCoeff.A1m1f(eps);
                GeodesicCoeff.C1f(eps, _C1a);
                _B11 = Geodesic.SinCosSeries(true, _ssig1, _csig1, _C1a);
                double s = Math.Sin(_B11), c = Math.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 & GeodesicMask.CAP_C1p) != 0)
            {
                _C1pa = new double[nC1p_ + 1];
                GeodesicCoeff.C1pf(eps, _C1pa);
            }

            if ((_caps & GeodesicMask.CAP_C2) != 0)
            {
                _C2a  = new double[nC2_ + 1];
                _A2m1 = GeodesicCoeff.A2m1f(eps);
                GeodesicCoeff.C2f(eps, _C2a);
                _B21 = Geodesic.SinCosSeries(true, _ssig1, _csig1, _C2a);
            }

            if ((_caps & GeodesicMask.CAP_C3) != 0)
            {
                _C3a = new double[nC3_];
                g.C3f(eps, _C3a);
                _A3c = -_f *_salp0 *g.A3f(eps);

                _B31 = Geodesic.SinCosSeries(true, _ssig1, _csig1, _C3a);
            }

            if ((_caps & GeodesicMask.CAP_C4) != 0)
            {
                _C4a = new double[nC4_];
                g.C4f(eps, _C4a);
                // Multiplier = a^2 * e^2 * cos(alpha0) * sin(alpha0)
                _A4  = GeoMath.Square(_a) * _calp0 * _salp0 * g._e2;
                _B41 = Geodesic.SinCosSeries(false, _ssig1, _csig1, _C4a);
            }

            _a13 = _s13 = double.NaN;
        }
예제 #5
0
        /**
         * Forward projection, from geographic to transverse Mercator.
         *
         * @param[in] lon0 central meridian of the projection (degrees).
         * @param[in] lat latitude of point (degrees).
         * @param[in] lon longitude of point (degrees).
         * @param[out] x easting of point (meters).
         * @param[out] y northing of point (meters).
         * @param[out] gamma meridian convergence at point (degrees).
         * @param[out] k scale of projection at point.
         *
         * No false easting or northing is added. \e lat should be in the range
         * [&minus;90&deg;, 90&deg;].
         **********************************************************************/
        public void Forward(double lon0, double lat, double lon,
                            out double x, out double y, out double gamma, out double k)
        {
            double e;

            lat = GeoMath.LatFix(lat);
            lon = GeoMath.AngDiff(lon0, lon, out e);
            // Explicitly enforce the parity
            int
                latsign = (lat < 0) ? -1 : 1,
                lonsign = (lon < 0) ? -1 : 1;

            lon *= lonsign;
            lat *= latsign;
            bool backside = lon > 90;

            if (backside)
            {
                if (lat == 0)
                {
                    latsign = -1;
                }
                lon = 180 - lon;
            }
            double sphi, cphi, slam, clam;

            GeoMath.Sincosd(lat, out sphi, out cphi);
            GeoMath.Sincosd(lon, out slam, out clam);
            // phi = latitude
            // phi' = conformal latitude
            // psi = isometric latitude
            // tau = tan(phi)
            // tau' = tan(phi')
            // [xi', eta'] = Gauss-Schreiber TM coordinates
            // [xi, eta] = Gauss-Krueger TM coordinates
            //
            // We use
            //   tan(phi') = Math.Sinh(psi)
            //   Math.Sin(phi') = tanh(psi)
            //   Math.Cos(phi') = sech(psi)
            //   denom^2    = 1-Math.Cos(phi')^2*Math.Sin(lam)^2 = 1-sech(psi)^2*Math.Sin(lam)^2
            //   Math.Sin(xip)   = Math.Sin(phi')/denom          = tanh(psi)/denom
            //   Math.Cos(xip)   = Math.Cos(phi')*Math.Cos(lam)/denom = sech(psi)*Math.Cos(lam)/denom
            //   Math.Cosh(etap) = 1/denom                  = 1/denom
            //   Math.Sinh(etap) = Math.Cos(phi')*Math.Sin(lam)/denom = sech(psi)*Math.Sin(lam)/denom
            double etap, xip;

            if (lat != 90)
            {
                double
                    tau  = sphi / cphi,
                    taup = GeoMath.Taupf(tau, _es);
                xip = Math.Atan2(taup, clam);
                // Used to be
                //   etap = Math::atanh(Math.Sin(lam) / Math.Cosh(psi));
                etap = GeoMath.Asinh(slam / GeoMath.Hypot(taup, clam));
                // convergence and scale for Gauss-Schreiber TM (xip, etap) -- gamma0 =
                // atan(tan(xip) * tanh(etap)) = atan(tan(lam) * Math.Sin(phi'));
                // Math.Sin(phi') = tau'/Math.Sqrt(1 + tau'^2)
                // Krueger p 22 (44)
                gamma = GeoMath.Atan2d(slam * taup, clam * GeoMath.Hypot(1, taup));
                // k0 = Math.Sqrt(1 - _e2 * Math.Sin(phi)^2) * (Math.Cos(phi') / Math.Cos(phi)) * Math.Cosh(etap)
                // Note 1/Math.Cos(phi) = Math.Cosh(psip);
                // and Math.Cos(phi') * Math.Cosh(etap) = 1/hypot(Math.Sinh(psi), Math.Cos(lam))
                //
                // This form has cancelling errors.  This property is lost if Math.Cosh(psip)
                // is replaced by 1/Math.Cos(phi), even though it's using "primary" data (phi
                // instead of psip).
                k = Math.Sqrt(_e2m + _e2 * GeoMath.Square(cphi)) * GeoMath.Hypot(1, tau)
                    / GeoMath.Hypot(taup, clam);
            }
            else
            {
                xip   = Math.PI / 2;
                etap  = 0;
                gamma = lon;
                k     = _c;
            }
            // {xi',eta'} is {northing,easting} for Gauss-Schreiber transverse Mercator
            // (for eta' = 0, xi' = bet). {xi,eta} is {northing,easting} for transverse
            // Mercator with ant scale on the central meridian (for eta = 0, xip =
            // rectifying latitude).  Define
            //
            //   zeta = xi + i*eta
            //   zeta' = xi' + i*eta'
            //
            // The conversion from conformal to rectifying latitude can be expressed as
            // a series in _n:
            //
            //   zeta = zeta' + sum(h[j-1]' * Math.Sin(2 * j * zeta'), j = 1..maxpow_)
            //
            // where h[j]' = O(_n^j).  The reversion of this series gives
            //
            //   zeta' = zeta - sum(h[j-1] * Math.Sin(2 * j * zeta), j = 1..maxpow_)
            //
            // which is used in Reverse.
            //
            // Evaluate sums via Clenshaw method.  See
            //    https://en.wikipedia.org/wiki/Clenshaw_algorithm
            //
            // Let
            //
            //    S = sum(a[k] * phi[k](x), k = 0..n)
            //    phi[k+1](x) = alpha[k](x) * phi[k](x) + beta[k](x) * phi[k-1](x)
            //
            // Evaluate S with
            //
            //    b[n+2] = b[n+1] = 0
            //    b[k] = alpha[k](x) * b[k+1] + beta[k+1](x) * b[k+2] + a[k]
            //    S = (a[0] + beta[1](x) * b[2]) * phi[0](x) + b[1] * phi[1](x)
            //
            // Here we have
            //
            //    x = 2 * zeta'
            //    phi[k](x) = Math.Sin(k * x)
            //    alpha[k](x) = 2 * Math.Cos(x)
            //    beta[k](x) = -1
            //    [ Math.Sin(A+B) - 2*Math.Cos(B)*Math.Sin(A) + Math.Sin(A-B) = 0, A = k*x, B = x ]
            //    n = maxpow_
            //    a[k] = _alp[k]
            //    S = b[1] * Math.Sin(x)
            //
            // For the derivative we have
            //
            //    x = 2 * zeta'
            //    phi[k](x) = Math.Cos(k * x)
            //    alpha[k](x) = 2 * Math.Cos(x)
            //    beta[k](x) = -1
            //    [ Math.Cos(A+B) - 2*Math.Cos(B)*Math.Cos(A) + Math.Cos(A-B) = 0, A = k*x, B = x ]
            //    a[0] = 1; a[k] = 2*k*_alp[k]
            //    S = (a[0] - b[2]) + b[1] * Math.Cos(x)
            //
            // Matrix formulation (not used here):
            //    phi[k](x) = [Math.Sin(k * x); k * Math.Cos(k * x)]
            //    alpha[k](x) = 2 * [Math.Cos(x), 0; -Math.Sin(x), Math.Cos(x)]
            //    beta[k](x) = -1 * [1, 0; 0, 1]
            //    a[k] = _alp[k] * [1, 0; 0, 1]
            //    b[n+2] = b[n+1] = [0, 0; 0, 0]
            //    b[k] = alpha[k](x) * b[k+1] + beta[k+1](x) * b[k+2] + a[k]
            //    N.B., for all k: b[k](1,2) = 0; b[k](1,1) = b[k](2,2)
            //    S = (a[0] + beta[1](x) * b[2]) * phi[0](x) + b[1] * phi[1](x)
            //    phi[0](x) = [0; 0]
            //    phi[1](x) = [Math.Sin(x); Math.Cos(x)]
            double
                c0 = Math.Cos(2 * xip), ch0 = Math.Cosh(2 * etap),
                s0 = Math.Sin(2 * xip), sh0 = Math.Sinh(2 * etap);

            int     n  = maxpow_;
            Complex a  = new Complex(2 * c0 * ch0, -2 * s0 * sh0); // 2 * Math.Cos(2*zeta')
            Complex y0 = new Complex((n & 1) != 0 ? _alp[n] : 0, 0);
            Complex y1;                                            // default initializer is 0+i0
            Complex z0 = new Complex((n & 1) != 0 ? 2 * n * _alp[n] : 0, 0);
            Complex z1;

            if ((n & 1) != 0)
            {
                --n;
            }
            while (n > 0)
            {
                y1 = a * y0 - y1 + _alp[n];
                z1 = a * z0 - z1 + 2 * n * _alp[n];
                --n;
                y0 = a * y1 - y0 + _alp[n];
                z0 = a * z1 - z0 + 2 * n * _alp[n];
                --n;
            }
            a /= 2;                               // Math.Cos(2*zeta')
            z1 = 1 - z1 + a * z0;
            a  = new Complex(s0 * ch0, c0 * sh0); // Math.Sin(2*zeta')
            y1 = new Complex(xip, etap) + a * y0;
            // Fold in change in convergence and scale for Gauss-Schreiber TM to
            // Gauss-Krueger TM.
            gamma -= GeoMath.Atan2d(z1.Imaginary, z1.Real);
            k     *= _b1 * Complex.Abs(z1);
            double xi = y1.Real, eta = y1.Imaginary;

            y = _a1 * _k0 * (backside ? Math.PI - xi : xi) * latsign;
            x = _a1 * _k0 * eta * lonsign;
            if (backside)
            {
                gamma = 180 - gamma;
            }
            gamma *= latsign * lonsign;
            gamma  = GeoMath.AngNormalize(gamma);
            k     *= _k0;
        }