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
@@ -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.