pygeodesy 25.12.12__py2.py3-none-any.whl → 26.1.16__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.
- pygeodesy/__init__.py +33 -25
- pygeodesy/albers.py +5 -5
- pygeodesy/cartesianBase.py +2 -2
- pygeodesy/constants.py +8 -1
- pygeodesy/datums.py +5 -8
- pygeodesy/ellipsoids.py +32 -19
- pygeodesy/elliptic.py +532 -139
- pygeodesy/fmath.py +6 -8
- pygeodesy/formy.py +6 -106
- pygeodesy/fsums.py +48 -30
- pygeodesy/geod3solve.py +26 -10
- pygeodesy/geodesici.py +5 -4
- pygeodesy/geodesicx/__init__.py +1 -1
- pygeodesy/geodesicx/gxarea.py +53 -23
- pygeodesy/geodsolve.py +4 -3
- pygeodesy/internals.py +10 -3
- pygeodesy/karney.py +21 -36
- pygeodesy/lazily.py +12 -5
- pygeodesy/lcc.py +3 -7
- pygeodesy/named.py +14 -10
- pygeodesy/props.py +5 -3
- pygeodesy/trf.py +2 -4
- pygeodesy/triaxials/bases.py +111 -58
- pygeodesy/triaxials/triaxial3.py +50 -54
- pygeodesy/triaxials/triaxial5.py +17 -49
- pygeodesy/utily.py +7 -7
- {pygeodesy-25.12.12.dist-info → pygeodesy-26.1.16.dist-info}/METADATA +26 -19
- {pygeodesy-25.12.12.dist-info → pygeodesy-26.1.16.dist-info}/RECORD +30 -30
- {pygeodesy-25.12.12.dist-info → pygeodesy-26.1.16.dist-info}/WHEEL +0 -0
- {pygeodesy-25.12.12.dist-info → pygeodesy-26.1.16.dist-info}/top_level.txt +0 -0
pygeodesy/fmath.py
CHANGED
|
@@ -10,8 +10,8 @@ from __future__ import division as _; del _ # noqa: E702 ;
|
|
|
10
10
|
from pygeodesy.basics import _copysign, copysign0, isbool, isint, isodd, \
|
|
11
11
|
isscalar, len2, map1, _xiterable, typename
|
|
12
12
|
from pygeodesy.constants import EPS0, EPS02, EPS1, NAN, PI, PI_2, PI_4, \
|
|
13
|
-
_0_0, _0_125, _0_25,
|
|
14
|
-
_copysign_0_0, isfinite, remainder
|
|
13
|
+
_0_0, _0_125, _0_25, _1_3rd, _0_5, _2_3rd, \
|
|
14
|
+
_1_0, _1_5, _copysign_0_0, isfinite, remainder
|
|
15
15
|
from pygeodesy.errors import _IsnotError, LenError, _TypeError, _ValueError, \
|
|
16
16
|
_xError, _xkwds, _xkwds_pop2, _xsError
|
|
17
17
|
from pygeodesy.fsums import _2float, Fsum, fsum, _isFsum_2Tuple, Fmt, unstr
|
|
@@ -22,16 +22,14 @@ from pygeodesy.lazily import _ALL_LAZY, _ALL_MODS as _MODS
|
|
|
22
22
|
from pygeodesy.units import Int_, _isHeight, _isRadius
|
|
23
23
|
|
|
24
24
|
from math import fabs, sqrt # pow
|
|
25
|
-
import operator as _operator # in .datums, .trf, .utm
|
|
25
|
+
import operator as _operator # in .datums, .elliptic, .trf, .utm
|
|
26
26
|
|
|
27
27
|
__all__ = _ALL_LAZY.fmath
|
|
28
|
-
__version__ = '
|
|
28
|
+
__version__ = '26.01.06'
|
|
29
29
|
|
|
30
30
|
# sqrt(2) - 1 <https://WikiPedia.org/wiki/Square_root_of_2>
|
|
31
31
|
_0_4142 = 0.41421356237309504880 # ~ 3_730_904_090_310_553 / 9_007_199_254_740_992
|
|
32
|
-
|
|
33
|
-
_1_6th = _1_0 / 6
|
|
34
|
-
_2_3rd = _1_3rd * 2
|
|
32
|
+
_1_6th = 1 / 6
|
|
35
33
|
_h_lt_b_ = 'abs(h) < abs(b)'
|
|
36
34
|
|
|
37
35
|
|
|
@@ -1049,7 +1047,7 @@ def sqrt3(x):
|
|
|
1049
1047
|
|
|
1050
1048
|
|
|
1051
1049
|
def sqrt_a(h, b):
|
|
1052
|
-
'''Compute C{I{a}} side of a right-angled triangle from
|
|
1050
|
+
'''Compute the C{I{a}} side of a right-angled triangle from
|
|
1053
1051
|
C{sqrt(B{h}**2 - B{b}**2)}.
|
|
1054
1052
|
|
|
1055
1053
|
@arg h: Hypotenuse or outer annulus radius (C{scalar}).
|
pygeodesy/formy.py
CHANGED
|
@@ -11,7 +11,7 @@ from pygeodesy.basics import _copysign, _isin # _args_kwds_count2
|
|
|
11
11
|
from pygeodesy.constants import EPS, EPS0, EPS1, PI, PI2, PI3, PI_2, R_M, \
|
|
12
12
|
_0_0s, float0_, isnon0, remainder, _umod_PI2, \
|
|
13
13
|
_0_0, _0_125, _0_25, _0_5, _1_0, _2_0, _4_0, \
|
|
14
|
-
_90_0, _180_0, _360_0
|
|
14
|
+
_90_0, _180_0, _360_0
|
|
15
15
|
from pygeodesy.datums import Datum, Ellipsoid, _ellipsoidal_datum, \
|
|
16
16
|
_mean_radius, _spherical_datum, _WGS84, _EWGS84
|
|
17
17
|
# from pygeodesy.ellipsoids import Ellipsoid, _EWGS84 # from .datums
|
|
@@ -23,7 +23,8 @@ from pygeodesy.fsums import fsumf_, Fmt, unstr
|
|
|
23
23
|
# from pygeodesy.internals import typename # from .named
|
|
24
24
|
from pygeodesy.interns import _delta_, _distant_, _inside_, _SPACE_, _too_
|
|
25
25
|
from pygeodesy.lazily import _ALL_LAZY, _ALL_MODS as _MODS
|
|
26
|
-
from pygeodesy.named import _name__, _name2__, _NamedTuple, _xnamed,
|
|
26
|
+
from pygeodesy.named import _name__, _name2__, _NamedTuple, _xnamed, \
|
|
27
|
+
typename
|
|
27
28
|
from pygeodesy.namedTuples import Bearing2Tuple, Distance4Tuple, LatLon2Tuple, \
|
|
28
29
|
Intersection3Tuple, PhiLam2Tuple
|
|
29
30
|
# from pygeodesy.streprs import Fmt, unstr # from .fsums
|
|
@@ -42,11 +43,10 @@ from contextlib import contextmanager
|
|
|
42
43
|
from math import atan, cos, degrees, fabs, radians, sin, sqrt # pow
|
|
43
44
|
|
|
44
45
|
__all__ = _ALL_LAZY.formy
|
|
45
|
-
__version__ = '
|
|
46
|
+
__version__ = '26.01.06'
|
|
46
47
|
|
|
47
48
|
_RADIANS2 = radians(_1_0)**2 # degree to radians-squared
|
|
48
49
|
_ratio_ = 'ratio'
|
|
49
|
-
_TOL53 = sqrt(pow(_0_5, _DIG53)) # elliperim
|
|
50
50
|
_xline_ = 'xline'
|
|
51
51
|
|
|
52
52
|
|
|
@@ -368,107 +368,6 @@ def _dS(fun_, radius, wrap, *lls, **adjust):
|
|
|
368
368
|
return r * radius
|
|
369
369
|
|
|
370
370
|
|
|
371
|
-
def elliperim(a, b):
|
|
372
|
-
'''Compute the perimeter of an ellipse with semi-axes C{a} and C{b} using U{SciPy's
|
|
373
|
-
ellipe<https://www.JohnDCook.com/perimeter_ellipse.html>} function or the U{AGM
|
|
374
|
-
<https://PaulBourke.net/geometry/ellipsecirc>} (Arithmetic Geometric Mean) method.
|
|
375
|
-
|
|
376
|
-
@return: The perimeter (C{scalar}, same units as C{a} and C{b}).
|
|
377
|
-
'''
|
|
378
|
-
if a < b:
|
|
379
|
-
a, b = b, a
|
|
380
|
-
if 0 < b < a:
|
|
381
|
-
try:
|
|
382
|
-
from scipy.special import ellipe
|
|
383
|
-
a *= float(ellipe(_1_0 - (b / a)**2)) * _4_0
|
|
384
|
-
except (AttributeError, ImportError):
|
|
385
|
-
# relative accuracy is about _TOL53**2
|
|
386
|
-
if (b * _DIG53) > (a * _TOL53):
|
|
387
|
-
c = a + b
|
|
388
|
-
d = a - b
|
|
389
|
-
m, s = -1, [c**2]
|
|
390
|
-
_s = s.append
|
|
391
|
-
while d > (b * _TOL53) and len(s) < 32: # 4..5 trips
|
|
392
|
-
b = sqrt(a * b)
|
|
393
|
-
a = c * _0_5
|
|
394
|
-
c = a + b
|
|
395
|
-
d = a - b
|
|
396
|
-
m *= 2
|
|
397
|
-
_s(m * d**2)
|
|
398
|
-
a = fsumf_(*s) * PI / c
|
|
399
|
-
else: # near flat
|
|
400
|
-
a *= _4_0
|
|
401
|
-
elif b < 0:
|
|
402
|
-
raise _ValueError(unstr(elliperim, a, b))
|
|
403
|
-
else: # circle or flat
|
|
404
|
-
a *= PI2 if b else _4_0
|
|
405
|
-
return a
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
# def elliperimR2(a, b):
|
|
409
|
-
# '''Compute the perimeter of an ellipse with semi-axes C{a} and C{b} using
|
|
410
|
-
# Ramanujan's U{2nd approximation<https://PaulBourke.net/geometry/ellipsecirc>}.
|
|
411
|
-
# '''
|
|
412
|
-
# if a < 0 or b < 0:
|
|
413
|
-
# raise ValueError(unstr(elliperimR2, a, b))
|
|
414
|
-
# p = a + b
|
|
415
|
-
# if p:
|
|
416
|
-
# t = ((a - b) / p)**2 * _3_0
|
|
417
|
-
# t = t / (_10_0 + sqrt(_4_0 - t)) + _1_0
|
|
418
|
-
# p *= t * PI
|
|
419
|
-
# return p
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
# def elliperim4arc3(a, b):
|
|
423
|
-
# '''Compute the perimeter of an ellipse with semi-axes C{a} and C{b} using
|
|
424
|
-
# the U{4 arc approximation<https://PaulBourke.net/geometry/ellipsecirc>}.
|
|
425
|
-
#
|
|
426
|
-
# @return: 3-Tuple C{(p, ra, rb)} with perimeter C{p}, arc radius C{ra}
|
|
427
|
-
# at the major and arc radius C{rb} at the minor semi-axes.
|
|
428
|
-
# '''
|
|
429
|
-
# _r = a < b
|
|
430
|
-
# if _r:
|
|
431
|
-
# a, b = b, a
|
|
432
|
-
# if 0 < b < a:
|
|
433
|
-
# h = hypot(a, b)
|
|
434
|
-
# L = (h - b) * _0_5
|
|
435
|
-
# p = atan2(b, a)
|
|
436
|
-
# s, c = sincos2(p)
|
|
437
|
-
# ra = L / c
|
|
438
|
-
# rb = (h - L) / s
|
|
439
|
-
# p = rb * p + ra * (PI_2 - p)
|
|
440
|
-
# elif b < 0:
|
|
441
|
-
# raise ValueError(unstr(elliperim4arc3, a, b))
|
|
442
|
-
# elif b == a:
|
|
443
|
-
# ra = rb = a
|
|
444
|
-
# p = a * PI_2
|
|
445
|
-
# else: # b == 0
|
|
446
|
-
# ra, rb = _0_0, a
|
|
447
|
-
# p = a
|
|
448
|
-
# p *= _4_0
|
|
449
|
-
# if _r:
|
|
450
|
-
# ra, rb = rb, ra
|
|
451
|
-
# return p, ra, rb
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
# def elliperimGKS(a, b):
|
|
455
|
-
# '''Compute the perimeter of an ellipse with semi-axes C{a} and C{b} using the U{Gauss-Kummer
|
|
456
|
-
# Series<https://www.JohnDCook.com/blog/2023/05/28/approximate-ellipse-perimeter/>}.
|
|
457
|
-
# '''
|
|
458
|
-
# if a < b:
|
|
459
|
-
# a, b = b, a
|
|
460
|
-
# if b < 0:
|
|
461
|
-
# raise ValueError(unstr(elliperimGKS, a, b))
|
|
462
|
-
# if b:
|
|
463
|
-
# p = a + b
|
|
464
|
-
# h = (a - b) / p
|
|
465
|
-
# h *= h
|
|
466
|
-
# p *= (1 + h * (1 / 4 + h * (1 / 64 + h * (1 / 256 + h * (25 / 16384 + h * (49 / 65536)))))) * PI
|
|
467
|
-
# else:
|
|
468
|
-
# p = _4_0 * a
|
|
469
|
-
# return p
|
|
470
|
-
|
|
471
|
-
|
|
472
371
|
def _ellipsoidal(earth, where):
|
|
473
372
|
'''(INTERNAL) Helper for distances.
|
|
474
373
|
'''
|
|
@@ -1028,7 +927,8 @@ def hartzell(pov, los=False, earth=_WGS84, **name_LatLon_and_kwds):
|
|
|
1028
927
|
n, kwds = _name2__(name_LatLon_and_kwds, name__=hartzell)
|
|
1029
928
|
try:
|
|
1030
929
|
D = _spherical_datum(earth, name__=hartzell)
|
|
1031
|
-
|
|
930
|
+
m = _MODS._triaxials_triaxial5
|
|
931
|
+
r, h, i = m._hartzell3(pov, los, D.ellipsoid._triaxial)
|
|
1032
932
|
|
|
1033
933
|
C = _MODS.cartesianBase.CartesianBase
|
|
1034
934
|
if kwds:
|
pygeodesy/fsums.py
CHANGED
|
@@ -5,16 +5,16 @@ u'''Class L{Fsum} for precision floating point summation similar to
|
|
|
5
5
|
Python's C{math.fsum}, but enhanced with I{precision running} summation
|
|
6
6
|
plus optionally, accurate I{TwoProduct} multiplication.
|
|
7
7
|
|
|
8
|
-
Accurate multiplication is based on the C{math.fma} function from
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
8
|
+
Accurate multiplication is based on the C{math.fma} function from Python
|
|
9
|
+
3.13 and newer or an equivalent C{fma} implementation for Python 3.12 and
|
|
10
|
+
older. Set env variable C{PYGEODESY_FSUM_F2PRODUCT} to C{"std"} or any
|
|
11
|
+
non-empty string or invoke function C{pygeodesy.f2product(True)} to enable
|
|
12
|
+
accurate multiplication. With C{"std"} the C{fma} implemention follows
|
|
13
|
+
the C{math.fma} function, otherwise the implementation of the C{PyGeodesy
|
|
14
|
+
24.09.09} release.
|
|
15
15
|
|
|
16
16
|
Generally, an L{Fsum} instance is considered a C{float} plus a small or
|
|
17
|
-
zero C{residue} aka C{residual}
|
|
17
|
+
zero C{residue} aka C{residual}, see property L{Fsum.residual}.
|
|
18
18
|
|
|
19
19
|
Set env variable C{PYGEODESY_FSUM_RESIDUAL} to a C{float} string greater
|
|
20
20
|
than C{"0.0"} as the threshold to throw a L{ResidualError} for a division,
|
|
@@ -28,7 +28,7 @@ L{Fsum.fint2} and L{Fsum.is_integer}. Also, L{Fsum} methods L{Fsum.pow},
|
|
|
28
28
|
L{Fsum.__ipow__}, L{Fsum.__pow__} and L{Fsum.__rpow__} return a (very long)
|
|
29
29
|
C{int} if invoked with optional argument C{mod} set to C{None}. The
|
|
30
30
|
C{residual} of an C{integer} L{Fsum} is between C{-1.0} and C{+1.0} and
|
|
31
|
-
will be C{INT0} if that is
|
|
31
|
+
will be C{INT0} if that L{Fsum} is an I{exact float} or I{exact integer}.
|
|
32
32
|
|
|
33
33
|
Set env variable C{PYGEODESY_FSUM_NONFINITES} to C{"std"} or use function
|
|
34
34
|
C{pygeodesy.nonfiniterrors(False)} to allow I{non-finite} C{float}s like
|
|
@@ -62,7 +62,7 @@ from math import fabs, isinf, isnan, \
|
|
|
62
62
|
ceil as _ceil, floor as _floor # PYCHOK used! .ltp
|
|
63
63
|
|
|
64
64
|
__all__ = _ALL_LAZY.fsums
|
|
65
|
-
__version__ = '
|
|
65
|
+
__version__ = '26.01.16'
|
|
66
66
|
|
|
67
67
|
from pygeodesy.interns import (
|
|
68
68
|
_PLUS_ as _add_op_, # in .auxilats.auxAngle
|
|
@@ -321,8 +321,8 @@ def nonfiniterrors(raiser=None):
|
|
|
321
321
|
'''
|
|
322
322
|
d = Fsum._isfine
|
|
323
323
|
if raiser is not None:
|
|
324
|
-
Fsum._isfine = {} if bool(raiser) else
|
|
325
|
-
return (False if d is
|
|
324
|
+
Fsum._isfine = {} if bool(raiser) else _nonfinites_isfine_kwds[True]
|
|
325
|
+
return (False if d is _nonfinites_isfine_kwds[True] else
|
|
326
326
|
_xkwds_get1(d, _isfine=_isfinite) is _isfinite) if d else True
|
|
327
327
|
|
|
328
328
|
|
|
@@ -370,7 +370,7 @@ def _Psum(ps, **name_f2product_nonfinites_RESIDUAL):
|
|
|
370
370
|
return F
|
|
371
371
|
|
|
372
372
|
|
|
373
|
-
def _Psum_(*ps, **name_f2product_nonfinites_RESIDUAL):
|
|
373
|
+
def _Psum_(*ps, **name_f2product_nonfinites_RESIDUAL):
|
|
374
374
|
'''(INTERNAL) Return an C{Fsum} from I{known scalar} C{ps}.
|
|
375
375
|
'''
|
|
376
376
|
return _Psum(ps, **name_f2product_nonfinites_RESIDUAL)
|
|
@@ -486,7 +486,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase, .fstats
|
|
|
486
486
|
|
|
487
487
|
@note: Handling of I{non-finites} as C{inf}, C{INF}, C{NINF}, C{nan} and C{NAN} is
|
|
488
488
|
determined by function L{nonfiniterrors<fsums.nonfiniterrors>} for the default
|
|
489
|
-
|
|
489
|
+
or by method L{nonfinites<Fsum.nonfinites>} for individual C{Fsum} instances,
|
|
490
490
|
overruling the default. For backward compatibility, I{non-finites} raise
|
|
491
491
|
exceptions by default.
|
|
492
492
|
|
|
@@ -829,7 +829,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase, .fstats
|
|
|
829
829
|
return self._cmp_0(other, _lt_op_ + _fset_op_) <= 0
|
|
830
830
|
|
|
831
831
|
def __len__(self):
|
|
832
|
-
'''Return the number of values accumulated (C{int}).
|
|
832
|
+
'''Return the number of (non-zero) values accumulated (C{int}).
|
|
833
833
|
'''
|
|
834
834
|
return self._n
|
|
835
835
|
|
|
@@ -1447,7 +1447,13 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase, .fstats
|
|
|
1447
1447
|
@arg other2: Addend (C{scalar}, an L{Fsum} or L{Fsum2Tuple}).
|
|
1448
1448
|
@kwarg nonfinites: Use C{B{nonfinites}=True} or C{False}, to
|
|
1449
1449
|
override L{nonfinites<Fsum.nonfinites>} and
|
|
1450
|
-
L{nonfiniterrors} default (C{bool}).
|
|
1450
|
+
the L{nonfiniterrors} default (C{bool}).
|
|
1451
|
+
'''
|
|
1452
|
+
f = self._fma(other1, other2, **nonfinites)
|
|
1453
|
+
return self._fset(f)
|
|
1454
|
+
|
|
1455
|
+
def _fma(self, other1, other2, **nonfinites): # in .elliptic
|
|
1456
|
+
'''(INTERNAL) Return C{self * B{other1} + B{other2}}.
|
|
1451
1457
|
'''
|
|
1452
1458
|
op = typename(self.fma)
|
|
1453
1459
|
_fs = self._ps_other
|
|
@@ -1459,7 +1465,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase, .fstats
|
|
|
1459
1465
|
elif _residue(other1) or _residue(other2):
|
|
1460
1466
|
fs = _2split3s(_fs(op, other1))
|
|
1461
1467
|
fs = _2products(s, fs, *_fs(op, other2))
|
|
1462
|
-
f =
|
|
1468
|
+
f = Fsum(fs, name=op, **nonfinites)
|
|
1463
1469
|
else:
|
|
1464
1470
|
f = _fma(s, other1, other2)
|
|
1465
1471
|
f = _2finite(f, **self._isfine)
|
|
@@ -1469,7 +1475,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase, .fstats
|
|
|
1469
1475
|
f = self._mul_reduce(s, other1) # INF, NAN, NINF
|
|
1470
1476
|
f += sum(_fs(op, other2))
|
|
1471
1477
|
f = self._nonfiniteX(X, op, f, **nonfinites)
|
|
1472
|
-
return
|
|
1478
|
+
return f
|
|
1473
1479
|
|
|
1474
1480
|
def fma_(self, *xys, **nonfinites):
|
|
1475
1481
|
'''Fused-multiply-accumulate C{for i in range(0, len(xys), B{2}):
|
|
@@ -1479,7 +1485,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase, .fstats
|
|
|
1479
1485
|
an L{Fsum} or L{Fsum2Tuple}), all positional.
|
|
1480
1486
|
@kwarg nonfinites: Use C{B{nonfinites}=True} or C{False}, to
|
|
1481
1487
|
override L{nonfinites<Fsum.nonfinites>} and
|
|
1482
|
-
L{nonfiniterrors} default (C{bool}).
|
|
1488
|
+
the L{nonfiniterrors} default (C{bool}).
|
|
1483
1489
|
|
|
1484
1490
|
@note: Equivalent to L{fdot_<pygeodesy.fmath.fdot_>}C{(*xys,
|
|
1485
1491
|
start=self)}.
|
|
@@ -1615,8 +1621,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase, .fstats
|
|
|
1615
1621
|
'''
|
|
1616
1622
|
if two: # delattrof(self, _f2product=None)
|
|
1617
1623
|
t = _xkwds_pop(self.__dict__, _f2product=None)
|
|
1618
|
-
|
|
1619
|
-
self._f2product = bool(two[0])
|
|
1624
|
+
self._optionals(f2product=two[0])
|
|
1620
1625
|
else: # getattrof(self, _f2product=None)
|
|
1621
1626
|
t = _xkwds_get(self.__dict__, _f2product=None)
|
|
1622
1627
|
return t
|
|
@@ -2070,25 +2075,21 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase, .fstats
|
|
|
2070
2075
|
@see: Function L{nonfiniterrors<fsums.nonfiniterrors>}.
|
|
2071
2076
|
|
|
2072
2077
|
@note: Use property L{nonfinitesOK<Fsum.nonfinitesOK>} to determine
|
|
2073
|
-
whether I{non-finites} are C{OK} for this L{Fsum}
|
|
2078
|
+
whether I{non-finites} are C{OK} for this L{Fsum} or by the
|
|
2074
2079
|
L{nonfiniterrors} default.
|
|
2075
2080
|
'''
|
|
2076
|
-
_ks = Fsum._nonfinites_isfine_kwds
|
|
2077
2081
|
if OK: # delattrof(self, _isfine=None)
|
|
2078
2082
|
k = _xkwds_pop(self.__dict__, _isfine=None)
|
|
2079
|
-
|
|
2080
|
-
self._isfine = _ks[bool(OK[0])]
|
|
2083
|
+
self._optionals(nonfinites=OK[0])
|
|
2081
2084
|
self._update()
|
|
2082
2085
|
else: # getattrof(self, _isfine=None)
|
|
2083
2086
|
k = _xkwds_get(self.__dict__, _isfine=None)
|
|
2087
|
+
_ks = _nonfinites_isfine_kwds
|
|
2084
2088
|
# dict(map(reversed, _ks.items())).get(k, None)
|
|
2085
2089
|
# raises a TypeError: unhashable type: 'dict'
|
|
2086
2090
|
return True if k is _ks[True] else (
|
|
2087
2091
|
False if k is _ks[False] else None)
|
|
2088
2092
|
|
|
2089
|
-
_nonfinites_isfine_kwds = {True: dict(_isfine=_isOK),
|
|
2090
|
-
False: dict(_isfine=_isfinite)}
|
|
2091
|
-
|
|
2092
2093
|
@property_RO
|
|
2093
2094
|
def nonfinitesOK(self):
|
|
2094
2095
|
'''Are I{non-finites} C{OK} for this L{Fsum} or by default? (C{bool}).
|
|
@@ -2111,9 +2112,9 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase, .fstats
|
|
|
2111
2112
|
'''(INTERNAL) Re/set options from keyword arguments.
|
|
2112
2113
|
'''
|
|
2113
2114
|
if f2product is not None:
|
|
2114
|
-
self.
|
|
2115
|
+
self._f2product = bool(f2product)
|
|
2115
2116
|
if nonfinites is not None:
|
|
2116
|
-
self.
|
|
2117
|
+
self._isfine = _nonfinites_isfine_kwds[bool(nonfinites)]
|
|
2117
2118
|
if name_RESIDUAL: # MUST be last
|
|
2118
2119
|
n, kwds = _name2__(**name_RESIDUAL)
|
|
2119
2120
|
if kwds:
|
|
@@ -2535,6 +2536,8 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase, .fstats
|
|
|
2535
2536
|
|
|
2536
2537
|
_ROs = _allPropertiesOf_n(3, Fsum, Property_RO) # PYCHOK see Fsum._update
|
|
2537
2538
|
|
|
2539
|
+
_nonfinites_isfine_kwds = {True: dict(_isfine=_isOK),
|
|
2540
|
+
False: dict(_isfine=_isfinite)}
|
|
2538
2541
|
if _NONFINITES == _std_: # PYCHOK no cover
|
|
2539
2542
|
_ = nonfiniterrors(False)
|
|
2540
2543
|
|
|
@@ -2685,6 +2688,21 @@ class Fsum2Tuple(_NamedTuple): # in .fstats
|
|
|
2685
2688
|
_Fsum_2Tuple_types = Fsum, Fsum2Tuple # PYCHOK lines
|
|
2686
2689
|
|
|
2687
2690
|
|
|
2691
|
+
class _Ksum(Fsum):
|
|
2692
|
+
'''(INTERNAL) For C{.karney._sum3}, specifically and only.
|
|
2693
|
+
'''
|
|
2694
|
+
_isfine = _nonfinites_isfine_kwds[True]
|
|
2695
|
+
|
|
2696
|
+
def __init__(self, s, t, *xs):
|
|
2697
|
+
ps = [t, s] if t else [s]
|
|
2698
|
+
self._ps = self._ps_acc(ps, xs, up=False)
|
|
2699
|
+
|
|
2700
|
+
@property_RO
|
|
2701
|
+
def _s_t_n3(self):
|
|
2702
|
+
s, t = self._fprs2
|
|
2703
|
+
return s, t, self._n
|
|
2704
|
+
|
|
2705
|
+
|
|
2688
2706
|
class ResidualError(_ValueError):
|
|
2689
2707
|
'''Error raised for a division, power or root operation of
|
|
2690
2708
|
an L{Fsum} instance with a C{residual} I{ratio} exceeding
|
pygeodesy/geod3solve.py
CHANGED
|
@@ -5,13 +5,14 @@ u'''Wrapper to invoke I{Karney}'s U{Geod3Solve
|
|
|
5
5
|
<https://GeographicLib.SourceForge.io/C++/doc/Geod3Solve.1.html>} utility
|
|
6
6
|
as a C{triaxial} geodesic, but intended I{mainly for testing purposes}.
|
|
7
7
|
|
|
8
|
-
Set env variable C{PYGEODESY_GEOD3SOLVE} to the (fully qualified) path
|
|
9
|
-
|
|
8
|
+
Set env variable C{PYGEODESY_GEOD3SOLVE} to the (fully qualified) path of
|
|
9
|
+
the C{Geod3Solve} executable or use property L{Geodesic3Solve.Geod3Solve
|
|
10
|
+
<geod3solve._Geodesic3SolveBase.Geod3Solve>}.
|
|
10
11
|
'''
|
|
11
12
|
|
|
12
13
|
from pygeodesy.angles import Ang, Deg, isAng, hypot
|
|
13
14
|
from pygeodesy.basics import _xinstanceof # typename
|
|
14
|
-
from pygeodesy.constants import _0_0, _0_5, _360_0
|
|
15
|
+
from pygeodesy.constants import _0_0, _0_5, _360_0, _over
|
|
15
16
|
from pygeodesy.errors import GeodesicError, _xkwds_get
|
|
16
17
|
# from pygeodesy.fmath import hypot # from .angles
|
|
17
18
|
# from pygeodesy.geodesicx import GeodesicAreaExact # _MODS
|
|
@@ -25,7 +26,7 @@ from pygeodesy.units import Degrees, Meter
|
|
|
25
26
|
# from pygeodesy.utily import sincos2d # from .karney
|
|
26
27
|
|
|
27
28
|
__all__ = _ALL_LAZY.geod3solve
|
|
28
|
-
__version__ = '
|
|
29
|
+
__version__ = '26.01.04'
|
|
29
30
|
|
|
30
31
|
_Triaxial3_WGS84 = Triaxial3s.WGS84_3r # a=6378172, b=6378102, c=6356752
|
|
31
32
|
|
|
@@ -39,24 +40,39 @@ class Geodesic3Error(GeodesicError):
|
|
|
39
40
|
class Geod3Solve8Tuple(_GTuple):
|
|
40
41
|
'''8-Tuple C{(bet1, omg1, alp1, bet2, omg2, alp2, s12, a12)} with C{ellipsoidal}
|
|
41
42
|
latitudes C{bet1} and C{bet2}, C{ellipsoidal} longitudes C{omg1} and C{omg2},
|
|
42
|
-
forward azimuths C{alp1} and C{alp2} in bearings from North,
|
|
43
|
-
C{meter}, conventionally and I{approximate} arc length {a12} in degrees, see
|
|
43
|
+
forward azimuths C{alp1} and C{alp2} in bearings from North, distance C{s12} in
|
|
44
|
+
C{meter}, conventionally and I{approximate} arc length C{a12} in degrees, see
|
|
44
45
|
U{Geod3Solve<https://GeographicLib.SourceForge.io/C++/doc/Geod3Solve.1.html>}.
|
|
45
46
|
'''
|
|
46
47
|
# from Geod3Solve --help option -f ... bet1 omg1 alp1 bet2 omg2 alp2 s12
|
|
47
48
|
_Names_ = ('bet1', 'omg1', 'alp1', 'bet2', 'omg2', 'alp2', _s12_, _a12_)
|
|
48
49
|
_Units_ = ( Deg, Deg, Deg, Deg, Deg, Deg, Meter, Deg)
|
|
49
50
|
|
|
51
|
+
# @Property_RO
|
|
52
|
+
# def A12(self):
|
|
53
|
+
# '''Approximate arc C{A12} as C{Deg}.
|
|
54
|
+
# '''
|
|
55
|
+
# t = self
|
|
56
|
+
# d = t.s12 or _0_0
|
|
57
|
+
# if d:
|
|
58
|
+
# a = hypot(Deg(t.bet2 - t.bet1).degrees,
|
|
59
|
+
# Deg(t.omg2 - t.omg1).degrees)
|
|
60
|
+
# d = (-a) if d < 0 else a
|
|
61
|
+
# return Deg(d)
|
|
62
|
+
|
|
50
63
|
|
|
51
64
|
class _Geodesic3SolveBase(_Solve3Base):
|
|
52
65
|
'''(INTERNAL) Base class for L{Geodesic3Solve} and L{GeodesicLine3Solve}.
|
|
53
66
|
'''
|
|
67
|
+
_a12x = Geod3Solve8Tuple._Names_.index(_a12_) # last
|
|
54
68
|
_Error = Geodesic3Error
|
|
55
69
|
_Names_Direct = _Names_Distance = \
|
|
56
|
-
_Names_Inverse = Geod3Solve8Tuple._Names_[:
|
|
70
|
+
_Names_Inverse = Geod3Solve8Tuple._Names_[:_a12x] # 7 only, always
|
|
57
71
|
_triaxial3 = _Triaxial3_WGS84
|
|
58
72
|
_Xable_name = _Xables.Geod3Solve.__name__ # typename
|
|
59
73
|
_Xable_path = _Xables.Geod3Solve()
|
|
74
|
+
# assert _a12x == len(Geod3Solve8Tuple._Names_) - 1
|
|
75
|
+
del _a12x
|
|
60
76
|
|
|
61
77
|
@Property_RO
|
|
62
78
|
def a(self):
|
|
@@ -161,11 +177,11 @@ class Geodesic3Solve(_Geodesic3SolveBase):
|
|
|
161
177
|
'''
|
|
162
178
|
a = r.s12 or _0_0
|
|
163
179
|
if a:
|
|
180
|
+
t = self.triaxial3
|
|
164
181
|
z = _toAzi(r.alp1) + _toAzi(r.alp2)
|
|
165
182
|
s, c = sincos2d(z * _0_5)
|
|
166
|
-
t
|
|
167
|
-
|
|
168
|
-
c / t._bc_elliperim) * _360_0
|
|
183
|
+
a *= hypot(_over(s, t.perimeter4ab), # azimuth!
|
|
184
|
+
_over(c, t.perimeter4bc)) * _360_0
|
|
169
185
|
r[_a12_] = a
|
|
170
186
|
return r
|
|
171
187
|
|
pygeodesy/geodesici.py
CHANGED
|
@@ -7,10 +7,11 @@ Class L{Intersector} is a pure Python version of I{Karney}'s C++ class U{Interse
|
|
|
7
7
|
<https://GeographicLib.SourceForge.io/C++/doc/classGeographicLib_1_1Intersect.html>}.
|
|
8
8
|
|
|
9
9
|
Class L{Intersectool} is a wrapper to invoke I{Karney}'s U{IntersectTool
|
|
10
|
-
<https://GeographicLib.SourceForge.io/C++/doc/IntersectTool.1.html>} utility,
|
|
10
|
+
<https://GeographicLib.SourceForge.io/C++/doc/IntersectTool.1.html>} utility, mainly intended I{for testing purposes}.
|
|
11
11
|
|
|
12
|
-
Set env variable C{PYGEODESY_INTERSECTTOOL} to the (fully qualified) path of the C{IntersectTool} executable
|
|
13
|
-
and some examples run C{"env PYGEODESY_INTERSECTTOOL=<IntersectTool-path>
|
|
12
|
+
Set env variable C{PYGEODESY_INTERSECTTOOL} to the (fully qualified) path of the C{IntersectTool} executable or use
|
|
13
|
+
property L{Intersectool.IntersectTool}. For usage and some examples run C{"env PYGEODESY_INTERSECTTOOL=<IntersectTool-path>
|
|
14
|
+
python3 -m pygeodesy.geodesici --help"}.
|
|
14
15
|
|
|
15
16
|
Both L{Intersectool} and L{Intersector} provide methods C{All}, C{Closest}, C{Next} and C{Segment} and produce
|
|
16
17
|
L{XDict} instances with 4 or more items. Adjacent methods C{All5}, C{Closest5}, C{Next5} and C{Segment} return
|
|
@@ -57,7 +58,7 @@ from pygeodesy.utily import atan2, sincos2, fabs, radians
|
|
|
57
58
|
# from math import ceil as _ceil, fabs, radians # .fsums, .utily
|
|
58
59
|
|
|
59
60
|
__all__ = _ALL_LAZY.geodesici
|
|
60
|
-
__version__ = '25.
|
|
61
|
+
__version__ = '25.12.31'
|
|
61
62
|
|
|
62
63
|
_0t = 0, # int
|
|
63
64
|
_1_1t = -1, +1
|
pygeodesy/geodesicx/__init__.py
CHANGED
pygeodesy/geodesicx/gxarea.py
CHANGED
|
@@ -19,19 +19,20 @@ from __future__ import division as _; del _ # noqa: E702 ;
|
|
|
19
19
|
|
|
20
20
|
from pygeodesy.basics import _copysign, isodd, unsigned0
|
|
21
21
|
from pygeodesy.constants import NAN, _0_0, _0_5, _720_0
|
|
22
|
+
from pygeodesy.fmath import _fma
|
|
22
23
|
from pygeodesy.internals import printf, typename
|
|
23
24
|
# from pygeodesy.interns import _COMMASPACE_ # from .lazily
|
|
24
25
|
from pygeodesy.karney import Area3Tuple, _diff182, GeodesicError, \
|
|
25
|
-
_norm180, _remainder,
|
|
26
|
+
_norm180, _remainder, _sum2
|
|
26
27
|
from pygeodesy.lazily import _ALL_DOCS, _COMMASPACE_
|
|
27
28
|
from pygeodesy.named import ADict, callername, _NamedBase, pairs
|
|
28
29
|
from pygeodesy.props import Property, Property_RO, property_RO
|
|
29
30
|
# from pygeodesy.streprs import pairs # from .named
|
|
30
31
|
|
|
31
|
-
from math import fmod as _fmod
|
|
32
|
+
from math import fabs, fmod as _fmod
|
|
32
33
|
|
|
33
34
|
__all__ = ()
|
|
34
|
-
__version__ = '25.
|
|
35
|
+
__version__ = '25.12.23'
|
|
35
36
|
|
|
36
37
|
|
|
37
38
|
class GeodesicAreaExact(_NamedBase):
|
|
@@ -427,30 +428,39 @@ class _Accumulator(_NamedBase):
|
|
|
427
428
|
@kwarg y: Initial value (C{scalar}).
|
|
428
429
|
@kwarg name: Optional C{B{name}=NN} (C{str}).
|
|
429
430
|
'''
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
elif y:
|
|
433
|
-
self._s, self._n = float(y), 1
|
|
431
|
+
self._s_t(*_s_t2(y))
|
|
432
|
+
self._n = 1
|
|
434
433
|
if name:
|
|
435
434
|
self.name = name
|
|
436
435
|
|
|
437
|
-
def Add(self,
|
|
438
|
-
'''Add
|
|
436
|
+
def Add(self, *ys):
|
|
437
|
+
'''Add one or more scalars or L{_Accumulator}s.
|
|
439
438
|
|
|
440
439
|
@return: Current C{sum}.
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
440
|
+
|
|
441
|
+
@see: C++ U{Accumulator.Add<https://GeographicLib.sourceforge.io/C++/doc/Accumulator_8hpp_source.html>}
|
|
442
|
+
for more details about Karney's and Shewchuk's addition.
|
|
443
|
+
'''
|
|
444
|
+
# _Accumulator().Add(1, 1e20, 2, 100, 5000, -1e20) ... 5103.0
|
|
445
|
+
s, t = self._s, self._t
|
|
446
|
+
for y in ys:
|
|
447
|
+
for y in _s_t2(y):
|
|
448
|
+
if y:
|
|
449
|
+
t, u = _sum2(t, y)
|
|
450
|
+
s, t = _sum2(s, t)
|
|
451
|
+
if s: # accumlate u in t
|
|
452
|
+
t += u
|
|
453
|
+
else: # s == 0 implies t == 0
|
|
454
|
+
s = u
|
|
455
|
+
self._n += 1
|
|
456
|
+
return self._s_t(s, t)
|
|
445
457
|
|
|
446
458
|
def Negate(self):
|
|
447
459
|
'''Negate sum.
|
|
448
460
|
|
|
449
461
|
@return: Current C{sum}.
|
|
450
462
|
'''
|
|
451
|
-
self._s
|
|
452
|
-
self._t = -self._t
|
|
453
|
-
return s # current .Sum()
|
|
463
|
+
return self._s_t(-self._s, -self._t)
|
|
454
464
|
|
|
455
465
|
@property_RO
|
|
456
466
|
def num(self):
|
|
@@ -459,22 +469,27 @@ class _Accumulator(_NamedBase):
|
|
|
459
469
|
return self._n
|
|
460
470
|
|
|
461
471
|
def Remainder(self, y):
|
|
462
|
-
'''Remainder
|
|
472
|
+
'''Remainder of division by B{C{y}}.
|
|
463
473
|
|
|
464
474
|
@return: Remainder of C{sum} / B{C{y}}.
|
|
465
475
|
'''
|
|
466
|
-
self.
|
|
467
|
-
|
|
468
|
-
self._n = -1
|
|
469
|
-
return self.Add(_0_0)
|
|
476
|
+
return self._s_t(_remainder(self._s, y),
|
|
477
|
+
_remainder(self._t, y))
|
|
470
478
|
|
|
471
479
|
def Reset(self, y=0):
|
|
472
|
-
'''
|
|
480
|
+
'''Reset from scalar or L{_Accumulator}.
|
|
473
481
|
'''
|
|
474
|
-
self.
|
|
482
|
+
self._s_t(*_s_t2(y))
|
|
483
|
+
self._n = 0
|
|
475
484
|
|
|
476
485
|
Set = Reset
|
|
477
486
|
|
|
487
|
+
def _s_t(self, s, t=0):
|
|
488
|
+
if t and fabs(s) < fabs(t):
|
|
489
|
+
s, t = t, s
|
|
490
|
+
self._s, self._t = s, t
|
|
491
|
+
return s
|
|
492
|
+
|
|
478
493
|
def Sum(self, y=0):
|
|
479
494
|
'''Return C{sum + B{y}}.
|
|
480
495
|
|
|
@@ -488,6 +503,17 @@ class _Accumulator(_NamedBase):
|
|
|
488
503
|
s = self
|
|
489
504
|
return s._s
|
|
490
505
|
|
|
506
|
+
def Times(self, y):
|
|
507
|
+
'''Multiply by a scalar.
|
|
508
|
+
|
|
509
|
+
@return: Current C{sum}.
|
|
510
|
+
'''
|
|
511
|
+
s = d = self._s
|
|
512
|
+
s *= y
|
|
513
|
+
d = _fma(y, d, -s)
|
|
514
|
+
t = _fma(y, self._t, d)
|
|
515
|
+
return self._s_t(s, t) # current .Sum()
|
|
516
|
+
|
|
491
517
|
def toStr(self, prec=6, sep=_COMMASPACE_, **unused): # PYCHOK signature
|
|
492
518
|
'''Return this C{_Accumulator} as string.
|
|
493
519
|
|
|
@@ -502,6 +528,10 @@ class _Accumulator(_NamedBase):
|
|
|
502
528
|
return sep.join(pairs(d, prec=prec))
|
|
503
529
|
|
|
504
530
|
|
|
531
|
+
def _s_t2(y):
|
|
532
|
+
return (y._s, y._t) if isinstance(y, _Accumulator) else (float(y),) # PYCHOK OK
|
|
533
|
+
|
|
534
|
+
|
|
505
535
|
__all__ += _ALL_DOCS(GeodesicAreaExact, PolygonArea)
|
|
506
536
|
|
|
507
537
|
# **) MIT License
|
pygeodesy/geodsolve.py
CHANGED
|
@@ -5,8 +5,9 @@ u'''Wrapper to invoke I{Karney}'s U{GeodSolve
|
|
|
5
5
|
<https://GeographicLib.SourceForge.io/C++/doc/GeodSolve.1.html>} utility
|
|
6
6
|
as an (exact) geodesic, but intended I{mainly for testing purposes}.
|
|
7
7
|
|
|
8
|
-
Set env variable C{PYGEODESY_GEODSOLVE} to the (fully qualified) path
|
|
9
|
-
|
|
8
|
+
Set env variable C{PYGEODESY_GEODSOLVE} to the (fully qualified) path of
|
|
9
|
+
the C{GeodSolve} executable or use property L{GeodesicSolve.GeodSolve
|
|
10
|
+
<geodsolve._GeodesicSolveBase.GeodSolve>}.
|
|
10
11
|
'''
|
|
11
12
|
|
|
12
13
|
from pygeodesy.basics import _xinstanceof # typename
|
|
@@ -28,7 +29,7 @@ from pygeodesy.solveBase import _SolveGDictBase, _SolveGDictLineBase
|
|
|
28
29
|
from pygeodesy.utily import _unrollon, _Wrap, wrap360
|
|
29
30
|
|
|
30
31
|
__all__ = _ALL_LAZY.geodsolve
|
|
31
|
-
__version__ = '25.12.
|
|
32
|
+
__version__ = '25.12.31'
|
|
32
33
|
|
|
33
34
|
|
|
34
35
|
class GeodSolve12Tuple(_GTuple):
|