pygeodesy 25.11.5__py2.py3-none-any.whl → 25.12.31__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 +46 -25
- pygeodesy/__main__.py +1 -1
- pygeodesy/albers.py +1 -1
- pygeodesy/angles.py +960 -0
- pygeodesy/auxilats/_CX_4.py +1 -1
- pygeodesy/auxilats/_CX_6.py +1 -1
- pygeodesy/auxilats/_CX_8.py +1 -1
- pygeodesy/auxilats/_CX_Rs.py +1 -1
- pygeodesy/auxilats/__init__.py +2 -2
- pygeodesy/auxilats/__main__.py +1 -1
- pygeodesy/auxilats/auxAngle.py +7 -8
- pygeodesy/auxilats/auxDLat.py +1 -1
- pygeodesy/auxilats/auxDST.py +1 -1
- pygeodesy/auxilats/auxLat.py +1 -1
- pygeodesy/auxilats/auxily.py +1 -1
- pygeodesy/azimuthal.py +6 -5
- pygeodesy/basics.py +14 -10
- pygeodesy/booleans.py +1 -1
- pygeodesy/cartesianBase.py +7 -7
- pygeodesy/clipy.py +1 -1
- pygeodesy/constants.py +29 -24
- pygeodesy/css.py +1 -1
- pygeodesy/datums.py +1 -1
- pygeodesy/deprecated/__init__.py +1 -1
- pygeodesy/deprecated/bases.py +1 -1
- pygeodesy/deprecated/classes.py +14 -7
- pygeodesy/deprecated/consterns.py +1 -1
- pygeodesy/deprecated/datum.py +1 -1
- pygeodesy/deprecated/functions.py +1 -1
- pygeodesy/deprecated/nvector.py +1 -1
- pygeodesy/deprecated/rhumbBase.py +1 -1
- pygeodesy/deprecated/rhumbaux.py +1 -1
- pygeodesy/deprecated/rhumbsolve.py +1 -1
- pygeodesy/deprecated/rhumbx.py +1 -1
- pygeodesy/dms.py +1 -1
- pygeodesy/ecef.py +1 -1
- pygeodesy/ecefLocals.py +1 -1
- pygeodesy/elevations.py +1 -1
- pygeodesy/ellipsoidalBase.py +1 -1
- pygeodesy/ellipsoidalBaseDI.py +1 -1
- pygeodesy/ellipsoidalExact.py +1 -1
- pygeodesy/ellipsoidalGeodSolve.py +1 -1
- pygeodesy/ellipsoidalKarney.py +1 -1
- pygeodesy/ellipsoidalNvector.py +1 -1
- pygeodesy/ellipsoidalVincenty.py +1 -1
- pygeodesy/ellipsoids.py +30 -17
- pygeodesy/elliptic.py +1 -1
- pygeodesy/epsg.py +1 -1
- pygeodesy/errors.py +8 -4
- pygeodesy/etm.py +1 -1
- pygeodesy/fmath.py +19 -14
- pygeodesy/formy.py +251 -10
- pygeodesy/frechet.py +1 -1
- pygeodesy/fstats.py +1 -1
- pygeodesy/fsums.py +41 -29
- pygeodesy/gars.py +1 -1
- pygeodesy/geod3solve.py +489 -0
- pygeodesy/geodesici.py +9 -8
- pygeodesy/geodesicw.py +1 -1
- pygeodesy/geodesicx/_C4_24.py +1 -1
- pygeodesy/geodesicx/_C4_27.py +1 -1
- pygeodesy/geodesicx/_C4_30.py +1 -1
- pygeodesy/geodesicx/__init__.py +2 -2
- pygeodesy/geodesicx/__main__.py +1 -1
- pygeodesy/geodesicx/gx.py +1 -1
- pygeodesy/geodesicx/gxarea.py +54 -24
- pygeodesy/geodesicx/gxbases.py +1 -1
- pygeodesy/geodesicx/gxline.py +1 -1
- pygeodesy/geodsolve.py +73 -104
- pygeodesy/geohash.py +1 -1
- pygeodesy/geoids.py +1 -1
- pygeodesy/hausdorff.py +1 -1
- pygeodesy/heights.py +1 -1
- pygeodesy/internals.py +1 -1
- pygeodesy/interns.py +3 -3
- pygeodesy/iters.py +1 -1
- pygeodesy/karney.py +152 -151
- pygeodesy/ktm.py +1 -1
- pygeodesy/latlonBase.py +1 -1
- pygeodesy/lazily.py +24 -13
- pygeodesy/lcc.py +1 -1
- pygeodesy/ltp.py +1 -1
- pygeodesy/ltpTuples.py +1 -1
- pygeodesy/mgrs.py +3 -3
- pygeodesy/named.py +15 -10
- pygeodesy/namedTuples.py +1 -1
- pygeodesy/nvectorBase.py +1 -1
- pygeodesy/osgr.py +1 -1
- pygeodesy/points.py +1 -1
- pygeodesy/props.py +6 -4
- pygeodesy/resections.py +1 -1
- pygeodesy/rhumb/__init__.py +8 -6
- pygeodesy/rhumb/aux_.py +1 -1
- pygeodesy/rhumb/bases.py +1 -1
- pygeodesy/rhumb/ekx.py +1 -1
- pygeodesy/rhumb/solve.py +91 -84
- pygeodesy/simplify.py +1 -1
- pygeodesy/solveBase.py +72 -49
- pygeodesy/sphericalBase.py +1 -1
- pygeodesy/sphericalNvector.py +1 -1
- pygeodesy/sphericalTrigonometry.py +1 -1
- pygeodesy/streprs.py +6 -4
- pygeodesy/trf.py +2 -4
- pygeodesy/triaxials/__init__.py +70 -0
- pygeodesy/triaxials/bases.py +966 -0
- pygeodesy/triaxials/conformal3.py +617 -0
- pygeodesy/triaxials/triaxial3.py +968 -0
- pygeodesy/{triaxials.py → triaxials/triaxial5.py} +353 -781
- pygeodesy/units.py +1 -1
- pygeodesy/unitsBase.py +1 -1
- pygeodesy/ups.py +2 -3
- pygeodesy/utily.py +17 -14
- pygeodesy/utm.py +1 -1
- pygeodesy/utmups.py +1 -1
- pygeodesy/utmupsBase.py +1 -1
- pygeodesy/vector2d.py +1 -1
- pygeodesy/vector3d.py +1 -1
- pygeodesy/vector3dBase.py +1 -1
- pygeodesy/webmercator.py +1 -1
- pygeodesy/wgrs.py +1 -1
- {pygeodesy-25.11.5.dist-info → pygeodesy-25.12.31.dist-info}/METADATA +28 -21
- pygeodesy-25.12.31.dist-info/RECORD +125 -0
- pygeodesy-25.11.5.dist-info/RECORD +0 -119
- {pygeodesy-25.11.5.dist-info → pygeodesy-25.12.31.dist-info}/WHEEL +0 -0
- {pygeodesy-25.11.5.dist-info → pygeodesy-25.12.31.dist-info}/top_level.txt +0 -0
pygeodesy/formy.py
CHANGED
|
@@ -8,26 +8,30 @@ from __future__ import division as _; del _ # noqa: E702 ;
|
|
|
8
8
|
|
|
9
9
|
from pygeodesy.basics import _copysign, _isin # _args_kwds_count2
|
|
10
10
|
# from pygeodesy.cartesianBase import CartesianBase # _MODS
|
|
11
|
-
from pygeodesy.constants import EPS, EPS0, EPS1, PI, PI2, PI3, PI_2, R_M, \
|
|
11
|
+
from pygeodesy.constants import EPS, EPS0, EPS1, EPS_2, 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
14
|
_90_0, _180_0, _360_0
|
|
15
|
+
from pygeodesy.constants import _3_0, _10_0, MANT_DIG as _DIG53 # PYCHOK used!
|
|
15
16
|
from pygeodesy.datums import Datum, Ellipsoid, _ellipsoidal_datum, \
|
|
16
17
|
_mean_radius, _spherical_datum, _WGS84, _EWGS84
|
|
17
18
|
# from pygeodesy.ellipsoids import Ellipsoid, _EWGS84 # from .datums
|
|
19
|
+
# from pygeodesy.elliptic import Elliptic # _MODS
|
|
18
20
|
from pygeodesy.errors import IntersectionError, LimitError, limiterrors, \
|
|
19
21
|
_TypeError, _ValueError, _xattr, _xError, \
|
|
20
22
|
_xcallable, _xkwds, _xkwds_pop2
|
|
21
|
-
from pygeodesy.fmath import euclid, fdot_, fprod, hypot, hypot2, sqrt0
|
|
22
|
-
from pygeodesy.fsums import fsumf_, Fmt, unstr
|
|
23
|
+
from pygeodesy.fmath import euclid, fdot_, fhorner, fprod, hypot, hypot2, sqrt0
|
|
24
|
+
from pygeodesy.fsums import fsum, fsumf_, Fmt, unstr
|
|
23
25
|
# from pygeodesy.internals import typename # from .named
|
|
24
|
-
from pygeodesy.interns import _delta_, _distant_, _inside_, _SPACE_, _too_
|
|
25
|
-
from pygeodesy.lazily import _ALL_LAZY, _ALL_MODS as _MODS
|
|
26
|
-
from pygeodesy.named import _name__, _name2__, _NamedTuple,
|
|
26
|
+
from pygeodesy.interns import _delta_, _distant_, _DOT_, _inside_, _SPACE_, _too_
|
|
27
|
+
from pygeodesy.lazily import _ALL_LAZY, _ALL_MODS as _MODS, _FOR_DOCS
|
|
28
|
+
from pygeodesy.named import callername, _name__, _name2__, _NamedTuple, \
|
|
29
|
+
_xnamed, typename
|
|
27
30
|
from pygeodesy.namedTuples import Bearing2Tuple, Distance4Tuple, LatLon2Tuple, \
|
|
28
31
|
Intersection3Tuple, PhiLam2Tuple
|
|
32
|
+
from pygeodesy.props import property_ROnce
|
|
29
33
|
# from pygeodesy.streprs import Fmt, unstr # from .fsums
|
|
30
|
-
# from pygeodesy.triaxials import _hartzell3 # _MODS
|
|
34
|
+
# from pygeodesy.triaxials.triaxial5 import _hartzell3 # _MODS
|
|
31
35
|
from pygeodesy.units import _isDegrees, _isHeight, _isRadius, Bearing, Degrees_, \
|
|
32
36
|
Distance, Distance_, Height, Lamd, Lat, Lon, Meter_, \
|
|
33
37
|
Phid, Radians, Radians_, Radius, Radius_, Scalar, _100km
|
|
@@ -42,13 +46,241 @@ from contextlib import contextmanager
|
|
|
42
46
|
from math import atan, cos, degrees, fabs, radians, sin, sqrt # pow
|
|
43
47
|
|
|
44
48
|
__all__ = _ALL_LAZY.formy
|
|
45
|
-
__version__ = '25.
|
|
49
|
+
__version__ = '25.12.31'
|
|
46
50
|
|
|
47
51
|
_RADIANS2 = radians(_1_0)**2 # degree to radians-squared
|
|
48
52
|
_ratio_ = 'ratio'
|
|
49
53
|
_xline_ = 'xline'
|
|
50
54
|
|
|
51
55
|
|
|
56
|
+
class Elliperim(object):
|
|
57
|
+
'''Singleton with various methods to compute the perimeter of an ellipse.
|
|
58
|
+
'''
|
|
59
|
+
_TOL53 = sqrt(EPS_2) # sqrt(pow(_0_5, _DIG53))
|
|
60
|
+
_TOL53_53 = _TOL53 / _DIG53 # "flat" b/a tolerance, 1.9e-10
|
|
61
|
+
# assert _DIG53 == 53
|
|
62
|
+
|
|
63
|
+
def AGM(self, a, b, maxit=_DIG53):
|
|
64
|
+
'''Return the perimeter of an ellipse with semi-axes C{a} and C{b} using the U{AGM
|
|
65
|
+
<https://PaulBourke.net/geometry/ellipsecirc>} (Arithmetic-Geometric Mean) method.
|
|
66
|
+
|
|
67
|
+
@kwarg maxit: Number of iterations (C{int}).
|
|
68
|
+
|
|
69
|
+
@raise ValueError: No convergence for B{C{maxit}} iterations.
|
|
70
|
+
'''
|
|
71
|
+
_, p, a, b = self._pab4(a, b)
|
|
72
|
+
if p is None:
|
|
73
|
+
c_ = []
|
|
74
|
+
ts = self._AGMs(a, b, max(maxit, _DIG53), c_)
|
|
75
|
+
p = fsum(ts, nonfinites=True)
|
|
76
|
+
p *= PI / c_[0]
|
|
77
|
+
return p
|
|
78
|
+
|
|
79
|
+
def _AGMs(self, a, b, maxit, c_):
|
|
80
|
+
'''(INTERNAL) Yield the C{AGM} terms and final C{c}.
|
|
81
|
+
'''
|
|
82
|
+
c = a + b
|
|
83
|
+
yield c**2
|
|
84
|
+
m = -1
|
|
85
|
+
t = self._TOL53
|
|
86
|
+
for _ in range(maxit): # 4..5 trips
|
|
87
|
+
b = sqrt(a * b)
|
|
88
|
+
a = c * _0_5
|
|
89
|
+
c = a + b
|
|
90
|
+
d = a - b
|
|
91
|
+
m *= 2
|
|
92
|
+
yield d**2 * m
|
|
93
|
+
if d <= (b * t):
|
|
94
|
+
break
|
|
95
|
+
else:
|
|
96
|
+
raise self._Error(maxit, d, b * t)
|
|
97
|
+
c_.append(c) # kludge
|
|
98
|
+
|
|
99
|
+
def Arc43(self, a, b):
|
|
100
|
+
'''Return the perimeter (and arcs) of an ellipse with semi-axes C{a} and C{b}
|
|
101
|
+
with the U{4-Arc<https://PaulBourke.net/geometry/ellipsecirc>} approximation.
|
|
102
|
+
|
|
103
|
+
@return: 3-Tuple C{(p, Ra, Rb)} with perimeter C{p}, arc radius C{Ra} at the
|
|
104
|
+
major and arc radius C{Rb} at the minor semi-axis.
|
|
105
|
+
'''
|
|
106
|
+
_r, p, a, b = self._pab4(a, b)
|
|
107
|
+
if p is None:
|
|
108
|
+
h = hypot(a, b)
|
|
109
|
+
p = atan2(b, a)
|
|
110
|
+
s, c = sincos2(p)
|
|
111
|
+
L = (h - b) * _0_5
|
|
112
|
+
Ra = L / c
|
|
113
|
+
Rb = (h - L) / s
|
|
114
|
+
p = Rb * p + Ra * (PI_2 - p)
|
|
115
|
+
p *= _4_0
|
|
116
|
+
else: # circle or flat
|
|
117
|
+
Ra, Rb = a, b
|
|
118
|
+
return (p, Rb, Ra) if _r else (p, Ra, Rb)
|
|
119
|
+
|
|
120
|
+
# def CR(self, a, b):
|
|
121
|
+
# '''Return the perimeter of an ellipse with semi-axes C{a} and C{b} using U{Rackauckas'
|
|
122
|
+
# <https://www.ChrisRackauckas.com/assets/Papers/ChrisRackauckas-The_Circumference_of_an_Ellipse.pdf>}
|
|
123
|
+
# approximation, also U{here<https://ExtremeLearning.com.AU/a-formula-for-the-perimeter-of-an-ellipse>}.
|
|
124
|
+
# '''
|
|
125
|
+
# _, p, a, b = self._pab4(a, b)
|
|
126
|
+
# if p is None:
|
|
127
|
+
# p = a + b
|
|
128
|
+
# h = ((a - b) / p)**2
|
|
129
|
+
# p *= (fhorner(h, 135168, -85760, -5568, 3867) /
|
|
130
|
+
# fhorner(h, 135168, -119552, 22208, 345)) * PI
|
|
131
|
+
# return p
|
|
132
|
+
|
|
133
|
+
def E2k(self, a, b):
|
|
134
|
+
'''Return the perimeter of an ellipse with semi-axes C{a} and C{b} from the complete
|
|
135
|
+
elliptic integral of the 2nd kind L{E(k)<pygeodesy.elliptic.Elliptic.cE>}.
|
|
136
|
+
'''
|
|
137
|
+
return self._ellip2k(a, b, self._ellipE)
|
|
138
|
+
|
|
139
|
+
def e2k(self, a, b, E_alt=None):
|
|
140
|
+
'''Return the perimeter of an ellipse with semi-axes C{a} and C{b} using U{SciPy's
|
|
141
|
+
ellipe<https://www.JohnDCook.com/perimeter_ellipse.html>} function or method
|
|
142
|
+
C{E_alt}, otherwise C{None}.
|
|
143
|
+
|
|
144
|
+
@kwarg E_alt: An other C{Elliperim}C{(a, b)} method to use in case C{SciPy's
|
|
145
|
+
ellipe} is not available.
|
|
146
|
+
'''
|
|
147
|
+
p = self._ellipe
|
|
148
|
+
if p is not None: # i.e. callable
|
|
149
|
+
p = self._ellip2k(a, b, p)
|
|
150
|
+
elif callable(E_alt): # and E_alt is not Elliperim.e2k
|
|
151
|
+
p = E_alt(a, b)
|
|
152
|
+
return p
|
|
153
|
+
|
|
154
|
+
def _ellipE(self, k):
|
|
155
|
+
'''(INTERNAL) Get the complete C{elliptic} integeral C{E(k)}.
|
|
156
|
+
'''
|
|
157
|
+
return _MODS.elliptic.Elliptic(k).cE
|
|
158
|
+
|
|
159
|
+
@property_ROnce
|
|
160
|
+
def _ellipe(self):
|
|
161
|
+
'''(INTERNAL) Wrap function C{scipy.special.ellipe}, I{once}.
|
|
162
|
+
'''
|
|
163
|
+
try:
|
|
164
|
+
from scipy.special import ellipe
|
|
165
|
+
|
|
166
|
+
def _ellipe(k):
|
|
167
|
+
return float(ellipe(k))
|
|
168
|
+
|
|
169
|
+
except (AttributeError, ImportError):
|
|
170
|
+
_ellipe = None
|
|
171
|
+
return _ellipe # overwrite property_ROnce
|
|
172
|
+
|
|
173
|
+
def _ellip2k(self, a, b, _ellip):
|
|
174
|
+
'''(INTERNAL) Helper for methods C{E2k} and C{e2k}.
|
|
175
|
+
'''
|
|
176
|
+
_, p, a, b = self._pab4(a, b)
|
|
177
|
+
if p is None: # see .ellipsoids.Ellipsoid.L
|
|
178
|
+
k = _1_0 - (b / a)**2
|
|
179
|
+
p = _ellip(k) * a * _4_0
|
|
180
|
+
return p
|
|
181
|
+
|
|
182
|
+
def _Error(self, maxit, d, t):
|
|
183
|
+
return _ValueError(maxit=maxit, txt=Fmt.no_convergence(d, t))
|
|
184
|
+
|
|
185
|
+
def GK(self, a, b):
|
|
186
|
+
'''Return the perimeter of an ellipse with semi-axes C{a} and C{b} using the U{Gauss-Kummer
|
|
187
|
+
<https://www.JohnDCook.com/blog/2023/05/28/approximate-ellipse-perimeter>} series, and
|
|
188
|
+
U{here<https://www.MathsIsFun.com/geometry/ellipse-perimeter.html>}, C{B{b / a} > 0.75}.
|
|
189
|
+
'''
|
|
190
|
+
_, p, a, b = self._pab4(a, b)
|
|
191
|
+
if p is None:
|
|
192
|
+
p = a + b
|
|
193
|
+
h = (a - b) / p
|
|
194
|
+
p *= fhorner(h**2, *self._GKs) * PI
|
|
195
|
+
return p
|
|
196
|
+
|
|
197
|
+
@property_ROnce
|
|
198
|
+
def _GKs(self):
|
|
199
|
+
'''(INTERNAL) Compute the Gauss-Kummer coefficients, I{once}.
|
|
200
|
+
'''
|
|
201
|
+
return (1, 1 / 4, 1 / 64, 1 / 256, 25 / 16384, 49 / 65536,
|
|
202
|
+
441 / 1048576, 1089 / 4194304) # overwrite property_ROnce
|
|
203
|
+
|
|
204
|
+
def HG(self, a, b, maxit=_DIG53):
|
|
205
|
+
'''Return the perimeter of an ellipse with semi-axes C{a} and C{b} using the U{HG
|
|
206
|
+
<https://web.Tecnico.ULisboa.PT/~mcasquilho/compute/com/,ellips/PerimeterOfEllipse.pdf>}
|
|
207
|
+
(HyperGeometric Gauss-Kummer) series.
|
|
208
|
+
|
|
209
|
+
@kwarg maxit: Number of iterations (C{int}), sufficient for C{B{b / a} > 0.125}.
|
|
210
|
+
|
|
211
|
+
@raise ValueError: No convergence for B{C{maxit}} iterations.
|
|
212
|
+
'''
|
|
213
|
+
_, p, a, b = self._pab4(a, b)
|
|
214
|
+
if p is None:
|
|
215
|
+
p = a + b
|
|
216
|
+
h = (a - b) / p
|
|
217
|
+
ts = self._HGs(h, max(maxit, _DIG53))
|
|
218
|
+
p *= fsum(ts, nonfinites=True) * PI
|
|
219
|
+
return p
|
|
220
|
+
|
|
221
|
+
def _HGs(self, h, maxit):
|
|
222
|
+
'''(INTERNAL) Yield the C{HG} terms.
|
|
223
|
+
'''
|
|
224
|
+
t = s_ = -1
|
|
225
|
+
s = _1_0
|
|
226
|
+
yield s
|
|
227
|
+
for u in range(-1, maxit * 2, 2):
|
|
228
|
+
t *= u / (u + 3) * h
|
|
229
|
+
t2 = t**2
|
|
230
|
+
s += t2
|
|
231
|
+
yield t2
|
|
232
|
+
if s == s_:
|
|
233
|
+
break
|
|
234
|
+
s_ = s
|
|
235
|
+
else:
|
|
236
|
+
s -= s_ - t2
|
|
237
|
+
raise self._Error(maxit, t2, s)
|
|
238
|
+
|
|
239
|
+
# def LS(self, a, b):
|
|
240
|
+
# '''Return the perimeter of an ellipse with semi-axes C{a} and C{b} using the U{Linderholm-Segal
|
|
241
|
+
# <https://www.JohnDCook.com/blog/2021/03/24/perimeter-of-an-ellipse>} formula, aka C{3/2 norm}.
|
|
242
|
+
# '''
|
|
243
|
+
# _, p, a, b = self._pab4(a, b)
|
|
244
|
+
# if p is None:
|
|
245
|
+
# p = pow(a, _1_5) + pow(b, _1_5)
|
|
246
|
+
# p = pow(p * _0_5, _2_3rd) * PI2
|
|
247
|
+
# return p
|
|
248
|
+
|
|
249
|
+
def _pab4(self, a, b):
|
|
250
|
+
_r = a < b
|
|
251
|
+
if _r:
|
|
252
|
+
a, b = b, a
|
|
253
|
+
if a > b:
|
|
254
|
+
if b > (a * self._TOL53_53):
|
|
255
|
+
p = None
|
|
256
|
+
elif b < 0:
|
|
257
|
+
t = callername() # underOK=True
|
|
258
|
+
t = _DOT_(typename(self), t)
|
|
259
|
+
raise _ValueError(unstr(t, a, b))
|
|
260
|
+
else: # "flat"
|
|
261
|
+
p = a * _4_0
|
|
262
|
+
else: # circle
|
|
263
|
+
p = a * PI2
|
|
264
|
+
return _r, p, a, b
|
|
265
|
+
|
|
266
|
+
def R2(self, a, b):
|
|
267
|
+
'''Return the perimeter of an ellipse with semi-axes C{a} and C{b} using U{Ramanujan's
|
|
268
|
+
2nd<https://PaulBourke.net/geometry/ellipsecirc>} approximation, C{B{b / a} > 0.9}.
|
|
269
|
+
'''
|
|
270
|
+
_, p, a, b = self._pab4(a, b)
|
|
271
|
+
if p is None:
|
|
272
|
+
p = a + b
|
|
273
|
+
h = (a - b) / p
|
|
274
|
+
h *= _3_0 * h
|
|
275
|
+
h /= sqrt(_4_0 - h) + _10_0 # /= chokes PyChecker?
|
|
276
|
+
p *= (h + _1_0) * PI
|
|
277
|
+
return p
|
|
278
|
+
|
|
279
|
+
if not _FOR_DOCS: # PYCHOK force epydoc
|
|
280
|
+
Elliperim = Elliperim() # singleton
|
|
281
|
+
del _FOR_DOCS
|
|
282
|
+
|
|
283
|
+
|
|
52
284
|
def angle2chord(rad, radius=R_M):
|
|
53
285
|
'''Get the chord length of a (central) angle or I{angular} distance.
|
|
54
286
|
|
|
@@ -367,6 +599,15 @@ def _dS(fun_, radius, wrap, *lls, **adjust):
|
|
|
367
599
|
return r * radius
|
|
368
600
|
|
|
369
601
|
|
|
602
|
+
def elliperim(a, b):
|
|
603
|
+
'''Compute the perimeter of an ellipse with semi-axes C{a} and C{b}
|
|
604
|
+
using the C{Elliperim.e2k} or C{Elliperim.AGM} method.
|
|
605
|
+
|
|
606
|
+
@return: The perimeter (C{scalar}, same units as C{a} and C{b}).
|
|
607
|
+
'''
|
|
608
|
+
return Elliperim.e2k(a, b, Elliperim.E2k)
|
|
609
|
+
|
|
610
|
+
|
|
370
611
|
def _ellipsoidal(earth, where):
|
|
371
612
|
'''(INTERNAL) Helper for distances.
|
|
372
613
|
'''
|
|
@@ -926,7 +1167,7 @@ def hartzell(pov, los=False, earth=_WGS84, **name_LatLon_and_kwds):
|
|
|
926
1167
|
n, kwds = _name2__(name_LatLon_and_kwds, name__=hartzell)
|
|
927
1168
|
try:
|
|
928
1169
|
D = _spherical_datum(earth, name__=hartzell)
|
|
929
|
-
r, h, i = _MODS.triaxials._hartzell3(pov, los, D.ellipsoid._triaxial)
|
|
1170
|
+
r, h, i = _MODS.triaxials.triaxial5._hartzell3(pov, los, D.ellipsoid._triaxial)
|
|
930
1171
|
|
|
931
1172
|
C = _MODS.cartesianBase.CartesianBase
|
|
932
1173
|
if kwds:
|
|
@@ -1663,7 +1904,7 @@ def vincentys_(phi2, phi1, lam21):
|
|
|
1663
1904
|
|
|
1664
1905
|
# **) MIT License
|
|
1665
1906
|
#
|
|
1666
|
-
# Copyright (C) 2016-
|
|
1907
|
+
# Copyright (C) 2016-2026 -- mrJean1 at Gmail -- All Rights Reserved.
|
|
1667
1908
|
#
|
|
1668
1909
|
# Permission is hereby granted, free of charge, to any person obtaining a
|
|
1669
1910
|
# copy of this software and associated documentation files (the "Software"),
|
pygeodesy/frechet.py
CHANGED
|
@@ -906,7 +906,7 @@ if __name__ == _DMAIN_:
|
|
|
906
906
|
|
|
907
907
|
# **) MIT License
|
|
908
908
|
#
|
|
909
|
-
# Copyright (C) 2016-
|
|
909
|
+
# Copyright (C) 2016-2026 -- mrJean1 at Gmail -- All Rights Reserved.
|
|
910
910
|
#
|
|
911
911
|
# Permission is hereby granted, free of charge, to any person obtaining a
|
|
912
912
|
# copy of this software and associated documentation files (the "Software"),
|
pygeodesy/fstats.py
CHANGED
|
@@ -804,7 +804,7 @@ __all__ += _ALL_DOCS(_FstatsBase, _FstatsNamed)
|
|
|
804
804
|
|
|
805
805
|
# **) MIT License
|
|
806
806
|
#
|
|
807
|
-
# Copyright (C) 2021-
|
|
807
|
+
# Copyright (C) 2021-2026 -- mrJean1 at Gmail -- All Rights Reserved.
|
|
808
808
|
#
|
|
809
809
|
# Permission is hereby granted, free of charge, to any person obtaining a
|
|
810
810
|
# copy of this software and associated documentation files (the "Software"),
|
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__ = '25.
|
|
65
|
+
__version__ = '25.12.24'
|
|
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,7 @@ 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
1451
|
'''
|
|
1452
1452
|
op = typename(self.fma)
|
|
1453
1453
|
_fs = self._ps_other
|
|
@@ -1479,7 +1479,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase, .fstats
|
|
|
1479
1479
|
an L{Fsum} or L{Fsum2Tuple}), all positional.
|
|
1480
1480
|
@kwarg nonfinites: Use C{B{nonfinites}=True} or C{False}, to
|
|
1481
1481
|
override L{nonfinites<Fsum.nonfinites>} and
|
|
1482
|
-
L{nonfiniterrors} default (C{bool}).
|
|
1482
|
+
the L{nonfiniterrors} default (C{bool}).
|
|
1483
1483
|
|
|
1484
1484
|
@note: Equivalent to L{fdot_<pygeodesy.fmath.fdot_>}C{(*xys,
|
|
1485
1485
|
start=self)}.
|
|
@@ -1615,8 +1615,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase, .fstats
|
|
|
1615
1615
|
'''
|
|
1616
1616
|
if two: # delattrof(self, _f2product=None)
|
|
1617
1617
|
t = _xkwds_pop(self.__dict__, _f2product=None)
|
|
1618
|
-
|
|
1619
|
-
self._f2product = bool(two[0])
|
|
1618
|
+
self._optionals(f2product=two[0])
|
|
1620
1619
|
else: # getattrof(self, _f2product=None)
|
|
1621
1620
|
t = _xkwds_get(self.__dict__, _f2product=None)
|
|
1622
1621
|
return t
|
|
@@ -2070,25 +2069,21 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase, .fstats
|
|
|
2070
2069
|
@see: Function L{nonfiniterrors<fsums.nonfiniterrors>}.
|
|
2071
2070
|
|
|
2072
2071
|
@note: Use property L{nonfinitesOK<Fsum.nonfinitesOK>} to determine
|
|
2073
|
-
whether I{non-finites} are C{OK} for this L{Fsum}
|
|
2072
|
+
whether I{non-finites} are C{OK} for this L{Fsum} or by the
|
|
2074
2073
|
L{nonfiniterrors} default.
|
|
2075
2074
|
'''
|
|
2076
|
-
_ks = Fsum._nonfinites_isfine_kwds
|
|
2077
2075
|
if OK: # delattrof(self, _isfine=None)
|
|
2078
2076
|
k = _xkwds_pop(self.__dict__, _isfine=None)
|
|
2079
|
-
|
|
2080
|
-
self._isfine = _ks[bool(OK[0])]
|
|
2077
|
+
self._optionals(nonfinites=OK[0])
|
|
2081
2078
|
self._update()
|
|
2082
2079
|
else: # getattrof(self, _isfine=None)
|
|
2083
2080
|
k = _xkwds_get(self.__dict__, _isfine=None)
|
|
2081
|
+
_ks = _nonfinites_isfine_kwds
|
|
2084
2082
|
# dict(map(reversed, _ks.items())).get(k, None)
|
|
2085
2083
|
# raises a TypeError: unhashable type: 'dict'
|
|
2086
2084
|
return True if k is _ks[True] else (
|
|
2087
2085
|
False if k is _ks[False] else None)
|
|
2088
2086
|
|
|
2089
|
-
_nonfinites_isfine_kwds = {True: dict(_isfine=_isOK),
|
|
2090
|
-
False: dict(_isfine=_isfinite)}
|
|
2091
|
-
|
|
2092
2087
|
@property_RO
|
|
2093
2088
|
def nonfinitesOK(self):
|
|
2094
2089
|
'''Are I{non-finites} C{OK} for this L{Fsum} or by default? (C{bool}).
|
|
@@ -2111,9 +2106,9 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase, .fstats
|
|
|
2111
2106
|
'''(INTERNAL) Re/set options from keyword arguments.
|
|
2112
2107
|
'''
|
|
2113
2108
|
if f2product is not None:
|
|
2114
|
-
self.
|
|
2109
|
+
self._f2product = bool(f2product)
|
|
2115
2110
|
if nonfinites is not None:
|
|
2116
|
-
self.
|
|
2111
|
+
self._isfine = _nonfinites_isfine_kwds[bool(nonfinites)]
|
|
2117
2112
|
if name_RESIDUAL: # MUST be last
|
|
2118
2113
|
n, kwds = _name2__(**name_RESIDUAL)
|
|
2119
2114
|
if kwds:
|
|
@@ -2535,6 +2530,8 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase, .fstats
|
|
|
2535
2530
|
|
|
2536
2531
|
_ROs = _allPropertiesOf_n(3, Fsum, Property_RO) # PYCHOK see Fsum._update
|
|
2537
2532
|
|
|
2533
|
+
_nonfinites_isfine_kwds = {True: dict(_isfine=_isOK),
|
|
2534
|
+
False: dict(_isfine=_isfinite)}
|
|
2538
2535
|
if _NONFINITES == _std_: # PYCHOK no cover
|
|
2539
2536
|
_ = nonfiniterrors(False)
|
|
2540
2537
|
|
|
@@ -2685,6 +2682,21 @@ class Fsum2Tuple(_NamedTuple): # in .fstats
|
|
|
2685
2682
|
_Fsum_2Tuple_types = Fsum, Fsum2Tuple # PYCHOK lines
|
|
2686
2683
|
|
|
2687
2684
|
|
|
2685
|
+
class _Ksum(Fsum):
|
|
2686
|
+
'''(INTERNAL) For C{.karney._sum3}, specifically and only.
|
|
2687
|
+
'''
|
|
2688
|
+
_isfine = _nonfinites_isfine_kwds[True]
|
|
2689
|
+
|
|
2690
|
+
def __init__(self, s, t, *xs):
|
|
2691
|
+
ps = [t, s] if t else [s]
|
|
2692
|
+
self._ps = self._ps_acc(ps, xs, up=False)
|
|
2693
|
+
|
|
2694
|
+
@property_RO
|
|
2695
|
+
def _s_t_n3(self):
|
|
2696
|
+
s, t = self._fprs2
|
|
2697
|
+
return s, t, self._n
|
|
2698
|
+
|
|
2699
|
+
|
|
2688
2700
|
class ResidualError(_ValueError):
|
|
2689
2701
|
'''Error raised for a division, power or root operation of
|
|
2690
2702
|
an L{Fsum} instance with a C{residual} I{ratio} exceeding
|
|
@@ -2863,7 +2875,7 @@ if __name__ == _DMAIN_:
|
|
|
2863
2875
|
|
|
2864
2876
|
# **) MIT License
|
|
2865
2877
|
#
|
|
2866
|
-
# Copyright (C) 2016-
|
|
2878
|
+
# Copyright (C) 2016-2026 -- mrJean1 at Gmail -- All Rights Reserved.
|
|
2867
2879
|
#
|
|
2868
2880
|
# Permission is hereby granted, free of charge, to any person obtaining a
|
|
2869
2881
|
# copy of this software and associated documentation files (the "Software"),
|
pygeodesy/gars.py
CHANGED
|
@@ -344,7 +344,7 @@ __all__ += _ALL_DOCS(decode3, # functions
|
|
|
344
344
|
|
|
345
345
|
# **) MIT License
|
|
346
346
|
#
|
|
347
|
-
# Copyright (C) 2016-
|
|
347
|
+
# Copyright (C) 2016-2026 -- mrJean1 at Gmail -- All Rights Reserved.
|
|
348
348
|
#
|
|
349
349
|
# Permission is hereby granted, free of charge, to any person obtaining a
|
|
350
350
|
# copy of this software and associated documentation files (the "Software"),
|