pygeodesy 24.10.24__py2.py3-none-any.whl → 24.12.12__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 (118) hide show
  1. {PyGeodesy-24.10.24.dist-info → PyGeodesy-24.12.12.dist-info}/METADATA +6 -6
  2. PyGeodesy-24.12.12.dist-info/RECORD +118 -0
  3. {PyGeodesy-24.10.24.dist-info → PyGeodesy-24.12.12.dist-info}/WHEEL +1 -1
  4. pygeodesy/__init__.py +5 -5
  5. pygeodesy/__main__.py +1 -1
  6. pygeodesy/albers.py +5 -5
  7. pygeodesy/auxilats/_CX_4.py +1 -1
  8. pygeodesy/auxilats/_CX_6.py +1 -1
  9. pygeodesy/auxilats/_CX_8.py +1 -1
  10. pygeodesy/auxilats/_CX_Rs.py +1 -1
  11. pygeodesy/auxilats/__init__.py +1 -1
  12. pygeodesy/auxilats/__main__.py +1 -1
  13. pygeodesy/auxilats/auxAngle.py +5 -5
  14. pygeodesy/auxilats/auxDLat.py +6 -6
  15. pygeodesy/auxilats/auxDST.py +2 -2
  16. pygeodesy/auxilats/auxLat.py +5 -5
  17. pygeodesy/auxilats/auxily.py +2 -2
  18. pygeodesy/azimuthal.py +55 -65
  19. pygeodesy/basics.py +35 -34
  20. pygeodesy/booleans.py +37 -37
  21. pygeodesy/cartesianBase.py +26 -65
  22. pygeodesy/clipy.py +1 -1
  23. pygeodesy/constants.py +7 -7
  24. pygeodesy/css.py +8 -9
  25. pygeodesy/datums.py +1 -1
  26. pygeodesy/deprecated/__init__.py +2 -2
  27. pygeodesy/deprecated/bases.py +1 -1
  28. pygeodesy/deprecated/classes.py +10 -10
  29. pygeodesy/deprecated/consterns.py +1 -1
  30. pygeodesy/deprecated/datum.py +1 -1
  31. pygeodesy/deprecated/functions.py +23 -13
  32. pygeodesy/deprecated/nvector.py +1 -1
  33. pygeodesy/deprecated/rhumbBase.py +1 -1
  34. pygeodesy/deprecated/rhumbaux.py +1 -1
  35. pygeodesy/deprecated/rhumbsolve.py +1 -1
  36. pygeodesy/deprecated/rhumbx.py +1 -1
  37. pygeodesy/dms.py +1 -1
  38. pygeodesy/ecef.py +63 -69
  39. pygeodesy/elevations.py +1 -1
  40. pygeodesy/ellipsoidalBase.py +106 -121
  41. pygeodesy/ellipsoidalBaseDI.py +115 -119
  42. pygeodesy/ellipsoidalExact.py +36 -38
  43. pygeodesy/ellipsoidalGeodSolve.py +1 -1
  44. pygeodesy/ellipsoidalKarney.py +1 -1
  45. pygeodesy/ellipsoidalNvector.py +1 -1
  46. pygeodesy/ellipsoidalVincenty.py +6 -5
  47. pygeodesy/ellipsoids.py +7 -8
  48. pygeodesy/elliptic.py +6 -6
  49. pygeodesy/epsg.py +1 -1
  50. pygeodesy/errors.py +25 -25
  51. pygeodesy/etm.py +84 -76
  52. pygeodesy/fmath.py +54 -51
  53. pygeodesy/formy.py +74 -106
  54. pygeodesy/frechet.py +1 -1
  55. pygeodesy/fstats.py +1 -1
  56. pygeodesy/fsums.py +82 -72
  57. pygeodesy/gars.py +1 -1
  58. pygeodesy/geodesici.py +4 -4
  59. pygeodesy/geodesicw.py +16 -15
  60. pygeodesy/geodesicx/_C4_24.py +2 -2
  61. pygeodesy/geodesicx/_C4_27.py +2 -2
  62. pygeodesy/geodesicx/_C4_30.py +2 -2
  63. pygeodesy/geodesicx/__init__.py +3 -3
  64. pygeodesy/geodesicx/__main__.py +1 -1
  65. pygeodesy/geodesicx/gx.py +6 -5
  66. pygeodesy/geodesicx/gxarea.py +2 -2
  67. pygeodesy/geodesicx/gxbases.py +2 -2
  68. pygeodesy/geodesicx/gxline.py +16 -12
  69. pygeodesy/geodsolve.py +8 -17
  70. pygeodesy/geohash.py +1 -1
  71. pygeodesy/geoids.py +6 -6
  72. pygeodesy/hausdorff.py +1 -1
  73. pygeodesy/heights.py +3 -3
  74. pygeodesy/internals.py +64 -80
  75. pygeodesy/interns.py +2 -3
  76. pygeodesy/iters.py +1 -1
  77. pygeodesy/karney.py +4 -4
  78. pygeodesy/ktm.py +20 -21
  79. pygeodesy/latlonBase.py +296 -346
  80. pygeodesy/lazily.py +15 -15
  81. pygeodesy/lcc.py +5 -5
  82. pygeodesy/ltp.py +55 -59
  83. pygeodesy/ltpTuples.py +208 -192
  84. pygeodesy/mgrs.py +9 -10
  85. pygeodesy/named.py +153 -3
  86. pygeodesy/namedTuples.py +58 -7
  87. pygeodesy/nvectorBase.py +122 -105
  88. pygeodesy/osgr.py +10 -13
  89. pygeodesy/points.py +1 -1
  90. pygeodesy/props.py +3 -3
  91. pygeodesy/resections.py +26 -26
  92. pygeodesy/rhumb/__init__.py +2 -2
  93. pygeodesy/rhumb/aux_.py +2 -2
  94. pygeodesy/rhumb/bases.py +2 -2
  95. pygeodesy/rhumb/ekx.py +4 -4
  96. pygeodesy/rhumb/solve.py +4 -4
  97. pygeodesy/simplify.py +291 -403
  98. pygeodesy/solveBase.py +1 -1
  99. pygeodesy/sphericalBase.py +1 -1
  100. pygeodesy/sphericalNvector.py +84 -127
  101. pygeodesy/sphericalTrigonometry.py +66 -71
  102. pygeodesy/streprs.py +10 -5
  103. pygeodesy/trf.py +1 -1
  104. pygeodesy/triaxials.py +23 -16
  105. pygeodesy/units.py +17 -17
  106. pygeodesy/unitsBase.py +1 -1
  107. pygeodesy/ups.py +4 -4
  108. pygeodesy/utily.py +202 -145
  109. pygeodesy/utm.py +10 -10
  110. pygeodesy/utmups.py +1 -1
  111. pygeodesy/utmupsBase.py +1 -1
  112. pygeodesy/vector2d.py +17 -17
  113. pygeodesy/vector3d.py +32 -23
  114. pygeodesy/vector3dBase.py +22 -19
  115. pygeodesy/webmercator.py +5 -5
  116. pygeodesy/wgrs.py +5 -5
  117. PyGeodesy-24.10.24.dist-info/RECORD +0 -118
  118. {PyGeodesy-24.10.24.dist-info → PyGeodesy-24.12.12.dist-info}/top_level.txt +0 -0
pygeodesy/etm.py CHANGED
@@ -11,13 +11,13 @@ Class L{ExactTransverseMercator} provides C{Exact Transverse Mercator} projectio
11
11
  instances of class L{Etm} represent ETM C{(easting, northing)} locations. See also
12
12
  I{Karney}'s utility U{TransverseMercatorProj<https://GeographicLib.SourceForge.io/C++/doc/
13
13
  TransverseMercatorProj.1.html>} and use C{"python[3] -m pygeodesy.etm ..."} to compare
14
- the results.
14
+ the results, see usage C{"python[3] -m pygeodesy.etm -h"}.
15
15
 
16
16
  Following is a copy of I{Karney}'s U{TransverseMercatorExact.hpp
17
17
  <https://GeographicLib.SourceForge.io/C++/doc/TransverseMercatorExact_8hpp_source.html>}
18
18
  file C{Header}.
19
19
 
20
- Copyright (C) U{Charles Karney<mailto:Karney@Alum.MIT.edu>} (2008-2023) and licensed
20
+ Copyright (C) U{Charles Karney<mailto:Karney@Alum.MIT.edu>} (2008-2024) and licensed
21
21
  under the MIT/X11 License. For more information, see the U{GeographicLib<https://
22
22
  GeographicLib.SourceForge.io>} documentation.
23
23
 
@@ -65,38 +65,40 @@ from __future__ import division as _; del _ # PYCHOK semicolon
65
65
 
66
66
  from pygeodesy.basics import map1, neg, neg_, _xinstanceof
67
67
  from pygeodesy.constants import EPS, EPS02, PI_2, PI_4, _K0_UTM, \
68
- _1_EPS, _0_0, _0_1, _0_5, _1_0, _2_0, \
69
- _3_0, _4_0, _90_0, isnear0, isnear90
68
+ _1_EPS, _0_0, _0_1, _0_5, _1_0, _2_0, \
69
+ _3_0, _90_0, isnear0, isnear90
70
+ from pygeodesy.constants import _4_0 # PYCHOK used!
70
71
  from pygeodesy.datums import _ellipsoidal_datum, _WGS84, _EWGS84
71
72
  # from pygeodesy.ellipsoids import _EWGS84 # from .datums
72
- from pygeodesy.elliptic import _ALL_LAZY, Elliptic
73
+ # from pygeodesy.elliptic import Elliptic # _MODS
73
74
  # from pygeodesy.errors import _incompatible # from .named
74
75
  # from pygeodesy.fsums import Fsum # from .fmath
75
76
  from pygeodesy.fmath import cbrt, hypot, hypot1, hypot2, Fsum
76
77
  from pygeodesy.interns import _COMMASPACE_, _near_, _SPACE_, _spherical_
77
78
  from pygeodesy.karney import _K_2_4, _copyBit, _diff182, _fix90, \
78
79
  _norm2, _norm180, _tand, _unsigned2
79
- # from pygeodesy.lazily import _ALL_LAZY # from .elliptic
80
- from pygeodesy.named import callername, _incompatible, _NamedBase
80
+ # from pygeodesy.lazily import _ALL_LAZY, _ALL_MODS as _MODS # from .named
81
+ from pygeodesy.named import callername, _incompatible, _NamedBase, \
82
+ _ALL_LAZY, _MODS
81
83
  from pygeodesy.namedTuples import Forward4Tuple, Reverse4Tuple
82
84
  from pygeodesy.props import deprecated_method, deprecated_property_RO, \
83
85
  Property_RO, property_RO, _update_all, \
84
- property_doc_
86
+ property_doc_, _allPropertiesOf_n
85
87
  from pygeodesy.streprs import Fmt, pairs, unstr
86
88
  from pygeodesy.units import Degrees, Scalar_
87
- from pygeodesy.utily import atan1d, atan2d, _loneg, sincos2
89
+ from pygeodesy.utily import atan1d, atan2, atan2d, _loneg, sincos2
88
90
  from pygeodesy.utm import _cmlon, _LLEB, _parseUTM5, _toBand, _toXtm8, \
89
91
  _to7zBlldfn, Utm, UTMError
90
92
 
91
- from math import asinh, atan2, degrees, radians, sinh, sqrt
93
+ from math import asinh, degrees, radians, sinh, sqrt
92
94
 
93
95
  __all__ = _ALL_LAZY.etm
94
- __version__ = '24.10.21'
96
+ __version__ = '24.11.24'
95
97
 
96
- _OVERFLOW = _1_EPS**2 # about 2e+31
97
- _TAYTOL = pow(EPS, 0.6)
98
+ _OVERFLOW = _1_EPS**2 # ~2e+31
99
+ _TAYTOL = pow(EPS, 0.6)
98
100
  _TAYTOL2 = _TAYTOL * _2_0
99
- _TOL_10 = EPS * _0_1
101
+ _TOL_10 = EPS * _0_1
100
102
  _TRIPS = 21 # C++ 10
101
103
 
102
104
 
@@ -218,12 +220,12 @@ class Etm(Utm):
218
220
  class ExactTransverseMercator(_NamedBase):
219
221
  '''Pure Python version of Karney's C++ class U{TransverseMercatorExact
220
222
  <https://GeographicLib.SourceForge.io/C++/doc/TransverseMercatorExact_8cpp_source.html>},
221
- a numerically exact transverse Mercator projection, further referred to as C{TMExact}.
223
+ a numerically exact transverse Mercator projection, abbreviated as C{TMExact}.
222
224
  '''
223
225
  _datum = _WGS84 # Datum
224
226
  _E = _EWGS84 # Ellipsoid
225
227
  _extendp = False # use extended domain
226
- # _iteration = None # ._sigmaInv2 and ._zetaInv2
228
+ # _iteration = None # _NameBase, ._sigmaInv2 and ._zetaInv2
227
229
  _k0 = _K0_UTM # central scale factor
228
230
  _lat0 = _0_0 # central parallel
229
231
  _lon0 = _0_0 # central meridian
@@ -240,7 +242,7 @@ class ExactTransverseMercator(_NamedBase):
240
242
  L{Ellipsoid}, L{Ellipsoid2} or L{a_f2Tuple}).
241
243
  @kwarg lon0: Central meridian, default (C{degrees180}).
242
244
  @kwarg k0: Central scale factor (C{float}).
243
- @kwarg extendp: Use the I{extended} domain (C{bool}), I{standard} otherwise.
245
+ @kwarg extendp: If C{True}, use the I{extended} domain, I{standard} otherwise (C{bool}).
244
246
  @kwarg raiser: If C{True}, throw an L{ETMError} for convergence failures (C{bool}).
245
247
  @kwarg name: Optional C{B{name}=NN} for the projection (C{str}).
246
248
 
@@ -349,7 +351,7 @@ class ExactTransverseMercator(_NamedBase):
349
351
  def _Eu(self):
350
352
  '''(INTERNAL) Get and cache C{Elliptic(_mu)}.
351
353
  '''
352
- return Elliptic(self._mu)
354
+ return _MODS.elliptic.Elliptic(self._mu)
353
355
 
354
356
  @Property_RO
355
357
  def _Eu_cE(self):
@@ -390,7 +392,7 @@ class ExactTransverseMercator(_NamedBase):
390
392
  def _Ev(self):
391
393
  '''(INTERNAL) Get and cache C{Elliptic(_mv)}.
392
394
  '''
393
- return Elliptic(self._mv)
395
+ return _MODS.elliptic.Elliptic(self._mv)
394
396
 
395
397
  @Property_RO
396
398
  def _Ev_cK(self):
@@ -416,7 +418,7 @@ class ExactTransverseMercator(_NamedBase):
416
418
  '''
417
419
  return self._Ev_cKE * 1.25 # _1_25
418
420
 
419
- @Property_RO
421
+ @property_RO
420
422
  def extendp(self):
421
423
  '''Get the domain (C{bool}), I{extended} or I{standard}.
422
424
  '''
@@ -635,7 +637,7 @@ class ExactTransverseMercator(_NamedBase):
635
637
  '''Set the central parallel and meridian.
636
638
 
637
639
  @arg lat0: Latitude of the central parallel (C{degrees90}).
638
- @arg lon0: Longitude of the central parallel (C{degrees180}).
640
+ @arg lon0: Longitude of the central meridian (C{degrees180}).
639
641
 
640
642
  @return: 2-Tuple C{(lat0, lon0)} of the previous central
641
643
  parallel and meridian.
@@ -644,7 +646,7 @@ class ExactTransverseMercator(_NamedBase):
644
646
  '''
645
647
  t = self._lat0, self.lon0
646
648
  self._lat0 = _fix90(Degrees(lat0=lat0, Error=ETMError))
647
- self. lon0 = lon0
649
+ self. lon0 = lon0 # lon0.setter
648
650
  return t
649
651
 
650
652
  def _resets(self, datum):
@@ -655,15 +657,15 @@ class ExactTransverseMercator(_NamedBase):
655
657
  @raise ETMError: Near-spherical B{C{datum}} or C{ellipsoid}.
656
658
  '''
657
659
  E = datum.ellipsoid
658
- mu = E.e2 # .eccentricity1st2
660
+ mu = E.e2 # E.eccentricity1st2
659
661
  mv = E.e21 # _1_0 - mu
660
662
  if isnear0(E.e) or isnear0(mu, eps0=EPS02) \
661
663
  or isnear0(mv, eps0=EPS02): # or sqrt(mu) != E.e
662
664
  raise ETMError(ellipsoid=E, txt=_near_(_spherical_))
663
665
 
664
666
  if self._datum or self._E:
665
- _i = ExactTransverseMercator.iteration._uname
666
- _update_all(self, _i, '_sigmaC', '_zetaC') # _under
667
+ _i = ExactTransverseMercator.iteration._uname # property_RO
668
+ _update_all(self, _i, '_sigmaC', '_zetaC', Base=Property_RO) # _under
667
669
 
668
670
  self._E = E
669
671
  self._mu = mu
@@ -757,7 +759,7 @@ class ExactTransverseMercator(_NamedBase):
757
759
  # k = sqrt(mv + mu / sec2) * sqrt(sec2) * sqrt(q2)
758
760
  # = sqrt(mv * sec2 + mu) * sqrt(q2)
759
761
  # = sqrt(mv + mv * tau**2 + mu) * sqrt(q2)
760
- k, q2 = _0_0, (mv * snv**2 + cnudnv**2)
762
+ k, q2 = _0_0, (snv**2 * mv + cnudnv**2)
761
763
  if q2 > 0:
762
764
  k2 = (tau**2 + _1_0) * mv + mu
763
765
  if k2 > 0:
@@ -809,9 +811,9 @@ class ExactTransverseMercator(_NamedBase):
809
811
  d = (cnv**2 + snuv**2 * mu)**2 * self._mv
810
812
  r = cnv * dnu * dnv
811
813
  i = cnu * snuv * mu
812
- du = (r**2 - i**2) / d # (r + i) * (r - i) / d
813
- dv = neg(r * i * _2_0 / d)
814
- return du, dv
814
+ du = (r + i) * (r - i) / d # (r**2 - i**2) / d
815
+ dv = r * i * _2_0 / d
816
+ return du, neg(dv)
815
817
 
816
818
  def _sigmaInv2(self, xi, eta):
817
819
  '''(INTERNAL) Invert C{sigma} using Newton's method.
@@ -874,7 +876,8 @@ class ExactTransverseMercator(_NamedBase):
874
876
  '''
875
877
  # snu, cnu, dnu = self._Eu.sncndn(u)
876
878
  # snv, cnv, dnv = self._Ev.sncndn(v)
877
- return self._Eu.sncndn(u, **jam) + self._Ev.sncndn(v, **jam)
879
+ return self._Eu.sncndn(u, **jam) + \
880
+ self._Ev.sncndn(v, **jam)
878
881
 
879
882
  def toStr(self, joined=_COMMASPACE_, **kwds): # PYCHOK signature
880
883
  '''Return a C{str} representation.
@@ -900,21 +903,19 @@ class ExactTransverseMercator(_NamedBase):
900
903
  real &taup, real &lam)}
901
904
  '''
902
905
  e, cnu2, mv = self._e, cnu**2, self._mv
903
- # Overflow value like atan(overflow) = pi/2
904
- t1 = t2 = _overflow(snu)
905
906
  # Lee 54.17 but write
906
907
  # atanh(snu * dnv) = asinh(snu * dnv / sqrt(cnu^2 + _mv * snu^2 * snv^2))
907
908
  # atanh(_e * snu / dnv) = asinh(_e * snu / sqrt(_mu * cnu^2 + _mv * cnv^2))
908
- d1 = cnu2 + mv * (snu * snv)**2
909
+ d1 = cnu2 + (snu * snv)**2 * mv
909
910
  if d1 > EPS02: # _EPSmin
910
911
  t1 = snu * dnv / sqrt(d1)
911
- else:
912
- d1 = 0
913
- d2 = self._mu * cnu2 + mv * cnv**2
912
+ else: # like atan(overflow) = pi/2
913
+ t1, d1 = _overflow(snu), 0
914
+ d2 = cnu2 * self._mu + cnv**2 * mv
914
915
  if d2 > EPS02: # _EPSmin
915
916
  t2 = sinh(e * asinh(e * snu / sqrt(d2)))
916
917
  else:
917
- d2 = 0
918
+ t2, d2 = _overflow(snu), 0
918
919
  # psi = asinh(t1) - asinh(t2)
919
920
  # taup = sinh(psi)
920
921
  taup = t1 * hypot1(t2) - t2 * hypot1(t1)
@@ -939,9 +940,9 @@ class ExactTransverseMercator(_NamedBase):
939
940
  snuv2 = snuv**2 * self._mu
940
941
  # Lee 54.21 but write (see A+S 16.21.4)
941
942
  # (1 - dnu^2 * snv^2) = (cnv^2 + _mu * snu^2 * snv^2)
942
- d = self._mv * (cnv2 + snuv2)**2 # max(d, EPS02)?
943
- du = cnu * dnuv * (cnv2 - snuv2) / d
944
- dv = cnv * snuv * (cnu2 + dnuv2) / d
943
+ d = (cnv2 + snuv2)**2 * self._mv # max(d, EPS02)?
944
+ du = (cnv2 - snuv2) * cnu * dnuv / d
945
+ dv = (cnu2 + dnuv2) * cnv * snuv / d
945
946
  return du, neg(dv)
946
947
 
947
948
  def _zetaInv2(self, taup, lam):
@@ -1021,6 +1022,9 @@ class ExactTransverseMercator(_NamedBase):
1021
1022
  g_k += atan1d(tau), degrees(lam)
1022
1023
  return g_k # or (g, k, lat, lon)
1023
1024
 
1025
+ _allPropertiesOf_n(22, ExactTransverseMercator, Property_RO) # PYCHOK assert
1026
+ del _0_1, _allPropertiesOf_n, EPS, _1_EPS, _EWGS84
1027
+
1024
1028
 
1025
1029
  def _overflow(x):
1026
1030
  '''(INTERNAL) Like C{copysign0(OVERFLOW, B{x})}.
@@ -1102,32 +1106,42 @@ if __name__ == '__main__': # MCCABE 16
1102
1106
  def _main():
1103
1107
 
1104
1108
  from pygeodesy import fstr, KTransverseMercator
1105
- # from pygeodesy.interns import _DASH_ # from internals
1106
- from pygeodesy.internals import printf, _usage, _DASH_
1109
+ from pygeodesy.interns import _BAR_, _COLONSPACE_, _DASH_, NN
1110
+ from pygeodesy.internals import printf, _usage
1107
1111
  from sys import argv, exit as _exit
1108
1112
 
1109
- def _help():
1110
- _exit(_usage(argv[0], '[-s | -t ]',
1111
- '[-p[recision] <ndigits>',
1112
- '[-f[orward] <lat> <lon>',
1113
- '|-r[everse] <easting> <northing>',
1114
- '|<lat> <lon>]',
1115
- '|-h[elp]'))
1113
+ def _error(why, _a=NN):
1114
+ if _a:
1115
+ why = 'option %r %s' % (_a, why)
1116
+ _exit(_COLONSPACE_(_usage(*argv), why))
1117
+
1118
+ def _help(*why):
1119
+ if why:
1120
+ printf(_COLONSPACE_(_usage(*argv), *why))
1121
+ _exit(_usage(argv[0], '[-s[eries]', _BAR_, '-t]',
1122
+ '[-p[recision] <ndigits>]',
1123
+ '[-f[orward] <lat> <lon>', _BAR_,
1124
+ '-r[everse] <easting> <northing>', _BAR_,
1125
+ '<lat> <lon>]', _BAR_,
1126
+ '-h[elp]'))
1127
+
1128
+ def _result(t4):
1129
+ printf(_COLONSPACE_(tm.classname, fstr(t4, prec=_p, sep=_SPACE_)))
1116
1130
 
1117
1131
  # mimick some of I{Karney}'s utility C{TransverseMercatorProj}
1118
1132
  _f = _r = _s = _t = False
1119
1133
  _p = -6
1120
- _as = argv[1:]
1121
- while _as and _as[0].startswith(_DASH_):
1122
- _a = _as.pop(0)
1134
+ args = argv[1:]
1135
+ while args and args[0].startswith(_DASH_):
1136
+ _a = args.pop(0)
1123
1137
  if len(_a) < 2:
1124
- _exit('%s: option %r invalid' % (_usage(*argv), _a))
1138
+ _error('invalid', _a)
1125
1139
  elif '-forward'.startswith(_a):
1126
1140
  _f, _r = True, False
1127
1141
  elif '-reverse'.startswith(_a):
1128
1142
  _f, _r = False, True
1129
- elif '-precision'.startswith(_a):
1130
- _p = int(_as.pop(0))
1143
+ elif '-precision'.startswith(_a) and args:
1144
+ _p = int(args.pop(0))
1131
1145
  elif '-series'.startswith(_a):
1132
1146
  _s, _t = True, False
1133
1147
  elif _a == '-t':
@@ -1135,52 +1149,46 @@ if __name__ == '__main__': # MCCABE 16
1135
1149
  elif '-help'.startswith(_a):
1136
1150
  _help()
1137
1151
  else:
1138
- _exit('%s: option %r not supported' % (_usage(*argv), _a))
1139
-
1140
- if len(_as) > 1:
1141
- f2 = map1(float, *_as[:2])
1142
- else:
1143
- printf('%s ...: incomplete', _usage(*argv))
1144
- _help()
1145
-
1146
- if _s: # -series
1147
- tm = KTransverseMercator()
1148
- else:
1149
- tm = ExactTransverseMercator(extendp=_t)
1152
+ _error('not supported', _a)
1153
+ if len(args) < 2:
1154
+ _help('incomplete')
1150
1155
 
1156
+ f2 = map1(float, *args[:2])
1157
+ tm = KTransverseMercator() if _s else \
1158
+ ExactTransverseMercator(extendp=_t)
1151
1159
  if _f:
1152
1160
  t = tm.forward(*f2)
1153
1161
  elif _r:
1154
1162
  t = tm.reverse(*f2)
1155
1163
  else:
1156
1164
  t = tm.forward(*f2)
1157
- printf('%s: %s', tm.classname, fstr(t, prec=_p, sep=_SPACE_))
1165
+ _result(t)
1158
1166
  t = tm.reverse(t.easting, t.northing)
1159
- printf('%s: %s', tm.classname, fstr(t, prec=_p, sep=_SPACE_))
1167
+ _result(t)
1160
1168
 
1161
1169
  _main()
1162
1170
 
1163
- # % python3.13 -m pygeodesy.etm -p 12 33.33 44.44
1171
+ # % python3.13 -m pygeodesy.etm -p 12 33.33 44.44
1164
1172
  # ExactTransverseMercator: 4276926.114803905599 4727193.767015309073 28.375536563148 1.233325101778
1165
1173
  # ExactTransverseMercator: 33.33 44.44 28.375536563148 1.233325101778
1166
1174
 
1167
- # % python3.13 -m pygeodesy.etm -s -p 12 33.33 44.44
1175
+ # % python3.13 -m pygeodesy.etm -s -p 12 33.33 44.44
1168
1176
  # KTransverseMercator: 4276926.114803904667 4727193.767015310004 28.375536563148 1.233325101778
1169
1177
  # KTransverseMercator: 33.33 44.44 28.375536563148 1.233325101778
1170
1178
 
1171
- # % python3.12 -m pygeodesy.etm -p 12 33.33 44.44
1179
+ # % python3.12 -m pygeodesy.etm -p 12 33.33 44.44
1172
1180
  # ExactTransverseMercator: 4276926.11480390653 4727193.767015309073 28.375536563148 1.233325101778
1173
1181
  # ExactTransverseMercator: 33.33 44.44 28.375536563148 1.233325101778
1174
1182
 
1175
- # % python3.12 -m pygeodesy.etm -s -p 12 33.33 44.44
1183
+ # % python3.12 -m pygeodesy.etm -s -p 12 33.33 44.44
1176
1184
  # KTransverseMercator: 4276926.114803904667 4727193.767015310004 28.375536563148 1.233325101778
1177
1185
  # KTransverseMercator: 33.33 44.44 28.375536563148 1.233325101778
1178
1186
 
1179
- # % python2 -m pygeodesy.etm -p 12 33.33 44.44
1187
+ # % python2 -m pygeodesy.etm -p 12 33.33 44.44
1180
1188
  # ExactTransverseMercator: 4276926.11480390653 4727193.767015309073 28.375536563148 1.233325101778
1181
1189
  # ExactTransverseMercator: 33.33 44.44 28.375536563148 1.233325101778
1182
1190
 
1183
- # % python2 -m pygeodesy.etm -s -p 12 33.33 44.44
1191
+ # % python2 -m pygeodesy.etm -s -p 12 33.33 44.44
1184
1192
  # KTransverseMercator: 4276926.114803904667 4727193.767015310004 28.375536563148 1.233325101778
1185
1193
  # KTransverseMercator: 33.33 44.44 28.375536563148 1.233325101778
1186
1194
 
@@ -1189,7 +1197,7 @@ if __name__ == '__main__': # MCCABE 16
1189
1197
 
1190
1198
  # **) MIT License
1191
1199
  #
1192
- # Copyright (C) 2016-2024 -- mrJean1 at Gmail -- All Rights Reserved.
1200
+ # Copyright (C) 2016-2025 -- mrJean1 at Gmail -- All Rights Reserved.
1193
1201
  #
1194
1202
  # Permission is hereby granted, free of charge, to any person obtaining a
1195
1203
  # copy of this software and associated documentation files (the "Software"),
pygeodesy/fmath.py CHANGED
@@ -12,9 +12,8 @@ from pygeodesy.constants import EPS0, EPS02, EPS1, NAN, PI, PI_2, PI_4, \
12
12
  _0_0, _0_125, _1_6th, _0_25, _1_3rd, _0_5, _1_0, \
13
13
  _1_5, _copysign_0_0, isfinite, remainder
14
14
  from pygeodesy.errors import _IsnotError, LenError, _TypeError, _ValueError, \
15
- _xError, _xkwds_pop2, _xsError
16
- from pygeodesy.fsums import _2float, Fsum, fsum, _isFsum_2Tuple, _1primed, \
17
- Fmt, unstr
15
+ _xError, _xkwds, _xkwds_pop2, _xsError
16
+ from pygeodesy.fsums import _2float, Fsum, fsum, _isFsum_2Tuple, Fmt, unstr
18
17
  from pygeodesy.interns import MISSING, _negative_, _not_scalar_
19
18
  from pygeodesy.lazily import _ALL_LAZY, _ALL_MODS as _MODS
20
19
  # from pygeodesy.streprs import Fmt, unstr # from .fsums
@@ -24,7 +23,7 @@ from math import fabs, sqrt # pow
24
23
  import operator as _operator # in .datums, .trf, .utm
25
24
 
26
25
  __all__ = _ALL_LAZY.fmath
27
- __version__ = '24.10.11'
26
+ __version__ = '24.12.02'
28
27
 
29
28
  # sqrt(2) - 1 <https://WikiPedia.org/wiki/Square_root_of_2>
30
29
  _0_4142 = 0.41421356237309504880 # ... ~ 3730904090310553 / 9007199254740992
@@ -62,14 +61,7 @@ class Fdot(Fsum):
62
61
  n = len(b)
63
62
  if len(a) != n: # PYCHOK no cover
64
63
  raise LenError(Fdot, a=len(a), b=n)
65
- self._faddot(n, a, b, **kwds)
66
-
67
- def _faddot(self, n, xs, ys, **kwds):
68
- if n > 0:
69
- _f = Fsum(**kwds)
70
- r = (_f(x).fmul(y) for x, y in zip(xs, ys)) # PYCHOK attr?
71
- self.fadd(_1primed(r) if n < 4 else r) # PYCHOK attr?
72
- return self
64
+ self._facc_dot(n, a, b, **kwds)
73
65
 
74
66
 
75
67
  class Fhorner(Fsum):
@@ -115,21 +107,23 @@ class Fhypot(Fsum):
115
107
  other settings, see class L{Fsum<Fsum.__init__>} and method
116
108
  L{root<Fsum.root>}.
117
109
  '''
110
+ def _r_X_kwds(power=None, raiser=True, root=2, **kwds):
111
+ # DEPRECATED keyword argument C{power=2}, use C{root=2}
112
+ return (root if power is None else power), raiser, kwds
113
+
118
114
  r = None # _xkwds_pop2 error
119
115
  try:
120
- r, kwds = _xkwds_pop2(root_name_f2product_nonfinites_RESIDUAL_raiser, root=2)
121
- r, kwds = _xkwds_pop2(kwds, power=r) # for backward compatibility
122
- t, kwds = _xkwds_pop2(kwds, raiser=True)
116
+ r, X, kwds = _r_X_kwds(**root_name_f2product_nonfinites_RESIDUAL_raiser)
123
117
  Fsum.__init__(self, **kwds)
124
118
  self(_0_0)
125
119
  if xs:
126
- self._facc_power(r, xs, Fhypot, raiser=t)
127
- self._fset(self.root(r, raiser=t))
120
+ self._facc_power(r, xs, Fhypot, raiser=X)
121
+ self._fset(self.root(r, raiser=X))
128
122
  except Exception as X:
129
123
  raise self._ErrorXs(X, xs, root=r)
130
124
 
131
125
 
132
- class Fpolynomial(Fdot):
126
+ class Fpolynomial(Fsum):
133
127
  '''Precision polynomial evaluation.
134
128
  '''
135
129
  def __init__(self, x, *cs, **name_f2product_nonfinites_RESIDUAL):
@@ -150,12 +144,10 @@ class Fpolynomial(Fdot):
150
144
 
151
145
  @see: Class L{Fhorner}, function L{fpolynomial} and method L{Fsum.fadd}.
152
146
  '''
153
- Fsum.__init__(self, *cs[:1], **name_f2product_nonfinites_RESIDUAL)
147
+ Fsum.__init__(self, **name_f2product_nonfinites_RESIDUAL)
154
148
  n = len(cs) - 1
155
- if n > 0:
156
- self._faddot(n, cs[1:], _powers(x, n), **name_f2product_nonfinites_RESIDUAL)
157
- elif n < 0:
158
- self(_0_0)
149
+ self(_0_0 if n < 0 else cs[0])
150
+ self._facc_dot(n, cs[1:], _powers(x, n), **name_f2product_nonfinites_RESIDUAL)
159
151
 
160
152
 
161
153
  class Fpowers(Fsum):
@@ -173,11 +165,11 @@ class Fpowers(Fsum):
173
165
  L{fpow<Fsum.fpow>}.
174
166
  '''
175
167
  try:
176
- t, kwds = _xkwds_pop2(name_f2product_nonfinites_RESIDUAL_raiser, raiser=True)
168
+ X, kwds = _xkwds_pop2(name_f2product_nonfinites_RESIDUAL_raiser, raiser=True)
177
169
  Fsum.__init__(self, **kwds)
178
170
  self(_0_0)
179
171
  if xs:
180
- self._facc_power(power, xs, Fpowers, raiser=t) # x**0 == 1
172
+ self._facc_power(power, xs, Fpowers, raiser=X) # x**0 == 1
181
173
  except Exception as X:
182
174
  raise self._ErrorXs(X, xs, power=power)
183
175
 
@@ -197,12 +189,12 @@ class Froot(Fsum):
197
189
  L{fpow<Fsum.fpow>}.
198
190
  '''
199
191
  try:
200
- raiser, kwds = _xkwds_pop2(name_f2product_nonfinites_RESIDUAL_raiser, raiser=True)
192
+ X, kwds = _xkwds_pop2(name_f2product_nonfinites_RESIDUAL_raiser, raiser=True)
201
193
  Fsum.__init__(self, **kwds)
202
194
  self(_0_0)
203
195
  if xs:
204
196
  self.fadd(xs)
205
- self(self.root(root, raiser=raiser))
197
+ self(self.root(root, raiser=X))
206
198
  except Exception as X:
207
199
  raise self._ErrorXs(X, xs, root=root)
208
200
 
@@ -431,35 +423,47 @@ def favg(a, b, f=_0_5, nonfinites=True):
431
423
  return float(F)
432
424
 
433
425
 
434
- def fdot(a, *b, **start):
435
- '''Return the precision dot product M{sum(a[i] * b[i] for ni=0..len(a))}.
426
+ def fdot(xs, *ys, **start_f2product_nonfinites):
427
+ '''Return the precision dot product M{sum(xs[i] * ys[i] for i in range(len(xs)))}.
436
428
 
437
- @arg a: Iterable of values (each C{scalar}, an L{Fsum} or L{Fsum2Tuple}).
438
- @arg b: Other values (each C{scalar}, an L{Fsum} or L{Fsum2Tuple}), all
439
- positional.
440
- @kwarg start: Optional bias C{B{start}=0} (C{scalar}, an L{Fsum} or
441
- L{Fsum2Tuple}).
429
+ @arg xs: Iterable of values (each C{scalar}, an L{Fsum} or L{Fsum2Tuple}).
430
+ @arg ys: Other values (each C{scalar}, an L{Fsum} or L{Fsum2Tuple}), all positional.
431
+ @kwarg start_f2product_nonfinites: Optional bias C{B{start}=0} (C{scalar}, an
432
+ L{Fsum} or L{Fsum2Tuple}) and settings C{B{f2product}=None} (C{bool})
433
+ and C{B{nonfinites=True}} (C{bool}), see class L{Fsum<Fsum.__init__>}.
442
434
 
443
435
  @return: Dot product (C{float}).
444
436
 
445
- @raise LenError: Unequal C{len(B{a})} and C{len(B{b})}.
437
+ @raise LenError: Unequal C{len(B{xs})} and C{len(B{ys})}.
446
438
 
447
439
  @see: Class L{Fdot}, U{Algorithm 5.10 B{DotK}
448
440
  <https://www.TUHH.De/ti3/paper/rump/OgRuOi05.pdf>} and function
449
441
  C{math.sumprod} in Python 3.12 and later.
450
442
  '''
451
- D = Fdot(a, nonfinites=True, *b, **start)
443
+ D = Fdot(xs, *ys, **_xkwds(start_f2product_nonfinites, nonfinites=True))
452
444
  return float(D)
453
445
 
454
446
 
455
- def fdot3(xs, ys, zs, start=0):
456
- '''Return the precision dot product M{start + sum(a[i] * b[i] * c[i]
457
- for i=0..len(a)-1)}.
447
+ def fdot_(*xys, **start_f2product_nonfinites):
448
+ '''Return the (precision) dot product M{sum(xys[i] * xys[i+1] for i in range(0, len(xys), B{2}))}.
449
+
450
+ @arg xys: Pairwise values (each C{scalar}, an L{Fsum} or L{Fsum2Tuple}), all positional.
451
+
452
+ @see: Function L{fdot} for further details.
453
+
454
+ @return: Dot product (C{float}).
455
+ '''
456
+ return fdot(xys[0::2], *xys[1::2], **start_f2product_nonfinites)
457
+
458
+
459
+ def fdot3(xs, ys, zs, **start_f2product_nonfinites):
460
+ '''Return the (precision) dot product M{start + sum(xs[i] * ys[i] * zs[i] for i in range(len(xs)))}.
461
+
462
+ @arg xs: X values iterable (each C{scalar}, an L{Fsum} or L{Fsum2Tuple}).
463
+ @arg ys: Y values iterable (each C{scalar}, an L{Fsum} or L{Fsum2Tuple}).
464
+ @arg zs: Z values iterable (each C{scalar}, an L{Fsum} or L{Fsum2Tuple}).
458
465
 
459
- @arg xs: Iterable (each C{scalar}, an L{Fsum} or L{Fsum2Tuple}).
460
- @arg ys: Iterable (each C{scalar}, an L{Fsum} or L{Fsum2Tuple}).
461
- @arg zs: Iterable (each C{scalar}, an L{Fsum} or L{Fsum2Tuple}).
462
- @kwarg start: Optional bias (C{scalar}, an L{Fsum} or L{Fsum2Tuple}).
466
+ @see: Function L{fdot} for further details.
463
467
 
464
468
  @return: Dot product (C{float}).
465
469
 
@@ -469,17 +473,16 @@ def fdot3(xs, ys, zs, start=0):
469
473
  if not n == len(ys) == len(zs):
470
474
  raise LenError(fdot3, xs=n, ys=len(ys), zs=len(zs))
471
475
 
472
- D = Fdot((), nonfinites=True, start=start)
473
- _f = Fsum(nonfinites=True) # f2product=True
474
- r = (_f(x).f2mul_(y, z) for x, y, z in zip(xs, ys, zs))
475
- D = D.fadd(_1primed(r) if n < 4 else r)
476
+ D = Fdot((), **_xkwds(start_f2product_nonfinites, nonfinites=True))
477
+ kwds = dict(f2product=D.f2product(), nonfinites=D.nonfinites())
478
+ _f = Fsum(**kwds)
479
+ D = D._facc(_f(x).f2mul_(y, z, **kwds) for x, y, z in zip(xs, ys, zs))
476
480
  return float(D)
477
481
 
478
482
 
479
483
  def fhorner(x, *cs, **incx):
480
- '''Horner form evaluation of polynomial M{sum(cs[i] * x**i for
481
- i=0..n)} with in- or decreasing exponent M{sum(... i=n..0)},
482
- where C{n = len(cs) - 1}.
484
+ '''Horner form evaluation of polynomial M{sum(cs[i] * x**i for i=0..n)} as
485
+ in- or decreasing exponent M{sum(... i=n..0)}, where C{n = len(cs) - 1}.
483
486
 
484
487
  @return: Horner sum (C{float}).
485
488
 
@@ -1092,7 +1095,7 @@ def zqrt(x):
1092
1095
 
1093
1096
  # **) MIT License
1094
1097
  #
1095
- # Copyright (C) 2016-2024 -- mrJean1 at Gmail -- All Rights Reserved.
1098
+ # Copyright (C) 2016-2025 -- mrJean1 at Gmail -- All Rights Reserved.
1096
1099
  #
1097
1100
  # Permission is hereby granted, free of charge, to any person obtaining a
1098
1101
  # copy of this software and associated documentation files (the "Software"),