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.
- PyGeodesy-24.3.24.dist-info/METADATA +272 -0
- PyGeodesy-24.3.24.dist-info/RECORD +115 -0
- PyGeodesy-24.3.24.dist-info/WHEEL +6 -0
- PyGeodesy-24.3.24.dist-info/top_level.txt +1 -0
- pygeodesy/LICENSE +21 -0
- pygeodesy/__init__.py +615 -0
- pygeodesy/__main__.py +103 -0
- pygeodesy/albers.py +867 -0
- pygeodesy/auxilats/_CX_4.py +218 -0
- pygeodesy/auxilats/_CX_6.py +314 -0
- pygeodesy/auxilats/_CX_8.py +475 -0
- pygeodesy/auxilats/__init__.py +54 -0
- pygeodesy/auxilats/__main__.py +86 -0
- pygeodesy/auxilats/auxAngle.py +548 -0
- pygeodesy/auxilats/auxDLat.py +302 -0
- pygeodesy/auxilats/auxDST.py +296 -0
- pygeodesy/auxilats/auxLat.py +848 -0
- pygeodesy/auxilats/auxily.py +272 -0
- pygeodesy/azimuthal.py +1150 -0
- pygeodesy/basics.py +892 -0
- pygeodesy/booleans.py +2031 -0
- pygeodesy/cartesianBase.py +1062 -0
- pygeodesy/clipy.py +704 -0
- pygeodesy/constants.py +516 -0
- pygeodesy/css.py +660 -0
- pygeodesy/datums.py +752 -0
- pygeodesy/deprecated/__init__.py +61 -0
- pygeodesy/deprecated/bases.py +40 -0
- pygeodesy/deprecated/classes.py +262 -0
- pygeodesy/deprecated/consterns.py +54 -0
- pygeodesy/deprecated/datum.py +40 -0
- pygeodesy/deprecated/functions.py +375 -0
- pygeodesy/deprecated/nvector.py +48 -0
- pygeodesy/deprecated/rhumbBase.py +32 -0
- pygeodesy/deprecated/rhumbaux.py +33 -0
- pygeodesy/deprecated/rhumbsolve.py +33 -0
- pygeodesy/deprecated/rhumbx.py +33 -0
- pygeodesy/dms.py +986 -0
- pygeodesy/ecef.py +1348 -0
- pygeodesy/elevations.py +279 -0
- pygeodesy/ellipsoidalBase.py +1224 -0
- pygeodesy/ellipsoidalBaseDI.py +913 -0
- pygeodesy/ellipsoidalExact.py +343 -0
- pygeodesy/ellipsoidalGeodSolve.py +343 -0
- pygeodesy/ellipsoidalKarney.py +403 -0
- pygeodesy/ellipsoidalNvector.py +685 -0
- pygeodesy/ellipsoidalVincenty.py +590 -0
- pygeodesy/ellipsoids.py +2476 -0
- pygeodesy/elliptic.py +1198 -0
- pygeodesy/epsg.py +243 -0
- pygeodesy/errors.py +804 -0
- pygeodesy/etm.py +1190 -0
- pygeodesy/fmath.py +1013 -0
- pygeodesy/formy.py +1818 -0
- pygeodesy/frechet.py +865 -0
- pygeodesy/fstats.py +760 -0
- pygeodesy/fsums.py +1898 -0
- pygeodesy/gars.py +358 -0
- pygeodesy/geodesicw.py +581 -0
- pygeodesy/geodesicx/_C4_24.py +1699 -0
- pygeodesy/geodesicx/_C4_27.py +2395 -0
- pygeodesy/geodesicx/_C4_30.py +3301 -0
- pygeodesy/geodesicx/__init__.py +48 -0
- pygeodesy/geodesicx/__main__.py +91 -0
- pygeodesy/geodesicx/gx.py +1382 -0
- pygeodesy/geodesicx/gxarea.py +535 -0
- pygeodesy/geodesicx/gxbases.py +154 -0
- pygeodesy/geodesicx/gxline.py +669 -0
- pygeodesy/geodsolve.py +426 -0
- pygeodesy/geohash.py +914 -0
- pygeodesy/geoids.py +1884 -0
- pygeodesy/hausdorff.py +892 -0
- pygeodesy/heights.py +1155 -0
- pygeodesy/interns.py +687 -0
- pygeodesy/iters.py +545 -0
- pygeodesy/karney.py +919 -0
- pygeodesy/ktm.py +633 -0
- pygeodesy/latlonBase.py +1766 -0
- pygeodesy/lazily.py +960 -0
- pygeodesy/lcc.py +684 -0
- pygeodesy/ltp.py +1107 -0
- pygeodesy/ltpTuples.py +1563 -0
- pygeodesy/mgrs.py +721 -0
- pygeodesy/named.py +1324 -0
- pygeodesy/namedTuples.py +683 -0
- pygeodesy/nvectorBase.py +695 -0
- pygeodesy/osgr.py +781 -0
- pygeodesy/points.py +1686 -0
- pygeodesy/props.py +628 -0
- pygeodesy/resections.py +1048 -0
- pygeodesy/rhumb/__init__.py +46 -0
- pygeodesy/rhumb/aux_.py +397 -0
- pygeodesy/rhumb/bases.py +1148 -0
- pygeodesy/rhumb/ekx.py +563 -0
- pygeodesy/rhumb/solve.py +572 -0
- pygeodesy/simplify.py +647 -0
- pygeodesy/solveBase.py +472 -0
- pygeodesy/sphericalBase.py +724 -0
- pygeodesy/sphericalNvector.py +1264 -0
- pygeodesy/sphericalTrigonometry.py +1447 -0
- pygeodesy/streprs.py +627 -0
- pygeodesy/trf.py +2079 -0
- pygeodesy/triaxials.py +1484 -0
- pygeodesy/units.py +969 -0
- pygeodesy/unitsBase.py +349 -0
- pygeodesy/ups.py +538 -0
- pygeodesy/utily.py +1231 -0
- pygeodesy/utm.py +762 -0
- pygeodesy/utmups.py +318 -0
- pygeodesy/utmupsBase.py +517 -0
- pygeodesy/vector2d.py +785 -0
- pygeodesy/vector3d.py +968 -0
- pygeodesy/vector3dBase.py +1049 -0
- pygeodesy/webmercator.py +383 -0
- pygeodesy/wgrs.py +439 -0
|
@@ -0,0 +1,1049 @@
|
|
|
1
|
+
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
|
|
4
|
+
u'''(INTERNAL) Private, 3-D vector base class C{Vector3dBase}.
|
|
5
|
+
|
|
6
|
+
A pure Python implementation of vector-based functions by I{(C) Chris Veness
|
|
7
|
+
2011-2015} published under the same MIT Licence**, see U{Vector-based geodesy
|
|
8
|
+
<https://www.Movable-Type.co.UK/scripts/latlong-vectors.html>}.
|
|
9
|
+
'''
|
|
10
|
+
|
|
11
|
+
from pygeodesy.basics import _copysign, islistuple, isscalar, map1, \
|
|
12
|
+
map2, _zip
|
|
13
|
+
from pygeodesy.constants import EPS, EPS0, INT0, PI, PI2, _copysignINF, \
|
|
14
|
+
_float0, isnear0, isnear1, isneg0, \
|
|
15
|
+
_pos_self, _0_0, _1_0
|
|
16
|
+
from pygeodesy.errors import CrossError, VectorError, _xcallable, _xError
|
|
17
|
+
from pygeodesy.fmath import euclid_, fdot, hypot_, hypot2_
|
|
18
|
+
from pygeodesy.interns import NN, _coincident_, _colinear_, \
|
|
19
|
+
_COMMASPACE_, _xyz_
|
|
20
|
+
from pygeodesy.lazily import _ALL_LAZY, _ALL_DOCS, _ALL_MODS as _MODS, \
|
|
21
|
+
_sys_version_info2
|
|
22
|
+
from pygeodesy.named import _NamedBase, _NotImplemented, _xother3
|
|
23
|
+
# from pygeodesy.namedTuples import Vector3Tuple # _MODS
|
|
24
|
+
from pygeodesy.props import deprecated_method, Property, Property_RO, \
|
|
25
|
+
property_doc_, property_RO, _update_all
|
|
26
|
+
from pygeodesy.streprs import Fmt, strs, unstr
|
|
27
|
+
from pygeodesy.units import Float, Scalar
|
|
28
|
+
# from pygeodesy.utily import sincos2 # _MODS
|
|
29
|
+
|
|
30
|
+
from math import atan2, ceil, fabs, floor, trunc
|
|
31
|
+
|
|
32
|
+
__all__ = _ALL_LAZY.vector3dBase
|
|
33
|
+
__version__ = '24.03.24'
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class Vector3dBase(_NamedBase): # sync __methods__ with .fsums.Fsum
|
|
37
|
+
'''(INTERNAL) Generic 3-D vector base class.
|
|
38
|
+
'''
|
|
39
|
+
_crosserrors = True # un/set by .errors.crosserrors
|
|
40
|
+
|
|
41
|
+
_ll = None # original latlon, '_fromll'
|
|
42
|
+
# _x = INT0 # X component
|
|
43
|
+
# _y = INT0 # Y component
|
|
44
|
+
# _z = INT0 # Z component
|
|
45
|
+
|
|
46
|
+
def __init__(self, x_xyz, y=INT0, z=INT0, ll=None, name=NN):
|
|
47
|
+
'''New L{Vector3d} or C{Vector3dBase} instance.
|
|
48
|
+
|
|
49
|
+
The vector may be normalised or use x, y, z for position and
|
|
50
|
+
distance from earth centre or height relative to the surface
|
|
51
|
+
of the earth' sphere or ellipsoid.
|
|
52
|
+
|
|
53
|
+
@arg x_xyz: X component of vector (C{scalar}) or a (3-D) vector
|
|
54
|
+
(C{Cartesian}, L{Ecef9Tuple}, C{Nvector}, L{Vector3d},
|
|
55
|
+
L{Vector3Tuple}, L{Vector4Tuple} or a C{tuple} or
|
|
56
|
+
C{list} of 3+ C{scalar} items).
|
|
57
|
+
@kwarg y: Y component of vector (C{scalar}), ignored if B{C{x_xyz}}
|
|
58
|
+
is not C{scalar}, otherwise same units as B{C{x_xyz}}.
|
|
59
|
+
@kwarg z: Z component of vector (C{scalar}), ignored if B{C{x_xyz}}
|
|
60
|
+
is not C{scalar}, otherwise same units as B{C{x_xyz}}.
|
|
61
|
+
@kwarg ll: Optional latlon reference (C{LatLon}).
|
|
62
|
+
@kwarg name: Optional name (C{str}).
|
|
63
|
+
|
|
64
|
+
@raise VectorError: Invalid B{C{x_xyz}}.
|
|
65
|
+
'''
|
|
66
|
+
self._x, \
|
|
67
|
+
self._y, \
|
|
68
|
+
self._z = _xyz3(type(self), x_xyz, y, z) if isscalar(x_xyz) else \
|
|
69
|
+
_xyz3(type(self), x_xyz)
|
|
70
|
+
if ll:
|
|
71
|
+
self._ll = ll
|
|
72
|
+
if name:
|
|
73
|
+
self.name = name
|
|
74
|
+
|
|
75
|
+
def __abs__(self):
|
|
76
|
+
'''Return the norm of this vector.
|
|
77
|
+
|
|
78
|
+
@return: Norm, unit length (C{float});
|
|
79
|
+
'''
|
|
80
|
+
return self.length
|
|
81
|
+
|
|
82
|
+
def __add__(self, other):
|
|
83
|
+
'''Add this to an other vector (L{Vector3d}).
|
|
84
|
+
|
|
85
|
+
@return: Vectorial sum (L{Vector3d}).
|
|
86
|
+
|
|
87
|
+
@raise TypeError: Incompatible B{C{other}} C{type}.
|
|
88
|
+
'''
|
|
89
|
+
return self.plus(other)
|
|
90
|
+
|
|
91
|
+
def __bool__(self): # PYCHOK PyChecker
|
|
92
|
+
'''Is this vector non-zero?
|
|
93
|
+
'''
|
|
94
|
+
return bool(self.x or self.y or self.z)
|
|
95
|
+
|
|
96
|
+
def __ceil__(self): # PYCHOK no cover
|
|
97
|
+
'''Return a vector with the C{ceil} of these components.
|
|
98
|
+
|
|
99
|
+
@return: Ceil-ed (L{Vector3d}).
|
|
100
|
+
'''
|
|
101
|
+
return self._mapped(ceil)
|
|
102
|
+
|
|
103
|
+
def __cmp__(self, other): # Python 2-
|
|
104
|
+
'''Compare this and an other vector (L{Vector3d}).
|
|
105
|
+
|
|
106
|
+
@return: -1, 0 or +1 (C{int}).
|
|
107
|
+
|
|
108
|
+
@raise TypeError: Incompatible B{C{other}} C{type}.
|
|
109
|
+
'''
|
|
110
|
+
n = self.others(other).length
|
|
111
|
+
return -1 if self.length < n else (
|
|
112
|
+
+1 if self.length > n else 0)
|
|
113
|
+
|
|
114
|
+
cmp = __cmp__
|
|
115
|
+
|
|
116
|
+
def __divmod__(self, other): # PYCHOK no cover
|
|
117
|
+
'''Not implemented.'''
|
|
118
|
+
return _NotImplemented(self, other)
|
|
119
|
+
|
|
120
|
+
def __eq__(self, other):
|
|
121
|
+
'''Is this vector equal to an other vector?
|
|
122
|
+
|
|
123
|
+
@arg other: The other vector (L{Vector3d}).
|
|
124
|
+
|
|
125
|
+
@return: C{True} if equal, C{False} otherwise.
|
|
126
|
+
|
|
127
|
+
@raise TypeError: Incompatible B{C{other}} C{type}.
|
|
128
|
+
'''
|
|
129
|
+
return self.isequalTo(other, eps=EPS0)
|
|
130
|
+
|
|
131
|
+
def __float__(self): # PYCHOK no cover
|
|
132
|
+
'''Not implemented.'''
|
|
133
|
+
return _NotImplemented(self)
|
|
134
|
+
|
|
135
|
+
def __floor__(self): # PYCHOK no cover
|
|
136
|
+
'''Return a vector with the C{floor} of these components.
|
|
137
|
+
|
|
138
|
+
@return: Floor-ed (L{Vector3d}).
|
|
139
|
+
'''
|
|
140
|
+
return self._mapped(floor)
|
|
141
|
+
|
|
142
|
+
def __floordiv__(self, other): # PYCHOK no cover
|
|
143
|
+
'''Not implemented.'''
|
|
144
|
+
return _NotImplemented(self, other)
|
|
145
|
+
|
|
146
|
+
def __format__(self, *other): # PYCHOK no cover
|
|
147
|
+
'''Not implemented.'''
|
|
148
|
+
return _NotImplemented(self, *other)
|
|
149
|
+
|
|
150
|
+
def __ge__(self, other):
|
|
151
|
+
'''Is this vector longer than or equal to an other vector?
|
|
152
|
+
|
|
153
|
+
@arg other: The other vector (L{Vector3d}).
|
|
154
|
+
|
|
155
|
+
@return: C{True} if so, C{False} otherwise.
|
|
156
|
+
|
|
157
|
+
@raise TypeError: Incompatible B{C{other}} C{type}.
|
|
158
|
+
'''
|
|
159
|
+
return self.length >= self.others(other).length
|
|
160
|
+
|
|
161
|
+
# def __getitem__(self, key):
|
|
162
|
+
# '''Return C{item} at index or slice C{[B{key}]}.
|
|
163
|
+
# '''
|
|
164
|
+
# return self.xyz[key]
|
|
165
|
+
|
|
166
|
+
def __gt__(self, other):
|
|
167
|
+
'''Is this vector longer than an other vector?
|
|
168
|
+
|
|
169
|
+
@arg other: The other vector (L{Vector3d}).
|
|
170
|
+
|
|
171
|
+
@return: C{True} if so, C{False} otherwise.
|
|
172
|
+
|
|
173
|
+
@raise TypeError: Incompatible B{C{other}} C{type}.
|
|
174
|
+
'''
|
|
175
|
+
return self.length > self.others(other).length
|
|
176
|
+
|
|
177
|
+
def __hash__(self): # PYCHOK no cover
|
|
178
|
+
'''Return this instance' C{hash}.
|
|
179
|
+
'''
|
|
180
|
+
return hash(self.xyz) # XXX id(self)?
|
|
181
|
+
|
|
182
|
+
def __iadd__(self, other):
|
|
183
|
+
'''Add this and an other vector I{in-place}, C{this += B{other}}.
|
|
184
|
+
|
|
185
|
+
@arg other: The other vector (L{Vector3d}).
|
|
186
|
+
|
|
187
|
+
@raise TypeError: Incompatible B{C{other}} C{type}.
|
|
188
|
+
'''
|
|
189
|
+
return self._xyz(self.plus(other))
|
|
190
|
+
|
|
191
|
+
def __ifloordiv__(self, other): # PYCHOK no cover
|
|
192
|
+
'''Not implemented.'''
|
|
193
|
+
return _NotImplemented(self, other)
|
|
194
|
+
|
|
195
|
+
def __imatmul__(self, other): # PYCHOK Python 3.5+
|
|
196
|
+
'''Cross multiply this and an other vector I{in-place}, C{this @= B{other}}.
|
|
197
|
+
|
|
198
|
+
@arg other: The other vector (L{Vector3d}).
|
|
199
|
+
|
|
200
|
+
@raise TypeError: Incompatible B{C{other}} C{type}.
|
|
201
|
+
|
|
202
|
+
@see: Luciano Ramalho, "Fluent Python", O'Reilly, 2016 p. 397+, 2022 p. 578+.
|
|
203
|
+
'''
|
|
204
|
+
return self._xyz(self.cross(other))
|
|
205
|
+
|
|
206
|
+
def __imod__(self, other): # PYCHOK no cover
|
|
207
|
+
'''Not implemented.'''
|
|
208
|
+
return _NotImplemented(self, other)
|
|
209
|
+
|
|
210
|
+
def __imul__(self, scalar):
|
|
211
|
+
'''Multiply this vector by a scalar I{in-place}, C{this *= B{scalar}}.
|
|
212
|
+
|
|
213
|
+
@arg scalar: Factor (C{scalar}).
|
|
214
|
+
|
|
215
|
+
@raise TypeError: Non-scalar B{C{scalar}}.
|
|
216
|
+
'''
|
|
217
|
+
return self._xyz(self.times(scalar))
|
|
218
|
+
|
|
219
|
+
def __int__(self): # PYCHOK no cover
|
|
220
|
+
'''Return a vector with the C{int} of these components.
|
|
221
|
+
|
|
222
|
+
@return: Int-ed (L{Vector3d}).
|
|
223
|
+
'''
|
|
224
|
+
v = self.classof(_0_0)
|
|
225
|
+
v._x, v._y, v._z = map2(int, self.xyz)
|
|
226
|
+
return v
|
|
227
|
+
|
|
228
|
+
def __ipow__(self, other, *mod): # PYCHOK no cover
|
|
229
|
+
'''Not implemented.'''
|
|
230
|
+
return _NotImplemented(self, other, *mod)
|
|
231
|
+
|
|
232
|
+
def __isub__(self, other):
|
|
233
|
+
'''Subtract an other vector from this one I{in-place}, C{this -= B{other}}.
|
|
234
|
+
|
|
235
|
+
@arg other: The other vector (L{Vector3d}).
|
|
236
|
+
|
|
237
|
+
@raise TypeError: Incompatible B{C{other}} C{type}.
|
|
238
|
+
'''
|
|
239
|
+
return self._xyz(self.minus(other))
|
|
240
|
+
|
|
241
|
+
# def __iter__(self):
|
|
242
|
+
# '''Return an C{iter}ator over this vector's components.
|
|
243
|
+
# '''
|
|
244
|
+
# return iter(self.xyz)
|
|
245
|
+
|
|
246
|
+
def __itruediv__(self, scalar):
|
|
247
|
+
'''Divide this vector by a scalar I{in-place}, C{this /= B{scalar}}.
|
|
248
|
+
|
|
249
|
+
@arg scalar: The divisor (C{scalar}).
|
|
250
|
+
|
|
251
|
+
@raise TypeError: Non-scalar B{C{scalar}}.
|
|
252
|
+
'''
|
|
253
|
+
return self._xyz(self.dividedBy(scalar))
|
|
254
|
+
|
|
255
|
+
def __le__(self, other): # Python 3+
|
|
256
|
+
'''Is this vector shorter than or equal to an other vector?
|
|
257
|
+
|
|
258
|
+
@arg other: The other vector (L{Vector3d}).
|
|
259
|
+
|
|
260
|
+
@return: C{True} if so, C{False} otherwise.
|
|
261
|
+
|
|
262
|
+
@raise TypeError: Incompatible B{C{other}} C{type}.
|
|
263
|
+
'''
|
|
264
|
+
return self.length <= self.others(other).length
|
|
265
|
+
|
|
266
|
+
# def __len__(self):
|
|
267
|
+
# '''Return C{3}, always.
|
|
268
|
+
# '''
|
|
269
|
+
# return len(self.xyz)
|
|
270
|
+
|
|
271
|
+
def __lt__(self, other): # Python 3+
|
|
272
|
+
'''Is this vector shorter than an other vector?
|
|
273
|
+
|
|
274
|
+
@arg other: The other vector (L{Vector3d}).
|
|
275
|
+
|
|
276
|
+
@return: C{True} if so, C{False} otherwise.
|
|
277
|
+
|
|
278
|
+
@raise TypeError: Incompatible B{C{other}} C{type}.
|
|
279
|
+
'''
|
|
280
|
+
return self.length < self.others(other).length
|
|
281
|
+
|
|
282
|
+
def __matmul__(self, other): # PYCHOK Python 3.5+
|
|
283
|
+
'''Compute the cross product of this and an other vector, C{this @ B{other}}.
|
|
284
|
+
|
|
285
|
+
@arg other: The other vector (L{Vector3d}).
|
|
286
|
+
|
|
287
|
+
@return: Cross product (L{Vector3d}).
|
|
288
|
+
|
|
289
|
+
@raise TypeError: Incompatible B{C{other}} C{type}.
|
|
290
|
+
'''
|
|
291
|
+
return self.cross(other)
|
|
292
|
+
|
|
293
|
+
def __mod__(self, other): # PYCHOK no cover
|
|
294
|
+
'''Not implemented.'''
|
|
295
|
+
return _NotImplemented(self, other)
|
|
296
|
+
|
|
297
|
+
def __mul__(self, scalar):
|
|
298
|
+
'''Multiply this vector by a scalar, C{this * B{scalar}}.
|
|
299
|
+
|
|
300
|
+
@arg scalar: Factor (C{scalar}).
|
|
301
|
+
|
|
302
|
+
@return: Product (L{Vector3d}).
|
|
303
|
+
'''
|
|
304
|
+
return self.times(scalar)
|
|
305
|
+
|
|
306
|
+
def __ne__(self, other):
|
|
307
|
+
'''Is this vector not equal to an other vector?
|
|
308
|
+
|
|
309
|
+
@arg other: The other vector (L{Vector3d}).
|
|
310
|
+
|
|
311
|
+
@return: C{True} if so, C{False} otherwise.
|
|
312
|
+
|
|
313
|
+
@raise TypeError: Incompatible B{C{other}} C{type}.
|
|
314
|
+
'''
|
|
315
|
+
return not self.isequalTo(other, eps=EPS0)
|
|
316
|
+
|
|
317
|
+
def __neg__(self):
|
|
318
|
+
'''Return the opposite of this vector.
|
|
319
|
+
|
|
320
|
+
@return: This instance negated (L{Vector3d})
|
|
321
|
+
'''
|
|
322
|
+
return self.classof(-self.x, -self.y, -self.z)
|
|
323
|
+
|
|
324
|
+
def __pos__(self): # PYCHOK no cover
|
|
325
|
+
'''Return this vector I{as-is} or a copy.
|
|
326
|
+
|
|
327
|
+
@return: This instance (L{Vector3d})
|
|
328
|
+
'''
|
|
329
|
+
return self if _pos_self else self.copy()
|
|
330
|
+
|
|
331
|
+
def __pow__(self, other, *mod): # PYCHOK no cover
|
|
332
|
+
'''Not implemented.'''
|
|
333
|
+
return _NotImplemented(self, other, *mod)
|
|
334
|
+
|
|
335
|
+
__radd__ = __add__ # PYCHOK no cover
|
|
336
|
+
|
|
337
|
+
def __rdivmod__ (self, other): # PYCHOK no cover
|
|
338
|
+
'''Not implemented.'''
|
|
339
|
+
return _NotImplemented(self, other)
|
|
340
|
+
|
|
341
|
+
# def __repr__(self):
|
|
342
|
+
# '''Return the default C{repr(this)}.
|
|
343
|
+
# '''
|
|
344
|
+
# return self.toRepr()
|
|
345
|
+
|
|
346
|
+
def __rfloordiv__(self, other): # PYCHOK no cover
|
|
347
|
+
'''Not implemented.'''
|
|
348
|
+
return _NotImplemented(self, other)
|
|
349
|
+
|
|
350
|
+
def __rmatmul__(self, other): # PYCHOK Python 3.5+
|
|
351
|
+
'''Compute the cross product of an other and this vector, C{B{other} @ this}.
|
|
352
|
+
|
|
353
|
+
@arg other: The other vector (L{Vector3d}).
|
|
354
|
+
|
|
355
|
+
@return: Cross product (L{Vector3d}).
|
|
356
|
+
|
|
357
|
+
@raise TypeError: Incompatible B{C{other}} C{type}.
|
|
358
|
+
'''
|
|
359
|
+
return self.others(other).cross(self)
|
|
360
|
+
|
|
361
|
+
def __rmod__(self, other): # PYCHOK no cover
|
|
362
|
+
'''Not implemented.'''
|
|
363
|
+
return _NotImplemented(self, other)
|
|
364
|
+
|
|
365
|
+
__rmul__ = __mul__
|
|
366
|
+
|
|
367
|
+
def __round__(self, *ndigits): # PYCHOK no cover
|
|
368
|
+
'''Return a vector with these components C{rounded}.
|
|
369
|
+
|
|
370
|
+
@arg ndigits: Optional number of digits (C{int}).
|
|
371
|
+
|
|
372
|
+
@return: Rounded (L{Vector3d}).
|
|
373
|
+
'''
|
|
374
|
+
# <https://docs.Python.org/3.12/reference/datamodel.html?#object.__round__>
|
|
375
|
+
return self.classof(*(round(_, *ndigits) for _ in self.xyz))
|
|
376
|
+
|
|
377
|
+
def __rpow__(self, other, *mod): # PYCHOK no cover
|
|
378
|
+
'''Not implemented.'''
|
|
379
|
+
return _NotImplemented(self, other, *mod)
|
|
380
|
+
|
|
381
|
+
def __rsub__(self, other): # PYCHOK no cover
|
|
382
|
+
'''Subtract this vector from an other vector, C{B{other} - this}.
|
|
383
|
+
|
|
384
|
+
@arg other: The other vector (L{Vector3d}).
|
|
385
|
+
|
|
386
|
+
@return: Difference (L{Vector3d}).
|
|
387
|
+
|
|
388
|
+
@raise TypeError: Incompatible B{C{other}} C{type}.
|
|
389
|
+
'''
|
|
390
|
+
return self.others(other).minus(self)
|
|
391
|
+
|
|
392
|
+
def __rtruediv__(self, scalar): # PYCHOK no cover
|
|
393
|
+
'''Not implemented.'''
|
|
394
|
+
return _NotImplemented(self, scalar)
|
|
395
|
+
|
|
396
|
+
# def __str__(self):
|
|
397
|
+
# '''Return the default C{str(self)}.
|
|
398
|
+
# '''
|
|
399
|
+
# return self.toStr()
|
|
400
|
+
|
|
401
|
+
def __sub__(self, other):
|
|
402
|
+
'''Subtract an other vector from this vector, C{this - B{other}}.
|
|
403
|
+
|
|
404
|
+
@arg other: The other vector (L{Vector3d}).
|
|
405
|
+
|
|
406
|
+
@return: Difference (L{Vector3d}).
|
|
407
|
+
|
|
408
|
+
@raise TypeError: Incompatible B{C{other}} C{type}.
|
|
409
|
+
'''
|
|
410
|
+
return self.minus(other)
|
|
411
|
+
|
|
412
|
+
def __truediv__(self, scalar):
|
|
413
|
+
'''Divide this vector by a scalar, C{this / B{scalar}}.
|
|
414
|
+
|
|
415
|
+
@arg scalar: The divisor (C{scalar}).
|
|
416
|
+
|
|
417
|
+
@return: Quotient (L{Vector3d}).
|
|
418
|
+
|
|
419
|
+
@raise TypeError: Non-scalar B{C{scalar}}.
|
|
420
|
+
'''
|
|
421
|
+
return self.dividedBy(scalar)
|
|
422
|
+
|
|
423
|
+
def __trunc__(self): # PYCHOK no cover
|
|
424
|
+
'''Return a vector with the C{trunc} of these components.
|
|
425
|
+
|
|
426
|
+
@return: Trunc-ed (L{Vector3d}).
|
|
427
|
+
'''
|
|
428
|
+
return self._mapped(trunc)
|
|
429
|
+
|
|
430
|
+
if _sys_version_info2 < (3, 0): # PYCHOK no cover
|
|
431
|
+
# <https://docs.Python.org/2/library/operator.html#mapping-operators-to-functions>
|
|
432
|
+
__div__ = __truediv__
|
|
433
|
+
__idiv__ = __itruediv__
|
|
434
|
+
__long__ = __int__
|
|
435
|
+
__nonzero__ = __bool__
|
|
436
|
+
__rdiv__ = __rtruediv__
|
|
437
|
+
|
|
438
|
+
def angleTo(self, other, vSign=None, wrap=False):
|
|
439
|
+
'''Compute the angle between this and an other vector.
|
|
440
|
+
|
|
441
|
+
@arg other: The other vector (L{Vector3d}).
|
|
442
|
+
@kwarg vSign: Optional vector, if supplied (and out of the
|
|
443
|
+
plane of this and the other), angle is signed
|
|
444
|
+
positive if this->other is clockwise looking
|
|
445
|
+
along vSign or negative in opposite direction,
|
|
446
|
+
otherwise angle is unsigned.
|
|
447
|
+
@kwarg wrap: If C{True}, wrap/unroll the angle to +/-PI (C{bool}).
|
|
448
|
+
|
|
449
|
+
@return: Angle (C{radians}).
|
|
450
|
+
|
|
451
|
+
@raise TypeError: If B{C{other}} or B{C{vSign}} not a L{Vector3d}.
|
|
452
|
+
'''
|
|
453
|
+
x = self.cross(other)
|
|
454
|
+
s = x.length
|
|
455
|
+
# use vSign as reference to set sign of s
|
|
456
|
+
if s and vSign and x.dot(vSign) < 0:
|
|
457
|
+
s = -s
|
|
458
|
+
|
|
459
|
+
a = atan2(s, self.dot(other))
|
|
460
|
+
if wrap and fabs(a) > PI:
|
|
461
|
+
a -= _copysign(PI2, a)
|
|
462
|
+
return a
|
|
463
|
+
|
|
464
|
+
def apply(self, fun2, other_x, *y_z, **fun2_kwds):
|
|
465
|
+
'''Apply a 2-argument function pairwise to the components
|
|
466
|
+
of this and an other vector.
|
|
467
|
+
|
|
468
|
+
@arg fun2: 2-Argument callable (C{any(scalar, scalar}),
|
|
469
|
+
return a C{scalar} or L{INT0} result.
|
|
470
|
+
@arg other_x: Other X component (C{scalar}) or a vector
|
|
471
|
+
with X, Y and Z components (C{Cartesian},
|
|
472
|
+
L{Ecef9Tuple}, C{Nvector}, L{Vector3d},
|
|
473
|
+
L{Vector3Tuple} or L{Vector4Tuple}).
|
|
474
|
+
@arg y_z: Other Y and Z components, positional (C{scalar}, C{scalar}).
|
|
475
|
+
@kwarg fun2_kwds: Optional keyword arguments for B{C{fun2}}.
|
|
476
|
+
|
|
477
|
+
@return: New, applied vector (L{Vector3d}).
|
|
478
|
+
|
|
479
|
+
@raise ValueError: Invalid B{C{other_x}} or B{C{y_z}}.
|
|
480
|
+
'''
|
|
481
|
+
_xcallable(fun2=fun2)
|
|
482
|
+
if fun2_kwds:
|
|
483
|
+
def _f2(a, b):
|
|
484
|
+
return fun2(a, b, **fun2_kwds)
|
|
485
|
+
else:
|
|
486
|
+
_f2 = fun2
|
|
487
|
+
|
|
488
|
+
xyz = _xyz3(self.apply, other_x, *y_z)
|
|
489
|
+
xyz = (_f2(a, b) for a, b in _zip(self.xyz, xyz)) # strict=True
|
|
490
|
+
return self.classof(*xyz)
|
|
491
|
+
|
|
492
|
+
def cross(self, other, raiser=None, eps0=EPS): # raiser=NN
|
|
493
|
+
'''Compute the cross product of this and an other vector.
|
|
494
|
+
|
|
495
|
+
@arg other: The other vector (L{Vector3d}).
|
|
496
|
+
@kwarg raiser: Optional, L{CrossError} label if raised (C{str},
|
|
497
|
+
non-L{NN}).
|
|
498
|
+
@kwarg eps0: Near-zero tolerance (C{scalar}), same units as
|
|
499
|
+
C{x}, C{y}, and C{z}.
|
|
500
|
+
|
|
501
|
+
@return: Cross product (L{Vector3d}).
|
|
502
|
+
|
|
503
|
+
@raise CrossError: Zero or near-zero cross product and both
|
|
504
|
+
B{C{raiser}} and L{pygeodesy.crosserrors} set.
|
|
505
|
+
|
|
506
|
+
@raise TypeError: Incompatible B{C{other}} C{type}.
|
|
507
|
+
'''
|
|
508
|
+
X, Y, Z = self.others(other).xyz
|
|
509
|
+
x, y, z = self.xyz
|
|
510
|
+
xyz = ((y * Z - Y * z),
|
|
511
|
+
(z * X - Z * x),
|
|
512
|
+
(x * Y - X * y))
|
|
513
|
+
|
|
514
|
+
if raiser and self.crosserrors and eps0 > 0 \
|
|
515
|
+
and max(map(fabs, xyz)) < eps0:
|
|
516
|
+
r = other._fromll or other
|
|
517
|
+
s = self._fromll or self
|
|
518
|
+
t = self.isequalTo(other, eps=eps0)
|
|
519
|
+
t = _coincident_ if t else _colinear_
|
|
520
|
+
raise CrossError(raiser, s, other=r, txt=t)
|
|
521
|
+
|
|
522
|
+
return self.classof(*xyz)
|
|
523
|
+
|
|
524
|
+
@property_doc_('''raise or ignore L{CrossError} exceptions (C{bool}).''')
|
|
525
|
+
def crosserrors(self):
|
|
526
|
+
'''Get L{CrossError} exceptions (C{bool}).
|
|
527
|
+
'''
|
|
528
|
+
return self._crosserrors
|
|
529
|
+
|
|
530
|
+
@crosserrors.setter # PYCHOK setter!
|
|
531
|
+
def crosserrors(self, raiser):
|
|
532
|
+
'''Raise or ignore L{CrossError} exceptions (C{bool}).
|
|
533
|
+
'''
|
|
534
|
+
self._crosserrors = bool(raiser)
|
|
535
|
+
|
|
536
|
+
def dividedBy(self, divisor):
|
|
537
|
+
'''Divide this vector by a scalar.
|
|
538
|
+
|
|
539
|
+
@arg divisor: The divisor (C{scalar}).
|
|
540
|
+
|
|
541
|
+
@return: New, scaled vector (L{Vector3d}).
|
|
542
|
+
|
|
543
|
+
@raise TypeError: Non-scalar B{C{divisor}}.
|
|
544
|
+
|
|
545
|
+
@raise VectorError: Invalid or zero B{C{divisor}}.
|
|
546
|
+
'''
|
|
547
|
+
d = Scalar(divisor=divisor)
|
|
548
|
+
try:
|
|
549
|
+
return self._times(_1_0 / d)
|
|
550
|
+
except (ValueError, ZeroDivisionError) as x:
|
|
551
|
+
raise VectorError(divisor=divisor, cause=x)
|
|
552
|
+
|
|
553
|
+
def dot(self, other):
|
|
554
|
+
'''Compute the dot (scalar) product of this and an other vector.
|
|
555
|
+
|
|
556
|
+
@arg other: The other vector (L{Vector3d}).
|
|
557
|
+
|
|
558
|
+
@return: Dot product (C{float}).
|
|
559
|
+
|
|
560
|
+
@raise TypeError: Incompatible B{C{other}} C{type}.
|
|
561
|
+
'''
|
|
562
|
+
return self.length2 if other is self else \
|
|
563
|
+
fdot(self.xyz, *self.others(other).xyz)
|
|
564
|
+
|
|
565
|
+
@deprecated_method
|
|
566
|
+
def equals(self, other, units=False): # PYCHOK no cover
|
|
567
|
+
'''DEPRECATED, use method C{isequalTo}.
|
|
568
|
+
'''
|
|
569
|
+
return self.isequalTo(other, units=units)
|
|
570
|
+
|
|
571
|
+
@Property_RO
|
|
572
|
+
def euclid(self):
|
|
573
|
+
'''I{Approximate} the length (norm, magnitude) of this vector (C{Float}).
|
|
574
|
+
|
|
575
|
+
@see: Properties C{length} and C{length2} and function
|
|
576
|
+
L{pygeodesy.euclid_}.
|
|
577
|
+
'''
|
|
578
|
+
return Float(euclid=euclid_(self.x, self.y, self.z))
|
|
579
|
+
|
|
580
|
+
def equirectangular(self, other):
|
|
581
|
+
'''I{Approximate} the different between this and an other vector.
|
|
582
|
+
|
|
583
|
+
@arg other: Vector to subtract (C{Vector3dBase}).
|
|
584
|
+
|
|
585
|
+
@return: The lenght I{squared} of the difference (C{Float}).
|
|
586
|
+
|
|
587
|
+
@raise TypeError: Incompatible B{C{other}} C{type}.
|
|
588
|
+
|
|
589
|
+
@see: Property C{length2}.
|
|
590
|
+
'''
|
|
591
|
+
d = self.minus(other)
|
|
592
|
+
return Float(equirectangular=hypot2_(d.x, d.y, d.z))
|
|
593
|
+
|
|
594
|
+
@Property
|
|
595
|
+
def _fromll(self):
|
|
596
|
+
'''(INTERNAL) Get the latlon reference (C{LatLon}) or C{None}.
|
|
597
|
+
'''
|
|
598
|
+
return self._ll
|
|
599
|
+
|
|
600
|
+
@_fromll.setter # PYCHOK setter!
|
|
601
|
+
def _fromll(self, ll):
|
|
602
|
+
'''(INTERNAL) Set the latlon reference (C{LatLon}) or C{None}.
|
|
603
|
+
'''
|
|
604
|
+
self._ll = ll or None
|
|
605
|
+
|
|
606
|
+
@property_RO
|
|
607
|
+
def homogeneous(self):
|
|
608
|
+
'''Get this vector's homogeneous representation (L{Vector3d}).
|
|
609
|
+
'''
|
|
610
|
+
x, y, z = self.xyz
|
|
611
|
+
if z:
|
|
612
|
+
x = x / z # /= chokes PyChecker
|
|
613
|
+
y = y / z
|
|
614
|
+
# z = _1_0
|
|
615
|
+
else:
|
|
616
|
+
if isneg0(z):
|
|
617
|
+
x = -x
|
|
618
|
+
y = -y
|
|
619
|
+
x = _copysignINF(x)
|
|
620
|
+
y = _copysignINF(y)
|
|
621
|
+
# z = NAN
|
|
622
|
+
return self.classof(x, y, _1_0)
|
|
623
|
+
|
|
624
|
+
def intermediateTo(self, other, fraction, **unused): # height=None, wrap=False
|
|
625
|
+
'''Locate the vector at a given fraction between (or along) this
|
|
626
|
+
and an other vector.
|
|
627
|
+
|
|
628
|
+
@arg other: The other vector (L{Vector3d}).
|
|
629
|
+
@arg fraction: Fraction between both vectors (C{scalar},
|
|
630
|
+
0.0 for this and 1.0 for the other vector).
|
|
631
|
+
|
|
632
|
+
@return: Intermediate vector (L{Vector3d}).
|
|
633
|
+
|
|
634
|
+
@raise TypeError: Incompatible B{C{other}} C{type}.
|
|
635
|
+
'''
|
|
636
|
+
f = Scalar(fraction=fraction)
|
|
637
|
+
if isnear0(f): # PYCHOK no cover
|
|
638
|
+
r = self
|
|
639
|
+
else:
|
|
640
|
+
r = self.others(other)
|
|
641
|
+
if not isnear1(f): # self * (1 - f) + r * f
|
|
642
|
+
r = self.plus(r.minus(self)._times(f))
|
|
643
|
+
return r
|
|
644
|
+
|
|
645
|
+
def isconjugateTo(self, other, minum=1, eps=EPS):
|
|
646
|
+
'''Determine whether this and an other vector are conjugates.
|
|
647
|
+
|
|
648
|
+
@arg other: The other vector (C{Cartesian}, L{Ecef9Tuple},
|
|
649
|
+
L{Vector3d}, C{Vector3Tuple} or C{Vector4Tuple}).
|
|
650
|
+
@kwarg minum: Minimal number of conjugates required (C{int}, 0..3).
|
|
651
|
+
@kwarg eps: Tolerance for equality and conjugation (C{scalar}),
|
|
652
|
+
same units as C{x}, C{y}, and C{z}.
|
|
653
|
+
|
|
654
|
+
@return: C{True} if both vector's components either match
|
|
655
|
+
or at least C{B{minum}} have opposite signs.
|
|
656
|
+
|
|
657
|
+
@raise TypeError: Incompatible B{C{other}} C{type}.
|
|
658
|
+
|
|
659
|
+
@see: Method C{isequalTo}.
|
|
660
|
+
'''
|
|
661
|
+
self.others(other)
|
|
662
|
+
n = 0
|
|
663
|
+
for a, b in zip(self.xyz, other.xyz):
|
|
664
|
+
if fabs(a + b) < eps and ((a < 0 and b > 0) or
|
|
665
|
+
(a > 0 and b < 0)):
|
|
666
|
+
n += 1 # conjugate
|
|
667
|
+
elif fabs(a - b) > eps:
|
|
668
|
+
return False # unequal
|
|
669
|
+
return bool(n >= minum)
|
|
670
|
+
|
|
671
|
+
def isequalTo(self, other, units=False, eps=EPS):
|
|
672
|
+
'''Check if this and an other vector are equal or equivalent.
|
|
673
|
+
|
|
674
|
+
@arg other: The other vector (L{Vector3d}).
|
|
675
|
+
@kwarg units: Optionally, compare the normalized, unit
|
|
676
|
+
version of both vectors.
|
|
677
|
+
@kwarg eps: Tolerance for equality (C{scalar}), same units as
|
|
678
|
+
C{x}, C{y}, and C{z}.
|
|
679
|
+
|
|
680
|
+
@return: C{True} if vectors are identical, C{False} otherwise.
|
|
681
|
+
|
|
682
|
+
@raise TypeError: Incompatible B{C{other}} C{type}.
|
|
683
|
+
|
|
684
|
+
@see: Method C{isconjugateTo}.
|
|
685
|
+
'''
|
|
686
|
+
if units:
|
|
687
|
+
self.others(other)
|
|
688
|
+
d = self.unit().minus(other.unit())
|
|
689
|
+
else:
|
|
690
|
+
d = self.minus(other)
|
|
691
|
+
return max(map(fabs, d.xyz)) < eps
|
|
692
|
+
|
|
693
|
+
@Property_RO
|
|
694
|
+
def length(self): # __dict__ value overwritten by Property_RO C{_united}
|
|
695
|
+
'''Get the length (norm, magnitude) of this vector (C{Float}).
|
|
696
|
+
|
|
697
|
+
@see: Properties L{length2} and L{euclid}.
|
|
698
|
+
'''
|
|
699
|
+
return Float(length=hypot_(self.x, self.y, self.z))
|
|
700
|
+
|
|
701
|
+
@Property_RO
|
|
702
|
+
def length2(self): # __dict__ value overwritten by Property_RO C{_united}
|
|
703
|
+
'''Get the length I{squared} of this vector (C{Float}).
|
|
704
|
+
|
|
705
|
+
@see: Property L{length} and method C{equirectangular}.
|
|
706
|
+
'''
|
|
707
|
+
return Float(length2=hypot2_(self.x, self.y, self.z))
|
|
708
|
+
|
|
709
|
+
def _mapped(self, func):
|
|
710
|
+
'''(INTERNAL) Map these components.
|
|
711
|
+
'''
|
|
712
|
+
return self.classof(*map2(func, self.xyz))
|
|
713
|
+
|
|
714
|
+
def minus(self, other):
|
|
715
|
+
'''Subtract an other vector from this vector.
|
|
716
|
+
|
|
717
|
+
@arg other: The other vector (L{Vector3d}).
|
|
718
|
+
|
|
719
|
+
@return: New vector difference (L{Vector3d}).
|
|
720
|
+
|
|
721
|
+
@raise TypeError: Incompatible B{C{other}} C{type}.
|
|
722
|
+
'''
|
|
723
|
+
xyz = self.others(other).xyz
|
|
724
|
+
return self._minus(*xyz)
|
|
725
|
+
|
|
726
|
+
def _minus(self, x, y, z):
|
|
727
|
+
'''(INTERNAL) Helper for methods C{.minus} and C{.minus_}.
|
|
728
|
+
'''
|
|
729
|
+
return self.classof(self.x - x, self.y - y, self.z - z)
|
|
730
|
+
|
|
731
|
+
def minus_(self, other_x, *y_z):
|
|
732
|
+
'''Subtract separate X, Y and Z components from this vector.
|
|
733
|
+
|
|
734
|
+
@arg other_x: X component (C{scalar}) or a vector's
|
|
735
|
+
X, Y, and Z components (C{Cartesian},
|
|
736
|
+
L{Ecef9Tuple}, C{Nvector}, L{Vector3d},
|
|
737
|
+
L{Vector3Tuple}, L{Vector4Tuple}).
|
|
738
|
+
@arg y_z: Y and Z components (C{scalar}, C{scalar}),
|
|
739
|
+
ignored if B{C{other_x}} is not C{scalar}.
|
|
740
|
+
|
|
741
|
+
@return: New, vectiorial vector (L{Vector3d}).
|
|
742
|
+
|
|
743
|
+
@raise ValueError: Invalid B{C{other_x}} or B{C{y_z}}.
|
|
744
|
+
'''
|
|
745
|
+
return self._minus(*_xyz3(self.minus_, other_x, *y_z))
|
|
746
|
+
|
|
747
|
+
def negate(self):
|
|
748
|
+
'''Return this vector in opposite direction.
|
|
749
|
+
|
|
750
|
+
@return: New, opposite vector (L{Vector3d}).
|
|
751
|
+
'''
|
|
752
|
+
return self.classof(-self.x, -self.y, -self.z)
|
|
753
|
+
|
|
754
|
+
__neg__ = negate # PYCHOK no cover
|
|
755
|
+
|
|
756
|
+
@Property_RO
|
|
757
|
+
def _N_vector(self):
|
|
758
|
+
'''(INTERNAL) Get the (C{nvectorBase._N_vector_})
|
|
759
|
+
'''
|
|
760
|
+
return _MODS.nvectorBase._N_vector_(*self.xyz, name=self.name)
|
|
761
|
+
|
|
762
|
+
def others(self, *other, **name_other_up):
|
|
763
|
+
'''Refined class comparison.
|
|
764
|
+
|
|
765
|
+
@arg other: The other vector (L{Vector3d}).
|
|
766
|
+
@kwarg name_other_up: Overriding C{name=other} and C{up=1}
|
|
767
|
+
keyword arguments.
|
|
768
|
+
|
|
769
|
+
@return: The B{C{other}} if compatible.
|
|
770
|
+
|
|
771
|
+
@raise TypeError: Incompatible B{C{other}} C{type}.
|
|
772
|
+
'''
|
|
773
|
+
other, name, up = _xother3(self, other, **name_other_up)
|
|
774
|
+
if not isinstance(other, Vector3dBase):
|
|
775
|
+
_NamedBase.others(self, other, name=name, up=up + 1)
|
|
776
|
+
return other
|
|
777
|
+
|
|
778
|
+
def plus(self, other):
|
|
779
|
+
'''Add this vector and an other vector.
|
|
780
|
+
|
|
781
|
+
@arg other: The other vector (L{Vector3d}).
|
|
782
|
+
|
|
783
|
+
@return: Vectorial sum (L{Vector3d}).
|
|
784
|
+
|
|
785
|
+
@raise TypeError: Incompatible B{C{other}} C{type}.
|
|
786
|
+
'''
|
|
787
|
+
xyz = self.others(other).xyz
|
|
788
|
+
return self._plus(*xyz)
|
|
789
|
+
|
|
790
|
+
sum = plus # alternate name
|
|
791
|
+
|
|
792
|
+
def _plus(self, x, y, z):
|
|
793
|
+
'''(INTERNAL) Helper for methods C{.plus} and C{.plus_}.
|
|
794
|
+
'''
|
|
795
|
+
return self.classof(self.x + x, self.y + y, self.z + z)
|
|
796
|
+
|
|
797
|
+
def plus_(self, other_x, *y_z):
|
|
798
|
+
'''Sum of this vector and separate X, Y and Z components.
|
|
799
|
+
|
|
800
|
+
@arg other_x: X component (C{scalar}) or a vector's
|
|
801
|
+
X, Y, and Z components (C{Cartesian},
|
|
802
|
+
L{Ecef9Tuple}, C{Nvector}, L{Vector3d},
|
|
803
|
+
L{Vector3Tuple}, L{Vector4Tuple}).
|
|
804
|
+
@arg y_z: Y and Z components (C{scalar}, C{scalar}),
|
|
805
|
+
ignored if B{C{other_x}} is not C{scalar}.
|
|
806
|
+
|
|
807
|
+
@return: New, vectiorial vector (L{Vector3d}).
|
|
808
|
+
|
|
809
|
+
@raise ValueError: Invalid B{C{other_x}} or B{C{y_z}}.
|
|
810
|
+
'''
|
|
811
|
+
return self._plus(*_xyz3(self.plus_, other_x, *y_z))
|
|
812
|
+
|
|
813
|
+
def rotate(self, axis, theta):
|
|
814
|
+
'''Rotate this vector around an axis by a specified angle.
|
|
815
|
+
|
|
816
|
+
@arg axis: The axis being rotated around (L{Vector3d}).
|
|
817
|
+
@arg theta: The angle of rotation (C{radians}).
|
|
818
|
+
|
|
819
|
+
@return: New, rotated vector (L{Vector3d}).
|
|
820
|
+
|
|
821
|
+
@see: U{Rotation matrix from axis and angle<https://WikiPedia.org/wiki/
|
|
822
|
+
Rotation_matrix#Rotation_matrix_from_axis_and_angle>} and
|
|
823
|
+
U{Quaternion-derived rotation matrix<https://WikiPedia.org/wiki/
|
|
824
|
+
Quaternions_and_spatial_rotation#Quaternion-derived_rotation_matrix>}.
|
|
825
|
+
'''
|
|
826
|
+
s, c = _MODS.utily.sincos2(theta) # rotation angle
|
|
827
|
+
d = _1_0 - c
|
|
828
|
+
if d or s:
|
|
829
|
+
p = self.unit().xyz # point being rotated
|
|
830
|
+
r = self.others(axis=axis).unit() # axis being rotated around
|
|
831
|
+
|
|
832
|
+
ax, ay, az = r.xyz # quaternion-derived rotation matrix
|
|
833
|
+
bx, by, bz = r.times(d).xyz
|
|
834
|
+
sx, sy, sz = r.times(s).xyz
|
|
835
|
+
|
|
836
|
+
x = fdot(p, ax * bx + c, ax * by - sz, ax * bz + sy)
|
|
837
|
+
y = fdot(p, ay * bx + sz, ay * by + c, ay * bz - sx)
|
|
838
|
+
z = fdot(p, az * bx - sy, az * by + sx, az * bz + c)
|
|
839
|
+
else: # unrotated
|
|
840
|
+
x, y, z = self.xyz
|
|
841
|
+
return self.classof(x, y, z)
|
|
842
|
+
|
|
843
|
+
@deprecated_method
|
|
844
|
+
def rotateAround(self, axis, theta): # PYCHOK no cover
|
|
845
|
+
'''DEPRECATED, use method C{rotate}.'''
|
|
846
|
+
return self.rotate(axis, theta)
|
|
847
|
+
|
|
848
|
+
def times(self, factor):
|
|
849
|
+
'''Multiply this vector by a scalar.
|
|
850
|
+
|
|
851
|
+
@arg factor: Scale factor (C{scalar}).
|
|
852
|
+
|
|
853
|
+
@return: New, scaled vector (L{Vector3d}).
|
|
854
|
+
|
|
855
|
+
@raise TypeError: Non-scalar B{C{factor}}.
|
|
856
|
+
'''
|
|
857
|
+
return self._times(Scalar(factor=factor))
|
|
858
|
+
|
|
859
|
+
def _times(self, s):
|
|
860
|
+
'''(INTERNAL) Helper for C{.dividedBy} and C{.times}.
|
|
861
|
+
'''
|
|
862
|
+
return self.classof(self.x * s, self.y * s, self.z * s)
|
|
863
|
+
|
|
864
|
+
def times_(self, other_x, *y_z):
|
|
865
|
+
'''Multiply this vector's components by separate X, Y and Z factors.
|
|
866
|
+
|
|
867
|
+
@arg other_x: X scale factor (C{scalar}) or a vector's
|
|
868
|
+
X, Y, and Z components as scale factors
|
|
869
|
+
(C{Cartesian}, L{Ecef9Tuple}, C{Nvector},
|
|
870
|
+
L{Vector3d}, L{Vector3Tuple}, L{Vector4Tuple}).
|
|
871
|
+
@arg y_z: Y and Z scale factors (C{scalar}, C{scalar}),
|
|
872
|
+
ignored if B{C{other_x}} is not C{scalar}.
|
|
873
|
+
|
|
874
|
+
@return: New, scaled vector (L{Vector3d}).
|
|
875
|
+
|
|
876
|
+
@raise ValueError: Invalid B{C{other_x}} or B{C{y_z}}.
|
|
877
|
+
'''
|
|
878
|
+
x, y, z = _xyz3(self.times_, other_x, *y_z)
|
|
879
|
+
return self.classof(self.x * x, self.y * y, self.z * z)
|
|
880
|
+
|
|
881
|
+
# @deprecated_method
|
|
882
|
+
# def to2ab(self): # PYCHOK no cover
|
|
883
|
+
# '''DEPRECATED, use property C{Nvector.philam}.
|
|
884
|
+
#
|
|
885
|
+
# @return: A L{PhiLam2Tuple}C{(phi, lam)}.
|
|
886
|
+
# '''
|
|
887
|
+
# return _MODS.formy.n_xyz2philam(self.x, self.y, self.z)
|
|
888
|
+
|
|
889
|
+
# @deprecated_method
|
|
890
|
+
# def to2ll(self): # PYCHOK no cover
|
|
891
|
+
# '''DEPRECATED, use property C{Nvector.latlon}.
|
|
892
|
+
#
|
|
893
|
+
# @return: A L{LatLon2Tuple}C{(lat, lon)}.
|
|
894
|
+
# '''
|
|
895
|
+
# return _MODS.formy.n_xyz2latlon(self.x, self.y, self.z)
|
|
896
|
+
|
|
897
|
+
@deprecated_method
|
|
898
|
+
def to3xyz(self): # PYCHOK no cover
|
|
899
|
+
'''DEPRECATED, use property L{xyz}.
|
|
900
|
+
'''
|
|
901
|
+
return self.xyz
|
|
902
|
+
|
|
903
|
+
def toStr(self, prec=5, fmt=Fmt.PAREN, sep=_COMMASPACE_): # PYCHOK expected
|
|
904
|
+
'''Return a string representation of this vector.
|
|
905
|
+
|
|
906
|
+
@kwarg prec: Number of decimal places (C{int}).
|
|
907
|
+
@kwarg fmt: Enclosing format to use (C{str}).
|
|
908
|
+
@kwarg sep: Separator between components (C{str}).
|
|
909
|
+
|
|
910
|
+
@return: Vector as "(x, y, z)" (C{str}).
|
|
911
|
+
'''
|
|
912
|
+
t = sep.join(strs(self.xyz, prec=prec))
|
|
913
|
+
return (fmt % (t,)) if fmt else t
|
|
914
|
+
|
|
915
|
+
def unit(self, ll=None):
|
|
916
|
+
'''Normalize this vector to unit length.
|
|
917
|
+
|
|
918
|
+
@kwarg ll: Optional, original location (C{LatLon}).
|
|
919
|
+
|
|
920
|
+
@return: Normalized vector (L{Vector3d}).
|
|
921
|
+
'''
|
|
922
|
+
u = self._united
|
|
923
|
+
if ll:
|
|
924
|
+
u._fromll = ll
|
|
925
|
+
return u
|
|
926
|
+
|
|
927
|
+
@Property_RO
|
|
928
|
+
def _united(self): # __dict__ value overwritten below
|
|
929
|
+
'''(INTERNAL) Get normalized vector (L{Vector3d}).
|
|
930
|
+
'''
|
|
931
|
+
n = self.length
|
|
932
|
+
if n > EPS0 and fabs(n - _1_0) > EPS0:
|
|
933
|
+
u = self._xnamed(self.dividedBy(n))
|
|
934
|
+
u._update(False, length=_1_0, length2=_1_0, _united=u)
|
|
935
|
+
else:
|
|
936
|
+
u = self.copy()
|
|
937
|
+
u._update(False, _united=u)
|
|
938
|
+
if self._fromll:
|
|
939
|
+
u._fromll = self._fromll
|
|
940
|
+
return u
|
|
941
|
+
|
|
942
|
+
@Property
|
|
943
|
+
def x(self):
|
|
944
|
+
'''Get the X component (C{float}).
|
|
945
|
+
'''
|
|
946
|
+
return self._x
|
|
947
|
+
|
|
948
|
+
@x.setter # PYCHOK setter!
|
|
949
|
+
def x(self, x):
|
|
950
|
+
'''Set the X component, if different (C{float}).
|
|
951
|
+
'''
|
|
952
|
+
x = Float(x=x)
|
|
953
|
+
if self._x != x:
|
|
954
|
+
_update_all(self, needed=3)
|
|
955
|
+
self._x = x
|
|
956
|
+
|
|
957
|
+
@Property
|
|
958
|
+
def xyz(self):
|
|
959
|
+
'''Get the X, Y and Z components (L{Vector3Tuple}C{(x, y, z)}).
|
|
960
|
+
'''
|
|
961
|
+
return _MODS.namedTuples.Vector3Tuple(self.x, self.y, self.z, name=self.name)
|
|
962
|
+
|
|
963
|
+
@xyz.setter # PYCHOK setter!
|
|
964
|
+
def xyz(self, xyz):
|
|
965
|
+
'''Set the X, Y and Z components (C{Cartesian}, L{Ecef9Tuple},
|
|
966
|
+
C{Nvector}, L{Vector3d}, L{Vector3Tuple}, L{Vector4Tuple}
|
|
967
|
+
or a C{tuple} or C{list} of 3+ C{scalar} items).
|
|
968
|
+
'''
|
|
969
|
+
self._xyz(xyz)
|
|
970
|
+
|
|
971
|
+
def _xyz(self, x_xyz, *y_z):
|
|
972
|
+
'''(INTERNAL) Set the C{_x}, C{_y} and C{_z} attributes.
|
|
973
|
+
'''
|
|
974
|
+
_update_all(self, needed=3)
|
|
975
|
+
self._x, self._y, self._z = _xyz3(_xyz_, x_xyz, *y_z)
|
|
976
|
+
return self
|
|
977
|
+
|
|
978
|
+
@property_RO
|
|
979
|
+
def x2y2z2(self):
|
|
980
|
+
'''Get the X, Y and Z components I{squared} (3-tuple C{(x**2, y**2, z**2)}).
|
|
981
|
+
'''
|
|
982
|
+
return self.x**2, self.y**2, self.z**2
|
|
983
|
+
|
|
984
|
+
@Property
|
|
985
|
+
def y(self):
|
|
986
|
+
'''Get the Y component (C{float}).
|
|
987
|
+
'''
|
|
988
|
+
return self._y
|
|
989
|
+
|
|
990
|
+
@y.setter # PYCHOK setter!
|
|
991
|
+
def y(self, y):
|
|
992
|
+
'''Set the Y component, if different (C{float}).
|
|
993
|
+
'''
|
|
994
|
+
y = Float(y=y)
|
|
995
|
+
if self._y != y:
|
|
996
|
+
_update_all(self, needed=3)
|
|
997
|
+
self._y = y
|
|
998
|
+
|
|
999
|
+
@Property
|
|
1000
|
+
def z(self):
|
|
1001
|
+
'''Get the Z component (C{float}).
|
|
1002
|
+
'''
|
|
1003
|
+
return self._z
|
|
1004
|
+
|
|
1005
|
+
@z.setter # PYCHOK setter!
|
|
1006
|
+
def z(self, z):
|
|
1007
|
+
'''Set the Z component, if different (C{float}).
|
|
1008
|
+
'''
|
|
1009
|
+
z = Float(z=z)
|
|
1010
|
+
if self._z != z:
|
|
1011
|
+
_update_all(self, needed=3)
|
|
1012
|
+
self._z = z
|
|
1013
|
+
|
|
1014
|
+
|
|
1015
|
+
def _xyz3(where, x_xyz, *y_z): # in .cartesianBase._rtp3
|
|
1016
|
+
'''(INTERNAL) Helper for C{Vector3dBase.__init__}, C{-.apply}, C{-.times_} and C{-._xyz}.
|
|
1017
|
+
'''
|
|
1018
|
+
try:
|
|
1019
|
+
x_y_z = map1(_float0, x_xyz, *y_z) if y_z else ( # islistuple for VectorXTuple
|
|
1020
|
+
map2(_float0, x_xyz[:3]) if islistuple(x_xyz, minum=3) else
|
|
1021
|
+
x_xyz.xyz)
|
|
1022
|
+
except (AttributeError, TypeError, ValueError) as x:
|
|
1023
|
+
raise _xError(x, unstr(where, x_xyz, *y_z))
|
|
1024
|
+
return x_y_z
|
|
1025
|
+
|
|
1026
|
+
|
|
1027
|
+
__all__ += _ALL_DOCS(Vector3dBase)
|
|
1028
|
+
|
|
1029
|
+
# **) MIT License
|
|
1030
|
+
#
|
|
1031
|
+
# Copyright (C) 2016-2024 -- mrJean1 at Gmail -- All Rights Reserved.
|
|
1032
|
+
#
|
|
1033
|
+
# Permission is hereby granted, free of charge, to any person obtaining a
|
|
1034
|
+
# copy of this software and associated documentation files (the "Software"),
|
|
1035
|
+
# to deal in the Software without restriction, including without limitation
|
|
1036
|
+
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
1037
|
+
# and/or sell copies of the Software, and to permit persons to whom the
|
|
1038
|
+
# Software is furnished to do so, subject to the following conditions:
|
|
1039
|
+
#
|
|
1040
|
+
# The above copyright notice and this permission notice shall be included
|
|
1041
|
+
# in all copies or substantial portions of the Software.
|
|
1042
|
+
#
|
|
1043
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
1044
|
+
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
1045
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
1046
|
+
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
|
1047
|
+
# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
|
1048
|
+
# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
1049
|
+
# OTHER DEALINGS IN THE SOFTWARE.
|