pygeodesy 24.6.9__py2.py3-none-any.whl → 24.7.7__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 (87) hide show
  1. {PyGeodesy-24.6.9.dist-info → PyGeodesy-24.7.7.dist-info}/METADATA +2 -2
  2. PyGeodesy-24.7.7.dist-info/RECORD +117 -0
  3. pygeodesy/__init__.py +39 -32
  4. pygeodesy/__main__.py +6 -1
  5. pygeodesy/albers.py +2 -2
  6. pygeodesy/auxilats/__init__.py +1 -1
  7. pygeodesy/auxilats/auxAngle.py +40 -39
  8. pygeodesy/auxilats/auxDLat.py +3 -2
  9. pygeodesy/auxilats/auxLat.py +16 -18
  10. pygeodesy/auxilats/auxily.py +1 -1
  11. pygeodesy/azimuthal.py +10 -10
  12. pygeodesy/basics.py +16 -4
  13. pygeodesy/booleans.py +4 -4
  14. pygeodesy/cartesianBase.py +11 -14
  15. pygeodesy/css.py +14 -18
  16. pygeodesy/datums.py +6 -6
  17. pygeodesy/deprecated/__init__.py +1 -1
  18. pygeodesy/deprecated/classes.py +25 -4
  19. pygeodesy/deprecated/datum.py +3 -3
  20. pygeodesy/deprecated/functions.py +6 -8
  21. pygeodesy/dms.py +23 -27
  22. pygeodesy/ecef.py +4 -4
  23. pygeodesy/elevations.py +4 -4
  24. pygeodesy/ellipsoidalBase.py +23 -28
  25. pygeodesy/ellipsoidalBaseDI.py +19 -23
  26. pygeodesy/ellipsoidalExact.py +3 -3
  27. pygeodesy/ellipsoidalGeodSolve.py +15 -23
  28. pygeodesy/ellipsoidalKarney.py +37 -60
  29. pygeodesy/ellipsoidalNvector.py +38 -44
  30. pygeodesy/ellipsoidalVincenty.py +11 -14
  31. pygeodesy/ellipsoids.py +107 -101
  32. pygeodesy/errors.py +109 -48
  33. pygeodesy/etm.py +32 -44
  34. pygeodesy/formy.py +55 -58
  35. pygeodesy/frechet.py +18 -20
  36. pygeodesy/fsums.py +3 -3
  37. pygeodesy/gars.py +3 -4
  38. pygeodesy/geodesici.py +1696 -0
  39. pygeodesy/geodesicw.py +15 -15
  40. pygeodesy/geodesicx/__init__.py +4 -4
  41. pygeodesy/geodesicx/gx.py +34 -55
  42. pygeodesy/geodesicx/gxbases.py +20 -8
  43. pygeodesy/geodesicx/gxline.py +93 -88
  44. pygeodesy/geodsolve.py +108 -59
  45. pygeodesy/geohash.py +26 -34
  46. pygeodesy/geoids.py +28 -37
  47. pygeodesy/hausdorff.py +17 -18
  48. pygeodesy/heights.py +2 -2
  49. pygeodesy/internals.py +46 -13
  50. pygeodesy/interns.py +2 -2
  51. pygeodesy/karney.py +78 -15
  52. pygeodesy/ktm.py +13 -16
  53. pygeodesy/latlonBase.py +17 -19
  54. pygeodesy/lazily.py +28 -25
  55. pygeodesy/lcc.py +28 -31
  56. pygeodesy/ltp.py +7 -8
  57. pygeodesy/ltpTuples.py +71 -73
  58. pygeodesy/mgrs.py +8 -9
  59. pygeodesy/named.py +19 -10
  60. pygeodesy/nvectorBase.py +9 -10
  61. pygeodesy/osgr.py +9 -9
  62. pygeodesy/points.py +6 -6
  63. pygeodesy/rhumb/__init__.py +1 -1
  64. pygeodesy/rhumb/aux_.py +5 -5
  65. pygeodesy/rhumb/bases.py +30 -31
  66. pygeodesy/rhumb/ekx.py +3 -4
  67. pygeodesy/rhumb/solve.py +21 -22
  68. pygeodesy/solveBase.py +177 -123
  69. pygeodesy/sphericalBase.py +10 -11
  70. pygeodesy/sphericalNvector.py +13 -13
  71. pygeodesy/sphericalTrigonometry.py +86 -97
  72. pygeodesy/streprs.py +4 -34
  73. pygeodesy/triaxials.py +48 -43
  74. pygeodesy/units.py +208 -275
  75. pygeodesy/unitsBase.py +115 -107
  76. pygeodesy/ups.py +26 -31
  77. pygeodesy/utily.py +8 -8
  78. pygeodesy/utm.py +35 -40
  79. pygeodesy/utmups.py +43 -46
  80. pygeodesy/utmupsBase.py +8 -9
  81. pygeodesy/vector3d.py +26 -27
  82. pygeodesy/vector3dBase.py +6 -7
  83. pygeodesy/webmercator.py +19 -21
  84. pygeodesy/wgrs.py +18 -20
  85. PyGeodesy-24.6.9.dist-info/RECORD +0 -116
  86. {PyGeodesy-24.6.9.dist-info → PyGeodesy-24.7.7.dist-info}/WHEEL +0 -0
  87. {PyGeodesy-24.6.9.dist-info → PyGeodesy-24.7.7.dist-info}/top_level.txt +0 -0
@@ -37,30 +37,28 @@ 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, isfinite
42
+ from pygeodesy.errors import _xError, _xkwds_pop2
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
- _K_2_0, _norm2, _norm180, _sincos2, _sincos2d
51
- from pygeodesy.props import Property_RO, _update_all
52
- # from pygeodesy.streprs import pairs # _MODS
50
+ _K_2_0, _llz2line, _norm2, _norm180, \
51
+ _sincos2, _sincos2d
52
+ from pygeodesy.named import Property_RO, _update_all
53
+ # from pygeodesy.props import Property_RO, _update_all # from .named
53
54
  from pygeodesy.utily import atan2d as _atan2d_reverse, sincos2
54
55
 
55
56
  from math import atan2, cos, degrees, fabs, floor, radians, sin
56
57
 
57
58
  __all__ = ()
58
- __version__ = '24.05.19'
59
+ __version__ = '24.07.09'
59
60
 
60
61
  _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
62
 
65
63
 
66
64
  def _update_glXs(gX): # see GeodesicExact.C4order and -._ef_reset_k2
@@ -95,23 +93,18 @@ class _GeodesicLineExact(_GeodesicBase):
95
93
  # _somg1 = _comg1 = NAN
96
94
  # _ssig1 = _csig1 = NAN
97
95
 
98
- def __init__(self, gX, lat1, lon1, azi1, caps, _debug, *salp1_calp1, **name):
96
+ def __init__(self, gX, lat1, lon1, azi1, caps, **name_):
99
97
  '''(INTERNAL) New C{[_]GeodesicLineExact} instance.
100
98
  '''
101
- _xGeodesicExact(gX=gX)
102
- Cs = Caps
103
- if _debug: # PYCHOK no cover
104
- self._debug |= _debug & Cs._DEBUG_ALL
105
- # _CapsBase.debug._update(self)
106
- if salp1_calp1:
107
- salp1, calp1 = salp1_calp1
108
- else:
99
+ # _xGeodesicExact(gX=gX)
100
+ if azi1 is None: # see GeodesicExact.InverseLine
101
+ (salp1, calp1), name_ = _xkwds_pop2(name_, _s_calp1=(_0_0, _1_0))
102
+ azi1 = _atan2d(salp1, calp1)
103
+ else: # guard against salp0 underflow, convert -0 to +0
109
104
  azi1 = _norm180(azi1)
110
- # guard against salp0 underflow,
111
- # also -0 is converted to +0
112
105
  salp1, calp1 = _sincos2d(_around(azi1))
113
- if name:
114
- self.name = name
106
+ if name_:
107
+ self.name = name_
115
108
 
116
109
  self._gX = gX # GeodesicExact only
117
110
  self._lat1 = lat1 = _fix90(lat1)
@@ -120,9 +113,9 @@ class _GeodesicLineExact(_GeodesicBase):
120
113
  self._salp1 = salp1
121
114
  self._calp1 = calp1
122
115
  # allow lat, azimuth and unrolling of lon
123
- self._caps = caps | Cs._LINE
116
+ self._caps = caps | Caps._LINE
124
117
 
125
- sbet1, cbet1 = gX._sinf1cos2d(_around(lat1))
118
+ sbet1, cbet1 = _sinf1cos2d(_around(lat1), gX.f1)
126
119
  self._dn1 = gX._dn(sbet1, cbet1)
127
120
  # Evaluate alp0 from sin(alp1) * cos(bet1) = sin(alp0), with alp0
128
121
  # in [0, pi/2 - |bet1|]. Alt: calp0 = hypot(sbet1, calp1 * cbet1),
@@ -145,11 +138,11 @@ class _GeodesicLineExact(_GeodesicBase):
145
138
  self._ssig1, self._csig1 = _norm2(sbet1, c) # sig1 in (-pi, pi]
146
139
  # _norm2(somg1, comg1) # no need to normalize!
147
140
  # _norm2(schi1?, cchi1) # no need to normalize!
148
- if not (caps & Cs.LINE_OFF):
141
+ if not (caps & Caps.LINE_OFF):
149
142
  _glXs.append(self)
150
143
  # no need to pre-compute other attrs based on _Caps.X. All are
151
- # Property_RO's, computed once and cached/memoized until reset
152
- # when C4order is changed or Elliptic function reset is invoked.
144
+ # Property_RO's, computed once and cached/memoized until reset when
145
+ # arc, distance, C4order is changed or Elliptic function is reset.
153
146
 
154
147
  def __del__(self): # XXX use weakref?
155
148
  if _glXs: # may be empty or None
@@ -298,22 +291,18 @@ class _GeodesicLineExact(_GeodesicBase):
298
291
  C{lon1}, C{azi1} and arc length C{a12} always included,
299
292
  except when C{a12=NAN}.
300
293
  '''
301
-
302
- r = GDict(a12=NAN, s12=NAN) # note both a12 and s12, always
303
- if not (arcmode or self._caps_DISTANCE_IN): # PYCHOK no cover
304
- return r # Uninitialized or impossible distance requested
305
-
306
294
  Cs = Caps
307
- if self._debug: # PYCHOK no cover
308
- outmask |= self._debug & Cs._DEBUG_DIRECT_LINE
309
- outmask &= self._caps & Cs._OUT_MASK
310
-
295
+ if outmask:
296
+ outmask &= self._caps & Cs._OUT_MASK
311
297
  eF = self._eF
312
298
  gX = self.geodesic # ._gX
299
+ r = GDict(a12=NAN, s12=NAN) # both a12 and s12, always
313
300
 
314
- if arcmode:
315
- # s12_a12 is spherical arc length
316
- E2 = _0_0
301
+ if not isfinite(s12_a12):
302
+ # E2 = sig12 = ssig12 = csig12 = NAN
303
+ return r._toNAN(outmask)
304
+ elif arcmode: # s12_a12 is (spherical) arc length
305
+ r.set_(a12=s12_a12)
317
306
  sig12 = radians(s12_a12)
318
307
  if _K_2_0:
319
308
  ssig12, csig12 = sincos2(sig12) # utily, no NEG0
@@ -322,31 +311,25 @@ class _GeodesicLineExact(_GeodesicBase):
322
311
  a -= floor(a / _180_0) * _180_0 # 0 <= 0 < 180
323
312
  ssig12 = _0_0 if a == 0 else sin(sig12)
324
313
  csig12 = _0_0 if a == 90 else cos(sig12)
325
- else: # s12_a12 is distance
314
+ E2 = _0_0
315
+ elif self._caps_DISTANCE_IN: # s12_a12 is distance
326
316
  t = s12_a12 / self._E0b
327
317
  s, c = _sincos2(t) # tau12
328
318
  # tau2 = tau1 + tau12
329
319
  E2 = -eF.deltaEinv(*_sincos12(-s, c, *self._stau1_ctau1))
330
320
  sig12 = fsum1f_(self._E1, -E2, t) # == t - (E2 - E1)
331
321
  ssig12, csig12 = _sincos2(sig12)
332
-
333
- salp0, calp0 = self._salp0, self._calp0
334
- ssig1, csig1 = self._ssig1, self._csig1
322
+ r.set_(a12=degrees(sig12))
323
+ else: # uninitialized or impossible distance requested
324
+ return r
335
325
 
336
326
  # sig2 = sig1 + sig12
337
- ssig2, csig2 = _sincos12(-ssig12, csig12, ssig1, csig1)
338
- dn2 = eF.fDelta(ssig2, csig2)
339
- # sin(bet2) = cos(alp0) * sin(sig2) and
340
- # cbet2 = hypot(salp0, calp0 * csig2). Alt:
341
- # cbet2 = hypot(csig2, salp0 * ssig2)
342
- sbet2, cbet2 = _sin1cos2(calp0, salp0, csig2, ssig2)
343
- if cbet2 == 0: # salp0 = 0, csig2 = 0, break degeneracy
344
- cbet2 = csig2 = _TINY
345
- # tan(alp0) = cos(sig2) * tan(alp2)
346
- salp2 = salp0
347
- calp2 = calp0 * csig2 # no need to normalize
327
+ ssig1, csig1 = self._ssig1, self._csig1
328
+ ssig2, csig2 = t = _sincos12(-ssig12, csig12, ssig1, csig1)
329
+ dn2 = eF.fDelta(*t)
348
330
 
349
- if (outmask & Cs.DISTANCE):
331
+ if (outmask & Cs.DISTANCE):
332
+ outmask ^= Cs.DISTANCE
350
333
  if arcmode: # or f_0_01
351
334
  E2 = eF.deltaE(ssig2, csig2, dn2)
352
335
  # AB1 = _E0 * (E2 - _E1)
@@ -358,10 +341,34 @@ class _GeodesicLineExact(_GeodesicBase):
358
341
  s12 = s12_a12
359
342
  r.set_(s12=s12)
360
343
 
344
+ if not outmask: # all done, see ._GenSet
345
+ return r
346
+
347
+ if self._debug: # PYCHOK no cover
348
+ outmask |= self._debug & Cs._DEBUG_DIRECT_LINE
349
+
361
350
  if (outmask & Cs._DEBUG_DIRECT_LINE): # PYCHOK no cover
362
351
  r.set_(sig12=sig12, dn2=dn2, b=gX.b, e2=gX.e2, f1=gX.f1,
363
352
  E0b=self._E0b, E1=self._E1, E2=E2, eFk2=eF.k2, eFa2=eF.alpha2)
364
353
 
354
+ # sin(bet2) = cos(alp0) * sin(sig2) and
355
+ # cbet2 = hypot(salp0, calp0 * csig2). Alt:
356
+ # cbet2 = hypot(csig2, salp0 * ssig2)
357
+ salp0, calp0 = self._salp0, self._calp0
358
+ sbet2, cbet2 = _sin1cos2(calp0, salp0, csig2, ssig2)
359
+ if cbet2 == 0: # salp0 = 0, csig2 = 0, break degeneracy
360
+ cbet2 = csig2 = _TINY
361
+ # tan(alp0) = cos(sig2) * tan(alp2)
362
+ salp2 = salp0
363
+ calp2 = calp0 * csig2 # no need to normalize
364
+
365
+ if (outmask & Cs.AZIMUTH):
366
+ r.set_(azi2=_atan2d_reverse(salp2, calp2,
367
+ reverse=outmask & Cs.REVERSE2))
368
+
369
+ if (outmask & Cs.LATITUDE):
370
+ r.set_(lat2=_atan2d(sbet2, gX.f1 * cbet2))
371
+
365
372
  if (outmask & Cs.LONGITUDE):
366
373
  schi1 = self._somg1
367
374
  cchi1 = self._cchi1
@@ -375,7 +382,7 @@ class _GeodesicLineExact(_GeodesicBase):
375
382
  tchi2 = t * schi2
376
383
  chi12 = t * fsum1f_(_a(ssig1, csig1), -_a(ssig2, csig2),
377
384
  _a(tchi2, cchi2), -_a(tchi1, cchi1), sig12)
378
- lon2 = self.lon1 + degrees(chi12 - lam12)
385
+ lon2 = self.lon1 + degrees(chi12 - lam12)
379
386
  else:
380
387
  chi12 = atan2(*_sincos12(schi1, cchi1, schi2, cchi2))
381
388
  lon2 = _norm180(self._lon1_norm180 + _norm180(degrees(chi12 - lam12)))
@@ -384,12 +391,6 @@ class _GeodesicLineExact(_GeodesicBase):
384
391
  r.set_(ssig2=ssig2, chi12=chi12, H0e2_f1=self._H0e2_f1,
385
392
  csig2=csig2, lam12=lam12, H1=self._H1)
386
393
 
387
- if (outmask & Cs.LATITUDE):
388
- r.set_(lat2=_atan2d(sbet2, gX.f1 * cbet2))
389
-
390
- if (outmask & Cs.AZIMUTH):
391
- r.set_(azi2=_atan2d_reverse(salp2, calp2, reverse=outmask & Cs.REVERSE2))
392
-
393
394
  if (outmask & Cs._REDUCEDLENGTH_GEODESICSCALE):
394
395
  dn1 = self._dn1
395
396
  J12 = self._D0k2 * fsumf_(eF.deltaD(ssig2, csig2, dn2), -self._D1, sig12)
@@ -441,10 +442,9 @@ class _GeodesicLineExact(_GeodesicBase):
441
442
  S12 += gX.c2 * atan2(salp12, calp12)
442
443
  r.set_(S12=S12)
443
444
 
444
- r.set_(a12=s12_a12 if arcmode else degrees(sig12),
445
+ r.set_(azi1=_norm180(self.azi1),
445
446
  lat1=self.lat1, # == _fix90(lat1)
446
- lon1=self.lon1 if (outmask & Cs.LONG_UNROLL) else self._lon1_norm180,
447
- azi1=_norm180(self.azi1))
447
+ lon1=self.lon1 if (outmask & Cs.LONG_UNROLL) else self._lon1_norm180)
448
448
  return r
449
449
 
450
450
  def _GenPosition(self, arcmode, s12_a12, outmask):
@@ -456,14 +456,24 @@ class _GeodesicLineExact(_GeodesicBase):
456
456
  r = self._GDictPosition(arcmode, s12_a12, outmask)
457
457
  return r.toDirect9Tuple()
458
458
 
459
- def _GenSet(self, arcmode, s13_a13):
459
+ def _GenSet(self, debug, s12=None, a12=None, **llz2):
460
460
  '''(INTERNAL) Aka C++ C{GenSetDistance}.
461
461
  '''
462
- if arcmode:
463
- self.SetArc(s13_a13)
464
- else:
465
- self.SetDistance(s13_a13)
466
- return self # for gx.GeodesicExact.InverseLine and -._GenDirectLine
462
+ Cs = Caps
463
+ if debug: # PYCHOK no cover
464
+ self._debug |= debug & Cs._DEBUG_ALL
465
+ # _CapsBase.debug._update(self)
466
+ if s12 is None:
467
+ if a12 is None: # see GeodesicExact.Line
468
+ return self
469
+ s12 = self._GDictPosition(True, a12, outmask=Cs.DISTANCE).s12 if a12 else _0_0
470
+ elif a12 is None:
471
+ a12 = self._GDictPosition(False, s12, 0).a12 if s12 else _0_0
472
+ self._s13 = s12
473
+ self._a13 = a12
474
+ self._caps |= Cs.DISTANCE | Cs.DISTANCE_IN
475
+ # _update_all(self) # new, from GeodesicExact.*Line
476
+ return _llz2line(self, **llz2)
467
477
 
468
478
  @Property_RO
469
479
  def geodesic(self):
@@ -597,8 +607,7 @@ class _GeodesicLineExact(_GeodesicBase):
597
607
  the reference point or C{NAN}.
598
608
  '''
599
609
  if self._a13 != a13:
600
- self._a13 = a13
601
- self._s13 = self._GDictPosition(True, a13, Caps.DISTANCE).s12 # if a13 else _0_0
610
+ self._GenSet(0, a12=a13)
602
611
  _update_all(self)
603
612
  return self._s13
604
613
 
@@ -611,10 +620,9 @@ class _GeodesicLineExact(_GeodesicBase):
611
620
  and the reference point or C{NAN}.
612
621
  '''
613
622
  if self._s13 != s13:
614
- self._s13 = s13
615
- self._a13 = self._GDictPosition(False, s13, 0).a12 if s13 else _0_0
623
+ self._GenSet(0, s12=s13)
616
624
  _update_all(self)
617
- return self._a13 # NAN for GeodesicLineExact without Cap.DISTANCE_IN
625
+ return self._a13
618
626
 
619
627
  @Property_RO
620
628
  def _stau1_ctau1(self):
@@ -626,20 +634,17 @@ class _GeodesicLineExact(_GeodesicBase):
626
634
  # unnecessary because Einv inverts E
627
635
  # return -self._eF.deltaEinv(stau1, ctau1)
628
636
 
629
- def toStr(self, prec=6, sep=_COMMASPACE_, **unused): # PYCHOK signature
637
+ def toStr(self, **prec_sep_name): # PYCHOK signature
630
638
  '''Return this C{GeodesicLineExact} as string.
631
639
 
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}).
640
+ @see: L{Ellipsoid.toStr<pygeodesy.ellipsoids.Ellipsoid.toStr>}
641
+ for further details.
636
642
 
637
643
  @return: C{GeodesicLineExact} (C{str}).
638
644
  '''
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))
645
+ C = _GeodesicLineExact
646
+ t = C.lat1, C.lon1, C.azi1, C.a13, C.s13, C.caps, C.geodesic
647
+ return self._instr(props=t, **prec_sep_name)
643
648
 
644
649
 
645
650
  __all__ += _ALL_DOCS(_GeodesicLineExact)
pygeodesy/geodsolve.py CHANGED
@@ -10,46 +10,31 @@ 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
- from pygeodesy.interns import NN, _a12_, _azi1_, _azi2_, \
15
- _lat1_, _lat2_, _lon1_, _lon2_, _m12_, \
16
- _M12_, _M21_, _s12_, _S12_, _UNDER_
17
- from pygeodesy.interns import _UNUSED_, _not_ # PYCHOK used!
18
- from pygeodesy.karney import _Azi, Caps, _Deg, GeodesicError, _GTuple, \
19
- _Pass, _Lat, _Lon, _M, _M2, _sincos2d
15
+ from pygeodesy.interns import NN, _UNDER_
16
+ from pygeodesy.karney import Caps, GeodesicError, GeodSolve12Tuple, \
17
+ _llz2line, _sincos2d, _0_0, NAN
20
18
  from pygeodesy.lazily import _ALL_DOCS, _ALL_LAZY, _ALL_MODS as _MODS, \
21
19
  _getenv, _PYGEODESY_GEODSOLVE_
22
20
  from pygeodesy.named import _name1__
23
21
  from pygeodesy.namedTuples import Destination3Tuple, Distance3Tuple
24
- from pygeodesy.props import Property, Property_RO
25
- from pygeodesy.solveBase import _SolveBase, _SolveLineBase
22
+ from pygeodesy.props import Property, Property_RO, property_RO
23
+ from pygeodesy.solveBase import _SolveGDictBase, _SolveGDictLineBase
26
24
  from pygeodesy.utily import _unrollon, _Wrap, wrap360
27
25
 
28
26
  __all__ = _ALL_LAZY.geodsolve
29
- __version__ = '24.06.04'
27
+ __version__ = '24.07.09'
30
28
 
31
29
 
32
- class GeodSolve12Tuple(_GTuple):
33
- '''12-Tuple C{(lat1, lon1, azi1, lat2, lon2, azi2, s12, a12, m12, M12, M21, S12)} with
34
- angles C{lat1}, C{lon1}, C{azi1}, C{lat2}, C{lon2} and C{azi2} and arc C{a12} all in
35
- 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
37
- scale factors C{M12} and C{M21}, both C{scalar}, see U{GeodSolve
38
- <https://GeographicLib.SourceForge.io/C++/doc/GeodSolve.1.html>}.
39
- '''
40
- # from GeodSolve --help option -f ... lat1 lon1 azi1 lat2 lon2 azi2 s12 a12 m12 M12 M21 S12
41
- _Names_ = (_lat1_, _lon1_, _azi1_, _lat2_, _lon2_, _azi2_, _s12_, _a12_, _m12_, _M12_, _M21_, _S12_)
42
- _Units_ = (_Lat, _Lon, _Azi, _Lat, _Lon, _Azi, _M, _Deg, _Pass, _Pass, _Pass, _M2)
43
-
44
-
45
- class _GeodesicSolveBase(_SolveBase):
30
+ class _GeodesicSolveBase(_SolveGDictBase):
46
31
  '''(INTERNAL) Base class for L{GeodesicSolve} and L{GeodesicLineSolve}.
47
32
  '''
48
33
  _Error = GeodesicError
49
34
  _Names_Direct = \
50
35
  _Names_Inverse = GeodSolve12Tuple._Names_
51
- _Solve_name = 'GeodSolve'
52
- _Solve_path = _getenv(_PYGEODESY_GEODSOLVE_, _PYGEODESY_GEODSOLVE_)
36
+ _Xable_name = 'GeodSolve'
37
+ _Xable_path = _getenv(_PYGEODESY_GEODSOLVE_, _PYGEODESY_GEODSOLVE_)
53
38
 
54
39
  @Property_RO
55
40
  def _b_option(self):
@@ -65,16 +50,12 @@ class _GeodesicSolveBase(_SolveBase):
65
50
  self._p_option +
66
51
  self._u_option)
67
52
 
68
- @Property_RO
69
- def _E_option(self):
70
- return ('-E',) if self.Exact else ()
71
-
72
53
  @Property
73
54
  def GeodSolve(self):
74
55
  '''Get the U{GeodSolve<https://GeographicLib.SourceForge.io/C++/doc/GeodSolve.1.html>}
75
56
  executable (C{filename}).
76
57
  '''
77
- return self._Solve_path
58
+ return self._Xable_path
78
59
 
79
60
  @GeodSolve.setter # PYCHOK setter!
80
61
  def GeodSolve(self, path):
@@ -84,20 +65,20 @@ class _GeodesicSolveBase(_SolveBase):
84
65
  @raise GeodesicError: Invalid B{C{path}}, B{C{path}} doesn't exist or
85
66
  isn't the C{GeodSolve} executable.
86
67
  '''
87
- self._setSolve(path)
68
+ self._setXable(path)
88
69
 
89
70
  def toStr(self, **prec_sep): # PYCHOK signature
90
71
  '''Return this C{GeodesicSolve} as string.
91
72
 
92
- @kwarg prec_sep: Keyword argumens C{B{prec}=6} and C{B{sep}=', '}
93
- for the C{float} C{prec}ision, number of decimal digits
94
- (0..9) and the C{sep}arator string to join. Trailing
95
- zero decimals are stripped for B{C{prec}} values of
96
- 1 and above, but kept for negative B{C{prec}} values.
73
+ @kwarg prec_sep: Keyword argumens C{B{prec}=6} and C{B{sep}=", "}
74
+ for the C{float} C{prec}ision, number of decimal digits
75
+ (0..9) and the C{sep}arator string to join. Trailing
76
+ zero decimals are stripped for B{C{prec}} values of 1
77
+ and above, but kept for negative B{C{prec}} values.
97
78
 
98
79
  @return: GeodesicSolve items (C{str}).
99
80
  '''
100
- return _SolveBase._toStr(self, GeodSolve=self.GeodSolve, **prec_sep)
81
+ return _SolveGDictBase._toStr(self, GeodSolve=self.GeodSolve, **prec_sep)
101
82
 
102
83
  @Property_RO
103
84
  def _u_option(self):
@@ -117,11 +98,11 @@ class GeodesicSolve(_GeodesicSolveBase):
117
98
  '''
118
99
 
119
100
  def Area(self, polyline=False, **name):
120
- '''Set up a L{GeodesicAreaExact} to compute area and
121
- perimeter of a polygon.
101
+ '''Set up a L{GeodesicAreaExact} to compute area and perimeter
102
+ of a polygon.
122
103
 
123
- @kwarg polyline: If C{True} perimeter only, otherwise
124
- area and perimeter (C{bool}).
104
+ @kwarg polyline: If C{True} perimeter only, otherwise area
105
+ and perimeter (C{bool}).
125
106
  @kwarg name: Optional C{B{name}=NN} (C{str}).
126
107
 
127
108
  @return: A L{GeodesicAreaExact} instance.
@@ -137,8 +118,8 @@ class GeodesicSolve(_GeodesicSolveBase):
137
118
  Polygon = Area # for C{geographiclib} compatibility
138
119
 
139
120
  def Direct3(self, lat1, lon1, azi1, s12): # PYCHOK outmask
140
- '''Return the destination lat, lon and reverse azimuth
141
- (final bearing) in C{degrees}.
121
+ '''Return the destination lat, lon and reverse azimuth (final bearing)
122
+ in C{degrees}.
142
123
 
143
124
  @return: L{Destination3Tuple}C{(lat, lon, final)}.
144
125
  '''
@@ -222,11 +203,14 @@ class GeodesicSolve(_GeodesicSolveBase):
222
203
  <https://GeographicLib.SourceForge.io/C++/doc/classGeographicLib_1_1GeodesicExact.html>} and
223
204
  Python U{Geodesic.InverseLine<https://GeographicLib.SourceForge.io/Python/doc/code.html>}.
224
205
  '''
225
- r = self.Inverse(lat1, lon1, lat2, lon2)
226
- return GeodesicLineSolve(self, lat1, lon1, r.azi1, **_name1__(caps_name, _or_nameof=self))
206
+ r = self.Inverse(lat1, lon1, lat2, lon2)
207
+ gl = GeodesicLineSolve(self, lat1, lon1, r.azi1, **_name1__(caps_name, _or_nameof=self))
208
+ gl._a13 = r.a12 # gl.SetArc(r.a12)
209
+ gl._s13 = r.s12 # gl.SetDistance(r.s12)
210
+ return _llz2line(gl, lat2=lat2, lon2=lon2, azi2=r.azi2)
227
211
 
228
212
 
229
- class GeodesicLineSolve(_GeodesicSolveBase, _SolveLineBase):
213
+ class GeodesicLineSolve(_GeodesicSolveBase, _SolveGDictLineBase):
230
214
  '''Wrapper to invoke I{Karney}'s U{GeodSolve<https://GeographicLib.SourceForge.io/C++/doc/GeodSolve.1.html>}
231
215
  as an C{Exact} version of I{Karney}'s Python class U{GeodesicLine<https://GeographicLib.SourceForge.io/C++/doc/
232
216
  python/code.html#geographiclib.geodesicline.GeodesicLine>}.
@@ -237,6 +221,8 @@ class GeodesicLineSolve(_GeodesicSolveBase, _SolveLineBase):
237
221
  @note: This C{geodesic} is intended I{for testing purposes only}, it invokes the C{GeodSolve}
238
222
  executable for I{every} method call.
239
223
  '''
224
+ _a13 = \
225
+ _s13 = NAN # see GeodesicSolve._InverseLine
240
226
 
241
227
  def __init__(self, geodesic, lat1, lon1, azi1, caps=Caps.ALL, **name):
242
228
  '''New L{GeodesicLineSolve} instance, allowing points to be found along
@@ -262,13 +248,28 @@ class GeodesicLineSolve(_GeodesicSolveBase, _SolveLineBase):
262
248
  _xinstanceof(GeodesicSolve, geodesic=geodesic)
263
249
  if (caps & Caps.LINE_OFF): # copy to avoid updates
264
250
  geodesic = geodesic.copy(deep=False, name=_UNDER_(NN, geodesic.name)) # NOT _under!
265
- _SolveLineBase.__init__(self, geodesic, lat1, lon1, caps, azi1=azi1, **name)
251
+ _SolveGDictLineBase.__init__(self, geodesic, lat1, lon1, caps, azi1=azi1, **name)
266
252
  try:
267
253
  self.GeodSolve = geodesic.GeodSolve # geodesic or copy of geodesic
268
254
  except GeodesicError:
269
255
  pass
270
256
 
271
- def ArcPosition(self, a12, outmask=_UNUSED_): # PYCHOK unused
257
+ @Property_RO
258
+ def a13(self):
259
+ '''Get the arc length to reference point 3 (C{degrees}).
260
+
261
+ @see: Methods L{Arc} and L{SetArc}.
262
+ '''
263
+ return self._a13
264
+
265
+ def Arc(self):
266
+ '''Return the arc length to reference point 3 (C{degrees} or C{NAN}).
267
+
268
+ @see: Method L{SetArc} and property L{a13}.
269
+ '''
270
+ return self.a13
271
+
272
+ def ArcPosition(self, a12, outmask=Caps.STANDARD): # PYCHOK unused
272
273
  '''Find the position on the line given B{C{a12}}.
273
274
 
274
275
  @arg a12: Spherical arc length from the first point to the
@@ -277,7 +278,7 @@ class GeodesicLineSolve(_GeodesicSolveBase, _SolveLineBase):
277
278
  @return: A C{GDict} with 12 items C{lat1, lon1, azi1, lat2, lon2,
278
279
  azi2, m12, a12, s12, M12, M21, S12}.
279
280
  '''
280
- return self._GDictInvoke(self._cmdArc, True, self._Names_Direct, a12)
281
+ return self._GDictInvoke(self._cmdArc, self._Names_Direct, a12)._unCaps(outmask)
281
282
 
282
283
  @Property_RO
283
284
  def azi1(self):
@@ -301,6 +302,17 @@ class GeodesicLineSolve(_GeodesicSolveBase, _SolveLineBase):
301
302
  '''
302
303
  return self._cmdDistance + ('-a',)
303
304
 
305
+ def Distance(self):
306
+ '''Return the distance to reference point 3 (C{meter} or C{NAN}).
307
+ '''
308
+ return self.s13
309
+
310
+ @property_RO
311
+ def geodesic(self):
312
+ '''Get the geodesic (L{GeodesicSolve}).
313
+ '''
314
+ return self._solve # see .solveBase._SolveLineBase
315
+
304
316
  def Intersecant2(self, lat0, lon0, radius, **kwds): # PYCHOK no cover
305
317
  '''B{Not implemented}, throws a C{NotImplementedError} always.'''
306
318
  self._notImplemented(lat0, lon0, radius, **kwds)
@@ -309,7 +321,7 @@ class GeodesicLineSolve(_GeodesicSolveBase, _SolveLineBase):
309
321
  '''B{Not implemented}, throws a C{NotImplementedError} always.'''
310
322
  self._notImplemented(lat0, lon0, **kwds)
311
323
 
312
- def Position(self, s12, outmask=_UNUSED_): # PYCHOK unused
324
+ def Position(self, s12, outmask=Caps.STANDARD):
313
325
  '''Find the position on the line given B{C{s12}}.
314
326
 
315
327
  @arg s12: Distance from the first point to the second (C{meter}).
@@ -317,21 +329,58 @@ class GeodesicLineSolve(_GeodesicSolveBase, _SolveLineBase):
317
329
  @return: A C{GDict} with 12 items C{lat1, lon1, azi1, lat2, lon2,
318
330
  azi2, m12, a12, s12, M12, M21, S12}, possibly C{a12=NAN}.
319
331
  '''
320
- return self._GDictInvoke(self._cmdDistance, True, self._Names_Direct, s12)
332
+ return self._GDictInvoke(self._cmdDistance, self._Names_Direct, s12)._unCaps(outmask)
333
+
334
+ @Property_RO
335
+ def s13(self):
336
+ '''Get the distance to reference point 3 (C{meter} or C{NAN}).
337
+
338
+ @see: Methods L{Distance} and L{SetDistance}.
339
+ '''
340
+ return self._s13
341
+
342
+ def SetArc(self, a13):
343
+ '''Set reference point 3 in terms relative to the first point.
344
+
345
+ @arg a13: Spherical arc length from the first to the reference
346
+ point (C{degrees}).
347
+
348
+ @return: The distance C{s13} (C{meter}) between the first and
349
+ the reference point or C{NAN}.
350
+ '''
351
+ if self._a13 != a13:
352
+ self._a13 = a13
353
+ self._s13 = self.ArcPosition(a13, outmask=Caps.DISTANCE).s12 # if a13 else _0_0
354
+ # _update_all(self)
355
+ return self._s13
356
+
357
+ def SetDistance(self, s13):
358
+ '''Set reference point 3 in terms relative to the first point.
359
+
360
+ @arg s13: Distance from the first to the reference point (C{meter}).
361
+
362
+ @return: The arc length C{a13} (C{degrees}) between the first
363
+ and the reference point or C{NAN}.
364
+ '''
365
+ if self._s13 != s13:
366
+ self._s13 = s13
367
+ self._a13 = self.Position(s13, outmask=Caps.DISTANCE).a12 if s13 else _0_0
368
+ # _update_all(self)
369
+ return self._a13 # NAN for GeodesicLineExact without Cap.DISTANCE_IN
321
370
 
322
371
  def toStr(self, **prec_sep): # PYCHOK signature
323
372
  '''Return this C{GeodesicLineSolve} as string.
324
373
 
325
- @kwarg prec_sep: Keyword argumens C{B{prec}=6} and C{B{sep}=', '}
326
- for the C{float} C{prec}ision, number of decimal digits
327
- (0..9) and the C{sep}arator string to join. Trailing
328
- zero decimals are stripped for B{C{prec}} values of
329
- 1 and above, but kept for negative B{C{prec}} values.
374
+ @kwarg prec_sep: Keyword argumens C{B{prec}=6} and C{B{sep}=", "}
375
+ for the C{float} C{prec}ision, number of decimal digits
376
+ (0..9) and the C{sep}arator string to join. Trailing
377
+ zero decimals are stripped for B{C{prec}} values of 1
378
+ and above, but kept for negative B{C{prec}} values.
330
379
 
331
380
  @return: GeodesicLineSolve items (C{str}).
332
381
  '''
333
- return _SolveLineBase._toStr(self, azi1=self.azi1, geodesic=self._solve,
334
- GeodSolve=self.GeodSolve, **prec_sep)
382
+ return _SolveGDictLineBase._toStr(self, azi1=self.azi1, geodesic=self._solve,
383
+ GeodSolve=self.GeodSolve, **prec_sep)
335
384
 
336
385
 
337
386
  __all__ += _ALL_DOCS(_GeodesicSolveBase)
@@ -345,7 +394,7 @@ if __name__ == '__main__':
345
394
  gS.verbose = '--verbose' in argv # or '-v' in argv
346
395
 
347
396
  if gS.GeodSolve in (_PYGEODESY_GEODSOLVE_, None): # not set
348
- gS.GeodSolve = '/opt/local/bin/GeodSolve' # '/opt/local/Cellar/geographiclib/1.51/bin/GeodSolve' # HomeBrew
397
+ gS.GeodSolve = '/opt/local/bin/GeodSolve' # '/opt/local/Cellar/geographiclib/2.3/bin/GeodSolve' # HomeBrew
349
398
  printf('version: %s', gS.version)
350
399
 
351
400
  r = gS.Direct(40.6, -73.8, 51, 5.5e6)