protected internal override void Unproject(double x, double y, out double latitude, out double longitude)
        {
            var ro = Math.Sign(_n) * Math.Sqrt(x * x + MathX.Square(_ro0 - y));

            // If ro is zero or very small then latitude will be +-Pi/2 depending on the sign of f.
            latitude  = 2 * Math.Atan(Math.Pow(_f / ro, _nInv)) - Math.PI / 2;
            longitude = MathX.Clamp(Math.PI, Math.Atan2(x, _ro0 - y) * _nInv);
        }
        protected internal override void Unproject(double x, double y, out double latitude, out double longitude)
        {
            var ros = x * x + (_ro0 - y) * (_ro0 - y);
            var a   = _cOverN2 - ros * _nHalf;

            if (double.IsNaN(a) || Math.Abs(a) > 1 + MathX.Tolerance)
            {
                throw new ArgumentOutOfRangeException(paramName: string.Join(",", new [] { nameof(x), nameof(y) }));
            }

            latitude  = Math.Asin(MathX.Clamp(1, a));
            longitude = MathX.Clamp(Math.PI, MathX.Atan2(x, _ro0 - y, "x, y") * _invN);
        }
        protected internal override void Project(double latitude, double longitude, out double x, out double y)
        {
            var sinLatitude = Math.Sin(latitude);
            var cosLatitude = Math.Cos(latitude);

            var sinLongitude = Math.Sin(longitude);
            var cosLongitude = Math.Cos(longitude);

            var a = _sinFiP * sinLatitude - _cosFiP * cosLatitude * sinLongitude;             // sin_fi_p

            if (double.IsNaN(a) || Math.Abs(a) > 1)
            {
                throw new ArgumentOutOfRangeException(string.Join(",", new[] { nameof(latitude), nameof(longitude) }));
            }

            x = MathX.Atan2(sinLatitude / cosLatitude * _cosFiP + sinLongitude * _sinFiP, cosLongitude, "latitude, longitude");
            y = MathX.Clamp(MaxY, Math.Log((1 + a) / (1 - a)) / 2);             // protect against +-Infinity
        }
        // x will be in range [-MaxX, MaxX]
        // y will be in range [-Pi, Pi]
        protected internal override void Project(double latitude, double longitude, out double x, out double y)
        {
            // North pole
            if (latitude >= Math.PI / 2 - MathX.Tolerance)
            {
                x = 0;
                y = Math.PI / 2;
                return;
            }

            // South pole
            if (latitude <= -Math.PI / 2 + MathX.Tolerance)
            {
                x = 0;
                y = -Math.PI / 2;
                return;
            }

            if (Math.Abs(latitude) <= MathX.Tolerance)
            {
                // East of India
                if (Math.Abs(longitude - Math.PI / 2) <= MathX.Tolerance)
                {
                    x = MaxX;
                    y = 0;
                    return;
                }
                // West of South America
                if (Math.Abs(longitude + Math.PI / 2) <= MathX.Tolerance)
                {
                    x = -MaxX;
                    y = 0;
                    return;
                }
            }

            double b = Math.Cos(latitude) * Math.Sin(longitude);

            x = MathX.Clamp(MaxX, Math.Log((1 + b) / (1 - b)) / 2);
            y = Math.Atan2(Math.Tan(latitude), Math.Cos(longitude));
        }
 protected internal override void Project(double latitude, double longitude, out double x, out double y)
 {
     x = longitude;
     y = MathX.Clamp(_maxY, Math.Log(Math.Tan(Math.PI / 4 + latitude / 2)));             // protect against +-Infinity
 }