pygeodesy 24.5.8__py2.py3-none-any.whl → 24.5.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 (83) hide show
  1. {PyGeodesy-24.5.8.dist-info → PyGeodesy-24.5.24.dist-info}/METADATA +2 -2
  2. PyGeodesy-24.5.24.dist-info/RECORD +116 -0
  3. pygeodesy/__init__.py +16 -12
  4. pygeodesy/__main__.py +9 -10
  5. pygeodesy/albers.py +42 -42
  6. pygeodesy/auxilats/__init__.py +1 -1
  7. pygeodesy/auxilats/__main__.py +7 -10
  8. pygeodesy/auxilats/auxAngle.py +32 -31
  9. pygeodesy/auxilats/auxLat.py +81 -51
  10. pygeodesy/azimuthal.py +123 -124
  11. pygeodesy/basics.py +165 -176
  12. pygeodesy/booleans.py +14 -15
  13. pygeodesy/cartesianBase.py +25 -23
  14. pygeodesy/clipy.py +3 -3
  15. pygeodesy/constants.py +8 -6
  16. pygeodesy/css.py +50 -42
  17. pygeodesy/datums.py +50 -48
  18. pygeodesy/dms.py +6 -6
  19. pygeodesy/ecef.py +27 -27
  20. pygeodesy/elevations.py +2 -2
  21. pygeodesy/ellipsoidalBase.py +28 -27
  22. pygeodesy/ellipsoidalBaseDI.py +8 -7
  23. pygeodesy/ellipsoidalNvector.py +11 -12
  24. pygeodesy/ellipsoids.py +41 -35
  25. pygeodesy/elliptic.py +12 -10
  26. pygeodesy/epsg.py +4 -3
  27. pygeodesy/errors.py +35 -13
  28. pygeodesy/etm.py +62 -53
  29. pygeodesy/fmath.py +48 -41
  30. pygeodesy/formy.py +93 -65
  31. pygeodesy/frechet.py +117 -102
  32. pygeodesy/fstats.py +52 -46
  33. pygeodesy/fsums.py +169 -145
  34. pygeodesy/gars.py +10 -9
  35. pygeodesy/geodesicw.py +32 -30
  36. pygeodesy/geodesicx/__init__.py +1 -1
  37. pygeodesy/geodesicx/__main__.py +4 -4
  38. pygeodesy/geodesicx/gx.py +40 -32
  39. pygeodesy/geodesicx/gxarea.py +15 -12
  40. pygeodesy/geodesicx/gxbases.py +3 -4
  41. pygeodesy/geodesicx/gxline.py +6 -8
  42. pygeodesy/geodsolve.py +28 -26
  43. pygeodesy/geohash.py +47 -44
  44. pygeodesy/geoids.py +37 -35
  45. pygeodesy/hausdorff.py +112 -99
  46. pygeodesy/heights.py +136 -129
  47. pygeodesy/internals.py +576 -0
  48. pygeodesy/interns.py +6 -207
  49. pygeodesy/iters.py +22 -19
  50. pygeodesy/karney.py +18 -15
  51. pygeodesy/ktm.py +31 -24
  52. pygeodesy/latlonBase.py +12 -11
  53. pygeodesy/lazily.py +140 -218
  54. pygeodesy/lcc.py +24 -25
  55. pygeodesy/ltp.py +83 -71
  56. pygeodesy/ltpTuples.py +7 -5
  57. pygeodesy/mgrs.py +5 -4
  58. pygeodesy/named.py +136 -49
  59. pygeodesy/namedTuples.py +33 -25
  60. pygeodesy/nvectorBase.py +10 -9
  61. pygeodesy/osgr.py +14 -12
  62. pygeodesy/points.py +13 -13
  63. pygeodesy/props.py +7 -7
  64. pygeodesy/rhumb/__init__.py +1 -1
  65. pygeodesy/rhumb/bases.py +3 -2
  66. pygeodesy/rhumb/solve.py +2 -2
  67. pygeodesy/solveBase.py +8 -7
  68. pygeodesy/sphericalTrigonometry.py +5 -5
  69. pygeodesy/streprs.py +8 -7
  70. pygeodesy/trf.py +8 -8
  71. pygeodesy/triaxials.py +67 -63
  72. pygeodesy/units.py +48 -50
  73. pygeodesy/unitsBase.py +24 -11
  74. pygeodesy/ups.py +7 -6
  75. pygeodesy/utily.py +4 -4
  76. pygeodesy/utm.py +53 -52
  77. pygeodesy/utmupsBase.py +11 -8
  78. pygeodesy/vector2d.py +6 -7
  79. pygeodesy/vector3d.py +16 -17
  80. pygeodesy/vector3dBase.py +5 -5
  81. PyGeodesy-24.5.8.dist-info/RECORD +0 -115
  82. {PyGeodesy-24.5.8.dist-info → PyGeodesy-24.5.24.dist-info}/WHEEL +0 -0
  83. {PyGeodesy-24.5.8.dist-info → PyGeodesy-24.5.24.dist-info}/top_level.txt +0 -0
pygeodesy/errors.py CHANGED
@@ -14,10 +14,11 @@ C{PYGEODESY_EXCEPTION_CHAINING=std} or to any non-empty string.
14
14
  # from pygeodesy.basics import isint, isodd, issubclassof, itemsorted, _xinstanceof, _zip # _MODS
15
15
  # from pygeodesy.ellipsoidalBase import CartesianEllipsoidalBase, LatLonEllipsoidalBase # _MODS
16
16
  # from pygeodesy import errors # _MODS, _MODS.getattr
17
- from pygeodesy.interns import MISSING, NN, _a_, _an_, _and_, _clip_, _COLON_, \
18
- _COLONSPACE_, _COMMASPACE_, _datum_, _ellipsoidal_, \
19
- _incompatible_, _invalid_, _len_, _not_, _or_, _SPACE_, \
20
- _specified_, _UNDER_, _vs_, _with_, _tailof
17
+ from pygeodesy.internals import _plural, _tailof
18
+ from pygeodesy.interns import MISSING, NN, _a_, _an_, _and_, _clip_, _COLON_, _COLONSPACE_, \
19
+ _COMMASPACE_, _datum_, _ellipsoidal_, _incompatible_, _invalid_, \
20
+ _keyword_, _len_, _not_, _or_, _SPACE_, _specified_, _UNDER_, \
21
+ _vs_, _with_
21
22
  from pygeodesy.lazily import _ALL_LAZY, _ALL_MODS as _MODS, _getenv, _PYTHON_X_DEV
22
23
  # from pygeodesy.streprs import Fmt, unstr # _MODS
23
24
  # from pygeodesy.vector3dBase import Vector3dBase # _MODS
@@ -25,9 +26,11 @@ from pygeodesy.lazily import _ALL_LAZY, _ALL_MODS as _MODS, _getenv, _PYTHON_X_D
25
26
  from copy import copy as _copy
26
27
 
27
28
  __all__ = _ALL_LAZY.errors # _ALL_DOCS('_InvalidError', '_IsnotError') _under
28
- __version__ = '24.05.06'
29
+ __version__ = '24.05.19'
29
30
 
31
+ _argument_ = 'argument'
30
32
  _box_ = 'box'
33
+ _expected_ = 'expected'
31
34
  _limiterrors = True # in .formy
32
35
  _name_value_ = repr('name=value')
33
36
  _rangerrors = True # in .dms
@@ -142,6 +145,21 @@ def _TypesError(name, value, *Types, **kwds):
142
145
  return _TypeError(name, value, txt=t, **kwds)
143
146
 
144
147
 
148
+ class _UnexpectedError(TypeError): # note, a TypeError!
149
+ '''(INTERNAL) Format a C{TypeError} I{without exception chaining}.
150
+ '''
151
+ def __init__(self, *args, **kwds):
152
+ n = len(kwds)
153
+ if args:
154
+ a = _plural(_argument_, len(args))
155
+ n = _and(a, _plural(_keyword_, n)) if n else a
156
+ else:
157
+ n = _plural(_SPACE_(_keyword_, _argument_), n)
158
+ u = _MODS.streprs.unstr(_SPACE_(n, NN), *args, **kwds)
159
+ # _error_init(TypeError, self, (u,), txt_not_=_expected_)
160
+ TypeError.__init__(self, _SPACE_(u, _not_, _expected_))
161
+
162
+
145
163
  class _ValueError(ValueError):
146
164
  '''(INTERNAL) Format a C{ValueError} with/-out exception chaining.
147
165
  '''
@@ -375,8 +393,8 @@ def crosserrors(raiser=None):
375
393
  return t
376
394
 
377
395
 
378
- def _error_init(Error, inst, args, fmt_name_value='%s (%r)', txt=NN,
379
- cause=None, **kwds): # by .lazily
396
+ def _error_init(Error, inst, args, fmt_name_value='%s (%r)', txt_not_=NN,
397
+ txt__=None, txt=NN, cause=None, **kwds):
380
398
  '''(INTERNAL) Format an error text and initialize an C{Error} instance.
381
399
 
382
400
  @arg Error: The error super-class (C{Exception}).
@@ -388,6 +406,8 @@ def _error_init(Error, inst, args, fmt_name_value='%s (%r)', txt=NN,
388
406
  given as C{name=value} keyword arguments.
389
407
  @kwarg fmt_name_value: Format for (name, value) (C{str}).
390
408
  @kwarg txt: Optional explanation of the error (C{str}).
409
+ @kwarg txt__: Alternate C{B{txt}=B{txt__}.__name__}.
410
+ @kwarg txt_not_: Negative explanation C{B{txt}=_not_(B{txt_not_})}.
391
411
  @kwarg cause: Optional, caught error (L{Exception}), for
392
412
  exception chaining (supported in Python 3+).
393
413
  @kwarg kwds: Additional C{B{name}=value} pairs, if any.
@@ -409,8 +429,10 @@ def _error_init(Error, inst, args, fmt_name_value='%s (%r)', txt=NN,
409
429
  t += _fmtuple(_MODS.basics.itemsorted(kwds))
410
430
  t = _or(*t) if t else _SPACE_(_name_value_, MISSING)
411
431
 
412
- if txt is not None:
413
- x = str(txt) or (str(cause) if cause else _invalid_)
432
+ x = _not_(txt_not_) if txt_not_ else (txt if txt__ is None
433
+ else txt__.__name__)
434
+ if x is not None:
435
+ x = str(x) or (str(cause) if cause else _invalid_)
414
436
  C = _COMMASPACE_ if _COLON_ in t else _COLONSPACE_
415
437
  t = C(t, x)
416
438
  # else: # LenError, _xzip, .dms, .heights, .vector2d
@@ -599,7 +621,7 @@ def _xcallable(**names_callables):
599
621
  '''
600
622
  for n, c in names_callables.items():
601
623
  if not callable(c):
602
- raise _TypeError(n, c, txt=_not_(callable.__name__))
624
+ raise _TypeError(n, c, txt_not_=callable.__name__) # txt__
603
625
 
604
626
 
605
627
  def _xdatum(datum1, datum2, Error=None):
@@ -625,7 +647,7 @@ def _xellipsoidal(**name_value): # see _xellipsoidall elel
625
647
  return v
626
648
  except AttributeError:
627
649
  pass
628
- raise _TypeError(n, v, txt=_not_(_ellipsoidal_))
650
+ raise _TypeError(n, v, txt_not_=_ellipsoidal_)
629
651
  raise _xAssertionError(_xellipsoidal, name_value)
630
652
 
631
653
 
@@ -716,7 +738,7 @@ except AttributeError:
716
738
  # b = getattr(inst, n, None)
717
739
  # if b is None: # invalid bool attr
718
740
  # t = _SPACE_(_EQUAL_(n, repr(v)), 'for', inst.__class__.__name__) # XXX .classname
719
- # raise _AttributeError(t, txt=_not_('applicable'))
741
+ # raise _AttributeError(t, txt_not_='applicable')
720
742
  # if v in (False, True) and v != b:
721
743
  # setattr(inst, NN(_UNDER_, n), v)
722
744
 
@@ -777,7 +799,7 @@ def _Xorder(_Coeffs, Error, **Xorder): # in .auxLat, .ktm, .rhumb.bases, .rhumb
777
799
  if m in _Coeffs and _MODS.basics.isint(m):
778
800
  return m
779
801
  t = sorted(map(str, _Coeffs.keys()))
780
- raise Error(X, m, txt=_not_(_or(*t)))
802
+ raise Error(X, m, txt_not_=_or(*t))
781
803
 
782
804
  # **) MIT License
783
805
  #
pygeodesy/etm.py CHANGED
@@ -71,10 +71,10 @@ from pygeodesy.datums import _ellipsoidal_datum, _WGS84, _EWGS84
71
71
  # from pygeodesy.ellipsoids import _EWGS84 # from .datums
72
72
  from pygeodesy.elliptic import _ALL_LAZY, Elliptic
73
73
  # from pygeodesy.errors import _incompatible # from .named
74
- from pygeodesy.fmath import cbrt, hypot, hypot1, hypot2
75
- from pygeodesy.fsums import Fsum, fsum1f_
76
- from pygeodesy.interns import NN, _COMMASPACE_, _DASH_, _near_, _SPACE_, \
77
- _spherical_, _usage
74
+ # from pygeodesy.fsums import Fsum # from .fmath
75
+ from pygeodesy.fmath import cbrt, hypot, hypot1, hypot2, Fsum
76
+ from pygeodesy.interns import _COMMASPACE_, _DASH_, _near_, _SPACE_, \
77
+ _spherical_
78
78
  from pygeodesy.karney import _copyBit, _diff182, _fix90, _norm2, _norm180, \
79
79
  _tand, _unsigned2
80
80
  # from pygeodesy.lazily import _ALL_LAZY # from .elliptic
@@ -92,7 +92,7 @@ from pygeodesy.utm import _cmlon, _LLEB, _parseUTM5, _toBand, _toXtm8, \
92
92
  from math import asinh, atan2, degrees, radians, sinh, sqrt
93
93
 
94
94
  __all__ = _ALL_LAZY.etm
95
- __version__ = '24.04.07'
95
+ __version__ = '24.05.24'
96
96
 
97
97
  _OVERFLOW = _1_EPS**2 # about 2e+31
98
98
  _TAYTOL = pow(EPS, 0.6)
@@ -157,13 +157,13 @@ class Etm(Utm):
157
157
  self._exactTM = exactTM
158
158
  self._scale0 = exactTM.k0
159
159
 
160
- def parse(self, strETM, name=NN):
160
+ def parse(self, strETM, **name):
161
161
  '''Parse a string to a similar L{Etm} instance.
162
162
 
163
- @arg strETM: The ETM coordinate (C{str}),
164
- see function L{parseETM5}.
165
- @kwarg name: Optional instance name (C{str}),
166
- overriding this name.
163
+ @arg strETM: The ETM coordinate (C{str}), see function
164
+ L{parseETM5}.
165
+ @kwarg name: Optional C{B{name}=NN} (C{str}), overriding
166
+ this name.
167
167
 
168
168
  @return: The instance (L{Etm}).
169
169
 
@@ -173,7 +173,7 @@ class Etm(Utm):
173
173
  and L{pygeodesy.parseUTMUPS5}.
174
174
  '''
175
175
  return parseETM5(strETM, datum=self.datum, Etm=self.classof,
176
- name=name or self.name)
176
+ name=self._name__(name))
177
177
 
178
178
  @deprecated_method
179
179
  def parseETM(self, strETM): # PYCHOK no cover
@@ -247,7 +247,7 @@ class ExactTransverseMercator(_NamedBase):
247
247
  _sigmaC = None # most recent _sigmaInv04 case C{int}
248
248
  _zetaC = None # most recent _zetaInv04 case C{int}
249
249
 
250
- def __init__(self, datum=_WGS84, lon0=0, k0=_K0_UTM, extendp=False, name=NN, raiser=False):
250
+ def __init__(self, datum=_WGS84, lon0=0, k0=_K0_UTM, extendp=False, raiser=False, **name):
251
251
  '''New L{ExactTransverseMercator} projection.
252
252
 
253
253
  @kwarg datum: The I{non-spherical} datum or ellipsoid (L{Datum},
@@ -255,8 +255,8 @@ class ExactTransverseMercator(_NamedBase):
255
255
  @kwarg lon0: Central meridian, default (C{degrees180}).
256
256
  @kwarg k0: Central scale factor (C{float}).
257
257
  @kwarg extendp: Use the I{extended} domain (C{bool}), I{standard} otherwise.
258
- @kwarg name: Optional name for the projection (C{str}).
259
258
  @kwarg raiser: If C{True}, throw an L{ETMError} for convergence failures (C{bool}).
259
+ @kwarg name: Optional C{B{name}=NN} for the projection (C{str}).
260
260
 
261
261
  @raise ETMError: Near-spherical B{C{datum}} or C{ellipsoid} or invalid B{C{lon0}}
262
262
  or B{C{k0}}.
@@ -444,14 +444,14 @@ class ExactTransverseMercator(_NamedBase):
444
444
 
445
445
  f = flattening
446
446
 
447
- def forward(self, lat, lon, lon0=None, name=NN): # MCCABE 13
447
+ def forward(self, lat, lon, lon0=None, **name): # MCCABE 13
448
448
  '''Forward projection, from geographic to transverse Mercator.
449
449
 
450
450
  @arg lat: Latitude of point (C{degrees}).
451
451
  @arg lon: Longitude of point (C{degrees}).
452
452
  @kwarg lon0: Central meridian (C{degrees180}), overriding
453
453
  the default if not C{None}.
454
- @kwarg name: Optional name (C{str}).
454
+ @kwarg name: Optional C{B{name}=NN} (C{str}).
455
455
 
456
456
  @return: L{Forward4Tuple}C{(easting, northing, gamma, scale)}.
457
457
 
@@ -500,7 +500,7 @@ class ExactTransverseMercator(_NamedBase):
500
500
  if _lon:
501
501
  x, g = neg_(x, g)
502
502
  return Forward4Tuple(x, y, g, k, iteration=self._iteration,
503
- name=name or self.name)
503
+ name=self._name__(name))
504
504
 
505
505
  def _Inv03(self, psi, dlam, _3_mv_e): # (xi, deta, _3_mv)
506
506
  '''(INTERNAL) Partial C{_zetaInv04} or C{_sigmaInv04}, Case 2
@@ -609,6 +609,7 @@ class ExactTransverseMercator(_NamedBase):
609
609
  _U_2 = Fsum(u).fsum2f_
610
610
  _V_2 = Fsum(v).fsum2f_
611
611
  # min iterations 2, max 6 or 7, mean 3.9 or 4.0
612
+ _hy2 = hypot2
612
613
  for i in range(1, _TRIPS): # GEOGRAPHICLIB_PANIC
613
614
  sncndn6 = self._sncndn6(u, v)
614
615
  du, dv = _zetaDwd2(*sncndn6)
@@ -620,7 +621,7 @@ class ExactTransverseMercator(_NamedBase):
620
621
  if d2 < tol2:
621
622
  r = False
622
623
  break
623
- d2 = hypot2(dU, dV)
624
+ d2 = _hy2(dU, dV)
624
625
 
625
626
  self._iteration = i
626
627
  if r: # PYCHOK no cover
@@ -680,14 +681,14 @@ class ExactTransverseMercator(_NamedBase):
680
681
  self._mu = mu
681
682
  self._mv = mv
682
683
 
683
- def reverse(self, x, y, lon0=None, name=NN):
684
+ def reverse(self, x, y, lon0=None, **name):
684
685
  '''Reverse projection, from Transverse Mercator to geographic.
685
686
 
686
687
  @arg x: Easting of point (C{meters}).
687
688
  @arg y: Northing of point (C{meters}).
688
- @kwarg lon0: Central meridian (C{degrees180}), overriding
689
- the default if not C{None}.
690
- @kwarg name: Optional name (C{str}).
689
+ @kwarg lon0: Optional central meridian (C{degrees180}),
690
+ overriding the default (C{iff not None}).
691
+ @kwarg name: Optional C{B{name}=NN} (C{str}).
691
692
 
692
693
  @return: L{Reverse4Tuple}C{(lat, lon, gamma, scale)}.
693
694
 
@@ -730,9 +731,9 @@ class ExactTransverseMercator(_NamedBase):
730
731
  lon, g = neg_(lon, g)
731
732
  lat += self._lat0
732
733
  lon += self._lon0 if lon0 is None else _norm180(lon0)
733
- return Reverse4Tuple(lat, _norm180(lon), g, k, # _norm180(lat)
734
+ return Reverse4Tuple(lat, _norm180(lon), g, k, # _fix90(lat)
734
735
  iteration=self._iteration,
735
- name=name or self.name)
736
+ name=self._name__(name))
736
737
 
737
738
  def _scaled2(self, tau, d2, snu, cnu, dnu, snv, cnv, dnv):
738
739
  '''(INTERNAL) C{scaled}.
@@ -768,7 +769,7 @@ class ExactTransverseMercator(_NamedBase):
768
769
  # = sqrt(mv + mv * tau**2 + mu) * sqrt(q2)
769
770
  k, q2 = _0_0, (mv * snv**2 + cnudnv**2)
770
771
  if q2 > 0:
771
- k2 = fsum1f_(mu, mv, mv * tau**2)
772
+ k2 = (tau**2 + _1_0) * mv + mu
772
773
  if k2 > 0:
773
774
  k = sqrt(k2) * sqrt(q2 / d2) * self.k0
774
775
  else:
@@ -811,13 +812,14 @@ class ExactTransverseMercator(_NamedBase):
811
812
  real /*v*/, real snv, real cnv, real dnv,
812
813
  real &du, real &dv)}.
813
814
  '''
815
+ mu = self._mu
814
816
  snuv = snu * snv
815
817
  # Reciprocal of 55.9: dw / ds = dn(w)^2/_mv,
816
818
  # expanding complex dn(w) using A+S 16.21.4
817
- d = self._mv * (cnv**2 + self._mu * snuv**2)**2
818
- r = cnv * dnu * dnv
819
- i = cnu * snuv * self._mu
820
- du = (r**2 - i**2) / d
819
+ d = (cnv**2 + snuv**2 * mu)**2 * self._mv
820
+ r = cnv * dnu * dnv
821
+ i = cnu * snuv * mu
822
+ du = (r**2 - i**2) / d # (r + i) * (r - i) / d
821
823
  dv = neg(r * i * _2_0 / d)
822
824
  return du, dv
823
825
 
@@ -1030,7 +1032,7 @@ class ExactTransverseMercator(_NamedBase):
1030
1032
  return g_k # or (g, k, lat, lon)
1031
1033
 
1032
1034
 
1033
- def parseETM5(strUTM, datum=_WGS84, Etm=Etm, falsed=True, name=NN):
1035
+ def parseETM5(strUTM, datum=_WGS84, Etm=Etm, falsed=True, **name):
1034
1036
  '''Parse a string representing a UTM coordinate, consisting
1035
1037
  of C{"zone[band] hemisphere easting northing"}.
1036
1038
 
@@ -1040,7 +1042,7 @@ def parseETM5(strUTM, datum=_WGS84, Etm=Etm, falsed=True, name=NN):
1040
1042
  @kwarg Etm: Optional class to return the UTM coordinate
1041
1043
  (L{Etm}) or C{None}.
1042
1044
  @kwarg falsed: Both easting and northing are C{falsed} (C{bool}).
1043
- @kwarg name: Optional B{C{Etm}} name (C{str}).
1045
+ @kwarg name: Optional B{C{Etm}} C{B{name}=NN} (C{str}).
1044
1046
 
1045
1047
  @return: The UTM coordinate (B{C{Etm}}) or if B{C{Etm}} is
1046
1048
  C{None}, a L{UtmUps5Tuple}C{(zone, hemipole, easting,
@@ -1051,29 +1053,31 @@ def parseETM5(strUTM, datum=_WGS84, Etm=Etm, falsed=True, name=NN):
1051
1053
 
1052
1054
  @raise TypeError: Invalid or near-spherical B{C{datum}}.
1053
1055
  '''
1054
- r = _parseUTM5(strUTM, datum, Etm, falsed, Error=ETMError, name=name)
1056
+ r = _parseUTM5(strUTM, datum, Etm, falsed, Error=ETMError, **name)
1055
1057
  return r
1056
1058
 
1057
1059
 
1058
1060
  def toEtm8(latlon, lon=None, datum=None, Etm=Etm, falsed=True,
1059
- name=NN, strict=True,
1060
- zone=None, **cmoff):
1061
+ strict=True, zone=None,
1062
+ **name_cmoff):
1061
1063
  '''Convert a geodetic lat-/longitude to an ETM coordinate.
1062
1064
 
1063
1065
  @arg latlon: Latitude (C{degrees}) or an (ellipsoidal)
1064
1066
  geodetic C{LatLon} instance.
1065
- @kwarg lon: Optional longitude (C{degrees}) or C{None}.
1067
+ @kwarg lon: Optional longitude (C{degrees}), required
1068
+ if B{C{latlon}} is in C{degrees}.
1066
1069
  @kwarg datum: Optional datum for the ETM coordinate,
1067
1070
  overriding B{C{latlon}}'s datum (L{Datum},
1068
1071
  L{Ellipsoid}, L{Ellipsoid2} or L{a_f2Tuple}).
1069
1072
  @kwarg Etm: Optional class to return the ETM coordinate
1070
1073
  (L{Etm}) or C{None}.
1071
1074
  @kwarg falsed: False both easting and northing (C{bool}).
1072
- @kwarg name: Optional B{C{Utm}} name (C{str}).
1073
1075
  @kwarg strict: Restrict B{C{lat}} to UTM ranges (C{bool}).
1074
1076
  @kwarg zone: Optional UTM zone to enforce (C{int} or C{str}).
1075
- @kwarg cmoff: DEPRECATED, use B{C{falsed}}. Offset longitude
1076
- from the zone's central meridian (C{bool}).
1077
+ @kwarg name_cmoff: Optional B{C{Etm}} C{B{name}=NN} (C{str})
1078
+ and DEPRECATED C{B{cmoff}=True} to offset longitude
1079
+ from the zone's central meridian (C{bool}), instead
1080
+ use C{B{falsed}=True}.
1077
1081
 
1078
1082
  @return: The ETM coordinate as an B{C{Etm}} instance or a
1079
1083
  L{UtmUps8Tuple}C{(zone, hemipole, easting, northing,
@@ -1097,23 +1101,25 @@ def toEtm8(latlon, lon=None, datum=None, Etm=Etm, falsed=True,
1097
1101
  @raise ValueError: The B{C{lon}} value is missing or B{C{latlon}}
1098
1102
  is invalid.
1099
1103
  '''
1100
- z, B, lat, lon, d, f, name = _to7zBlldfn(latlon, lon, datum,
1101
- falsed, name, zone,
1102
- strict, ETMError, **cmoff)
1104
+ z, B, lat, lon, d, f, n = _to7zBlldfn(latlon, lon, datum,
1105
+ falsed, zone, strict,
1106
+ ETMError, **name_cmoff)
1103
1107
  lon0 = _cmlon(z) if f else None
1104
1108
  x, y, g, k = d.exactTM.forward(lat, lon, lon0=lon0)
1105
1109
 
1106
1110
  return _toXtm8(Etm, z, lat, x, y, B, d, g, k, f,
1107
- name, latlon, d.exactTM, Error=ETMError)
1111
+ n, latlon, d.exactTM, Error=ETMError)
1108
1112
 
1109
1113
 
1110
1114
  if __name__ == '__main__': # MCCABE 13
1111
1115
 
1112
1116
  from pygeodesy import fstr, KTransverseMercator, printf
1117
+ from pygeodesy.internals import _usage
1113
1118
  from sys import argv, exit as _exit
1114
1119
 
1115
1120
  # mimick some of I{Karney}'s utility C{TransverseMercatorProj}
1116
1121
  _f = _r = _s = _t = False
1122
+ _p = -6
1117
1123
  _as = argv[1:]
1118
1124
  while _as and _as[0].startswith(_DASH_):
1119
1125
  _a = _as.pop(0)
@@ -1123,16 +1129,19 @@ if __name__ == '__main__': # MCCABE 13
1123
1129
  _f, _r = True, False
1124
1130
  elif '-reverse'.startswith(_a):
1125
1131
  _f, _r = False, True
1132
+ elif '-precision'.startswith(_a):
1133
+ _p = int(_as.pop(0))
1126
1134
  elif '-series'.startswith(_a):
1127
1135
  _s, _t = True, False
1128
1136
  elif _a == '-t':
1129
1137
  _s, _t = False, True
1130
1138
  elif '-help'.startswith(_a):
1131
- _exit(_usage(argv[0], '[-s | -t]',
1139
+ _exit(_usage(argv[0], '[-s | -t ]',
1140
+ '[-p[recision] <ndigits>',
1132
1141
  '[-f[orward] <lat> <lon>',
1133
- '| -r[everse] <easting> <northing>',
1134
- '| <lat> <lon>]',
1135
- '| -h[elp]'))
1142
+ '|-r[everse] <easting> <northing>',
1143
+ '|<lat> <lon>]',
1144
+ '|-h[elp]'))
1136
1145
  else:
1137
1146
  _exit('%s: option %r not supported' % (_usage(*argv), _a))
1138
1147
  if len(_as) > 1:
@@ -1151,18 +1160,18 @@ if __name__ == '__main__': # MCCABE 13
1151
1160
  t = tm.reverse(*f2)
1152
1161
  else:
1153
1162
  t = tm.forward(*f2)
1154
- printf('%s: %s', tm.classname, fstr(t, sep=_SPACE_))
1163
+ printf('%s: %s', tm.classname, fstr(t, prec=_p, sep=_SPACE_))
1155
1164
  t = tm.reverse(t.easting, t.northing)
1156
- printf('%s: %s', tm.classname, fstr(t, sep=_SPACE_))
1165
+ printf('%s: %s', tm.classname, fstr(t, prec=_p, sep=_SPACE_))
1157
1166
 
1158
1167
 
1159
- # % python3 -m pygeodesy.etm 33.33 44.44
1160
- # ExactTransverseMercator: 4276926.114804 4727193.767015 28.375537 1.233325
1161
- # ExactTransverseMercator: 33.33 44.44 28.375537 1.233325
1168
+ # % python3 -m pygeodesy.etm -p 12 33.33 44.44
1169
+ # ExactTransverseMercator: 4276926.11480390653 4727193.767015309073 28.375536563148 1.233325101778
1170
+ # ExactTransverseMercator: 33.33 44.44 28.375536563148 1.233325101778
1162
1171
 
1163
- # % python3 -m pygeodesy.etm -s 33.33 44.44
1164
- # KTransverseMercator: 4276926.114804 4727193.767015 28.375537 1.233325
1165
- # KTransverseMercator: 33.33 44.44 28.375537 1.233325
1172
+ # % python3 -m pygeodesy.etm -s -p 12 33.33 44.44
1173
+ # KTransverseMercator: 4276926.114803904667 4727193.767015310004 28.375536563148 1.233325101778
1174
+ # KTransverseMercator: 33.33 44.44 28.375536563148 1.233325101778
1166
1175
 
1167
1176
  # % echo 33.33 44.44 | .../bin/TransverseMercatorProj
1168
1177
  # 4276926.114804 4727193.767015 28.375536563148 1.233325101778
pygeodesy/fmath.py CHANGED
@@ -7,7 +7,7 @@ u'''Utilities using precision floating point summation.
7
7
  from __future__ import division as _; del _ # PYCHOK semicolon
8
8
 
9
9
  from pygeodesy.basics import _copysign, copysign0, isbool, isint, isscalar, \
10
- len2, map1
10
+ len2, map1, _xiterable
11
11
  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
  _N_1_0, _1_5, _copysign_0_0, _isfinite, remainder
@@ -24,7 +24,7 @@ from math import fabs, sqrt # pow
24
24
  import operator as _operator # in .datums, .trf, .utm
25
25
 
26
26
  __all__ = _ALL_LAZY.fmath
27
- __version__ = '24.05.07'
27
+ __version__ = '24.05.24'
28
28
 
29
29
  # sqrt(2) <https://WikiPedia.org/wiki/Square_root_of_2>
30
30
  _0_4142 = 0.41421356237309504880 # ... sqrt(2) - 1
@@ -39,12 +39,13 @@ class Fdot(Fsum):
39
39
  '''New L{Fdot} precision dot product M{sum(a[i] * b[i] for
40
40
  i=0..len(a)-1)}.
41
41
 
42
- @arg a: Iterable of values (each C{scalar} or an L{Fsum} or
43
- L{Fsum2Tuple} instance).
44
- @arg b: Other values (each C{scalar} or an L{Fsum} or L{Fsum2Tuple}
45
- instance), all positional.
46
- @kwarg name_RESIDUAL: Optional C{B{name}=NN} and C{B{RESIDUAL}=0.0}
47
- threshold, see L{Fsum<Fsum.__init__>}.
42
+ @arg a: Iterable of values (each C{scalar} or an L{Fsum} or L{Fsum2Tuple}
43
+ instance).
44
+ @arg b: Other values (each C{scalar} or an L{Fsum} or L{Fsum2Tuple} instance),
45
+ all positional.
46
+ @kwarg name_RESIDUAL: Optional C{B{name}=NN} (C{str}) and the C{B{RESIDUAL}=0.0}
47
+ threshold (C{scalar}) for raising L{ResidualError}s, see class
48
+ L{Fsum<Fsum.__init__>}.
48
49
 
49
50
  @raise LenError: Unequal C{len(B{a})} and C{len(B{b})}.
50
51
 
@@ -68,10 +69,11 @@ class Fhorner(Fsum):
68
69
  i=0..len(cs)-1)}.
69
70
 
70
71
  @arg x: Polynomial argument (C{scalar} or an L{Fsum} or L{Fsum2Tuple}).
71
- @arg cs: Polynomial coeffients (each C{scalar} or an L{Fsum} or
72
- L{Fsum2Tuple} instance), all positional.
73
- @kwarg name_RESIDUAL: Optional C{B{name}=NN} and C{B{RESIDUAL}=0.0}
74
- threshold, see L{Fsum<Fsum.__init__>}.
72
+ @arg cs: Polynomial coeffients (each C{scalar} or an L{Fsum} or L{Fsum2Tuple}
73
+ instance), all positional.
74
+ @kwarg name_RESIDUAL: Optional C{B{name}=NN} (C{str}) and the C{B{RESIDUAL}=0.0}
75
+ threshold (C{scalar}) for raising L{ResidualError}s, see class
76
+ L{Fsum<Fsum.__init__>}.
75
77
 
76
78
  @raise OverflowError: Partial C{2sum} overflow.
77
79
 
@@ -107,11 +109,12 @@ class Fhypot(Fsum):
107
109
  '''New L{Fhypot} hypotenuse of (the I{root} of) several components
108
110
  (raised to the power I{root}).
109
111
 
110
- @arg xs: Components (each C{scalar} or an L{Fsum} or L{Fsum2Tuple}
111
- instance), all positional.
112
- @kwarg root_name_RESIDUAL_raiser: Optional, exponent and C{B{root}=2}
113
- order, C{B{name}=NN}, C{B{RESIDUAL}=0.0} threshold and
114
- C{B{raiser}=True}, see class L{Fsum<Fsum.__init__>} and
112
+ @arg xs: Components (each C{scalar} or an L{Fsum} or L{Fsum2Tuple} instance),
113
+ all positional.
114
+ @kwarg root_name_RESIDUAL_raiser: Optional, exponent and C{B{root}=2} order
115
+ (C{scalar}), C{B{name}=NN} (C{str}), the C{B{RESIDUAL}=0.0}
116
+ threshold (C{scalar}) and C{B{raiser}=True} (C{bool}) for
117
+ raising L{ResidualError}s, see class L{Fsum<Fsum.__init__>} and
115
118
  method L{root<Fsum.root>}.
116
119
  '''
117
120
  r = None # _xkwds_pop2 error
@@ -134,10 +137,11 @@ class Fpolynomial(Fsum):
134
137
  M{sum(cs[i] * x**i for i=0..len(cs)-1)}.
135
138
 
136
139
  @arg x: Polynomial argument (C{scalar} or an L{Fsum} or L{Fsum2Tuple}).
137
- @arg cs: Polynomial coeffients (each C{scalar} or an L{Fsum} or
138
- L{Fsum2Tuple} instance), all positional.
139
- @kwarg name_RESIDUAL: Optional C{B{name}=NN} and C{B{RESIDUAL}=0.0}
140
- threshold, see L{Fsum<Fsum.__init__>}.
140
+ @arg cs: Polynomial coeffients (each C{scalar} or an L{Fsum} or L{Fsum2Tuple}
141
+ instance), all positional.
142
+ @kwarg name_RESIDUAL: Optional C{B{name}=NN} (C{str}) and the C{B{RESIDUAL}=0.0}
143
+ threshold (C{scalar}) for raising L{ResidualError}s, see class
144
+ L{Fsum<Fsum.__init__>}.
141
145
 
142
146
  @raise OverflowError: Partial C{2sum} overflow.
143
147
 
@@ -162,11 +166,12 @@ class Fpowers(Fsum):
162
166
  '''New L{Fpowers} sum of (the I{power} of) several bases.
163
167
 
164
168
  @arg power: The exponent (C{scalar} or an L{Fsum} or L{Fsum2Tuple}).
165
- @arg xs: One or more bases (each C{scalar} or an L{Fsum} or L{Fsum2Tuple}
166
- instance), all positional.
167
- @kwarg name_RESIDUAL_raiser: Optional C{B{name}=NN}, C{B{RESIDUAL}=0.0}
168
- threshold and C{B{raiser}=True}, see L{Fsum<Fsum.__init__>}
169
- and L{fpow<Fsum.fpow>}.
169
+ @arg xs: One or more bases (each C{scalar} or an L{Fsum} or L{Fsum2Tuple} instance),
170
+ all positional.
171
+ @kwarg name_RESIDUAL_raiser: Optional C{B{name}=NN} (C{str}), the C{B{RESIDUAL}=0.0}
172
+ threshold (C{scalar}) and C{B{raiser}=True} (C{bool}) for raising
173
+ L{ResidualError}s, see class L{Fsum<Fsum.__init__>} and method
174
+ L{fpow<Fsum.fpow>}.
170
175
  '''
171
176
  try:
172
177
  raiser = _Fsum__init__(self, **name_RESIDUAL_raiser)
@@ -183,11 +188,12 @@ class Froot(Fsum):
183
188
  '''New L{Froot} root of a precision sum.
184
189
 
185
190
  @arg root: The order (C{scalar} or an L{Fsum} or L{Fsum2Tuple}), non-zero.
186
- @arg xs: Items to summate (each a C{scalar} or an L{Fsum} or L{Fsum2Tuple}
187
- instance), all positional.
188
- @kwarg name_RESIDUAL_raiser: Optional C{B{name}=NN}, C{B{RESIDUAL}=0.0}
189
- threshold and C{B{raiser}=True}, see L{Fsum<Fsum.__init__>}
190
- and L{fpow<Fsum.fpow>}.
191
+ @arg xs: Items to summate (each a C{scalar} or an L{Fsum} or L{Fsum2Tuple} instance),
192
+ all positional.
193
+ @kwarg name_RESIDUAL_raiser: Optional C{B{name}=NN} (C{str}), the C{B{RESIDUAL}=0.0}
194
+ threshold (C{scalar}) and C{B{raiser}=True} (C{bool}) for raising
195
+ L{ResidualError}s, see class L{Fsum<Fsum.__init__>} and method
196
+ L{fpow<Fsum.fpow>}.
191
197
  '''
192
198
  try:
193
199
  raiser = _Fsum__init__(self, **name_RESIDUAL_raiser)
@@ -521,17 +527,18 @@ def fidw(xs, ds, beta=2):
521
527
  _F = Fsum
522
528
  W = _F()
523
529
  X = _F()
524
- for i, d in enumerate(ds):
530
+ for i, d in enumerate(_xiterable(ds)):
525
531
  x = xs[i]
526
- if d < EPS0:
527
- if d < 0:
532
+ D = _F(d)
533
+ if D < EPS0:
534
+ if D < 0:
528
535
  raise ValueError(_negative_)
529
536
  x = float(x)
530
537
  i = n
531
538
  break
532
- D = _F(d).fpow(b)
533
- W += D
534
- X += D.fmul(x)
539
+ if D.fpow(b):
540
+ W += D
541
+ X += D.fmul(x)
535
542
  else:
536
543
  x = X.fover(W, raiser=False)
537
544
  i += 1 # len(xs) >= len(ds)
@@ -630,12 +637,12 @@ except ImportError:
630
637
  def fprod(xs, start=1):
631
638
  '''Iterable product, like C{math.prod} or C{numpy.prod}.
632
639
 
633
- @arg xs: Terms to be multiplied, an iterable, list,
634
- tuple, etc. (C{scalar}s).
635
- @kwarg start: Initial term, also the value returned
640
+ @arg xs: Iterable of values to be multiplied (each
641
+ C{scalar} or an L{Fsum}).
642
+ @kwarg start: Initial value, also the value returned
636
643
  for an empty B{C{xs}} (C{scalar}).
637
644
 
638
- @return: The product (C{float}).
645
+ @return: The product (C{float} or an L{Fsum}).
639
646
 
640
647
  @see: U{NumPy.prod<https://docs.SciPy.org/doc/
641
648
  numpy/reference/generated/numpy.prod.html>}.