/// <summary> /// Forward projection, from geographic to gnomonic. /// </summary> /// <param name="lat0">latitude of center point of projection (degrees).</param> /// <param name="lon0">longitude of center point of projection (degrees).</param> /// <param name="lat">latitude of point (degrees).</param> /// <param name="lon">longitude of point (degrees).</param> /// <param name="azi">azimuth of geodesic at point (degrees).</param> /// <param name="rk">reciprocal of azimuthal scale at point.</param> /// <returns> /// <i>x</i>, easting of point (meters) and <i>y</i>, northing of point (meters). /// </returns> /// <remarks> /// <paramref name="lat0"/> and <paramref name="lat"/> should be in the range [−90°, 90°]. /// The scale of the projection is 1/<paramref name="rk"/>^2 in the "radial" direction, <paramref name="azi"/> clockwise from true north, /// and is 1/<paramref name="rk"/> in the direction perpendicular to this. If the point lies "over the horizon", i.e., /// if <paramref name="rk"/> ≤ 0, then <see cref="double.NaN"/>s are returned for <i>x</i> and <i>y</i> /// (the correct values are returned for <paramref name="azi"/> and <paramref name="rk"/>). /// A call to <see cref="Forward(double, double, double, double, out double, out double)"/> followed by a call to /// <see cref="Reverse(double, double, double, double, out double, out double)"/> will return the /// original (<paramref name="lat"/>, <paramref name="lon"/>) (to within roundoff) provided the point in not over the horizon. /// </remarks> public (double x, double y) Forward(double lat0, double lon0, double lat, double lon, out double azi, out double rk) { double x, y; _earth.GenInverse(lat0, lon0, lat, lon, GeodesicFlags.Azimuth | GeodesicFlags.ReducedLength | GeodesicFlags.GeodesicScale, out _, out var azi0, out azi, out var m, out var M, out _, out _); rk = M; if (M <= 0) { x = y = double.NaN; } else { var rho = m / M; SinCosd(azi0, out x, out y); x *= rho; y *= rho; } return(x, y); }