pygeodesy 24.3.24__py2.py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (115) hide show
  1. PyGeodesy-24.3.24.dist-info/METADATA +272 -0
  2. PyGeodesy-24.3.24.dist-info/RECORD +115 -0
  3. PyGeodesy-24.3.24.dist-info/WHEEL +6 -0
  4. PyGeodesy-24.3.24.dist-info/top_level.txt +1 -0
  5. pygeodesy/LICENSE +21 -0
  6. pygeodesy/__init__.py +615 -0
  7. pygeodesy/__main__.py +103 -0
  8. pygeodesy/albers.py +867 -0
  9. pygeodesy/auxilats/_CX_4.py +218 -0
  10. pygeodesy/auxilats/_CX_6.py +314 -0
  11. pygeodesy/auxilats/_CX_8.py +475 -0
  12. pygeodesy/auxilats/__init__.py +54 -0
  13. pygeodesy/auxilats/__main__.py +86 -0
  14. pygeodesy/auxilats/auxAngle.py +548 -0
  15. pygeodesy/auxilats/auxDLat.py +302 -0
  16. pygeodesy/auxilats/auxDST.py +296 -0
  17. pygeodesy/auxilats/auxLat.py +848 -0
  18. pygeodesy/auxilats/auxily.py +272 -0
  19. pygeodesy/azimuthal.py +1150 -0
  20. pygeodesy/basics.py +892 -0
  21. pygeodesy/booleans.py +2031 -0
  22. pygeodesy/cartesianBase.py +1062 -0
  23. pygeodesy/clipy.py +704 -0
  24. pygeodesy/constants.py +516 -0
  25. pygeodesy/css.py +660 -0
  26. pygeodesy/datums.py +752 -0
  27. pygeodesy/deprecated/__init__.py +61 -0
  28. pygeodesy/deprecated/bases.py +40 -0
  29. pygeodesy/deprecated/classes.py +262 -0
  30. pygeodesy/deprecated/consterns.py +54 -0
  31. pygeodesy/deprecated/datum.py +40 -0
  32. pygeodesy/deprecated/functions.py +375 -0
  33. pygeodesy/deprecated/nvector.py +48 -0
  34. pygeodesy/deprecated/rhumbBase.py +32 -0
  35. pygeodesy/deprecated/rhumbaux.py +33 -0
  36. pygeodesy/deprecated/rhumbsolve.py +33 -0
  37. pygeodesy/deprecated/rhumbx.py +33 -0
  38. pygeodesy/dms.py +986 -0
  39. pygeodesy/ecef.py +1348 -0
  40. pygeodesy/elevations.py +279 -0
  41. pygeodesy/ellipsoidalBase.py +1224 -0
  42. pygeodesy/ellipsoidalBaseDI.py +913 -0
  43. pygeodesy/ellipsoidalExact.py +343 -0
  44. pygeodesy/ellipsoidalGeodSolve.py +343 -0
  45. pygeodesy/ellipsoidalKarney.py +403 -0
  46. pygeodesy/ellipsoidalNvector.py +685 -0
  47. pygeodesy/ellipsoidalVincenty.py +590 -0
  48. pygeodesy/ellipsoids.py +2476 -0
  49. pygeodesy/elliptic.py +1198 -0
  50. pygeodesy/epsg.py +243 -0
  51. pygeodesy/errors.py +804 -0
  52. pygeodesy/etm.py +1190 -0
  53. pygeodesy/fmath.py +1013 -0
  54. pygeodesy/formy.py +1818 -0
  55. pygeodesy/frechet.py +865 -0
  56. pygeodesy/fstats.py +760 -0
  57. pygeodesy/fsums.py +1898 -0
  58. pygeodesy/gars.py +358 -0
  59. pygeodesy/geodesicw.py +581 -0
  60. pygeodesy/geodesicx/_C4_24.py +1699 -0
  61. pygeodesy/geodesicx/_C4_27.py +2395 -0
  62. pygeodesy/geodesicx/_C4_30.py +3301 -0
  63. pygeodesy/geodesicx/__init__.py +48 -0
  64. pygeodesy/geodesicx/__main__.py +91 -0
  65. pygeodesy/geodesicx/gx.py +1382 -0
  66. pygeodesy/geodesicx/gxarea.py +535 -0
  67. pygeodesy/geodesicx/gxbases.py +154 -0
  68. pygeodesy/geodesicx/gxline.py +669 -0
  69. pygeodesy/geodsolve.py +426 -0
  70. pygeodesy/geohash.py +914 -0
  71. pygeodesy/geoids.py +1884 -0
  72. pygeodesy/hausdorff.py +892 -0
  73. pygeodesy/heights.py +1155 -0
  74. pygeodesy/interns.py +687 -0
  75. pygeodesy/iters.py +545 -0
  76. pygeodesy/karney.py +919 -0
  77. pygeodesy/ktm.py +633 -0
  78. pygeodesy/latlonBase.py +1766 -0
  79. pygeodesy/lazily.py +960 -0
  80. pygeodesy/lcc.py +684 -0
  81. pygeodesy/ltp.py +1107 -0
  82. pygeodesy/ltpTuples.py +1563 -0
  83. pygeodesy/mgrs.py +721 -0
  84. pygeodesy/named.py +1324 -0
  85. pygeodesy/namedTuples.py +683 -0
  86. pygeodesy/nvectorBase.py +695 -0
  87. pygeodesy/osgr.py +781 -0
  88. pygeodesy/points.py +1686 -0
  89. pygeodesy/props.py +628 -0
  90. pygeodesy/resections.py +1048 -0
  91. pygeodesy/rhumb/__init__.py +46 -0
  92. pygeodesy/rhumb/aux_.py +397 -0
  93. pygeodesy/rhumb/bases.py +1148 -0
  94. pygeodesy/rhumb/ekx.py +563 -0
  95. pygeodesy/rhumb/solve.py +572 -0
  96. pygeodesy/simplify.py +647 -0
  97. pygeodesy/solveBase.py +472 -0
  98. pygeodesy/sphericalBase.py +724 -0
  99. pygeodesy/sphericalNvector.py +1264 -0
  100. pygeodesy/sphericalTrigonometry.py +1447 -0
  101. pygeodesy/streprs.py +627 -0
  102. pygeodesy/trf.py +2079 -0
  103. pygeodesy/triaxials.py +1484 -0
  104. pygeodesy/units.py +969 -0
  105. pygeodesy/unitsBase.py +349 -0
  106. pygeodesy/ups.py +538 -0
  107. pygeodesy/utily.py +1231 -0
  108. pygeodesy/utm.py +762 -0
  109. pygeodesy/utmups.py +318 -0
  110. pygeodesy/utmupsBase.py +517 -0
  111. pygeodesy/vector2d.py +785 -0
  112. pygeodesy/vector3d.py +968 -0
  113. pygeodesy/vector3dBase.py +1049 -0
  114. pygeodesy/webmercator.py +383 -0
  115. pygeodesy/wgrs.py +439 -0
@@ -0,0 +1,572 @@
1
+
2
+ # -*- coding: utf-8 -*-
3
+
4
+ u'''Wrapper to invoke I{Karney}'s U{RhumbSolve
5
+ <https://GeographicLib.SourceForge.io/C++/doc/RhumbSolve.1.html>} utility
6
+ as an (exact) rhumb or rhumb line from I{either GeographicLib 2.0 or 2.2+}.
7
+
8
+ @note: Set env variable C{PYGEODESY_RHUMBSOLVE} to the (fully qualified)
9
+ path of the C{RhumbSolve} executable.
10
+ '''
11
+ from pygeodesy.basics import _xinstanceof
12
+ from pygeodesy.constants import _0_0, _180_0, _N_180_0, _over, _90_0 # PYCHOK used!
13
+ from pygeodesy.errors import RhumbError # PYCHOK used!
14
+ from pygeodesy.interns import NN, _a12_, _azi12_, _lat2_, _lon2_, _s12_, _S12_, _UNDER_
15
+ from pygeodesy.karney import Caps, GDict, _norm180, Rhumb8Tuple, _sincos2d
16
+ from pygeodesy.lazily import _ALL_DOCS, _ALL_LAZY, _ALL_MODS as _MODS, _getenv, \
17
+ _PYGEODESY_RHUMBSOLVE_
18
+ from pygeodesy.namedTuples import Destination3Tuple, Distance3Tuple
19
+ from pygeodesy.props import deprecated_method, Property, Property_RO
20
+ from pygeodesy.solveBase import _SolveBase, _SolveLineBase
21
+ from pygeodesy.utily import _unrollon, _Wrap, wrap360
22
+
23
+ __all__ = _ALL_LAZY.rhumb_solve
24
+ __version__ = '24.02.21'
25
+
26
+
27
+ class _RhumbSolveBase(_SolveBase):
28
+ '''(INTERNAL) Base class for L{RhumbSolve} and L{RhumbLineSolve}.
29
+ '''
30
+ _Error = RhumbError
31
+ _Names_Direct = _lat2_, _lon2_, _S12_
32
+ _Names_Inverse = _azi12_, _s12_, _S12_
33
+ _Solve_name = 'RhumbSolve'
34
+ _Solve_path = _getenv(_PYGEODESY_RHUMBSOLVE_, _PYGEODESY_RHUMBSOLVE_)
35
+
36
+ @Property_RO
37
+ def _cmdBasic(self):
38
+ '''(INTERNAL) Get the basic C{RhumbSolve} cmd (C{tuple}).
39
+ '''
40
+ return (self.RhumbSolve,) + self._e_option \
41
+ + self._p_option \
42
+ + self._s_option
43
+
44
+ @Property
45
+ def RhumbSolve(self):
46
+ '''Get the U{RhumbSolve<https://GeographicLib.SourceForge.io/C++/doc/RhumbSolve.1.html>}
47
+ executable (C{filename}).
48
+ '''
49
+ return self._Solve_path
50
+
51
+ @RhumbSolve.setter # PYCHOK setter!
52
+ def RhumbSolve(self, path):
53
+ '''Set the U{RhumbSolve<https://GeographicLib.SourceForge.io/C++/doc/RhumbSolve.1.html>}
54
+ executable (C{filename}), the (fully qualified) path to the C{RhumbSolve} executable.
55
+
56
+ @raise RhumbError: Invalid B{C{path}}, B{C{path}} doesn't exist or isn't
57
+ the C{RhumbSolve} executable.
58
+ '''
59
+ self._setSolve(path)
60
+
61
+ @Property_RO
62
+ def _s_option(self): # == not -E for GeodSolve
63
+ return () if self.Exact else ('-s',)
64
+
65
+ def toStr(self, **prec_sep): # PYCHOK signature
66
+ '''Return this C{RhumbSolve} as string.
67
+
68
+ @kwarg prec_sep: Keyword argumens C{B{prec}=6} and C{B{sep}=', '}
69
+ for the C{float} C{prec}ision, number of decimal digits
70
+ (0..9) and the C{sep}arator string to join. Trailing
71
+ zero decimals are stripped for B{C{prec}} values of
72
+ 1 and above, but kept for negative B{C{prec}} values.
73
+
74
+ @return: RhumbSolve items (C{str}).
75
+ '''
76
+ return self._toStr(RhumbSolve=self.RhumbSolve, **prec_sep)
77
+
78
+ # @Property_RO
79
+ # def _u_option(self):
80
+ # return '-u' if self.unroll else ()
81
+
82
+
83
+ class RhumbSolve(_RhumbSolveBase):
84
+ '''Wrapper to invoke I{Karney}'s U{RhumbSolve<https://GeographicLib.SourceForge.io/C++/doc/RhumbSolve.1.html>}
85
+ like a class, similar to L{pygeodesy.Rhumb} and L{pygeodesy.RhumbAux}.
86
+
87
+ @note: Use property C{RhumbSolve} or env variable C{PYGEODESY_RHUMBSOLVE} to specify the (fully
88
+ qualified) path to the C{RhumbSolve} executable.
89
+
90
+ @note: This C{rhumb} is intended I{for testing purposes only}, it invokes the C{RhumbSolve}
91
+ executable for I{every} method call.
92
+ '''
93
+ # def Area(self, polyline=False, name=NN):
94
+ # '''Set up a L{RhumbArea} to compute area and
95
+ # perimeter of a polygon.
96
+ #
97
+ # @kwarg polyline: If C{True} perimeter only, otherwise
98
+ # area and perimeter (C{bool}).
99
+ # @kwarg name: Optional name (C{str}).
100
+ #
101
+ # @return: A L{RhumbArea} instance.
102
+ #
103
+ # @note: The B{C{debug}} setting is passed as C{verbose}
104
+ # to the returned L{RhumbAreaExact} instance.
105
+ # '''
106
+ # rA = _MODS.rhumbs.rhumb*.RhumbArea(self, polyline=polyline,
107
+ # name=name or self.name)
108
+ # if self.verbose or self.debug: # PYCHOK no cover
109
+ # rA.verbose = True
110
+ # return rA
111
+
112
+ # Polygon = Area # for C{geographiclib} compatibility
113
+
114
+ def _azimuth_reverse(self, azimuth):
115
+ '''(INTERNAL) Reverse final azimuth C{azimuth}.
116
+ '''
117
+ z = _norm180(float(azimuth))
118
+ if self.reverse2: # like .utils.atan2d
119
+ z += _180_0 if z < 0 else _N_180_0
120
+ return z
121
+
122
+ def _Direct(self, ll1, azi12, s12, **outmask):
123
+ '''(INTERNAL) Short-cut version, see .latlonBase.
124
+ '''
125
+ return self.Direct(ll1.lat, ll1.lon, azi12, s12, **outmask)
126
+
127
+ def Direct3(self, lat1, lon1, azi1, s12): # PYCHOK outmask
128
+ '''Return the destination lat, lon and reverse azimuth
129
+ (final bearing) in C{degrees}.
130
+
131
+ @return: L{Destination3Tuple}C{(lat, lon, final)}.
132
+ '''
133
+ r = self._GDictDirect(lat1, lon1, azi1, False, s12, floats=False)
134
+ z = self._azimuth_reverse(r.azi12)
135
+ return Destination3Tuple(float(r.lat2), float(r.lon2), wrap360(z),
136
+ iteration=r._iteration)
137
+
138
+ def _DirectLine(self, ll1, azi12, **name_caps):
139
+ '''(INTERNAL) Short-cut version, see .latlonBase.
140
+ '''
141
+ return self.DirectLine(ll1.lat, ll1.lon, azi12, **name_caps)
142
+
143
+ def DirectLine(self, lat1, lon1, azi1, caps=Caps.STANDARD, name=NN):
144
+ '''Set up a L{RhumbLineSolve} in terms of the I{direct} rhumb
145
+ problem to compute several points on a single rhumb line.
146
+
147
+ @arg lat1: Latitude of the first point (C{degrees}).
148
+ @arg lon1: Longitude of the first point (C{degrees}).
149
+ @arg azi1: Azimuth at the first point (compass C{degrees}).
150
+ @kwarg caps: Bit-or'ed combination of L{Caps} values specifying
151
+ the capabilities the L{RhumbLineSolve} instance
152
+ should possess, always C{Caps.ALL}.
153
+
154
+ @return: A L{RhumbLineSolve} instance.
155
+
156
+ @note: If the point is at a pole, the azimuth is defined by keeping
157
+ B{C{lon1}} fixed, writing C{B{lat1} = ±(90 − ε)}, and taking
158
+ the limit C{ε → 0+}.
159
+
160
+ @see: C++ U{RhumbExact.Line
161
+ <https://GeographicLib.SourceForge.io/C++/doc/classGeographicLib_1_1RhumbExact.html>}
162
+ and Python U{Rhumb.Line<https://GeographicLib.SourceForge.io/Python/doc/code.html>}.
163
+ '''
164
+ return RhumbLineSolve(self, lat1, lon1, azi1, caps=caps, name=name or self.name)
165
+
166
+ def _GDictDirect(self, lat, lon, azi1, arcmode, s12_a12, *unused, **floats): # PYCHOK signature
167
+ '''(INTERNAL) Get C{_GenDirect}-like result as an 8-item C{GDict}.
168
+ '''
169
+ d = _RhumbSolveBase._GDictDirect(self, lat, lon, azi1, arcmode, s12_a12, **floats)
170
+ r = GDict(lat1=lat, lon1=lon, azi12=azi1, s12=s12_a12) # a12=_over(s12_a12, self._mpd)
171
+ r.update(d)
172
+ return r
173
+
174
+ def _GDictInverse(self, lat1, lon1, lat2, lon2, *unused, **floats): # PYCHOK signature
175
+ '''(INTERNAL) Get C{_GenInverse}-like result as an 8-item C{GDict}, but
176
+ I{without} C{_SALPs_CALPs_}.
177
+ '''
178
+ i = _RhumbSolveBase._GDictInverse(self, lat1, lon1, lat2, lon2, **floats)
179
+ a = _over(float(i.s12), self._mpd) # for .Inverse1
180
+ r = GDict(lat1=lat1, lon1=lon1, lat2=lat2, lon2=lon2, a12=a)
181
+ r.update(i)
182
+ return r
183
+
184
+ def _Inverse(self, ll1, ll2, wrap, **unused):
185
+ '''(INTERNAL) Short-cut version, see .latlonBase.
186
+ '''
187
+ if wrap:
188
+ ll2 = _unrollon(ll1, _Wrap.point(ll2))
189
+ return self._GDictInverse(ll1.lat, ll1.lon, ll2.lat, ll2.lon)
190
+
191
+ def Inverse3(self, lat1, lon1, lat2, lon2): # PYCHOK outmask
192
+ '''Return the distance in C{meter} and the forward and
193
+ reverse azimuths (initial and final bearing) in C{degrees}.
194
+
195
+ @return: L{Distance3Tuple}C{(distance, initial, final)}.
196
+ '''
197
+ r = self._GDictInverse(lat1, lon1, lat2, lon2, floats=False)
198
+ z = self._azimuth_reverse(r.azi12)
199
+ return Distance3Tuple(float(r.s12), wrap360(r.azi12), wrap360(z),
200
+ iteration=r._iteration)
201
+
202
+ def _InverseLine(self, ll1, ll2, wrap, **name_caps):
203
+ '''(INTERNAL) Short-cut version, see .latlonBase.
204
+ '''
205
+ if wrap:
206
+ ll2 = _unrollon(ll1, _Wrap.point(ll2))
207
+ return self.InverseLine(ll1.lat, ll1.lon, ll2.lat, ll2.lon, **name_caps)
208
+
209
+ def InverseLine(self, lat1, lon1, lat2, lon2, caps=Caps.STANDARD, name=NN):
210
+ '''Define a L{RhumbLineSolve} in terms of the I{inverse}
211
+ rhumb problem.
212
+
213
+ @arg lat1: Latitude of the first point (C{degrees90}).
214
+ @arg lon1: Longitude of the first point (C{degrees180}).
215
+ @arg lat2: Latitude of the second point (C{degrees90}).
216
+ @arg lon2: Longitude of the second point (C{degrees180}).
217
+ @kwarg caps: Optional C{caps}, see L{RhumbLine} C{B{caps}}.
218
+
219
+ @return: A L{RhumbLineSolve} instance and invoke its method
220
+ L{RhumbLine.Position} to compute each point.
221
+
222
+ @note: Updates to this rhumb are reflected in the returned
223
+ rhumb line.
224
+ '''
225
+ r = self.Inverse(lat1, lon1, lat2, lon2) # outmask=Caps.AZIMUTH
226
+ return RhumbLineSolve(self, lat1, lon1, r.azi12, caps=caps,
227
+ name=name or self.name)
228
+
229
+ Line = DirectLine
230
+
231
+
232
+ class RhumbLineSolve(_RhumbSolveBase, _SolveLineBase):
233
+ '''Wrapper to invoke I{Karney}'s U{RhumbSolve<https://GeographicLib.SourceForge.io/C++/doc/RhumbSolve.1.html>}
234
+ like a class, similar to L{pygeodesy.RhumbLine} and L{pygeodesy.RhumbLineAux}.
235
+
236
+ @note: Use property C{RhumbSolve} or env variable C{PYGEODESY_RHUMBSOLVE} to specify the (fully
237
+ qualified) path to the C{RhumbSolve} executable.
238
+
239
+ @note: This C{rhumb line} is intended I{for testing purposes only}, it invokes the C{RhumbSolve}
240
+ executable for I{every} method call.
241
+ '''
242
+ def __init__(self, rhumb, lat1, lon1, azi12, caps=Caps.STANDARD, name=NN):
243
+ '''New L{RhumbLineSolve} instance, allowing points to be found along
244
+ a rhumb starting at C{(B{lat1}, B{lon1})} with azimuth B{C{azi12}}.
245
+
246
+ @arg rhumb: The rhumb to use (L{RhumbSolve}).
247
+ @arg lat1: Latitude of the first point (C{degrees90}).
248
+ @arg lon1: Longitude of the first point (C{degrees180}).
249
+ @arg azi12: Azimuth of the rhumb line (compass C{degrees180}).
250
+ @kwarg caps: Bit-or'ed combination of L{Caps} values specifying
251
+ the capabilities the L{RhumbLineSolve} instance should
252
+ possess, always C{Caps.ALL}. Use C{Caps.LINE_OFF}
253
+ if updates to the B{C{rhumb}} should I{not} be
254
+ reflected in this L{RhumbLineSolve} instance.
255
+
256
+ @kwarg name: Optional name (C{str}).
257
+
258
+ @raise RhumbError: Invalid path for C{RhumbSolve} executable or
259
+ isn't the C{RhumbSolve} executable, see
260
+ property C{B{rhumb}.RhumbSolve}.
261
+
262
+ @raise TypeError: Invalid B{C{rhumb}}.
263
+ '''
264
+ _xinstanceof(RhumbSolve, rhumb=rhumb)
265
+ if (caps & Caps.LINE_OFF): # copy to avoid updates
266
+ rhumb = rhumb.copy(deep=False, name=NN(_UNDER_, rhumb.name))
267
+ _SolveLineBase.__init__(self, rhumb, lat1, lon1, caps, name, azi12=azi12)
268
+ try:
269
+ self.RhumbSolve = rhumb.RhumbSolve # rhumb or copy of rhumb
270
+ except RhumbError:
271
+ pass
272
+
273
+ # def ArcPosition(self, a12, *unused):
274
+ # '''Find the position on the line given B{C{a12}}.
275
+ #
276
+ # @arg a12: Spherical arc length from the first point to the
277
+ # second point (C{degrees}).
278
+ #
279
+ # @return: A C{dict} with 8 items C{lat1, lon1, lat2, lon2,
280
+ # azi12, a12, s12, S12}.
281
+ # '''
282
+ # s = a12 * self._mpd
283
+ # a = self._GDictInvoke(self._cmdArc, True, self._Names_Direct, s)
284
+ # r = GDict(a12=a12, s12=s, **self._lla1)
285
+ # r.updated(a)
286
+ # return r
287
+
288
+ @Property_RO
289
+ def azi12(self):
290
+ '''Get this rhumb line's azimuth (compass C{degrees}).
291
+ '''
292
+ return self._lla1.azi12
293
+
294
+ azi1 = azi12 # like GeodesicLineSolve
295
+
296
+ @Property_RO
297
+ def azi12_sincos2(self): # PYCHOK no cover
298
+ '''Get the sine and cosine of this rhumb line's azimuth (2-tuple C{(sin, cos)}).
299
+ '''
300
+ return _sincos2d(self.azi12)
301
+
302
+ azi1_sincos2 = azi12_sincos2
303
+
304
+ # @Property_RO
305
+ # def _cmdArc(self):
306
+ # '''(INTERNAL) Get the C{RhumbSolve} I{-a -L} cmd (C{tuple}).
307
+ # '''
308
+ # return self._cmdDistance + ('-a',)
309
+
310
+ def Position(self, s12, **unused):
311
+ '''Find the position on the line given B{C{s12}}.
312
+
313
+ @arg s12: Distance from the first point to the second (C{meter}).
314
+
315
+ @return: A L{GDict} with 7 items C{lat1, lon1, lat2, lon2,
316
+ azi12, s12, S12}.
317
+ '''
318
+ d = self._GDictInvoke(self._cmdDistance, True, self._Names_Direct, s12)
319
+ r = GDict(s12=s12, **self._lla1) # a12=_over(s12, self._mpd)
320
+ r.update(d)
321
+ return r
322
+
323
+ def toStr(self, **prec_sep): # PYCHOK signature
324
+ '''Return this C{RhumbLineSolve} as string.
325
+
326
+ @kwarg prec_sep: Keyword argumens C{B{prec}=6} and C{B{sep}=', '}
327
+ for the C{float} C{prec}ision, number of decimal digits
328
+ (0..9) and the C{sep}arator string to join. Trailing
329
+ zero decimals are stripped for B{C{prec}} values of
330
+ 1 and above, but kept for negative B{C{prec}} values.
331
+
332
+ @return: RhumbLineSolve items (C{str}).
333
+ '''
334
+ return _SolveLineBase._toStr(self, azi12=self.azi12, rhumb=self._solve,
335
+ RhumbSolve=self.RhumbSolve, **prec_sep)
336
+
337
+
338
+ class RhumbSolve7Tuple(Rhumb8Tuple):
339
+ '''7-Tuple C{(lat1, lon1, lat2, lon2, azi12, s12, S12)} with lat- C{lat1},
340
+ C{lat2} and longitudes C{lon1}, C{lon2} of both points, the azimuth of
341
+ the rhumb line C{azi12}, the distance C{s12} and the area C{S12} under
342
+ the rhumb line between both points.
343
+ '''
344
+ assert Rhumb8Tuple._Names_.index(_a12_) == 7
345
+ _Names_ = Rhumb8Tuple._Names_[:7] # drop a12
346
+ _Units_ = Rhumb8Tuple._Units_[:7]
347
+
348
+ @deprecated_method
349
+ def _to7Tuple(self): # PYCHOK no cover
350
+ '''DEPRECATED, I{don't use!}
351
+ '''
352
+ return _MODS.deprecated.classes.Rhumb7Tuple(self[:7])
353
+
354
+
355
+ __all__ += _ALL_DOCS(_RhumbSolveBase)
356
+
357
+ if __name__ == '__main__':
358
+
359
+ from pygeodesy.lazily import printf
360
+ from sys import argv
361
+
362
+ def rhumb_intercept(rS, lat1, lon1, lat2, lon2, azi2, s23):
363
+ # using RhumbSolve and GeodesicExact for I{Karney}'s C++ U{rhumb-intercept.cpp
364
+ # <https://SourceForge.net/p/geographiclib/discussion/1026620/thread/2ddc295e/>
365
+ from pygeodesy.constants import EPS4 as _TOL
366
+ from pygeodesy.karney import _diff182
367
+
368
+ E = rS.ellipsoid
369
+ gX = E.geodesicx # == GeodesicExact(E)
370
+ m = gX.STANDARD | gX.REDUCEDLENGTH | gX.GEODESICSCALE
371
+
372
+ rlS = rS.Line(lat2, lon2, azi2)
373
+ sa, _ = rlS.azi12_sincos2 # aka _salp, _calp
374
+ for i in range(1, 16):
375
+ p = rlS.Position(s23) # outmask=gX.LATITUDE_LONGITUDE
376
+ r = gX.Inverse(lat1, lon1, p.lat2, p.lon2, outmask=m)
377
+ d, _ = _diff182(azi2, r.azi2, K_2_0=True)
378
+ s, c = _sincos2d(d)
379
+ printf('%2d %.3f %.8f, %.8f, %.8e',
380
+ i, s23, r.lat2, r.lon2, c)
381
+ s2, c2 = _sincos2d(r.lat2)
382
+ c2 *= E.rocTransverse(r.lat2)
383
+ if c2 and r.m12:
384
+ s *= (s2 * sa) / c2 - s * r.M21 / r.m12
385
+ t = (c / s) if s else _0_0
386
+ if abs(t) < _TOL:
387
+ break
388
+ s23 += t
389
+ else:
390
+ break
391
+
392
+ rS = RhumbSolve(name='Test')
393
+ rS.verbose = '--verbose' in argv # or '-v' in argv
394
+
395
+ if rS.RhumbSolve in (_PYGEODESY_RHUMBSOLVE_, None): # not set
396
+ rS.RhumbSolve = '/opt/local/bin/RhumbSolve' # '/opt/local/Cellar/geographiclib/2.2/bin/RhumbSolve' # HomeBrew
397
+ printf('version: %s', rS.version)
398
+
399
+ if len(argv) > 6: # 60 0 30 0 45 1e6
400
+ t = (14, 's23'), (7, 'lat3'), (11, 'lon3'), (13, 'cos()')
401
+ printf(' '.join('%*s' % _ for _ in t))
402
+ rhumb_intercept(rS, *map(float, argv[-6:]))
403
+ exit()
404
+
405
+ r = rS.Direct(40.6, -73.8, 51, 5.5e6)
406
+ printf('Direct: %r', r, nl=1)
407
+ printf('Direct3: %r', rS.Direct3(40.6, -73.8, 51, 5.5e6))
408
+
409
+ printf('Inverse: %r', rS.Inverse( 40.6, -73.8, 51.6, -0.5), nl=1)
410
+ printf('Inverse1: %r', rS.Inverse1(40.6, -73.8, 51.6, -0.5))
411
+ printf('Inverse3: %r', rS.Inverse3(40.6, -73.8, 51.6, -0.5))
412
+
413
+ printf('Inverse: %r', rS.Inverse( 40.6, -73.8, 35.8, 140.3), nl=1)
414
+ printf('Inverse1: %r', rS.Inverse1(40.6, -73.8, 35.8, 140.3))
415
+ printf('Inverse3: %r', rS.Inverse3(40.6, -73.8, 35.8, 140.3))
416
+
417
+ rlS = RhumbLineSolve(rS, 40.6, -73.8, 51, name='LineTest')
418
+ p = rlS.Position(5.5e6)
419
+ printf('Position: %s %r', p == r, p, nl=1)
420
+ # p = rlS.ArcPosition(49.475527)
421
+ # printf('ArcPosition: %s %r', p == r, p)
422
+
423
+ # % python3 -m pygeodesy.rhumb.solve
424
+
425
+ # version: /opt/local/bin/RhumbSolve: GeographicLib version 1.51
426
+ #
427
+ # Direct: GDict(S12=44095641862956.148438, azi12=51, lat1=40.6, lat2=71.6889, lon1=-73.8, lon2=0.25552, s12=5500000.0)
428
+ # Direct3: Destination3Tuple(lat=71.6889, lon=0.25552, final=51.0)
429
+ #
430
+ # Inverse: GDict(S12=37395209100030.367188, a12=51.929543, azi12=77.76839, lat1=40.6, lat2=51.6, lon1=-73.8, lon2=-0.5, s12=5771083.383328)
431
+ # Inverse1: 51.92954250756195
432
+ # Inverse3: Distance3Tuple(distance=5771083.383328, initial=77.76839, final=77.76839)
433
+ #
434
+ # Inverse: GDict(S12=-63760642939072.492188, a12=115.02062, azi12=-92.388888, lat1=40.6, lat2=35.8, lon1=-73.8, lon2=140.3, s12=12782581.067684)
435
+ # Inverse1: 115.02061966879258
436
+ # Inverse3: Distance3Tuple(distance=12782581.067684, initial=267.611112, final=267.611112)
437
+ #
438
+ # Position: True GDict(S12=44095641862956.148438, azi12=51, lat1=40.6, lat2=71.6889, lon1=-73.8, lon2=0.25552, s12=5500000.0)
439
+
440
+
441
+ # % python3 -m pygeodesy.rhumb.solve --verbose
442
+
443
+ # RhumbSolve 'Test' 1: /opt/local/bin/RhumbSolve --version (invoke)
444
+ # RhumbSolve 'Test' 1: /opt/local/bin/RhumbSolve: GeographicLib version 1.51 (0)
445
+ # version: /opt/local/bin/RhumbSolve: GeographicLib version 1.51
446
+ # RhumbSolve 'Test' 2: /opt/local/bin/RhumbSolve -p 10 \ 40.600000000000001 -73.799999999999997 51.0 5500000.0 (Direct)
447
+ # RhumbSolve 'Test' 2: lat2=71.688899882813047, lon2=0.255519824423445, S12=44095641862956.148 (0)
448
+
449
+ # Direct: GDict(S12=44095641862956.148438, azi12=51, lat1=40.6, lat2=71.6889, lon1=-73.8, lon2=0.25552, s12=5500000.0)
450
+ # RhumbSolve 'Test' 3: /opt/local/bin/RhumbSolve -p 10 \ 40.600000000000001 -73.799999999999997 51.0 5500000.0 (Direct3)
451
+ # RhumbSolve 'Test' 3: lat2=71.688899882813047, lon2=0.255519824423445, S12=44095641862956.148 (0)
452
+ # Direct3: Destination3Tuple(lat=71.6889, lon=0.25552, final=51.0)
453
+ # RhumbSolve 'Test' 4: /opt/local/bin/RhumbSolve -p 10 -i \ 40.600000000000001 -73.799999999999997 51.600000000000001 -0.5 (Inverse)
454
+ # RhumbSolve 'Test' 4: azi12=77.768389710255661, s12=5771083.3833280317, S12=37395209100030.367 (0)
455
+
456
+ # Inverse: GDict(S12=37395209100030.367188, a12=51.929543, azi12=77.76839, lat1=40.6, lat2=51.6, lon1=-73.8, lon2=-0.5, s12=5771083.383328)
457
+ # RhumbSolve 'Test' 5: /opt/local/bin/RhumbSolve -p 10 -i \ 40.600000000000001 -73.799999999999997 51.600000000000001 -0.5 (Inverse1)
458
+ # RhumbSolve 'Test' 5: azi12=77.768389710255661, s12=5771083.3833280317, S12=37395209100030.367 (0)
459
+ # Inverse1: 51.92954250756195
460
+ # RhumbSolve 'Test' 6: /opt/local/bin/RhumbSolve -p 10 -i \ 40.600000000000001 -73.799999999999997 51.600000000000001 -0.5 (Inverse3)
461
+ # RhumbSolve 'Test' 6: azi12=77.768389710255661, s12=5771083.3833280317, S12=37395209100030.367 (0)
462
+ # Inverse3: Distance3Tuple(distance=5771083.383328, initial=77.76839, final=77.76839)
463
+ # RhumbSolve 'Test' 7: /opt/local/bin/RhumbSolve -p 10 -i \ 40.600000000000001 -73.799999999999997 35.799999999999997 140.300000000000011 (Inverse)
464
+ # RhumbSolve 'Test' 7: azi12=-92.388887981699639, s12=12782581.0676841792, S12=-63760642939072.492 (0)
465
+
466
+ # Inverse: GDict(S12=-63760642939072.492188, a12=115.02062, azi12=-92.388888, lat1=40.6, lat2=35.8, lon1=-73.8, lon2=140.3, s12=12782581.067684)
467
+ # RhumbSolve 'Test' 8: /opt/local/bin/RhumbSolve -p 10 -i \ 40.600000000000001 -73.799999999999997 35.799999999999997 140.300000000000011 (Inverse1)
468
+ # RhumbSolve 'Test' 8: azi12=-92.388887981699639, s12=12782581.0676841792, S12=-63760642939072.492 (0)
469
+ # Inverse1: 115.02061966879258
470
+ # RhumbSolve 'Test' 9: /opt/local/bin/RhumbSolve -p 10 -i \ 40.600000000000001 -73.799999999999997 35.799999999999997 140.300000000000011 (Inverse3)
471
+ # RhumbSolve 'Test' 9: azi12=-92.388887981699639, s12=12782581.0676841792, S12=-63760642939072.492 (0)
472
+ # Inverse3: Distance3Tuple(distance=12782581.067684, initial=267.611112, final=267.611112)
473
+
474
+ # Position: True GDict(S12=44095641862956.148438, azi12=51, lat1=40.6, lat2=71.6889, lon1=-73.8, lon2=0.25552, s12=5500000.0)
475
+
476
+
477
+ # % python3 -m pygeodesy.rhumb.solve
478
+
479
+ # version: /opt/local/bin/RhumbSolve: GeographicLib version 2.2
480
+
481
+ # Direct: GDict(azi12=51, lat1=40.6, lat2=71.6889, lon1=-73.8, lon2=0.25552, s12=5500000.0, S12=44095641862956.109375)
482
+ # Direct3: Destination3Tuple(lat=71.6889, lon=0.25552, final=51.0)
483
+
484
+ # Inverse: GDict(a12=51.929543, azi12=77.76839, lat1=40.6, lat2=51.6, lon1=-73.8, lon2=-0.5, s12=5771083.383328, S12=37395209100030.390625)
485
+ # Inverse1: 51.92954250756191
486
+ # Inverse3: Distance3Tuple(distance=5771083.383328, initial=77.76839, final=77.76839)
487
+
488
+ # Inverse: GDict(a12=115.02062, azi12=-92.388888, lat1=40.6, lat2=35.8, lon1=-73.8, lon2=140.3, s12=12782581.067684, S12=-63760642939072.5)
489
+ # Inverse1: 115.02061966879249
490
+ # Inverse3: Distance3Tuple(distance=12782581.067684, initial=267.611112, final=267.611112)
491
+
492
+ # Position: True GDict(azi12=51, lat1=40.6, lat2=71.6889, lon1=-73.8, lon2=0.25552, s12=5500000.0, S12=44095641862956.109375)
493
+
494
+
495
+ # % python3 -m pygeodesy.rhumb.solve --verbose
496
+
497
+ # RhumbSolve 'Test' 1: /opt/local/bin/RhumbSolve --version (invoke)
498
+ # RhumbSolve 'Test' 1: /opt/local/bin/RhumbSolve: GeographicLib version 2.2 (0)
499
+ # version: /opt/local/bin/RhumbSolve: GeographicLib version 2.2
500
+ # RhumbSolve 'Test' 2: /opt/local/bin/RhumbSolve -p 10 \ 40.600000000000001 -73.799999999999997 51.0 5500000.0 (Direct)
501
+ # RhumbSolve 'Test' 2: lat2=71.688899882813018, lon2=0.255519824423402, S12=44095641862956.109 (0)
502
+
503
+ # Direct: GDict(azi12=51, lat1=40.6, lat2=71.6889, lon1=-73.8, lon2=0.25552, s12=5500000.0, S12=44095641862956.109375)
504
+ # RhumbSolve 'Test' 3: /opt/local/bin/RhumbSolve -p 10 \ 40.600000000000001 -73.799999999999997 51.0 5500000.0 (Direct3)
505
+ # RhumbSolve 'Test' 3: lat2=71.688899882813018, lon2=0.255519824423402, S12=44095641862956.109 (0)
506
+ # Direct3: Destination3Tuple(lat=71.6889, lon=0.25552, final=51.0)
507
+ # RhumbSolve 'Test' 4: /opt/local/bin/RhumbSolve -p 10 -i \ 40.600000000000001 -73.799999999999997 51.600000000000001 -0.5 (Inverse)
508
+ # RhumbSolve 'Test' 4: azi12=77.768389710255661, s12=5771083.383328028, S12=37395209100030.391 (0)
509
+
510
+ # Inverse: GDict(a12=51.929543, azi12=77.76839, lat1=40.6, lat2=51.6, lon1=-73.8, lon2=-0.5, s12=5771083.383328, S12=37395209100030.390625)
511
+ # RhumbSolve 'Test' 5: /opt/local/bin/RhumbSolve -p 10 -i \ 40.600000000000001 -73.799999999999997 51.600000000000001 -0.5 (Inverse1)
512
+ # RhumbSolve 'Test' 5: azi12=77.768389710255661, s12=5771083.383328028, S12=37395209100030.391 (0)
513
+ # Inverse1: 51.92954250756191
514
+ # RhumbSolve 'Test' 6: /opt/local/bin/RhumbSolve -p 10 -i \ 40.600000000000001 -73.799999999999997 51.600000000000001 -0.5 (Inverse3)
515
+ # RhumbSolve 'Test' 6: azi12=77.768389710255661, s12=5771083.383328028, S12=37395209100030.391 (0)
516
+ # Inverse3: Distance3Tuple(distance=5771083.383328, initial=77.76839, final=77.76839)
517
+ # RhumbSolve 'Test' 7: /opt/local/bin/RhumbSolve -p 10 -i \ 40.600000000000001 -73.799999999999997 35.799999999999997 140.300000000000011 (Inverse)
518
+ # RhumbSolve 'Test' 7: azi12=-92.388887981699654, s12=12782581.0676841699, S12=-63760642939072.5 (0)
519
+
520
+ # Inverse: GDict(a12=115.02062, azi12=-92.388888, lat1=40.6, lat2=35.8, lon1=-73.8, lon2=140.3, s12=12782581.067684, S12=-63760642939072.5)
521
+ # RhumbSolve 'Test' 8: /opt/local/bin/RhumbSolve -p 10 -i \ 40.600000000000001 -73.799999999999997 35.799999999999997 140.300000000000011 (Inverse1)
522
+ # RhumbSolve 'Test' 8: azi12=-92.388887981699654, s12=12782581.0676841699, S12=-63760642939072.5 (0)
523
+ # Inverse1: 115.02061966879249
524
+ # RhumbSolve 'Test' 9: /opt/local/bin/RhumbSolve -p 10 -i \ 40.600000000000001 -73.799999999999997 35.799999999999997 140.300000000000011 (Inverse3)
525
+ # RhumbSolve 'Test' 9: azi12=-92.388887981699654, s12=12782581.0676841699, S12=-63760642939072.5 (0)
526
+ # Inverse3: Distance3Tuple(distance=12782581.067684, initial=267.611112, final=267.611112)
527
+
528
+ # Position: True GDict(azi12=51, lat1=40.6, lat2=71.6889, lon1=-73.8, lon2=0.25552, s12=5500000.0, S12=44095641862956.109375)
529
+
530
+
531
+ # % python3 -m pygeodesy.rhumb.solve 60 0 30 0 45 1e6
532
+
533
+ # version: /opt/local/bin/RhumbSolve: GeographicLib version 2.2
534
+ # s23 lat3 lon3 cos()
535
+ # 1 1000000.000 36.37559999, 7.58982303, -5.83098638e-01
536
+ # 2 4532573.097 58.84251798, 41.57078946, 4.05349594e-01
537
+ # 3 2233216.895 44.22871762, 17.86660260, -2.91432608e-01
538
+ # 4 3168401.173 50.17678842, 26.60741388, 3.00555188e-02
539
+ # 5 3082690.347 49.63189746, 25.76374255, -1.49446251e-04
540
+ # 6 3083112.629 49.63458216, 25.76787599, -2.59865190e-09
541
+ # 7 3083112.636 49.63458221, 25.76787606, 4.96052409e-16
542
+ # 8 3083112.636 49.63458221, 25.76787606, -4.96052409e-16
543
+ # 9 3083112.636 49.63458221, 25.76787606, 4.96052409e-16
544
+ # 10 3083112.636 49.63458221, 25.76787606, -4.96052409e-16
545
+ # 11 3083112.636 49.63458221, 25.76787606, 4.96052409e-16
546
+ # 12 3083112.636 49.63458221, 25.76787606, -4.96052409e-16
547
+ # 13 3083112.636 49.63458221, 25.76787606, 4.96052409e-16
548
+ # 14 3083112.636 49.63458221, 25.76787606, -4.96052409e-16
549
+ # 15 3083112.636 49.63458221, 25.76787606, 4.96052409e-16
550
+
551
+
552
+ # **) MIT License
553
+ #
554
+ # Copyright (C) 2022-2024 -- mrJean1 at Gmail -- All Rights Reserved.
555
+ #
556
+ # Permission is hereby granted, free of charge, to any person obtaining a
557
+ # copy of this software and associated documentation files (the "Software"),
558
+ # to deal in the Software without restriction, including without limitation
559
+ # the rights to use, copy, modify, merge, publish, distribute, sublicense,
560
+ # and/or sell copies of the Software, and to permit persons to whom the
561
+ # Software is furnished to do so, subject to the following conditions:
562
+ #
563
+ # The above copyright notice and this permission notice shall be included
564
+ # in all copies or substantial portions of the Software.
565
+ #
566
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
567
+ # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
568
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
569
+ # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
570
+ # OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
571
+ # ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
572
+ # OTHER DEALINGS IN THE SOFTWARE.