pygeodesy 24.8.4__py2.py3-none-any.whl → 24.9.9__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 (76) hide show
  1. {PyGeodesy-24.8.4.dist-info → PyGeodesy-24.9.9.dist-info}/METADATA +17 -16
  2. PyGeodesy-24.9.9.dist-info/RECORD +118 -0
  3. {PyGeodesy-24.8.4.dist-info → PyGeodesy-24.9.9.dist-info}/WHEEL +1 -1
  4. pygeodesy/__init__.py +23 -23
  5. pygeodesy/__main__.py +46 -47
  6. pygeodesy/auxilats/_CX_4.py +104 -181
  7. pygeodesy/auxilats/_CX_6.py +152 -277
  8. pygeodesy/auxilats/_CX_8.py +211 -438
  9. pygeodesy/auxilats/_CX_Rs.py +222 -0
  10. pygeodesy/auxilats/__init__.py +2 -2
  11. pygeodesy/auxilats/__main__.py +30 -38
  12. pygeodesy/auxilats/auxDST.py +2 -2
  13. pygeodesy/auxilats/auxLat.py +28 -36
  14. pygeodesy/auxilats/auxily.py +30 -50
  15. pygeodesy/basics.py +18 -7
  16. pygeodesy/booleans.py +10 -11
  17. pygeodesy/cartesianBase.py +5 -5
  18. pygeodesy/constants.py +35 -34
  19. pygeodesy/ellipsoidalBase.py +18 -15
  20. pygeodesy/ellipsoidalExact.py +2 -2
  21. pygeodesy/ellipsoidalGeodSolve.py +2 -2
  22. pygeodesy/ellipsoidalKarney.py +2 -2
  23. pygeodesy/ellipsoidalNvector.py +2 -2
  24. pygeodesy/ellipsoidalVincenty.py +7 -6
  25. pygeodesy/elliptic.py +154 -88
  26. pygeodesy/epsg.py +3 -3
  27. pygeodesy/etm.py +71 -59
  28. pygeodesy/fmath.py +99 -90
  29. pygeodesy/fsums.py +201 -14
  30. pygeodesy/gars.py +9 -8
  31. pygeodesy/geodesici.py +6 -5
  32. pygeodesy/geodesicx/_C4_24.py +1 -3
  33. pygeodesy/geodesicx/_C4_27.py +1 -3
  34. pygeodesy/geodesicx/_C4_30.py +1 -3
  35. pygeodesy/geodesicx/__init__.py +1 -1
  36. pygeodesy/geodesicx/__main__.py +44 -46
  37. pygeodesy/geodesicx/gx.py +3 -3
  38. pygeodesy/geodesicx/gxarea.py +5 -5
  39. pygeodesy/geodesicx/gxbases.py +32 -18
  40. pygeodesy/geodsolve.py +3 -3
  41. pygeodesy/geohash.py +18 -11
  42. pygeodesy/geoids.py +293 -315
  43. pygeodesy/heights.py +150 -158
  44. pygeodesy/internals.py +70 -9
  45. pygeodesy/interns.py +4 -4
  46. pygeodesy/karney.py +83 -60
  47. pygeodesy/ktm.py +4 -4
  48. pygeodesy/latlonBase.py +13 -7
  49. pygeodesy/lazily.py +13 -8
  50. pygeodesy/ltp.py +5 -6
  51. pygeodesy/ltpTuples.py +7 -1
  52. pygeodesy/mgrs.py +47 -42
  53. pygeodesy/named.py +8 -4
  54. pygeodesy/namedTuples.py +14 -1
  55. pygeodesy/osgr.py +7 -7
  56. pygeodesy/points.py +2 -2
  57. pygeodesy/props.py +7 -6
  58. pygeodesy/resections.py +7 -7
  59. pygeodesy/rhumb/__init__.py +1 -1
  60. pygeodesy/rhumb/aux_.py +42 -60
  61. pygeodesy/rhumb/solve.py +3 -3
  62. pygeodesy/simplify.py +10 -10
  63. pygeodesy/sphericalBase.py +3 -3
  64. pygeodesy/sphericalTrigonometry.py +2 -2
  65. pygeodesy/streprs.py +3 -3
  66. pygeodesy/triaxials.py +207 -201
  67. pygeodesy/units.py +3 -3
  68. pygeodesy/unitsBase.py +4 -4
  69. pygeodesy/utmupsBase.py +3 -3
  70. pygeodesy/vector2d.py +158 -51
  71. pygeodesy/vector3d.py +13 -52
  72. pygeodesy/vector3dBase.py +81 -63
  73. pygeodesy/webmercator.py +3 -3
  74. pygeodesy/wgrs.py +20 -22
  75. PyGeodesy-24.8.4.dist-info/RECORD +0 -117
  76. {PyGeodesy-24.8.4.dist-info → PyGeodesy-24.9.9.dist-info}/top_level.txt +0 -0
pygeodesy/fsums.py CHANGED
@@ -4,6 +4,11 @@
4
4
  u'''Class L{Fsum} for precision floating point summation and I{running}
5
5
  summation based on, respectively similar to Python's C{math.fsum}.
6
6
 
7
+ Class L{Fsum} also supports accurate multiplication for Python 3.13 and
8
+ later, but as an option for older Python versions. For more details, see
9
+ method L{f2product<Fsum.f2product>}, class L{Fsum2product} and U{Accurate
10
+ Sum and Dot Product<https://www.TUHH.De/ti3/paper/rump/OgRuOi05.pdf>}.
11
+
7
12
  Generally, an L{Fsum} instance is considered a C{float} plus a small or zero
8
13
  C{residual} value, see property L{Fsum.residual}. However, there are several
9
14
  C{integer} L{Fsum} cases, for example the result of C{ceil}, C{floor},
@@ -25,13 +30,13 @@ from __future__ import division as _; del _ # PYCHOK semicolon
25
30
 
26
31
  from pygeodesy.basics import isbool, iscomplex, isint, isscalar, \
27
32
  _signOf, itemsorted, signOf, _xiterable, \
28
- _xiterablen, _enquote
29
- from pygeodesy.constants import INT0, _isfinite, NEG0, _pos_self, \
33
+ _xiterablen
34
+ from pygeodesy.constants import INT0, _isfinite, MANT_DIG, NEG0, _pos_self, \
30
35
  _0_0, _1_0, _N_1_0, Float, Int
31
36
  from pygeodesy.errors import _OverflowError, _TypeError, _UnexpectedError, \
32
37
  _ValueError, _xError, _xError2, _xkwds_get1, \
33
38
  _xkwds_pop2
34
- # from pygeodesy.internals import _enquote # from .basics
39
+ from pygeodesy.internals import _enquote, _passarg
35
40
  from pygeodesy.interns import NN, _arg_, _COMMASPACE_, _DASH_, _DOT_, \
36
41
  _EQUAL_, _from_, _LANGLE_, _NOTEQUAL_, \
37
42
  _not_finite_, _PERCENT_, _PLUS_, \
@@ -47,7 +52,7 @@ from pygeodesy.streprs import Fmt, fstr, unstr
47
52
  from math import ceil as _ceil, fabs, floor as _floor # PYCHOK used! .ltp
48
53
 
49
54
  __all__ = _ALL_LAZY.fsums
50
- __version__ = '24.06.11'
55
+ __version__ = '24.09.10'
51
56
 
52
57
  _add_op_ = _PLUS_ # in .auxilats.auxAngle
53
58
  _eq_op_ = _EQUAL_ * 2 # _DEQUAL_
@@ -132,6 +137,81 @@ def _2floats(xs, origin=0, _X=_X_ps, _x=float):
132
137
  _xError(X, Fmt.INDEX(xs=i), x)
133
138
 
134
139
 
140
+ try: # MCCABE 14
141
+ from math import fma as _fma
142
+
143
+ def _2products(x, ys, **unused):
144
+ # TwoProductFMA U{Algorithm 3.5
145
+ # <https://www.TUHH.De/ti3/paper/rump/OgRuOi05.pdf>}
146
+ for y in ys:
147
+ f = x * y
148
+ yield f
149
+ yield _fma(x, y, -f)
150
+
151
+ _2split3s = _passarg # NOP
152
+
153
+ except ImportError: # Python 3.12-
154
+
155
+ def _fma(*a_b_c): # in .fmath
156
+ # mimick C{math.fma} from Python 3.13+
157
+ # <https://MomentsInGraphics.De/FMA.html>
158
+ # >>> a = 1.00000011920929
159
+ # >>> b = 53400708
160
+ # >>> c = -b
161
+ # >>> _fma(a, b, c)
162
+ # 6.365860485903399
163
+ # >>> (a * b) + c
164
+ # 6.3658604845404625
165
+
166
+ def _as_n_d(x):
167
+ try:
168
+ if _isfinite(x):
169
+ # int.as_integer_ratio since 3.8
170
+ return x.as_integer_ratio()
171
+ except (AttributeError, OverflowError, TypeError, ValueError):
172
+ pass
173
+ return float(x), 1
174
+
175
+ (na, da), (nb, db), (nc, dc) = map(_as_n_d, a_b_c)
176
+ n = na * nb * dc + da * db * nc
177
+ d = da * db * dc
178
+ return float(n / d)
179
+
180
+ def _2products(x, y3s, two=False): # PYCHOK redef
181
+ # TwoProduct U{Algorithm 3.3
182
+ # <https://www.TUHH.De/ti3/paper/rump/OgRuOi05.pdf>}
183
+ _, a, b = _2split3(x)
184
+ for y, c, d in y3s:
185
+ y *= x
186
+ yield y
187
+ if two:
188
+ yield b * d - (((y - a * c) - b * c) - a * d)
189
+ # = b * d + (a * d - ((y - a * c) - b * c))
190
+ # = b * d + (a * d + (b * c - (y - a * c)))
191
+ # = b * d + (a * d + (b * c + (a * c - y)))
192
+ else:
193
+ yield a * c - y
194
+ yield b * c
195
+ if d:
196
+ yield a * d
197
+ yield b * d
198
+
199
+ _2FACTOR = pow(2, (MANT_DIG + 1) // 2) + 1
200
+
201
+ def _2split3(x):
202
+ # Split U{Algorithm 3.2
203
+ # <ttps://www.TUHH.De/ti3/paper/rump/OgRuOi05.pdf>}
204
+ a = c = x * _2FACTOR
205
+ a -= c - x
206
+ b = x - a
207
+ return x, a, b
208
+
209
+ def _2split3s(xs): # PYCHOK redef
210
+ return map(_2split3, xs)
211
+
212
+ del MANT_DIG
213
+
214
+
135
215
  def _Fsumf_(*xs): # floats=True, in .auxLat, ...
136
216
  '''(INTERNAL) An C{Fsum} of I{known scalars}.
137
217
  '''
@@ -325,6 +405,8 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
325
405
  file I{Modules/mathmodule.c} and the issue log U{Full precision summation
326
406
  <https://Bugs.Python.org/issue2819>}.
327
407
  '''
408
+ _f2product = _2split3s is _passarg # True for 3.13+
409
+ _math_fma = _fma if _f2product else None
328
410
  _math_fsum = None
329
411
  _n = 0
330
412
  # _ps = [] # partial sums
@@ -467,7 +549,9 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
467
549
  def __hash__(self): # PYCHOK no cover
468
550
  '''Return this instance' C{hash}.
469
551
  '''
470
- return hash(self._ps) # XXX id(self)?
552
+ # @see: U{Notes for type implementors<https://docs.Python.org/
553
+ # 3/library/numbers.html#numbers.Rational>}
554
+ return hash(self.partials) # tuple.__hash__()
471
555
 
472
556
  def __iadd__(self, other):
473
557
  '''Apply C{B{self} += B{other}} to this instance.
@@ -890,6 +974,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
890
974
  f._ps = list(self._ps) # separate list
891
975
  if not deep:
892
976
  f._n = 1
977
+ # assert f._f2product == self._f2product
893
978
  # assert f._Fsum is f
894
979
  return f
895
980
 
@@ -901,6 +986,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
901
986
  f = _Named.copy(self, deep=False, name=n)
902
987
  f._ps = list(self._ps) # separate list
903
988
  # assert f._n == self._n
989
+ # assert f._f2product == self._f2product
904
990
  # assert f._Fsum is f
905
991
  return f
906
992
 
@@ -1111,9 +1197,10 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1111
1197
  # raise self._Error(op, other, _AssertionError, txt__=signOf)
1112
1198
  return DivMod2Tuple(q, self) # q is C{int} in Python 3+, but C{float} in Python 2-
1113
1199
 
1114
- def _fhorner(self, x, cs, op): # in .fmath
1200
+ def _fhorner(self, x, cs, op, incx=True): # in .fmath
1115
1201
  '''(INTERNAL) Add an L{Fhorner} evaluation of polynomial
1116
- M{sum(cs[i] * x**i for i=0..len(cs)-1)}.
1202
+ C{sum(cs[i] * B{x}**i for i=0..len(cs)-1) if B{incx}
1203
+ else sum(... i=len(cs)-1..0)}.
1117
1204
  '''
1118
1205
  if _xiterablen(cs):
1119
1206
  H = Fsum(name__=self._fhorner)
@@ -1123,11 +1210,11 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1123
1210
  _mul = H._mul_scalar
1124
1211
  x = _2float(x=x)
1125
1212
  if len(cs) > 1 and x:
1126
- for c in reversed(cs):
1213
+ for c in (reversed(cs) if incx else cs):
1127
1214
  H._fset_ps(_mul(x, op))
1128
1215
  H._fadd(c, op, up=False)
1129
1216
  else: # x == 0
1130
- H = cs[0]
1217
+ H = cs[0] if cs else _0_0
1131
1218
  self._fadd(H, op)
1132
1219
  return self
1133
1220
 
@@ -1222,6 +1309,38 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1222
1309
  q = self._ftruediv(other, op, **raiser_RESIDUAL) # == self
1223
1310
  return self._fset(q.floor) # floor(q)
1224
1311
 
1312
+ def fma(self, other1, other2): #
1313
+ '''Fused-multiply-add C{self *= B{other1}; self += B{other2}}.
1314
+
1315
+ @arg other1: A C{scalar}, an L{Fsum} or L{Fsum2Tuple} instance.
1316
+ @arg other2: A C{scalar}, an L{Fsum} or L{Fsum2Tuple} instance.
1317
+
1318
+ @note: Uses C{math.fma} in Python 3.13+, provided C{self},
1319
+ B{C{other1}} and B{C{other2}} are all C{scalar}.
1320
+ '''
1321
+ if len(self._ps) == 1 and isscalar(other1, both=True) \
1322
+ and isscalar(other2, both=True):
1323
+ p = _fma(self._ps[0], other1, other2)
1324
+ self._ps[:] = self._finite(p, self.fma.__name__),
1325
+ if other2:
1326
+ self._n += 1
1327
+ else:
1328
+ self._f2mul(self.fma.__name__, other1)
1329
+ self += other2
1330
+ return self
1331
+
1332
+ # def _fma_scalar(self, op, x, *ys): # in .karney
1333
+ # '''(INTERNAL) Apply C{self.fma(B{x}, B{y}) for B{y} in B{ys}}
1334
+ # for scalar C{x} and C{y}s.
1335
+ # '''
1336
+ # ps = self._ps
1337
+ # if ps and ys:
1338
+ # for y in ys:
1339
+ # ps[:] = self._ps_acc(list(y), _2products(x, _2split3s(ps)))
1340
+ # for p in (ps if op else()):
1341
+ # self._finite(p, op)
1342
+ # return self
1343
+
1225
1344
  fmul = __imul__
1226
1345
 
1227
1346
  def _fmul(self, other, op):
@@ -1232,6 +1351,8 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1232
1351
  f = self._mul_Fsum(other, op)
1233
1352
  elif len(other._ps) != 1: # and len(self._ps) == 1
1234
1353
  f = other._mul_scalar(self._ps[0], op)
1354
+ elif self._f2product: # len(other._ps) == 1
1355
+ f = self._mul_scalar(other._ps[0], op)
1235
1356
  else: # len(other._ps) == len(self._ps) == 1
1236
1357
  f = self._finite(self._ps[0] * other._ps[0])
1237
1358
  else:
@@ -1239,6 +1360,29 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1239
1360
  f = self._mul_scalar(s, op)
1240
1361
  return self._fset(f) # n=len(self) + 1
1241
1362
 
1363
+ def f2mul(self, *others):
1364
+ '''Apply C{B{self} *= B{other} for B{other} in B{others}} where each B{other}
1365
+ is C{scalar}, an L{Fsum} or L{Fsum2Tuple} applying accurate multiplication
1366
+ as if L{f2product<Fsum.f2product>}C{=True}.
1367
+
1368
+ @see: U{Equations 2.3<https://www.TUHH.De/ti3/paper/rump/OzOgRuOi06.pdf>}
1369
+ '''
1370
+ return self._f2mul(self.f2mul.__name__, *others)
1371
+
1372
+ def _f2mul(self, op, *others):
1373
+ '''(INTERNAL) See method C{f2mul}.
1374
+ '''
1375
+ P = _Psum(self._ps)
1376
+ ps = P._ps
1377
+ if ps and others:
1378
+ for p in self._ps_other(op, *others):
1379
+ pfs = _2products(p, _2split3s(ps))
1380
+ ps[:] = P._ps_acc([], pfs, up=False)
1381
+ for p in ps:
1382
+ self._finite(p, op)
1383
+ self._fset(P, op=op)
1384
+ return self
1385
+
1242
1386
  def fover(self, over, **raiser_RESIDUAL):
1243
1387
  '''Apply C{B{self} /= B{over}} and summate.
1244
1388
 
@@ -1276,6 +1420,24 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1276
1420
  f = self._pow(other, other, op, **raiser_RESIDUAL)
1277
1421
  return self._fset(f) # n=max(len(self), 1)
1278
1422
 
1423
+ def f2product(self, *two):
1424
+ '''Turn this instance' accurate I{TwoProduct} multiplication or or off.
1425
+
1426
+ @arg two: If C{True}, turn I{TwoProduct} on, if C{False} off or if
1427
+ C{None} or if omitted, keep the current setting.
1428
+
1429
+ @return: The previous C{f2product} setting (C{bool}).
1430
+
1431
+ @see: On Python 3.13 and later I{TwoProduct} is based on I{TwoProductFMA}
1432
+ U{Algorithm 3.5<https://www.TUHH.De/ti3/paper/rump/OgRuOi05.pdf>}
1433
+ otherwise on the slower I{TwoProduct} and I{Split} U{Algorithms
1434
+ 3.3 and 3.2<https://www.TUHH.De/ti3/paper/rump/OgRuOi05.pdf>}.
1435
+ '''
1436
+ t = self._f2product
1437
+ if two and two[0] is not None:
1438
+ self._f2product = bool(two[0])
1439
+ return t
1440
+
1279
1441
  @Property
1280
1442
  def _fprs(self):
1281
1443
  '''(INTERNAL) Get and cache this instance' precision
@@ -1834,10 +1996,16 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1834
1996
  def _pfs(ps, fs):
1835
1997
  if len(ps) < len(fs):
1836
1998
  ps, fs = fs, ps
1999
+ if self._f2product:
2000
+ ps = tuple(_2split3s(ps))
2001
+ _xys = _2products
2002
+ else:
2003
+ def _xys(x, ys):
2004
+ return (x * y for y in ys)
2005
+
1837
2006
  _fin = _isfinite
1838
2007
  for f in fs:
1839
- for p in ps:
1840
- p *= f
2008
+ for p in _xys(f, ps):
1841
2009
  yield p if _fin(p) else self._finite(p, op)
1842
2010
 
1843
2011
  return Fsum()._facc_scalar(_pfs(self._ps, factors), up=False)
@@ -1849,6 +2017,16 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1849
2017
  for p in self._ps:
1850
2018
  yield -p
1851
2019
 
2020
+ def _ps_other(self, op, *others):
2021
+ '''(INTERNAL) Yield the partials of all C{other}s.
2022
+ '''
2023
+ for other in others:
2024
+ if _isFsumTuple(other):
2025
+ for p in other._ps:
2026
+ yield p
2027
+ else:
2028
+ yield self._scalar(other, op)
2029
+
1852
2030
  def _ps_1sum(self, *less):
1853
2031
  '''(INTERNAL) Return the partials sum, 1-primed C{less} some scalars.
1854
2032
  '''
@@ -1970,8 +2148,8 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1970
2148
  def signOf(self, res=True):
1971
2149
  '''Determine the sign of this instance.
1972
2150
 
1973
- @kwarg res: If C{True} consider, otherwise
1974
- ignore the residual (C{bool}).
2151
+ @kwarg res: If C{True}, consider, otherwise ignore
2152
+ the residual (C{bool}).
1975
2153
 
1976
2154
  @return: The sign (C{int}, -1, 0 or +1).
1977
2155
  '''
@@ -1992,7 +2170,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1992
2170
  def toStr(self, lenc=True, **prec_sep_fmt): # PYCHOK signature
1993
2171
  '''Return this C{Fsum} instance as string.
1994
2172
 
1995
- @kwarg lenc: If C{True} include the current C{[len]} of this
2173
+ @kwarg lenc: If C{True}, include the current C{[len]} of this
1996
2174
  L{Fsum} enclosed in I{[brackets]} (C{bool}).
1997
2175
  @kwarg prec_sep_fmt: Optional keyword arguments for method
1998
2176
  L{Fsum2Tuple.toStr}.
@@ -2034,6 +2212,15 @@ def _Float_Int(arg, **name_Error):
2034
2212
  return U(arg, **name_Error)
2035
2213
 
2036
2214
 
2215
+ def Fsum2product(*xs, **name_RESIDUAL):
2216
+ '''Return an L{Fsum} with L{f2product<Fsum.f2product>} accurate
2217
+ multiplication I{turned on}.
2218
+ '''
2219
+ F = Fsum(*xs, **name_RESIDUAL)
2220
+ F.f2product(True)
2221
+ return F
2222
+
2223
+
2037
2224
  class DivMod2Tuple(_NamedTuple):
2038
2225
  '''2-Tuple C{(div, mod)} with the quotient C{div} and remainder
2039
2226
  C{mod} results of a C{divmod} operation.
pygeodesy/gars.py CHANGED
@@ -18,8 +18,9 @@ Transcoded from I{Charles Karney}'s C++ class U{GARS
18
18
  from pygeodesy.constants import _off90, _1_over, _0_5, \
19
19
  _1_0 # PYCHOK used!
20
20
  from pygeodesy.errors import _ValueError, _xkwds, _xStrError
21
- from pygeodesy.interns import NN, _0to9_, _AtoZnoIO_, _COMMA_, _SPACE_
22
- from pygeodesy.lazily import _ALL_LAZY, _ALL_OTHER
21
+ from pygeodesy.interns import NN, _0to9_, _AtoZnoIO_, _COMMA_, \
22
+ _INV_, _SPACE_
23
+ from pygeodesy.lazily import _ALL_DOCS, _ALL_LAZY
23
24
  from pygeodesy.named import _name__, Fmt, isstr, Property_RO
24
25
  from pygeodesy.namedTuples import LatLon2Tuple, LatLonPrec3Tuple
25
26
  # from pygeodesy.props import Property_RO # from .named
@@ -29,7 +30,7 @@ from pygeodesy.units import Int_, Lat, Lon, Precision_, Scalar_, Str
29
30
  from math import floor
30
31
 
31
32
  __all__ = _ALL_LAZY.gars
32
- __version__ = '24.08.02'
33
+ __version__ = '24.08.13'
33
34
 
34
35
  _Digits = _0to9_
35
36
  _LatLen = 2
@@ -86,7 +87,7 @@ def _2garstr2(garef):
86
87
  try:
87
88
  n, garstr = len(garef), garef.upper()
88
89
  if n < _MinLen or n > _MaxLen \
89
- or garstr[:3] == 'INV' \
90
+ or garstr.startswith(_INV_) \
90
91
  or not garstr.isalnum():
91
92
  raise ValueError()
92
93
  return garstr, _2Precision(n - _MinLen)
@@ -200,8 +201,8 @@ def decode3(garef, center=True, **name):
200
201
  '''Decode a C{garef} to lat-, longitude and precision.
201
202
 
202
203
  @arg garef: To be decoded (L{Garef} or C{str}).
203
- @kwarg center: If C{True} the center, otherwise the south-west,
204
- lower-left corner (C{bool}).
204
+ @kwarg center: If C{True}, use the garef's center, otherwise
205
+ the south-west, lower-left corner (C{bool}).
205
206
 
206
207
  @return: A L{LatLonPrec3Tuple}C{(lat, lon, precision)}.
207
208
 
@@ -339,8 +340,8 @@ def resolution(prec):
339
340
  return _Resolutions[max(0, min(p, _MaxPrec))]
340
341
 
341
342
 
342
- __all__ += _ALL_OTHER(decode3, # functions
343
- encode, precision, resolution)
343
+ __all__ += _ALL_DOCS(decode3, # functions
344
+ encode, precision, resolution)
344
345
 
345
346
  # **) MIT License
346
347
  #
pygeodesy/geodesici.py CHANGED
@@ -28,8 +28,9 @@ from __future__ import division as _; del _ # PYCHOK semicolon
28
28
  from pygeodesy.basics import _copy, _enumereverse, map1, \
29
29
  _xinstanceof, _xor
30
30
  from pygeodesy.constants import EPS, INF, INT0, PI, PI2, PI_4, \
31
- _0_0, _0_5, _1_0, _1_5, _2_0, _3_0, \
32
- _90_0, isfinite
31
+ _0_0, _0_5, _1_0, _1_5, _2_0, \
32
+ _3_0, _64_0, _90_0, isfinite, \
33
+ _EPSjam # PYCHOK used!
33
34
  from pygeodesy.ellipsoids import _EWGS84, Fmt, unstr
34
35
  from pygeodesy.errors import GeodesicError, IntersectionError, _an, \
35
36
  _xgeodesics, _xkwds_get, _xkwds_kwds, \
@@ -56,7 +57,7 @@ from pygeodesy.utily import sincos2, atan2, fabs, radians
56
57
  # from math import atan2, ceil as _ceil, fabs, radians # .fsums, .utily
57
58
 
58
59
  __all__ = _ALL_LAZY.geodesici
59
- __version__ = '24.07.25'
60
+ __version__ = '24.08.28'
60
61
 
61
62
  _0t = 0, # int
62
63
  _1_1t = -1, +1
@@ -1237,7 +1238,7 @@ class Intersector(_IntersectBase):
1237
1238
  return sx, zx, sAx, sBx
1238
1239
 
1239
1240
  def _polarB3(self, lats=False): # PYCHOK no cover
1240
- latx = 64.0
1241
+ latx = _64_0
1241
1242
  lat = _90_0 - latx
1242
1243
  if self.f:
1243
1244
  _d, _pD2 = fdot, self._polarDist2
@@ -1393,7 +1394,7 @@ class Intersector(_IntersectBase):
1393
1394
 
1394
1395
  @Property_RO
1395
1396
  def _Tol(self): # convergence tolerance
1396
- return self._cHalf * pow(EPS, 0.75) # _0_75
1397
+ return self._cHalf * _EPSjam
1397
1398
 
1398
1399
  def toStr(self, **prec_sep_name): # PYCHOK signature
1399
1400
  '''Return this C{Intersector} as string.
@@ -13,7 +13,7 @@ U{GeographicLib<https://GeographicLib.SourceForge.io>} documentation.
13
13
  from pygeodesy.geodesicx.gxbases import _Gfloats, _f, _f2
14
14
 
15
15
  __all__ = ()
16
- __version__ = '23.08.19'
16
+ __version__ = '24.09.04'
17
17
 
18
18
  _g = _Gfloats(24)
19
19
  _coeffs_24 = _g(( # GEOGRAPHICLIB_GEODESICEXACT_ORDER == 24
@@ -1672,8 +1672,6 @@ _coeffs_24 = _g(( # GEOGRAPHICLIB_GEODESICEXACT_ORDER == 24
1672
1672
  # C4[23], coeff of eps^23, polynomial in n of order 0
1673
1673
  _f(1 << 41), _f(0xc5e28ed2c935ab), # PYCHOK exported
1674
1674
  )) # 2900 / 2708
1675
- if __name__ == '__main__':
1676
- _g.prints()
1677
1675
  del _g, _Gfloats, _f, _f2
1678
1676
 
1679
1677
  # **) MIT License
@@ -13,7 +13,7 @@ U{GeographicLib<https://GeographicLib.SourceForge.io>} documentation.
13
13
  from pygeodesy.geodesicx.gxbases import _Gfloats, _f, _f2
14
14
 
15
15
  __all__ = ()
16
- __version__ = '23.08.19'
16
+ __version__ = '24.09.04'
17
17
 
18
18
  _g = _Gfloats(27)
19
19
  _coeffs_27 = _g(( # GEOGRAPHICLIB_GEODESICEXACT_ORDER == 27
@@ -2368,8 +2368,6 @@ _coeffs_27 = _g(( # GEOGRAPHICLIB_GEODESICEXACT_ORDER == 27
2368
2368
  # C4[26], coeff of eps^26, polynomial in n of order 0
2369
2369
  _f(1 << 48), _f2(2126, 0x8c0e9e949456f), # PYCHOK exported
2370
2370
  )) # 4032 / 3764
2371
- if __name__ == '__main__':
2372
- _g.prints()
2373
2371
  del _g, _Gfloats, _f, _f2
2374
2372
 
2375
2373
  # **) MIT License
@@ -30,7 +30,7 @@ U{GeographicLib<https://GeographicLib.SourceForge.io>} documentation.
30
30
  from pygeodesy.geodesicx.gxbases import _Gfloats, _f, _f2
31
31
 
32
32
  __all__ = ()
33
- __version__ = '23.08.19'
33
+ __version__ = '24.09.04'
34
34
 
35
35
  _g = _Gfloats(30)
36
36
  _coeffs_30 = _g(( # GEOGRAPHICLIB_GEODESICEXACT_ORDER == 30
@@ -3273,8 +3273,6 @@ _coeffs_30 = _g(( # GEOGRAPHICLIB_GEODESICEXACT_ORDER == 30
3273
3273
  # C4[29], coeff of eps^29, polynomial in n of order 0
3274
3274
  _f(1 << 53), _f2(88602, 0xec373d36a45df), # PYCHOK exported
3275
3275
  )) # 5425 / 5107
3276
- if __name__ == '__main__':
3277
- _g.prints()
3278
3276
  del _g, _Gfloats, _f, _f2
3279
3277
 
3280
3278
 
@@ -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.07.09'
26
+ __version__ = '24.09.06'
27
27
 
28
28
  # **) MIT License
29
29
  #
@@ -5,65 +5,63 @@ u'''Print L{geodesicx} version, etc. using C{python -m pygeodesy.geodesicx}.
5
5
  '''
6
6
 
7
7
  __all__ = ()
8
- __version__ = '24.05.31'
8
+ __version__ = '24.09.06'
9
9
 
10
10
 
11
- def _C4stats(nC4=None): # PYCHOK no cover
12
- '''(INTERNAL) Get the C{C4} stats.
13
- '''
14
- from pygeodesy import GeodesicExact, geodesicx
15
-
16
- gX = GeodesicExact(C4order=nC4)
17
- cs = geodesicx.gx._C4coeffs(gX.C4order)
18
- ss = set(cs) # without duplicates
19
- pc = '%.1f%%' % (len(ss) * 100.0 / len(cs))
20
- cx = gX._C4x
21
- return dict(C4order=gX.C4order, C4len=len(cs), C4set=len(ss), C4set_len=pc, C4x=len(cx))
22
-
23
-
24
- def _main(): # PYCHOK no cover
25
-
26
- import os.path as os_path
11
+ def _main(**C4order): # PYCHOK no cover
27
12
 
28
13
  try:
29
- from pygeodesy import geodesicx as _gx, GeodesicError, \
30
- GeodesicSolve, printf, pygeodesy_abspath
31
- from pygeodesy.internals import _Pythonarchine, _usage
32
- from pygeodesy.interns import _COMMASPACE_, _DOT_, _SPACE_, _version_
33
- from pygeodesy.streprs import Fmt
34
-
35
- def _dot_attr(name, value):
36
- return Fmt.DOT(Fmt.EQUAL(name, value))
37
-
38
- s = tuple(sorted(_C4stats().items()))
39
- p = [_dot_attr(*t) for t in (((_version_, _gx.__version__),) + s)]
40
-
41
- def _name_version(pkg):
42
- return _SPACE_(pkg.__name__, pkg.__version__)
43
-
44
- v = _Pythonarchine()
14
+ from pygeodesy import GeodesicExact, geodesicx
15
+ from pygeodesy.internals import _fper, _name_version, \
16
+ printf, _sizeof, _versions
17
+ from pygeodesy.interns import _COMMASPACE_, _EQUAL_
45
18
  try:
46
- import geographiclib
47
- v.append(_name_version(geographiclib))
19
+ import numpy
48
20
  except ImportError:
49
- pass
21
+ numpy = None
22
+
23
+ gX = GeodesicExact(**C4order)
24
+ cs = geodesicx.gx._C4coeffs(gX.C4order)
25
+ n = len(cs)
26
+ u = n if numpy else len(set(cs))
27
+ z = cs.nbytes if numpy else _sizeof(cs)
28
+ p = dict(C4order=gX.C4order, C4n=n, C4u=u,
29
+ C4u_n=_fper(u, n), C4x=len(gX._C4x),
30
+ C4t=type(cs).__name__, C4z=z)
31
+ p = list(_EQUAL_(*t) for t in p.items())
32
+ if numpy:
33
+ p.append(_name_version(numpy))
50
34
  try:
51
- g = GeodesicSolve()
52
- v.append(g.version)
53
- except GeodesicError:
35
+ import geographiclib
36
+ p.append(_name_version(geographiclib))
37
+ except ImportError:
54
38
  pass
55
39
 
56
- g = _gx.__name__
57
- x = os_path.basename(pygeodesy_abspath)
58
- if not g.startswith(x):
59
- g = _DOT_(x, g)
60
- printf('%s%s (%s)', g, _COMMASPACE_.join(p), _COMMASPACE_.join(v))
40
+ g = _name_version(geodesicx)
41
+ printf('%s: %s (%s)', g, _COMMASPACE_.join(p), _versions())
61
42
 
62
43
  except ImportError:
63
- printf(_usage(__file__))
44
+ from pygeodesy.internals import _usage
45
+ print(_usage(__file__))
46
+
47
+
48
+ from sys import argv # .internals._isPyChecker
49
+ _main(C4order=int(argv[1])) if len(argv) == 2 and argv[1].isdigit() else _main()
50
+
51
+ # % python3.13 -m pygeodesy.geodesicx 30
52
+ # pygeodesy.geodesicx 24.09.06: C4order=30, C4n=5425, C4u=5107, C4u_n=94.1%, C4x=465, C4t=tuple, C4z=166008 (pygeodesy 24.9.6 Python 3.13.0rc1 64bit arm64 macOS 14.6.1)
53
+ # % python3.12 -m pygeodesy.geodesicx 30
54
+ # pygeodesy.geodesicx 24.09.06: C4order=30, C4n=5425, C4u=5425, C4u_n=100.0%, C4x=465, C4t=ndarray, C4z=43400, numpy 2.1.0, geographiclib 2.0 (pygeodesy 24.9.6 Python 3.12.5 64bit arm64 macOS 14.6.1)
64
55
 
56
+ # % python3.13 -m pygeodesy.geodesicx 27
57
+ # pygeodesy.geodesicx 24.09.06: C4order=27, C4n=4032, C4u=3764, C4u_n=93.4%, C4x=378, C4t=tuple, C4z=122632 (pygeodesy 24.9.6 Python 3.13.0rc1 64bit arm64 macOS 14.6.1)
58
+ # % python3.12 -m pygeodesy.geodesicx 27
59
+ # pygeodesy.geodesicx 24.09.06: C4order=27, C4n=4032, C4u=4032, C4u_n=100.0%, C4x=378, C4t=ndarray, C4z=32256, numpy 2.1.0, geographiclib 2.0 (pygeodesy 24.9.6 Python 3.12.5 64bit arm64 macOS 14.6.1)
65
60
 
66
- _main()
61
+ # % python3.13 -m pygeodesy.geodesicx 24
62
+ # pygeodesy.geodesicx 24.09.06: C4order=24, C4n=2900, C4u=2708, C4u_n=93.4%, C4x=300, C4t=tuple, C4z=88232 (pygeodesy 24.9.6 Python 3.13.0rc1 64bit arm64 macOS 14.6.1)
63
+ # % python3.12 -m pygeodesy.geodesicx 24
64
+ # pygeodesy.geodesicx 24.09.06: C4order=24, C4n=2900, C4u=2900, C4u_n=100.0%, C4x=300, C4t=ndarray, C4z=23200, numpy 2.1.0, geographiclib 2.0 (pygeodesy 24.9.6 Python 3.12.5 64bit arm64 macOS 14.6.1)
67
65
 
68
66
  # **) MIT License
69
67
  #
pygeodesy/geodesicx/gx.py CHANGED
@@ -63,7 +63,7 @@ from pygeodesy.utily import atan2d as _atan2d_reverse, _unrollon, _Wrap, wrap360
63
63
  from math import atan2, copysign, cos, degrees, fabs, radians, sqrt
64
64
 
65
65
  __all__ = ()
66
- __version__ = '24.07.11'
66
+ __version__ = '24.08.13'
67
67
 
68
68
  _MAXIT1 = 20
69
69
  _MAXIT2 = 10 + _MAXIT1 + MANT_DIG # MANT_DIG == C++ digits
@@ -218,8 +218,8 @@ class GeodesicExact(_GeodesicBase):
218
218
  '''Set up a L{GeodesicAreaExact} to compute area and
219
219
  perimeter of a polygon.
220
220
 
221
- @kwarg polyline: If C{True} perimeter only, otherwise
222
- area and perimeter (C{bool}).
221
+ @kwarg polyline: If C{True}, compute the perimeter only, otherwise
222
+ the perimeter and area (C{bool}).
223
223
  @kwarg name: Optional C{B{name}=NN} (C{str}).
224
224
 
225
225
  @return: A L{GeodesicAreaExact} instance.
@@ -21,7 +21,7 @@ from pygeodesy.basics import isodd, unsigned0
21
21
  from pygeodesy.constants import NAN, _0_0, _0_5, _720_0
22
22
  # from pygeodesy.interns import _COMMASPACE_ # from .lazily
23
23
  from pygeodesy.karney import Area3Tuple, _diff182, GeodesicError, \
24
- _norm180, _remainder, _sum2_
24
+ _norm180, _remainder, _sum3
25
25
  from pygeodesy.lazily import _ALL_DOCS, printf, _COMMASPACE_
26
26
  from pygeodesy.named import ADict, callername, _NamedBase, pairs
27
27
  from pygeodesy.props import Property, Property_RO, property_RO
@@ -30,7 +30,7 @@ from pygeodesy.props import Property, Property_RO, property_RO
30
30
  from math import fmod as _fmod
31
31
 
32
32
  __all__ = ()
33
- __version__ = '24.05.19'
33
+ __version__ = '24.09.04'
34
34
 
35
35
 
36
36
  class GeodesicAreaExact(_NamedBase):
@@ -413,8 +413,8 @@ class PolygonArea(GeodesicAreaExact):
413
413
 
414
414
  @arg earth: A geodesic (L{GeodesicExact}, I{wrapped}
415
415
  C{Geodesic} or L{GeodesicSolve}).
416
- @kwarg polyline: If C{True} perimeter only, otherwise
417
- area and perimeter (C{bool}).
416
+ @kwarg polyline: If C{True}, compute the perimeter only, otherwise
417
+ perimeter and area (C{bool}).
418
418
  @kwarg name: Optional C{B{name}=NN} (C{str}).
419
419
 
420
420
  @raise GeodesicError: Invalid B{C{earth}}.
@@ -451,7 +451,7 @@ class _Accumulator(_NamedBase):
451
451
  @return: Current C{sum}.
452
452
  '''
453
453
  self._n += 1
454
- self._s, self._t = _sum2_(self._s, self._t, y)
454
+ self._s, self._t, _ = _sum3(self._s, self._t, y)
455
455
  return self._s # current .Sum()
456
456
 
457
457
  def Negate(self):