1

2# -*- coding: utf-8 -*-

3

4 u'''Wrapper to invoke I{Karney}'s U{GeodSolve

5<https://GeographicLib.SourceForge.io/C++/doc/GeodSolve.1.html>} utility

6 as an (exact) geodesic, but intended I{for testing purposes only}.

7

8 Set env variable C{PYGEODESY_GEODSOLVE} to the (fully qualified) path

9 of the C{GeodSolve} executable.

10'''

11

12 from pygeodesy.basics import _xinstanceof

13# from pygeodesy.constants import NAN, _0_0 # from .karney

14# from pygeodesy.geodesicx import GeodesicAreaExact # _MODS

15 from pygeodesy.interns import NN, _UNDER_

16 from pygeodesy.karney import Caps, GeodesicError, GeodSolve12Tuple, \

17 _sincos2d, _Xables, _0_0, NAN

18 from pygeodesy.lazily import _ALL_DOCS, _ALL_LAZY, _ALL_MODS as _MODS

19 from pygeodesy.named import _name1__

20 from pygeodesy.namedTuples import Destination3Tuple, Distance3Tuple

21 from pygeodesy.props import Property, Property_RO, property_RO

22 from pygeodesy.solveBase import _SolveGDictBase, _SolveGDictLineBase

23 from pygeodesy.utily import _unrollon, _Wrap, wrap360

24

25__all__ = _ALL_LAZY.geodsolve

26__version__ = '24.10.14'

27

28

29 class _GeodesicSolveBase(_SolveGDictBase):

30 '''(INTERNAL) Base class for L{GeodesicSolve} and L{GeodesicLineSolve}.

31 '''

32 _Error = GeodesicError

33 _Names_Direct = \

34 _Names_Inverse = GeodSolve12Tuple._Names_

35 _Xable_name = _Xables.GeodSolve.__name__

36 _Xable_path = _Xables.GeodSolve()

37

38 @Property_RO

39 def _b_option(self):

40 return ('-b',) if self.reverse2 else ()

41

42 @Property_RO

43 def _cmdBasic(self):

44 '''(INTERNAL) Get the basic C{GeodSolve} cmd (C{tuple}).

45 '''

46 return (self.GeodSolve, '-f') + (self._b_option +

47 self._e_option +

48 self._E_option +

49 self._p_option +

50 self._u_option)

51

52 @Property

53 def GeodSolve(self):

54 '''Get the U{GeodSolve<https://GeographicLib.SourceForge.io/C++/doc/GeodSolve.1.html>}

55 executable (C{filename}).

56 '''

57 return self._Xable_path

58

59 @GeodSolve.setter # PYCHOK setter!

60 def GeodSolve(self, path):

61 '''Set the U{GeodSolve<https://GeographicLib.SourceForge.io/C++/doc/GeodSolve.1.html>}

62 executable (C{filename}), the (fully qualified) path to the C{GeodSolve} executable.

63

64 @raise GeodesicError: Invalid B{C{path}}, B{C{path}} doesn't exist or

65 isn't the C{GeodSolve} executable.

66 '''

67 self._setXable(path)

68

69 def toStr(self, **prec_sep): # PYCHOK signature

70 '''Return this C{GeodesicSolve} as string.

71

72 @kwarg prec_sep: Keyword argumens C{B{prec}=6} and C{B{sep}=", "}

73 for the C{float} C{prec}ision, number of decimal digits

74 (0..9) and the C{sep}arator string to join. Trailing

75 zero decimals are stripped for B{C{prec}} values of 1

76 and above, but kept for negative B{C{prec}} values.

77

78 @return: GeodesicSolve items (C{str}).

79 '''

80 return _SolveGDictBase._toStr(self, GeodSolve=self.GeodSolve, **prec_sep)

81

82 @Property_RO

83 def _u_option(self):

84 return ('-u',) if self.unroll else ()

85

86

87 class GeodesicSolve(_GeodesicSolveBase):

88 '''Wrapper to invoke I{Karney}'s U{GeodSolve<https://GeographicLib.SourceForge.io/C++/doc/GeodSolve.1.html>}

89 as an C{Exact} version of I{Karney}'s Python class U{Geodesic<https://GeographicLib.SourceForge.io/C++/doc/

90 python/code.html#geographiclib.geodesic.Geodesic>}.

91

92 @note: Use property C{GeodSolve} or env variable C{PYGEODESY_GEODSOLVE} to specify the (fully

93 qualified) path to the C{GeodSolve} executable.

94

95 @note: This C{geodesic} is intended I{for testing purposes only}, it invokes the C{GeodSolve}

96 executable for I{every} method call.

97 '''

98

99 def Area(self, polyline=False, **name):

100 '''Set up a L{GeodesicAreaExact} to compute area and perimeter

101 of a polygon.

102

103 @kwarg polyline: If C{True}, compute the perimeter only, otherwise

104 perimeter and area (C{bool}).

105 @kwarg name: Optional C{B{name}=NN} (C{str}).

106

107 @return: A L{GeodesicAreaExact} instance.

108

109 @note: The B{C{debug}} setting is passed as C{verbose}

110 to the returned L{GeodesicAreaExact} instance.

111 '''

112 gaX = _MODS.geodesicx.GeodesicAreaExact(self, polyline=polyline, **name)

113 if self.verbose or self.debug: # PYCHOK no cover

114 gaX.verbose = True

115 return gaX

116

117 Polygon = Area # for C{geographiclib} compatibility

118

119 def Direct3(self, lat1, lon1, azi1, s12): # PYCHOK outmask

120 '''Return the destination lat, lon and reverse azimuth (final bearing)

121 in C{degrees}.

122

123 @return: L{Destination3Tuple}C{(lat, lon, final)}.

124 '''

125 r = self._GDictDirect(lat1, lon1, azi1, False, s12, floats=False)

126 return Destination3Tuple(float(r.lat2), float(r.lon2), wrap360(r.azi2),

127 iteration=r._iteration)

128

129 def _DirectLine(self, ll1, azi12, **caps_name): # PYCHOK no cover

130 '''(INTERNAL) Short-cut version.

131 '''

132 return self.DirectLine(ll1.lat, ll1.lon, azi12, **caps_name)

133

134 def DirectLine(self, lat1, lon1, azi1, **caps_name):

135 '''Set up a L{GeodesicLineSolve} to compute several points

136 on a single geodesic.

137

138 @arg lat1: Latitude of the first point (C{degrees}).

139 @arg lon1: Longitude of the first point (C{degrees}).

140 @arg azi1: Azimuth at the first point (compass C{degrees}).

141 @kwarg caps_name: Optional C{B{name}=NN} (C{str}) and keyword

142 argument C{B{caps}=Caps.ALL}, bit-or'ed combination

143 of L{Caps} values specifying the capabilities the

144 L{GeodesicLineSolve} instance should possess.

145

146 @return: A L{GeodesicLineSolve} instance.

147

148 @note: If the point is at a pole, the azimuth is defined by keeping

149 B{C{lon1}} fixed, writing C{B{lat1} = ±(90 − ε)}, and taking

150 the limit C{ε → 0+}.

151

152 @see: C++ U{GeodesicExact.Line

153 <https://GeographicLib.SourceForge.io/C++/doc/classGeographicLib_1_1GeodesicExact.html>}

154 and Python U{Geodesic.Line<https://GeographicLib.SourceForge.io/Python/doc/code.html>}.

155 '''

156 return GeodesicLineSolve(self, lat1, lon1, azi1, **_name1__(caps_name, _or_nameof=self))

157

158 Line = DirectLine

159

160 def _Inverse(self, ll1, ll2, wrap, **outmask): # PYCHOK no cover

161 '''(INTERNAL) Short-cut version, see .ellipsoidalBaseDI.intersecant2.

162 '''

163 if wrap:

164 ll2 = _unrollon(ll1, _Wrap.point(ll2))

165 return self.Inverse(ll1.lat, ll1.lon, ll2.lat, ll2.lon, **outmask)

166

167 def Inverse3(self, lat1, lon1, lat2, lon2): # PYCHOK outmask

168 '''Return the distance in C{meter} and the forward and

169 reverse azimuths (initial and final bearing) in C{degrees}.

170

171 @return: L{Distance3Tuple}C{(distance, initial, final)}.

172 '''

173 r = self._GDictInverse(lat1, lon1, lat2, lon2, floats=False)

174 return Distance3Tuple(float(r.s12), wrap360(r.azi1), wrap360(r.azi2),

175 iteration=r._iteration)

176

177 def _InverseLine(self, ll1, ll2, wrap, **caps_name): # PYCHOK no cover

178 '''(INTERNAL) Short-cut version.

179 '''

180 if wrap:

181 ll2 = _unrollon(ll1, _Wrap.point(ll2))

182 return self.InverseLine(ll1.lat, ll1.lon, ll2.lat, ll2.lon, **caps_name)

183

184# def _InverseLine2(self, lat1, lon1, lat2, lon2, **caps_name): # in .geodesici

185# '''(INTERNAL) Helper for L{InverseLine} and L{_InverseLine}.

186# '''

187# r = self.Inverse(lat1, lon1, lat2, lon2)

188# gl = GeodesicLineSolve(self, lat1, lon1, r.azi1, **_name1__(caps_name, _or_nameof=self))

189# gl._a13 = r.a12 # gl.SetArc(r.a12)

190# gl._s13 = r.s12 # gl.SetDistance(r.s12)

191# return gl, r

192

193 def InverseLine(self, lat1, lon1, lat2, lon2, **caps_name): # PYCHOK no cover

194 '''Set up a L{GeodesicLineSolve} to compute several points

195 on a single geodesic.

196

197 @arg lat1: Latitude of the first point (C{degrees}).

198 @arg lon1: Longitude of the first point (C{degrees}).

199 @arg lat2: Latitude of the second point (C{degrees}).

200 @arg lon2: Longitude of the second point (C{degrees}).

201 @kwarg caps_name: Optional C{B{name}=NN} (C{str}) and keyword

202 argument C{B{caps}=Caps.ALL}, bit-or'ed combination

203 of L{Caps} values specifying the capabilities the

204 L{GeodesicLineSolve} instance should possess.

205

206 @return: A L{GeodesicLineSolve} instance.

207

208 @note: Both B{C{lat1}} and B{C{lat2}} should in the range C{[-90, +90]}.

209

210 @see: C++ U{GeodesicExact.InverseLine

211 <https://GeographicLib.SourceForge.io/C++/doc/classGeographicLib_1_1GeodesicExact.html>} and

212 Python U{Geodesic.InverseLine<https://GeographicLib.SourceForge.io/Python/doc/code.html>}.

213 '''

214 r = self.Inverse(lat1, lon1, lat2, lon2)

215 gl = GeodesicLineSolve(self, lat1, lon1, r.azi1, **_name1__(caps_name, _or_nameof=self))

216 gl._a13 = r.a12 # gl.SetArc(r.a12)

217 gl._s13 = r.s12 # gl.SetDistance(r.s12)

218 return gl

219

220

221 class GeodesicLineSolve(_GeodesicSolveBase, _SolveGDictLineBase):

222 '''Wrapper to invoke I{Karney}'s U{GeodSolve<https://GeographicLib.SourceForge.io/C++/doc/GeodSolve.1.html>}

223 as an C{Exact} version of I{Karney}'s Python class U{GeodesicLine<https://GeographicLib.SourceForge.io/C++/doc/

224 python/code.html#geographiclib.geodesicline.GeodesicLine>}.

225

226 @note: Use property C{GeodSolve} or env variable C{PYGEODESY_GEODSOLVE} to specify the (fully

227 qualified) path to the C{GeodSolve} executable.

228

229 @note: This C{geodesic} is intended I{for testing purposes only}, it invokes the C{GeodSolve}

230 executable for I{every} method call.

231 '''

232 _a13 = \

233 _s13 = NAN # see GeodesicSolve._InverseLine

234

235 def __init__(self, geodesic, lat1, lon1, azi1, caps=Caps.ALL, **name):

236 '''New L{GeodesicLineSolve} instance, allowing points to be found along

237 a geodesic starting at C{(B{lat1}, B{lon1})} with azimuth B{C{azi1}}.

238

239 @arg geodesic: The geodesic to use (L{GeodesicSolve}).

240 @arg lat1: Latitude of the first point (C{degrees}).

241 @arg lon1: Longitude of the first point (C{degrees}).

242 @arg azi1: Azimuth at the first points (compass C{degrees}).

243 @kwarg caps: Bit-or'ed combination of L{Caps} values specifying the

244 capabilities the L{GeodesicLineSolve} instance should possess,

245 C{B{caps}=Caps.ALL} always. Include C{Caps.LINE_OFF} if

246 updates to the B{C{geodesic}} should I{not} be reflected in

247 this L{GeodesicLineSolve} instance.

248 @kwarg name: Optional C{B{name}=NN} (C{str}).

249

250 @raise GeodesicError: Invalid path for the C{GeodSolve} executable

251 or isn't the C{GeodSolve} executable, see

252 property C{geodesic.GeodSolve}.

253

254 @raise TypeError: Invalid B{C{geodesic}}.

255 '''

256 _xinstanceof(GeodesicSolve, geodesic=geodesic)

257 if (caps & Caps.LINE_OFF): # copy to avoid updates

258 geodesic = geodesic.copy(deep=False, name=_UNDER_(NN, geodesic.name)) # NOT _under!

259 _SolveGDictLineBase.__init__(self, geodesic, lat1, lon1, caps, azi1=azi1, **name)

260 try:

261 self.GeodSolve = geodesic.GeodSolve # geodesic or copy of geodesic

262 except GeodesicError:

263 pass

264

265 @Property_RO

266 def a13(self):

267 '''Get the arc length to reference point 3 (C{degrees}).

268

269 @see: Methods L{Arc} and L{SetArc}.

270 '''

271 return self._a13

272

273 def Arc(self):

274 '''Return the arc length to reference point 3 (C{degrees} or C{NAN}).

275

276 @see: Method L{SetArc} and property L{a13}.

277 '''

278 return self.a13

279

280 def ArcPosition(self, a12, outmask=Caps.STANDARD): # PYCHOK unused

281 '''Find the position on the line given B{C{a12}}.

282

283 @arg a12: Spherical arc length from the first point to the

284 second point (C{degrees}).

285

286 @return: A C{GDict} with 12 items C{lat1, lon1, azi1, lat2, lon2,

287 azi2, m12, a12, s12, M12, M21, S12}.

288 '''

289 return self._GDictInvoke(self._cmdArc, self._Names_Direct, a12)._unCaps(outmask)

290

291 @Property_RO

292 def azi1(self):

293 '''Get the azimuth at the first point (compass C{degrees}).

294 '''

295 return self._lla1.azi1

296

297 azi12 = azi1 # like RhumbLineSolve

298

299 @Property_RO

300 def azi1_sincos2(self):

301 '''Get the sine and cosine of the first point's azimuth (2-tuple C{(sin, cos)}).

302 '''

303 return _sincos2d(self.azi1)

304

305 azi12_sincos2 = azi1_sincos2

306

307 @Property_RO

308 def _cmdArc(self):

309 '''(INTERNAL) Get the C{GeodSolve} I{-a -L} cmd (C{tuple}).

310 '''

311 return self._cmdDistance + ('-a',)

312

313 def Distance(self):

314 '''Return the distance to reference point 3 (C{meter} or C{NAN}).

315 '''

316 return self.s13

317

318 @property_RO

319 def geodesic(self):

320 '''Get the geodesic (L{GeodesicSolve}).

321 '''

322 return self._solve # see .solveBase._SolveLineBase

323

324 def Intersecant2(self, lat0, lon0, radius, **kwds): # PYCHOK no cover

325 '''B{Not implemented}, throws a C{NotImplementedError} always.'''

326 self._notImplemented(lat0, lon0, radius, **kwds)

327

328 def PlumbTo(self, lat0, lon0, **kwds): # PYCHOK no cover

329 '''B{Not implemented}, throws a C{NotImplementedError} always.'''

330 self._notImplemented(lat0, lon0, **kwds)

331

332 def Position(self, s12, outmask=Caps.STANDARD):

333 '''Find the position on the line given B{C{s12}}.

334

335 @arg s12: Distance from the first point to the second (C{meter}).

336

337 @return: A C{GDict} with 12 items C{lat1, lon1, azi1, lat2, lon2,

338 azi2, m12, a12, s12, M12, M21, S12}, possibly C{a12=NAN}.

339 '''

340 return self._GDictInvoke(self._cmdDistance, self._Names_Direct, s12)._unCaps(outmask)

341

342 @Property_RO

343 def s13(self):

344 '''Get the distance to reference point 3 (C{meter} or C{NAN}).

345

346 @see: Methods L{Distance} and L{SetDistance}.

347 '''

348 return self._s13

349

350 def SetArc(self, a13):

351 '''Set reference point 3 in terms relative to the first point.

352

353 @arg a13: Spherical arc length from the first to the reference

354 point (C{degrees}).

355

356 @return: The distance C{s13} (C{meter}) between the first and

357 the reference point or C{NAN}.

358 '''

359 if self._a13 != a13:

360 self._a13 = a13

361 self._s13 = self.ArcPosition(a13, outmask=Caps.DISTANCE).s12 # if a13 else _0_0

362# _update_all(self)

363 return self._s13

364

365 def SetDistance(self, s13):

366 '''Set reference point 3 in terms relative to the first point.

367

368 @arg s13: Distance from the first to the reference point (C{meter}).

369

370 @return: The arc length C{a13} (C{degrees}) between the first

371 and the reference point or C{NAN}.

372 '''

373 if self._s13 != s13:

374 self._s13 = s13

375 self._a13 = self.Position(s13, outmask=Caps.DISTANCE).a12 if s13 else _0_0

376# _update_all(self)

377 return self._a13 # NAN for GeodesicLineExact without Cap.DISTANCE_IN

378

379 def toStr(self, **prec_sep): # PYCHOK signature

380 '''Return this C{GeodesicLineSolve} as string.

381

382 @kwarg prec_sep: Keyword argumens C{B{prec}=6} and C{B{sep}=", "}

383 for the C{float} C{prec}ision, number of decimal digits

384 (0..9) and the C{sep}arator string to join. Trailing

385 zero decimals are stripped for B{C{prec}} values of 1

386 and above, but kept for negative B{C{prec}} values.

387

388 @return: GeodesicLineSolve items (C{str}).

389 '''

390 return _SolveGDictLineBase._toStr(self, azi1=self.azi1, geodesic=self._solve,

391 GeodSolve=self.GeodSolve, **prec_sep)

392

393

394__all__ += _ALL_DOCS(_GeodesicSolveBase)

395

396 if __name__ == '__main__':

397

398 def _main():

399 from pygeodesy import printf

400 from sys import argv

401

402 gS = GeodesicSolve(name='Test')

403 gS.verbose = '--verbose' in argv # or '-v' in argv

404

405 if not _Xables.X_OK(gS.GeodSolve): # not set

406 gS.GeodSolve = _Xables.GeodSolve(_Xables.bin_)

407 printf('version: %s', gS.version)

408

409 r = gS.Direct(40.6, -73.8, 51, 5.5e6)

410 printf('Direct: %r', r, nl=1)

411 printf('Direct3: %r', gS.Direct3(40.6, -73.8, 51, 5.5e6))

412

413 printf('Inverse: %r', gS.Inverse( 40.6, -73.8, 51.6, -0.5), nl=1)

414 printf('Inverse1: %r', gS.Inverse1(40.6, -73.8, 51.6, -0.5))

415 printf('Inverse3: %r', gS.Inverse3(40.6, -73.8, 51.6, -0.5))

416

417 glS = GeodesicLineSolve(gS, 40.6, -73.8, 51, name='LineTest')

418 p = glS.Position(5.5e6)

419 printf('Position: %5s %r', p == r, p, nl=1)

420 p = glS.ArcPosition(49.475527)

421 printf('ArcPosition: %5s %r', p == r, p)

422

423 _main()

424

425# % python3 -m pygeodesy.geodsolve

426

427# version: /opt/local/bin/GeodSolve: GeographicLib version 2.2

428

429# Direct: GDict(a12=49.475527, azi1=51.0, azi2=107.189397, lat1=40.6, lat2=51.884565, lon1=-73.8, lon2=-1.141173, m12=4844148.703101, M12=0.650911, M21=0.651229, s12=5500000.0, S12=39735075134877.09375)

430# Direct3: Destination3Tuple(lat=51.884565, lon=-1.141173, final=107.189397)

431

432# Inverse: GDict(a12=49.94131, azi1=51.198883, azi2=107.821777, lat1=40.6, lat2=51.6, lon1=-73.8, lon2=-0.5, m12=4877684.602706, M12=0.64473, M21=0.645046, s12=5551759.400319, S12=40041368848742.53125)

433# Inverse1: 49.94131021789904

434# Inverse3: Distance3Tuple(distance=5551759.400319, initial=51.198883, final=107.821777)

435

436# Position: True GDict(a12=49.475527, azi1=51.0, azi2=107.189397, lat1=40.6, lat2=51.884565, lon1=-73.8, lon2=-1.141173, m12=4844148.703101, M12=0.650911, M21=0.651229, s12=5500000.0, S12=39735075134877.09375)

437# ArcPosition: False GDict(a12=49.475527, azi1=51.0, azi2=107.189397, lat1=40.6, lat2=51.884565, lon1=-73.8, lon2=-1.141174, m12=4844148.669561, M12=0.650911, M21=0.651229, s12=5499999.948497, S12=39735074737272.734375)

438

439

440# % python3 -m pygeodesy.geodsolve

441

442# version: /opt/local/bin/GeodSolve: GeographicLib version 2.3

443

444# Direct: GDict(a12=49.475527, azi1=51.0, azi2=107.189397, lat1=40.6, lat2=51.884565, lon1=-73.8, lon2=-1.141173, m12=4844148.703101, M12=0.650911, M21=0.651229, s12=5500000.0, S12=39735075134877.078125)

445# Direct3: Destination3Tuple(lat=51.884565, lon=-1.141173, final=107.189397)

446

447# Inverse: GDict(a12=49.94131, azi1=51.198883, azi2=107.821777, lat1=40.6, lat2=51.6, lon1=-73.8, lon2=-0.5, m12=4877684.602706, M12=0.64473, M21=0.645046, s12=5551759.400319, S12=40041368848742.53125)

448# Inverse1: 49.94131021789904

449# Inverse3: Distance3Tuple(distance=5551759.400319, initial=51.198883, final=107.821777)

450

451# Position: False GDict(a12=49.475527, azi1=51.0, azi2=107.189397, lat1=40.6, lat2=51.884565, lon1=-73.8, lon2=-1.141173, s12=5500000.0)

452# ArcPosition: False GDict(a12=49.475527, azi1=51.0, azi2=107.189397, lat1=40.6, lat2=51.884565, lon1=-73.8, lon2=-1.141174, s12=5499999.948497)

453

454

455# % python3 -m pygeodesy.geodsolve --verbose

456

457# GeodesicSolve 'Test' 1: /opt/local/bin/GeodSolve --version (invoke)

458# GeodesicSolve 'Test' 1: /opt/local/bin/GeodSolve: GeographicLib version 2.2 (0)

459# version: /opt/local/bin/GeodSolve: GeographicLib version 2.2

460# GeodesicSolve 'Test' 2: /opt/local/bin/GeodSolve -f -E -p 10 \ 40.600000000000001 -73.799999999999997 51.0 5500000.0 (Direct)

461# GeodesicSolve 'Test' 2: lat1=40.600000000000001, lon1=-73.799999999999997, azi1=51.0, lat2=51.884564505606761, lon2=-1.141172861200829, azi2=107.189397162605886, s12=5500000.0, a12=49.475527463251467, m12=4844148.703101486, M12=0.65091056699808603, M21=0.65122865892196558, S12=39735075134877.094 (0)

462

463# Direct: GDict(a12=49.475527, azi1=51.0, azi2=107.189397, lat1=40.6, lat2=51.884565, lon1=-73.8, lon2=-1.141173, m12=4844148.703101, M12=0.650911, M21=0.651229, s12=5500000.0, S12=39735075134877.09375)

464# GeodesicSolve 'Test' 3: /opt/local/bin/GeodSolve -f -E -p 10 \ 40.600000000000001 -73.799999999999997 51.0 5500000.0 (Direct3)

465# GeodesicSolve 'Test' 3: lat1=40.600000000000001, lon1=-73.799999999999997, azi1=51.0, lat2=51.884564505606761, lon2=-1.141172861200829, azi2=107.189397162605886, s12=5500000.0, a12=49.475527463251467, m12=4844148.703101486, M12=0.65091056699808603, M21=0.65122865892196558, S12=39735075134877.094 (0)

466# Direct3: Destination3Tuple(lat=51.884565, lon=-1.141173, final=107.189397)

467# GeodesicSolve 'Test' 4: /opt/local/bin/GeodSolve -f -E -p 10 -i \ 40.600000000000001 -73.799999999999997 51.600000000000001 -0.5 (Inverse)

468# GeodesicSolve 'Test' 4: lat1=40.600000000000001, lon1=-73.799999999999997, azi1=51.198882845579824, lat2=51.600000000000001, lon2=-0.5, azi2=107.821776735514248, s12=5551759.4003186841, a12=49.941310217899037, m12=4877684.6027061976, M12=0.64472969205948238, M21=0.64504567852134398, S12=40041368848742.531 (0)

469

470# Inverse: GDict(a12=49.94131, azi1=51.198883, azi2=107.821777, lat1=40.6, lat2=51.6, lon1=-73.8, lon2=-0.5, m12=4877684.602706, M12=0.64473, M21=0.645046, s12=5551759.400319, S12=40041368848742.53125)

471# GeodesicSolve 'Test' 5: /opt/local/bin/GeodSolve -f -E -p 10 -i \ 40.600000000000001 -73.799999999999997 51.600000000000001 -0.5 (Inverse1)

472# GeodesicSolve 'Test' 5: lat1=40.600000000000001, lon1=-73.799999999999997, azi1=51.198882845579824, lat2=51.600000000000001, lon2=-0.5, azi2=107.821776735514248, s12=5551759.4003186841, a12=49.941310217899037, m12=4877684.6027061976, M12=0.64472969205948238, M21=0.64504567852134398, S12=40041368848742.531 (0)

473# Inverse1: 49.94131021789904

474# GeodesicSolve 'Test' 6: /opt/local/bin/GeodSolve -f -E -p 10 -i \ 40.600000000000001 -73.799999999999997 51.600000000000001 -0.5 (Inverse3)

475# GeodesicSolve 'Test' 6: lat1=40.600000000000001, lon1=-73.799999999999997, azi1=51.198882845579824, lat2=51.600000000000001, lon2=-0.5, azi2=107.821776735514248, s12=5551759.4003186841, a12=49.941310217899037, m12=4877684.6027061976, M12=0.64472969205948238, M21=0.64504567852134398, S12=40041368848742.531 (0)

476# Inverse3: Distance3Tuple(distance=5551759.400319, initial=51.198883, final=107.821777)

477

478# Position: True GDict(a12=49.475527, azi1=51.0, azi2=107.189397, lat1=40.6, lat2=51.884565, lon1=-73.8, lon2=-1.141173, m12=4844148.703101, M12=0.650911, M21=0.651229, s12=5500000.0, S12=39735075134877.09375)

479# ArcPosition: False GDict(a12=49.475527, azi1=51.0, azi2=107.189397, lat1=40.6, lat2=51.884565, lon1=-73.8, lon2=-1.141174, m12=4844148.669561, M12=0.650911, M21=0.651229, s12=5499999.948497, S12=39735074737272.734375)

480

481

482# % python3 -m pygeodesy.geodsolve --verbose

483

484# GeodesicSolve 'Test'@1: /opt/local/bin/GeodSolve --version (invoke)

485# GeodesicSolve 'Test'@1: '/opt/local/bin/GeodSolve: GeographicLib version 2.3' (0, stdout/-err)

486# GeodesicSolve 'Test'@1: /opt/local/bin/GeodSolve: GeographicLib version 2.3 (0)

487# version: /opt/local/bin/GeodSolve: GeographicLib version 2.3

488# GeodesicSolve 'Test'@2: /opt/local/bin/GeodSolve -f -E -p 10 \ 40.600000000000001 -73.799999999999997 51.0 5500000.0 (Direct)

489# GeodesicSolve 'Test'@2: '40.600000000000001 -73.799999999999997 51.000000000000000 51.884564505606761 -1.141172861200843 107.189397162605871 5500000.0000000000 49.475527463251460 4844148.7031014860 0.65091056699808614 0.65122865892196569 39735075134877.078' (0, stdout/-err)

490# GeodesicSolve 'Test'@2: lat1=40.600000000000001, lon1=-73.799999999999997, azi1=51.0, lat2=51.884564505606761, lon2=-1.141172861200843, azi2=107.189397162605871, s12=5500000.0, a12=49.47552746325146, m12=4844148.703101486, M12=0.65091056699808614, M21=0.65122865892196569, S12=39735075134877.078 (0)

491

492# Direct: GDict(a12=49.475527, azi1=51.0, azi2=107.189397, lat1=40.6, lat2=51.884565, lon1=-73.8, lon2=-1.141173, m12=4844148.703101, M12=0.650911, M21=0.651229, s12=5500000.0, S12=39735075134877.078125)

493# GeodesicSolve 'Test'@3: /opt/local/bin/GeodSolve -f -E -p 10 \ 40.600000000000001 -73.799999999999997 51.0 5500000.0 (Direct3)

494# GeodesicSolve 'Test'@3: '40.600000000000001 -73.799999999999997 51.000000000000000 51.884564505606761 -1.141172861200843 107.189397162605871 5500000.0000000000 49.475527463251460 4844148.7031014860 0.65091056699808614 0.65122865892196569 39735075134877.078' (0, stdout/-err)

495# GeodesicSolve 'Test'@3: lat1=40.600000000000001, lon1=-73.799999999999997, azi1=51.0, lat2=51.884564505606761, lon2=-1.141172861200843, azi2=107.189397162605871, s12=5500000.0, a12=49.47552746325146, m12=4844148.703101486, M12=0.65091056699808614, M21=0.65122865892196569, S12=39735075134877.078 (0)

496# Direct3: Destination3Tuple(lat=51.884565, lon=-1.141173, final=107.189397)

497# GeodesicSolve 'Test'@4: /opt/local/bin/GeodSolve -f -E -p 10 -i \ 40.600000000000001 -73.799999999999997 51.600000000000001 -0.5 (Inverse)

498# GeodesicSolve 'Test'@4: '40.600000000000001 -73.799999999999997 51.198882845579824 51.600000000000001 -0.500000000000000 107.821776735514248 5551759.4003186813 49.941310217899037 4877684.6027061967 0.64472969205948238 0.64504567852134398 40041368848742.531' (0, stdout/-err)

499# GeodesicSolve 'Test'@4: lat1=40.600000000000001, lon1=-73.799999999999997, azi1=51.198882845579824, lat2=51.600000000000001, lon2=-0.5, azi2=107.821776735514248, s12=5551759.4003186813, a12=49.941310217899037, m12=4877684.6027061967, M12=0.64472969205948238, M21=0.64504567852134398, S12=40041368848742.531 (0)

500

501# Inverse: GDict(a12=49.94131, azi1=51.198883, azi2=107.821777, lat1=40.6, lat2=51.6, lon1=-73.8, lon2=-0.5, m12=4877684.602706, M12=0.64473, M21=0.645046, s12=5551759.400319, S12=40041368848742.53125)

502# GeodesicSolve 'Test'@5: /opt/local/bin/GeodSolve -f -E -p 10 -i \ 40.600000000000001 -73.799999999999997 51.600000000000001 -0.5 (Inverse1)

503# GeodesicSolve 'Test'@5: '40.600000000000001 -73.799999999999997 51.198882845579824 51.600000000000001 -0.500000000000000 107.821776735514248 5551759.4003186813 49.941310217899037 4877684.6027061967 0.64472969205948238 0.64504567852134398 40041368848742.531' (0, stdout/-err)

504# GeodesicSolve 'Test'@5: lat1=40.600000000000001, lon1=-73.799999999999997, azi1=51.198882845579824, lat2=51.600000000000001, lon2=-0.5, azi2=107.821776735514248, s12=5551759.4003186813, a12=49.941310217899037, m12=4877684.6027061967, M12=0.64472969205948238, M21=0.64504567852134398, S12=40041368848742.531 (0)

505# Inverse1: 49.94131021789904

506# GeodesicSolve 'Test'@6: /opt/local/bin/GeodSolve -f -E -p 10 -i \ 40.600000000000001 -73.799999999999997 51.600000000000001 -0.5 (Inverse3)

507# GeodesicSolve 'Test'@6: '40.600000000000001 -73.799999999999997 51.198882845579824 51.600000000000001 -0.500000000000000 107.821776735514248 5551759.4003186813 49.941310217899037 4877684.6027061967 0.64472969205948238 0.64504567852134398 40041368848742.531' (0, stdout/-err)

508# GeodesicSolve 'Test'@6: lat1=40.600000000000001, lon1=-73.799999999999997, azi1=51.198882845579824, lat2=51.600000000000001, lon2=-0.5, azi2=107.821776735514248, s12=5551759.4003186813, a12=49.941310217899037, m12=4877684.6027061967, M12=0.64472969205948238, M21=0.64504567852134398, S12=40041368848742.531 (0)

509# Inverse3: Distance3Tuple(distance=5551759.400319, initial=51.198883, final=107.821777)

510

511# Position: False GDict(a12=49.475527, azi1=51.0, azi2=107.189397, lat1=40.6, lat2=51.884565, lon1=-73.8, lon2=-1.141173, s12=5500000.0)

512# ArcPosition: False GDict(a12=49.475527, azi1=51.0, azi2=107.189397, lat1=40.6, lat2=51.884565, lon1=-73.8, lon2=-1.141174, s12=5499999.948497)

513

514# **) MIT License

515#

516# Copyright (C) 2016-2024 -- mrJean1 at Gmail -- All Rights Reserved.

517#

518# Permission is hereby granted, free of charge, to any person obtaining a

519# copy of this software and associated documentation files (the "Software"),

520# to deal in the Software without restriction, including without limitation

521# the rights to use, copy, modify, merge, publish, distribute, sublicense,

522# and/or sell copies of the Software, and to permit persons to whom the

523# Software is furnished to do so, subject to the following conditions:

524#

525# The above copyright notice and this permission notice shall be included

526# in all copies or substantial portions of the Software.

527#

528# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS

529# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,

530# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL

531# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR

532# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,

533# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR

534# OTHER DEALINGS IN THE SOFTWARE.