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/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.02'
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.06.15'
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.06.12'
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}), otherwise unfalsed.
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} and L{soddy4}.
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, _otherV3d, \
27
- trilaterate2d2, trilaterate3d2, Vector3d # PYCHOK unused
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.05.17'
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 circum4_(*points, **useZ_Vector_and_kwds):
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: The points (each a C{Cartesian}, L{Vector3d}, C{Vector3Tuple},
249
+ @arg points: Iterable of points (each a C{Cartesian}, L{Vector3d}, C{Vector3Tuple}
239
250
  or C{Vector4Tuple}).
240
- @kwarg useZ_Vector_and_kwds: Keyword arguments C{B{useZ}=True} (C{bool})
241
- to use the Z components, otherwise force all C{z=INT0}, class
242
- C{B{Vector}=None} to return the center point with optionally,
243
- additional nB{C{Vector}} keyword arguments, otherwise the
244
- first B{C{points}}' (sub-)class is used.
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}, Jekel, Charles F. U{I{Least
259
- Squares Sphere Fit}<https://Jekel.me/2015/Least-Squares-Sphere-Fit/>} Sep 13, 2015,
260
- U{Appendix A<https://hdl.handle.net/10019.1/98627>}, U{numpy.linalg.lstsq<https://
261
- NumPy.org/doc/stable/reference/generated/numpy.linalg.lstsq.html>} and U{Eberly 6
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).xyz + _1_0_1T)
282
+ A.append(v.times(_2_0).xyz3 + _1_0_1T)
276
283
  b.append(v.length2)
277
284
 
278
- with _numpy(circum4_, n=n) as _np:
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, _ = _np.least_squares4(A, b, rcond=None) # to silence warning
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__=circum4_) # .__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(kwds, clas=ps[0].classof, name=c.name))
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
- raise IntersectionError(_coincident_ if b < EPS0 or c < EPS0 else (
363
- _colinear_ if _iscolinearWith(A, B, C) else _invalid_))
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
- @Property_RO
397
- def least_squares4(self):
415
+ def least_squares3(self, A, b):
398
416
  '''Linear least-squares function.
399
417
  '''
400
- return self.np.linalg.lstsq
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 _F3d2(F):
672
- # map numpy 4-vector to floats tuple and Vector3d
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
- return T, Vector3d(*T[1:])
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 and null_space Z
688
- A = [(_1_0_1T + c.times(_N_2_0).xyz) for c in (c1, c2, c3)]
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 = _F3d2(Z) # [4 x 1]
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
- # perturbe radii and vector b slightly by eps and eps * 4
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 = _F3d2(A.dot(b))
700
- # quadratic polynomial, coefficients ordered (^0, ^1, ^2)
701
- t = _np.real_roots(fdot(X, _N_1_0, *x.xyz),
702
- fdot(Z, _N_0_5, *x.xyz) * _2_0, z2)
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
- # coincident, concentric, colinear, too distant, no intersection:
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
- _no_intersection(coin))
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{trilaterate2d2} and L{trilaterate3d2}.
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.06.18'
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).xyz
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 the
897
- trilateration and optional, additional B{C{Vector}}
898
- keyword arguments, otherwise B{C{center1}}'s
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