def GenPosition(self, arcmode, s12_a12, outmask): from geographiclib.geodesic import Geodesic a12 = lat2 = lon2 = azi2 = s12 = m12 = M12 = M21 = S12 = Math.nan outmask &= self._caps & Geodesic.OUT_ALL if not (arcmode or (self._caps & Geodesic.DISTANCE_IN & Geodesic.OUT_ALL)): # Uninitialized or impossible distance calculation requested return a12, lat2, lon2, azi2, s12, m12, M12, M21, S12 # Avoid warning about uninitialized B12. B12 = 0 AB1 = 0 if arcmode: # Interpret s12_a12 as spherical arc length sig12 = s12_a12 * Math.degree s12a = abs(s12_a12) s12a -= 180 * math.floor(s12a / 180) ssig12 = 0 if s12a == 0 else math.sin(sig12) csig12 = 0 if s12a == 90 else math.cos(sig12) else: # Interpret s12_a12 as distance tau12 = s12_a12 / (self._b * (1 + self._A1m1)) s = math.sin(tau12) c = math.cos(tau12) # tau2 = tau1 + tau12 B12 = -Geodesic.SinCosSeries( True, self._stau1 * c + self._ctau1 * s, self._ctau1 * c - self._stau1 * s, self._C1pa, Geodesic.nC1p_) sig12 = tau12 - (B12 - self._B11) ssig12 = math.sin(sig12) csig12 = math.cos(sig12) if abs(self._f) > 0.01: # Reverted distance series is inaccurate for |f| > 1/100, so correct # sig12 with 1 Newton iteration. The following table shows the # approximate maximum error for a = WGS_a() and various f relative to # GeodesicExact. # erri = the error in the inverse solution (nm) # errd = the error in the direct solution (series only) (nm) # errda = the error in the direct solution (series + 1 Newton) (nm) # # f erri errd errda # -1/5 12e6 1.2e9 69e6 # -1/10 123e3 12e6 765e3 # -1/20 1110 108e3 7155 # -1/50 18.63 200.9 27.12 # -1/100 18.63 23.78 23.37 # -1/150 18.63 21.05 20.26 # 1/150 22.35 24.73 25.83 # 1/100 22.35 25.03 25.31 # 1/50 29.80 231.9 30.44 # 1/20 5376 146e3 10e3 # 1/10 829e3 22e6 1.5e6 # 1/5 157e6 3.8e9 280e6 ssig2 = self._ssig1 * csig12 + self._csig1 * ssig12 csig2 = self._csig1 * csig12 - self._ssig1 * ssig12 B12 = Geodesic.SinCosSeries(True, ssig2, csig2, self._C1a, Geodesic.nC1_) serr = ((1 + self._A1m1) * (sig12 + (B12 - self._B11)) - s12_a12 / self._b) sig12 = sig12 - serr / math.sqrt(1 + self._k2 * Math.sq(ssig2)) ssig12 = math.sin(sig12) csig12 = math.cos(sig12) # Update B12 below # real omg12, lam12, lon12 # real ssig2, csig2, sbet2, cbet2, somg2, comg2, salp2, calp2 # sig2 = sig1 + sig12 ssig2 = self._ssig1 * csig12 + self._csig1 * ssig12 csig2 = self._csig1 * csig12 - self._ssig1 * ssig12 dn2 = math.sqrt(1 + self._k2 * Math.sq(ssig2)) if outmask & (Geodesic.DISTANCE | Geodesic.REDUCEDLENGTH | Geodesic.GEODESICSCALE): if arcmode or abs(self._f) > 0.01: B12 = Geodesic.SinCosSeries(True, ssig2, csig2, self._C1a, Geodesic.nC1_) AB1 = (1 + self._A1m1) * (B12 - self._B11) # sin(bet2) = cos(alp0) * sin(sig2) sbet2 = self._calp0 * ssig2 # Alt: cbet2 = hypot(csig2, salp0 * ssig2) cbet2 = math.hypot(self._salp0, self._calp0 * csig2) if cbet2 == 0: # I.e., salp0 = 0, csig2 = 0. Break the degeneracy in this case cbet2 = csig2 = Geodesic.tiny_ # tan(omg2) = sin(alp0) * tan(sig2) somg2 = self._salp0 * ssig2 comg2 = csig2 # No need to normalize # tan(alp0) = cos(sig2)*tan(alp2) salp2 = self._salp0 calp2 = self._calp0 * csig2 # No need to normalize # omg12 = omg2 - omg1 omg12 = math.atan2(somg2 * self._comg1 - comg2 * self._somg1, comg2 * self._comg1 + somg2 * self._somg1) if outmask & Geodesic.DISTANCE: s12 = self._b * ( (1 + self._A1m1) * sig12 + AB1) if arcmode else s12_a12 if outmask & Geodesic.LONGITUDE: lam12 = omg12 + self._A3c * (sig12 + (Geodesic.SinCosSeries( True, ssig2, csig2, self._C3a, Geodesic.nC3_ - 1) - self._B31)) lon12 = lam12 / Math.degree # Use Math.AngNormalize2 because longitude might have wrapped # multiple times. lon12 = Math.AngNormalize2(lon12) lon2 = Math.AngNormalize(self._lon1 + lon12) if outmask & Geodesic.LATITUDE: lat2 = math.atan2(sbet2, self._f1 * cbet2) / Math.degree if outmask & Geodesic.AZIMUTH: # minus signs give range [-180, 180). 0- converts -0 to +0. azi2 = 0 - math.atan2(-salp2, calp2) / Math.degree if outmask & (Geodesic.REDUCEDLENGTH | Geodesic.GEODESICSCALE): B22 = Geodesic.SinCosSeries(True, ssig2, csig2, self._C2a, Geodesic.nC2_) AB2 = (1 + self._A2m1) * (B22 - self._B21) J12 = (self._A1m1 - self._A2m1) * sig12 + (AB1 - AB2) if outmask & Geodesic.REDUCEDLENGTH: # Add parens around (_csig1 * ssig2) and (_ssig1 * csig2) to ensure # accurate cancellation in the case of coincident points. m12 = self._b * ( (dn2 * (self._csig1 * ssig2) - self._dn1 * (self._ssig1 * csig2)) - self._csig1 * csig2 * J12) if outmask & Geodesic.GEODESICSCALE: t = (self._k2 * (ssig2 - self._ssig1) * (ssig2 + self._ssig1) / (self._dn1 + dn2)) M12 = csig12 + (t * ssig2 - csig2 * J12) * self._ssig1 / self._dn1 M21 = csig12 - (t * self._ssig1 - self._csig1 * J12) * ssig2 / dn2 if outmask & Geodesic.AREA: B42 = Geodesic.SinCosSeries(False, ssig2, csig2, self._C4a, Geodesic.nC4_) # real salp12, calp12 if self._calp0 == 0 or self._salp0 == 0: # alp12 = alp2 - alp1, used in atan2 so no need to normalized salp12 = salp2 * self._calp1 - calp2 * self._salp1 calp12 = calp2 * self._calp1 + salp2 * self._salp1 # The right thing appears to happen if alp1 = +/-180 and alp2 = 0, viz # salp12 = -0 and alp12 = -180. However this depends on the sign being # attached to 0 correctly. The following ensures the correct behavior. if salp12 == 0 and calp12 < 0: salp12 = Geodesic.tiny_ * self._calp1 calp12 = -1 else: # tan(alp) = tan(alp0) * sec(sig) # tan(alp2-alp1) = (tan(alp2) -tan(alp1)) / (tan(alp2)*tan(alp1)+1) # = calp0 * salp0 * (csig1-csig2) / (salp0^2 + calp0^2 * csig1*csig2) # If csig12 > 0, write # csig1 - csig2 = ssig12 * (csig1 * ssig12 / (1 + csig12) + ssig1) # else # csig1 - csig2 = csig1 * (1 - csig12) + ssig12 * ssig1 # No need to normalize salp12 = self._calp0 * self._salp0 * ( self._csig1 * (1 - csig12) + ssig12 * self._ssig1 if csig12 <= 0 else ssig12 * (self._csig1 * ssig12 / (1 + csig12) + self._ssig1)) calp12 = (Math.sq(self._salp0) + Math.sq(self._calp0) * self._csig1 * csig2) S12 = self._c2 * math.atan2(salp12, calp12) + self._A4 * (B42 - self._B41) a12 = s12_a12 if arcmode else sig12 / Math.degree return a12, lat2, lon2, azi2, s12, m12, M12, M21, S12