pygeodesy 24.3.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.
Files changed (115) hide show
  1. PyGeodesy-24.3.24.dist-info/METADATA +272 -0
  2. PyGeodesy-24.3.24.dist-info/RECORD +115 -0
  3. PyGeodesy-24.3.24.dist-info/WHEEL +6 -0
  4. PyGeodesy-24.3.24.dist-info/top_level.txt +1 -0
  5. pygeodesy/LICENSE +21 -0
  6. pygeodesy/__init__.py +615 -0
  7. pygeodesy/__main__.py +103 -0
  8. pygeodesy/albers.py +867 -0
  9. pygeodesy/auxilats/_CX_4.py +218 -0
  10. pygeodesy/auxilats/_CX_6.py +314 -0
  11. pygeodesy/auxilats/_CX_8.py +475 -0
  12. pygeodesy/auxilats/__init__.py +54 -0
  13. pygeodesy/auxilats/__main__.py +86 -0
  14. pygeodesy/auxilats/auxAngle.py +548 -0
  15. pygeodesy/auxilats/auxDLat.py +302 -0
  16. pygeodesy/auxilats/auxDST.py +296 -0
  17. pygeodesy/auxilats/auxLat.py +848 -0
  18. pygeodesy/auxilats/auxily.py +272 -0
  19. pygeodesy/azimuthal.py +1150 -0
  20. pygeodesy/basics.py +892 -0
  21. pygeodesy/booleans.py +2031 -0
  22. pygeodesy/cartesianBase.py +1062 -0
  23. pygeodesy/clipy.py +704 -0
  24. pygeodesy/constants.py +516 -0
  25. pygeodesy/css.py +660 -0
  26. pygeodesy/datums.py +752 -0
  27. pygeodesy/deprecated/__init__.py +61 -0
  28. pygeodesy/deprecated/bases.py +40 -0
  29. pygeodesy/deprecated/classes.py +262 -0
  30. pygeodesy/deprecated/consterns.py +54 -0
  31. pygeodesy/deprecated/datum.py +40 -0
  32. pygeodesy/deprecated/functions.py +375 -0
  33. pygeodesy/deprecated/nvector.py +48 -0
  34. pygeodesy/deprecated/rhumbBase.py +32 -0
  35. pygeodesy/deprecated/rhumbaux.py +33 -0
  36. pygeodesy/deprecated/rhumbsolve.py +33 -0
  37. pygeodesy/deprecated/rhumbx.py +33 -0
  38. pygeodesy/dms.py +986 -0
  39. pygeodesy/ecef.py +1348 -0
  40. pygeodesy/elevations.py +279 -0
  41. pygeodesy/ellipsoidalBase.py +1224 -0
  42. pygeodesy/ellipsoidalBaseDI.py +913 -0
  43. pygeodesy/ellipsoidalExact.py +343 -0
  44. pygeodesy/ellipsoidalGeodSolve.py +343 -0
  45. pygeodesy/ellipsoidalKarney.py +403 -0
  46. pygeodesy/ellipsoidalNvector.py +685 -0
  47. pygeodesy/ellipsoidalVincenty.py +590 -0
  48. pygeodesy/ellipsoids.py +2476 -0
  49. pygeodesy/elliptic.py +1198 -0
  50. pygeodesy/epsg.py +243 -0
  51. pygeodesy/errors.py +804 -0
  52. pygeodesy/etm.py +1190 -0
  53. pygeodesy/fmath.py +1013 -0
  54. pygeodesy/formy.py +1818 -0
  55. pygeodesy/frechet.py +865 -0
  56. pygeodesy/fstats.py +760 -0
  57. pygeodesy/fsums.py +1898 -0
  58. pygeodesy/gars.py +358 -0
  59. pygeodesy/geodesicw.py +581 -0
  60. pygeodesy/geodesicx/_C4_24.py +1699 -0
  61. pygeodesy/geodesicx/_C4_27.py +2395 -0
  62. pygeodesy/geodesicx/_C4_30.py +3301 -0
  63. pygeodesy/geodesicx/__init__.py +48 -0
  64. pygeodesy/geodesicx/__main__.py +91 -0
  65. pygeodesy/geodesicx/gx.py +1382 -0
  66. pygeodesy/geodesicx/gxarea.py +535 -0
  67. pygeodesy/geodesicx/gxbases.py +154 -0
  68. pygeodesy/geodesicx/gxline.py +669 -0
  69. pygeodesy/geodsolve.py +426 -0
  70. pygeodesy/geohash.py +914 -0
  71. pygeodesy/geoids.py +1884 -0
  72. pygeodesy/hausdorff.py +892 -0
  73. pygeodesy/heights.py +1155 -0
  74. pygeodesy/interns.py +687 -0
  75. pygeodesy/iters.py +545 -0
  76. pygeodesy/karney.py +919 -0
  77. pygeodesy/ktm.py +633 -0
  78. pygeodesy/latlonBase.py +1766 -0
  79. pygeodesy/lazily.py +960 -0
  80. pygeodesy/lcc.py +684 -0
  81. pygeodesy/ltp.py +1107 -0
  82. pygeodesy/ltpTuples.py +1563 -0
  83. pygeodesy/mgrs.py +721 -0
  84. pygeodesy/named.py +1324 -0
  85. pygeodesy/namedTuples.py +683 -0
  86. pygeodesy/nvectorBase.py +695 -0
  87. pygeodesy/osgr.py +781 -0
  88. pygeodesy/points.py +1686 -0
  89. pygeodesy/props.py +628 -0
  90. pygeodesy/resections.py +1048 -0
  91. pygeodesy/rhumb/__init__.py +46 -0
  92. pygeodesy/rhumb/aux_.py +397 -0
  93. pygeodesy/rhumb/bases.py +1148 -0
  94. pygeodesy/rhumb/ekx.py +563 -0
  95. pygeodesy/rhumb/solve.py +572 -0
  96. pygeodesy/simplify.py +647 -0
  97. pygeodesy/solveBase.py +472 -0
  98. pygeodesy/sphericalBase.py +724 -0
  99. pygeodesy/sphericalNvector.py +1264 -0
  100. pygeodesy/sphericalTrigonometry.py +1447 -0
  101. pygeodesy/streprs.py +627 -0
  102. pygeodesy/trf.py +2079 -0
  103. pygeodesy/triaxials.py +1484 -0
  104. pygeodesy/units.py +969 -0
  105. pygeodesy/unitsBase.py +349 -0
  106. pygeodesy/ups.py +538 -0
  107. pygeodesy/utily.py +1231 -0
  108. pygeodesy/utm.py +762 -0
  109. pygeodesy/utmups.py +318 -0
  110. pygeodesy/utmupsBase.py +517 -0
  111. pygeodesy/vector2d.py +785 -0
  112. pygeodesy/vector3d.py +968 -0
  113. pygeodesy/vector3dBase.py +1049 -0
  114. pygeodesy/webmercator.py +383 -0
  115. pygeodesy/wgrs.py +439 -0
pygeodesy/vector3d.py ADDED
@@ -0,0 +1,968 @@
1
+
2
+ # -*- coding: utf-8 -*-
3
+
4
+ u'''Extended 3-D vector class L{Vector3d} and functions.
5
+
6
+ Function L{intersection3d3}, L{intersections2}, L{parse3d}, L{sumOf},
7
+ L{trilaterate2d2} and L{trilaterate3d2}.
8
+ '''
9
+
10
+ from pygeodesy.constants import EPS, EPS0, EPS1, EPS4, INT0, isnear0, \
11
+ _0_0, _1_0
12
+ from pygeodesy.errors import IntersectionError, _ValueError, VectorError, \
13
+ _xattr, _xError, _xkwds, _xkwds_get, _xkwds_item2
14
+ from pygeodesy.fmath import euclid, fabs, fdot, hypot, sqrt, fsum1_
15
+ # from pygeodesy.fsums import fsum1_ # from .fmath
16
+ # from pygeodesy.formy import _radical2 # in _intersects2 below
17
+ from pygeodesy.interns import NN, _COMMA_, _concentric_, _intersection_, \
18
+ _near_, _negative_, _no_, _too_
19
+ from pygeodesy.iters import PointsIter, Fmt
20
+ from pygeodesy.lazily import _ALL_DOCS, _ALL_LAZY, _ALL_MODS as _MODS
21
+ from pygeodesy.named import _xnamed, _xotherError
22
+ from pygeodesy.namedTuples import Intersection3Tuple, NearestOn2Tuple, \
23
+ NearestOn6Tuple, Vector3Tuple # Vector4Tuple
24
+ # from pygeodesy.nvectorBase import _nsumOf # _MODS
25
+ # from pygeodesy.streprs import Fmt # from .iters
26
+ from pygeodesy.units import _fi_j2, _isDegrees, Radius, Radius_
27
+ from pygeodesy.utily import atan2b, sincos2d
28
+ # from pygeodesy.vector2d import .... # in .... below
29
+ from pygeodesy.vector3dBase import Vector3dBase
30
+
31
+ # from math import fabs, sqrt # from .fmath
32
+
33
+ __all__ = _ALL_LAZY.vector3d
34
+ __version__ = '24.02.20'
35
+
36
+
37
+ class Vector3d(Vector3dBase):
38
+ '''Extended 3-D vector.
39
+
40
+ In a geodesy context, these may be used to represent:
41
+ - n-vector, the normal to a point on the earth's surface
42
+ - Earth-Centered, Earth-Fixed (ECEF) cartesian (== spherical n-vector)
43
+ - great circle normal to the vector
44
+ - motion vector on the earth's surface
45
+ - etc.
46
+ '''
47
+
48
+ def bearing(self, useZ=True):
49
+ '''Get this vector's "bearing", the angle off the +Z axis, clockwise.
50
+
51
+ @kwarg useZ: If C{True}, use the Z component, otherwise ignore the
52
+ Z component and consider the +Y as the +Z axis.
53
+
54
+ @return: Bearing (compass C{degrees}).
55
+ '''
56
+ x, y = self.x, self.y
57
+ if useZ:
58
+ x, y = hypot(x, y), self.z
59
+ return atan2b(x, y)
60
+
61
+ def circin6(self, point2, point3, eps=EPS4):
62
+ '''Return the radius and center of the I{inscribed} aka I{In- circle}
63
+ of a (3-D) triangle formed by this and two other points.
64
+
65
+ @arg point2: Second point (C{Cartesian}, L{Vector3d}, C{Vector3Tuple},
66
+ C{Vector4Tuple} or C{Vector2Tuple} if C{B{useZ}=False}).
67
+ @arg point3: Third point (C{Cartesian}, L{Vector3d}, C{Vector3Tuple},
68
+ C{Vector4Tuple} or C{Vector2Tuple} if C{B{useZ}=False}).
69
+ @kwarg eps: Tolerance for function L{pygeodesy.trilaterate3d2} if
70
+ C{B{useZ} is True} otherwise L{pygeodesy.trilaterate2d2}.
71
+
72
+ @return: L{Circin6Tuple}C{(radius, center, deltas, cA, cB, cC)}. The
73
+ C{center} and contact points C{cA}, C{cB} and C{cC}, each an
74
+ instance of this (sub-)class, are co-planar with this and the
75
+ two given points.
76
+
77
+ @raise ImportError: Package C{numpy} not found, not installed or older
78
+ than version 1.10.
79
+
80
+ @raise IntersectionError: Near-coincident or -colinear points or
81
+ a trilateration or C{numpy} issue.
82
+
83
+ @raise TypeError: Invalid B{C{point2}} or B{C{point3}}.
84
+
85
+ @see: Function L{pygeodesy.circin6}, U{Incircle
86
+ <https://MathWorld.Wolfram.com/Incircle.html>} and U{Contact
87
+ Triangle<https://MathWorld.Wolfram.com/ContactTriangle.html>}.
88
+ '''
89
+ try:
90
+ return _MODS.vector2d._circin6(self, point2, point3, eps=eps, useZ=True)
91
+ except (AssertionError, TypeError, ValueError) as x:
92
+ raise _xError(x, point=self, point2=point2, point3=point3)
93
+
94
+ def circum3(self, point2, point3, circum=True, eps=EPS4):
95
+ '''Return the radius and center of the smallest circle I{through} or
96
+ I{containing} this and two other (3-D) points.
97
+
98
+ @arg point2: Second point (C{Cartesian}, L{Vector3d}, C{Vector3Tuple}
99
+ or C{Vector4Tuple}).
100
+ @arg point3: Third point (C{Cartesian}, L{Vector3d}, C{Vector3Tuple}
101
+ or C{Vector4Tuple}).
102
+ @kwarg circum: If C{True} return the C{circumradius} and C{circumcenter},
103
+ always, ignoring the I{Meeus}' Type I case (C{bool}).
104
+ @kwarg eps: Tolerance passed to function L{pygeodesy.trilaterate3d2}.
105
+
106
+ @return: A L{Circum3Tuple}C{(radius, center, deltas)}. The C{center}, an
107
+ instance of this (sub-)class, is co-planar with this and the two
108
+ given points.
109
+
110
+ @raise ImportError: Package C{numpy} not found, not installed or older than
111
+ version 1.10.
112
+
113
+ @raise IntersectionError: Near-concentric, -coincident or -colinear points
114
+ or a trilateration or C{numpy} issue.
115
+
116
+ @raise TypeError: Invalid B{C{point2}} or B{C{point3}}.
117
+
118
+ @see: Function L{pygeodesy.circum3} and methods L{circum4_} and L{meeus2}.
119
+ '''
120
+ try:
121
+ return _MODS.vector2d._circum3(self, point2, point3, circum=circum,
122
+ eps=eps, useZ=True, clas=self.classof)
123
+ except (AssertionError, TypeError, ValueError) as x:
124
+ raise _xError(x, point=self, point2=point2, point3=point3, circum=circum)
125
+
126
+ def circum4_(self, *points):
127
+ '''Best-fit a sphere through this and two or more other (3-D) points.
128
+
129
+ @arg points: Other points (each a C{Cartesian}, L{Vector3d}, C{Vector3Tuple}
130
+ or C{Vector4Tuple}).
131
+
132
+ @return: L{Circum4Tuple}C{(radius, center, rank, residuals)} with C{center}
133
+ an instance if this (sub-)class.
134
+
135
+ @raise ImportError: Package C{numpy} not found, not installed or
136
+ older than version 1.10.
137
+
138
+ @raise NumPyError: Some C{numpy} issue.
139
+
140
+ @raise PointsError: Too few B{C{points}}.
141
+
142
+ @raise TypeError: One of the B{C{points}} invalid.
143
+
144
+ @see: Function L{pygeodesy.circum4_} and methods L{circum3} and L{meeus2}.
145
+ '''
146
+ return _MODS.vector2d.circum4_(self, *points, useZ=True, Vector=self.classof)
147
+
148
+ def iscolinearWith(self, point1, point2, eps=EPS):
149
+ '''Check whether this and two other (3-D) points are colinear.
150
+
151
+ @arg point1: One point (C{Cartesian}, L{Vector3d}, C{Vector3Tuple}
152
+ or C{Vector4Tuple}).
153
+ @arg point2: An other point (C{Cartesian}, L{Vector3d}, C{Vector3Tuple}
154
+ or C{Vector4Tuple}).
155
+ @kwarg eps: Tolerance (C{scalar}), same units as C{x},
156
+ C{y}, and C{z}.
157
+
158
+ @return: C{True} if this point is colinear with B{C{point1}} and
159
+ B{C{point2}}, C{False} otherwise.
160
+
161
+ @raise TypeError: Invalid B{C{point1}} or B{C{point2}}.
162
+
163
+ @see: Method L{nearestOn}.
164
+ '''
165
+ v = self if self.name else _otherV3d(NN_OK=False, this=self)
166
+ return _MODS.vector2d._iscolinearWith(v, point1, point2, eps=eps)
167
+
168
+ def meeus2(self, point2, point3, circum=False):
169
+ '''Return the radius and I{Meeus}' Type of the smallest circle I{through}
170
+ or I{containing} this and two other (3-D) points.
171
+
172
+ @arg point2: Second point (C{Cartesian}, L{Vector3d}, C{Vector3Tuple}
173
+ or C{Vector4Tuple}).
174
+ @arg point3: Third point (C{Cartesian}, L{Vector3d}, C{Vector3Tuple}
175
+ or C{Vector4Tuple}).
176
+ @kwarg circum: If C{True} return the C{circumradius} and C{circumcenter}
177
+ always, overriding I{Meeus}' Type II case (C{bool}).
178
+
179
+ @return: L{Meeus2Tuple}C{(radius, Type)}, with C{Type} the C{circumcenter}
180
+ iff C{B{circum}=True}.
181
+
182
+ @raise IntersectionError: Coincident or colinear points, iff C{B{circum}=True}.
183
+
184
+ @raise TypeError: Invalid B{C{point2}} or B{C{point3}}.
185
+
186
+ @see: Function L{pygeodesy.meeus2} and methods L{circum3} and L{circum4_}.
187
+ '''
188
+ try:
189
+ return _MODS.vector2d._meeus2(self, point2, point3, circum, clas=self.classof)
190
+ except (TypeError, ValueError) as x:
191
+ raise _xError(x, point=self, point2=point2, point3=point3, circum=circum)
192
+
193
+ def nearestOn(self, point1, point2, within=True):
194
+ '''Locate the point between two points closest to this point.
195
+
196
+ @arg point1: Start point (C{Cartesian}, L{Vector3d}, C{Vector3Tuple} or
197
+ C{Vector4Tuple}).
198
+ @arg point2: End point (C{Cartesian}, L{Vector3d}, C{Vector3Tuple} or
199
+ C{Vector4Tuple}).
200
+ @kwarg within: If C{True} return the closest point between the given
201
+ points, otherwise the closest point on the extended
202
+ line through both points (C{bool}).
203
+
204
+ @return: Closest point, either B{C{point1}} or B{C{point2}} or an instance
205
+ of this (sub-)class.
206
+
207
+ @raise TypeError: Invalid B{C{point1}} or B{C{point2}}.
208
+
209
+ @see: Method L{sphericalTrigonometry.LatLon.nearestOn3} and U{3-D Point-Line
210
+ Distance<https://MathWorld.Wolfram.com/Point-LineDistance3-Dimensional.html>}.
211
+ '''
212
+ return _nearestOn2(self, point1, point2, within=within).closest
213
+
214
+ def nearestOn6(self, points, closed=False, useZ=True): # eps=EPS
215
+ '''Locate the point on a path or polygon closest to this point.
216
+
217
+ The closest point is either on and within the extent of a polygon
218
+ edge or the nearest of that edge's end points.
219
+
220
+ @arg points: The path or polygon points (C{Cartesian}, L{Vector3d},
221
+ C{Vector3Tuple} or C{Vector4Tuple}[]).
222
+ @kwarg closed: Optionally, close the path or polygon (C{bool}).
223
+ @kwarg useZ: If C{True}, use the Z components, otherwise force C{z=INT0} (C{bool}).
224
+
225
+ @return: A L{NearestOn6Tuple}C{(closest, distance, fi, j, start, end)}
226
+ with the C{closest}, the C{start} and the C{end} point each
227
+ an instance of this point's (sub-)class.
228
+
229
+ @raise PointsError: Insufficient number of B{C{points}}
230
+
231
+ @raise TypeError: Non-cartesian B{C{points}}.
232
+
233
+ @note: Distances measured with method L{Vector3d.equirectangular}.
234
+
235
+ @see: Function L{nearestOn6}.
236
+ '''
237
+ return nearestOn6(self, points, closed=closed, useZ=useZ) # Vector=self.classof
238
+
239
+ def parse(self, str3d, sep=_COMMA_, name=NN):
240
+ '''Parse an C{"x, y, z"} string to a L{Vector3d} instance.
241
+
242
+ @arg str3d: X, y and z string (C{str}), see function L{parse3d}.
243
+ @kwarg sep: Optional separator (C{str}).
244
+ @kwarg name: Optional instance name (C{str}), overriding this name.
245
+
246
+ @return: The instance (L{Vector3d}).
247
+
248
+ @raise VectorError: Invalid B{C{str3d}}.
249
+ '''
250
+ return parse3d(str3d, sep=sep, Vector=self.classof, name=name or self.name)
251
+
252
+ def radii11(self, point2, point3):
253
+ '''Return the radii of the C{Circum-}, C{In-}, I{Soddy} and C{Tangent}
254
+ circles of a (3-D) triangle.
255
+
256
+ @arg point2: Second point (C{Cartesian}, L{Vector3d}, C{Vector3Tuple},
257
+ C{Vector4Tuple} or C{Vector2Tuple} if C{B{useZ}=False}).
258
+ @arg point3: Third point (C{Cartesian}, L{Vector3d}, C{Vector3Tuple},
259
+ C{Vector4Tuple} or C{Vector2Tuple} if C{B{useZ}=False}).
260
+
261
+ @return: L{Radii11Tuple}C{(rA, rB, rC, cR, rIn, riS, roS, a, b, c, s)}.
262
+
263
+ @raise TriangleError: Near-coincident or -colinear points.
264
+
265
+ @raise TypeError: Invalid B{C{point2}} or B{C{point3}}.
266
+
267
+ @see: Function L{pygeodesy.radii11}, U{Incircle
268
+ <https://MathWorld.Wolfram.com/Incircle.html>}, U{Soddy Circles
269
+ <https://MathWorld.Wolfram.com/SoddyCircles.html>} and U{Tangent
270
+ Circles<https://MathWorld.Wolfram.com/TangentCircles.html>}.
271
+ '''
272
+ try:
273
+ return _MODS.vector2d._radii11ABC(self, point2, point3, useZ=True)[0]
274
+ except (TypeError, ValueError) as x:
275
+ raise _xError(x, point=self, point2=point2, point3=point3)
276
+
277
+ def soddy4(self, point2, point3, eps=EPS4):
278
+ '''Return the radius and center of the C{inner} I{Soddy} circle of a
279
+ (3-D) triangle.
280
+
281
+ @arg point2: Second point (C{Cartesian}, L{Vector3d}, C{Vector3Tuple},
282
+ C{Vector4Tuple} or C{Vector2Tuple} if C{B{useZ}=False}).
283
+ @arg point3: Third point (C{Cartesian}, L{Vector3d}, C{Vector3Tuple},
284
+ C{Vector4Tuple} or C{Vector2Tuple} if C{B{useZ}=False}).
285
+ @kwarg eps: Tolerance for function L{pygeodesy.trilaterate3d2} if
286
+ C{B{useZ} is True} otherwise L{pygeodesy.trilaterate2d2}.
287
+
288
+ @return: L{Soddy4Tuple}C{(radius, center, deltas, outer)}. The C{center},
289
+ an instance of B{C{point1}}'s (sub-)class, is co-planar with the
290
+ three given points.
291
+
292
+ @raise ImportError: Package C{numpy} not found, not installed or older
293
+ than version 1.10.
294
+
295
+ @raise IntersectionError: Near-coincident or -colinear points or
296
+ a trilateration or C{numpy} issue.
297
+
298
+ @raise TypeError: Invalid B{C{point2}} or B{C{point3}}.
299
+
300
+ @see: Function L{pygeodesy.soddy4}.
301
+ '''
302
+ return _MODS.vector2d.soddy4(self, point2, point3, eps=eps, useZ=True)
303
+
304
+ def trilaterate2d2(self, radius, center2, radius2, center3, radius3, eps=EPS, z=INT0):
305
+ '''Trilaterate this and two other circles, each given as a (2-D) center
306
+ and a radius.
307
+
308
+ @arg radius: Radius of this circle (same C{units} as this C{x} and C{y}.
309
+ @arg center2: Center of the 2nd circle (C{Cartesian}, L{Vector3d},
310
+ C{Vector2Tuple}, C{Vector3Tuple} or C{Vector4Tuple}).
311
+ @arg radius2: Radius of this circle (same C{units} as this C{x} and C{y}.
312
+ @arg center3: Center of the 3rd circle (C{Cartesian}, L{Vector3d},
313
+ C{Vector2Tuple}, C{Vector3Tuple} or C{Vector4Tuple}).
314
+ @arg radius3: Radius of the 3rd circle (same C{units} as this C{x} and C{y}.
315
+ @kwarg eps: Tolerance to check the trilaterated point I{delta} on all
316
+ 3 circles (C{scalar}) or C{None} for no checking.
317
+ @kwarg z: Optional Z component of the trilaterated point (C{scalar}).
318
+
319
+ @return: Trilaterated point, an instance of this (sub-)class with C{z=B{z}}.
320
+
321
+ @raise IntersectionError: No intersection, near-concentric or -colinear
322
+ centers, trilateration failed some other way
323
+ or the trilaterated point is off one circle
324
+ by more than B{C{eps}}.
325
+
326
+ @raise TypeError: Invalid B{C{center2}} or B{C{center3}}.
327
+
328
+ @raise UnitError: Invalid B{C{radius1}}, B{C{radius2}} or B{C{radius3}}.
329
+
330
+ @see: Function L{pygeodesy.trilaterate2d2}.
331
+ '''
332
+
333
+ def _xyr3(r, **name_v):
334
+ v = _otherV3d(useZ=False, **name_v)
335
+ return v.x, v.y, r
336
+
337
+ try:
338
+ return _MODS.vector2d._trilaterate2d2(*(_xyr3(radius, center=self) +
339
+ _xyr3(radius2, center2=center2) +
340
+ _xyr3(radius3, center3=center3)),
341
+ eps=eps, Vector=self.classof, z=z)
342
+ except (AssertionError, TypeError, ValueError) as x:
343
+ raise _xError(x, center=self, radius=radius,
344
+ center2=center2, radius2=radius2,
345
+ center3=center3, radius3=radius3)
346
+
347
+ def trilaterate3d2(self, radius, center2, radius2, center3, radius3, eps=EPS):
348
+ '''Trilaterate this and two other spheres, each given as a (3-D) center
349
+ and a radius.
350
+
351
+ @arg radius: Radius of this sphere (same C{units} as this C{x}, C{y}
352
+ and C{z}).
353
+ @arg center2: Center of the 2nd sphere (C{Cartesian}, L{Vector3d},
354
+ C{Vector3Tuple} or C{Vector4Tuple}).
355
+ @arg radius2: Radius of this sphere (same C{units} as this C{x}, C{y}
356
+ and C{z}).
357
+ @arg center3: Center of the 3rd sphere (C{Cartesian}, , L{Vector3d},
358
+ C{Vector3Tuple} or C{Vector4Tuple}).
359
+ @arg radius3: Radius of the 3rd sphere (same C{units} as this C{x}, C{y}
360
+ and C{z}).
361
+ @kwarg eps: Pertubation tolerance (C{scalar}), same units as C{x}, C{y}
362
+ and C{z} or C{None} for no pertubations.
363
+
364
+ @return: 2-Tuple with two trilaterated points, each an instance of this
365
+ (sub-)class. Both points are the same instance if all three
366
+ spheres intersect or abut in a single point.
367
+
368
+ @raise ImportError: Package C{numpy} not found, not installed or
369
+ older than version 1.10.
370
+
371
+ @raise IntersectionError: Near-concentric, -colinear, too distant or
372
+ non-intersecting spheres or C{numpy} issue.
373
+
374
+ @raise NumPyError: Some C{numpy} issue.
375
+
376
+ @raise TypeError: Invalid B{C{center2}} or B{C{center3}}.
377
+
378
+ @raise UnitError: Invalid B{C{radius}}, B{C{radius2}} or B{C{radius3}}.
379
+
380
+ @note: Package U{numpy<https://PyPI.org/project/numpy>} is required,
381
+ version 1.10 or later.
382
+
383
+ @see: Norrdine, A. U{I{An Algebraic Solution to the Multilateration
384
+ Problem}<https://www.ResearchGate.net/publication/275027725>}
385
+ and U{I{implementation}<https://www.ResearchGate.net/publication/288825016>}.
386
+ '''
387
+ try:
388
+ c1 = _otherV3d(center=self, NN_OK=False)
389
+ return _MODS.vector2d._trilaterate3d2(c1, Radius_(radius, low=eps),
390
+ center2, radius2,
391
+ center3, radius3,
392
+ eps=eps, clas=self.classof)
393
+ except (AssertionError, TypeError, ValueError) as x:
394
+ raise _xError(x, center=self, radius=radius,
395
+ center2=center2, radius2=radius2,
396
+ center3=center3, radius3=radius3)
397
+
398
+
399
+ def _intersect3d3(start1, end1, start2, end2, eps=EPS, useZ=False): # MCCABE 16 in .formy.intersection2, .rhumbBase
400
+ # (INTERNAL) Intersect two lines, see L{intersection3d3} below,
401
+ # separated to allow callers to embellish any exceptions
402
+
403
+ def _corners2(s1, b1, s2, useZ):
404
+ # Get the C{s1'} and C{e1'} corners of a right-angle
405
+ # triangle with the hypotenuse thru C{s1} at bearing
406
+ # C{b1} and the right angle at C{s2}
407
+ dx, dy, d = s2.minus(s1).xyz
408
+ if useZ and not isnear0(d): # not supported
409
+ raise IntersectionError(useZ=d, bearing=b1)
410
+ s, c = sincos2d(b1)
411
+ if s and c:
412
+ dx *= c / s
413
+ dy *= s / c
414
+ e1 = Vector3d(s2.x, s1.y + dx, s1.z)
415
+ s1 = Vector3d(s1.x + dy, s2.y, s1.z)
416
+ else: # orthogonal
417
+ d = euclid(dx, dy) # hypot?
418
+ e1 = Vector3d(s1.x + s * d, s1.y + c * d, s1.z)
419
+ return s1, e1
420
+
421
+ def _outside(t, d2, o): # -o before start#, +o after end#
422
+ return -o if t < 0 else (o if t > d2 else 0) # XXX d2 + eps?
423
+
424
+ s1 = t = _otherV3d(useZ=useZ, start1=start1)
425
+ s2 = _otherV3d(useZ=useZ, start2=start2)
426
+ b1 = _isDegrees(end1)
427
+ if b1: # bearing, make an e1
428
+ s1, e1 = _corners2(s1, end1, s2, useZ)
429
+ else:
430
+ e1 = _otherV3d(useZ=useZ, end1=end1)
431
+ b2 = _isDegrees(end2)
432
+ if b2: # bearing, make an e2
433
+ s2, e2 = _corners2(s2, end2, t, useZ)
434
+ else:
435
+ e2 = _otherV3d(useZ=useZ, end2=end2)
436
+
437
+ a = e1.minus(s1)
438
+ b = e2.minus(s2)
439
+ c = s2.minus(s1)
440
+
441
+ ab = a.cross(b)
442
+ d = fabs(c.dot(ab))
443
+ e = max(EPS0, eps or _0_0)
444
+ if d > EPS0 and ab.length > e: # PYCHOK no cover
445
+ d = d / ab.length # /= chokes PyChecker
446
+ if d > e: # argonic, skew lines distance
447
+ raise IntersectionError(skew_d=d, txt=_no_(_intersection_))
448
+
449
+ # co-planar, non-skew lines
450
+ ab2 = ab.length2
451
+ if ab2 < e: # colinear, parallel or null line(s)
452
+ x = a.length2 > b.length2
453
+ if x: # make C{a} the shortest
454
+ a, b = b, a
455
+ s1, s2 = s2, s1
456
+ e1, e2 = e2, e1
457
+ b1, b2 = b2, b1
458
+ if b.length2 < e: # PYCHOK no cover
459
+ if c.length < e:
460
+ return s1, 0, 0
461
+ elif e2.minus(e1).length < e:
462
+ return e1, 0, 0
463
+ elif a.length2 < e: # null (s1, e1), non-null (s2, e2)
464
+ # like _nearestOn2(s1, s2, e2, within=False, eps=e)
465
+ t = s1.minus(s2).dot(b)
466
+ v = s2.plus(b.times(t / b.length2))
467
+ if s1.minus(v).length < e:
468
+ o = 0 if b2 else _outside(t, b.length2, 1 if x else 2)
469
+ return (v, o, 0) if x else (v, 0, o)
470
+ raise IntersectionError(length2=ab2, txt=_no_(_intersection_))
471
+
472
+ cb = c.cross(b)
473
+ t = cb.dot(ab)
474
+ o1 = 0 if b1 else _outside(t, ab2, 1)
475
+ v = s1.plus(a.times(t / ab2))
476
+ o2 = 0 if b2 else _outside(v.minus(s2).dot(b), b.length2, 2)
477
+ return v, o1, o2
478
+
479
+
480
+ def intersection3d3(start1, end1, start2, end2, eps=EPS, useZ=True,
481
+ **Vector_and_kwds):
482
+ '''Compute the intersection point of two (2- or 3-D) lines, each defined
483
+ by two points or by a point and a bearing.
484
+
485
+ @arg start1: Start point of the first line (C{Cartesian}, L{Vector3d},
486
+ C{Vector3Tuple} or C{Vector4Tuple}).
487
+ @arg end1: End point of the first line (C{Cartesian}, L{Vector3d},
488
+ C{Vector3Tuple} or C{Vector4Tuple}) or the bearing at
489
+ B{C{start1}} (compass C{degrees}).
490
+ @arg start2: Start point of the second line (C{Cartesian}, L{Vector3d},
491
+ C{Vector3Tuple} or C{Vector4Tuple}).
492
+ @arg end2: End point of the second line (C{Cartesian}, L{Vector3d},
493
+ C{Vector3Tuple} or C{Vector4Tuple}) or the bearing at
494
+ B{C{start2}} (Ccompass C{degrees}).
495
+ @kwarg eps: Tolerance for skew line distance and length (C{EPS}).
496
+ @kwarg useZ: If C{True}, use the Z components, otherwise force C{z=INT0} (C{bool}).
497
+ @kwarg Vector_and_kwds: Optional class C{B{Vector}=None} to return the
498
+ intersection points and optional, additional B{C{Vector}}
499
+ keyword arguments, otherwise B{C{start1}}'s (sub-)class.
500
+
501
+ @return: An L{Intersection3Tuple}C{(point, outside1, outside2)} with
502
+ C{point} an instance of B{C{Vector}} or B{C{start1}}'s (sub-)class.
503
+
504
+ @note: The C{outside} values is C{0} for lines specified by point and bearing.
505
+
506
+ @raise IntersectionError: Invalid, skew, non-co-planar or otherwise
507
+ non-intersecting lines.
508
+
509
+ @see: U{Line-line intersection<https://MathWorld.Wolfram.com/Line-LineIntersection.html>}
510
+ and U{line-line distance<https://MathWorld.Wolfram.com/Line-LineDistance.html>},
511
+ U{skew lines<https://MathWorld.Wolfram.com/SkewLines.html>} and U{point-line
512
+ distance<https://MathWorld.Wolfram.com/Point-LineDistance3-Dimensional.html>}.
513
+ '''
514
+ try:
515
+ v, o1, o2 = _intersect3d3(start1, end1, start2, end2, eps=eps, useZ=useZ)
516
+ except (TypeError, ValueError) as x:
517
+ raise _xError(x, start1=start1, end1=end1, start2=start2, end2=end2)
518
+ v = _nVc(v, **_xkwds(Vector_and_kwds, clas=start1.classof,
519
+ name=intersection3d3.__name__))
520
+ return Intersection3Tuple(v, o1, o2)
521
+
522
+
523
+ def intersections2(center1, radius1, center2, radius2, sphere=True, **Vector_and_kwds):
524
+ '''Compute the intersection of two spheres or circles, each defined by a (3-D)
525
+ center point and a radius.
526
+
527
+ @arg center1: Center of the first sphere or circle (C{Cartesian}, L{Vector3d},
528
+ C{Vector3Tuple} or C{Vector4Tuple}).
529
+ @arg radius1: Radius of the first sphere or circle (same units as the
530
+ B{C{center1}} coordinates).
531
+ @arg center2: Center of the second sphere or circle (C{Cartesian}, L{Vector3d},
532
+ C{Vector3Tuple} or C{Vector4Tuple}).
533
+ @arg radius2: Radius of the second sphere or circle (same units as the
534
+ B{C{center1}} and B{C{center2}} coordinates).
535
+ @kwarg sphere: If C{True} compute the center and radius of the intersection of
536
+ two spheres. If C{False}, ignore the C{z}-component and compute
537
+ the intersection of two circles (C{bool}).
538
+ @kwarg Vector_and_kwds: Optional class C{B{Vector}=None} to return the
539
+ intersection points and optional, additional B{C{Vector}}
540
+ keyword arguments, otherwise B{C{center1}}'s (sub-)class.
541
+
542
+ @return: If B{C{sphere}} is C{True}, a 2-tuple of the C{center} and C{radius}
543
+ of the intersection of the I{spheres}. The C{radius} is C{0.0} for
544
+ abutting spheres (and the C{center} is aka the I{radical center}).
545
+
546
+ If B{C{sphere}} is C{False}, a 2-tuple with the two intersection
547
+ points of the I{circles}. For abutting circles, both points are
548
+ the same instance, aka the I{radical center}.
549
+
550
+ @raise IntersectionError: Concentric, invalid or non-intersecting spheres
551
+ or circles.
552
+
553
+ @raise TypeError: Invalid B{C{center1}} or B{C{center2}}.
554
+
555
+ @raise UnitError: Invalid B{C{radius1}} or B{C{radius2}}.
556
+
557
+ @see: U{Sphere-Sphere<https://MathWorld.Wolfram.com/Sphere-SphereIntersection.html>} and
558
+ U{Circle-Circle<https://MathWorld.Wolfram.com/Circle-CircleIntersection.html>}
559
+ Intersection.
560
+ '''
561
+ try:
562
+ return _intersects2(center1, Radius_(radius1=radius1),
563
+ center2, Radius_(radius2=radius2), sphere=sphere,
564
+ clas=center1.classof, **Vector_and_kwds)
565
+ except (TypeError, ValueError) as x:
566
+ raise _xError(x, center1=center1, radius1=radius1, center2=center2, radius2=radius2)
567
+
568
+
569
+ def _intersects2(center1, r1, center2, r2, sphere=True, too_d=None, # in CartesianEllipsoidalBase.intersections2,
570
+ **clas_Vector_and_kwds): # .ellipsoidalBaseDI._intersections2, .formy.intersections2
571
+ # (INTERNAL) Intersect two spheres or circles, see L{intersections2}
572
+ # above, separated to allow callers to embellish any exceptions
573
+
574
+ def _nV3(x, y, z):
575
+ v = Vector3d(x, y, z)
576
+ n = intersections2.__name__
577
+ return _nVc(v, **_xkwds(clas_Vector_and_kwds, name=n))
578
+
579
+ def _xV3(c1, u, x, y):
580
+ xy1 = x, y, _1_0 # transform to original space
581
+ return _nV3(fdot(xy1, u.x, -u.y, c1.x),
582
+ fdot(xy1, u.y, u.x, c1.y), _0_0)
583
+
584
+ c1 = _otherV3d(useZ=sphere, center1=center1)
585
+ c2 = _otherV3d(useZ=sphere, center2=center2)
586
+
587
+ if r1 < r2: # r1, r2 == R, r
588
+ c1, c2 = c2, c1
589
+ r1, r2 = r2, r1
590
+
591
+ m = c2.minus(c1)
592
+ d = m.length
593
+ if d < max(r2 - r1, EPS):
594
+ raise IntersectionError(_near_(_concentric_)) # XXX ConcentricError?
595
+
596
+ o = fsum1_(-d, r1, r2) # overlap == -(d - (r1 + r2))
597
+ # compute intersections with c1 at (0, 0) and c2 at (d, 0), like
598
+ # <https://MathWorld.Wolfram.com/Circle-CircleIntersection.html>
599
+ if o > EPS: # overlapping, r1, r2 == R, r
600
+ x = _MODS.formy._radical2(d, r1, r2).xline
601
+ y = _1_0 - (x / r1)**2
602
+ if y > EPS:
603
+ y = r1 * sqrt(y) # y == a / 2
604
+ elif y < 0: # PYCHOK no cover
605
+ raise IntersectionError(_negative_)
606
+ else: # abutting
607
+ y = _0_0
608
+ elif o < 0: # PYCHOK no cover
609
+ if too_d is not None:
610
+ d = too_d
611
+ raise IntersectionError(_too_(Fmt.distant(d)))
612
+ else: # abutting
613
+ x, y = r1, _0_0
614
+
615
+ u = m.unit()
616
+ if sphere: # sphere center and radius
617
+ c = c1 if x < EPS else (
618
+ c2 if x > EPS1 else c1.plus(u.times(x)))
619
+ t = _nV3(c.x, c.y, c.z), Radius(y)
620
+
621
+ elif y > 0: # intersecting circles
622
+ t = _xV3(c1, u, x, y), _xV3(c1, u, x, -y)
623
+ else: # abutting circles
624
+ t = _xV3(c1, u, x, 0)
625
+ t = t, t
626
+ return t
627
+
628
+
629
+ def iscolinearWith(point, point1, point2, eps=EPS, useZ=True):
630
+ '''Check whether a point is colinear with two other (2- or 3-D) points.
631
+
632
+ @arg point: The point (L{Vector3d}, C{Vector3Tuple} or C{Vector4Tuple}).
633
+ @arg point1: First point (L{Vector3d}, C{Vector3Tuple} or C{Vector4Tuple}).
634
+ @arg point2: Second point (L{Vector3d}, C{Vector3Tuple} or C{Vector4Tuple}).
635
+ @kwarg eps: Tolerance (C{scalar}), same units as C{x}, C{y} and C{z}.
636
+ @kwarg useZ: If C{True}, use the Z components, otherwise force C{z=INT0} (C{bool}).
637
+
638
+ @return: C{True} if B{C{point}} is colinear B{C{point1}} and B{C{point2}},
639
+ C{False} otherwise.
640
+
641
+ @raise TypeError: Invalid B{C{point}}, B{C{point1}} or B{C{point2}}.
642
+
643
+ @see: Function L{nearestOn}.
644
+ '''
645
+ p = _otherV3d(useZ=useZ, point=point)
646
+ return _MODS.vector2d._iscolinearWith(p, point1, point2, eps=eps, useZ=useZ)
647
+
648
+
649
+ def nearestOn(point, point1, point2, within=True, useZ=True, Vector=None, **Vector_kwds):
650
+ '''Locate the point between two points closest to a reference (2- or 3-D).
651
+
652
+ @arg point: Reference point (C{Cartesian}, L{Vector3d}, C{Vector3Tuple}
653
+ or C{Vector4Tuple}).
654
+ @arg point1: Start point (C{Cartesian}, L{Vector3d}, C{Vector3Tuple} or
655
+ C{Vector4Tuple}).
656
+ @arg point2: End point (C{Cartesian}, L{Vector3d}, C{Vector3Tuple} or
657
+ C{Vector4Tuple}).
658
+ @kwarg within: If C{True} return the closest point between both given
659
+ points, otherwise the closest point on the extended line
660
+ through both points (C{bool}).
661
+ @kwarg useZ: If C{True}, use the Z components, otherwise force C{z=INT0} (C{bool}).
662
+ @kwarg Vector: Class to return closest point (C{Cartesian}, L{Vector3d}
663
+ or C{Vector3Tuple}) or C{None}.
664
+ @kwarg Vector_kwds: Optional, additional B{C{Vector}} keyword arguments,
665
+ ignored if C{B{Vector} is None}.
666
+
667
+ @return: Closest point, either B{C{point1}} or B{C{point2}} or an instance
668
+ of the B{C{point}}'s (sub-)class or B{C{Vector}} if not C{None}.
669
+
670
+ @raise TypeError: Invalid B{C{point}}, B{C{point1}} or B{C{point2}}.
671
+
672
+ @see: U{3-D Point-Line Distance<https://MathWorld.Wolfram.com/Point-LineDistance3-Dimensional.html>},
673
+ C{Cartesian} and C{LatLon} methods C{nearestOn}, method L{sphericalTrigonometry.LatLon.nearestOn3}
674
+ and function L{sphericalTrigonometry.nearestOn3}.
675
+ '''
676
+ p0 = _otherV3d(useZ=useZ, point =point)
677
+ p1 = _otherV3d(useZ=useZ, point1=point1)
678
+ p2 = _otherV3d(useZ=useZ, point2=point2)
679
+
680
+ n = nearestOn.__name__
681
+ p, _ = _nearestOn2(p0, p1, p2, within=within)
682
+ if Vector is not None:
683
+ p = Vector(p.x, p.y, **_xkwds(Vector_kwds, z=p.z, name=n))
684
+ elif p is p1:
685
+ p = point1
686
+ elif p is p2:
687
+ p = point2
688
+ else: # ignore Vector_kwds
689
+ p = point.classof(p.x, p.y, _xkwds_get(Vector_kwds, z=p.z), name=n)
690
+ return p
691
+
692
+
693
+ def _nearestOn2(p0, p1, p2, within=True, eps=EPS):
694
+ # (INTERNAL) Closest point and fraction, see L{nearestOn} above,
695
+ # separated to allow callers to embellish any exceptions
696
+ p21 = p2.minus(p1)
697
+ d2 = p21.length2
698
+ if d2 < eps: # coincident
699
+ p = p1 # ~= p2
700
+ t = 0
701
+ else: # see comments in .points.nearestOn5
702
+ t = p0.minus(p1).dot(p21) / d2
703
+ if within and t < eps:
704
+ p = p1
705
+ t = 0
706
+ elif within and t > (_1_0 - eps):
707
+ p = p2
708
+ t = 1
709
+ else:
710
+ p = p1.plus(p21.times(t))
711
+ return NearestOn2Tuple(p, t)
712
+
713
+
714
+ def nearestOn6(point, points, closed=False, useZ=True, **Vector_and_kwds): # eps=EPS
715
+ '''Locate the point on a path or polygon closest to a reference point.
716
+
717
+ The closest point on each polygon edge is either the nearest of that
718
+ edge's end points or a point in between.
719
+
720
+ @arg point: Reference point (C{Cartesian}, L{Vector3d}, C{Vector3Tuple} or
721
+ C{Vector4Tuple}).
722
+ @arg points: The path or polygon points (C{Cartesian}, L{Vector3d},
723
+ C{Vector3Tuple} or C{Vector4Tuple}[]).
724
+ @kwarg closed: Optionally, close the path or polygon (C{bool}).
725
+ @kwarg useZ: If C{True}, use the Z components, otherwise force C{z=INT0} (C{bool}).
726
+ @kwarg Vector_and_kwds: Optional class C{B{Vector}=None} to return the closest
727
+ point and optional, additional B{C{Vector}} keyword
728
+ arguments, otherwise B{C{point}}'s (sub-)class.
729
+
730
+ @return: A L{NearestOn6Tuple}C{(closest, distance, fi, j, start, end)} with the
731
+ C{closest}, the C{start} and the C{end} point each an instance of the
732
+ B{C{Vector}} keyword argument of if {B{Vector}=None} or not specified,
733
+ an instance of the reference B{C{point}}'s (sub-)class.
734
+
735
+ @raise PointsError: Insufficient number of B{C{points}}
736
+
737
+ @raise TypeError: Non-cartesian B{C{point}} and B{C{points}}.
738
+
739
+ @note: Distances measured with method L{Vector3d.equirectangular}. For
740
+ geodetic distances use function L{nearestOn5} or one of the
741
+ C{LatLon.nearestOn6} methods.
742
+ '''
743
+ r = _otherV3d(useZ=useZ, point=point)
744
+ D2 = r.equirectangular # distance squared
745
+
746
+ Ps = PointsIter(points, loop=1, name=nearestOn6.__name__)
747
+ p1 = c = s = e = _otherV3d(useZ=useZ, i=0, points=Ps[0])
748
+ c2 = D2(c) # == r.minus(c).length2
749
+
750
+ f = i = 0 # p1..p2 == points[i]..[j]
751
+ for j, p2 in Ps.enumerate(closed=closed):
752
+ p2 = _otherV3d(useZ=useZ, i=j, points=p2)
753
+ p, t = _nearestOn2(r, p1, p2) # within=True, eps=EPS
754
+ d2 = D2(p) # == r.minus(p).length2
755
+ if d2 < c2:
756
+ c2, c, s, e, f = d2, p, p1, p2, (i + t)
757
+ p1, i = p2, j
758
+
759
+ f, j = _fi_j2(f, len(Ps)) # like .ellipsoidalBaseDI._nearestOn2_
760
+
761
+ kwds = _xkwds(Vector_and_kwds, clas=point.classof, name=Ps.name)
762
+ v = _nVc(c, **kwds)
763
+ s = _nVc(s, **kwds) if s is not c else v
764
+ e = _nVc(e, **kwds) if e is not c else v
765
+ return NearestOn6Tuple(v, sqrt(c2), f, j, s, e)
766
+
767
+
768
+ def _nVc(v, clas=None, name=NN, Vector=None, **Vector_kwds): # in .vector2d
769
+ # return a named C{Vector} or C{clas} instance
770
+ if Vector is not None:
771
+ v = Vector(v.x, v.y, v.z, **Vector_kwds)
772
+ elif clas is not None:
773
+ v = clas(v.x, v.y, v.z) # ignore Vector_kwds
774
+ return _xnamed(v, name) if name else v
775
+
776
+
777
+ def _otherV3d(useZ=True, NN_OK=True, i=None, **name_v):
778
+ # check named vector instance, return Vector3d
779
+ n, v = _xkwds_item2(name_v)
780
+ if useZ and isinstance(v, Vector3dBase):
781
+ return v if NN_OK or v.name else v.copy(name=Fmt.INDEX(n, i))
782
+
783
+ n = Fmt.INDEX(n, i)
784
+ try:
785
+ return Vector3d(v.x, v.y, (v.z if useZ else INT0), name=n)
786
+ except AttributeError: # no .x, .y or .z attr
787
+ pass
788
+ raise _xotherError(Vector3d(0, 0, 0), v, name=n, up=2)
789
+
790
+
791
+ def parse3d(str3d, sep=_COMMA_, Vector=Vector3d, **Vector_kwds):
792
+ '''Parse an C{"x, y, z"} string.
793
+
794
+ @arg str3d: X, y and z values (C{str}).
795
+ @kwarg sep: Optional separator (C{str}).
796
+ @kwarg Vector: Optional class (L{Vector3d}).
797
+ @kwarg Vector_kwds: Optional B{C{Vector}} keyword arguments,
798
+ ignored if C{B{Vector} is None}.
799
+
800
+ @return: A B{C{Vector}} instance or if B{C{Vector}} is C{None},
801
+ a named L{Vector3Tuple}C{(x, y, z)}.
802
+
803
+ @raise VectorError: Invalid B{C{str3d}}.
804
+ '''
805
+ try:
806
+ v = [float(v.strip()) for v in str3d.split(sep)]
807
+ n = len(v)
808
+ if n != 3:
809
+ raise _ValueError(len=n)
810
+ except (TypeError, ValueError) as x:
811
+ raise VectorError(str3d=str3d, cause=x)
812
+ return _xnamed((Vector3Tuple(v) if Vector is None else # *v
813
+ Vector(*v, **Vector_kwds)), parse3d.__name__)
814
+
815
+
816
+ def sumOf(vectors, Vector=Vector3d, **Vector_kwds):
817
+ '''Compute the I{vectorial} sum of two oe more vectors.
818
+
819
+ @arg vectors: Vectors to be added (L{Vector3d}[]).
820
+ @kwarg Vector: Optional class for the vectorial sum (L{Vector3d}).
821
+ @kwarg Vector_kwds: Optional B{C{Vector}} keyword arguments,
822
+ ignored if C{B{Vector} is None}.
823
+
824
+ @return: Vectorial sum as B{C{Vector}} or if B{C{Vector}} is
825
+ C{None}, a named L{Vector3Tuple}C{(x, y, z)}.
826
+
827
+ @raise VectorError: No B{C{vectors}}.
828
+ '''
829
+ try:
830
+ t = _MODS.nvectorBase._nsumOf(vectors, 0, None, {}) # no H
831
+ except (TypeError, ValueError) as x:
832
+ raise VectorError(vectors=vectors, Vector=Vector, cause=x)
833
+ x, y, z = t[:3]
834
+ n = sumOf.__name__
835
+ return Vector3Tuple(x, y, z, name=n) if Vector is None else \
836
+ Vector(x, y, z, **_xkwds(Vector_kwds, name=n))
837
+
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 _MODS.vector2d._trilaterate2d2(x1, y1, radius1,
874
+ x2, y2, radius2,
875
+ x3, y3, radius3, eps=eps, **Vector_and_kwds)
876
+
877
+
878
+ def trilaterate3d2(center1, radius1, center2, radius2, center3, radius3,
879
+ eps=EPS, **Vector_and_kwds):
880
+ '''Trilaterate three spheres, each given as a (3-D) center and a radius.
881
+
882
+ @arg center1: Center of the 1st sphere (C{Cartesian}, L{Vector3d},
883
+ C{Vector3Tuple} or C{Vector4Tuple}).
884
+ @arg radius1: Radius of the 1st sphere (same C{units} as C{x}, C{y}
885
+ and C{z}).
886
+ @arg center2: Center of the 2nd sphere (C{Cartesian}, L{Vector3d},
887
+ C{Vector3Tuple} or C{Vector4Tuple}).
888
+ @arg radius2: Radius of this sphere (same C{units} as C{x}, C{y}
889
+ and C{z}).
890
+ @arg center3: Center of the 3rd sphere (C{Cartesian}, L{Vector3d},
891
+ C{Vector3Tuple} or C{Vector4Tuple}).
892
+ @arg radius3: Radius of the 3rd sphere (same C{units} as C{x}, C{y}
893
+ and C{z}).
894
+ @kwarg eps: Pertubation tolerance (C{scalar}), same units as C{x},
895
+ 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.
900
+
901
+ @return: 2-Tuple with two trilaterated points, each a B{C{Vector}}
902
+ instance. Both points are the same instance if all three
903
+ spheres abut/intersect in a single point.
904
+
905
+ @raise ImportError: Package C{numpy} not found, not installed or
906
+ older than version 1.10.
907
+
908
+ @raise IntersectionError: Near-concentric, -colinear, too distant or
909
+ non-intersecting spheres.
910
+
911
+ @raise NumPyError: Some C{numpy} issue.
912
+
913
+ @raise TypeError: Invalid B{C{center1}}, B{C{center2}} or B{C{center3}}.
914
+
915
+ @raise UnitError: Invalid B{C{radius1}}, B{C{radius2}} or B{C{radius3}}.
916
+
917
+ @see: Norrdine, A. U{I{An Algebraic Solution to the Multilateration
918
+ Problem}<https://www.ResearchGate.net/publication/275027725>},
919
+ the U{I{implementation}<https://www.ResearchGate.net/publication/
920
+ 288825016>} and function L{pygeodesy.trilaterate2d2}.
921
+ '''
922
+ try:
923
+ return _MODS.vector2d._trilaterate3d2(_otherV3d(center1=center1, NN_OK=False),
924
+ Radius_(radius1=radius1, low=eps),
925
+ center2, radius2, center3, radius3, eps=eps,
926
+ clas=center1.classof, **Vector_and_kwds)
927
+ except (AssertionError, TypeError, ValueError) as x:
928
+ raise _xError(x, center1=center1, radius1=radius1,
929
+ center2=center2, radius2=radius2,
930
+ center3=center3, radius3=radius3)
931
+
932
+
933
+ def _xyzhdn3(xyz, height, datum, ll): # in .cartesianBase, .nvectorBase
934
+ '''(INTERNAL) Get a C{(h, d, name)} 3-tuple.
935
+ '''
936
+ h = height or _xattr(xyz, height=None) \
937
+ or _xattr(xyz, h=None) \
938
+ or _xattr(ll, height=None)
939
+
940
+ d = datum or _xattr(xyz, datum=None) \
941
+ or _xattr(ll, datum=None)
942
+
943
+ return h, d, _xattr(xyz, name=NN)
944
+
945
+
946
+ __all__ += _ALL_DOCS(intersections2, sumOf, Vector3dBase)
947
+
948
+ # **) MIT License
949
+ #
950
+ # Copyright (C) 2016-2024 -- mrJean1 at Gmail -- All Rights Reserved.
951
+ #
952
+ # Permission is hereby granted, free of charge, to any person obtaining a
953
+ # copy of this software and associated documentation files (the "Software"),
954
+ # to deal in the Software without restriction, including without limitation
955
+ # the rights to use, copy, modify, merge, publish, distribute, sublicense,
956
+ # and/or sell copies of the Software, and to permit persons to whom the
957
+ # Software is furnished to do so, subject to the following conditions:
958
+ #
959
+ # The above copyright notice and this permission notice shall be included
960
+ # in all copies or substantial portions of the Software.
961
+ #
962
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
963
+ # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
964
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
965
+ # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
966
+ # OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
967
+ # ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
968
+ # OTHER DEALINGS IN THE SOFTWARE.