pygeodesy 24.8.4__py2.py3-none-any.whl → 24.8.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.
- {PyGeodesy-24.8.4.dist-info → PyGeodesy-24.8.24.dist-info}/METADATA +16 -15
- {PyGeodesy-24.8.4.dist-info → PyGeodesy-24.8.24.dist-info}/RECORD +50 -50
- {PyGeodesy-24.8.4.dist-info → PyGeodesy-24.8.24.dist-info}/WHEEL +1 -1
- pygeodesy/__init__.py +23 -23
- pygeodesy/auxilats/auxDST.py +2 -2
- pygeodesy/basics.py +3 -3
- pygeodesy/cartesianBase.py +5 -5
- pygeodesy/constants.py +11 -11
- pygeodesy/ellipsoidalBase.py +18 -15
- pygeodesy/ellipsoidalExact.py +2 -2
- pygeodesy/ellipsoidalGeodSolve.py +2 -2
- pygeodesy/ellipsoidalKarney.py +2 -2
- pygeodesy/ellipsoidalNvector.py +2 -2
- pygeodesy/ellipsoidalVincenty.py +7 -6
- pygeodesy/epsg.py +3 -3
- pygeodesy/fmath.py +2 -1
- pygeodesy/fsums.py +4 -4
- pygeodesy/gars.py +9 -8
- pygeodesy/geodesicx/gx.py +3 -3
- pygeodesy/geodesicx/gxarea.py +3 -3
- pygeodesy/geodsolve.py +3 -3
- pygeodesy/geohash.py +18 -11
- pygeodesy/geoids.py +293 -315
- pygeodesy/heights.py +150 -158
- pygeodesy/internals.py +21 -1
- pygeodesy/interns.py +2 -3
- pygeodesy/latlonBase.py +13 -7
- pygeodesy/lazily.py +6 -6
- pygeodesy/ltp.py +5 -6
- pygeodesy/ltpTuples.py +7 -1
- pygeodesy/named.py +5 -4
- pygeodesy/namedTuples.py +14 -1
- pygeodesy/osgr.py +7 -7
- pygeodesy/points.py +2 -2
- pygeodesy/resections.py +7 -7
- pygeodesy/rhumb/solve.py +3 -3
- pygeodesy/simplify.py +10 -10
- pygeodesy/sphericalBase.py +3 -3
- pygeodesy/sphericalTrigonometry.py +2 -2
- pygeodesy/streprs.py +3 -3
- pygeodesy/triaxials.py +207 -201
- pygeodesy/units.py +3 -3
- pygeodesy/unitsBase.py +4 -4
- pygeodesy/utmupsBase.py +3 -3
- pygeodesy/vector2d.py +158 -51
- pygeodesy/vector3d.py +13 -52
- pygeodesy/vector3dBase.py +81 -63
- pygeodesy/webmercator.py +3 -3
- pygeodesy/wgrs.py +20 -22
- {PyGeodesy-24.8.4.dist-info → PyGeodesy-24.8.24.dist-info}/top_level.txt +0 -0
pygeodesy/units.py
CHANGED
|
@@ -27,7 +27,7 @@ from pygeodesy.unitsBase import Float, Int, _NamedUnit, Radius, Str, Fmt, fstr
|
|
|
27
27
|
from math import degrees, isnan, radians
|
|
28
28
|
|
|
29
29
|
__all__ = _ALL_LAZY.units
|
|
30
|
-
__version__ = '24.08.
|
|
30
|
+
__version__ = '24.08.13'
|
|
31
31
|
|
|
32
32
|
|
|
33
33
|
class Float_(Float):
|
|
@@ -193,7 +193,7 @@ class Degrees(Float):
|
|
|
193
193
|
def toRepr(self, std=False, **prec_fmt_ints): # PYCHOK prec=8, ...
|
|
194
194
|
'''Return a representation of this C{Degrees}.
|
|
195
195
|
|
|
196
|
-
@kwarg std: If C{True} return the standard C{repr}, otherwise
|
|
196
|
+
@kwarg std: If C{True}, return the standard C{repr}, otherwise
|
|
197
197
|
the named representation (C{bool}).
|
|
198
198
|
|
|
199
199
|
@see: Methods L{Degrees.toStr}, L{Float.toRepr} and function
|
|
@@ -290,7 +290,7 @@ class Radians(Float):
|
|
|
290
290
|
def toRepr(self, std=False, **prec_fmt_ints): # PYCHOK prec=8, ...
|
|
291
291
|
'''Return a representation of this C{Radians}.
|
|
292
292
|
|
|
293
|
-
@kwarg std: If C{True} return the standard C{repr}, otherwise
|
|
293
|
+
@kwarg std: If C{True}, return the standard C{repr}, otherwise
|
|
294
294
|
the named representation (C{bool}).
|
|
295
295
|
|
|
296
296
|
@see: Methods L{Radians.toStr}, L{Float.toRepr} and function
|
pygeodesy/unitsBase.py
CHANGED
|
@@ -15,7 +15,7 @@ from pygeodesy.named import modulename, _Named, property_doc_
|
|
|
15
15
|
from pygeodesy.streprs import Fmt, fstr
|
|
16
16
|
|
|
17
17
|
__all__ = _ALL_LAZY.unitsBase
|
|
18
|
-
__version__ = '24.
|
|
18
|
+
__version__ = '24.08.13'
|
|
19
19
|
|
|
20
20
|
|
|
21
21
|
class _NamedUnit(_Named):
|
|
@@ -156,7 +156,7 @@ class Float(float, _NamedUnit):
|
|
|
156
156
|
def toRepr(self, std=False, **prec_fmt_ints): # PYCHOK prec=8, ...
|
|
157
157
|
'''Return a representation of this C{Float}.
|
|
158
158
|
|
|
159
|
-
@kwarg std: If C{True} return the standard C{repr},
|
|
159
|
+
@kwarg std: If C{True}, return the standard C{repr},
|
|
160
160
|
otherwise the named representation (C{bool}).
|
|
161
161
|
|
|
162
162
|
@see: Function L{fstr<pygeodesy.streprs.fstr>} and methods
|
|
@@ -216,7 +216,7 @@ class Int(int, _NamedUnit):
|
|
|
216
216
|
def toRepr(self, std=False, **unused): # PYCHOK **unused
|
|
217
217
|
'''Return a representation of this C{Int}.
|
|
218
218
|
|
|
219
|
-
@kwarg std: If C{True} return the standard C{repr},
|
|
219
|
+
@kwarg std: If C{True}, return the standard C{repr},
|
|
220
220
|
otherwise the named representation (C{bool}).
|
|
221
221
|
|
|
222
222
|
@see: Method L{Int.__repr__} for more documentation.
|
|
@@ -298,7 +298,7 @@ class Str(str, _NamedUnit):
|
|
|
298
298
|
def toRepr(self, std=False, **unused): # PYCHOK **unused
|
|
299
299
|
'''Return a representation of this C{Str}.
|
|
300
300
|
|
|
301
|
-
@kwarg std: If C{True} return the standard C{repr},
|
|
301
|
+
@kwarg std: If C{True}, return the standard C{repr},
|
|
302
302
|
otherwise the named representation (C{bool}).
|
|
303
303
|
|
|
304
304
|
@see: Method L{Str.__repr__} for more documentation.
|
pygeodesy/utmupsBase.py
CHANGED
|
@@ -27,7 +27,7 @@ from pygeodesy.units import Band, Easting, Northing, Scalar, Zone
|
|
|
27
27
|
from pygeodesy.utily import _Wrap, wrap360
|
|
28
28
|
|
|
29
29
|
__all__ = _ALL_LAZY.utmupsBase
|
|
30
|
-
__version__ = '24.
|
|
30
|
+
__version__ = '24.08.13'
|
|
31
31
|
|
|
32
32
|
_UPS_BANDS = _A_, _B_, _Y_, _Z_ # UPS polar bands SE, SW, NE, NW
|
|
33
33
|
# _UTM_BANDS = _MODS.utm._Bands
|
|
@@ -157,8 +157,8 @@ class UtmUpsBase(_NamedBase):
|
|
|
157
157
|
def eastingnorthing2(self, falsed=True):
|
|
158
158
|
'''Return easting and northing, falsed or unfalsed.
|
|
159
159
|
|
|
160
|
-
@kwarg falsed: If C{True} return easting and northing falsed
|
|
161
|
-
(C{bool})
|
|
160
|
+
@kwarg falsed: If C{True}, return easting and northing falsed,
|
|
161
|
+
otherwise unfalsed (C{bool}).
|
|
162
162
|
|
|
163
163
|
@return: An L{EasNor2Tuple}C{(easting, northing)} in C{meter}.
|
|
164
164
|
'''
|
pygeodesy/vector2d.py
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
# -*- coding: utf-8 -*-
|
|
3
3
|
|
|
4
4
|
u'''2- or 3-D vectorial functions L{circin6}, L{circum3}, L{circum4_},
|
|
5
|
-
L{iscolinearWith}, L{meeus2}, L{nearestOn}, L{radii11}
|
|
5
|
+
L{iscolinearWith}, L{meeus2}, L{nearestOn}, L{radii11}, L{soddy4} and
|
|
6
|
+
L{trilaterate2d2}.
|
|
6
7
|
'''
|
|
7
8
|
|
|
8
9
|
from pygeodesy.basics import len2, map2, _xnumpy
|
|
@@ -23,14 +24,14 @@ from pygeodesy.namedTuples import LatLon3Tuple, Vector2Tuple
|
|
|
23
24
|
# from pygeodesy.props import Property_RO # from .named
|
|
24
25
|
from pygeodesy.streprs import Fmt, unstr
|
|
25
26
|
from pygeodesy.units import Float, Int, Meter, Radius, Radius_
|
|
26
|
-
from pygeodesy.vector3d import iscolinearWith, nearestOn, _nearestOn2, _nVc,
|
|
27
|
-
|
|
27
|
+
from pygeodesy.vector3d import iscolinearWith, nearestOn, _nearestOn2, _nVc, \
|
|
28
|
+
_otherV3d, trilaterate3d2, Vector3d # PYCHOK unused
|
|
28
29
|
|
|
29
30
|
from contextlib import contextmanager
|
|
30
31
|
# from math import fabs, sqrt # from .fmath
|
|
31
32
|
|
|
32
33
|
__all__ = _ALL_LAZY.vector2d
|
|
33
|
-
__version__ = '24.
|
|
34
|
+
__version__ = '24.08.19'
|
|
34
35
|
|
|
35
36
|
_cA_ = 'cA'
|
|
36
37
|
_cB_ = 'cB'
|
|
@@ -115,6 +116,16 @@ class Soddy4Tuple(_NamedTuple):
|
|
|
115
116
|
_Units_ = ( Radius, _Pass, _Pass, Radius)
|
|
116
117
|
|
|
117
118
|
|
|
119
|
+
class Triaxum5Tuple(_NamedTuple):
|
|
120
|
+
'''5-Tuple C{(a, b, c, rank, residuals)} with the (unordered) triaxial radii
|
|
121
|
+
C{a}, C{b} and C{c} of an ellipsoid I{least-squares} fitted through given
|
|
122
|
+
points and the C{rank} and C{residuals} -if any- from U{numpy.linalg.lstsq
|
|
123
|
+
<https://NumPy.org/doc/stable/reference/generated/numpy.linalg.lstsq.html>}.
|
|
124
|
+
'''
|
|
125
|
+
_Names_ = (_a_, _b_, _c_, _rank_, _residuals_)
|
|
126
|
+
_Units_ = ( Radius, Radius, Radius, Int, _Pass)
|
|
127
|
+
|
|
128
|
+
|
|
118
129
|
def circin6(point1, point2, point3, eps=EPS4, useZ=True):
|
|
119
130
|
'''Return the radius and center of the I{inscribed} aka I{Incircle} of
|
|
120
131
|
a (2- or 3-D) triangle.
|
|
@@ -187,7 +198,7 @@ def circum3(point1, point2, point3, circum=True, eps=EPS4, useZ=True):
|
|
|
187
198
|
C{Vector4Tuple}).
|
|
188
199
|
@arg point3: Third point (C{Cartesian}, L{Vector3d}, C{Vector3Tuple} or
|
|
189
200
|
C{Vector4Tuple}).
|
|
190
|
-
@kwarg circum: If C{True} return the C{circumradius} and C{circumcenter}
|
|
201
|
+
@kwarg circum: If C{True}, return the C{circumradius} and C{circumcenter}
|
|
191
202
|
always, ignoring the I{Meeus}' Type I case (C{bool}).
|
|
192
203
|
@kwarg eps: Tolerance for function L{pygeodesy.trilaterate3d2} if C{B{useZ}
|
|
193
204
|
is True} else L{pygeodesy.trilaterate2d2}.
|
|
@@ -232,16 +243,16 @@ def _circum3(p1, point2, point3, circum=True, eps=EPS4, useZ=True, dLL3=False,
|
|
|
232
243
|
return Circum3Tuple(r, c, d)
|
|
233
244
|
|
|
234
245
|
|
|
235
|
-
def
|
|
246
|
+
def circum4(points, useZ=True, **Vector_and_kwds):
|
|
236
247
|
'''Best-fit a sphere through three or more (3-D) points.
|
|
237
248
|
|
|
238
|
-
@arg points:
|
|
249
|
+
@arg points: Iterable of points (each a C{Cartesian}, L{Vector3d}, C{Vector3Tuple}
|
|
239
250
|
or C{Vector4Tuple}).
|
|
240
|
-
@kwarg
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
251
|
+
@kwarg useZ: If C{True}, use the points' Z component, otherwise force C{z=INT0}
|
|
252
|
+
(C{bool}).
|
|
253
|
+
@kwarg Vector_and_kwds: Optional class C{B{Vector}=None} to return the center point
|
|
254
|
+
and optional, additional B{C{Vector}} keyword arguments, otherwise
|
|
255
|
+
the first B{C{points}}' (sub-)class is used.
|
|
245
256
|
|
|
246
257
|
@return: L{Circum4Tuple}C{(radius, center, rank, residuals)} with C{center} an
|
|
247
258
|
instance of C{B{points}[0]}' (sub-)class or B{C{Vector}} if specified.
|
|
@@ -255,40 +266,47 @@ def circum4_(*points, **useZ_Vector_and_kwds):
|
|
|
255
266
|
|
|
256
267
|
@raise TypeError: One of the B{C{points}} is invalid.
|
|
257
268
|
|
|
258
|
-
@see: Functions L{pygeodesy.circum3} and L{pygeodesy.meeus2},
|
|
259
|
-
Squares Sphere Fit
|
|
260
|
-
U{Appendix A<https://hdl.handle.net/10019.1/98627>}, U{numpy.linalg.lstsq
|
|
261
|
-
NumPy.org/doc/stable/reference/generated/numpy.linalg.lstsq.html>} and U{Eberly
|
|
262
|
-
<https://www.sci.Utah.EDU/~balling/FEtools/doc_files/LeastSquaresFitting.pdf>}.
|
|
269
|
+
@see: Functions L{pygeodesy.circum3} and L{pygeodesy.meeus2}, I{Charles Jekel}'s
|
|
270
|
+
U{"Least Squares Sphere Fit"<https://Jekel.me/2015/Least-Squares-Sphere-Fit/>},
|
|
271
|
+
U{Appendix A<https://hdl.handle.net/10019.1/98627>}, U{numpy.linalg.lstsq
|
|
272
|
+
<https://NumPy.org/doc/stable/reference/generated/numpy.linalg.lstsq.html>} and U{Eberly
|
|
273
|
+
6<https://www.sci.Utah.EDU/~balling/FEtools/doc_files/LeastSquaresFitting.pdf>}.
|
|
263
274
|
'''
|
|
264
|
-
def _useZ_kwds(useZ=True, **kwds):
|
|
265
|
-
return useZ, kwds
|
|
266
|
-
|
|
267
275
|
n, ps = len2(points)
|
|
268
276
|
if n < 3:
|
|
269
277
|
raise PointsError(points=n, txt=_too_(_few_))
|
|
270
|
-
useZ, kwds = _useZ_kwds(**useZ_Vector_and_kwds)
|
|
271
278
|
|
|
272
279
|
A, b = [], []
|
|
273
280
|
for i, p in enumerate(ps):
|
|
274
281
|
v = _otherV3d(useZ=useZ, i=i, points=p)
|
|
275
|
-
A.append(v.times(_2_0).
|
|
282
|
+
A.append(v.times(_2_0).xyz3 + _1_0_1T)
|
|
276
283
|
b.append(v.length2)
|
|
277
284
|
|
|
278
|
-
with _numpy(
|
|
285
|
+
with _numpy(circum4, n=n) as _np:
|
|
279
286
|
A = _np.array(A).reshape((n, 4))
|
|
280
287
|
b = _np.array(b).reshape((n, 1))
|
|
281
|
-
C, R, rk
|
|
282
|
-
C = map2(float, C)
|
|
283
|
-
R = map2(float, R) # empty if rk < 4 or n <= 4
|
|
288
|
+
C, R, rk = _np.least_squares3(A, b)
|
|
284
289
|
|
|
285
|
-
c = Vector3d(*C[:3], name__=
|
|
290
|
+
c = Vector3d(*C[:3], name__=circum4) # .__name__
|
|
286
291
|
r = Radius(sqrt(fsumf_(C[3], *c.x2y2z2)), name=c.name)
|
|
287
292
|
|
|
288
|
-
c = _nVc(c, **_xkwds(
|
|
293
|
+
c = _nVc(c, **_xkwds(Vector_and_kwds, clas=ps[0].classof, name=c.name))
|
|
289
294
|
return Circum4Tuple(r, c, rk, R)
|
|
290
295
|
|
|
291
296
|
|
|
297
|
+
def circum4_(*points, **useZ_Vector_and_kwds):
|
|
298
|
+
'''Best-fit a sphere through three or more (3-D) positional points.
|
|
299
|
+
|
|
300
|
+
@arg points: The points (each a C{Cartesian}, L{Vector3d}, C{Vector3Tuple}
|
|
301
|
+
or C{Vector4Tuple}), all positional.
|
|
302
|
+
@kwarg useZ_Vector_and_kwds: Keyword arguments C{B{useZ}=True} and
|
|
303
|
+
C{B{Vector}=None}, see function L{circum4}.
|
|
304
|
+
|
|
305
|
+
@see: Function L{circum4} for further details.
|
|
306
|
+
'''
|
|
307
|
+
return circum4(points, **useZ_Vector_and_kwds)
|
|
308
|
+
|
|
309
|
+
|
|
292
310
|
def _iscolinearWith(p, point1, point2, eps=EPS, useZ=True):
|
|
293
311
|
# (INTERNAL) Check colinear, see L{iscolinearWith} above,
|
|
294
312
|
# separated to allow callers to embellish any exceptions
|
|
@@ -308,7 +326,7 @@ def meeus2(point1, point2, point3, circum=False, useZ=True):
|
|
|
308
326
|
C{Vector4Tuple} or C{Vector2Tuple} if C{B{useZ}=False}).
|
|
309
327
|
@arg point3: Third point (C{Cartesian}, L{Vector3d}, C{Vector3Tuple},
|
|
310
328
|
C{Vector4Tuple} or C{Vector2Tuple} if C{B{useZ}=False}).
|
|
311
|
-
@kwarg circum: If C{True} return the C{circumradius} and C{circumcenter}
|
|
329
|
+
@kwarg circum: If C{True}, return the C{circumradius} and C{circumcenter}
|
|
312
330
|
always, overriding I{Meeus}' Type II case (C{bool}).
|
|
313
331
|
@kwarg useZ: If C{True}, use the Z components, otherwise force C{z=INT0} (C{bool}).
|
|
314
332
|
|
|
@@ -359,8 +377,9 @@ def _meeus4(A, point2, point3, circum=False, useZ=True, clas=None, **clas_kwds):
|
|
|
359
377
|
_Fsumf_(_1_0, -b, c) * _Fsumf_(_N_1_0, b, c)
|
|
360
378
|
r = R.fover(a)
|
|
361
379
|
if r < EPS02:
|
|
362
|
-
|
|
363
|
-
|
|
380
|
+
t = _coincident_ if b < EPS0 or c < EPS0 else (
|
|
381
|
+
_colinear_ if _iscolinearWith(A, B, C) else _invalid_)
|
|
382
|
+
raise IntersectionError(t)
|
|
364
383
|
r = b * c / sqrt(r)
|
|
365
384
|
t = None # Meeus' Type II
|
|
366
385
|
else: # obtuse or right angle at A
|
|
@@ -393,11 +412,13 @@ class _numpy(object): # see also .formy._idllmn6, .geodesicw._wargs, .latlonBas
|
|
|
393
412
|
def array(self):
|
|
394
413
|
return self.np.array
|
|
395
414
|
|
|
396
|
-
|
|
397
|
-
def least_squares4(self):
|
|
415
|
+
def least_squares3(self, A, b):
|
|
398
416
|
'''Linear least-squares function.
|
|
399
417
|
'''
|
|
400
|
-
|
|
418
|
+
C, R, rk, _ = self.np.linalg.lstsq(A, b, rcond=None) # to silence warning
|
|
419
|
+
C = map2(float, C)
|
|
420
|
+
R = map2(float, R) # empty if rk < 4 or n <= 4
|
|
421
|
+
return C, R, int(rk)
|
|
401
422
|
|
|
402
423
|
@Property_RO
|
|
403
424
|
def np(self):
|
|
@@ -578,6 +599,49 @@ def soddy4(point1, point2, point3, eps=EPS4, useZ=True):
|
|
|
578
599
|
return Soddy4Tuple(r, c, d, t.roS)
|
|
579
600
|
|
|
580
601
|
|
|
602
|
+
def triaxum5(points, useZ=True):
|
|
603
|
+
'''Best-fit a triaxial ellipsoid through three or more (3-D) points.
|
|
604
|
+
|
|
605
|
+
@arg points: Iterable of points (each a C{Cartesian}, L{Vector3d}, C{Vector3Tuple}
|
|
606
|
+
or C{Vector4Tuple}).
|
|
607
|
+
@kwarg useZ: If C{True}, use the points' Z component, otherwise force C{z=INT0}
|
|
608
|
+
(C{bool}).
|
|
609
|
+
|
|
610
|
+
@return: L{Triaxum5Tuple}C{(a, b, c, rank, residuals)} with the unordered triaxial
|
|
611
|
+
radii C{a}, C{b} and C{c} in C{meter}, same units as the points' coordinates.
|
|
612
|
+
|
|
613
|
+
@raise ImportError: Package C{numpy} not found, not installed or older than version 1.10.
|
|
614
|
+
|
|
615
|
+
@raise NumPyError: Some C{numpy} issue.
|
|
616
|
+
|
|
617
|
+
@raise PointsError: Too few B{C{points}}.
|
|
618
|
+
|
|
619
|
+
@raise TypeError: One of the B{C{points}} is invalid.
|
|
620
|
+
|
|
621
|
+
@see: I{Charles Jekel}'s U{"Least Squares Ellipsoid Fit"<https://Jekel.me/2020/Least-Squares-Ellipsoid-Fit/>}
|
|
622
|
+
and U{numpy.linalg.lstsq<https://NumPy.org/doc/stable/reference/generated/numpy.linalg.lstsq.html>}.
|
|
623
|
+
'''
|
|
624
|
+
n, ps = len2(points)
|
|
625
|
+
if n < 3:
|
|
626
|
+
raise PointsError(points=n, txt=_too_(_few_))
|
|
627
|
+
|
|
628
|
+
A = []
|
|
629
|
+
for i, p in enumerate(ps):
|
|
630
|
+
v = _otherV3d(useZ=useZ, i=i, points=p)
|
|
631
|
+
A.append(v.x2y2z2)
|
|
632
|
+
|
|
633
|
+
with _numpy(triaxum5, n=n) as _np:
|
|
634
|
+
A = _np.array(A)
|
|
635
|
+
b = _1_0_1T * n
|
|
636
|
+
T, R, rk = _np.least_squares3(A, b)
|
|
637
|
+
|
|
638
|
+
def _sqrt(x):
|
|
639
|
+
return sqrt(_1_0 / x) if x else _0_0
|
|
640
|
+
|
|
641
|
+
a, b, c = map(_sqrt, T)
|
|
642
|
+
return Triaxum5Tuple(a, b, c, rk, R)
|
|
643
|
+
|
|
644
|
+
|
|
581
645
|
def _tricenter3d2(p1, r1, p2, r2, p3, r3, eps=EPS4, useZ=True, dLL3=False, **kwds):
|
|
582
646
|
# (INTERNAL) Trilaterate and disambiguate the 3-D center
|
|
583
647
|
d, kwds = None, _xkwds(kwds, eps=eps, coin=True)
|
|
@@ -605,6 +669,45 @@ def _tricenter3d2(p1, r1, p2, r2, p3, r3, eps=EPS4, useZ=True, dLL3=False, **kwd
|
|
|
605
669
|
return c, d
|
|
606
670
|
|
|
607
671
|
|
|
672
|
+
def trilaterate2d2(x1, y1, radius1, x2, y2, radius2, x3, y3, radius3,
|
|
673
|
+
eps=None, **Vector_and_kwds):
|
|
674
|
+
'''Trilaterate three circles, each given as a (2-D) center and a radius.
|
|
675
|
+
|
|
676
|
+
@arg x1: Center C{x} coordinate of the 1st circle (C{scalar}).
|
|
677
|
+
@arg y1: Center C{y} coordinate of the 1st circle (C{scalar}).
|
|
678
|
+
@arg radius1: Radius of the 1st circle (C{scalar}).
|
|
679
|
+
@arg x2: Center C{x} coordinate of the 2nd circle (C{scalar}).
|
|
680
|
+
@arg y2: Center C{y} coordinate of the 2nd circle (C{scalar}).
|
|
681
|
+
@arg radius2: Radius of the 2nd circle (C{scalar}).
|
|
682
|
+
@arg x3: Center C{x} coordinate of the 3rd circle (C{scalar}).
|
|
683
|
+
@arg y3: Center C{y} coordinate of the 3rd circle (C{scalar}).
|
|
684
|
+
@arg radius3: Radius of the 3rd circle (C{scalar}).
|
|
685
|
+
@kwarg eps: Tolerance to check the trilaterated point I{delta} on
|
|
686
|
+
all 3 circles (C{scalar}) or C{None} for no checking.
|
|
687
|
+
@kwarg Vector_and_kwds: Optional class C{B{Vector}=None} to return
|
|
688
|
+
the trilateration and optional, additional
|
|
689
|
+
B{C{Vector}} keyword arguments).
|
|
690
|
+
|
|
691
|
+
@return: Trilaterated point as C{B{Vector}(x, y, **B{Vector_kwds})}
|
|
692
|
+
or L{Vector2Tuple}C{(x, y)} if C{B{Vector} is None}.
|
|
693
|
+
|
|
694
|
+
@raise IntersectionError: No intersection, near-concentric or -colinear
|
|
695
|
+
centers, trilateration failed some other way
|
|
696
|
+
or the trilaterated point is off one circle
|
|
697
|
+
by more than B{C{eps}}.
|
|
698
|
+
|
|
699
|
+
@raise UnitError: Invalid B{C{radius1}}, B{C{radius2}} or B{C{radius3}}.
|
|
700
|
+
|
|
701
|
+
@see: U{Issue #49<https://GitHub.com/mrJean1/PyGeodesy/issues/49>},
|
|
702
|
+
U{Find X location using 3 known (X,Y) location using trilateration
|
|
703
|
+
<https://math.StackExchange.com/questions/884807>} and function
|
|
704
|
+
L{pygeodesy.trilaterate3d2}.
|
|
705
|
+
'''
|
|
706
|
+
return _trilaterate2d2(x1, y1, radius1,
|
|
707
|
+
x2, y2, radius2,
|
|
708
|
+
x3, y3, radius3, eps=eps, **Vector_and_kwds)
|
|
709
|
+
|
|
710
|
+
|
|
608
711
|
def _trilaterate2d2(x1, y1, radius1, x2, y2, radius2, x3, y3, radius3,
|
|
609
712
|
coin=False, eps=None,
|
|
610
713
|
Vector=None, **Vector_kwds):
|
|
@@ -662,16 +765,21 @@ def _trilaterate2d2(x1, y1, radius1, x2, y2, radius2, x3, y3, radius3,
|
|
|
662
765
|
return t
|
|
663
766
|
|
|
664
767
|
|
|
665
|
-
def _trilaterate3d2(c1, r1, c2, r2, c3, r3, eps=EPS4, coin=False,
|
|
768
|
+
def _trilaterate3d2(c1, r1, c2, r2, c3, r3, eps=EPS4, coin=False, # MCCABE 13
|
|
666
769
|
**clas_Vector_and_kwds):
|
|
667
770
|
# (INTERNAL) Intersect three spheres or circles, see function
|
|
668
771
|
# L{pygeodesy.trilaterate3d2}, separated to allow callers to
|
|
669
772
|
# embellish exceptions, like C{FloatingPointError}s from C{numpy}
|
|
670
773
|
|
|
671
|
-
def
|
|
672
|
-
#
|
|
774
|
+
def _Arow4(c):
|
|
775
|
+
# make a row for matrix A (1, -2x, -2y, -2z)
|
|
776
|
+
return _1_0_1T + c.times(_N_2_0).xyz3
|
|
777
|
+
|
|
778
|
+
def _F4d3(F):
|
|
779
|
+
# map numpy 4-vector to floats and xyz3
|
|
673
780
|
T = map2(float, F)
|
|
674
|
-
|
|
781
|
+
t = T[1:]
|
|
782
|
+
return T, t, Vector3d(*t)
|
|
675
783
|
|
|
676
784
|
def _N3(t01, x, z):
|
|
677
785
|
# compute x, y and z and return as B{C{clas}} or B{C{Vector}}
|
|
@@ -684,22 +792,22 @@ def _trilaterate3d2(c1, r1, c2, r2, c3, r3, eps=EPS4, coin=False,
|
|
|
684
792
|
rs = (r1, Radius_(radius2=r2, low=EPS),
|
|
685
793
|
Radius_(radius3=r3, low=EPS))
|
|
686
794
|
|
|
687
|
-
# get matrix A[3 x 4], its pseudo-inverse
|
|
688
|
-
A = [(
|
|
795
|
+
# get matrix A[3 x 4], its null_space Z and pseudo-inverse
|
|
796
|
+
A = [_Arow4(c) for c in (c1, c2, c3)]
|
|
689
797
|
with _numpy(trilaterate3d2, A=A, eps=eps) as _np:
|
|
690
798
|
Z, _ = _np.null_space2(A, eps)
|
|
691
799
|
if Z is not None:
|
|
692
|
-
Z, z =
|
|
800
|
+
Z, _, z = _F4d3(Z) # [4 x 1]
|
|
693
801
|
z2 = z.length2
|
|
694
802
|
A = _np.pseudo_inverse(A) # [4 x 3]
|
|
695
803
|
bs = [c.length2 for c in (c1, c2, c3)]
|
|
696
|
-
#
|
|
804
|
+
# perturb radii slightly by eps and eps * 4
|
|
697
805
|
for p in _tri5perturbs(eps, min(rs)):
|
|
698
806
|
b = [((r + p)**2 - b) for r, b in zip(rs, bs)] # [3 x 1]
|
|
699
|
-
X, x =
|
|
700
|
-
# quadratic polynomial, coefficients
|
|
701
|
-
t = _np.real_roots(fdot(X, _N_1_0, *
|
|
702
|
-
fdot(Z, _N_0_5, *
|
|
807
|
+
X, t, x = _F4d3(A.dot(b)) # [4 * 1]
|
|
808
|
+
# quadratic polynomial, coefficients order (^0, ^1, ^2)
|
|
809
|
+
t = _np.real_roots(fdot(X, _N_1_0, *t),
|
|
810
|
+
fdot(Z, _N_0_5, *t) * _2_0, z2)
|
|
703
811
|
if t:
|
|
704
812
|
v = _N3(t[0], x, z)
|
|
705
813
|
if len(t) < 2: # one intersection
|
|
@@ -711,10 +819,7 @@ def _trilaterate3d2(c1, r1, c2, r2, c3, r3, eps=EPS4, coin=False,
|
|
|
711
819
|
t = (u, v) if u.x < v.x else (v, u)
|
|
712
820
|
return t
|
|
713
821
|
|
|
714
|
-
|
|
715
|
-
# create the explanation and and throw an IntersectionError
|
|
716
|
-
|
|
717
|
-
def _no_intersection(coin):
|
|
822
|
+
def _no_itersection(coin, Z):
|
|
718
823
|
t = _no_(_intersection_)
|
|
719
824
|
if coin:
|
|
720
825
|
def _reprs(*crs):
|
|
@@ -726,11 +831,13 @@ def _trilaterate3d2(c1, r1, c2, r2, c3, r3, eps=EPS4, coin=False,
|
|
|
726
831
|
t = _COMMASPACE_(t, _no_(_numpy.null_space2.__name__))
|
|
727
832
|
return t
|
|
728
833
|
|
|
834
|
+
# coincident, concentric, colinear, too distant, no intersection:
|
|
835
|
+
# create the explanation and and throw an IntersectionError
|
|
729
836
|
t = _tri4near2far(c1, r1, c2, r2, coin) or \
|
|
730
837
|
_tri4near2far(c1, r1, c3, r3, coin) or \
|
|
731
838
|
_tri4near2far(c2, r2, c3, r3, coin) or (
|
|
732
839
|
_colinear_ if _iscolinearWith(c1, c2, c3, eps=eps) else
|
|
733
|
-
|
|
840
|
+
_no_itersection(coin, Z))
|
|
734
841
|
raise IntersectionError(t, txt=None)
|
|
735
842
|
|
|
736
843
|
|
pygeodesy/vector3d.py
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
|
|
4
4
|
u'''Extended 3-D vector class L{Vector3d} and functions.
|
|
5
5
|
|
|
6
|
-
Function L{intersection3d3}, L{intersections2}, L{parse3d}, L{sumOf}
|
|
7
|
-
L{
|
|
6
|
+
Function L{intersection3d3}, L{intersections2}, L{parse3d}, L{sumOf} and
|
|
7
|
+
L{trilaterate3d2}.
|
|
8
8
|
'''
|
|
9
9
|
|
|
10
10
|
from pygeodesy.constants import EPS, EPS0, EPS1, EPS4, INT0, isnear0, \
|
|
@@ -31,7 +31,7 @@ from pygeodesy.vector3dBase import Vector3dBase
|
|
|
31
31
|
# from math import fabs, sqrt # from .fmath
|
|
32
32
|
|
|
33
33
|
__all__ = _ALL_LAZY.vector3d
|
|
34
|
-
__version__ = '24.
|
|
34
|
+
__version__ = '24.08.18'
|
|
35
35
|
|
|
36
36
|
_vector2d = _MODS.into(vector2d=__name__)
|
|
37
37
|
|
|
@@ -101,7 +101,7 @@ class Vector3d(Vector3dBase):
|
|
|
101
101
|
or C{Vector4Tuple}).
|
|
102
102
|
@arg point3: Third point (C{Cartesian}, L{Vector3d}, C{Vector3Tuple}
|
|
103
103
|
or C{Vector4Tuple}).
|
|
104
|
-
@kwarg circum: If C{True} return the C{circumradius} and C{circumcenter},
|
|
104
|
+
@kwarg circum: If C{True}, return the C{circumradius} and C{circumcenter},
|
|
105
105
|
always, ignoring the I{Meeus}' Type I case (C{bool}).
|
|
106
106
|
@kwarg eps: Tolerance passed to function L{pygeodesy.trilaterate3d2}.
|
|
107
107
|
|
|
@@ -175,7 +175,7 @@ class Vector3d(Vector3dBase):
|
|
|
175
175
|
or C{Vector4Tuple}).
|
|
176
176
|
@arg point3: Third point (C{Cartesian}, L{Vector3d}, C{Vector3Tuple}
|
|
177
177
|
or C{Vector4Tuple}).
|
|
178
|
-
@kwarg circum: If C{True} return the C{circumradius} and C{circumcenter}
|
|
178
|
+
@kwarg circum: If C{True}, return the C{circumradius} and C{circumcenter}
|
|
179
179
|
always, overriding I{Meeus}' Type II case (C{bool}).
|
|
180
180
|
|
|
181
181
|
@return: L{Meeus2Tuple}C{(radius, Type)}, with C{Type} the C{circumcenter}
|
|
@@ -199,7 +199,7 @@ class Vector3d(Vector3dBase):
|
|
|
199
199
|
C{Vector4Tuple}).
|
|
200
200
|
@arg point2: End point (C{Cartesian}, L{Vector3d}, C{Vector3Tuple} or
|
|
201
201
|
C{Vector4Tuple}).
|
|
202
|
-
@kwarg within: If C{True} return the closest point between the given
|
|
202
|
+
@kwarg within: If C{True}, return the closest point between the given
|
|
203
203
|
points, otherwise the closest point on the extended
|
|
204
204
|
line through both points (C{bool}).
|
|
205
205
|
|
|
@@ -406,7 +406,7 @@ def _intersect3d3(start1, end1, start2, end2, eps=EPS, useZ=False): # MCCABE 16
|
|
|
406
406
|
# Get the C{s1'} and C{e1'} corners of a right-angle
|
|
407
407
|
# triangle with the hypotenuse thru C{s1} at bearing
|
|
408
408
|
# C{b1} and the right angle at C{s2}
|
|
409
|
-
dx, dy, d = s2.minus(s1).
|
|
409
|
+
dx, dy, d = s2.minus(s1).xyz3
|
|
410
410
|
if useZ and not isnear0(d): # not supported
|
|
411
411
|
raise IntersectionError(useZ=d, bearing=b1)
|
|
412
412
|
s, c = sincos2d(b1)
|
|
@@ -535,7 +535,7 @@ def intersections2(center1, radius1, center2, radius2, sphere=True, **Vector_and
|
|
|
535
535
|
C{Vector3Tuple} or C{Vector4Tuple}).
|
|
536
536
|
@arg radius2: Radius of the second sphere or circle (same units as the
|
|
537
537
|
B{C{center1}} and B{C{center2}} coordinates).
|
|
538
|
-
@kwarg sphere: If C{True} compute the center and radius of the intersection of
|
|
538
|
+
@kwarg sphere: If C{True}, compute the center and radius of the intersection of
|
|
539
539
|
two spheres. If C{False}, ignore the C{z}-component and compute
|
|
540
540
|
the intersection of two circles (C{bool}).
|
|
541
541
|
@kwarg Vector_and_kwds: Optional class C{B{Vector}=None} to return the
|
|
@@ -657,7 +657,7 @@ def nearestOn(point, point1, point2, within=True, useZ=True, Vector=None, **Vect
|
|
|
657
657
|
C{Vector4Tuple}).
|
|
658
658
|
@arg point2: End point (C{Cartesian}, L{Vector3d}, C{Vector3Tuple} or
|
|
659
659
|
C{Vector4Tuple}).
|
|
660
|
-
@kwarg within: If C{True} return the closest point between both given
|
|
660
|
+
@kwarg within: If C{True}, return the closest point between both given
|
|
661
661
|
points, otherwise the closest point on the extended line
|
|
662
662
|
through both points (C{bool}).
|
|
663
663
|
@kwarg useZ: If C{True}, use the Z components, otherwise force C{z=INT0} (C{bool}).
|
|
@@ -836,45 +836,6 @@ def sumOf(vectors, Vector=Vector3d, **Vector_kwds):
|
|
|
836
836
|
Vector(x, y, z, **_xkwds(Vector_kwds, name__=sumOf)) # .__name__
|
|
837
837
|
|
|
838
838
|
|
|
839
|
-
def trilaterate2d2(x1, y1, radius1, x2, y2, radius2, x3, y3, radius3,
|
|
840
|
-
eps=None, **Vector_and_kwds):
|
|
841
|
-
'''Trilaterate three circles, each given as a (2-D) center and a radius.
|
|
842
|
-
|
|
843
|
-
@arg x1: Center C{x} coordinate of the 1st circle (C{scalar}).
|
|
844
|
-
@arg y1: Center C{y} coordinate of the 1st circle (C{scalar}).
|
|
845
|
-
@arg radius1: Radius of the 1st circle (C{scalar}).
|
|
846
|
-
@arg x2: Center C{x} coordinate of the 2nd circle (C{scalar}).
|
|
847
|
-
@arg y2: Center C{y} coordinate of the 2nd circle (C{scalar}).
|
|
848
|
-
@arg radius2: Radius of the 2nd circle (C{scalar}).
|
|
849
|
-
@arg x3: Center C{x} coordinate of the 3rd circle (C{scalar}).
|
|
850
|
-
@arg y3: Center C{y} coordinate of the 3rd circle (C{scalar}).
|
|
851
|
-
@arg radius3: Radius of the 3rd circle (C{scalar}).
|
|
852
|
-
@kwarg eps: Tolerance to check the trilaterated point I{delta} on all
|
|
853
|
-
3 circles (C{scalar}) or C{None} for no checking.
|
|
854
|
-
@kwarg Vector_and_kwds: Optional class C{B{Vector}=None} to return the
|
|
855
|
-
trilateration and optional, additional B{C{Vector}}
|
|
856
|
-
keyword arguments, otherwise (L{Vector3d}).
|
|
857
|
-
|
|
858
|
-
@return: Trilaterated point as C{B{Vector}(x, y, **B{Vector_kwds})}
|
|
859
|
-
or L{Vector2Tuple}C{(x, y)} if C{B{Vector} is None}..
|
|
860
|
-
|
|
861
|
-
@raise IntersectionError: No intersection, near-concentric or -colinear
|
|
862
|
-
centers, trilateration failed some other way
|
|
863
|
-
or the trilaterated point is off one circle
|
|
864
|
-
by more than B{C{eps}}.
|
|
865
|
-
|
|
866
|
-
@raise UnitError: Invalid B{C{radius1}}, B{C{radius2}} or B{C{radius3}}.
|
|
867
|
-
|
|
868
|
-
@see: U{Issue #49<https://GitHub.com/mrJean1/PyGeodesy/issues/49>},
|
|
869
|
-
U{Find X location using 3 known (X,Y) location using trilateration
|
|
870
|
-
<https://math.StackExchange.com/questions/884807>} and function
|
|
871
|
-
L{pygeodesy.trilaterate3d2}.
|
|
872
|
-
'''
|
|
873
|
-
return _vector2d._trilaterate2d2(x1, y1, radius1,
|
|
874
|
-
x2, y2, radius2,
|
|
875
|
-
x3, y3, radius3, eps=eps, **Vector_and_kwds)
|
|
876
|
-
|
|
877
|
-
|
|
878
839
|
def trilaterate3d2(center1, radius1, center2, radius2, center3, radius3,
|
|
879
840
|
eps=EPS, **Vector_and_kwds):
|
|
880
841
|
'''Trilaterate three spheres, each given as a (3-D) center and a radius.
|
|
@@ -893,10 +854,10 @@ def trilaterate3d2(center1, radius1, center2, radius2, center3, radius3,
|
|
|
893
854
|
and C{z}).
|
|
894
855
|
@kwarg eps: Pertubation tolerance (C{scalar}), same units as C{x},
|
|
895
856
|
C{y} and C{z} or C{None} for no pertubations.
|
|
896
|
-
@kwarg Vector_and_kwds: Optional class C{B{Vector}=None} to return
|
|
897
|
-
trilateration and optional, additional
|
|
898
|
-
keyword arguments, otherwise
|
|
899
|
-
(sub-)class.
|
|
857
|
+
@kwarg Vector_and_kwds: Optional class C{B{Vector}=None} to return
|
|
858
|
+
the trilateration and optional, additional
|
|
859
|
+
B{C{Vector}} keyword arguments, otherwise
|
|
860
|
+
the B{C{center1}}'s (sub-)class.
|
|
900
861
|
|
|
901
862
|
@return: 2-Tuple with two trilaterated points, each a B{C{Vector}}
|
|
902
863
|
instance. Both points are the same instance if all three
|