Exemplo n.º 1
0
        /// <summary>
        /// Convert angle (in degrees) into a DMS string (using d, ', and ").
        /// </summary>
        /// <param name="angle">input angle (degrees)</param>
        /// <param name="trailing"><see cref="TrailingUnit"/> value indicating the trailing units of the string (this component is given as a decimal number if necessary).</param>
        /// <param name="prec">the number of digits after the decimal point for the trailing component.</param>
        /// <param name="ind"><see cref="HemisphereIndicator"/> value indicating additional formatting.</param>
        /// <param name="dmssep">if not <c>0</c>, use as the <see cref="DMS"/> separator character (instead of d, ', " delimiters).</param>
        /// <returns>formatted string</returns>
        /// <remarks>
        /// The interpretation of <paramref name="ind"/> is as follows:
        /// <list type="bullet">
        /// <item><paramref name="ind"/> == <see cref="HemisphereIndicator.None"/>,
        /// signed result no leading zeros on degrees except in the units place, e.g., <c>-8d03'</c>.</item>
        /// <item><paramref name="ind"/> == <see cref="HemisphereIndicator.Latitude"/>,
        /// trailing N or S hemisphere designator, no sign, pad degrees to 2 digits, e.g., <c>08d03'S</c>.
        /// </item>
        /// <item><paramref name="ind"/> == <see cref="HemisphereIndicator.Longitude"/>,
        /// trailing E or W hemisphere designator, no sign, pad degrees to 3 digits, e.g., <c>008d03'W</c>.
        /// </item>
        /// <item><paramref name="ind"/> == <see cref="HemisphereIndicator.Azimuth"/>,
        /// convert to the range [0, 360°), no sign, pad degrees to 3 digits, e.g., <c>351d57'</c>.
        /// </item>
        /// </list>
        /// The integer parts of the minutes and seconds components are always given with 2 digits.
        /// </remarks>
        public static string Encode(double angle, TrailingUnit trailing, int prec, HemisphereIndicator ind = HemisphereIndicator.None, char dmssep = '\0')
        {
            // Assume check on range of input angle has been made by calling
            // routine (which might be able to offer a better diagnostic).
            if (!IsFinite(angle))
            {
                return(angle < 0 ? "-inf" :
                       (angle > 0 ? "inf" : "nan"));
            }

            // 15 - 2 * trailing = ceiling(log10(2^53/90/60^trailing)).
            // This suffices to give full real precision for numbers in [-90,90]
            prec = Min(15 + 0 - 2 * (int)trailing, prec);
            var scale = 1d;

            for (var i = 0; i < (int)trailing; ++i)
            {
                scale *= 60;
            }
            for (var i = 0; i < prec; ++i)
            {
                scale *= 10;
            }
            if (ind == HemisphereIndicator.Azimuth)
            {
                angle -= Floor(angle / 360) * 360;
            }
            int sign = angle < 0 ? -1 : 1;

            angle *= sign;

            // Break off integer part to preserve precision in manipulation of
            // fractional part.
            double
                idegree = Floor(angle),
                fdegree = (angle - idegree) * scale + 0.5;

            {
                // Implement the "round ties to even" rule
                var f = Floor(fdegree);
                fdegree = (f == fdegree && (f % 2) == 1) ? f - 1 : f;
            }
            fdegree /= scale;
            if (fdegree >= 1)
            {
                idegree += 1;
                fdegree -= 1;
            }
            Span <double> pieces = stackalloc[] { fdegree, 0, 0 };

            for (var i = 1; i <= (int)trailing; ++i)
            {
                double
                    ip = Floor(pieces[i - 1]),
                    fp = pieces[i - 1] - ip;
                pieces[i]     = fp * 60;
                pieces[i - 1] = ip;
            }
            pieces[0] += idegree;
            var s = new StringBuilder();

            if (ind == HemisphereIndicator.None && sign < 0)
            {
                s.Append('-');
            }
            var w = 0;

            switch (trailing)
            {
            case TrailingUnit.Degree:
                if (ind != HemisphereIndicator.None)
                {
                    w = 1 + Min((int)ind, 2) + prec + (prec != 0 ? 1 : 0);
                }
                s.Append(pieces[0].ToFixedString(prec).PadLeft(w, '0'));
                // Don't include degree designator (d) if it is the trailing component.
                break;

            default:
                if (ind != HemisphereIndicator.None)
                {
                    w = 1 + Min((int)ind, 2);
                }
                s.Append(pieces[0].ToString().PadLeft(w, '0'))
                .Append(dmssep != 0 ? dmssep : char.ToLower(dmsindicators_[0]));
                switch (trailing)
                {
                case TrailingUnit.Minute:
                    w = 2 + prec + (prec != 0 ? 1 : 0);
                    s.Append(pieces[1].ToFixedString(prec).PadLeft(w, '0'));
                    if (dmssep != 0)
                    {
                        s.Append(char.ToLower(dmsindicators_[1]));
                    }
                    break;

                case TrailingUnit.Second:
                    s.AppendFormat("{0:D2}", (int)pieces[1])
                    .Append(dmssep != 0 ? dmssep : char.ToLower(dmsindicators_[1]))
                    .Append(pieces[2].ToFixedString(prec).PadLeft(2 + prec + (prec != 0 ? 1 : 0), '0'));
                    if (dmssep == 0)
                    {
                        s.Append(char.ToLower(dmsindicators_[2]));
                    }
                    break;

                default:
                    break;
                }
                break;
            }
            if (ind != HemisphereIndicator.None && ind != HemisphereIndicator.Azimuth)
            {
                s.Append(hemispheres_[(ind == HemisphereIndicator.Latitude ? 0 : 2) + (sign < 0 ? 0 : 1)]);
            }
            return(s.ToString());
        }
Exemplo n.º 2
0
 /// <summary>
 /// Convert angle into a <see cref="DMS"/> string (using d, ', and ") selecting the trailing component based on the precision.
 /// </summary>
 /// <param name="angle">input angle (degrees)</param>
 /// <param name="prec">the precision relative to 1 degree.</param>
 /// <param name="ind"><see cref="HemisphereIndicator"/> value indicating additional formatting.</param>
 /// <param name="dmssep">if not <c>0</c>, use as the <see cref="DMS"/> separator character (instead of d, ', " delimiters).</param>
 /// <returns>formatted string</returns>
 /// <remarks>
 /// <paramref name="prec"/> indicates the precision relative to 1 degree, e.g., <paramref name="prec"/> = 3 gives a result accurate to 0.1' and
 /// <paramref name="prec"/> = 4 gives a result accurate to 1". <paramref name="ind"/> is interpreted as in
 /// <see cref="Encode(double, TrailingUnit, int, HemisphereIndicator, char)"/> with the additional facility that <see cref="HemisphereIndicator.Number"/>
 /// represents angle as a number in fixed format with precision <paramref name="prec"/>.
 /// </remarks>
 public static string Encode(double angle, int prec, HemisphereIndicator ind = HemisphereIndicator.None, char dmssep = '\0')
 => ind == HemisphereIndicator.Number ? angle.ToFixedString(prec) :
 Encode(angle,
        prec < 2 ? TrailingUnit.Degree : (prec < 4 ? TrailingUnit.Minute : TrailingUnit.Second),
        prec < 2 ? prec : (prec < 4 ? prec - 2 : prec - 4),
        ind, dmssep);