pygeodesy 24.6.1__py2.py3-none-any.whl → 24.6.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 (89) hide show
  1. {PyGeodesy-24.6.1.dist-info → PyGeodesy-24.6.24.dist-info}/METADATA +2 -2
  2. PyGeodesy-24.6.24.dist-info/RECORD +117 -0
  3. pygeodesy/__init__.py +33 -32
  4. pygeodesy/albers.py +2 -2
  5. pygeodesy/auxilats/__init__.py +1 -1
  6. pygeodesy/auxilats/auxAngle.py +40 -39
  7. pygeodesy/auxilats/auxDLat.py +3 -2
  8. pygeodesy/auxilats/auxLat.py +16 -18
  9. pygeodesy/auxilats/auxily.py +1 -1
  10. pygeodesy/azimuthal.py +10 -10
  11. pygeodesy/basics.py +9 -1
  12. pygeodesy/booleans.py +53 -66
  13. pygeodesy/cartesianBase.py +143 -155
  14. pygeodesy/css.py +14 -18
  15. pygeodesy/datums.py +6 -6
  16. pygeodesy/deprecated/__init__.py +1 -1
  17. pygeodesy/deprecated/classes.py +16 -2
  18. pygeodesy/deprecated/datum.py +3 -3
  19. pygeodesy/deprecated/functions.py +6 -8
  20. pygeodesy/dms.py +23 -27
  21. pygeodesy/ecef.py +49 -55
  22. pygeodesy/elevations.py +4 -4
  23. pygeodesy/ellipsoidalBase.py +28 -70
  24. pygeodesy/ellipsoidalBaseDI.py +19 -23
  25. pygeodesy/ellipsoidalExact.py +3 -3
  26. pygeodesy/ellipsoidalGeodSolve.py +15 -23
  27. pygeodesy/ellipsoidalKarney.py +37 -60
  28. pygeodesy/ellipsoidalNvector.py +44 -50
  29. pygeodesy/ellipsoidalVincenty.py +11 -14
  30. pygeodesy/ellipsoids.py +107 -101
  31. pygeodesy/errors.py +101 -49
  32. pygeodesy/etm.py +32 -44
  33. pygeodesy/formy.py +55 -58
  34. pygeodesy/frechet.py +20 -23
  35. pygeodesy/fsums.py +4 -4
  36. pygeodesy/gars.py +3 -4
  37. pygeodesy/geodesici.py +909 -0
  38. pygeodesy/geodesicw.py +11 -13
  39. pygeodesy/geodesicx/__init__.py +4 -4
  40. pygeodesy/geodesicx/gx.py +18 -28
  41. pygeodesy/geodesicx/gxbases.py +20 -8
  42. pygeodesy/geodesicx/gxline.py +16 -22
  43. pygeodesy/geodsolve.py +102 -34
  44. pygeodesy/geohash.py +39 -60
  45. pygeodesy/geoids.py +28 -37
  46. pygeodesy/hausdorff.py +21 -23
  47. pygeodesy/heights.py +15 -28
  48. pygeodesy/internals.py +19 -12
  49. pygeodesy/interns.py +4 -10
  50. pygeodesy/iters.py +2 -2
  51. pygeodesy/karney.py +20 -4
  52. pygeodesy/ktm.py +13 -16
  53. pygeodesy/latlonBase.py +202 -191
  54. pygeodesy/lazily.py +96 -59
  55. pygeodesy/lcc.py +29 -32
  56. pygeodesy/ltp.py +43 -24
  57. pygeodesy/ltpTuples.py +190 -183
  58. pygeodesy/mgrs.py +35 -9
  59. pygeodesy/named.py +106 -72
  60. pygeodesy/namedTuples.py +43 -14
  61. pygeodesy/nvectorBase.py +23 -27
  62. pygeodesy/osgr.py +9 -9
  63. pygeodesy/points.py +7 -7
  64. pygeodesy/rhumb/__init__.py +1 -1
  65. pygeodesy/rhumb/aux_.py +5 -5
  66. pygeodesy/rhumb/bases.py +30 -31
  67. pygeodesy/rhumb/ekx.py +3 -4
  68. pygeodesy/rhumb/solve.py +8 -61
  69. pygeodesy/solveBase.py +22 -19
  70. pygeodesy/sphericalBase.py +26 -21
  71. pygeodesy/sphericalNvector.py +13 -13
  72. pygeodesy/sphericalTrigonometry.py +86 -97
  73. pygeodesy/streprs.py +8 -36
  74. pygeodesy/trf.py +3 -3
  75. pygeodesy/triaxials.py +117 -91
  76. pygeodesy/units.py +229 -321
  77. pygeodesy/unitsBase.py +116 -108
  78. pygeodesy/ups.py +26 -31
  79. pygeodesy/utily.py +12 -11
  80. pygeodesy/utm.py +35 -40
  81. pygeodesy/utmups.py +43 -46
  82. pygeodesy/utmupsBase.py +9 -10
  83. pygeodesy/vector3d.py +59 -62
  84. pygeodesy/vector3dBase.py +17 -15
  85. pygeodesy/webmercator.py +19 -21
  86. pygeodesy/wgrs.py +18 -20
  87. PyGeodesy-24.6.1.dist-info/RECORD +0 -116
  88. {PyGeodesy-24.6.1.dist-info → PyGeodesy-24.6.24.dist-info}/WHEEL +0 -0
  89. {PyGeodesy-24.6.1.dist-info → PyGeodesy-24.6.24.dist-info}/top_level.txt +0 -0
pygeodesy/geodesicw.py CHANGED
@@ -37,7 +37,7 @@ from contextlib import contextmanager
37
37
  # from math import fabs # from .utily
38
38
 
39
39
  __all__ = _ALL_LAZY.geodesicw
40
- __version__ = '24.05.24'
40
+ __version__ = '24.06.24'
41
41
 
42
42
  _plumb_ = 'plumb'
43
43
  _TRIPS = 65
@@ -71,8 +71,8 @@ class _gWrapped(_kWrapped):
71
71
 
72
72
  @arg a_ellipsoid: The equatorial radius I{a} (C{meter}, conventionally),
73
73
  an ellipsoid (L{Ellipsoid}) or a datum (L{Datum}).
74
- @arg f: The ellipsoid's flattening (C{scalar}), ignored if B{C{a_ellipsoid})
75
- is not C{meter}.
74
+ @arg f: The ellipsoid's flattening (C{scalar}), required if B{C{a_ellipsoid})
75
+ is C{meter}, ignored otherwise.
76
76
  @kwarg name: Optional C{B{name}=NN} (C{str}).
77
77
  '''
78
78
  _earth_datum(self, a_ellipsoid, f=f, **name) # raiser=NN
@@ -429,17 +429,16 @@ class _gWrapped(_kWrapped):
429
429
  _wrapped = _gWrapped() # PYCHOK singleton, .ellipsoids, .test/base.py
430
430
 
431
431
 
432
- def Geodesic(a_ellipsoid, f=None, **name):
432
+ def Geodesic(a_ellipsoid=_EWGS84, f=None, **name):
433
433
  '''Return a I{wrapped} C{geodesic.Geodesic} instance from I{Karney}'s
434
434
  Python U{geographiclib<https://PyPI.org/project/geographiclib>},
435
435
  provide the latter is installed, otherwise an C{ImportError}.
436
436
 
437
437
  @arg a_ellipsoid: An ellipsoid (L{Ellipsoid}) or datum (L{Datum})
438
438
  or the equatorial radius I{a} of the ellipsoid (C{meter}).
439
- @arg f: The flattening of the ellipsoid (C{scalar}), ignored if
440
- B{C{a_ellipsoid}}) is not specified as C{meter}.
441
- @kwarg name: Optional ellipsoid C{B{name}=NN} (C{str}), ignored
442
- like B{C{f}}.
439
+ @arg f: The flattening of the ellipsoid (C{scalar}), required if
440
+ B{C{a_ellipsoid}}) is C{meter}, ignored otherwise.
441
+ @kwarg name: Optional C{B{name}=NN} (C{str}), ignored like B{C{f}}.
443
442
  '''
444
443
  return _wrapped.Geodesic(a_ellipsoid, f=f, **name)
445
444
 
@@ -453,11 +452,10 @@ def GeodesicLine(geodesic, lat1, lon1, azi1, caps=Caps._STD_LINE):
453
452
  @arg lat1: Latitude of the first points (C{degrees}).
454
453
  @arg lon1: Longitude of the first points (C{degrees}).
455
454
  @arg azi1: Azimuth at the first points (compass C{degrees360}).
456
- @kwarg caps: Optional, bit-or'ed combination of L{Caps} values
457
- specifying the capabilities the C{GeodesicLine}
458
- instance should possess, i.e., which quantities can
459
- be returned by calls to C{GeodesicLine.Position}
460
- and C{GeodesicLine.ArcPosition}.
455
+ @kwarg caps: Optional, bit-or'ed combination of L{Caps} values specifying
456
+ the capabilities the C{GeodesicLine} instance should possess,
457
+ i.e., which quantities can be returned by methods
458
+ C{GeodesicLine.Position} and C{GeodesicLine.ArcPosition}.
461
459
  '''
462
460
  return _wrapped.GeodesicLine(geodesic, lat1, lon1, azi1, caps=caps)
463
461
 
@@ -2,7 +2,7 @@
2
2
  # -*- coding: utf-8 -*-
3
3
 
4
4
  u'''A pure Python version of I{Karney}'s C++ classes U{GeodesicExact
5
- <https://GeographicLib.SourceForge.io/C++/classGeographicLib_1_1GeodesicExact.html>}
5
+ <https://GeographicLib.SourceForge.io/C++/doc/classGeographicLib_1_1GeodesicExact.html>}
6
6
  and U{GeodesicLineExact
7
7
  <https://geographiclib.sourceforge.io/C++/doc/classGeographicLib_1_1GeodesicLine.html>}.
8
8
 
@@ -13,8 +13,8 @@ and the background information at U{Geodesics on an ellipsoid of revolution
13
13
 
14
14
  Also, compare C{GeodesicExact} and C{GeodesicLineExact} to I{standard} classes C{Geodesic}
15
15
  respectively C{GeodesicLine} from I{Karney}'s Python implementation U{geographiclib
16
- <https://GeographicLib.SourceForge.io/C++/doc/other.html#python>}, see module
17
- L{pygeodesy.karney}.
16
+ <https://GeographicLib.SourceForge.io/C++/doc/other.html#python>}, see modules
17
+ L{pygeodesy.geodesicw} and L{pygeodesy.karney}.
18
18
  '''
19
19
 
20
20
  from pygeodesy.geodesicx.gx import GeodesicExact, GeodesicLineExact # PYCHOK exported
@@ -23,7 +23,7 @@ from pygeodesy.karney import Caps, GeodesicError
23
23
  from pygeodesy.lazily import _ALL_DOCS, _ALL_LAZY
24
24
 
25
25
  __all__ = _ALL_LAZY.geodesicx + _ALL_DOCS(Caps, GeodesicError)
26
- __version__ = '24.05.31'
26
+ __version__ = '24.06.19'
27
27
 
28
28
  # **) MIT License
29
29
  #
pygeodesy/geodesicx/gx.py CHANGED
@@ -44,25 +44,26 @@ from pygeodesy.constants import EPS, EPS0, EPS02, MANT_DIG, NAN, PI, _EPSqrt, \
44
44
  from pygeodesy.datums import _earth_datum, _WGS84, _EWGS84
45
45
  # from pygeodesy.ellipsoids import _EWGS84 # from .datums
46
46
  from pygeodesy.errors import GeodesicError, _xkwds_pop2
47
- from pygeodesy.fmath import hypot as _hypot
47
+ from pygeodesy.fmath import hypot as _hypot, Fmt
48
48
  from pygeodesy.fsums import fsumf_, fsum1f_
49
49
  from pygeodesy.geodesicx.gxbases import _cosSeries, _GeodesicBase, \
50
- _sincos12, _sin1cos2, _xnC4
51
- from pygeodesy.geodesicx.gxline import _GeodesicLineExact, _TINY, _update_glXs
52
- from pygeodesy.interns import NN, _COMMASPACE_, _DOT_, _UNDER_
50
+ _sincos12, _sin1cos2, _sinf1cos2d, \
51
+ _TINY, _xnC4
52
+ from pygeodesy.geodesicx.gxline import _GeodesicLineExact, _update_glXs
53
+ from pygeodesy.interns import NN, _DOT_, _UNDER_
53
54
  from pygeodesy.karney import GDict, _around, _atan2d, Caps, _cbrt, _diff182, \
54
55
  _fix90, _K_2_0, _norm2, _norm180, _polynomial, \
55
56
  _signBit, _sincos2, _sincos2d, _sincos2de, _unsigned2
56
57
  from pygeodesy.lazily import _ALL_DOCS, _ALL_MODS as _MODS
57
58
  from pygeodesy.namedTuples import Destination3Tuple, Distance3Tuple
58
59
  from pygeodesy.props import deprecated_Property, Property, Property_RO, property_RO
59
- from pygeodesy.streprs import Fmt, pairs
60
+ # from pygeodesy.streprs import Fmt # from .fmath
60
61
  from pygeodesy.utily import atan2d as _atan2d_reverse, _unrollon, _Wrap, wrap360
61
62
 
62
63
  from math import atan2, copysign, cos, degrees, fabs, radians, sqrt
63
64
 
64
65
  __all__ = ()
65
- __version__ = '24.05.31'
66
+ __version__ = '24.06.24'
66
67
 
67
68
  _MAXIT1 = 20
68
69
  _MAXIT2 = 10 + _MAXIT1 + MANT_DIG # MANT_DIG == C++ digits
@@ -553,10 +554,10 @@ class GeodesicExact(_GeodesicBase):
553
554
  # to check, e.g., on verifying quadrants in atan2. In addition,
554
555
  # this enforces some symmetries in the results returned.
555
556
 
556
- # Initialize for the meridian. No longitude calculation is
557
- # done in this case to let the parameter default to 0.
558
- sbet1, cbet1 = self._sinf1cos2d(lat1)
559
- sbet2, cbet2 = self._sinf1cos2d(lat2)
557
+ # Initialize for the meridian. No longitude calculation is done in
558
+ # this case to let the parameter default to 0.
559
+ sbet1, cbet1 = _sinf1cos2d(lat1, self.f1)
560
+ sbet2, cbet2 = _sinf1cos2d(lat2, self.f1)
560
561
  # If cbet1 < -sbet1, then cbet2 - cbet1 is a sensitive measure
561
562
  # of the |bet1| - |bet2|. Alternatively (cbet1 >= -sbet1),
562
563
  # abs(sbet2) + sbet1 is a better measure. This logic is used
@@ -1241,27 +1242,16 @@ class GeodesicExact(_GeodesicBase):
1241
1242
 
1242
1243
  Polygon = Area # for C{geographiclib} compatibility
1243
1244
 
1244
- def _sinf1cos2d(self, lat):
1245
- '''(INTERNAL) Helper, also for C{_G_GeodesicLineExact}.
1246
- '''
1247
- sbet, cbet = _sincos2d(lat)
1248
- # ensure cbet1 = +epsilon at poles; doing the fix on beta means
1249
- # that sig12 will be <= 2*tiny for two points at the same pole
1250
- sbet, cbet = _norm2(sbet * self.f1, cbet)
1251
- return sbet, (cbet if fabs(cbet) > _TINY else _TINY)
1252
-
1253
- def toStr(self, prec=6, sep=_COMMASPACE_, **unused): # PYCHOK signature
1245
+ def toStr(self, **prec_sep_name): # PYCHOK signature
1254
1246
  '''Return this C{GeodesicExact} as string.
1255
1247
 
1256
- @kwarg prec: The C{float} precision, number of decimal digits (0..9).
1257
- Trailing zero decimals are stripped for B{C{prec}} values
1258
- of 1 and above, but kept for negative B{C{prec}} values.
1259
- @kwarg sep: Separator to join (C{str}).
1248
+ @see: L{Ellipsoid.toStr<pygeodesy.ellipsoids.Ellipsoid.toStr>}
1249
+ for further details.
1260
1250
 
1261
- @return: Tuple items (C{str}).
1251
+ @return: C{GeodesicExact} (C{str}).
1262
1252
  '''
1263
- d = dict(ellipsoid=self.ellipsoid, C4order=self.C4order)
1264
- return sep.join(pairs(d, prec=prec))
1253
+ return self._instr(props=(GeodesicExact.ellipsoid,),
1254
+ C4order=self.C4order, **prec_sep_name)
1265
1255
 
1266
1256
 
1267
1257
  class GeodesicLineExact(_GeodesicLineExact):
@@ -1290,7 +1280,7 @@ class GeodesicLineExact(_GeodesicLineExact):
1290
1280
  '''
1291
1281
  _xinstanceof(GeodesicExact, geodesic=geodesic)
1292
1282
  if (caps & Caps.LINE_OFF): # copy to avoid updates
1293
- geodesic = geodesic.copy(deep=False, name=_UNDER_(NN, geodesic.name)) # NOT _under!
1283
+ geodesic = geodesic.copy(deep=False, name=_UNDER_(NN, geodesic.name))
1294
1284
  # _update_all(geodesic)
1295
1285
  _GeodesicLineExact.__init__(self, geodesic, lat1, lon1, azi1, caps, 0, **name)
1296
1286
 
@@ -9,19 +9,21 @@ U{GeographicLib<https://GeographicLib.SourceForge.io>} documentation.
9
9
  '''
10
10
 
11
11
  from pygeodesy.basics import isodd, _MODS
12
- from pygeodesy.constants import _0_0, _100_0
12
+ from pygeodesy.constants import _EPSmin as _TINY, _0_0, _100_0
13
13
  from pygeodesy.errors import _or, _xkwds_item2
14
14
  from pygeodesy.fmath import hypot as _hypot
15
- from pygeodesy.karney import _CapsBase, GeodesicError, _2cos2x, _sum2_
15
+ from pygeodesy.karney import _CapsBase, GeodesicError, _2cos2x, \
16
+ _norm2, _sincos2d, _sum2_
16
17
  # from pygeodesy.lazily import _MODS, printf # .basics, _MODS
17
18
 
18
- from math import ldexp as _ldexp
19
+ from math import fabs, ldexp as _ldexp
19
20
 
20
21
  __all__ = ()
21
- __version__ = '24.05.19'
22
+ __version__ = '24.06.16'
22
23
 
23
24
  # valid C{nC4}s and C{C4order}s, see _xnC4 below
24
25
  _nC4s = {24: 2900, 27: 4032, 30: 5425}
26
+ # assert (_TINY * EPS) > 0 and (_TINY + EPS) == EPS # underflow guard
25
27
 
26
28
 
27
29
  class _GeodesicBase(_CapsBase): # in .geodsolve
@@ -71,15 +73,15 @@ def _cosSeries(c4s, sx, cx): # PYCHOK shared .geodesicx.gx and -.gxline
71
73
  y0 = t0 = y1 = t1 = _0_0
72
74
  c4 = list(c4s)
73
75
  _c4 = c4.pop
74
- _s2 = _sum2_
75
76
  if isodd(len(c4)):
76
77
  y0 = _c4()
78
+ _s2 = _sum2_
77
79
  while c4:
78
80
  # y1 = ar * y0 - y1 + c4.pop()
79
81
  # y0 = ar * y1 - y0 + c4.pop()
80
82
  y1, t1 = _s2(ar * y0, ar * t0, -y1, -t1, _c4())
81
83
  y0, t0 = _s2(ar * y1, ar * t1, -y0, -t0, _c4())
82
- # s = cx * (y0 - y1)
84
+ # s = (y0 - y1) * cx
83
85
  s, _ = _s2(cx * y0, _0_0, cx * t0, -cx * y1, -cx * t1)
84
86
  return s
85
87
 
@@ -93,7 +95,7 @@ def _f2(hi, lo): # in .geodesicx._C4_24, _27 and _30
93
95
  return _ldexp(_f(hi), 52) + _f(lo)
94
96
 
95
97
 
96
- def _sincos12(sin1, cos1, sin2, cos2, sineg0=False): # PYCHOK shared
98
+ def _sincos12(sin1, cos1, sin2, cos2, sineg0=False):
97
99
  '''(INTERNAL) Compute the sine and cosine of angle
98
100
  M{ang12 = atan2(sin2, cos2) - atan2(sin1, cos1)}.
99
101
 
@@ -111,7 +113,7 @@ def _sincos12(sin1, cos1, sin2, cos2, sineg0=False): # PYCHOK shared
111
113
  return s, c
112
114
 
113
115
 
114
- def _sin1cos2(sin1, cos1, sin2, cos2): # PYCHOK shared
116
+ def _sin1cos2(sin1, cos1, sin2, cos2):
115
117
  '''(INTERNAL) Compute the C{sin1 * cos2} sine and its cosine.
116
118
 
117
119
  @return: 2-Tuple C{(sin1 * cos2, hypot(sin1 * sin2, cos1)}.
@@ -121,6 +123,16 @@ def _sin1cos2(sin1, cos1, sin2, cos2): # PYCHOK shared
121
123
  return s, c # _norm2(s, c)
122
124
 
123
125
 
126
+ def _sinf1cos2d(lat, f1):
127
+ '''(INTERNAL) See C{GeodesicExact} and C{_GeodesicLineExact}.
128
+ '''
129
+ sbet, cbet = _sincos2d(lat)
130
+ # ensure cbet1 = +epsilon at poles; doing the fix on beta means
131
+ # that sig12 will be <= 2*tiny for two points at the same pole
132
+ sbet, cbet = _norm2(sbet * f1, cbet)
133
+ return sbet, (cbet if fabs(cbet) > _TINY else _TINY)
134
+
135
+
124
136
  def _xnC4(**name_nC4):
125
137
  '''(INTERNAL) Validate C{C4order}.
126
138
  '''
@@ -37,30 +37,27 @@ from __future__ import division as _; del _ # PYCHOK semicolon
37
37
  # - s and c prefixes mean sin and cos
38
38
 
39
39
  # from pygeodesy.basics import _xinstanceof # _MODS
40
- from pygeodesy.constants import NAN, _EPSmin, _EPSqrt as _TOL, _0_0, \
41
- _1_0, _180_0, _2__PI, _copysign_1_0
42
- from pygeodesy.errors import _xError, _COMMASPACE_
40
+ from pygeodesy.constants import NAN, _EPSqrt as _TOL, _0_0, _1_0, \
41
+ _180_0, _2__PI, _copysign_1_0
42
+ # from pygeodesy.errors import _xError # _MODS
43
43
  from pygeodesy.fsums import fsumf_, fsum1f_
44
44
  from pygeodesy.geodesicx.gxbases import _cosSeries, _GeodesicBase, \
45
- _sincos12, _sin1cos2
45
+ _sincos12, _sin1cos2, \
46
+ _sinf1cos2d, _TINY
46
47
  # from pygeodesy.geodesicw import _Intersecant2 # _MODS
47
- # from pygeodesy.interns import _COMMASPACE_ # from .errors
48
48
  from pygeodesy.lazily import _ALL_DOCS, _ALL_MODS as _MODS
49
49
  from pygeodesy.karney import _around, _atan2d, Caps, GDict, _fix90, \
50
50
  _K_2_0, _norm2, _norm180, _sincos2, _sincos2d
51
- from pygeodesy.props import Property_RO, _update_all
52
- # from pygeodesy.streprs import pairs # _MODS
51
+ from pygeodesy.named import Property_RO, _update_all
52
+ # from pygeodesy.props import Property_RO, _update_all # from .named
53
53
  from pygeodesy.utily import atan2d as _atan2d_reverse, sincos2
54
54
 
55
55
  from math import atan2, cos, degrees, fabs, floor, radians, sin
56
56
 
57
57
  __all__ = ()
58
- __version__ = '24.05.19'
58
+ __version__ = '24.06.24'
59
59
 
60
60
  _glXs = [] # instances of C{[_]GeodesicLineExact} to be updated
61
- # underflow guard, we require _TINY * EPS > 0, _TINY + EPS == EPS
62
- _TINY = _EPSmin
63
- # assert (_TINY * EPS) > 0 and (_TINY + EPS) == EPS
64
61
 
65
62
 
66
63
  def _update_glXs(gX): # see GeodesicExact.C4order and -._ef_reset_k2
@@ -122,7 +119,7 @@ class _GeodesicLineExact(_GeodesicBase):
122
119
  # allow lat, azimuth and unrolling of lon
123
120
  self._caps = caps | Cs._LINE
124
121
 
125
- sbet1, cbet1 = gX._sinf1cos2d(_around(lat1))
122
+ sbet1, cbet1 = _sinf1cos2d(_around(lat1), gX.f1)
126
123
  self._dn1 = gX._dn(sbet1, cbet1)
127
124
  # Evaluate alp0 from sin(alp1) * cos(bet1) = sin(alp0), with alp0
128
125
  # in [0, pi/2 - |bet1|]. Alt: calp0 = hypot(sbet1, calp1 * cbet1),
@@ -501,7 +498,7 @@ class _GeodesicLineExact(_GeodesicBase):
501
498
  try:
502
499
  return _MODS.geodesicw._Intersecant2(self, lat0, lon0, radius, tol=tol)
503
500
  except (TypeError, ValueError) as x:
504
- raise _xError(x, lat0, lon0, radius, tol=_TOL)
501
+ raise _MODS.errors._xError(x, lat0, lon0, radius, tol=_TOL)
505
502
 
506
503
  @Property_RO
507
504
  def _H0e2_f1(self):
@@ -626,20 +623,17 @@ class _GeodesicLineExact(_GeodesicBase):
626
623
  # unnecessary because Einv inverts E
627
624
  # return -self._eF.deltaEinv(stau1, ctau1)
628
625
 
629
- def toStr(self, prec=6, sep=_COMMASPACE_, **unused): # PYCHOK signature
626
+ def toStr(self, **prec_sep_name): # PYCHOK signature
630
627
  '''Return this C{GeodesicLineExact} as string.
631
628
 
632
- @kwarg prec: The C{float} precision, number of decimal digits (0..9).
633
- Trailing zero decimals are stripped for B{C{prec}} values
634
- of 1 and above, but kept for negative B{C{prec}} values.
635
- @kwarg sep: Separator to join (C{str}).
629
+ @see: L{Ellipsoid.toStr<pygeodesy.ellipsoids.Ellipsoid.toStr>}
630
+ for further details.
636
631
 
637
632
  @return: C{GeodesicLineExact} (C{str}).
638
633
  '''
639
- d = dict(geodesic=self.geodesic,
640
- lat1=self.lat1, lon1=self.lon1, azi1=self.azi1,
641
- a13=self.a13, s13=self.s13)
642
- return sep.join(_MODS.streprs.pairs(d, prec=prec))
634
+ C = _GeodesicLineExact
635
+ t = C.lat1, C.lon1, C.azi1, C.a13, C.s13, C.geodesic
636
+ return self._instr(props=t, **prec_sep_name)
643
637
 
644
638
 
645
639
  __all__ += _ALL_DOCS(_GeodesicLineExact)
pygeodesy/geodsolve.py CHANGED
@@ -10,30 +10,32 @@ of the C{GeodSolve} executable.
10
10
  '''
11
11
 
12
12
  from pygeodesy.basics import _xinstanceof
13
+ # from pygeodesy.constants import NAN, _0_0 # from .karney
13
14
  # from pygeodesy.geodesicx import GeodesicAreaExact # _MODS
14
15
  from pygeodesy.interns import NN, _a12_, _azi1_, _azi2_, \
15
16
  _lat1_, _lat2_, _lon1_, _lon2_, _m12_, \
16
17
  _M12_, _M21_, _s12_, _S12_, _UNDER_
17
18
  from pygeodesy.interns import _UNUSED_, _not_ # PYCHOK used!
18
19
  from pygeodesy.karney import _Azi, Caps, _Deg, GeodesicError, _GTuple, \
19
- _Pass, _Lat, _Lon, _M, _M2, _sincos2d
20
+ _Pass, _Lat, _Lon, _M, _M2, _sincos2d, \
21
+ _0_0, NAN
20
22
  from pygeodesy.lazily import _ALL_DOCS, _ALL_LAZY, _ALL_MODS as _MODS, \
21
23
  _getenv, _PYGEODESY_GEODSOLVE_
22
24
  from pygeodesy.named import _name1__
23
25
  from pygeodesy.namedTuples import Destination3Tuple, Distance3Tuple
24
- from pygeodesy.props import Property, Property_RO
26
+ from pygeodesy.props import Property, Property_RO, property_RO
25
27
  from pygeodesy.solveBase import _SolveBase, _SolveLineBase
26
28
  from pygeodesy.utily import _unrollon, _Wrap, wrap360
27
29
 
28
30
  __all__ = _ALL_LAZY.geodsolve
29
- __version__ = '24.05.31'
31
+ __version__ = '24.06.26'
30
32
 
31
33
 
32
34
  class GeodSolve12Tuple(_GTuple):
33
35
  '''12-Tuple C{(lat1, lon1, azi1, lat2, lon2, azi2, s12, a12, m12, M12, M21, S12)} with
34
36
  angles C{lat1}, C{lon1}, C{azi1}, C{lat2}, C{lon2} and C{azi2} and arc C{a12} all in
35
37
  C{degrees}, initial C{azi1} and final C{azi2} forward azimuths, distance C{s12} and
36
- reduced length C{m12} in C{meter}, area C{S12} in C{meter} I{squared} and geodesic
38
+ reduced length C{m12} in C{meter}, area C{S12} in C{meter} I{squared} and geodesic
37
39
  scale factors C{M12} and C{M21}, both C{scalar}, see U{GeodSolve
38
40
  <https://GeographicLib.SourceForge.io/C++/doc/GeodSolve.1.html>}.
39
41
  '''
@@ -59,11 +61,11 @@ class _GeodesicSolveBase(_SolveBase):
59
61
  def _cmdBasic(self):
60
62
  '''(INTERNAL) Get the basic C{GeodSolve} cmd (C{tuple}).
61
63
  '''
62
- return (self.GeodSolve,) + self._b_option \
63
- + self._e_option \
64
- + self._E_option \
65
- + self._p_option \
66
- + self._u_option + ('-f',)
64
+ return (self.GeodSolve, '-f') + (self._b_option +
65
+ self._e_option +
66
+ self._E_option +
67
+ self._p_option +
68
+ self._u_option)
67
69
 
68
70
  @Property_RO
69
71
  def _E_option(self):
@@ -222,8 +224,11 @@ class GeodesicSolve(_GeodesicSolveBase):
222
224
  <https://GeographicLib.SourceForge.io/C++/doc/classGeographicLib_1_1GeodesicExact.html>} and
223
225
  Python U{Geodesic.InverseLine<https://GeographicLib.SourceForge.io/Python/doc/code.html>}.
224
226
  '''
225
- r = self.Inverse(lat1, lon1, lat2, lon2)
226
- return GeodesicLineSolve(self, lat1, lon1, r.azi1, **_name1__(caps_name, _or_nameof=self))
227
+ r = self.Inverse(lat1, lon1, lat2, lon2)
228
+ gl = GeodesicLineSolve(self, lat1, lon1, r.azi1, **_name1__(caps_name, _or_nameof=self))
229
+ gl._a13 = r.a12 # gl.SetArc(r.a12)
230
+ gl._s13 = r.s12 # gl.SetDistance(r.s12)
231
+ return gl
227
232
 
228
233
 
229
234
  class GeodesicLineSolve(_GeodesicSolveBase, _SolveLineBase):
@@ -237,6 +242,8 @@ class GeodesicLineSolve(_GeodesicSolveBase, _SolveLineBase):
237
242
  @note: This C{geodesic} is intended I{for testing purposes only}, it invokes the C{GeodSolve}
238
243
  executable for I{every} method call.
239
244
  '''
245
+ _a13 = \
246
+ _s13 = NAN # see GeodesicSolve._InverseLine
240
247
 
241
248
  def __init__(self, geodesic, lat1, lon1, azi1, caps=Caps.ALL, **name):
242
249
  '''New L{GeodesicLineSolve} instance, allowing points to be found along
@@ -268,7 +275,22 @@ class GeodesicLineSolve(_GeodesicSolveBase, _SolveLineBase):
268
275
  except GeodesicError:
269
276
  pass
270
277
 
271
- def ArcPosition(self, a12, outmask=_UNUSED_): # PYCHOK unused
278
+ @Property_RO
279
+ def a13(self):
280
+ '''Get the arc length to reference point 3 (C{degrees}).
281
+
282
+ @see: Methods L{Arc} and L{SetArc}.
283
+ '''
284
+ return self._a13
285
+
286
+ def Arc(self):
287
+ '''Return the arc length to reference point 3 (C{degrees} or C{NAN}).
288
+
289
+ @see: Method L{SetArc} and property L{a13}.
290
+ '''
291
+ return self.a13
292
+
293
+ def ArcPosition(self, a12, outmask=Caps.STANDARD): # PYCHOK unused
272
294
  '''Find the position on the line given B{C{a12}}.
273
295
 
274
296
  @arg a12: Spherical arc length from the first point to the
@@ -277,7 +299,7 @@ class GeodesicLineSolve(_GeodesicSolveBase, _SolveLineBase):
277
299
  @return: A C{GDict} with 12 items C{lat1, lon1, azi1, lat2, lon2,
278
300
  azi2, m12, a12, s12, M12, M21, S12}.
279
301
  '''
280
- return self._GDictInvoke(self._cmdArc, True, self._Names_Direct, a12)
302
+ return self._GDictInvoke(self._cmdArc, True, self._Names_Direct, a12)._unCaps(outmask)
281
303
 
282
304
  @Property_RO
283
305
  def azi1(self):
@@ -301,6 +323,17 @@ class GeodesicLineSolve(_GeodesicSolveBase, _SolveLineBase):
301
323
  '''
302
324
  return self._cmdDistance + ('-a',)
303
325
 
326
+ def Distance(self):
327
+ '''Return the distance to reference point 3 (C{meter} or C{NAN}).
328
+ '''
329
+ return self.s13
330
+
331
+ @property_RO
332
+ def geodesic(self):
333
+ '''Get the geodesic (L{GeodesicSolve}).
334
+ '''
335
+ return self._solve # see .solveBase._SolveLineBase
336
+
304
337
  def Intersecant2(self, lat0, lon0, radius, **kwds): # PYCHOK no cover
305
338
  '''B{Not implemented}, throws a C{NotImplementedError} always.'''
306
339
  self._notImplemented(lat0, lon0, radius, **kwds)
@@ -309,7 +342,7 @@ class GeodesicLineSolve(_GeodesicSolveBase, _SolveLineBase):
309
342
  '''B{Not implemented}, throws a C{NotImplementedError} always.'''
310
343
  self._notImplemented(lat0, lon0, **kwds)
311
344
 
312
- def Position(self, s12, outmask=_UNUSED_): # PYCHOK unused
345
+ def Position(self, s12, outmask=Caps.STANDARD):
313
346
  '''Find the position on the line given B{C{s12}}.
314
347
 
315
348
  @arg s12: Distance from the first point to the second (C{meter}).
@@ -317,7 +350,44 @@ class GeodesicLineSolve(_GeodesicSolveBase, _SolveLineBase):
317
350
  @return: A C{GDict} with 12 items C{lat1, lon1, azi1, lat2, lon2,
318
351
  azi2, m12, a12, s12, M12, M21, S12}, possibly C{a12=NAN}.
319
352
  '''
320
- return self._GDictInvoke(self._cmdDistance, True, self._Names_Direct, s12)
353
+ return self._GDictInvoke(self._cmdDistance, True, self._Names_Direct, s12)._unCaps(outmask)
354
+
355
+ @Property_RO
356
+ def s13(self):
357
+ '''Get the distance to reference point 3 (C{meter} or C{NAN}).
358
+
359
+ @see: Methods L{Distance} and L{SetDistance}.
360
+ '''
361
+ return self._s13
362
+
363
+ def SetArc(self, a13):
364
+ '''Set reference point 3 in terms relative to the first point.
365
+
366
+ @arg a13: Spherical arc length from the first to the reference
367
+ point (C{degrees}).
368
+
369
+ @return: The distance C{s13} (C{meter}) between the first and
370
+ the reference point or C{NAN}.
371
+ '''
372
+ if self._a13 != a13:
373
+ self._a13 = a13
374
+ self._s13 = self.ArcPosition(a13, outmask=Caps.DISTANCE).s12 # if a13 else _0_0
375
+ # _update_all(self)
376
+ return self._s13
377
+
378
+ def SetDistance(self, s13):
379
+ '''Set reference point 3 in terms relative to the first point.
380
+
381
+ @arg s13: Distance from the first to the reference point (C{meter}).
382
+
383
+ @return: The arc length C{a13} (C{degrees}) between the first
384
+ and the reference point or C{NAN}.
385
+ '''
386
+ if self._s13 != s13:
387
+ self._s13 = s13
388
+ self._a13 = self.Position(s13, outmask=Caps.DISTANCE).a12 if s13 else _0_0
389
+ # _update_all(self)
390
+ return self._a13 # NAN for GeodesicLineExact without Cap.DISTANCE_IN
321
391
 
322
392
  def toStr(self, **prec_sep): # PYCHOK signature
323
393
  '''Return this C{GeodesicLineSolve} as string.
@@ -362,49 +432,47 @@ if __name__ == '__main__':
362
432
  p = glS.ArcPosition(49.475527)
363
433
  printf('ArcPosition: %s %r', p == r, p)
364
434
 
365
- # % python3 -m pygeodesy.geodsolve
366
435
 
367
- # version: /opt/local/bin/GeodSolve: GeographicLib version 1.51
436
+ # % python3 -m pygeodesy.geodsolve
368
437
 
369
- # version: /opt/local/bin/GeodSolve: GeographicLib version 1.51
438
+ # version: /opt/local/bin/GeodSolve: GeographicLib version 2.2
370
439
 
371
- # Direct: GDict(M12=0.650911, M21=0.651229, S12=39735075134877.09375, a12=49.475527, azi1=51.0, azi2=107.189397, lat1=40.6, lat2=51.884565, lon1=-73.8, lon2=-1.141173, m12=4844148.703101, s12=5500000.0)
440
+ # 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)
372
441
  # Direct3: Destination3Tuple(lat=51.884565, lon=-1.141173, final=107.189397)
373
442
 
374
- # Inverse: GDict(M12=0.64473, M21=0.645046, S12=40041368848742.53125, a12=49.94131, azi1=51.198883, azi2=107.821777, lat1=40.6, lat2=51.6, lon1=-73.8, lon2=-0.5, m12=4877684.602706, s12=5551759.400319)
443
+ # 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)
375
444
  # Inverse1: 49.94131021789904
376
445
  # Inverse3: Distance3Tuple(distance=5551759.400319, initial=51.198883, final=107.821777)
377
446
 
378
- # Position: True GDict(M12=0.650911, M21=0.651229, S12=39735075134877.09375, a12=49.475527, azi1=51.0, azi2=107.189397, lat1=40.6, lat2=51.884565, lon1=-73.8, lon2=-1.141173, m12=4844148.703101, s12=5500000.0)
379
- # ArcPosition: False GDict(M12=0.650911, M21=0.651229, S12=39735074737272.734375, a12=49.475527, azi1=51.0, azi2=107.189397, lat1=40.6, lat2=51.884565, lon1=-73.8, lon2=-1.141174, m12=4844148.669561, s12=5499999.948497)
447
+ # 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)
448
+ # 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)
380
449
 
381
450
 
382
451
  # % python3 -m pygeodesy.geodsolve --verbose
383
452
 
384
453
  # GeodesicSolve 'Test' 1: /opt/local/bin/GeodSolve --version (invoke)
385
- # GeodesicSolve 'Test' 1: /opt/local/bin/GeodSolve: GeographicLib version 1.51 (0)
386
- # version: /opt/local/bin/GeodSolve: GeographicLib version 1.51
387
- # GeodesicSolve 'Test' 2: /opt/local/bin/GeodSolve -E -p 10 -f \ 40.600000000000001 -73.799999999999997 51.0 5500000.0 (Direct)
454
+ # GeodesicSolve 'Test' 1: /opt/local/bin/GeodSolve: GeographicLib version 2.2 (0)
455
+ # version: /opt/local/bin/GeodSolve: GeographicLib version 2.2
456
+ # GeodesicSolve 'Test' 2: /opt/local/bin/GeodSolve -f -E -p 10 \ 40.600000000000001 -73.799999999999997 51.0 5500000.0 (Direct)
388
457
  # 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)
389
458
 
390
- # Direct: GDict(M12=0.650911, M21=0.651229, S12=39735075134877.09375, a12=49.475527, azi1=51.0, azi2=107.189397, lat1=40.6, lat2=51.884565, lon1=-73.8, lon2=-1.141173, m12=4844148.703101, s12=5500000.0)
391
- # GeodesicSolve 'Test' 3: /opt/local/bin/GeodSolve -E -p 10 -f \ 40.600000000000001 -73.799999999999997 51.0 5500000.0 (Direct3)
459
+ # 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)
460
+ # GeodesicSolve 'Test' 3: /opt/local/bin/GeodSolve -f -E -p 10 \ 40.600000000000001 -73.799999999999997 51.0 5500000.0 (Direct3)
392
461
  # 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)
393
462
  # Direct3: Destination3Tuple(lat=51.884565, lon=-1.141173, final=107.189397)
394
- # GeodesicSolve 'Test' 4: /opt/local/bin/GeodSolve -E -p 10 -f -i \ 40.600000000000001 -73.799999999999997 51.600000000000001 -0.5 (Inverse)
463
+ # GeodesicSolve 'Test' 4: /opt/local/bin/GeodSolve -f -E -p 10 -i \ 40.600000000000001 -73.799999999999997 51.600000000000001 -0.5 (Inverse)
395
464
  # 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)
396
465
 
397
- # Inverse: GDict(M12=0.64473, M21=0.645046, S12=40041368848742.53125, a12=49.94131, azi1=51.198883, azi2=107.821777, lat1=40.6, lat2=51.6, lon1=-73.8, lon2=-0.5, m12=4877684.602706, s12=5551759.400319)
398
- # GeodesicSolve 'Test' 5: /opt/local/bin/GeodSolve -E -p 10 -f -i \ 40.600000000000001 -73.799999999999997 51.600000000000001 -0.5 (Inverse1)
466
+ # 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)
467
+ # GeodesicSolve 'Test' 5: /opt/local/bin/GeodSolve -f -E -p 10 -i \ 40.600000000000001 -73.799999999999997 51.600000000000001 -0.5 (Inverse1)
399
468
  # 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)
400
469
  # Inverse1: 49.94131021789904
401
- # GeodesicSolve 'Test' 6: /opt/local/bin/GeodSolve -E -p 10 -f -i \ 40.600000000000001 -73.799999999999997 51.600000000000001 -0.5 (Inverse3)
470
+ # GeodesicSolve 'Test' 6: /opt/local/bin/GeodSolve -f -E -p 10 -i \ 40.600000000000001 -73.799999999999997 51.600000000000001 -0.5 (Inverse3)
402
471
  # 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)
403
472
  # Inverse3: Distance3Tuple(distance=5551759.400319, initial=51.198883, final=107.821777)
404
473
 
405
- # Position: True GDict(M12=0.650911, M21=0.651229, S12=39735075134877.09375, a12=49.475527, azi1=51.0, azi2=107.189397, lat1=40.6, lat2=51.884565, lon1=-73.8, lon2=-1.141173, m12=4844148.703101, s12=5500000.0)
406
- # ArcPosition: False GDict(M12=0.650911, M21=0.651229, S12=39735074737272.734375, a12=49.475527, azi1=51.0, azi2=107.189397, lat1=40.6, lat2=51.884565, lon1=-73.8, lon2=-1.141174, m12=4844148.669561, s12=5499999.948497)
407
-
474
+ # 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)
475
+ # 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)
408
476
 
409
477
  # **) MIT License
410
478
  #