/** * Constructor for a geodesic line staring at latitude <i>lat1</i>, longitude * <i>lon1</i>, and azimuth <i>azi1</i> (all in degrees) with a subset of the * capabilities included. * <p> * @param g A {@link Geodesic} object used to compute the necessary * information about the GeodesicLine. * @param lat1 latitude of point 1 (degrees). * @param lon1 longitude of point 1 (degrees). * @param azi1 azimuth at point 1 (degrees). * @param caps bitor'ed combination of {@link GeodesicMask} values * specifying the capabilities the GeodesicLine object should possess, * i.e., which quantities can be returned in calls to {@link #Position * Position}. * <p> * The {@link GeodesicMask} values are * <ul> * <li> * <i>caps</i> |= {@link GeodesicMask#LATITUDE} for the latitude * <i>lat2</i>; this is added automatically; * <li> * <i>caps</i> |= {@link GeodesicMask#LONGITUDE} for the latitude * <i>lon2</i>; * <li> * <i>caps</i> |= {@link GeodesicMask#AZIMUTH} for the latitude * <i>azi2</i>; this is added automatically; * <li> * <i>caps</i> |= {@link GeodesicMask#DISTANCE} for the distance * <i>s12</i>; * <li> * <i>caps</i> |= {@link GeodesicMask#REDUCEDLENGTH} for the reduced length * <i>m12</i>; * <li> * <i>caps</i> |= {@link GeodesicMask#GEODESICSCALE} for the geodesic * scales <i>M12</i> and <i>M21</i>; * <li> * <i>caps</i> |= {@link GeodesicMask#AREA} for the area <i>S12</i>; * <li> * <i>caps</i> |= {@link GeodesicMask#DISTANCE_IN} permits the length of * the geodesic to be given in terms of <i>s12</i>; without this capability * the length can only be specified in terms of arc length; * <li> * <i>caps</i> |= {@link GeodesicMask#ALL} for all of the above. * </ul> **********************************************************************/ public GeodesicLine(Geodesic g, double lat1, double lon1, double azi1, int caps) { azi1 = GeoMath.AngNormalize(azi1); double salp1, calp1; // Guard against underflow in salp0 { Pair p = GeoMath.SinCosD(GeoMath.AngRound(azi1)); salp1 = p.First; calp1 = p.Second; } LineInit(g, lat1, lon1, azi1, salp1, calp1, caps); }
private void LineInit(Geodesic g, double lat1, double lon1, double azi1, double salp1, double calp1, int caps) { _a = g.equatorialRadius; _f = g.ellipsoidFlattening; _b = g._b; _c2 = g._c2; _f1 = g._f1; // Always allow latitude and azimuth and unrolling the longitude _caps = caps | GeodesicMask.LATITUDE | GeodesicMask.AZIMUTH | GeodesicMask.LONG_UNROLL; _lat1 = GeoMath.LatFix(lat1); _lon1 = lon1; _azi1 = azi1; _salp1 = salp1; _calp1 = calp1; double cbet1, sbet1; { Pair p = GeoMath.SinCosD(GeoMath.AngRound(_lat1)); sbet1 = _f1 * p.First; cbet1 = p.Second; } // Ensure cbet1 = +epsilon at poles { Pair p = GeoMath.Norm(sbet1, cbet1); sbet1 = p.First; cbet1 = Math.Max(Geodesic.tiny_, p.Second); } _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 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; { Pair p = GeoMath.Norm(_ssig1, _csig1); _ssig1 = p.First; _csig1 = p.Second; } // sig1 in (-pi, pi] // GeoMath.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) { _A1m1 = Geodesic.A1m1f(eps); _C1a = new double[nC1_ + 1]; Geodesic.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]; Geodesic.C1pf(eps, _C1pa); } if ((_caps & GeodesicMask.CAP_C2) != 0) { _C2a = new double[nC2_ + 1]; _A2m1 = Geodesic.A2m1f(eps); Geodesic.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); } }