/// <summary> /// Initialize a geod_geodesicline object. /// </summary> /// <param name="g">The <see cref="geod_geodesic"/> object specifying the ellipsoid.</param> /// <param name="lat1">Latitude of point 1 (degrees).</param> /// <param name="lon1">Longitude of point 1 (degrees).</param> /// <param name="azi1">Azimuth at point 1 (degrees).</param> /// <param name="caps">Bitor'ed combination of <see cref="geod_mask"/> values. See remarks for more informations.</param> /// <remarks> /// <paramref name="caps"/> bitor'ed combination of geod_mask() values specifying /// the capabilities the geod_geodesicline object should possess, i.e., which /// quantities can be returned in calls to geod_position() and /// geod_genposition(). /// /// <paramref name="lat1"/> should be in the range [-90deg, 90deg]. /// <paramref name="lon1"/> and <paramref name="azi1"/> should be in the range [-540deg, 540deg). /// /// The geod_mask values are: /// - caps|=GEOD_LATITUDE for the latitude lat2; this is added automatically, /// - caps|=GEOD_LONGITUDE for the latitude lon2, /// - caps|=GEOD_AZIMUTH for the latitude azi2; this is added automatically, /// - caps|=GEOD_DISTANCE for the distance s12, /// - caps|=GEOD_REDUCEDLENGTH for the reduced length m12, /// - caps|=GEOD_GEODESICSCALE for the geodesic scales M12 and M21, /// - caps|=GEOD_AREA for the area S12, /// - caps|=GEOD_DISTANCE_IN permits the length of the /// geodesic to be given in terms of s12; without this capability the /// length can only be specified in terms of arc length. /// /// A value of caps=0 is treated as GEOD.LATITUDE|GEOD.LONGITUDE|GEOD.AZIMUTH|GEOD.DISTANCE_IN /// (to support the solution of the "standard" direct problem). /// </remarks> public geod_geodesicline(geod_geodesic g, double lat1, double lon1, double azi1, GEOD caps) { a = g.a; f = g.f; b = g.b; c2 = g.c2; f1 = g.f1; // If caps is 0 assume the standard direct calculation this.caps = (caps != 0?caps:GEOD.DISTANCE_IN | GEOD.LONGITUDE) | GEOD.LATITUDE | GEOD.AZIMUTH | GEOD.LONG_UNROLL; // always allow latitude and azimuth and unrolling of longitude this.lat1 = lat1; this.lon1 = lon1; // Guard against underflow in salp0 this.azi1 = AngRound(AngNormalize(azi1)); // alp1 is in [0, pi] double alp1 = this.azi1 * degree; // Enforce sin(pi)==0 and cos(pi/2)==0. Better to face the ensuing // problems directly than to skirt them. salp1 = this.azi1 == -180?0:Math.Sin(alp1); calp1 = Math.Abs(this.azi1) == 90?0:Math.Cos(alp1); double phi = lat1 * degree; // Ensure cbet1=+epsilon at poles double sbet1 = f1 * Math.Sin(phi); double cbet1 = Math.Abs(lat1) == 90?tiny:Math.Cos(phi); norm2(ref sbet1, ref cbet1); dn1 = Math.Sqrt(1 + g.ep2 * sq(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 = hypotx(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 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; norm2(ref ssig1, ref csig1); // sig1 in (-pi, pi] // norm2(ref somg1, ref comg1); -- don't need to normalize! k2 = sq(calp0) * g.ep2; double eps = k2 / (2 * (1 + Math.Sqrt(1 + k2)) + k2); if ((this.caps & GEOD.CAP_C1) != 0) { double s, c; A1m1 = geod_geodesic.A1m1f(eps); geod_geodesic.C1f(eps, C1a); B11 = SinCosSeries(true, ssig1, csig1, C1a, nC1); 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 ((this.caps & GEOD.CAP_C1p) != 0) { geod_geodesic.C1pf(eps, C1pa); } if ((this.caps & GEOD.CAP_C2) != 0) { A2m1 = geod_geodesic.A2m1f(eps); geod_geodesic.C2f(eps, C2a); B21 = SinCosSeries(true, ssig1, csig1, C2a, nC2); } if ((this.caps & GEOD.CAP_C3) != 0) { g.C3f(eps, C3a); A3c = -f *salp0 *g.A3f(eps); B31 = SinCosSeries(true, ssig1, csig1, C3a, nC3 - 1); } if ((this.caps & GEOD.CAP_C4) != 0) { g.C4f(eps, C4a); // Multiplier=a^2*e^2*cos(alpha0)*sin(alpha0) A4 = sq(a) * calp0 * salp0 * g.e2; B41 = SinCosSeries(false, ssig1, csig1, C4a, nC4); } }